Skip to content

Commit

Permalink
Merge pull request #190 from ethereum/dev
Browse files Browse the repository at this point in the history
## Release v1.1.1:

* Ensure keystore permissions are 440 #176
* Adds Prater testnet support #189
* Removes support for old testnets (Witti, Altona, Medalla, Spadina, Zinken) #189
  • Loading branch information
CarlBeek committed Mar 22, 2021
2 parents ed5a6d3 + 32c60c0 commit 7bac110
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 31 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ You can find the audit report by Trail of Bits [here](https://github.com/trailof

### For Linux or MacOS users

#### File Permissions

On Unix-based systems, keystores and the `deposit_data*.json` have `440`/`-r--r-----` file permissions (user & group read only). This improves security by limiting which users and processes that have access to these files. If you are getting `permission denied` errors when handling your keystores, consider changing which user/group owns the file (with `chown`) or, if need be, change the file permissions with `chmod`.

#### Option 1. Download binary executable file

##### Step 1. Installation
Expand Down Expand Up @@ -289,10 +293,10 @@ You can also run the tool with optional arguments:
docker run -it --rm -v $(pwd)/validator_keys:/app/validator_keys ethereum/eth2.0-deposit-cli new-mnemonic --num_validators=<NUM_VALIDATORS> --mnemonic_language=english --folder=<YOUR_FOLDER_PATH>
```

Example for 1 validator on the [Medalla testnet](https://medalla.launchpad.ethereum.org/) using english:
Example for 1 validator on the [Prater testnet](https://prater.launchpad.ethereum.org/) using english:

```sh
docker run -it --rm -v $(pwd)/validator_keys:/app/validator_keys ethereum/eth2.0-deposit-cli new-mnemonic --num_validators=1 --mnemonic_language=english --chain=medalla
docker run -it --rm -v $(pwd)/validator_keys:/app/validator_keys ethereum/eth2.0-deposit-cli new-mnemonic --num_validators=1 --mnemonic_language=english --chain=prater
```

###### Arguments
Expand Down
2 changes: 2 additions & 0 deletions eth2deposit/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ def export_deposit_data_json(self, folder: str) -> str:
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())
if os.name == 'posix':
os.chmod(filefolder, int('440', 8)) # Read for owner & group
return filefolder

def verify_keystores(self, keystore_filefolders: List[str], password: str) -> bool:
Expand Down
7 changes: 5 additions & 2 deletions eth2deposit/key_handling/keystore.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
field as dataclass_field
)
import json
import os
from py_ecc.bls import G2ProofOfPossession as bls
from secrets import randbits
from typing import Any, Dict, Union
Expand Down Expand Up @@ -90,12 +91,14 @@ class Keystore(BytesDataclass):
def kdf(self, **kwargs: Any) -> bytes:
return scrypt(**kwargs) if 'scrypt' in self.crypto.kdf.function else PBKDF2(**kwargs)

def save(self, file: str) -> None:
def save(self, filefolder: str) -> None:
"""
Save self as a JSON keystore.
"""
with open(file, 'w') as f:
with open(filefolder, 'w') as f:
f.write(self.as_json())
if os.name == 'posix':
os.chmod(filefolder, int('440', 8)) # Read for owner & group

@classmethod
def from_json(cls, json_dict: Dict[Any, Any]) -> 'Keystore':
Expand Down
26 changes: 5 additions & 21 deletions eth2deposit/settings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Dict, NamedTuple


DEPOSIT_CLI_VERSION = '1.1.0'
DEPOSIT_CLI_VERSION = '1.1.1'


class BaseChainSetting(NamedTuple):
Expand All @@ -10,38 +10,22 @@ class BaseChainSetting(NamedTuple):


MAINNET = 'mainnet'
WITTI = 'witti'
ALTONA = 'altona'
MEDALLA = 'medalla'
SPADINA = 'spadina'
ZINKEN = 'zinken'
PYRMONT = 'pyrmont'
PRATER = 'prater'


# Eth2 Mainnet setting
MainnetSetting = BaseChainSetting(ETH2_NETWORK_NAME=MAINNET, GENESIS_FORK_VERSION=bytes.fromhex('00000000'))
# Eth2 spec v0.11.3 testnet
WittiSetting = BaseChainSetting(ETH2_NETWORK_NAME=WITTI, GENESIS_FORK_VERSION=bytes.fromhex('00000113'))
# Eth2 spec v0.12.1 testnet
AltonaSetting = BaseChainSetting(ETH2_NETWORK_NAME=ALTONA, GENESIS_FORK_VERSION=bytes.fromhex('00000121'))
# Eth2 "official" public testnet (spec v0.12.2)
MedallaSetting = BaseChainSetting(ETH2_NETWORK_NAME=MEDALLA, GENESIS_FORK_VERSION=bytes.fromhex('00000001'))
# Eth2 "dress rehearsal" testnet (spec v0.12.3)
SpadinaSetting = BaseChainSetting(ETH2_NETWORK_NAME=SPADINA, GENESIS_FORK_VERSION=bytes.fromhex('00000002'))
# Eth2 "dress rehearsal" testnet (spec v0.12.3)
ZinkenSetting = BaseChainSetting(ETH2_NETWORK_NAME=ZINKEN, GENESIS_FORK_VERSION=bytes.fromhex('00000003'))
# Eth2 pre-launch testnet (spec v1.0.0)
PyrmontSetting = BaseChainSetting(ETH2_NETWORK_NAME=PYRMONT, GENESIS_FORK_VERSION=bytes.fromhex('00002009'))
# Eth2 testnet (spec v1.0.1)
PraterSetting = BaseChainSetting(ETH2_NETWORK_NAME=PRATER, GENESIS_FORK_VERSION=bytes.fromhex('00001020'))


ALL_CHAINS: Dict[str, BaseChainSetting] = {
MAINNET: MainnetSetting,
WITTI: WittiSetting,
ALTONA: AltonaSetting,
MEDALLA: MedallaSetting,
SPADINA: SpadinaSetting,
ZINKEN: ZinkenSetting,
PYRMONT: PyrmontSetting,
PRATER: PraterSetting,
}


Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setup(
name="eth2deposit",
version='1.1.0',
version='1.1.1',
py_modules=["eth2deposit"],
packages=find_packages(exclude=('tests', 'docs')),
python_requires=">=3.7,<4",
Expand Down
4 changes: 4 additions & 0 deletions tests/test_cli/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ def clean_key_folder(my_folder_path: str) -> None:
def get_uuid(key_file: str) -> str:
keystore = Keystore.from_file(key_file)
return keystore.uuid


def get_permissions(path: str, file_name: str) -> str:
return oct(os.stat(os.path.join(path, file_name)).st_mode & 0o777)
11 changes: 10 additions & 1 deletion tests/test_cli/test_existing_menmonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from eth2deposit.deposit import cli
from eth2deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME
from.helpers import clean_key_folder, get_uuid
from.helpers import clean_key_folder, get_permissions, get_uuid


def test_existing_mnemonic() -> None:
Expand Down Expand Up @@ -38,6 +38,10 @@ def test_existing_mnemonic() -> None:
]
assert len(set(all_uuid)) == 5

# Verify file permissions
if os.name == 'posix':
for file_name in key_files:
assert get_permissions(validator_keys_folder_path, file_name) == '0o440'
# Clean up
clean_key_folder(my_folder_path)

Expand Down Expand Up @@ -85,5 +89,10 @@ async def test_script() -> None:
validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
_, _, key_files = next(os.walk(validator_keys_folder_path))

# Verify file permissions
if os.name == 'posix':
for file_name in key_files:
assert get_permissions(validator_keys_folder_path, file_name) == '0o440'

# Clean up
clean_key_folder(my_folder_path)
12 changes: 11 additions & 1 deletion tests/test_cli/test_new_mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from eth2deposit.cli import new_mnemonic
from eth2deposit.deposit import cli
from eth2deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME
from .helpers import clean_key_folder, get_uuid
from .helpers import clean_key_folder, get_permissions, get_uuid


def test_new_mnemonic(monkeypatch) -> None:
Expand Down Expand Up @@ -40,6 +40,11 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
]
assert len(set(all_uuid)) == 1

# Verify file permissions
if os.name == 'posix':
for file_name in key_files:
assert get_permissions(validator_keys_folder_path, file_name) == '0o440'

# Clean up
clean_key_folder(my_folder_path)

Expand Down Expand Up @@ -103,5 +108,10 @@ async def test_script() -> None:
]
assert len(set(all_uuid)) == 5

# Verify file permissions
if os.name == 'posix':
for file_name in key_files:
assert get_permissions(validator_keys_folder_path, file_name) == '0o440'

# Clean up
clean_key_folder(my_folder_path)
12 changes: 11 additions & 1 deletion tests/test_cli/test_regeneration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from eth2deposit.cli import new_mnemonic
from eth2deposit.deposit import cli
from eth2deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME
from .helpers import clean_key_folder, get_uuid
from .helpers import clean_key_folder, get_permissions, get_uuid


def test_regeneration(monkeypatch) -> None:
Expand Down Expand Up @@ -47,6 +47,11 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
for key_file in part_1_key_files]
assert len(set(all_uuid)) == 2

# Verify file permissions
if os.name == 'posix':
for file_name in part_1_key_files:
assert get_permissions(validator_keys_folder_path_1, file_name) == '0o440'

# Part 2: existing-mnemonic
runner = CliRunner()
# Create index 1 and 2
Expand Down Expand Up @@ -78,6 +83,11 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str:
assert keystore_1_1['pubkey'] == keystore_2_0['pubkey']
assert keystore_1_1['path'] == keystore_2_0['path']

# Verify file permissions
if os.name == 'posix':
for file_name in part_2_key_files:
assert get_permissions(validator_keys_folder_path_2, file_name) == '0o440'

# Clean up
clean_key_folder(folder_path_1)
clean_key_folder(folder_path_2)
4 changes: 2 additions & 2 deletions tests/test_credentials.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from eth2deposit.credentials import CredentialList
from eth2deposit.settings import MedallaSetting
from eth2deposit.settings import MainnetSetting


def test_from_mnemonic() -> None:
Expand All @@ -11,6 +11,6 @@ def test_from_mnemonic() -> None:
mnemonic_password="",
num_keys=1,
amounts=[32, 32],
chain_setting=MedallaSetting,
chain_setting=MainnetSetting,
start_index=1,
)

0 comments on commit 7bac110

Please sign in to comment.