mirror of
https://gitlab.com/pulsechaincom/staking-deposit-cli.git
synced 2024-12-23 11:57:19 +00:00
Add fork_version
1. Use v0.12.0 signing format 2. `GENESIS_FORK_VERSION` may be various for mainnet and testnets. Add option for it.
This commit is contained in:
parent
2359a9d033
commit
08eeed26ed
@ -60,6 +60,7 @@ You can also run the tool with optional arguments:
|
||||
| `--num_validators` | Non-negative integer | The number of signing keys you want to generate. Note that the child key(s) are generated via the same master key. |
|
||||
| `--mnemonic_language` | String. Options: `czech`, `chinese_traditional`, `chinese_simplified`, `english`, `spanish`, `italian`, `korean`. Default to `english` | The mnemonic language |
|
||||
| `--folder` | String. Pointing to `./validator_keys` by default | The folder path for the keystore(s) and deposit(s) |
|
||||
| `--chain` | String. `mainnet` by defualt | The chain setting for the signing domain. |
|
||||
|
||||
### For Windows users
|
||||
|
||||
|
@ -12,7 +12,7 @@ from eth2deposit.key_handling.keystore import (
|
||||
from eth2deposit.utils.constants import BLS_WITHDRAWAL_PREFIX
|
||||
from eth2deposit.utils.crypto import SHA256
|
||||
from eth2deposit.utils.ssz import (
|
||||
compute_domain,
|
||||
compute_deposit_domain,
|
||||
compute_signing_root,
|
||||
DepositMessage,
|
||||
Deposit,
|
||||
@ -20,11 +20,12 @@ from eth2deposit.utils.ssz import (
|
||||
|
||||
|
||||
class ValidatorCredentials:
|
||||
def __init__(self, *, mnemonic: str, index: int, amount: int):
|
||||
def __init__(self, *, mnemonic: str, index: int, amount: int, fork_version: bytes):
|
||||
self.signing_key_path = 'm/12381/3600/%s/0' % index
|
||||
self.signing_sk = mnemonic_and_path_to_key(mnemonic=mnemonic, path=self.signing_key_path)
|
||||
self.withdrawal_sk = mnemonic_and_path_to_key(mnemonic=mnemonic, path=self.signing_key_path + '/0')
|
||||
self.amount = amount
|
||||
self.fork_version = fork_version
|
||||
|
||||
@property
|
||||
def signing_pk(self) -> bytes:
|
||||
@ -57,11 +58,17 @@ class ValidatorCredentials:
|
||||
|
||||
|
||||
def mnemonic_to_credentials(*, mnemonic: str, num_keys: int,
|
||||
amounts: List[int], start_index: int=0,) -> List[ValidatorCredentials]:
|
||||
amounts: List[int], fork_version: bytes, start_index: int=0,) -> List[ValidatorCredentials]:
|
||||
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]
|
||||
credentials = [
|
||||
ValidatorCredentials(
|
||||
mnemonic=mnemonic,
|
||||
index=index,
|
||||
amount=amounts[index],
|
||||
fork_version=fork_version,
|
||||
) for index in key_indices
|
||||
]
|
||||
return credentials
|
||||
|
||||
|
||||
@ -69,12 +76,12 @@ def export_keystores(*, credentials: List[ValidatorCredentials], password: str,
|
||||
return [credential.save_signing_keystore(password=password, folder=folder) for credential in credentials]
|
||||
|
||||
|
||||
def sign_deposit_data(deposit_data: DepositMessage, sk: int) -> Deposit:
|
||||
def sign_deposit_data(deposit_data: DepositMessage, sk: int, fork_version: bytes) -> Deposit:
|
||||
'''
|
||||
Given a DepositMessage, it signs its root and returns a Deposit
|
||||
'''
|
||||
assert bls.PrivToPub(sk) == deposit_data.pubkey
|
||||
domain = compute_domain()
|
||||
domain = compute_deposit_domain(fork_version)
|
||||
signing_root = compute_signing_root(deposit_data, domain)
|
||||
signed_deposit_data = Deposit(
|
||||
**deposit_data.as_dict(),
|
||||
@ -91,10 +98,11 @@ def export_deposit_data_json(*, credentials: List[ValidatorCredentials], folder:
|
||||
withdrawal_credentials=credential.withdrawal_credentials,
|
||||
amount=credential.amount,
|
||||
)
|
||||
signed_deposit_datum = sign_deposit_data(deposit_datum, credential.signing_sk)
|
||||
signed_deposit_datum = sign_deposit_data(deposit_datum, credential.signing_sk, credential.fork_version)
|
||||
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})
|
||||
datum_dict.update({'fork_version': credential.fork_version})
|
||||
deposit_data.append(datum_dict)
|
||||
|
||||
filefolder = os.path.join(folder, 'deposit_data-%i.json' % time.time())
|
||||
|
@ -19,6 +19,11 @@ from eth2deposit.utils.constants import (
|
||||
DEFAULT_VALIDATOR_KEYS_FOLDER_NAME,
|
||||
)
|
||||
from eth2deposit.utils.ascii_art import RHINO_0
|
||||
from eth2deposit.settings import (
|
||||
ALL_CHAINS,
|
||||
MAINNET,
|
||||
get_setting,
|
||||
)
|
||||
|
||||
words_path = os.path.join(os.getcwd(), WORD_LISTS_PATH)
|
||||
languages = get_languages(words_path)
|
||||
@ -54,7 +59,7 @@ def check_python_version() -> None:
|
||||
'--num_validators',
|
||||
prompt='Please choose how many validators you wish to run',
|
||||
required=True,
|
||||
type=int, # type: ignore
|
||||
type=int,
|
||||
)
|
||||
@click.option(
|
||||
'--mnemonic_language',
|
||||
@ -67,18 +72,29 @@ def check_python_version() -> None:
|
||||
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
||||
default=os.getcwd()
|
||||
)
|
||||
@click.option(
|
||||
'--chain',
|
||||
type=click.Choice(ALL_CHAINS.keys(), case_sensitive=False),
|
||||
default=MAINNET,
|
||||
)
|
||||
@click.password_option(prompt='Type the password that secures your validator keystore(s)')
|
||||
def main(num_validators: int, mnemonic_language: str, folder: str, password: str):
|
||||
def main(num_validators: int, mnemonic_language: str, folder: str, chain: str, password: str) -> None:
|
||||
check_python_version()
|
||||
mnemonic = generate_mnemonic(mnemonic_language, words_path)
|
||||
amounts = [MAX_DEPOSIT_AMOUNT] * num_validators
|
||||
folder = os.path.join(folder, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
|
||||
setting = get_setting(chain)
|
||||
if not os.path.exists(folder):
|
||||
os.mkdir(folder)
|
||||
click.clear()
|
||||
click.echo(RHINO_0)
|
||||
click.echo('Creating your keys.')
|
||||
credentials = mnemonic_to_credentials(mnemonic=mnemonic, num_keys=num_validators, amounts=amounts)
|
||||
credentials = mnemonic_to_credentials(
|
||||
mnemonic=mnemonic,
|
||||
num_keys=num_validators,
|
||||
amounts=amounts,
|
||||
fork_version=setting.GENESIS_FORK_VERSION,
|
||||
)
|
||||
click.echo('Saving your keystore(s).')
|
||||
keystore_filefolders = export_keystores(credentials=credentials, password=password, folder=folder)
|
||||
click.echo('Creating your deposit(s).')
|
||||
|
19
eth2deposit/settings.py
Normal file
19
eth2deposit/settings.py
Normal file
@ -0,0 +1,19 @@
|
||||
from typing import Dict, NamedTuple
|
||||
|
||||
|
||||
class BaseChainSetting(NamedTuple):
|
||||
GENESIS_FORK_VERSION: bytes
|
||||
|
||||
|
||||
MainnetSetting = BaseChainSetting(
|
||||
GENESIS_FORK_VERSION=bytes.fromhex('00000000'),
|
||||
)
|
||||
|
||||
MAINNET = 'mainnet'
|
||||
ALL_CHAINS: Dict[str, BaseChainSetting] = {
|
||||
MAINNET: MainnetSetting,
|
||||
}
|
||||
|
||||
|
||||
def get_setting(chain_name: str = MAINNET) -> BaseChainSetting:
|
||||
return ALL_CHAINS[chain_name]
|
@ -1,9 +1,10 @@
|
||||
import os
|
||||
|
||||
|
||||
ZERO_BYTES32 = b'\x00' * 32
|
||||
|
||||
# Spec constants
|
||||
DOMAIN_DEPOSIT = bytes.fromhex('03000000')
|
||||
GENESIS_FORK_VERSION = bytes.fromhex('00000000')
|
||||
BLS_WITHDRAWAL_PREFIX = bytes.fromhex('00')
|
||||
|
||||
MIN_DEPOSIT_AMOUNT = 2 ** 0 * 10 ** 9
|
||||
|
@ -8,13 +8,12 @@ from typing import Any, Dict
|
||||
from py_ecc.bls import G2ProofOfPossession as bls
|
||||
|
||||
from eth2deposit.utils.ssz import (
|
||||
compute_domain,
|
||||
compute_deposit_domain,
|
||||
compute_signing_root,
|
||||
Deposit,
|
||||
DepositMessage,
|
||||
)
|
||||
from eth2deposit.utils.constants import (
|
||||
DOMAIN_DEPOSIT,
|
||||
MAX_DEPOSIT_AMOUNT,
|
||||
MIN_DEPOSIT_AMOUNT,
|
||||
)
|
||||
@ -37,6 +36,7 @@ def verify_deposit(deposit_data_dict: Dict[str, Any]) -> bool:
|
||||
amount = deposit_data_dict['amount']
|
||||
signature = BLSSignature(bytes.fromhex(deposit_data_dict['signature']))
|
||||
deposit_data_root = bytes.fromhex(deposit_data_dict['signed_deposit_data_root'])
|
||||
fork_version = bytes.fromhex(deposit_data_dict['fork_version'])
|
||||
|
||||
# Verify deposit amount
|
||||
if not MIN_DEPOSIT_AMOUNT < amount <= MAX_DEPOSIT_AMOUNT:
|
||||
@ -44,7 +44,7 @@ def verify_deposit(deposit_data_dict: Dict[str, Any]) -> bool:
|
||||
|
||||
# Verify deposit signature && pubkey
|
||||
deposit_message = DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount)
|
||||
domain = compute_domain(domain_type=DOMAIN_DEPOSIT)
|
||||
domain = compute_deposit_domain(fork_version)
|
||||
signing_root = compute_signing_root(deposit_message, domain)
|
||||
if not bls.Verify(pubkey, signing_root, signature):
|
||||
return False
|
||||
|
@ -2,14 +2,14 @@ from ssz import (
|
||||
ByteVector,
|
||||
Serializable,
|
||||
uint64,
|
||||
bytes4,
|
||||
bytes32,
|
||||
bytes48,
|
||||
bytes96
|
||||
)
|
||||
|
||||
from eth2deposit.utils.constants import (
|
||||
DOMAIN_DEPOSIT,
|
||||
GENESIS_FORK_VERSION,
|
||||
ZERO_BYTES32,
|
||||
)
|
||||
|
||||
bytes8 = ByteVector(8)
|
||||
@ -17,25 +17,45 @@ bytes8 = ByteVector(8)
|
||||
|
||||
# Crypto Domain SSZ
|
||||
|
||||
class SigningRoot(Serializable):
|
||||
class SigningData(Serializable):
|
||||
fields = [
|
||||
('object_root', bytes32),
|
||||
('domain', bytes8)
|
||||
('domain', bytes32)
|
||||
]
|
||||
|
||||
|
||||
def compute_domain(domain_type: bytes=DOMAIN_DEPOSIT, fork_version: bytes=GENESIS_FORK_VERSION) -> bytes:
|
||||
class ForkData(Serializable):
|
||||
fields = [
|
||||
('current_version', bytes4),
|
||||
('genesis_validators_root', bytes32),
|
||||
]
|
||||
|
||||
|
||||
def compute_deposit_domain(fork_version: bytes) -> bytes:
|
||||
"""
|
||||
Return the domain for the ``domain_type`` and ``fork_version``.
|
||||
Deposit-only `compute_domain`
|
||||
"""
|
||||
return domain_type + fork_version
|
||||
assert len(fork_version) == 4
|
||||
domain_type = DOMAIN_DEPOSIT
|
||||
fork_data_root = compute_deposit_fork_data_root(fork_version)
|
||||
return domain_type + fork_data_root[:28]
|
||||
|
||||
|
||||
def compute_deposit_fork_data_root(current_version: bytes) -> bytes:
|
||||
genesis_validators_root = ZERO_BYTES32 # For deposit, it's fixed value
|
||||
assert len(current_version) == 4
|
||||
return ForkData(
|
||||
current_version=current_version,
|
||||
genesis_validators_root=genesis_validators_root,
|
||||
).hash_tree_root
|
||||
|
||||
|
||||
def compute_signing_root(ssz_object: Serializable, domain: bytes) -> bytes:
|
||||
"""
|
||||
Return the signing root of an object by calculating the root of the object-domain tree.
|
||||
"""
|
||||
domain_wrapped_object = SigningRoot(
|
||||
assert len(domain) == 32
|
||||
domain_wrapped_object = SigningData(
|
||||
object_root=ssz_object.hash_tree_root,
|
||||
domain=domain,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user