-
Notifications
You must be signed in to change notification settings - Fork 21
feat(fraud-proofs): Implement UpdateProtocolConfig #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2f4c446
feat(fraud-proofs): Implement UpdateProtocolConfig
snawaz 8b64c8e
Use layout views for UpdateProtocolConfig
snawaz c07b94a
Update config tests for one verifier MVP
snawaz fa2516f
Cover one-byte tag for config updates
snawaz 4118986
Drop trivial UpdateProtocolConfig instruction data test
snawaz 1e95426
Rename protocol config update tests
snawaz e2f30ed
Preserve protocol config bump on update
snawaz 0e2fede
dev review
snawaz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| // Update uses the same payload as init because every init-time config value in | ||
| // this args type is also authority-updatable. Account identity fields such as | ||
| // authority, fee vault, pause state, discriminator, and bump are preserved by | ||
| // the processor instead of coming from instruction data. | ||
| pub type UpdateProtocolConfigArgs = super::InitProtocolConfigArgs; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| mod init_protocol_config; | ||
| mod register_operator; | ||
| mod register_verifier; | ||
| mod update_protocol_config; | ||
| mod update_verifier_registry; | ||
|
|
||
| pub use init_protocol_config::*; | ||
| pub use register_operator::*; | ||
| pub use register_verifier::*; | ||
| pub use update_protocol_config::*; | ||
| pub use update_verifier_registry::*; |
31 changes: 31 additions & 0 deletions
31
dlp-api/src/v2/instruction_builder/update_protocol_config.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| use solana_program::{ | ||
| instruction::{AccountMeta, Instruction}, | ||
| pubkey::Pubkey, | ||
| }; | ||
| use wheels::layout::Encodable; | ||
|
|
||
| use crate::{ | ||
| compat::Modernize, | ||
| v2::{ | ||
| pda::protocol_config_pda, DlpV2Instruction, UpdateProtocolConfigArgs, | ||
| }, | ||
| }; | ||
|
|
||
| /// Builds the instruction that updates global v2 config for future work. | ||
| pub fn update_protocol_config( | ||
| authority: Pubkey, | ||
| args: UpdateProtocolConfigArgs, | ||
| ) -> Instruction { | ||
| Instruction { | ||
| program_id: crate::id().modernize(), | ||
| accounts: vec![ | ||
| AccountMeta::new_readonly(authority, true), | ||
| AccountMeta::new(protocol_config_pda().modernize(), false), | ||
| ], | ||
| data: [ | ||
| DlpV2Instruction::UpdateProtocolConfig.to_vec(), | ||
| args.encode().unwrap(), | ||
| ] | ||
| .concat(), | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| mod init_protocol_config; | ||
| mod register_operator; | ||
| mod register_verifier; | ||
| mod update_protocol_config; | ||
| mod update_verifier_registry; | ||
|
|
||
| pub use init_protocol_config::*; | ||
| pub use register_operator::*; | ||
| pub use register_verifier::*; | ||
| pub use update_protocol_config::*; | ||
| pub use update_verifier_registry::*; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| use dlp_api::{ | ||
| error::DlpError, | ||
| v2::{pda::PROTOCOL_CONFIG_SEED, ProtocolConfig, UpdateProtocolConfigArgs}, | ||
| }; | ||
| use pinocchio::{error::ProgramError, AccountView, ProgramResult}; | ||
| use wheels::{ | ||
| layout::{Decodable, Encodable}, | ||
| require_eq_keys, require_n_accounts, require_signer, | ||
| }; | ||
|
|
||
| use crate::requires::require_initialized_pda; | ||
|
|
||
| /// Update global v2 config for future commitments. | ||
| /// | ||
| /// Accounts: | ||
| /// 0: `[signer]` protocol authority | ||
| /// 1: `[writable]` ProtocolConfig PDA | ||
| #[inline(never)] | ||
| pub fn process_update_protocol_config( | ||
| accounts: &[AccountView], | ||
| data: &[u8], | ||
| ) -> ProgramResult { | ||
| let [ | ||
| authority, // force multi-line | ||
| protocol_config, | ||
| ] = require_n_accounts!(accounts, 2); | ||
|
|
||
| let args = UpdateProtocolConfigArgs::decode(data)?; | ||
| super::validate_protocol_config_args(&args)?; | ||
|
|
||
| require_signer!(authority); | ||
| require_initialized_pda( | ||
| protocol_config, | ||
| &[PROTOCOL_CONFIG_SEED], | ||
| &crate::fast::ID, | ||
| true, | ||
| "protocol config", | ||
| )?; | ||
|
|
||
| let protocol_config_data = protocol_config.try_borrow()?; | ||
| let current = ProtocolConfig::decode(protocol_config_data.as_ref())?; | ||
| if current.discriminator() != ProtocolConfig::DISCRIMINATOR { | ||
| return Err(ProgramError::InvalidAccountData); | ||
| } | ||
| require_eq_keys!( | ||
| current.authority(), | ||
| authority.address(), | ||
| DlpError::InvalidAuthority | ||
| ); | ||
|
|
||
| let updated = ProtocolConfig { | ||
| discriminator: ProtocolConfig::DISCRIMINATOR, | ||
| bump: current.bump(), | ||
| authority: *current.authority(), | ||
| // CHECKPOINT: pause/unpause remains a separate design decision; this | ||
| // instruction deliberately preserves the current emergency-stop state. | ||
| paused: current.paused(), | ||
| resolver: *args.resolver(), | ||
| protocol_fee_vault: *current.protocol_fee_vault(), | ||
| min_operator_bond: args.min_operator_bond(), | ||
| min_verifier_bond: args.min_verifier_bond(), | ||
| min_challenger_stake: args.min_challenger_stake(), | ||
| challenge_window_slots: args.challenge_window_slots(), | ||
| operator_response_timeout_slots: args.operator_response_timeout_slots(), | ||
| challenger_reveal_timeout_slots: args.challenger_reveal_timeout_slots(), | ||
| payout_timelock_slots: args.payout_timelock_slots(), | ||
| verifiers_per_commitment: args.verifiers_per_commitment(), | ||
| approval_threshold: args.approval_threshold(), | ||
| max_window_extensions: args.max_window_extensions(), | ||
| match_penalty_bps: args.match_penalty_bps(), | ||
| }; | ||
| drop(protocol_config_data); | ||
|
|
||
| updated.encode_to(protocol_config.try_borrow_mut()?.as_mut())?; | ||
|
|
||
| Ok(()) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,216 @@ | ||
| use dlp_api::{ | ||
| pda::fees_vault_pda, | ||
| v2::{ | ||
| instruction_builder::update_protocol_config, | ||
| pda::{protocol_config_pda, PROTOCOL_CONFIG_SEED}, | ||
| ProtocolConfig, UpdateProtocolConfigArgs, | ||
| }, | ||
| }; | ||
| use solana_sdk::{ | ||
| pubkey::Pubkey, | ||
| signature::{Keypair, Signer}, | ||
| transaction::Transaction, | ||
| }; | ||
| use wheels::layout::Decodable; | ||
|
|
||
| mod fixtures; | ||
|
|
||
| use crate::fixtures::v2::{ | ||
| initialize_protocol_config, setup_program_test_env, | ||
| valid_protocol_config_args, | ||
| }; | ||
|
|
||
| #[tokio::test] | ||
| async fn test_update_protocol_config() { | ||
| let (banks, payer, authority, blockhash) = setup_program_test_env().await; | ||
| let initial_args = valid_protocol_config_args(); | ||
|
|
||
| initialize_protocol_config( | ||
| &banks, | ||
| &payer, | ||
| &authority, | ||
| blockhash, | ||
| initial_args.clone(), | ||
| ) | ||
| .await; | ||
|
|
||
| let update_args = UpdateProtocolConfigArgs { | ||
| resolver: Pubkey::new_unique(), | ||
| min_operator_bond: initial_args.min_operator_bond + 10, | ||
| min_verifier_bond: initial_args.min_verifier_bond + 12, | ||
| min_challenger_stake: initial_args.min_challenger_stake + 16, | ||
| challenge_window_slots: initial_args.challenge_window_slots + 9, | ||
| operator_response_timeout_slots: initial_args | ||
| .operator_response_timeout_slots | ||
| + 13, | ||
| challenger_reveal_timeout_slots: initial_args | ||
| .challenger_reveal_timeout_slots | ||
| + 19, | ||
| payout_timelock_slots: initial_args.payout_timelock_slots + 21, | ||
| verifiers_per_commitment: initial_args.verifiers_per_commitment, | ||
| approval_threshold: initial_args.approval_threshold, | ||
| max_window_extensions: initial_args.max_window_extensions + 1, | ||
| match_penalty_bps: initial_args.match_penalty_bps + 200, | ||
| }; | ||
|
|
||
| let blockhash = banks.get_latest_blockhash().await.unwrap(); | ||
| let ix = update_protocol_config(authority.pubkey(), update_args.clone()); | ||
| let tx = Transaction::new_signed_with_payer( | ||
| &[ix], | ||
| Some(&payer.pubkey()), | ||
| &[&payer, &authority], | ||
| blockhash, | ||
| ); | ||
|
|
||
| assert!(banks.process_transaction(tx).await.is_ok()); | ||
|
|
||
| let protocol_config_account = banks | ||
| .get_account(protocol_config_pda()) | ||
| .await | ||
| .unwrap() | ||
| .unwrap(); | ||
| let protocol_config = | ||
| ProtocolConfig::decode(&protocol_config_account.data).unwrap(); | ||
| let (_, expected_protocol_config_bump) = | ||
| Pubkey::find_program_address(&[PROTOCOL_CONFIG_SEED], &dlp_api::id()); | ||
|
|
||
| assert_eq!( | ||
| protocol_config.discriminator(), | ||
| ProtocolConfig::DISCRIMINATOR | ||
| ); | ||
| assert_eq!(protocol_config.bump(), expected_protocol_config_bump); | ||
| assert_eq!(*protocol_config.authority(), authority.pubkey()); | ||
| assert!(!protocol_config.paused()); | ||
| assert_eq!(*protocol_config.resolver(), update_args.resolver); | ||
| assert_eq!(*protocol_config.protocol_fee_vault(), fees_vault_pda()); | ||
| assert_eq!( | ||
| protocol_config.min_operator_bond(), | ||
| update_args.min_operator_bond | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.min_verifier_bond(), | ||
| update_args.min_verifier_bond | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.min_challenger_stake(), | ||
| update_args.min_challenger_stake | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.challenge_window_slots(), | ||
| update_args.challenge_window_slots | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.operator_response_timeout_slots(), | ||
| update_args.operator_response_timeout_slots | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.challenger_reveal_timeout_slots(), | ||
| update_args.challenger_reveal_timeout_slots | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.payout_timelock_slots(), | ||
| update_args.payout_timelock_slots | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.verifiers_per_commitment(), | ||
| update_args.verifiers_per_commitment | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.approval_threshold(), | ||
| update_args.approval_threshold | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.max_window_extensions(), | ||
| update_args.max_window_extensions | ||
| ); | ||
| assert_eq!( | ||
| protocol_config.match_penalty_bps(), | ||
| update_args.match_penalty_bps | ||
| ); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_update_protocol_config_fails_with_wrong_authority() { | ||
| let (banks, payer, authority, blockhash) = setup_program_test_env().await; | ||
|
|
||
| initialize_protocol_config( | ||
| &banks, | ||
| &payer, | ||
| &authority, | ||
| blockhash, | ||
| valid_protocol_config_args(), | ||
| ) | ||
| .await; | ||
|
|
||
| let wrong_authority = Keypair::new(); | ||
| let blockhash = banks.get_latest_blockhash().await.unwrap(); | ||
| let ix = update_protocol_config( | ||
| wrong_authority.pubkey(), | ||
| valid_protocol_config_args(), | ||
| ); | ||
| let tx = Transaction::new_signed_with_payer( | ||
| &[ix], | ||
| Some(&payer.pubkey()), | ||
| &[&payer, &wrong_authority], | ||
| blockhash, | ||
| ); | ||
|
|
||
| assert!(banks.process_transaction(tx).await.is_err()); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_update_protocol_config_fails_with_invalid_args() { | ||
| let (banks, payer, authority, blockhash) = setup_program_test_env().await; | ||
|
|
||
| initialize_protocol_config( | ||
| &banks, | ||
| &payer, | ||
| &authority, | ||
| blockhash, | ||
| valid_protocol_config_args(), | ||
| ) | ||
| .await; | ||
|
|
||
| let mut args = valid_protocol_config_args(); | ||
| args.approval_threshold = args.verifiers_per_commitment + 1; | ||
|
|
||
| let blockhash = banks.get_latest_blockhash().await.unwrap(); | ||
| let ix = update_protocol_config(authority.pubkey(), args); | ||
| let tx = Transaction::new_signed_with_payer( | ||
| &[ix], | ||
| Some(&payer.pubkey()), | ||
| &[&payer, &authority], | ||
| blockhash, | ||
| ); | ||
|
|
||
| assert!(banks.process_transaction(tx).await.is_err()); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_update_protocol_config_fails_with_wrong_protocol_config_pda() { | ||
| let (banks, payer, authority, blockhash) = setup_program_test_env().await; | ||
|
|
||
| initialize_protocol_config( | ||
| &banks, | ||
| &payer, | ||
| &authority, | ||
| blockhash, | ||
| valid_protocol_config_args(), | ||
| ) | ||
| .await; | ||
|
|
||
| let blockhash = banks.get_latest_blockhash().await.unwrap(); | ||
| let mut ix = update_protocol_config( | ||
| authority.pubkey(), | ||
| valid_protocol_config_args(), | ||
| ); | ||
| ix.accounts[1].pubkey = Pubkey::new_unique(); | ||
| let tx = Transaction::new_signed_with_payer( | ||
| &[ix], | ||
| Some(&payer.pubkey()), | ||
| &[&payer, &authority], | ||
| blockhash, | ||
| ); | ||
|
|
||
| assert!(banks.process_transaction(tx).await.is_err()); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Remove the production
unwrap()from argument encoding.Line 27 panics if
args.encode()returns an error. Return and propagate the encoding error, or provide an explicit invariant that proves encoding cannot fail.As per path instructions, "Treat any usage of
.unwrap()or.expect()in production Rust code as a MAJOR issue."🤖 Prompt for AI Agents
Source: Path instructions