diff --git a/README.md b/README.md index c19a65e..61d8ab0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # 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. +Operators CLI generates validators keys and deposit data for the validators. +The validator keys have to be uploaded to the Hashicorp Vault for the secure storage. +Read more about operators onboarding process [here](https://docs.stakewise.io/node-operator/dao-proposal) ## Usage @@ -11,7 +12,7 @@ See [releases page](https://github.com/stakewise/cli/releases) to download and d ### Step 2. Generate DAO proposal -Run the following command to generate DAO proposal: +Run the following command to generate the DAO proposal specification: ```bash ./operator-cli generate-proposal @@ -23,17 +24,17 @@ It will allow you to restore the keys in case the Vault will get corrupted or lo ### 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. +In your post you must include the `Specification` section that was generated in the previous step. ### 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/charts/operator#readme) +If the proposal from the previous step got approved by the DAO, follow the instructions [here](https://docs.stakewise.io/node-operator/infrastructure) 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**. +Also, **using the same mnemonic for multiple vaults will result into validators slashings**. Run the following command to sync new validator keys to the vault: @@ -41,15 +42,15 @@ Run the following command to sync new validator keys to the vault: ./operator-cli sync-vault ``` -After uploading the keys, restart the validators that got new keys added. +After uploading the keys, make sure you have the right number of validators running and restart those that got new keys added. ### Step 6. Commit Operator -Once you're 100% ready for ether assignments to the validators, commit your operator: +Once you're 100% ready for Ether assignments, commit your operator: -- Go to the [Operators smart contract](https://etherscan.io/address/
#writeProxyContract) +- Go to the Operators smart contract ([Goerli](https://goerli.etherscan.io/address/0x0d92156861a0BC7037cC21470327Bd3Bc750EB1D#writeProxyContract), Mainnet) - 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. +- Call `commitOperator` function. If that's your onboarding, you must deposit 1 ETH (specified in Wei) 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). @@ -57,11 +58,12 @@ Your validators will get ether assigned, and you can claim your operator rewards ### 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 | +| Variable | Description | Required | Default | +|--------------------------------|----------------------------------------------------------------------------------|----------|-------------------------------------------------------------------------| +| MAINNET_WITHDRAWAL_CREDENTIALS | The withdrawal credentials of the validators used in mainnet deposit data | No | 0x0100000000000000000000002296e122c1a20fca3cac3371357bdad3be0df079 | +| PRATER_WITHDRAWAL_CREDENTIALS | The withdrawal credentials of the validators used in prater testnet deposit data | No | 0x003e294ffc37978496f1b9298d5984ad4d55d4e2d1e6a06ee6904810c7b9e0d5 | +| 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 | diff --git a/operator_cli/commands/generate_proposal.py b/operator_cli/commands/generate_proposal.py index 44725f4..a1567b8 100644 --- a/operator_cli/commands/generate_proposal.py +++ b/operator_cli/commands/generate_proposal.py @@ -141,12 +141,15 @@ def generate_proposal(chain: str, existing_vault: bool) -> None: default=50.00, value_proc=validate_share_percentage, ) - specification += f""" + if share_percentage > 0: + specification += f""" - DAO calls `setOperator` function of `Roles` contract with the following parameters: * account: `{operator}` * revenueShare: `{share_percentage}` +""" + specification += """ - 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 diff --git a/operator_cli/commands/sync_vault.py b/operator_cli/commands/sync_vault.py index ac77072..b900800 100644 --- a/operator_cli/commands/sync_vault.py +++ b/operator_cli/commands/sync_vault.py @@ -93,3 +93,8 @@ def sync_vault(chain: str) -> None: vault.apply_vault_changes() vault.verify_vault_keystores() + click.echo( + f"Make sure you have the following validators" + f" running in the {namespace} namespace: {','.join(sorted(vault.vault_validator_names))}." + f" Restart them if they were updated." + ) diff --git a/operator_cli/eth2.py b/operator_cli/eth2.py index 925deb6..140d5d8 100644 --- a/operator_cli/eth2.py +++ b/operator_cli/eth2.py @@ -17,7 +17,7 @@ derive_child_SK, derive_master_SK, ) -from eth2deposit.settings import BaseChainSetting +from eth2deposit.settings import PRATER, BaseChainSetting from eth2deposit.utils.ssz import DepositData as SSZDepositData from eth2deposit.utils.ssz import ( DepositMessage, @@ -33,7 +33,11 @@ from operator_cli.merkle_tree import MerkleTree from operator_cli.queries import REGISTRATIONS_QUERY -from operator_cli.settings import MIGRATE_LEGACY, WITHDRAWAL_CREDENTIALS +from operator_cli.settings import ( + MAINNET_WITHDRAWAL_CREDENTIALS, + MIGRATE_LEGACY, + PRATER_WITHDRAWAL_CREDENTIALS, +) from operator_cli.typings import ( BLSPrivkey, Bytes4, @@ -237,9 +241,17 @@ def generate_merkle_deposit_datum( validator_keypairs: List[KeyPair], ) -> Tuple[HexStr, List[MerkleDepositData]]: """Generates deposit data with merkle proofs for the validators.""" - withdrawal_credentials_bytes: Bytes32 = Bytes32( - w3.toBytes(hexstr=WITHDRAWAL_CREDENTIALS) - ) + if chain_setting.ETH2_NETWORK_NAME == PRATER: + withdrawal_credentials: HexStr = PRATER_WITHDRAWAL_CREDENTIALS + withdrawal_credentials_bytes: Bytes32 = Bytes32( + w3.toBytes(hexstr=PRATER_WITHDRAWAL_CREDENTIALS) + ) + else: + withdrawal_credentials: HexStr = MAINNET_WITHDRAWAL_CREDENTIALS + withdrawal_credentials_bytes: Bytes32 = Bytes32( + w3.toBytes(hexstr=MAINNET_WITHDRAWAL_CREDENTIALS) + ) + deposit_amount_gwei: Gwei = Gwei(int(w3.fromWei(deposit_amount, "gwei"))) merkle_deposit_datum: List[MerkleDepositData] = [] merkle_elements: List[bytes] = [] @@ -270,7 +282,7 @@ def generate_merkle_deposit_datum( public_key=public_key, signature=w3.toHex(signature), amount=str(deposit_amount), - withdrawal_credentials=WITHDRAWAL_CREDENTIALS, + withdrawal_credentials=withdrawal_credentials, deposit_data_root=w3.toHex(deposit_data_root), proof=[], ) diff --git a/operator_cli/settings.py b/operator_cli/settings.py index ff3226d..fd3b8ad 100644 --- a/operator_cli/settings.py +++ b/operator_cli/settings.py @@ -15,10 +15,14 @@ PRATER: PraterSetting, } -WITHDRAWAL_CREDENTIALS = config( - "WITHDRAWAL_CREDENTIALS", +MAINNET_WITHDRAWAL_CREDENTIALS = config( + "MAINNET_WITHDRAWAL_CREDENTIALS", default="0x0100000000000000000000002296e122c1a20fca3cac3371357bdad3be0df079", ) +PRATER_WITHDRAWAL_CREDENTIALS = config( + "PRATER_WITHDRAWAL_CREDENTIALS", + default="0x003e294ffc37978496f1b9298d5984ad4d55d4e2d1e6a06ee6904810c7b9e0d5", +) # extra pins to pinata for redundancy IPFS_PIN_ENDPOINTS = config( diff --git a/operator_cli/vault.py b/operator_cli/vault.py index 6f79046..e4b4568 100644 --- a/operator_cli/vault.py +++ b/operator_cli/vault.py @@ -437,6 +437,13 @@ def sync_vault_keystores(self) -> None: ) def verify_vault_keystores(self) -> None: + # clean up cached property + try: + # noinspection PyPropertyAccess + del self.vault_validator_names + except AttributeError: + pass + public_keys: Set[BLSPubkey] = set() with click.progressbar( self.vault_validator_names,