diff --git a/README.md b/README.md index 2a9b46825..df6fdfec4 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,12 @@ There are two modules in the oracle: ### Accounting module Accounting module updates the protocol TVL, distributes node-operator rewards, updates information about the number of exited and stuck validators and processes user withdrawal requests. -Also Accounting module makes decision to turn on/off the bunker. +Also Accounting module makes decision to turn on/off the bunker. **Flow** The oracle work is delineated by time periods called frames. Oracles finalize a report in each frame. -The default Accounting Oracle frame length on mainnet is 225 epochs, which is 24 hours (it could be changed by DAO). +The default Accounting Oracle frame length on mainnet is 225 epochs, which is 24 hours (it could be changed by DAO). The frame includes these stages: - **Waiting** - oracle starts as daemon and wakes up every 12 seconds (by default) in order to find the last finalized slot (ref slot). @@ -67,10 +67,10 @@ Oracle needs two weeks of archived data. | Client | Tested | Notes | |-------------------------------------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Geth](https://geth.ethereum.org/) | | `--gcmode=archive`
`--syncmode=snap`

OR

`--gcmode=archive`
`--syncmode=full` | +| [Geth](https://geth.ethereum.org/) | ✅ | `--gcmode=archive`
`--syncmode=snap`

OR

`--gcmode=archive`
`--syncmode=full` | | [Nethermind](https://nethermind.io/) | | Not tested yet | -| [Besu](https://besu.hyperledger.org/en/stable/) | | Use
`--rpc-max-logs-range=100000`
`--sync-mode=FULL`
`--data-storage-format="FOREST"`
`--pruning-enabled`
`--pruning-blocks-retained=100000`
params | -| [Erigon](https://github.com/ledgerwatch/erigon) | | Use
`--prune=htc`
`--prune.h.before=100000`
`--prune.t.before=100000`
`--prune.c.before=100000`
params | +| [Besu](https://besu.hyperledger.org/en/stable/) | ✅ | Use
`--rpc-max-logs-range=100000`
`--sync-mode=FULL`
`--data-storage-format="FOREST"`
`--pruning-enabled`
`--pruning-blocks-retained=100000`
params | +| [Erigon](https://github.com/ledgerwatch/erigon) | ✅ | Use
`--prune=htc`
`--prune.h.before=100000`
`--prune.t.before=100000`
`--prune.c.before=100000`
params | ### Consensus Client Node @@ -78,11 +78,11 @@ Also, to calculate some metrics for bunker mode Oracle needs [archive](https://e | Client | Tested | Notes | |---------------------------------------------------|--------|-------------------------------------------------------------------------------------------------------------------------------------------------| -| [Lighthouse](https://lighthouse.sigmaprime.io/) | | Use `--reconstruct-historic-states` param | -| [Lodestar](https://nethermind.io/) | | Not tested yet | -| [Nimbus](https://besu.hyperledger.org/en/stable/) | | Not tested yet | -| [Prysm](https://github.com/ledgerwatch/erigon) | | Use
`--grpc-max-msg-size=104857600`
`--enable-historical-state-representation=true`
`--slots-per-archive-point=1024`
params | -| [Teku](https://docs.teku.consensys.net) | | Use
`--data-storage-mode=archive`
`--data-storage-archive-frequency=1024`
`--reconstruct-historic-states=true`
params | +| [Lighthouse](https://lighthouse.sigmaprime.io/) | ✅ | Use `--reconstruct-historic-states` param | +| [Lodestar](https://lodestar.chainsafe.io) | | Not tested yet | +| [Nimbus](https://nimbus.team) | | Not tested yet | +| [Prysm](https://github.com/prysmaticlabs/prysm) | ✅ | Use
`--grpc-max-msg-size=104857600`
`--enable-historical-state-representation=true`
`--slots-per-archive-point=1024`
params | +| [Teku](https://docs.teku.consensys.net) | ✅ | Use
`--data-storage-mode=archive`
`--data-storage-archive-frequency=1024`
`--reconstruct-historic-states=true`
params | ### Keys API Service @@ -150,13 +150,13 @@ Full variables list could be found [here](https://github.com/lidofinance/lido-or Oracle could be executed once in "manual" mode. To do this setup `DAEMON` variable to 'False'. -**Note**: Use `-it` option to run manual mode in Docker container in interactive mode. +**Note**: Use `-it` option to run manual mode in Docker container in interactive mode. Example `docker run -ti --env-file .env --rm lidofinance/oracle:{tag} {type}` In this mode Oracle will build report as usual (if contracts are reportable) and before submitting transactions Oracle will ask for manual input to send transaction. -In manual mode all sleeps are disabled and `ALLOW_REPORTING_IN_BUNKER_MODE` is True. +In manual mode all sleeps are disabled and `ALLOW_REPORTING_IN_BUNKER_MODE` is True. ## Env variables @@ -166,6 +166,8 @@ In manual mode all sleeps are disabled and `ALLOW_REPORTING_IN_BUNKER_MODE` is T | `CONSENSUS_CLIENT_URI` | URI of the Consensus Layer client | True | `http://localhost:5052` | | `KEYS_API_URI` | URI of the Keys API | True | `http://localhost:8080` | | `LIDO_LOCATOR_ADDRESS` | Address of the Lido contract | True | `0x1...` | +| `CSM_ORACLE_ADDRESS` | Address of the CSFeeOracle contract | ? | `0x1...` | +| `CSM_MODULE_ADDRESS` | Address of the CSModule contract | ? | `0x1...` | | `MEMBER_PRIV_KEY` | Private key of the Oracle member account | False | `0x1...` | | `MEMBER_PRIV_KEY_FILE` | A path to the file contained the private key of the Oracle member account. It takes precedence over `MEMBER_PRIV_KEY` | False | `/app/private_key` | | `FINALIZATION_BATCH_MAX_REQUEST_COUNT` | The size of the batch to be finalized per request (The larger the batch size, the more memory of the contract is used but the fewer requests are needed) | False | `1000` | @@ -173,6 +175,7 @@ In manual mode all sleeps are disabled and `ALLOW_REPORTING_IN_BUNKER_MODE` is T | `DAEMON` | If False Oracle runs one cycle and ask for manual input to send report. | False | `True` | | `TX_GAS_ADDITION` | Used to modify gas parameter that used in transaction. (gas = estimated_gas + TX_GAS_ADDITION) | False | `100000` | | `CYCLE_SLEEP_IN_SECONDS` | The time between cycles of the oracle's activity | False | `12` | +| `MAX_CYCLE_LIFETIME_IN_SECONDS` | The maximum time for a cycle to continue (0 = infinity) | False | `3000` | | `SUBMIT_DATA_DELAY_IN_SLOTS` | The difference in slots between submit data transactions from Oracles. It is used to prevent simultaneous sending of transactions and, as a result, transactions revert. | False | `6` | | `HTTP_REQUEST_TIMEOUT_EXECUTION` | Timeout for HTTP execution layer requests | False | `120` | | `HTTP_REQUEST_TIMEOUT_CONSENSUS` | Timeout for HTTP consensus layer requests | False | `300` | @@ -181,16 +184,17 @@ In manual mode all sleeps are disabled and `ALLOW_REPORTING_IN_BUNKER_MODE` is T | `HTTP_REQUEST_TIMEOUT_KEYS_API` | Timeout for HTTP keys api requests | False | `120` | | `HTTP_REQUEST_RETRY_COUNT_KEYS_API` | Total number of retries to fetch data from endpoint for keys api requests | False | `300` | | `HTTP_REQUEST_SLEEP_BEFORE_RETRY_IN_SECONDS_KEYS_API` | The delay http provider sleeps if API is stuck for keys api | False | `300` | +| `EVENTS_SEARCH_STEP` | Maximum length of a range for eth_getLogs method calls | False | `10000` | | `PRIORITY_FEE_PERCENTILE` | Priority fee percentile from prev block that would be used to send tx | False | `3` | | `MIN_PRIORITY_FEE` | Min priority fee that would be used to send tx | False | `50000000` | | `MAX_PRIORITY_FEE` | Max priority fee that would be used to send tx | False | `100000000000` | ### Mainnet variables -> LIDO_LOCATOR_ADDRESS=0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb +> LIDO_LOCATOR_ADDRESS=0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb > ALLOW_REPORTING_IN_BUNKER_MODE=False ### Goerli variables -> LIDO_LOCATOR_ADDRESS=0x1eDf09b5023DC86737b59dE68a8130De878984f5 +> LIDO_LOCATOR_ADDRESS=0x1eDf09b5023DC86737b59dE68a8130De878984f5 > ALLOW_REPORTING_IN_BUNKER_MODE=True ### Alerts @@ -225,26 +229,26 @@ groups: The oracle exposes the following basic metrics: -| Metric name | Description | Labels | -|-----------------------------|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------| -| build_info | Build info | version, branch, commit | -| env_variables_info | Env variables for the app | ACCOUNT, LIDO_LOCATOR_ADDRESS, FINALIZATION_BATCH_MAX_REQUEST_COUNT, MAX_CYCLE_LIFETIME_IN_SECONDS | -| genesis_time | Fetched genesis time from node | | -| account_balance | Fetched account balance from EL | address | -| slot_number | Last fetched slot number from CL | state (`head` or `finalized`) | -| block_number | Last fetched block number from CL | state (`head` or `finalized`) | -| functions_duration | Histogram metric with duration of each main function in the app | name, status | -| el_requests_duration | Histogram metric with duration of each EL request | endpoint, call_method, call_to, code, domain | -| cl_requests_duration | Histogram metric with duration of each CL request | endpoint, code, domain | -| keys_api_requests_duration | Histogram metric with duration of each KeysAPI request | endpoint, code, domain | -| keys_api_latest_blocknumber | Latest block number from KeysAPI metadata | | -| transaction_count | Total count of transactions. Success or failure | status | -| member_info | Oracle member info | is_report_member, is_submit_member, is_fast_lane | -| member_last_report_ref_slot | Member last report ref slot | | -| frame_current_ref_slot | Current frame ref slot | | -| frame_deadline_slot | Current frame deadline slot | | -| frame_prev_report_ref_slot | Previous report ref slot | | -| contract_on_pause | Contract on pause | | +| Metric name | Description | Labels | +|-----------------------------|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| build_info | Build info | version, branch, commit | +| env_variables_info | Env variables for the app | ACCOUNT, LIDO_LOCATOR_ADDRESS, CSM_ORACLE_ADDRESS, CSM_MODULE_ADDRESS, FINALIZATION_BATCH_MAX_REQUEST_COUNT, MAX_CYCLE_LIFETIME_IN_SECONDS | +| genesis_time | Fetched genesis time from node | | +| account_balance | Fetched account balance from EL | address | +| slot_number | Last fetched slot number from CL | state (`head` or `finalized`) | +| block_number | Last fetched block number from CL | state (`head` or `finalized`) | +| functions_duration | Histogram metric with duration of each main function in the app | name, status | +| el_requests_duration | Histogram metric with duration of each EL request | endpoint, call_method, call_to, code, domain | +| cl_requests_duration | Histogram metric with duration of each CL request | endpoint, code, domain | +| keys_api_requests_duration | Histogram metric with duration of each KeysAPI request | endpoint, code, domain | +| keys_api_latest_blocknumber | Latest block number from KeysAPI metadata | | +| transaction_count | Total count of transactions. Success or failure | status | +| member_info | Oracle member info | is_report_member, is_submit_member, is_fast_lane | +| member_last_report_ref_slot | Member last report ref slot | | +| frame_current_ref_slot | Current frame ref slot | | +| frame_deadline_slot | Current frame deadline slot | | +| frame_prev_report_ref_slot | Previous report ref slot | | +| contract_on_pause | Contract on pause | | Special metrics for accounting oracle: @@ -266,6 +270,12 @@ Special metrics for ejector oracle: | ejector_max_withdrawal_epoch | Max exit epoch between all validators in CL | | | ejector_validators_count_to_eject | Validators count to eject | | +Special metrics for CSM oracle: + +| Metric name | Description | Labels | +|-----------------------------------|---------------------------------------------|--------| +| TBD | TBD | | + # Development Python version: 3.11 @@ -281,13 +291,25 @@ poetry install ## Startup -Required variables +Required variables for accounting and ejector modules + +```bash +export EXECUTION_CLIENT_URI=... +export CONSENSUS_CLIENT_URI=... +export KEYS_API_URI=... +export LIDO_LOCATOR_ADDRESS=... +``` + +Required variables for CSM module ```bash export EXECUTION_CLIENT_URI=... export CONSENSUS_CLIENT_URI=... export KEYS_API_URI=... export LIDO_LOCATOR_ADDRESS=... +export CSM_ORACLE_ADDRESS=... +export CSM_MODULE_ADDRESS=... +export MAX_CYCLE_LIFETIME_IN_SECONDS=0 ``` Run oracle module @@ -300,6 +322,7 @@ Where `{module}` is one of: - `accounting` - `ejector` +- `csm` - `check` ## Tests diff --git a/src/main.py b/src/main.py index 284148095..934096b69 100644 --- a/src/main.py +++ b/src/main.py @@ -41,12 +41,17 @@ def main(module_name: OracleModule): 'module': module_name, 'ACCOUNT': variables.ACCOUNT.address if variables.ACCOUNT else 'Dry', 'LIDO_LOCATOR_ADDRESS': variables.LIDO_LOCATOR_ADDRESS, + 'CSM_ORACLE_ADDRESS': variables.CSM_ORACLE_ADDRESS, + 'CSM_MODULE_ADDRESS': variables.CSM_MODULE_ADDRESS, + 'FINALIZATION_BATCH_MAX_REQUEST_COUNT': variables.FINALIZATION_BATCH_MAX_REQUEST_COUNT, 'MAX_CYCLE_LIFETIME_IN_SECONDS': variables.MAX_CYCLE_LIFETIME_IN_SECONDS, }, }) ENV_VARIABLES_INFO.info({ "ACCOUNT": str(variables.ACCOUNT.address) if variables.ACCOUNT else 'Dry', "LIDO_LOCATOR_ADDRESS": str(variables.LIDO_LOCATOR_ADDRESS), + "CSM_ORACLE_ADDRESS": str(variables.CSM_ORACLE_ADDRESS), + "CSM_MODULE_ADDRESS": str(variables.CSM_MODULE_ADDRESS), "FINALIZATION_BATCH_MAX_REQUEST_COUNT": str(variables.FINALIZATION_BATCH_MAX_REQUEST_COUNT), "MAX_CYCLE_LIFETIME_IN_SECONDS": str(variables.MAX_CYCLE_LIFETIME_IN_SECONDS), }) diff --git a/src/modules/submodules/oracle_module.py b/src/modules/submodules/oracle_module.py index 674ede2c2..546202ef2 100644 --- a/src/modules/submodules/oracle_module.py +++ b/src/modules/submodules/oracle_module.py @@ -54,7 +54,6 @@ def run_as_daemon(self): logger.info({'msg': 'Startup new cycle.'}) self.cycle_handler() - # TODO: We need a configurable timeout for CSM, just use a variable? @timeout(variables.MAX_CYCLE_LIFETIME_IN_SECONDS) def cycle_handler(self): blockstamp = self._receive_last_finalized_slot() diff --git a/src/variables.py b/src/variables.py index 4b5662153..c2185d7ab 100644 --- a/src/variables.py +++ b/src/variables.py @@ -76,7 +76,7 @@ HEALTHCHECK_SERVER_PORT = int(os.getenv('HEALTHCHECK_SERVER_PORT', 9010)) -MAX_CYCLE_LIFETIME_IN_SECONDS = int(os.getenv("MAX_CYCLE_LIFETIME_IN_SECONDS", 3000)) +MAX_CYCLE_LIFETIME_IN_SECONDS = int(os.getenv("MAX_CYCLE_LIFETIME_IN_SECONDS", 3000)) or None def check_all_required_variables():