lido
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: Expand the third phase in Oracle tags: Oracle status: --- # Expand the third phase in Oracle ![chain](https://hackmd.io/_uploads/ByxL78ih7R.png) ## 🎯 TL;DR With the introduction of new modules, the Oracle third phase report might not fit into a single transaction. This document presents an approach for implementing a multi-transactional third phase. * Depending on the size of the third-phase report, it may be split into multiple transactions. * The third-phase transaction consists of items, where each item represents a portion of the report data. * The processing of each item must not exceed the gas limit defined for a single item. The item count per transaction limit specifies the maximum number of items in a single transaction. Therefore, the processing of items in each third-phase transaction will not exceed the gas limit, as determined by the product of the item gas cost limit and the item count limit. * Each module within a third phase transaction will be processed independently. If the processing of any module fails, the finalization process for other unaffected modules will not be blocked. * The integrity and sequence of the report data will be maintained across multiple transactions. ## 𓀀 Motivation The third phase in the Accounting Oracle plays a crucial role in providing updated counts of stuck and exited validators for each node operator within the Lido protocol. This data required for distributing rewards and allocating new stake among node operators. Currently, the third phase is restricted to 200 Node Operators updates due to node operators limit in NOR contract and Ethereum block size limitations, which is sufficient for the Curated Module and Simple DVT modules. However, with the introduction of the Community Staking Module, which don't impose any limits on node operator counts, updates for all node operators cannot be accommodated within a single transaction due to block gas limitation. The impending launch of new staking modules within Lido necessitates a scalable solution for accommodating an increasing number of node operators. ### 🛑 Technical Constraints The Accounting Oracle report can require an update for all Node Operators in Lido. A very large transaction could potentially take longer to process or even stall, as validators might delay including it due to its size and the commitment of a large portion of a block's gas limit. The third phase of the Accounting report has two main parts: - Provide Node Operator states to Staking Modules. - Finalization hook. The finalization hook is out of scope for this document. Detailed improvements for it are described in the [Reward distribution](https://hackmd.io/@lido/HJYbVq5b0) document. As for Node Operator states updates, the [estimates for the CSM module](https://hackmd.io/SiwTUvtBSNKfoLyA_8LvtA?view) indicate that the report for 1000 Node Operators consumes approximately 37 million of gas. However, this estimate appears outdated. After reviewing the current code, 7.8 million gas for 1000 Node Operator updates seems like a more reasonable estimation. ::: spoiler Estimation Per Operator Cost: - 500 (decoding) - 800 (storage read) - 5000 (storage write) - 500 (condition checks) - 1000 (event emission) Total per operator cost: 7800 gas. For 1000 Operators: ``` 24,700 (base cost) + 1000 * 7800 (per operator cost) 24,700 + 7,800,000 = 7,824,700 ``` ::: <br/> In the case of a growing CSM module and adding new SimpleDVT modules, we might be at risk that accounting oracle report will not fit into the single transaction. ## 📋 Proposed Solution To address these challenges, a multi-transactional approach is proposed for the third phase of the Accounting Oracle report. The second-phase report is assumed to contain the hash of the first third-phase transaction, while each third-phase transaction will include the hash of the subsequent third-phase transaction. The last transaction will contain`ZERO_HASH`. ![image](https://hackmd.io/_uploads/H1WNjOZxR.png) <br /> Proposed design ensures that each transaction of the third phase has been confirmed through the hash consensus of oracles during the first phase. It is not possible to make changes to any transaction without altering the report hash, thereby maintaining the integrity of the report. The process for verifying each transaction is described in the [Implementation Details](#Implementation-Details) section. <br /> ![image](https://hackmd.io/_uploads/B1GuC1K70.png) <br /> The current third phase single transaction data format is as follows: ``` | 3 bytes | 2 bytes | X bytes | ... | itemIndex | itemType | itemPayload | ... ``` The new version will include the `nextHash`: ``` | 32 bytes | array of items | nextHash | ... ``` Example ``` | 32 bytes | 3 bytes | 2 bytes | X bytes | ... | nextHash | itemIndex | itemType | itemPayload | ... ``` ![image](https://hackmd.io/_uploads/r1KcIXV1C.png) #### Finalization hook Once the delivery of updates is complete, the Accounting Oracle triggers the Finalization hook (call Staking Router`onValidatorsCountsByNodeOperatorReportingFinished`). This signalizes that all Node Operator updates have been successfully delivered and the Accounting Oracle report is finalized. Currently, in the Curated Module and Simple DVT modules, the finalization hook initiates reward distribution. #### Empty third phase No changes are required in the case when the third phase report is empty. The second-phase report will contain `ZERO_HASH` The AccountingOracle's `submitReportExtraDataEmpty` is called, which validates the report state and calls the finalization hook (Staking Router `onValidatorsCountsByNodeOperatorReportingFinished`). ## Implementation Details ### Processing third phase transactions In the second phase, accounting oracle contract save in storage the hash of the first transaction's from the series of third-phase transactions. During the third phase, we then verify that the hash of the report matches the expected hash previously stored in the state. Upon confirming a match, we proceed to process the items and update the storage with the next expected hash, as indicated by the report's `nextHash` field. After processing the transaction portion of the report data (items), the `ExtraDataSubmitted` event will be emitted. ```solidity event ExtraDataSubmitted(uint256 indexed refSlot, uint256 itemsProcessed, uint256 itemsCount); ``` #### Sequence of the report data ![image](https://hackmd.io/_uploads/ByV1Yl97C.png) Currently the third phase report data is split into items based on composite keys. These keys include the itemType, moduleId, and the first node operator ID in each item. The proposed design for multi-transaction processing assumes that dividing the report into items will maintain the integrity and sequence of the report data. Report data must not be sent in an unsorted order, and each transaction must contain sorted items. The sorting order is maintained across all third phase transactions. For more details, check the “Sanity checks” section below. #### Count of items in third phase transaction ![image](https://hackmd.io/_uploads/rJ4vaecQA.png) Each transaction consists of items, with each item representing a portion of the report data. A new parameter, `maxItemsPerExtraDataTransaction`, is suggested to define the maximum count of items in a single transaction. ``` // Maximum count of items in a third phase transaction maxItemsPerExtraDataTransaction ``` The exact item count will be based on the following fundamental assumptions: - Transaction processing should not exceed the maximum gas cost allocated for a third phase transaction. - The processing of each item should not exceed the maximum gas cost allocated for item. The gas-based calculations provided below are intended to illustrate our method for determining the specific value for the maximum item count. The Oracle will not store any gas limits; they are used solely for the purpose of calculating the maximum item count in transactions. ``` // More accurate values will be determined // after a precise calculation of gas consumption for each module update. // Maximum gas cost for processing a third phase transaction maxExtraDataTransactionGasCost = 8_000_000; // Maximum gas cost for processing a single item maxExtraDataTransactionItemGasCost = 1_000_000; // Maximum item count in a third phase transaction maxItemsPerExtraDataTransaction = maxExtraDataTransactionGasCost / maxExtraDataTransactionItemGasCost; ``` #### Third phase items payload ![image](https://hackmd.io/_uploads/Bkt5vP4JA.png) The proposed design does not restrict the format of the data that can be sent in an item's payload. However, as mentioned in the previous section, the processing of each item must not exceed the maximum gas cost allocated for an item. Each item type may implement its own sanity checks to ensure data integrity and adherence to gas limitations. Currently, for all modules (Curated, SimpleDVT, and CSM), the oracle's third phase supports only two types of items: - Updating the count of exited validators for node operators. - Updating the count of stuck validators for node operators. ##### Update validators count for Node operators Due to the similarity between updating exited and stuck validators count, a single `maxNodeOperatorsPerExtraDataItem` can be used to ensure that the data processing for an item does not exceed the gas limit for an item. ```solidity // Maximum node operator updates per item maxNodeOperatorsPerExtraDataItem ``` The gas-based calculations provided below are intended to illustrate our method for determining the specific value for the maximum node operator updates per item. The Oracle will not store any gas limits; they are used solely for the purpose of calculating the maximum node operator updates per item. ```solidity // Maximum node operator updates per item maxNodeOperatorsPerExtraDataItem = maxExtraDataTransactionItemGasCost / maxExtraDataTransactionNodeOperatorUpdateGasCost ``` Where `maxExtraDataTransactionNodeOperatorUpdateGasCost` is defined as the maximum gas cost for updating the count of stuck or exited validators for a single node operator across all modules. Let's assume that M1, M2, and M3 represent the gas consumption for updating a single node operator (either stuck or exited validators count) in the respective modules with IDs 1, 2, and 3. The maximum gas consumption across these modules for node operator count updates (stuck and exited) is then calculated as follows: ``` maxExtraDataTransactionNodeOperatorUpdateGasCost = max( M1_nodeOperatorUpdateStuckValidatorsCountGasCost, M1_nodeOperatorUpdateExitedValidatorsCountGasCost, M2_nodeOperatorUpdateStuckValidatorsCountGasCost, M2_nodeOperatorUpdateExitedValidatorsCountGasCost, M3_nodeOperatorUpdateStuckValidatorsCountGasCost, M3_nodeOperatorUpdateExitedValidatorsCountGasCost ) ``` The concrete value of maximum operator update gas cost will be determined after a precise calculation of the gas consumption. #### Distribute items across transactions ![image](https://hackmd.io/_uploads/BJezF_NJ0.png) The proposed multi-transaction design specifies the maximum item count for each third-phase transaction without dictating the method by which the oracle will pack items within these transactions. Suppose the maximum item count per transaction is 3, and the oracle's third-phase report contains 4 items that need to be packed into 2 transactions. ``` // maximum items count per transaction maxItemsPerExtraDataTransaction = 3 // third phase report contais 4 items totalItemsCount = 4 ``` Total: 4 items, will be split on 2 transactions The most advisable strategies are: 1. Including as many items as possible in each transaction. ![image](https://hackmd.io/_uploads/SkdYtYVk0.png) 2. Distributing items across transactions. ![image](https://hackmd.io/_uploads/B1wjFYNJC.png) 3. Distributing items evenly according to actual items payload ![image](https://hackmd.io/_uploads/SkeLTVFJR.png) The first strategy is the easiest to implement, but the large transactions may take longer to be included in a block. We might consider the even distribution approach because, in this scenario, the processing time for each transaction should be equal. However, implementation could be challenging because the algorithm must take into account both the number of items and the actual payload of these items. ### Sanity checks #### Exited validators limit Currently, the `churnValidatorsPerDayLimit` parameter from the `OracleReportSanityChecker` is used to validate both newly exited validators `checkExitedValidatorsRatePerDay` and newly deposited validators `_checkAppearedValidatorsChurnLimit`. This threshold is set at 20,000 validators, a suitable number for scenarios that involve adding a large volume of new validators. However, this threshold proves excessively high for the validator exit process, where the Ethereum churn limit should be taken into account. We propose dividing this into two distinct parameters, each with more precise values tailored to the respective cases. On the second phase the `checkExitedValidatorsRatePerDay` sanity check will use new `exitedValidatorsPerDayChurnLimit`. And the `_checkAppearedValidatorsChurnLimit` check will use the value from `appearedValidatorsPerDayLimit`. After the third phase for each module we check that total count of exited validators is equal to the exited validators count which we got on the second phase. (StakingRouter line 567) ```js // https://docs.lido.fi/guides/verify-lido-v2-upgrade-manual#depositsecuritymodule // The maximum number of validators that Lido can deposit per day. /** * const BLOCKS_PER_DAY = (24 * 60 * 60) / 12 = 7200 * const DSM_MAX_DEPOSITS_PER_BLOCK = 150 * const DSM_MIN_DEPOSIT_BLOCK_DISTANCE = 25 * * const appearedValidatorsPerDayLimit = BLOCKS_PER_DAY / DSM_MIN_DEPOSIT_BLOCK_DISTANCE * DSM_MAX_DEPOSITS_PER_BLOCK // 43200 */ appearedValidatorsPerDayLimit: 43200 /** * const currentValidatorsNumber = 1385929 * const ACTIVATION_CHURN_LIMIT = 8 * const VALIDATORS_PER_DAY = (BLOCKS_PER_DAY / 32) * ACTIVATION_CHURN_LIMIT * const VALIDATORS_AFTER_TWO_YEARS = VALIDATORS_PER_DAY * 365 * 2 + currentValidatorsNumber // 1385929 + (1800 * 365 * 2) * * const INITIAL_CHURN_LIMIT = 7 * const CHURN_LIMIT_QUOTIENT = 65_536 * const VALIDATORS_WITH_INITIAL_CHURN_LIMIT = 500_000 * * const exitedValidatorsPerDayChurnLimit = * Math.floor((VALIDATORS_AFTER_TWO_YEARS - VALIDATORS_WITH_INITIAL_CHURN_LIMIT) / CHURN_LIMIT_QUOTIENT) + * INITIAL_CHURN_LIMIT // 40 * * const maxExitedValidatorsPerDay = exitedValidatorsPerDayChurnLimit * (BLOCKS_PER_DAY / 32) // 9000 */ exitedValidatorsPerDayChurnLimit: 9000 ``` ##### appearedValidatorsPerDayLimit Above is an example calculation and recommended configuration. In our calculations we rely on the DSM limits. This number can be specified much lower, but its calculation will be dynamic, where the limits can vary greatly due to the number of rewards, withdrawals. We therefore recommend the use of static limits based on data from the DSM. Will be used as a value in the check: `_checkAppearedValidatorsChurnLimit` ##### exitedValidatorsPerDayChurnLimit In the calculation of this parameter, we calculate the growth of CHURN_LIMIT two years ahead. This parameter will be valid until the implementation of MEB. Will be used as a value in the check: `checkExitedValidatorsRatePerDay` #### Integrity and sequence of the report data Currently, during the third phase of report processing, the Accounting Oracle validates that items are sorted according to composite keys. These keys include the itemType, moduleId, and the first node operator ID in each item. // | 2 bytes | 19 bytes | 3 bytes | 8 bytes | // | itemType | 00000000 | moduleId | firstNodeOpId | uint256 sortingKey The Accounting Oracle stores the `lastSortingKey` in `ExtraDataIterState` and updates it during processing the third phase transaction. The proposed design for multi-transaction processing assumes that dividing the report into items will maintain the integrity and sequence of the report data. The `lastSortingKey` will be reused to validate the order of items within multiple transactions. #### Split report data to items. Currently the `OracleReportSanityChecker` contains parameters to establish the maximum number of node operators that can be updated during the third phase of the report. Here is the current configuration for the on-chain sanity check, designed to accommodate two modules: Curated and SimpleDVT. It is configured to handle the delivery of up to four data lists. Each list represents either stuck or exited validators for node operators within a specific module, with no single list exceeding 50 node operators. In total, this allows for up to 200 updates concerning node operators. ```js "maxAccountingExtraDataListItemsCount": 4, "maxNodeOperatorsPerExtraDataItemCount": 50 ``` ```solidity interface IOracleReportSanityChecker { function checkAccountingExtraDataListItemCount(uint256 _extraDataListItemsCount) external view; function checkNodeOperatorsPerExtraDataItemCount(uint256 _itemIndex, uint256 _nodeOperatorsCount) external view; } ``` These parameters and related sanity checks will be changed as the multi-transaction approach no longer imposes limits on the entire report size. Rather than validating the entire report size, we will independently validate each transaction during the third phase. ```js "maxItemsPerExtraDataTransaction": 8 "maxNodeOperatorsPerExtraDataItem": 24 ``` ```solidity interface IOracleReportSanityChecker { function checkExtraDataItemsCountPerTransaction(uint256 _extraDataListItemsCount) external view; function checkNodeOperatorsPerExtraDataItemCount(uint256 _itemIndex, uint256 _nodeOperatorsCount) external view; } ``` ##### Determining Limit Values The gas consumption for updating a single node operator: - CSM: - ~16_650 Average - ~41_150 Max (unstuck keys in specific condition) - Curated-based: ~15_500 The processing of each item should not exceed the specified gas limit of 1_000_000 gas. Each transaction can contain up to 8 items, each consuming 1_000_000 gas (totaling 8_000_000 gas). Using the higher value: CSM: 41_150 gas This results in: 24 operators per item (1_000_000 / 41_150 = 24.3) - maxItemsPerExtraDataTransaction = 8 - maxNodeOperatorsPerExtraDataItem = 24 By setting these values, we ensure that each transaction stays within the 8_000_000 gas limit.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully