From d58c8a2b96a6271fd84cd96bc3a1a7f6763f29c2 Mon Sep 17 00:00:00 2001 From: Sarfaraz Nawaz Date: Sat, 29 Aug 2026 01:41:59 +0530 Subject: [PATCH 1/5] feat(fraud-proofs): Implement RaiseChallenge --- dlp-api/src/v2/args/mod.rs | 2 + dlp-api/src/v2/args/raise_challenge.rs | 14 + dlp-api/src/v2/instruction.rs | 2 + dlp-api/src/v2/instruction_builder/mod.rs | 2 + .../v2/instruction_builder/raise_challenge.rs | 50 ++ dlp-api/src/v2/pda.rs | 18 + dlp-api/src/v2/state/challenge.rs | 45 ++ dlp-api/src/v2/state/mod.rs | 2 + src/v2/processor/fraud_proofs/mod.rs | 2 + .../processor/fraud_proofs/raise_challenge.rs | 271 +++++++++ src/v2/processor/mod.rs | 3 + tests/test_v2_raise_challenge.rs | 553 ++++++++++++++++++ 12 files changed, 964 insertions(+) create mode 100644 dlp-api/src/v2/args/raise_challenge.rs create mode 100644 dlp-api/src/v2/instruction_builder/raise_challenge.rs create mode 100644 dlp-api/src/v2/state/challenge.rs create mode 100644 src/v2/processor/fraud_proofs/raise_challenge.rs create mode 100644 tests/test_v2_raise_challenge.rs diff --git a/dlp-api/src/v2/args/mod.rs b/dlp-api/src/v2/args/mod.rs index dd8ec8d2..79d708e5 100644 --- a/dlp-api/src/v2/args/mod.rs +++ b/dlp-api/src/v2/args/mod.rs @@ -3,6 +3,7 @@ mod init_protocol_config; mod post_commitment; +mod raise_challenge; mod register_operator; mod register_verifier; mod update_protocol_config; @@ -11,6 +12,7 @@ mod write_state_buffer; pub use init_protocol_config::*; pub use post_commitment::*; +pub use raise_challenge::*; pub use register_operator::*; pub use register_verifier::*; pub use update_protocol_config::*; diff --git a/dlp-api/src/v2/args/raise_challenge.rs b/dlp-api/src/v2/args/raise_challenge.rs new file mode 100644 index 00000000..a66c4abb --- /dev/null +++ b/dlp-api/src/v2/args/raise_challenge.rs @@ -0,0 +1,14 @@ +use wheels::variable_offset_layout; + +#[derive(Clone, Debug, PartialEq, Eq)] +#[variable_offset_layout(buffer_offset = 1)] +pub struct RaiseChallengeArgs { + /// State commitment hash stored in the pending commitment being challenged. + pub state_commitment_hash: [u8; 32], + + /// Salted hash binding the challenger state to this challenge. + pub challenge_hash: [u8; 32], + + /// Lamports locked in the challenge account until reveal or resolution. + pub stake_lamports: u64, +} diff --git a/dlp-api/src/v2/instruction.rs b/dlp-api/src/v2/instruction.rs index c80f685a..80f24fb0 100644 --- a/dlp-api/src/v2/instruction.rs +++ b/dlp-api/src/v2/instruction.rs @@ -27,6 +27,8 @@ pub enum DlpV2Instruction { WriteStateBuffer = 107, /// Applies an approved v2 commitment to the delegated account. FinalizeCommitment = 108, + /// Raises a hash-only challenge against a v2 pending commitment. + RaiseChallenge = 109, } impl DlpV2Instruction { diff --git a/dlp-api/src/v2/instruction_builder/mod.rs b/dlp-api/src/v2/instruction_builder/mod.rs index f75bcf66..5e9fbe05 100644 --- a/dlp-api/src/v2/instruction_builder/mod.rs +++ b/dlp-api/src/v2/instruction_builder/mod.rs @@ -2,6 +2,7 @@ mod approve_commitment; mod finalize_commitment; mod init_protocol_config; mod post_commitment; +mod raise_challenge; mod register_operator; mod register_verifier; mod update_protocol_config; @@ -12,6 +13,7 @@ pub use approve_commitment::*; pub use finalize_commitment::*; pub use init_protocol_config::*; pub use post_commitment::*; +pub use raise_challenge::*; pub use register_operator::*; pub use register_verifier::*; pub use update_protocol_config::*; diff --git a/dlp-api/src/v2/instruction_builder/raise_challenge.rs b/dlp-api/src/v2/instruction_builder/raise_challenge.rs new file mode 100644 index 00000000..78989121 --- /dev/null +++ b/dlp-api/src/v2/instruction_builder/raise_challenge.rs @@ -0,0 +1,50 @@ +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; +use solana_sdk_ids::system_program; +use wheels::layout::Encodable; + +use crate::{ + compat::{Compatize, Modernize}, + v2::{ + pda::{challenge_pda, pending_commitment_pda, protocol_config_pda}, + DlpV2Instruction, RaiseChallengeArgs, + }, +}; + +/// Builds the instruction that raises a hash-only v2 challenge. +pub fn raise_challenge( + challenger: Pubkey, + account: Pubkey, + commit_id: u64, + args: RaiseChallengeArgs, +) -> Instruction { + Instruction { + program_id: crate::id().modernize(), + accounts: vec![ + AccountMeta::new(challenger, true), + AccountMeta::new( + challenge_pda( + &account.compatize(), + commit_id, + &challenger.compatize(), + ) + .modernize(), + false, + ), + AccountMeta::new( + pending_commitment_pda(&account.compatize(), commit_id) + .modernize(), + false, + ), + AccountMeta::new_readonly(protocol_config_pda().modernize(), false), + AccountMeta::new_readonly(system_program::id(), false), + ], + data: [ + DlpV2Instruction::RaiseChallenge.to_vec(), + args.encode().unwrap(), + ] + .concat(), + } +} diff --git a/dlp-api/src/v2/pda.rs b/dlp-api/src/v2/pda.rs index 7438f320..b0ee16c3 100644 --- a/dlp-api/src/v2/pda.rs +++ b/dlp-api/src/v2/pda.rs @@ -6,6 +6,7 @@ pub const VERIFIER_BOND_SEED: &[u8] = b"verifier-bond"; pub const VERIFIER_REGISTRY_SEED: &[u8] = b"verifier-registry"; pub const STATE_BUFFER_SEED: &[u8] = b"state-buffer"; pub const PENDING_COMMITMENT_SEED: &[u8] = b"pending-commitment"; +pub const CHALLENGE_SEED: &[u8] = b"challenge"; // TODO (snawaz): Precompute these addresses if PDA derivation becomes const-safe. @@ -61,3 +62,20 @@ pub fn pending_commitment_pda(account: &Pubkey, commit_id: u64) -> Pubkey { ) .0 } + +pub fn challenge_pda( + account: &Pubkey, + commit_id: u64, + challenger: &Pubkey, +) -> Pubkey { + Pubkey::find_program_address( + &[ + CHALLENGE_SEED, + account.as_ref(), + &commit_id.to_le_bytes(), + challenger.as_ref(), + ], + &crate::id(), + ) + .0 +} diff --git a/dlp-api/src/v2/state/challenge.rs b/dlp-api/src/v2/state/challenge.rs new file mode 100644 index 00000000..0a3b8d9f --- /dev/null +++ b/dlp-api/src/v2/state/challenge.rs @@ -0,0 +1,45 @@ +use wheels::fixed_offset_layout; + +use crate::compat::Pubkey; + +pub const CHALLENGE_STATUS_AWAITING_REVEAL: u8 = 1; +pub const CHALLENGE_STATUS_AWAITING_RESOLVER: u8 = 2; +pub const CHALLENGE_STATUS_TERMINAL: u8 = 3; + +/// PDA: `["challenge", account, commit_id, challenger]`. +/// Created by `RaiseChallenge`. +/// Closed by `CloseTerminalAccounts` after terminal challenge outcome. +#[derive(Clone, Debug, PartialEq, Eq)] +#[fixed_offset_layout(buffer_offset = 0)] +pub struct Challenge { + /// Account type marker. + pub discriminator: [u8; 8], + + /// Current challenge lifecycle state. + pub status: u8, + + /// PendingCommitment being challenged. + pub pending_commitment: Pubkey, + + /// Challenger that locked stake and owns the reveal. + pub challenger_identity: Pubkey, + + /// State commitment hash copied from the pending commitment at raise time. + pub state_commitment_hash: [u8; 32], + + /// Salted hash binding the challenger state to this challenge. + pub challenge_hash: [u8; 32], + + /// Lamports locked in this challenge account. + pub challenger_stake_lamports: u64, + + /// Slot when the challenge was raised. + pub raised_slot: u64, + + /// Slot after which an unrevealed challenge can be timed out. + pub reveal_deadline_slot: u64, +} + +impl Challenge { + pub const DISCRIMINATOR: [u8; 8] = *b"v2chal00"; +} diff --git a/dlp-api/src/v2/state/mod.rs b/dlp-api/src/v2/state/mod.rs index a39d8513..d50236ae 100644 --- a/dlp-api/src/v2/state/mod.rs +++ b/dlp-api/src/v2/state/mod.rs @@ -1,3 +1,4 @@ +mod challenge; mod operator_bond; mod pending_commitment; mod protocol_config; @@ -5,6 +6,7 @@ mod state_buffer; mod verifier_bond; mod verifier_registry; +pub use challenge::*; pub use operator_bond::*; pub use pending_commitment::*; pub use protocol_config::*; diff --git a/src/v2/processor/fraud_proofs/mod.rs b/src/v2/processor/fraud_proofs/mod.rs index 7c677870..c585e735 100644 --- a/src/v2/processor/fraud_proofs/mod.rs +++ b/src/v2/processor/fraud_proofs/mod.rs @@ -3,9 +3,11 @@ mod approve_commitment; mod finalize_commitment; mod post_commitment; +mod raise_challenge; mod write_state_buffer; pub use approve_commitment::*; pub use finalize_commitment::*; pub use post_commitment::*; +pub use raise_challenge::*; pub use write_state_buffer::*; diff --git a/src/v2/processor/fraud_proofs/raise_challenge.rs b/src/v2/processor/fraud_proofs/raise_challenge.rs new file mode 100644 index 00000000..ebd77518 --- /dev/null +++ b/src/v2/processor/fraud_proofs/raise_challenge.rs @@ -0,0 +1,271 @@ +use dlp_api::{ + error::DlpError, + v2::{ + pda::{CHALLENGE_SEED, PENDING_COMMITMENT_SEED, PROTOCOL_CONFIG_SEED}, + Challenge, PendingCommitment, ProtocolConfig, RaiseChallengeArgs, + SelectedVerifier, CHALLENGE_STATUS_AWAITING_REVEAL, + PENDING_COMMITMENT_STATUS_ACTIVE, + PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL, + }, +}; +use pinocchio::{ + cpi::{Seed, Signer}, + error::ProgramError, + sysvars::{clock::Clock, Sysvar}, + AccountView, ProgramResult, +}; +use pinocchio_system::instructions as system; +use wheels::{ + layout::{Decodable, Encodable}, + require_eq, require_ge, require_le, require_n_accounts, require_signer, +}; + +use crate::{ + processor::fast::utils::pda::create_pda, + requires::{ + require_initialized_pda, require_owned_pda, require_uninitialized_pda, + StandardCtx, + }, +}; + +/// Raise a hash-only challenge against one v2 pending commitment. +/// +/// Accounts: +/// 0: `[signer, writable]` challenger identity and stake payer +/// 1: `[writable]` Challenge PDA +/// 2: `[writable]` PendingCommitment PDA +/// 3: `[]` ProtocolConfig PDA +/// 4: `[]` system program, required by system CPI +#[inline(never)] +pub fn process_raise_challenge( + accounts: &[AccountView], + data: &[u8], +) -> ProgramResult { + let [ + challenger, // force multi-line + challenge, + pending_commitment, + protocol_config, + _system_program, + ] = require_n_accounts!(accounts, 5); + + let args = RaiseChallengeArgs::decode(data)?; + + require_signer!(challenger); + if !challenger.is_writable() { + return Err(ProgramError::Immutable); + } + require_owned_pda( + pending_commitment, + &crate::fast::ID, + "pending commitment", + )?; + + let protocol_config_data = protocol_config.try_borrow()?; + let protocol_config_state = + load_protocol_config(protocol_config, protocol_config_data.as_ref())?; + validate_protocol_config(&protocol_config_state, &args)?; + + let pending_data = pending_commitment.try_borrow()?; + let pending_state = PendingCommitment::decode(pending_data.as_ref())?; + let clock = Clock::get()?; + validate_pending_commitment( + &pending_state, + pending_commitment, + &args, + clock.slot, + )?; + + let pending_account_pubkey = *pending_state.account_pubkey(); + let commit_id_bytes = pending_state.commit_id().to_le_bytes(); + let challenge_bump = require_uninitialized_pda( + challenge, + &[ + CHALLENGE_SEED, + pending_account_pubkey.as_ref(), + &commit_id_bytes, + challenger.address().as_ref(), + ], + &crate::fast::ID, + true, + StandardCtx::new("challenge"), + )?; + + let challenge_address = challenge.address().clone(); + let reveal_deadline_slot = clock + .slot + .checked_add(protocol_config_state.challenger_reveal_timeout_slots()) + .ok_or(DlpError::Overflow)?; + + let challenge_state = Challenge { + discriminator: Challenge::DISCRIMINATOR, + status: CHALLENGE_STATUS_AWAITING_REVEAL, + pending_commitment: pending_commitment.address().clone(), + challenger_identity: challenger.address().clone(), + state_commitment_hash: *args.state_commitment_hash(), + challenge_hash: *args.challenge_hash(), + challenger_stake_lamports: args.stake_lamports(), + raised_slot: clock.slot, + reveal_deadline_slot, + }; + let updated_pending = + copy_pending_with_challenge(&pending_state, challenge_address); + drop(pending_data); + drop(protocol_config_data); + + create_pda( + challenge, + &crate::fast::ID, + Challenge::DATA_LEN, + &[Signer::from(&[ + Seed::from(CHALLENGE_SEED), + Seed::from(pending_account_pubkey.as_ref()), + Seed::from(&commit_id_bytes), + Seed::from(challenger.address().as_ref()), + Seed::from(&[challenge_bump]), + ])], + challenger, + )?; + + system::Transfer { + from: challenger, + to: challenge, + lamports: args.stake_lamports(), + } + .invoke()?; + + challenge_state.encode_to(challenge.try_borrow_mut()?.as_mut())?; + updated_pending.encode_to(pending_commitment.try_borrow_mut()?.as_mut())?; + + Ok(()) +} + +fn load_protocol_config<'a>( + protocol_config: &AccountView, + data: &'a [u8], +) -> Result, ProgramError> { + require_initialized_pda( + protocol_config, + &[PROTOCOL_CONFIG_SEED], + &crate::fast::ID, + false, + "protocol config", + )?; + + Ok(ProtocolConfig::decode(data)?) +} + +fn validate_protocol_config( + protocol_config: &dlp_api::v2::ProtocolConfigView<'_>, + args: &dlp_api::v2::RaiseChallengeArgsView<'_>, +) -> ProgramResult { + if protocol_config.discriminator() != ProtocolConfig::DISCRIMINATOR { + return Err(ProgramError::InvalidAccountData); + } + require_eq!( + protocol_config.paused(), + false, + ProgramError::InvalidAccountData + ); + require_ge!( + args.stake_lamports(), + protocol_config.min_challenger_stake(), + ProgramError::InvalidInstructionData + ); + + Ok(()) +} + +fn validate_pending_commitment( + pending_commitment: &dlp_api::v2::PendingCommitmentView<'_>, + pending_commitment_account: &AccountView, + args: &dlp_api::v2::RaiseChallengeArgsView<'_>, + current_slot: u64, +) -> ProgramResult { + if pending_commitment.discriminator() != PendingCommitment::DISCRIMINATOR { + return Err(ProgramError::InvalidAccountData); + } + + let commit_id_bytes = pending_commitment.commit_id().to_le_bytes(); + require_initialized_pda( + pending_commitment_account, + &[ + PENDING_COMMITMENT_SEED, + pending_commitment.account_pubkey().as_ref(), + &commit_id_bytes, + ], + &crate::fast::ID, + true, + "pending commitment", + )?; + + require_eq!( + pending_commitment.status(), + PENDING_COMMITMENT_STATUS_ACTIVE, + ProgramError::InvalidInstructionData + ); + require_le!( + current_slot, + pending_commitment.challenge_window_end_slot(), + ProgramError::InvalidInstructionData + ); + require_eq!( + pending_commitment.active_challenge().is_none(), + true, + ProgramError::InvalidInstructionData + ); + require_eq!( + pending_commitment.resolved_state_source().is_none(), + true, + ProgramError::InvalidInstructionData + ); + require_eq!( + args.state_commitment_hash(), + pending_commitment.state_commitment_hash(), + ProgramError::InvalidInstructionData + ); + + Ok(()) +} + +fn copy_pending_with_challenge( + pending: &dlp_api::v2::PendingCommitmentView<'_>, + challenge: dlp_api::compat::Pubkey, +) -> PendingCommitment { + PendingCommitment { + discriminator: PendingCommitment::DISCRIMINATOR, + status: PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL, + operator_identity: *pending.operator_identity(), + operator_bond: *pending.operator_bond(), + account_pubkey: *pending.account_pubkey(), + commit_id: pending.commit_id(), + delegation_record: *pending.delegation_record(), + da_pointer_hash: *pending.da_pointer_hash(), + account_state_hash: *pending.account_state_hash(), + data_hash: *pending.data_hash(), + lamports: pending.lamports(), + owner: *pending.owner(), + state_commitment_hash: *pending.state_commitment_hash(), + verifier_registry: *pending.verifier_registry(), + verifier_registry_revision: pending.verifier_registry_revision(), + challenge_window_id: pending.challenge_window_id(), + posted_slot: pending.posted_slot(), + activation_slot: pending.activation_slot(), + challenge_window_end_slot: pending.challenge_window_end_slot(), + approval_count: pending.approval_count(), + approval_threshold: pending.approval_threshold(), + active_challenge: Some(challenge), + resolved_state_source: pending.resolved_state_source(), + er_slot: pending.er_slot(), + _pad_before_selected_verifiers: [0; 7], + selected_verifiers: pending + .selected_verifiers() + .iter() + .map(|verifier| SelectedVerifier { + verifier_identity: *verifier.verifier_identity(), + approved: verifier.approved(), + _pad_after_approved: [0; 7], + }) + .collect(), + } +} diff --git a/src/v2/processor/mod.rs b/src/v2/processor/mod.rs index ba9f2bf2..396013a9 100644 --- a/src/v2/processor/mod.rs +++ b/src/v2/processor/mod.rs @@ -42,5 +42,8 @@ pub fn process_instruction( DlpV2Instruction::FinalizeCommitment => { process_finalize_commitment(accounts, data) } + DlpV2Instruction::RaiseChallenge => { + process_raise_challenge(accounts, data) + } } } diff --git a/tests/test_v2_raise_challenge.rs b/tests/test_v2_raise_challenge.rs new file mode 100644 index 00000000..de7d7864 --- /dev/null +++ b/tests/test_v2_raise_challenge.rs @@ -0,0 +1,553 @@ +use dlp_api::{ + pda::{ + delegation_metadata_pda_from_delegated_account, + delegation_record_pda_from_delegated_account, + }, + v2::{ + instruction_builder::{ + approve_commitment, finalize_commitment, post_commitment, + raise_challenge, register_operator, register_verifier, + update_verifier_registry, write_state_buffer, + }, + pda::{challenge_pda, pending_commitment_pda, CHALLENGE_SEED}, + Challenge, DlpV2Instruction, PendingCommitment, PostCommitmentArgs, + RaiseChallengeArgs, RegisterOperatorArgs, RegisterVerifierArgs, + WriteStateBufferArgs, CHALLENGE_STATUS_AWAITING_REVEAL, + PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL, + VERIFIER_REGISTRY_ACTION_ADD, + }, +}; +use solana_program::{native_token::LAMPORTS_PER_SOL, rent::Rent}; +use solana_program_test::{ + BanksClientError, ProgramTest, ProgramTestBanksClientExt, + ProgramTestContext, +}; +use solana_sdk::{ + account::Account, + hash::Hash, + instruction::Instruction, + pubkey::Pubkey, + signature::{Keypair, Signer}, + transaction::Transaction, +}; +use solana_sdk_ids::system_program; +use wheels::layout::{Decodable, Encodable}; + +mod fixtures; + +use crate::fixtures::{ + create_delegation_metadata_data, create_delegation_record_data, + v2::{init_v2, valid_args}, +}; + +#[test] +fn test_v2_raise_challenge_instruction_data_uses_one_byte_tag() { + let args = RaiseChallengeArgs { + state_commitment_hash: [1; 32], + challenge_hash: [2; 32], + stake_lamports: 3, + }; + let ix = raise_challenge( + Pubkey::new_unique(), + Pubkey::new_unique(), + 7, + args.clone(), + ); + let encoded_args = args.encode().unwrap(); + + assert_eq!(ix.data[0], DlpV2Instruction::RaiseChallenge as u8); + assert_eq!(ix.data.len(), 1 + encoded_args.len()); + assert_eq!(&ix.data[1..], encoded_args.as_slice()); + + let decoded = + ::decode(&ix.data[1..]).unwrap(); + assert_eq!(*decoded.state_commitment_hash(), args.state_commitment_hash); + assert_eq!(*decoded.challenge_hash(), args.challenge_hash); + assert_eq!(decoded.stake_lamports(), args.stake_lamports); +} + +#[test] +fn test_v2_challenge_pda_uses_account_commit_id_and_challenger() { + let account = Pubkey::new_unique(); + let challenger = Pubkey::new_unique(); + let commit_id = 7_u64; + let expected = Pubkey::find_program_address( + &[ + CHALLENGE_SEED, + account.as_ref(), + &commit_id.to_le_bytes(), + challenger.as_ref(), + ], + &dlp_api::ID, + ) + .0; + + assert_eq!(challenge_pda(&account, commit_id, &challenger), expected); +} + +#[tokio::test] +async fn test_v2_raise_challenge() { + let mut env = setup_raise_challenge_env().await; + post_v2_commitment(&mut env).await.unwrap(); + + let pending_before = read_pending_commitment(&mut env).await; + let args = valid_raise_challenge_args(pending_before.state_commitment_hash); + let challenge_address = challenge_pda( + &env.delegated_account, + env.commit_id, + &env.challenger.pubkey(), + ); + + raise_v2_challenge(&mut env, args.clone()).await.unwrap(); + + let challenge_account = env + .context + .banks_client + .get_account(challenge_address) + .await + .unwrap() + .unwrap(); + let challenge = + ::decode(&challenge_account.data).unwrap(); + + assert_eq!(challenge.discriminator(), Challenge::DISCRIMINATOR); + assert_eq!(challenge.status(), CHALLENGE_STATUS_AWAITING_REVEAL); + assert_eq!( + *challenge.pending_commitment(), + pending_commitment_pda(&env.delegated_account, env.commit_id) + ); + assert_eq!(*challenge.challenger_identity(), env.challenger.pubkey()); + assert_eq!( + *challenge.state_commitment_hash(), + args.state_commitment_hash + ); + assert_eq!(*challenge.challenge_hash(), args.challenge_hash); + assert_eq!(challenge.challenger_stake_lamports(), args.stake_lamports); + assert_eq!( + challenge.reveal_deadline_slot(), + challenge.raised_slot() + + env.config_args.challenger_reveal_timeout_slots + ); + assert_eq!( + challenge_account.lamports, + Rent::default().minimum_balance(Challenge::DATA_LEN) + + args.stake_lamports + ); + + let pending_after = read_pending_commitment(&mut env).await; + assert_eq!( + pending_after.status, + PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL + ); + assert_eq!(pending_after.active_challenge, Some(challenge_address)); +} + +#[tokio::test] +async fn test_v2_raise_challenge_fails_without_challenger_signature() { + let mut env = setup_raise_challenge_env().await; + post_v2_commitment(&mut env).await.unwrap(); + + let pending = read_pending_commitment(&mut env).await; + let args = valid_raise_challenge_args(pending.state_commitment_hash); + let mut ix = raise_challenge( + env.challenger.pubkey(), + env.delegated_account, + env.commit_id, + args, + ); + ix.accounts[0].is_signer = false; + + assert!(process_ix(&mut env.context, ix, &[]).await.is_err()); +} + +#[tokio::test] +async fn test_v2_raise_challenge_fails_below_min_stake() { + let mut env = setup_raise_challenge_env().await; + post_v2_commitment(&mut env).await.unwrap(); + + let pending = read_pending_commitment(&mut env).await; + let mut args = valid_raise_challenge_args(pending.state_commitment_hash); + args.stake_lamports = env.config_args.min_challenger_stake - 1; + + assert!(raise_v2_challenge(&mut env, args).await.is_err()); +} + +#[tokio::test] +async fn test_v2_raise_challenge_fails_after_challenge_window() { + let mut env = setup_raise_challenge_env().await; + post_v2_commitment(&mut env).await.unwrap(); + warp_past_challenge_window(&mut env).await; + + let pending = read_pending_commitment(&mut env).await; + let args = valid_raise_challenge_args(pending.state_commitment_hash); + + assert!(raise_v2_challenge(&mut env, args).await.is_err()); +} + +#[tokio::test] +async fn test_v2_raise_challenge_fails_with_wrong_state_commitment_hash() { + let mut env = setup_raise_challenge_env().await; + post_v2_commitment(&mut env).await.unwrap(); + + let mut args = valid_raise_challenge_args([1; 32]); + args.state_commitment_hash[0] ^= 1; + + assert!(raise_v2_challenge(&mut env, args).await.is_err()); +} + +#[tokio::test] +async fn test_v2_raise_challenge_fails_when_challenge_already_active() { + let mut env = setup_raise_challenge_env().await; + post_v2_commitment(&mut env).await.unwrap(); + + let pending = read_pending_commitment(&mut env).await; + let args = valid_raise_challenge_args(pending.state_commitment_hash); + raise_v2_challenge(&mut env, args.clone()).await.unwrap(); + + assert!(raise_v2_challenge(&mut env, args).await.is_err()); +} + +#[tokio::test] +async fn test_v2_finalize_commitment_fails_with_active_challenge() { + let mut env = setup_raise_challenge_env().await; + post_v2_commitment(&mut env).await.unwrap(); + approve_v2_commitment(&mut env).await.unwrap(); + + let pending = read_pending_commitment(&mut env).await; + let args = valid_raise_challenge_args(pending.state_commitment_hash); + raise_v2_challenge(&mut env, args).await.unwrap(); + warp_past_challenge_window(&mut env).await; + + assert!(finalize_v2_commitment(&mut env).await.is_err()); +} + +struct RaiseChallengeEnv { + context: ProgramTestContext, + operator: Keypair, + verifier: Keypair, + challenger: Keypair, + delegated_account: Pubkey, + committed_owner: Pubkey, + committed_lamports: u64, + commit_id: u64, + config_args: dlp_api::v2::InitProtocolConfigArgs, +} + +async fn setup_raise_challenge_env() -> RaiseChallengeEnv { + let mut program_test = ProgramTest::new("dlp", dlp_api::ID, None); + program_test.prefer_bpf(true); + + let authority = Keypair::new(); + let operator = Keypair::new(); + let verifier = Keypair::new(); + let challenger = Keypair::new(); + let delegated_account = Pubkey::new_unique(); + let committed_owner = Pubkey::new_unique(); + let commit_id = 1; + let final_state_data = vec![9, 8, 7, 6, 5]; + let record_lamports = LAMPORTS_PER_SOL; + let committed_lamports = LAMPORTS_PER_SOL; + + add_lamport_account(&mut program_test, authority.pubkey()); + add_lamport_account(&mut program_test, operator.pubkey()); + add_lamport_account(&mut program_test, verifier.pubkey()); + add_lamport_account(&mut program_test, challenger.pubkey()); + + program_test.add_account( + delegated_account, + Account { + lamports: record_lamports, + data: vec![1, 2], + owner: dlp_api::ID, + executable: false, + rent_epoch: 0, + }, + ); + program_test.add_account( + delegation_record_pda_from_delegated_account(&delegated_account), + Account { + lamports: LAMPORTS_PER_SOL, + data: create_delegation_record_data( + operator.pubkey(), + committed_owner, + Some(record_lamports), + ), + owner: dlp_api::ID, + executable: false, + rent_epoch: 0, + }, + ); + program_test.add_account( + delegation_metadata_pda_from_delegated_account(&delegated_account), + Account { + lamports: LAMPORTS_PER_SOL, + data: create_delegation_metadata_data( + authority.pubkey(), + &[], + false, + ), + owner: dlp_api::ID, + executable: false, + rent_epoch: 0, + }, + ); + + let mut context = program_test.start_with_context().await; + let config_args = valid_args(); + init_v2( + &context.banks_client, + &context.payer, + &authority, + context.last_blockhash, + config_args.clone(), + ) + .await; + + register_v2_operator( + &mut context, + &operator, + &authority, + config_args.min_operator_bond, + ) + .await + .unwrap(); + register_and_add_v2_verifier( + &mut context, + &verifier, + &authority, + config_args.min_verifier_bond, + ) + .await + .unwrap(); + + write_v2_state_buffer( + &mut context, + &operator, + delegated_account, + WriteStateBufferArgs { + commit_id, + total_len: final_state_data.len() as u32, + offset: 0, + chunk: final_state_data.clone(), + }, + ) + .await + .unwrap(); + + RaiseChallengeEnv { + context, + operator, + verifier, + challenger, + delegated_account, + committed_owner, + committed_lamports, + commit_id, + config_args, + } +} + +async fn post_v2_commitment( + env: &mut RaiseChallengeEnv, +) -> Result<(), BanksClientError> { + let ix = post_commitment( + env.operator.pubkey(), + env.delegated_account, + PostCommitmentArgs { + commit_id: env.commit_id, + lamports: env.committed_lamports, + owner: env.committed_owner, + da_pointer_hash: [9; 32], + er_slot: Some(42), + }, + ); + + process_ix(&mut env.context, ix, &[&env.operator]).await +} + +async fn approve_v2_commitment( + env: &mut RaiseChallengeEnv, +) -> Result<(), BanksClientError> { + let ix = approve_commitment( + env.verifier.pubkey(), + env.delegated_account, + env.commit_id, + ); + + process_ix(&mut env.context, ix, &[&env.verifier]).await +} + +async fn raise_v2_challenge( + env: &mut RaiseChallengeEnv, + args: RaiseChallengeArgs, +) -> Result<(), BanksClientError> { + let ix = raise_challenge( + env.challenger.pubkey(), + env.delegated_account, + env.commit_id, + args, + ); + + process_ix(&mut env.context, ix, &[&env.challenger]).await +} + +async fn finalize_v2_commitment( + env: &mut RaiseChallengeEnv, +) -> Result<(), BanksClientError> { + let ix = finalize_commitment( + env.operator.pubkey(), + env.delegated_account, + env.commit_id, + ); + + process_ix(&mut env.context, ix, &[&env.operator]).await +} + +async fn register_v2_operator( + context: &mut ProgramTestContext, + operator: &Keypair, + authority: &Keypair, + amount_lamports: u64, +) -> Result<(), BanksClientError> { + let ix = register_operator( + operator.pubkey(), + authority.pubkey(), + RegisterOperatorArgs { amount_lamports }, + ); + + process_ix(context, ix, &[operator, authority]).await +} + +async fn register_and_add_v2_verifier( + context: &mut ProgramTestContext, + verifier: &Keypair, + authority: &Keypair, + amount_lamports: u64, +) -> Result<(), BanksClientError> { + let ix = register_verifier( + verifier.pubkey(), + authority.pubkey(), + RegisterVerifierArgs { amount_lamports }, + ); + process_ix(context, ix, &[verifier, authority]).await?; + + let ix = update_verifier_registry( + authority.pubkey(), + verifier.pubkey(), + dlp_api::v2::UpdateVerifierRegistryArgs { + action: VERIFIER_REGISTRY_ACTION_ADD, + weight: 1, + }, + ); + + process_ix(context, ix, &[authority]).await +} + +async fn write_v2_state_buffer( + context: &mut ProgramTestContext, + operator: &Keypair, + account: Pubkey, + args: WriteStateBufferArgs, +) -> Result<(), BanksClientError> { + let ix = write_state_buffer( + context.payer.pubkey(), + operator.pubkey(), + account, + args, + ); + + process_ix(context, ix, &[operator]).await +} + +async fn process_ix( + context: &mut ProgramTestContext, + ix: Instruction, + signers: &[&Keypair], +) -> Result<(), BanksClientError> { + let latest_blockhash: Hash = + context.banks_client.get_latest_blockhash().await.unwrap(); + let blockhash = context + .banks_client + .get_new_latest_blockhash(&latest_blockhash) + .await + .unwrap(); + let tx = { + let mut all_signers = Vec::with_capacity(signers.len() + 1); + all_signers.push(&context.payer); + all_signers.extend_from_slice(signers); + + Transaction::new_signed_with_payer( + &[ix], + Some(&context.payer.pubkey()), + &all_signers, + blockhash, + ) + }; + + context.banks_client.process_transaction(tx).await +} + +async fn warp_past_challenge_window(env: &mut RaiseChallengeEnv) { + let pending_commitment = read_pending_commitment(env).await; + env.context + .warp_to_slot(pending_commitment.challenge_window_end_slot + 1) + .unwrap(); +} + +struct PendingCommitmentSnapshot { + status: u8, + active_challenge: Option, + state_commitment_hash: [u8; 32], + challenge_window_end_slot: u64, +} + +async fn read_pending_commitment( + env: &mut RaiseChallengeEnv, +) -> PendingCommitmentSnapshot { + let pending_commitment_account = env + .context + .banks_client + .get_account(pending_commitment_pda( + &env.delegated_account, + env.commit_id, + )) + .await + .unwrap() + .unwrap(); + + let pending_commitment = ::decode( + &pending_commitment_account.data, + ) + .unwrap(); + + PendingCommitmentSnapshot { + status: pending_commitment.status(), + active_challenge: pending_commitment.active_challenge().cloned(), + state_commitment_hash: *pending_commitment.state_commitment_hash(), + challenge_window_end_slot: pending_commitment + .challenge_window_end_slot(), + } +} + +fn add_lamport_account(program_test: &mut ProgramTest, pubkey: Pubkey) { + program_test.add_account( + pubkey, + Account { + lamports: LAMPORTS_PER_SOL, + data: vec![], + owner: system_program::id(), + executable: false, + rent_epoch: 0, + }, + ); +} + +fn valid_raise_challenge_args( + state_commitment_hash: [u8; 32], +) -> RaiseChallengeArgs { + RaiseChallengeArgs { + state_commitment_hash, + challenge_hash: [7; 32], + stake_lamports: 3, + } +} From 6b3a0d6e31c4c258c1b2fc9d3b692522b2e5c209 Mon Sep 17 00:00:00 2001 From: Sarfaraz Nawaz Date: Sat, 29 Aug 2026 03:01:17 +0530 Subject: [PATCH 2/5] Prepare Challenge state for reveal --- dlp-api/src/v2/state/challenge.rs | 22 +++++++++++++++++++ .../processor/fraud_proofs/raise_challenge.rs | 10 +++++++-- tests/test_v2_raise_challenge.rs | 8 ++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/dlp-api/src/v2/state/challenge.rs b/dlp-api/src/v2/state/challenge.rs index 0a3b8d9f..8653c04c 100644 --- a/dlp-api/src/v2/state/challenge.rs +++ b/dlp-api/src/v2/state/challenge.rs @@ -6,6 +6,10 @@ pub const CHALLENGE_STATUS_AWAITING_REVEAL: u8 = 1; pub const CHALLENGE_STATUS_AWAITING_RESOLVER: u8 = 2; pub const CHALLENGE_STATUS_TERMINAL: u8 = 3; +pub const CHALLENGE_OUTCOME_NONE: u8 = 0; +pub const CHALLENGE_OUTCOME_INVALID_REVEAL: u8 = 1; +pub const CHALLENGE_OUTCOME_MATCHING_STATE_CHALLENGER_PENALIZED: u8 = 2; + /// PDA: `["challenge", account, commit_id, challenger]`. /// Created by `RaiseChallenge`. /// Closed by `CloseTerminalAccounts` after terminal challenge outcome. @@ -18,6 +22,12 @@ pub struct Challenge { /// Current challenge lifecycle state. pub status: u8, + /// Terminal outcome, or `CHALLENGE_OUTCOME_NONE` before resolution. + pub outcome: u8, + + /// Keeps the following fixed-width fields 8-byte aligned. + pub _pad_after_outcome: [u8; 6], + /// PendingCommitment being challenged. pub pending_commitment: Pubkey, @@ -30,6 +40,18 @@ pub struct Challenge { /// Salted hash binding the challenger state to this challenge. pub challenge_hash: [u8; 32], + /// Challenger-revealed account lamports. Zero until reveal. + pub challenger_lamports: u64, + + /// Challenger-revealed account owner. Default pubkey until reveal. + pub challenger_owner: Pubkey, + + /// Challenger-revealed account data hash. Zero until reveal. + pub challenger_data_hash: [u8; 32], + + /// Challenger StateBuffer PDA used for reveal. Default pubkey until reveal. + pub challenger_state_buffer: Pubkey, + /// Lamports locked in this challenge account. pub challenger_stake_lamports: u64, diff --git a/src/v2/processor/fraud_proofs/raise_challenge.rs b/src/v2/processor/fraud_proofs/raise_challenge.rs index ebd77518..f4d01470 100644 --- a/src/v2/processor/fraud_proofs/raise_challenge.rs +++ b/src/v2/processor/fraud_proofs/raise_challenge.rs @@ -3,8 +3,8 @@ use dlp_api::{ v2::{ pda::{CHALLENGE_SEED, PENDING_COMMITMENT_SEED, PROTOCOL_CONFIG_SEED}, Challenge, PendingCommitment, ProtocolConfig, RaiseChallengeArgs, - SelectedVerifier, CHALLENGE_STATUS_AWAITING_REVEAL, - PENDING_COMMITMENT_STATUS_ACTIVE, + SelectedVerifier, CHALLENGE_OUTCOME_NONE, + CHALLENGE_STATUS_AWAITING_REVEAL, PENDING_COMMITMENT_STATUS_ACTIVE, PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL, }, }; @@ -100,10 +100,16 @@ pub fn process_raise_challenge( let challenge_state = Challenge { discriminator: Challenge::DISCRIMINATOR, status: CHALLENGE_STATUS_AWAITING_REVEAL, + outcome: CHALLENGE_OUTCOME_NONE, + _pad_after_outcome: [0; 6], pending_commitment: pending_commitment.address().clone(), challenger_identity: challenger.address().clone(), state_commitment_hash: *args.state_commitment_hash(), challenge_hash: *args.challenge_hash(), + challenger_lamports: 0, + challenger_owner: Default::default(), + challenger_data_hash: [0; 32], + challenger_state_buffer: Default::default(), challenger_stake_lamports: args.stake_lamports(), raised_slot: clock.slot, reveal_deadline_slot, diff --git a/tests/test_v2_raise_challenge.rs b/tests/test_v2_raise_challenge.rs index de7d7864..de4bfdea 100644 --- a/tests/test_v2_raise_challenge.rs +++ b/tests/test_v2_raise_challenge.rs @@ -12,7 +12,8 @@ use dlp_api::{ pda::{challenge_pda, pending_commitment_pda, CHALLENGE_SEED}, Challenge, DlpV2Instruction, PendingCommitment, PostCommitmentArgs, RaiseChallengeArgs, RegisterOperatorArgs, RegisterVerifierArgs, - WriteStateBufferArgs, CHALLENGE_STATUS_AWAITING_REVEAL, + WriteStateBufferArgs, CHALLENGE_OUTCOME_NONE, + CHALLENGE_STATUS_AWAITING_REVEAL, PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL, VERIFIER_REGISTRY_ACTION_ADD, }, @@ -112,6 +113,7 @@ async fn test_v2_raise_challenge() { assert_eq!(challenge.discriminator(), Challenge::DISCRIMINATOR); assert_eq!(challenge.status(), CHALLENGE_STATUS_AWAITING_REVEAL); + assert_eq!(challenge.outcome(), CHALLENGE_OUTCOME_NONE); assert_eq!( *challenge.pending_commitment(), pending_commitment_pda(&env.delegated_account, env.commit_id) @@ -122,6 +124,10 @@ async fn test_v2_raise_challenge() { args.state_commitment_hash ); assert_eq!(*challenge.challenge_hash(), args.challenge_hash); + assert_eq!(challenge.challenger_lamports(), 0); + assert_eq!(*challenge.challenger_owner(), Pubkey::default()); + assert_eq!(*challenge.challenger_data_hash(), [0; 32]); + assert_eq!(*challenge.challenger_state_buffer(), Pubkey::default()); assert_eq!(challenge.challenger_stake_lamports(), args.stake_lamports); assert_eq!( challenge.reveal_deadline_slot(), From 6cdc6a19af5ddab197da365287fb1ed8e451b05b Mon Sep 17 00:00:00 2001 From: Sarfaraz Nawaz Date: Mon, 31 Aug 2026 13:26:44 +0530 Subject: [PATCH 3/5] Drop trivial RaiseChallenge instruction data test --- tests/test_v2_raise_challenge.rs | 34 ++++---------------------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/tests/test_v2_raise_challenge.rs b/tests/test_v2_raise_challenge.rs index de4bfdea..7c58575b 100644 --- a/tests/test_v2_raise_challenge.rs +++ b/tests/test_v2_raise_challenge.rs @@ -10,9 +10,9 @@ use dlp_api::{ update_verifier_registry, write_state_buffer, }, pda::{challenge_pda, pending_commitment_pda, CHALLENGE_SEED}, - Challenge, DlpV2Instruction, PendingCommitment, PostCommitmentArgs, - RaiseChallengeArgs, RegisterOperatorArgs, RegisterVerifierArgs, - WriteStateBufferArgs, CHALLENGE_OUTCOME_NONE, + Challenge, PendingCommitment, PostCommitmentArgs, RaiseChallengeArgs, + RegisterOperatorArgs, RegisterVerifierArgs, WriteStateBufferArgs, + CHALLENGE_OUTCOME_NONE, CHALLENGE_STATUS_AWAITING_REVEAL, PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL, VERIFIER_REGISTRY_ACTION_ADD, @@ -32,7 +32,7 @@ use solana_sdk::{ transaction::Transaction, }; use solana_sdk_ids::system_program; -use wheels::layout::{Decodable, Encodable}; +use wheels::layout::Decodable; mod fixtures; @@ -41,32 +41,6 @@ use crate::fixtures::{ v2::{init_v2, valid_args}, }; -#[test] -fn test_v2_raise_challenge_instruction_data_uses_one_byte_tag() { - let args = RaiseChallengeArgs { - state_commitment_hash: [1; 32], - challenge_hash: [2; 32], - stake_lamports: 3, - }; - let ix = raise_challenge( - Pubkey::new_unique(), - Pubkey::new_unique(), - 7, - args.clone(), - ); - let encoded_args = args.encode().unwrap(); - - assert_eq!(ix.data[0], DlpV2Instruction::RaiseChallenge as u8); - assert_eq!(ix.data.len(), 1 + encoded_args.len()); - assert_eq!(&ix.data[1..], encoded_args.as_slice()); - - let decoded = - ::decode(&ix.data[1..]).unwrap(); - assert_eq!(*decoded.state_commitment_hash(), args.state_commitment_hash); - assert_eq!(*decoded.challenge_hash(), args.challenge_hash); - assert_eq!(decoded.stake_lamports(), args.stake_lamports); -} - #[test] fn test_v2_challenge_pda_uses_account_commit_id_and_challenger() { let account = Pubkey::new_unique(); From 08ae804c0d3b87d990bce45fbc7cf0cac930c7be Mon Sep 17 00:00:00 2001 From: Sarfaraz Nawaz Date: Tue, 1 Sep 2026 05:23:26 +0530 Subject: [PATCH 4/5] Rename challenge tests --- tests/test_v2_raise_challenge.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/test_v2_raise_challenge.rs b/tests/test_v2_raise_challenge.rs index 7c58575b..545fc898 100644 --- a/tests/test_v2_raise_challenge.rs +++ b/tests/test_v2_raise_challenge.rs @@ -12,8 +12,7 @@ use dlp_api::{ pda::{challenge_pda, pending_commitment_pda, CHALLENGE_SEED}, Challenge, PendingCommitment, PostCommitmentArgs, RaiseChallengeArgs, RegisterOperatorArgs, RegisterVerifierArgs, WriteStateBufferArgs, - CHALLENGE_OUTCOME_NONE, - CHALLENGE_STATUS_AWAITING_REVEAL, + CHALLENGE_OUTCOME_NONE, CHALLENGE_STATUS_AWAITING_REVEAL, PENDING_COMMITMENT_STATUS_AWAITING_CHALLENGER_REVEAL, VERIFIER_REGISTRY_ACTION_ADD, }, @@ -38,11 +37,11 @@ mod fixtures; use crate::fixtures::{ create_delegation_metadata_data, create_delegation_record_data, - v2::{init_v2, valid_args}, + v2::{initialize_protocol_config, valid_protocol_config_args}, }; #[test] -fn test_v2_challenge_pda_uses_account_commit_id_and_challenger() { +fn test_challenge_pda_uses_account_commit_id_and_challenger() { let account = Pubkey::new_unique(); let challenger = Pubkey::new_unique(); let commit_id = 7_u64; @@ -61,7 +60,7 @@ fn test_v2_challenge_pda_uses_account_commit_id_and_challenger() { } #[tokio::test] -async fn test_v2_raise_challenge() { +async fn test_raise_challenge() { let mut env = setup_raise_challenge_env().await; post_v2_commitment(&mut env).await.unwrap(); @@ -123,7 +122,7 @@ async fn test_v2_raise_challenge() { } #[tokio::test] -async fn test_v2_raise_challenge_fails_without_challenger_signature() { +async fn test_raise_challenge_fails_without_challenger_signature() { let mut env = setup_raise_challenge_env().await; post_v2_commitment(&mut env).await.unwrap(); @@ -141,7 +140,7 @@ async fn test_v2_raise_challenge_fails_without_challenger_signature() { } #[tokio::test] -async fn test_v2_raise_challenge_fails_below_min_stake() { +async fn test_raise_challenge_fails_below_min_stake() { let mut env = setup_raise_challenge_env().await; post_v2_commitment(&mut env).await.unwrap(); @@ -153,7 +152,7 @@ async fn test_v2_raise_challenge_fails_below_min_stake() { } #[tokio::test] -async fn test_v2_raise_challenge_fails_after_challenge_window() { +async fn test_raise_challenge_fails_after_challenge_window() { let mut env = setup_raise_challenge_env().await; post_v2_commitment(&mut env).await.unwrap(); warp_past_challenge_window(&mut env).await; @@ -165,7 +164,7 @@ async fn test_v2_raise_challenge_fails_after_challenge_window() { } #[tokio::test] -async fn test_v2_raise_challenge_fails_with_wrong_state_commitment_hash() { +async fn test_raise_challenge_fails_with_wrong_state_commitment_hash() { let mut env = setup_raise_challenge_env().await; post_v2_commitment(&mut env).await.unwrap(); @@ -176,7 +175,7 @@ async fn test_v2_raise_challenge_fails_with_wrong_state_commitment_hash() { } #[tokio::test] -async fn test_v2_raise_challenge_fails_when_challenge_already_active() { +async fn test_raise_challenge_fails_when_challenge_already_active() { let mut env = setup_raise_challenge_env().await; post_v2_commitment(&mut env).await.unwrap(); @@ -188,7 +187,7 @@ async fn test_v2_raise_challenge_fails_when_challenge_already_active() { } #[tokio::test] -async fn test_v2_finalize_commitment_fails_with_active_challenge() { +async fn test_finalize_commitment_fails_with_active_challenge() { let mut env = setup_raise_challenge_env().await; post_v2_commitment(&mut env).await.unwrap(); approve_v2_commitment(&mut env).await.unwrap(); @@ -273,8 +272,8 @@ async fn setup_raise_challenge_env() -> RaiseChallengeEnv { ); let mut context = program_test.start_with_context().await; - let config_args = valid_args(); - init_v2( + let config_args = valid_protocol_config_args(); + initialize_protocol_config( &context.banks_client, &context.payer, &authority, From 2e42ebc74b866cb7e7138e52cc9999f427c2817c Mon Sep 17 00:00:00 2001 From: Sarfaraz Nawaz Date: Wed, 2 Sep 2026 16:10:03 +0530 Subject: [PATCH 5/5] Remove pending revision from challenge update --- src/v2/processor/fraud_proofs/raise_challenge.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/v2/processor/fraud_proofs/raise_challenge.rs b/src/v2/processor/fraud_proofs/raise_challenge.rs index f4d01470..e4e2835a 100644 --- a/src/v2/processor/fraud_proofs/raise_challenge.rs +++ b/src/v2/processor/fraud_proofs/raise_challenge.rs @@ -253,7 +253,6 @@ fn copy_pending_with_challenge( owner: *pending.owner(), state_commitment_hash: *pending.state_commitment_hash(), verifier_registry: *pending.verifier_registry(), - verifier_registry_revision: pending.verifier_registry_revision(), challenge_window_id: pending.challenge_window_id(), posted_slot: pending.posted_slot(), activation_slot: pending.activation_slot(),