Skip to content

Commit

Permalink
Add CLI readme (#8)
Browse files Browse the repository at this point in the history
* Add CLI readme

* Update poetry dependencies
  • Loading branch information
tsudmi authored Nov 18, 2021
1 parent 8668370 commit 5220097
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 177 deletions.
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,67 @@
# CLI
# Operator CLI

Operators CLI is responsible for generating the validators keys,
deposit data required to register them and uploading the keys to the Hashicorp Vault.

## Usage

### Step 1. Installation

See [releases page](https://github.com/stakewise/cli/releases) to download and decompress the corresponding binary files.

### Step 2. Generate DAO proposal

Run the following command to generate DAO proposal:

```bash
./operator-cli generate-proposal
```

**NB! You must store the generated mnemonic in a secure cold storage.
It will allow you to restore the keys in case the Vault will get corrupted or lost.**

### Step 3. Submit DAO proposal

Create a post about joining operators set at [StakeWise Forum](https://vote.stakewise.io).
In your post you must include the `Specification` section that was generated in step 2.

### Step 4. Deploy ETH2 infrastructure

If the proposal from step 3 is approved by the DAO, follow the instructions [here](https://github.com/stakewise/helm-charts/tree/main/operator#readme)
to deploy the ETH2 staking infrastructure.

### Step 5. Sync keys to the Vault

You must **use the same mnemonic** as generated in step 1.
Also, **using the same mnemonic for multiple vaults will result into validator slashing**.

Run the following command to sync new validator keys to the vault:

```bash
./operator-cli sync-vault
```

After uploading the keys, restart the validators that got new keys added.

### Step 6. Commit Operator

Once you're 100% ready for ether assignments to the validators, commit your operator:

- Go to the [Operators smart contract](https://etherscan.io/address/<address>#writeProxyContract)
- Click on `Connect to Web3` button and connect your wallet. The address must match the one used during proposal generation.
- Call `commitOperator` function. If that's your onboarding, you must deposit 1 ETH collateral together with the call.

Congratulations on becoming StakeWise Node Operator🎉.
Your validators will get ether assigned, and you can claim your operator rewards from [Farms Page](https://app.stakewise.io/farms).


### Operator CLI Environment Settings

| Variable | Description | Required | Default |
|------------------------------|---------------------------------------------------------------------|----------|-------------------------------------------------------------------------|
| WITHDRAWAL_CREDENTIALS | The withdrawal credentials of the validators used in deposit data | No | 0x0100000000000000000000002296e122c1a20fca3cac3371357bdad3be0df079 |
| IPFS_PIN_ENDPOINTS | The IPFS endpoint where the deposit data will be uploaded | No | /dns/ipfs.infura.io/tcp/5001/https |
| IPFS_FETCH_ENDPOINTS | The IPFS endpoints from where the deposit data will be fetched | No | https://gateway.pinata.cloud,http://cloudflare-ipfs.com,https://ipfs.io |
| IPFS_PINATA_API_KEY | The Pinata API key for uploading deposit data for the redundancy | No | - |
| IPFS_PINATA_SECRET_KEY | The Pinata Secret key for uploading deposit data for the redundancy | No | - |
| VAULT_VALIDATORS_MOUNT_POINT | The mount point in Hashicorp Vault for storing validator keys | No | validators |
8 changes: 4 additions & 4 deletions operator_cli/commands/generate_proposal.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def generate_proposal(chain: str, existing_vault: bool) -> None:
specification = f"""
## Specification
- Call `addOperator` function of `PoolValidators` contract with the following parameters:
- DAO calls `addOperator` function of `PoolValidators` contract with the following parameters:
* operator: `{operator}`
* initializeMerkleRoot: `{initialize_merkle_root}`
* initializeMerkleProofs: `{initialize_ipfs_url}`
Expand All @@ -143,21 +143,21 @@ def generate_proposal(chain: str, existing_vault: bool) -> None:
)
specification += f"""
- Call `setOperator` function of `Roles` contract with the following parameters:
- DAO calls `setOperator` function of `Roles` contract with the following parameters:
* account: `{operator}`
* revenueShare: `{share_percentage}`
- If the proposal will be approved, the operator must perform the following steps:
* Call `./operator-cli sync-vault` with the same mnemonic as used for the proposal
* Call `operator-cli sync-vault` with the same mnemonic as used for the proposal
* Create or update validators and make sure the new keys are added
* Call `commitOperator` from the `{operator}` address together with 1 ETH collateral
"""
else:
specification += f"""
- If the proposal will be approved, the operator must perform the following steps:
* Call `./operator-cli sync-vault` with the same mnemonic as used for generating the proposal
* Call `operator-cli sync-vault` with the same mnemonic as used for generating the proposal
* Create or update validators and make sure the new keys are added
* Call `commitOperator` from the `{operator}` address
"""
Expand Down
11 changes: 10 additions & 1 deletion operator_cli/commands/sync_vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ def sync_vault(chain: str) -> None:
except InvalidRequest:
pass

namespace = click.prompt(
"Enter the validators kubernetes namespace",
default="validators",
type=click.STRING,
)
mnemonic = click.prompt(
'Enter your mnemonic separated by spaces (" ")',
value_proc=validate_mnemonic,
Expand All @@ -79,7 +84,11 @@ def sync_vault(chain: str) -> None:
)

vault = Vault(
vault_client=vault_client, beacon=beacon_client, chain=chain, mnemonic=mnemonic
vault_client=vault_client,
beacon=beacon_client,
chain=chain,
mnemonic=mnemonic,
namespace=namespace,
)

vault.apply_vault_changes()
Expand Down
10 changes: 4 additions & 6 deletions operator_cli/eth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,13 @@ def generate_password() -> str:
while True:
password = [secrets.choice(alphabet) for _ in range(20)]
password_set = set(password)
if not (
if (
upper_set.intersection(password_set)
and lower_set.intersection(password_set)
and special_set.intersection(password_set)
and digits_set.intersection(password_set)
):
continue

return "".join(password)
return "".join(password)


def get_deposit_data_signature(
Expand Down Expand Up @@ -274,7 +272,7 @@ def generate_merkle_deposit_datum(
amount=str(deposit_amount),
withdrawal_credentials=WITHDRAWAL_CREDENTIALS,
deposit_data_root=w3.toHex(deposit_data_root),
proof="",
proof=[],
)
merkle_deposit_datum.append(deposit_data)

Expand All @@ -283,7 +281,7 @@ def generate_merkle_deposit_datum(
# collect proofs
for i, deposit_data in enumerate(merkle_deposit_datum):
proof: List[HexStr] = merkle_tree.get_hex_proof(merkle_elements[i])
deposit_data["proof"] = ",".join(proof)
deposit_data["proof"] = proof

# calculate merkle root
merkle_root: HexStr = merkle_tree.get_hex_root()
Expand Down
4 changes: 0 additions & 4 deletions operator_cli/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import os
from typing import Dict

from decouple import Csv, config
Expand All @@ -16,8 +15,6 @@
PRATER: PraterSetting,
}

OUTPUT_DIR = config("OUTPUT_DIR", default=os.path.join(os.getcwd(), "output"))

WITHDRAWAL_CREDENTIALS = config(
"WITHDRAWAL_CREDENTIALS",
default="0x0100000000000000000000002296e122c1a20fca3cac3371357bdad3be0df079",
Expand All @@ -44,7 +41,6 @@
VAULT_VALIDATORS_MOUNT_POINT = config(
"VAULT_VALIDATORS_MOUNT_POINT", default="validators"
)
VALIDATORS_NAMESPACE = config("VALIDATORS_NAMESPACE", default="validators")

ETHEREUM_MAINNET_SUBGRAPH_URL = config(
"ETHEREUM_MAINNET_SUBGRAPH_URL",
Expand Down
4 changes: 2 additions & 2 deletions operator_cli/typings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, NamedTuple, NewType, TypedDict
from typing import Dict, List, NamedTuple, NewType, TypedDict

from eth_typing import HexStr

Expand All @@ -19,7 +19,7 @@ class MerkleDepositData(TypedDict):
amount: str
withdrawal_credentials: HexStr
deposit_data_root: HexStr
proof: str
proof: List[HexStr]


class VaultKeystore(TypedDict):
Expand Down
10 changes: 4 additions & 6 deletions operator_cli/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@
)
from operator_cli.ipfs import get_operator_deposit_datum
from operator_cli.queries import get_stakewise_gql_client
from operator_cli.settings import (
MIGRATE_LEGACY,
VALIDATORS_NAMESPACE,
VAULT_VALIDATORS_MOUNT_POINT,
)
from operator_cli.settings import MIGRATE_LEGACY, VAULT_VALIDATORS_MOUNT_POINT
from operator_cli.typings import SigningKey, VaultKeystore, VaultState

MAX_KEYS_PER_VALIDATOR = 100
Expand Down Expand Up @@ -63,11 +59,13 @@ def __init__(
beacon: Beacon,
chain: str,
mnemonic: str,
namespace: str,
):
self.vault_client = vault_client
self.sw_gql_client = get_stakewise_gql_client(chain)
self.beacon = beacon
self.mnemonic = mnemonic
self.namespace = namespace
self.check_mnemonic()

@cached_property
Expand Down Expand Up @@ -402,7 +400,7 @@ def sync_vault_validators(self) -> None:
name=validator_name,
policies=[validator_name],
bound_service_account_names=validator_name,
bound_service_account_namespaces=VALIDATORS_NAMESPACE,
bound_service_account_namespaces=self.namespace,
)
bar.update(1)

Expand Down
Loading

0 comments on commit 5220097

Please sign in to comment.