diff --git a/docs/admin-abilities.md b/docs/admin-abilities.md index e69de29b..82558991 100644 --- a/docs/admin-abilities.md +++ b/docs/admin-abilities.md @@ -0,0 +1,8 @@ +--- +layout: default +title: Admin Abilities +--- + +# Admin Abilities + +[List and explanation of admin abilities goes here] diff --git a/docs/assets/state-machine-diagram.png b/docs/assets/state-machine-diagram.png new file mode 100644 index 00000000..b8494d45 Binary files /dev/null and b/docs/assets/state-machine-diagram.png differ diff --git a/docs/cli.md b/docs/cli.md index e69de29b..14cb207d 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -0,0 +1,355 @@ +--- +layout: default +title: Parameters +--- + +# CLI + +# Accounts + +| Account | Address | +| -------------- | -------------------------------------------- | +| Program | Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 | +| Steward Config | jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv | +| Steward State | 9BAmGVLGxzqct6bkgjWmKSv3BFB6iKYXNBQp8GWG1LDY | +| Authority | 9eZbWiHsPRsxLSiHxzg2pkXsAuQMwAjQrda7C7e21Fw6 | + +# CLI Commands + +Build CLI binary: + +```bash +cargo build -p steward-cli --release +``` + +## Permissionless Commands + +### View Config + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 view-config --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv +``` + +### View State + +Displays high level Steward internal operations including current state, total number of validators in the pool, next cycle epoch, etc. + +```bash +./target/release/steward-cli --json-rpc-url $(solana config get | grep "RPC URL" | awk '{print $3}') view-state --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv +``` + +### View State of Single Validator + +Displays state of a single Validator. + +```bash +./target/release/steward-cli --json-rpc-url $(solana config get | grep "RPC URL" | awk '{print $3}') view-state --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --vote-account J1to1yufRnoWn81KYg1XkTWzmKjnYSnmE2VY8DGUJ9Qv +``` + +Output: + +``` +Vote Account: J1to1yufRnoWn81KYg1XkTWzmKjnYSnmE2VY8DGUJ9Qv +Stake Account: 6PAY8LEswawgCGnzB3tKGJBtELUwDpeMfDCiNpCyNt8q +Transient Stake Account: C2AurJCKxp5Q8DbaZ84aiSUiKKazqgRVsUiTiihqNYui +Steward List Index: 3 +Overall Rank: 441 +Score: 0 +Yield Score: 912832510 +Passing Eligibility Criteria: No +Target Delegation Percent: 0.0% + +Is Instant Unstake: false +Is blacklisted: false + +Validator History Index: 321 + +Active Lamports: 3398839 (0.00 ◎) +Transient Lamports: 0 (0.00 ◎) +Steward Internal Lamports: 114590 +Status: 🟩 Active +Marked for removal: false +Marked for immediate removal: false +``` + +`Vote Account`: Validator's vote account address + +`Stake Account`: Validator's stake account from this stake pool + +`Transient Stake Account`: Validator's transient stake account used for activating/deactivating stake + +`Steward List Index`: Position in the Steward list, 1-1 with spl-stake-pool `ValidatorList` + +`Overall Rank`: Validator's rank among all validators, indicating priority for stake if Target is nonzero, and priority for unstaking if target is zero + +`Passing Eligibility Criteria`: Indicates if validator meets binary eligibility requirements + +`Score`: Validator's overall score + +`Yield Score`: Validator's relative yield score + +`Target Delegation Percent`: Share of the stake pool TVL this validator is targeted to receive. Not a guaranteed amount - dependent on staking and unstaking priority. + +`Is Instant Unstake`: Indicates if this validator should be immediately unstaked + +`Is blacklisted`: Indicates if validator is blacklisted from the pool + +`Validator History Index`: Position in the validator history + +`Active Lamports`: Amount of actively staked lamports + +`Transient Lamports`: Amount of lamports in transient state + +`Steward Internal Lamports`: Steward's internal tracking of stake used to detect user deposits + +`Status`: Validator's `StakeStatus` in the spl-stake-pool `ValidatorList` account + +`Marked for removal`: Indicates if validator is flagged for removal next epoch + +`Marked for immediate removal`: Indicates if validator is flagged for immediate removal + +### View State of All Validators + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 --json-rpc-url $(solana config get | grep "RPC URL" | awk '{print $3}') view-state --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --verbose +``` + +### View Next Index To Remove + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 view-next-index-to-remove --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv +``` + +### Auto Remove Validator + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 auto-remove-validator-from-pool --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json --validator-index-to-remove 1397 +``` + +### Auto Add Validator + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 auto-add-validator-from-pool --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json --vote-account 4m64H5TbwAGtZVnxaGAVoTSwjZGV8BCLKRPr8agKQv4Z +``` + +### Manually Update All Vote Accounts + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 manually-copy-all-vote-accounts --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json --priority-fee 300000 +``` + +## Manually Update Vote Account + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 manually-copy-vote-account --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json --validator-index-to-update 1 +``` + +### Manually Remove Validator + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 manually-remove-validator --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --authority-keypair-path ../../credentials/stakenet_test.json --validator-index-to-remove 0 +``` + +## Remove Bad Validators + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 remove-bad-validators --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json +``` + +## Permissionless Cranks + +## Crank Epoch Maintenance + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 crank-epoch-maintenance --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json +``` + +## Crank Compute Score + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 crank-compute-score --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json +``` + +## Crank Compute Delegations + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 crank-compute-delegations --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json +``` + +## Crank Idle + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 crank-idle --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json +``` + +## Crank Compute Instant Unstake + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 crank-compute-instant-unstake --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json +``` + +## Crank Rebalance + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 crank-rebalance --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json +``` + +## Crank Steward + +```bash +./target/release/steward-cli --json-rpc-url $(solana config get | grep "RPC URL" | awk '{print $3}') crank-steward --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --payer-keypair-path ../../credentials/stakenet_test.json --priority-fee 200000 +``` + +## Privileged Commands + +### Create Steward + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 init-steward \ + --authority-keypair-path ../../credentials/stakenet_test.json \ + --steward-config-keypair-path ../../credentials/steward_config.json \ + --stake-pool 3DuPtyTAKrxKfHkSPZ5fqCayMcGru1BarAKKTfGDeo2j \ + --mev-commission-range 10 \ + --epoch-credits-range 30 \ + --commission-range 30 \ + --mev-commission-bps-threshold 1000 \ + --commission-threshold 5 \ + --historical-commission-threshold 50 \ + --scoring-delinquency-threshold-ratio 0.85 \ + --instant-unstake-delinquency-threshold-ratio 0.70 \ + --num-delegation-validators 200 \ + --scoring-unstake-cap-bps 750 \ + --instant-unstake-cap-bps 1000 \ + --stake-deposit-unstake-cap-bps 1000 \ + --compute-score-slot-range 50000 \ + --instant-unstake-epoch-progress 0.50 \ + --instant-unstake-inputs-epoch-progress 0.50 \ + --num-epochs-between-scoring 3 \ + --minimum-stake-lamports 100000000000 \ + --minimum-voting-epochs 5 +``` + +### Realloc State + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 realloc-state --authority-keypair-path ../../credentials/stakenet_test.json --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv +``` + +### Update Config + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 update-config \ + --authority-keypair-path ../../credentials/stakenet_test.json \ + --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv \ + --num-epochs-between-scoring 3 +``` + +### Update Authority + +`blacklist` | `admin` | `parameters` + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 update-authority blacklist \ + --authority-keypair-path ../../credentials/stakenet_test.json \ + --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv \ + --new-authority aaaDerwdMyzNkoX1aSoTi3UtFe2W45vh5wCgQNhsjF8 +``` + +### Set Staker + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 set-staker \ + --authority-keypair-path ../../credentials/stakenet_test.json \ + --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv +``` + +### Revert Staker + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 revert-staker \ + --authority-keypair-path ../../credentials/stakenet_test.json \ + --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv +``` + +### Pause + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 pause \ + --authority-keypair-path ../../credentials/stakenet_test.json \ + --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --print-tx +``` + +### Resume + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 resume \ + --authority-keypair-path ../../credentials/stakenet_test.json \ + --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --print-tx +``` + +### Reset State + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 reset-state --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --authority-keypair-path ../../credentials/stakenet_test.json +``` + +### Add To Blacklist + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 add-to-blacklist --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --authority-keypair-path ../../credentials/stakenet_test.json --validator-history-index-to-blacklist 2168 +``` + +### Remove From Blacklist + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 remove-from-blacklist --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --authority-keypair-path ../../credentials/stakenet_test.json --validator-history-index-to-deblacklist 2168 +``` + +## Close Steward + +```bash +./target/release/steward-cli --program-id Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 close-steward --steward-config jitoVjT9jRUyeXHzvCwzPgHj7yWNRhLcUoXtes4wtjv --authority-keypair-path ../../credentials/stakenet_test.json +``` + +# Deploy and Upgrade + +- upgrade solana cli to 1.18.16 +- create a new keypair: `solana-keygen new -o credentials/temp-buffer.json` +- use anchor `0.30.0`: `avm install 0.30.0 && avm use 0.30.0` +- make sure your configured keypair is program authority +- build .so file: `anchor build --no-idl` +- Write to buffer: `solana program write-buffer --use-rpc --buffer credentials/temp-buffer.json --with-compute-unit-price 10000 --max-sign-attempts 10000 target/deploy/jito_steward.so --keypair credentials/stakenet_test.json` +- Upgrade: `solana program upgrade $(solana address --keypair credentials/temp-buffer.json) Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 --keypair credentials/stakenet_test.json` +- Close Buffers: `solana program close --buffers --keypair credentials/stakenet_test.json` +- Upgrade Program Size: `solana program extend Stewardf95sJbmtcZsyagb2dg4Mo8eVQho8gpECvLx8 1000000 --keypair credentials/stakenet_test.json` + +# Initial Parameters + +```bash +# Note - Do not use this .env when updating the parameters - this will update them all +MEV_COMMISSION_RANGE=10 +EPOCH_CREDITS_RANGE=30 +COMMISSION_RANGE=30 +MEV_COMMISSION_BPS_THRESHOLD=1000 +COMMISSION_THRESHOLD=5 +HISTORICAL_COMMISSION_THRESHOLD=50 +SCORING_DELINQUENCY_THRESHOLD_RATIO=0.85 +INSTANT_UNSTAKE_DELINQUENCY_THRESHOLD_RATIO=0.70 +NUM_DELEGATION_VALIDATORS=200 +SCORING_UNSTAKE_CAP_BPS=750 +INSTANT_UNSTAKE_CAP_BPS=1000 +STAKE_DEPOSIT_UNSTAKE_CAP_BPS=1000 +COMPUTE_SCORE_SLOT_RANGE=1000 +INSTANT_UNSTAKE_EPOCH_PROGRESS=0.50 +INSTANT_UNSTAKE_INPUTS_EPOCH_PROGRESS=0.50 +NUM_EPOCHS_BETWEEN_SCORING=3 +MINIMUM_STAKE_LAMPORTS=100000000000 +MINIMUM_VOTING_EPOCHS=5 +``` + +# Getting Ready to Merge + +```bash +cargo +nightly-2024-02-04 clippy --all-features --all-targets --tests -- -D warnings +anchor build --idl idl +``` diff --git a/docs/index.md b/docs/index.md index c61ccdac..b4809cc5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,13 +3,13 @@ layout: default title: Steward Program Documentation --- -# Steward Program +_Note: documentation for the Validator History program is a work in progress. Please see the top level [README](https://github.com/jito-foundation/stakenet/blob/master/README.md) for more information._ -The Steward Program is an Anchor program designed to manage the staking authority for a SPL Stake Pool. Using on-chain validator history, the steward selects a set of high-performing validators to delegate to, maintains the desired level of stake on those validators over time, and continuously monitors and re-evaluates the validator set at a set cadence. +# Steward Program -## Purpose +The Steward Program is an Anchor program designed to manage the staking authority for a SPL Stake Pool. Using on-chain [validator history](https://github.com/jito-foundation/stakenet) the steward selects a set of high-performing validators to delegate to, maintains the desired level of stake on those validators over time, and continuously monitors and re-evaluates the validator set at a set cadence. Initially, the validator selection is customized for the JitoSOL stake pool criteria and will be deployed to manage that stake pool. Additionally, the steward surfaces this staking algorithm through variable parameters to be decided by [Jito DAO](https://gov.jito.network/dao/Jito). In turn, this greatly decentralizes the stake pool operations. -The Steward Program was created to automatically manage the Jito Stake Pool. Using on-chain validator history data, the steward chooses who to stake to and how much by way of its staking algorithm. Additionally, the steward surfaces this staking algorithm through variable parameters to be decided by Jito DAO. In turn, this greatly decentralizes the stake pool operations. +The core operations of the Steward Program are permissionless such that any cranker can operate the system. However there are some [admin/management functions](#admin-abilities) that allow for tweaking parameters and system maintenance. ## Table of Contents diff --git a/docs/parameters.md b/docs/parameters.md index e69de29b..40660974 100644 --- a/docs/parameters.md +++ b/docs/parameters.md @@ -0,0 +1,34 @@ +--- +layout: default +title: Parameters +--- + +# Parameters + +Live parameters can be seen on-chain at https://jito.network/stakenet/steward/config or via the CLI. + +| Parameter | Value | Description | +| --------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Scoring Parameters** | | | +| `mev_commission_range` | 10 | Number of recent epochs used to evaluate MEV commissions and running Jito for scoring | +| `epoch_credits_range` | 30 | Number of recent epochs used to evaluate yield | +| `commission_range` | 30 | Number of recent epochs used to evaluate commissions for scoring | +| `mev_commission_bps_threshold` | 1000 | Maximum allowable MEV commission in mev_commission_range (stored in basis points) | +| `commission_threshold` | 5 | Maximum allowable validator commission in commission_range (stored in percent) | +| `historical_commission_threshold` | 50 | Maximum allowable validator commission in all history (stored in percent) | +| `scoring_delinquency_threshold_ratio` | 0.85 | Minimum ratio of slots voted on for each epoch for a validator to be eligible for stake. Used as proxy for validator reliability/restart timeliness. Ratio is number of epoch_credits / blocks_produced | +| | | | +| **Delegation Parameters** | | | +| `instant_unstake_delinquency_threshold_ratio` | 0.70 | Same as scoring_delinquency_threshold_ratio but evaluated every epoch | +| `num_delegation_validators` | 200 | Number of validators who are eligible for stake (validator set size) | +| `scoring_unstake_cap_bps` | 750 | Percent of total pool lamports that can be unstaked due to new delegation set (in basis points) | +| `instant_unstake_cap_bps` | 1000 | Percent of total pool lamports that can be unstaked due to instant unstaking (in basis points) | +| `stake_deposit_unstake_cap_bps` | 1000 | Percent of total pool lamports that can be unstaked due to stake deposits above target lamports (in basis points) | +| | | | +| **State Machine Operation Parameters** | | | +| `compute_score_slot_range` | 1000 | Scoring window such that the validators are all scored within a similar timeframe (in slots) | +| `instant_unstake_epoch_progress` | 0.90 | Point in epoch progress before instant unstake can be computed | +| `instant_unstake_inputs_epoch_progress` | 0.50 | Inputs to “Compute Instant Unstake” need to be updated past this point in epoch progress | +| `num_epochs_between_scoring` | 10 | Cycle length - Number of epochs to run the Monitor->Rebalance loop | +| `minimum_stake_lamports` | 5,000,000,000,000 (5000 SOL) | Minimum number of stake lamports for a validator to be considered for the pool | +| `minimum_voting_epochs` | 5 | Minimum number of consecutive epochs a validator has to vote before it can be considered for the pool | diff --git a/docs/program-overview.md b/docs/program-overview.md index 70cf49b5..63a4210a 100644 --- a/docs/program-overview.md +++ b/docs/program-overview.md @@ -5,4 +5,166 @@ title: Program Overview # Program Overview -[Content about the program overview goes here] +The Steward Program's main functions include: + +1. **Validator Selection and Management**: The program selects validators, monitors their performance, and adjusts stake allocations to maintain optimal performance. +2. **[State Machine](#state-machine)**: A state machine representing the progress throughout a cycle (10-epoch period) for scoring and delegations. +3. **[Adding and Removing Validators](#validator-management)**: Automatically handles the addition and removal of validators based on predefined criteria. +4. **[Admin Abilities](#admin-abilities)**: Allows administrators to update parameters, manage a blacklist, pause/unpause the state machine, and execute passthrough instructions for SPL Stake Pool. + +## State Machine + +The state machine represents the progress throughout a cycle (10-epoch period for scoring and delegations). + +### Compute Scores + +At the start of a 10 epoch cycle (“the cycle”), all validators are scored. We save the overall score, which combines yield performance as well as binary criteria for eligibility, and we also save the yield-only score. + +The `score` is for determining eligibility to be staked in the pool, the `yield_score` determines the unstaking order (who gets unstaked first, lowest to highest). + +The following metrics are used to calculate the `score` and `yield_score`: + +- `mev_commission_score`: If max mev commission in `mev_commission_range` epochs is less than threshold, score is 1.0, else 0 +- `commission_score`: If any commission within the individual's validator history exceeds the historical_commission_threshold, score it 0.0, else 1.0. This effectively bans validators who have performed commission manipulation. +- `blacklisted_score`: If validator is blacklisted, score is 0.0, else 1.0 +- `superminority_score`: If validator is not in the superminority, score is 1.0, else 0.0 +- `delinquency_score`: If delinquency is not > threshold in any epoch, score is 1.0, else 0.0 +- `running_jito_score`: If validator has a mev commission in the last 10 epochs, score is 1.0, else 0.0 + +> Note: All data comes from the `ValidatorHistory` account for each validator. + +To formula to calculate the `score` and `yield_score`: + +```rust +let yield_score = (average_vote_credits / average_blocks) + * (1. - commission); + +let score = mev_commission_score + * commission_score + * blacklisted_score + * superminority_score + * delinquency_score + * running_jito_score + * yield_score +``` + +As a validator, in order to receive a high score for JitoSOL, you must meet these binary eligibility criteria, and return a high rate of rewards to your stakers. The eligibility criteria ensure that we're delegating to validators that meet some important properties for decentralization, Solana network health, operator quality, and MEV sharing. The yield score is an objective way to compare validators' relative yield and ensure we're returning a competitive APY to JitoSOL holders, which in turn attracts more stake to delegate to validators. + +In this version 0 of the score formula, there is no weighting of any factor above any other, because it is a product of all factors. But because all factors besides `yield_score` will only be `1.0` or `0.0`, yield is the main factor for determining validator ranking assuming all eligibility criteria is met. Even if one of the eligibility factors is not met, or the score is not high enough to be selected for the pool delegation, it is still advantageous to have a high `yield_score` as it is used for ranking which validators to unstake first. + +For a breakdown of the formulas used for each score, see the Appendix. + +Take a look at the implementation in [score.rs](./src/score.rs#L14) + +### Compute Delegations + +Once all the validators are scored, we need to calculate the stake distribution we will be aiming for during this cycle. + +The top 200 of these validators by overall score will become our validator set, with each receiving 1/200th of the share of the pool. If there are fewer than 200 validators eligible (having a non-zero score), the “ideal” validators are all of the eligible validators. + +At the end of this step, we have a list of target delegations, representing proportions of the share of the pool, not fixed lamport amounts. + +### Idle + +Once the delegation amounts are set, the Steward waits until we’ve reached the 95% point of the epoch to run the next step. + +### Compute Instant Unstake + +All validators are checked for a set of Instant Unstaking criteria, like commission rugs, delinquency, etc. If they hit the criteria, they are marked for the rest of the cycle. + +The following criteria are used to determine if a validator should be instantly unstaked: + +- `delinquency_check`: Checks if validator has missed > `instant_unstake_delinquency_threshold_ratio` of votes this epoch +- `commission_check`: Checks if validator has increased commission > `commission_threshold` +- `mev_commission_check`: Checks if validator has increased MEV commission > `mev_commission_bps_threshold` +- `is_blacklisted`: Checks if validator was added to blacklist blacklisted + +If any of these criteria are true, we mark the validator for instant unstaking: + +```rust +let instant_unstake = + delinquency_check || commission_check || mev_commission_check || is_blacklisted; +``` + +Take a look at the implementation in [score.rs](./src/score.rs#L212) + +### Rebalance + +One instruction is called for each validator, to increase or decrease stake if a validator is not at the target delegation. + +For each validator, we first check if the target balance is greater or less than the current balance. + +If the target is less, we attempt to undelegate stake: + +When undelegating stake, we want to protect against massive unstaking events due to bugs or network anomalies, to preserve yield. There are two considerations with this: +There are 3 main reasons we may want to unstake. We want to identify when each of these cases happen, and let some amount of unstaking happen for each case throughout a cycle. + +- the pool is out of line with the ideal top 200 validators and should be rebalanced, +- a validator is marked for instant unstake, or +- a validator gets a stake deposit putting it far above the target delegation. + +We want to run the rebalance step in parallel across all validators, meaning these instructions can be run in any order, but we need to have some notion of the unstaking priority/ordering so the worst validators can be unstaked before the cap is hit. Pre-calculating the balance changes is difficult since balances can change at any time due to user stake deposits and withdrawals, and the total lamports of the pool can change at any time. + +To address 1: we set a cap for each unstake condition, and track the amount unstaked for that condition per cycle. (scoring_unstake_cap, instant_unstake_cap, stake_deposit_unstake_cap) + +To address 2: in each instruction, we calculate how much this validator is able to be unstaked in real-time, based on the current balances of all validators, unstaking caps, and which validators would be “ahead” in priority for unstaking before caps are hit. (Lower yield_score = higher priority). If all the “worse” validators will be unstaked and hit the caps before this one can, no unstaking is done on this validator. + +For each validator, its active stake balance in lamports is then saved. In the next epoch, any lamports above this can be assumed to be a stake deposit, and can be unstaked. + +If the target is greater, we attempt to delegate stake: + +In a similar vein to unstaking, we want to be able to customize the priority of staking so that instructions can be run in any order, but stake is going to better validators given a limited pool reserve. + +We calculate how much this validator is able to be staked in real time, given the number of validators “ahead” in priority (based on overall score). If all validators who need stake are able to be filled and there is still stake left over in the reserve, this validator gets the stake it needs, either up to the target or until the reserve is empty, whichever is first. + +If stake was delegated, the balance is updated. + +Note that because of the 1-epoch delay in cooling down stake that’s unstaked from validators, there will be many instances where the reserve won’t have enough balance to match the stake needs for everyone, but the following epoch, it will (assuming no withdrawals). + +If we are already at the target, nothing happens. + +Progress is marked so this validator won’t be adjusted again this epoch. After all validators’ progress is marked “true”, we transition to idle. + +### Rest of the cycle + +After unstaking is done, the state machine moves back into Idle. In next epoch and in the rest of the epochs for the cycle, it repeats these steps: + +- Compute Instant Unstake +- Rebalance +- Idle + +At the start of the next cycle, we move back to Compute Scores, and all those pieces of state are reset to 0. + +### Diagram + +[View in Figma](https://www.figma.com/board/zBcpTOu1zqKIoEnd54sWqH/Steward-Program?node-id=0-1&t=1FK9C2TtNvFEfOU6-1) + +![State Machine Diagram](./assets/state-machine-diagram.png) + +## Validator Management + +### Adding Validators + +The JitoSOL pool aims to have as many active validators as possible. Validators are added permissionlessly if they meet the following criteria: + +- At least 5 epochs of voting. +- Minimum SOL stake of 5000. + +There are approximately 1300 validators that meet these criteria today, with a capacity for 5000 validators. + +### Removing Validators + +Validators are removed if: + +- The validator’s vote account closes. +- The validator stops voting for 5 epochs, leading to the deactivation of the stake account in the stake pool. + +## Admin Abilities + +There are 3 authorities. `blacklist_authority`, `parameters_authority`, and `admin`. + +`blacklist_authority` is used to add/remove validators to/from the blacklist to prevent delegation. + +`parameters_authority` is used to update the parameters, which affects scoring and delegation. + +`admin` is used for all other perimissioned actions, including updating authorities, pausing the state machine, and executing passthrough instructions for SPL Stake Pool that require the staker as a signer. diff --git a/docs/state-machine.md b/docs/state-machine.md deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/terminology.md b/docs/terminology.md index e3e8b876..a56f0bd8 100644 --- a/docs/terminology.md +++ b/docs/terminology.md @@ -1,23 +1,35 @@ --- -title: Jito (Re)staking Terminology +layout: default +title: StakeNet Terminology --- -# Terminology - -- **Node**: A piece of software running the required protocols as specified by the node consensus network. -- **Node Consensus Network (NCN)**: A set of nodes running software for the same network, working together to achieve - consensus and provide services. -- **Operator**: An entity that manages one or more nodes in the node consensus network. -- **Vault**: A smart contract that securely holds staked assets and delegates them to operators. -- **Vault Receipt Token (VRT)**: A tokenized representation of staked assets inside a vault, allowing for liquidity and - composability. -- **Staking**: The process of locking up assets as collateral to support network operations and earn rewards. -- **(Re)staking**: The process of staking already-staked assets, such as liquid staking tokens to participate in - additional networks or services. -- **Slashing**: A penalty mechanism where a portion of staked assets is forfeited if an operator misbehaves or fails to - meet performance requirements. -- **Delegation**: The act of assigning staked assets to specific operators within the network. -- **Multi-Asset Staking**: The ability to stake various types of SPL tokens, not limited to native blockchain tokens. -- **Liquid Staking**: A form of staking that provides liquidity to Solana stakers. -- **SPL Token**: Solana Program Library Token, a standard for creating and managing tokens on the Solana blockchain. -- **Epoch**: A fixed period in the blockchain during which staking rewards are calculated and distributed. +**Steward Program**: An Anchor program designed to manage the staking authority for a SPL Stake Pool, selecting and managing validators based on performance metrics. + +**SPL Stake Pool**: A Solana program that allows for the creation of stake pools, enabling users to delegate their stake to multiple validators through a single pool and receive a Liquid Staking Token (LST) representing their stake. + +**Validator History Program**: +A Solana program that maintains historical performance data for validators. It records and stores various metrics over time on-chain. + +**Delegation**: The amount of stake targeted to a specific validator by the Steward Program. + +**State Machine**: The core logic of the Steward Program that manages different operational states and transitions between them. + +**Cycle**: A period of time (currently 10 epochs) in the Steward Program during which validators are selected and delegations are managed. The scores and validator selections are fixed for the duration of a cycle. + +**Validator Score**: A numerical representation of a validator's performance and desirability within the Steward Program. + +**Yield Score**: A component of the validator score that represents the validator's efficiency in generating rewards for delegators, taking into account factors like epoch credits and commission. + +**Rebalancing**: The process of adjusting stake allocations among validators to maintain desired proportions or react to performance changes. + +**Instant Unstaking**: A process that allows for immediate removal of staked SOL under certain conditions from a validator, bypassing usual rebalancing. + +**Blacklist**: A list of validators that are excluded from receiving delegations through the Steward Program. + +**Delinquency**: A state where a validator is not its duties adequately, often measured by missed vote opportunities. In the context of the Steward Program, this is measured by voting less than a specific threshold in a given epoch. + +**Epoch Credits**: A measure of a validator's performance, representing the number of times it has correctly voted on blocks in a given epoch. Directly impacts validator staking rewards. Also known as Vote Credits. + +**Commission**: The percentage of rewards that a validator keeps for itself before distributing the remainder to its delegators. + +**Superminority**: The highest-staked validators in the network who collectively hold more than 33.3% of the stake. diff --git a/docs/validators/scoring-system.md b/docs/validators/scoring-system.md index 1e936d1f..b48538f2 100644 --- a/docs/validators/scoring-system.md +++ b/docs/validators/scoring-system.md @@ -5,4 +5,126 @@ title: Scoring System # Scoring System -[In-depth explanation of the scoring system with visuals goes here] +$` +\displaylines{ +\text{mev\_commission\_score} = +\begin{cases} +1.0 & \text{if } \max(\text{mev\_commission}_{t_1, t_2}) \leq \text{mev\_commission\_bps\_threshold} \\ +0.0 & \text{otherwise} +\end{cases} \\ +\text{where } t_1 = \text{current\_epoch} - \text{mev\_commission\_range} \\ +\text{and } t_2 = \text{current\_epoch} +} +`$ + +--- + +$` +\displaylines{ +\text{running\_jito\_score} = +\begin{cases} +1.0 & \text{if any MEV commission exists in } t_1 \text{ to } t_2 \\ +0.0 & \text{otherwise} +\end{cases} \\ +\text{where } t_1 = \text{current\_epoch} - \text{mev\_commission\_range} \\ +\text{and } t_2 = \text{current\_epoch} +} +`$ + +--- + +$` +\displaylines{ +\text{delinquency\_score} = +\begin{cases} +1.0 & \text{if } \left( \frac{\text{vote\_credits}_t}{\text{total\_blocks}_t} \right) > \text{scoring\_delinquency\_threshold\_ratio} \text{ for all } t_1 \leq t \leq t_2 \\ +0.0 & \text{otherwise} +\end{cases} \\ +\text{where } t_1 = \text{current\_epoch} - \text{epoch\_credits\_range} \\ +\text{and } t_2 = \text{current\_epoch} - 1 +} +`$ + +--- + +$` +\displaylines{ +\text{commission\_score} = +\begin{cases} +1.0 & \text{if } \max(\text{commission}_{t_1, t_2}) \leq \text{commission\_threshold} \\ +0.0 & \text{otherwise} +\end{cases} \\ +\text{where } t_1 = \text{current\_epoch} - \text{commission\_range} \\ +\text{and } t_2 = \text{current\_epoch} +} +`$ + +--- + +$` +\displaylines{ +\text{historical\_commission\_score} = +\begin{cases} +1.0 & \text{if } \max(\text{historical\_commission}_{t_1, t_2}) \leq \text{historical\_commission\_threshold} \\ +0.0 & \text{otherwise} +\end{cases} \\ +\text{where } t_1 = \text{first\_reliable\_epoch} = 520 \\ +\text{and } t_2 = \text{current\_epoch} +} +`$ + +--- + +$` +\displaylines{ +\text{blacklisted\_score} = +\begin{cases} +0.0 & \text{if blacklisted in current epoch} \\ +1.0 & \text{otherwise} +\end{cases} +} +`$ + +--- + +$` +\displaylines{ +\text{superminority\_score} = +\begin{cases} +0.0 & \text{if in superminority in current epoch} \\ +1.0 & \text{otherwise} +\end{cases} \\ +} +`$ + +--- + +$` +\displaylines{ +\text{vote\_credits\_ratio} = \frac{\sum_{t=t_1}^{t_2} \text{vote\_credits}_t}{\sum_{t=t_1}^{t_2} \text{total\_blocks}_t} \\ +\text{where } t_1 = \text{current\_epoch} - \text{epoch\_credits\_range} \\ +\text{and } t_2 = \text{current\_epoch} - 1 +} +`$ + +Note: total_blocks is the field in ClusterHistory that tracks how many blocks were created by the cluster in a given epoch. This represents the maximum number of vote credits that a validator can earn. Vote credits are synonymous with epoch credits. + +--- + +$` +\displaylines{ +\text{yield\_score} = \text{vote\_credits\_ratio} \times (1 - max(\text{commission}_{t_1, t_2})) \\ +\text{where } t_1 = \text{current\_epoch} - \text{commission\_range} \\ +\text{and } t_2 = \text{current\_epoch} +} +`$ + +Note: Yield score is a relative measure of the yield returned to stakers by the validator, not an exact measure of its APY. + +--- + +$` +\displaylines{ +\text{final\_score} = \text{mev\_commission\_score} \times \text{commission\_score} \times \text{historical\_commission\_score} \times \text{blacklisted\_score} \times \text{superminority\_score} \times \text{delinquency\_score} \times \text{running\_jito\_score} \times \text{yield\_score} +} +`$