# Eth2.0 Validator API Standard ## Why? - The main goal of this effort is to increase client diversity. UIs are very important to some users so this standard should ensure all clients have at least one. - The secondary goal is to offload UI work from core dev teams to other parties that can prioritize this work over Altair, the merge, etc. :::warning **WIP**: This standard must be cheap to implement to be successful. Core devs are encouraged to voice concerns around complexity to ensure a timely implementation. ::: ---- [TOC] Minimal validator API to enable a basic validator manager workflow. **Supports**: - Import, delete keystores - Import, export slashing db Does **not support**: - Metrics visualization or plotting any type of chain data. Instead, should be done with metrics tooling such as Grafana or block explorers - VoluntaryExit. Instead, should be done with the deposit cli tooling. - Backup accounts. When migrating validator clients stakers should re-generate the keystores from the mnemonic or retrieve them from a self-managed backup. - Displaying logs. All routes SHOULD be disabled by default and only be enabled with a CLI flag such as `--enable-validator-api`. All sensitive routes MUST be authenticated. There SHOULD exist a single password per validator client binary such that multiple users are not supported. The validator API MUST allow to add and delete accounts but SHOULD not allow to retrieve them. Users are encouraged to use alternative locations to backup their keystores and use them to perform validator client migration. # Routes All account and slashing protection routes MUST be authenticated with a token set by the Login route. The server MUST not rely on the UI's Javascript to add extra headers for authentication, and SHOULD use an HttpOnly cookie token for authentication. - Authenticated routes MUST return `401 Unauthorized` if no token is found. - Authenticated routes MUST return `403 Forbidden` if a token is found but is invalid or incorrect. :::warning **WIP**: Routes prefix and versioning TBD ::: ## `GET /accounts` - ListAccounts ```json Request { // The maximum number of accounts to return in the response. // This field is optional. page_size: int32 // A pagination token returned from a previous call to `ListAccounts` // that indicates where this listing should continue from. // This field is optional. page_token: string } Response { accounts: { // The validating public key. validating_pubkey: bytes // The derivation path (if present in the imported keystore). derivation_path: string // Latest slashing protection DB data item associated with this pubkey // Signal no slashing protection DB data with a value of TBD (0, null, "") slashing_protection_last_entry: string }[] // A pagination token returned from a previous call // that indicates from where listing should continue. // This field is optional next_page_token: string // Total count matching the request. total_size: int32 } ``` ## `POST /accounts/import` - ImportAccounts Import keystores generated by the Eth2.0 deposit CLI tooling. All keystores MUST be encrypted with the same password. ```json Request { // JSON-encoded keystore files to import during wallet creation. keystores_imported: string[] // Password to unlock imported keystore files. keystores_password: string } Response { imported_pubkeys: bytes[] } ``` ## `POST /accounts/delete` DeleteAccounts Delete keystores for the pubkeys provided in the request. The validator client MUST stop validating with these keystores immediately and MUST return a 200 status only after confirming no more signatures can be created with those keys. ```json Request { // List of public keys to delete. pubkeys: bytes[]; } Response { // List of public keys successfully deleted. deleted_pubkeys: bytes[]; } ``` ## `GET /slashing_protection` - ExportSlashingProtection MUST return a file with the format defined in [EIP-3076: Slashing Protection Interchange Format](https://eips.ethereum.org/EIPS/eip-3076). If `pubkeys` is not specified MUST return data for all pubkeys in the local slashing protection DB. ```json Request { // List of public keys to export slashing protection DB data. // This field is optional pubkeys: bytes[]; } Response { // JSON representation of the slash protection file: string } ``` ## `POST /slashing_protection` - ImportSlashingProtection MUST accept a file with the format defined in [EIP-3076: Slashing Protection Interchange Format](https://eips.ethereum.org/EIPS/eip-3076). Users are encouraged to use the `slashing_protection_last_entry` field to confirm that they successfully added slashing protection data for their accounts. ```json Request { // JSON representation of the slash protection slashing_protection_json: string } // Response empty ``` ## `GET /login` - LoginStatus Convenience method to check if user has signed up, and their token is valid. ```json // Request empty Response { has_signed_up: bool } ``` ## `POST /login` - Login ```json Request { password: string password_confirmation: string } // Response empty ``` MUST validate the provided passwords against a local "Record". After successful validation MUST return a `Set-Cookie` header ```json Set-Cookie: SESSION=<token>; HttpOnly; SameSite=Strict ``` Cookie atributes: - `HttpOnly`: MUST be included - `SameSite`: MUST be set to `Strict` - `Max-Age`: MAY be included - `Secure`: SHOULD not be included. Validator client servers may not have TLS/SSL certificates - `Domain`: MAY be included ## `POST /signup` - Signup ```json Request { password: string password_confirmation: string } // Response empty ``` Register a new password to be used to encrypt the local wallet. The password MUST: - Be stored locally hashed with salt to authenticate future logins. - Be used to encrypt and decrypt the local representation of the imported keystores The validator binary MUST expose a CLI argument `--persist-password` to enable persisting the password such that it can be restarted and perform validator duties without requiring interaction by the user. ## `POST /logout` Logout MUST invalidate the token set by the Login route if found in the request headers. ```json // Request empty // Response empty ``` ## `POST /change_password` - ChangePassword The validator client MUST re-encrypt the local representation of the keystores if applicable. ```json Request { current_password: string; password: string; password_confirmation: string; } // Response empty ```