diff --git a/docs/design/mimd-0024-fraud-proofs.md b/docs/design/mimd-0024-fraud-proofs.md index 6e84e459..ea2f6c97 100644 --- a/docs/design/mimd-0024-fraud-proofs.md +++ b/docs/design/mimd-0024-fraud-proofs.md @@ -163,7 +163,7 @@ state_commitment_hash = H( delegation_cluster, operator_identity, account_pubkey, - commit_id, + commit_seq, delegation_record, da_pointer_hash, account_state_hash, @@ -177,7 +177,7 @@ challenge_hash = H( operator_identity, challenger_identity, account_pubkey, - commit_id, + commit_seq, challenger_lamports, challenger_owner, challenger_data_hash, @@ -198,9 +198,9 @@ Seed strings are placeholders until frozen. | `OperatorBond` | `["operator-bond", delegation_cluster]` | Cluster-scoped operator stake for the challenge flow that survives validator rotation. | | `VerifierBond` | `["verifier-bond", delegation_cluster, verifier]` | Verifier stake isolated to one cluster. | | `VerifierRegistry` | `["verifier-registry", delegation_cluster]` | Verifiers selectable for one challenge-enabled cluster. | -| `PendingCommitment` | `["pending-commitment", account, commit_id]` | Main commitment state machine. | -| `StateBuffer` | `["state-buffer", account, commit_id, authority]` | Chunked state payload opened by an operator or challenger. The payload may be full `Account::data` bytes or an encoded diff. | -| `Challenge` | `["challenge", account, commit_id, challenger]` | One challenge against one pending commitment. | +| `PendingCommitment` | `["pending-commitment", account, commit_seq]` | Main commitment state machine. | +| `StateBuffer` | `["state-buffer", account, commit_seq, authority]` | Chunked state payload opened by an operator or challenger. The payload may be full `Account::data` bytes or an encoded diff. | +| `Challenge` | `["challenge", account, commit_seq, challenger]` | One challenge against one pending commitment. | | `PayoutTimelock` | `["payout-timelock", challenge]` | Delayed payout for correct challenger. | ### Essential Fields @@ -376,11 +376,11 @@ pub enum ResolvedStateSource { ChallengerReveal, } -/// PDA: `["pending-commitment", account, commit_id]` +/// PDA: `["pending-commitment", account, commit_seq]` /// Created by: `ProposeCommitment`. /// Closed by: `CloseTerminalAccounts` after finalize, cancel, or expiry. /// -/// One account per delegated account and commit id. +/// One account per delegated account and commit sequence number. pub struct PendingCommitment { /// Current state-machine state for this commitment. pub status: PendingCommitmentStatus, @@ -392,8 +392,8 @@ pub struct PendingCommitment { pub operator_bond: Pubkey, /// Delegated account whose base-layer state will be finalized. pub account_pubkey: Pubkey, - /// Validator-chosen nonce for this account commitment. - pub commit_id: u64, + /// Monotonically increasing sequence number for this account's commitments. + pub commit_seq: u32, /// Delegation metadata tying this account to the ER context. pub delegation_record: Pubkey, /// Hash of replay/data-availability pointer bytes. @@ -406,8 +406,8 @@ pub struct PendingCommitment { pub lamports: u64, /// Owner committed by the operator. pub owner: Pubkey, - /// Hash binding cluster, validator, account, commit id, delegation, DA, - /// and state. + /// Hash binding cluster, validator, account, commit sequence number, + /// delegation, DA, and state. pub state_commitment_hash: [u8; 32], /// Registry account used when this commitment was posted. pub verifier_registry: Pubkey, @@ -454,7 +454,7 @@ pub struct SelectedVerifier { // Review: `challenge_window_id` should be kept only if window extensions or // retries need an explicit round id. -/// PDA: `["state-buffer", account, commit_id, authority]` +/// PDA: `["state-buffer", account, commit_seq, authority]` /// Created by: first `WriteStateBuffer`. /// Frozen by: final `WriteStateBuffer` chunk. /// Closed by: `CloseTerminalAccounts`. @@ -470,7 +470,7 @@ pub struct StateBuffer { pub account_pubkey: Pubkey, /// Commitment this buffer belongs to. - pub commit_id: u64, + pub commit_seq: u32, /// Hash of the uploaded payload. Zero until finalized. pub data_hash: [u8; 32], @@ -509,7 +509,7 @@ pub enum ChallengeOutcome { ChallengerCorrectOperatorSlashed, } -/// PDA: `["challenge", account, commit_id, challenger]` +/// PDA: `["challenge", account, commit_seq, challenger]` /// Created by: `RaiseChallenge`. /// Closed by: `CloseTerminalAccounts` after a terminal challenge outcome. /// @@ -596,8 +596,8 @@ Commitment, approval, challenge, resolution, and finalization instructions. | Instruction | Expected invoker | Description | | --- | --- | --- | -| `WriteStateBuffer` | Authority for a known state-buffer flow | System-level instruction. Creates, grows, writes, and finalizes a DLP-owned `StateBuffer` PDA using the fixed `["state-buffer", account, commit_id, authority]` seed pattern. The instruction remains cluster-neutral; consuming instructions validate the payload, authority, and cluster relationship. | -| `ProposeCommitment` | Current validator | Requires `Challenge` mode, verifies the delegation's cluster and current validator, consumes only cluster-scoped bond and registry accounts, selects verifiers, and starts the challenge window. `DelegationRecord` and `DelegationMetadata` are temporary v1-layout dependencies until their v2 replacement exists. | +| `WriteStateBuffer` | Authority for a known state-buffer flow | System-level instruction. Creates, grows, writes, and finalizes a DLP-owned `StateBuffer` PDA using the fixed `["state-buffer", account, commit_seq, authority]` seed pattern. The instruction remains cluster-neutral; consuming instructions validate the payload, authority, and cluster relationship. | +| `ProposeCommitment` | Current validator | Requires `Challenge` mode, verifies the delegation's cluster and current validator, consumes only cluster-scoped bond and registry accounts, selects verifiers, and starts the challenge window. `DelegationRecord` and `DelegationMetadata` are temporary v1-layout dependencies until their v2 replacement exists. | | `ApproveCommitment` | Selected verifier | Records approval from the selected verifier. v2 MVP requires exactly one selected verifier, so no verifier index is needed. Duplicate approvals do not increase the count. | | `RaiseChallenge` | Challenger | Requires the cluster stored by the pending commitment, locks challenger stake, stores the salted `challenge_hash`, and blocks normal finalization until reveal, timeout, or resolution. It does not consume challenger state bytes. | | `ChallengerReveal` | Challenger | Opens the challenged state and salt, validates both state buffers and the stored `challenge_hash`, penalizes invalid or matching reveals, or moves mismatches to resolver decision. | @@ -653,10 +653,10 @@ create and write accounts in the `StateBuffer` namespace, using the fixed seed pattern: ```text -["state-buffer", account, commit_id, authority] +["state-buffer", account, commit_seq, authority] ``` -The header must bind the same `authority`, `account_pubkey`, and `commit_id`. +The header must bind the same `authority`, `account_pubkey`, and `commit_seq`. The buffer is inert until a consuming instruction validates the authority role: - `ProposeCommitment` accepts a finalized buffer whose authority is the operator; @@ -666,7 +666,7 @@ The buffer is inert until a consuming instruction validates the authority role: needs buffered transport, and validates authority against the challenger. If future protocol flows need multiple buffer purposes for the same -`account + commit_id + authority`, add an explicit `buffer_kind` or purpose seed +`account + commit_seq + authority`, add an explicit `buffer_kind` or purpose seed and matching header field. Reusing the same address space for unrelated opened state can create collisions or confused-consumer bugs. @@ -676,7 +676,7 @@ duplicate retries remain valid. Consuming instructions must check: -- buffer PDA seeds match `account_pubkey`, `commit_id`, and `authority`; +- buffer PDA seeds match `account_pubkey`, `commit_seq`, and `authority`; - buffer is finalized; - buffer commitment matches the `PendingCommitment`; - authority matches the operator for `ProposeCommitment` and happy-path @@ -692,7 +692,7 @@ are too slow. ```rust pub struct WriteStateBufferArgs<'a> { - pub commit_id: u64, + pub commit_seq: u32, pub total_len: u32, /// Must equal StateBuffer.payload.len(), unless retrying an exact old chunk. pub offset: u32, @@ -700,7 +700,7 @@ pub struct WriteStateBufferArgs<'a> { } pub struct ProposeCommitmentArgs { - pub commit_id: u64, + pub commit_seq: u32, pub lamports: u64, pub owner: Pubkey, pub da_pointer_hash: [u8; 32], @@ -743,7 +743,7 @@ pub struct ResolveDisputeArgs { instructions retain their validator-key authorization model. - `WriteStateBuffer` must run before `ProposeCommitment` for the operator state. `ProposeCommitment` rejects if the operator buffer is missing, unfinished, or - belongs to another account, commit id, or current validator. + belongs to another account, commit sequence number, or current validator. - `ProposeCommitment` reads `data_hash` from the finalized operator buffer, verifies that the delegation, operator bond, and verifier registry belong to the same cluster, computes `account_state_hash` and diff --git a/interface/src/args/delegate.rs b/interface/src/args/delegate.rs new file mode 100644 index 00000000..bda44a87 --- /dev/null +++ b/interface/src/args/delegate.rs @@ -0,0 +1,48 @@ +use wheels::variable_offset_layout; + +/// One owner-program seed used to derive the delegated account. +#[derive(Clone, Debug, PartialEq, Eq)] +#[variable_offset_layout(buffer_offset = unknown)] +pub struct DelegationSeed { + #[flexible = 1] + pub value: Vec, +} + +impl<'a> DelegationSeedView<'a> { + /// Returns the seed without its one-byte variable-layout length prefix. + pub fn as_slice(&self) -> &'a [u8] { + &self.bytes()[1..] + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[variable_offset_layout(buffer_offset = 1)] +pub struct DelegateArgs { + /// Bump for the owner-program delegate buffer PDA. + /// Set by the instruction builder. + pub delegate_buffer_bump: u8, + + /// Bump for the DLP-owned DelegationState PDA. + /// Set by the instruction builder. + pub delegation_state_bump: u8, + + /// Bump used with `seeds` to derive an off-curve delegated account. + /// Must be zero for an on-curve delegated account. + pub delegated_account_bump: u8, + + /// Owner-program seeds used to derive an off-curve delegated account. + #[flexible = 1] + #[element_size = variable] + pub seeds: Vec, +} + +impl DelegateArgs { + pub fn new(delegated_account_bump: u8, seeds: Vec) -> Self { + Self { + delegate_buffer_bump: 0, + delegation_state_bump: 0, + delegated_account_bump, + seeds, + } + } +} diff --git a/interface/src/args/mod.rs b/interface/src/args/mod.rs index 68103ced..127da0fc 100644 --- a/interface/src/args/mod.rs +++ b/interface/src/args/mod.rs @@ -2,6 +2,7 @@ // instruction tag, so v2 instruction args use `buffer_offset = 1`. mod challenger_reveal; +mod delegate; mod init_delegation_cluster; mod propose_commitment; mod raise_challenge; @@ -13,6 +14,7 @@ mod update_verifier_registry; mod write_state_buffer; pub use challenger_reveal::*; +pub use delegate::*; pub use init_delegation_cluster::*; pub use propose_commitment::*; pub use raise_challenge::*; diff --git a/interface/src/args/propose_commitment.rs b/interface/src/args/propose_commitment.rs index ba6face9..7c7b5f40 100644 --- a/interface/src/args/propose_commitment.rs +++ b/interface/src/args/propose_commitment.rs @@ -5,7 +5,7 @@ use crate::compat::Pubkey; #[derive(Clone, Debug, PartialEq, Eq)] #[variable_offset_layout(buffer_offset = 1)] pub struct ProposeCommitmentArgs { - pub commit_id: u64, + pub commit_seq: u32, pub lamports: u64, diff --git a/interface/src/args/write_state_buffer.rs b/interface/src/args/write_state_buffer.rs index ce6781bc..4fed230e 100644 --- a/interface/src/args/write_state_buffer.rs +++ b/interface/src/args/write_state_buffer.rs @@ -3,7 +3,7 @@ use wheels::variable_offset_layout; #[derive(Clone, Debug, PartialEq, Eq)] #[variable_offset_layout(buffer_offset = 1)] pub struct WriteStateBufferArgs { - pub commit_id: u64, + pub commit_seq: u32, pub total_len: u32, diff --git a/interface/src/instruction.rs b/interface/src/instruction.rs index 33241858..e0a65cb9 100644 --- a/interface/src/instruction.rs +++ b/interface/src/instruction.rs @@ -33,6 +33,8 @@ pub enum DlpV2Instruction { ChallengerReveal = 110, /// Applies resolver decision for a v2 challenge. ResolveDispute = 111, + /// Delegates an account to one delegation cluster. + Delegate = 112, } impl DlpV2Instruction { diff --git a/interface/src/instruction_builder/approve_commitment.rs b/interface/src/instruction_builder/approve_commitment.rs index 400d0478..b7b9efa1 100644 --- a/interface/src/instruction_builder/approve_commitment.rs +++ b/interface/src/instruction_builder/approve_commitment.rs @@ -14,7 +14,7 @@ pub fn approve_commitment( verifier: Pubkey, delegation_cluster: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, ) -> Instruction { Instruction { program_id: crate::id().modernize(), @@ -29,7 +29,7 @@ pub fn approve_commitment( false, ), AccountMeta::new( - pending_commitment_pda(&account.compatize(), commit_id) + pending_commitment_pda(&account.compatize(), commit_seq) .modernize(), false, ), diff --git a/interface/src/instruction_builder/challenger_reveal.rs b/interface/src/instruction_builder/challenger_reveal.rs index bf86d698..767c5397 100644 --- a/interface/src/instruction_builder/challenger_reveal.rs +++ b/interface/src/instruction_builder/challenger_reveal.rs @@ -17,7 +17,7 @@ pub fn challenger_reveal( operator: Pubkey, delegation_cluster: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, args: ChallengerRevealArgs, ) -> Instruction { Instruction { @@ -27,21 +27,21 @@ pub fn challenger_reveal( AccountMeta::new( challenge_pda( &account.compatize(), - commit_id, + commit_seq, &challenger.compatize(), ) .modernize(), false, ), AccountMeta::new( - pending_commitment_pda(&account.compatize(), commit_id) + pending_commitment_pda(&account.compatize(), commit_seq) .modernize(), false, ), AccountMeta::new_readonly( state_buffer_pda( &account.compatize(), - commit_id, + commit_seq, &operator.compatize(), ) .modernize(), @@ -50,7 +50,7 @@ pub fn challenger_reveal( AccountMeta::new_readonly( state_buffer_pda( &account.compatize(), - commit_id, + commit_seq, &challenger.compatize(), ) .modernize(), diff --git a/interface/src/instruction_builder/delegate.rs b/interface/src/instruction_builder/delegate.rs new file mode 100644 index 00000000..9bae73fd --- /dev/null +++ b/interface/src/instruction_builder/delegate.rs @@ -0,0 +1,52 @@ +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; +use solana_sdk_ids::system_program; +use wheels::{layout::Encodable, DataLayoutError}; + +use crate::{ + compat::{Compatize, Modernize}, + legacy::pda::DELEGATE_BUFFER_TAG, + pda::{delegation_state_pda, DELEGATION_STATE_SEED}, + DelegateArgs, DlpV2Instruction, +}; + +/// Builds the shared instruction that delegates an account to one cluster. +pub fn delegate( + payer: Pubkey, + delegated_account: Pubkey, + original_owner: Pubkey, + delegation_cluster: Pubkey, + mut args: DelegateArgs, +) -> Result { + let (delegate_buffer, delegate_buffer_bump) = Pubkey::find_program_address( + &[DELEGATE_BUFFER_TAG, delegated_account.as_ref()], + &original_owner, + ); + let (_, delegation_state_bump) = Pubkey::find_program_address( + &[DELEGATION_STATE_SEED, delegated_account.as_ref()], + &crate::id().modernize(), + ); + args.delegate_buffer_bump = delegate_buffer_bump; + args.delegation_state_bump = delegation_state_bump; + let data = [DlpV2Instruction::Delegate.to_vec(), args.encode()?].concat(); + + Ok(Instruction { + program_id: crate::id().modernize(), + accounts: vec![ + AccountMeta::new(payer, true), + AccountMeta::new(delegated_account, true), + AccountMeta::new_readonly(original_owner, false), + AccountMeta::new(delegate_buffer, false), + AccountMeta::new( + delegation_state_pda(&delegated_account.compatize()) + .modernize(), + false, + ), + AccountMeta::new_readonly(delegation_cluster, false), + AccountMeta::new_readonly(system_program::id(), false), + ], + data, + }) +} diff --git a/interface/src/instruction_builder/finalize_commitment.rs b/interface/src/instruction_builder/finalize_commitment.rs index 4d3d73c4..506d70b2 100644 --- a/interface/src/instruction_builder/finalize_commitment.rs +++ b/interface/src/instruction_builder/finalize_commitment.rs @@ -19,13 +19,13 @@ pub fn finalize_commitment( operator: Pubkey, delegation_cluster: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, ) -> Instruction { finalize_commitment_with_state_authority( operator, delegation_cluster, account, - commit_id, + commit_seq, None, ) } @@ -35,17 +35,20 @@ pub fn finalize_challenger_commitment( challenger: Pubkey, delegation_cluster: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, ) -> Instruction { - let challenge = - challenge_pda(&account.compatize(), commit_id, &challenger.compatize()) - .modernize(); + let challenge = challenge_pda( + &account.compatize(), + commit_seq, + &challenger.compatize(), + ) + .modernize(); finalize_commitment_with_state_authority( challenger, delegation_cluster, account, - commit_id, + commit_seq, Some(challenge), ) } @@ -54,13 +57,14 @@ fn finalize_commitment_with_state_authority( state_authority: Pubkey, delegation_cluster: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, challenge: Option, ) -> Instruction { let mut accounts = vec![ AccountMeta::new(state_authority, true), AccountMeta::new( - pending_commitment_pda(&account.compatize(), commit_id).modernize(), + pending_commitment_pda(&account.compatize(), commit_seq) + .modernize(), false, ), AccountMeta::new(account, false), @@ -79,7 +83,7 @@ fn finalize_commitment_with_state_authority( AccountMeta::new_readonly( state_buffer_pda( &account.compatize(), - commit_id, + commit_seq, &state_authority.compatize(), ) .modernize(), diff --git a/interface/src/instruction_builder/mod.rs b/interface/src/instruction_builder/mod.rs index 5479da8b..2a4b5eb0 100644 --- a/interface/src/instruction_builder/mod.rs +++ b/interface/src/instruction_builder/mod.rs @@ -1,5 +1,6 @@ mod approve_commitment; mod challenger_reveal; +mod delegate; mod finalize_commitment; mod init_delegation_cluster; mod propose_commitment; @@ -13,6 +14,7 @@ mod write_state_buffer; pub use approve_commitment::*; pub use challenger_reveal::*; +pub use delegate::*; pub use finalize_commitment::*; pub use init_delegation_cluster::*; pub use propose_commitment::*; diff --git a/interface/src/instruction_builder/propose_commitment.rs b/interface/src/instruction_builder/propose_commitment.rs index 046559bd..d4bd7b94 100644 --- a/interface/src/instruction_builder/propose_commitment.rs +++ b/interface/src/instruction_builder/propose_commitment.rs @@ -34,14 +34,14 @@ pub fn propose_commitment( false, ), AccountMeta::new( - pending_commitment_pda(&account.compatize(), args.commit_id) + pending_commitment_pda(&account.compatize(), args.commit_seq) .modernize(), false, ), AccountMeta::new_readonly( state_buffer_pda( &account.compatize(), - args.commit_id, + args.commit_seq, ¤t_validator.compatize(), ) .modernize(), diff --git a/interface/src/instruction_builder/raise_challenge.rs b/interface/src/instruction_builder/raise_challenge.rs index c2f4c844..04d6e12e 100644 --- a/interface/src/instruction_builder/raise_challenge.rs +++ b/interface/src/instruction_builder/raise_challenge.rs @@ -16,7 +16,7 @@ pub fn raise_challenge( challenger: Pubkey, delegation_cluster: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, args: RaiseChallengeArgs, ) -> Instruction { Instruction { @@ -26,14 +26,14 @@ pub fn raise_challenge( AccountMeta::new( challenge_pda( &account.compatize(), - commit_id, + commit_seq, &challenger.compatize(), ) .modernize(), false, ), AccountMeta::new( - pending_commitment_pda(&account.compatize(), commit_id) + pending_commitment_pda(&account.compatize(), commit_seq) .modernize(), false, ), diff --git a/interface/src/instruction_builder/resolve_dispute.rs b/interface/src/instruction_builder/resolve_dispute.rs index da155e75..8d65cc52 100644 --- a/interface/src/instruction_builder/resolve_dispute.rs +++ b/interface/src/instruction_builder/resolve_dispute.rs @@ -17,7 +17,7 @@ pub fn resolve_dispute( delegation_cluster: Pubkey, challenger: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, args: ResolveDisputeArgs, ) -> Instruction { Instruction { @@ -27,14 +27,14 @@ pub fn resolve_dispute( AccountMeta::new( challenge_pda( &account.compatize(), - commit_id, + commit_seq, &challenger.compatize(), ) .modernize(), false, ), AccountMeta::new( - pending_commitment_pda(&account.compatize(), commit_id) + pending_commitment_pda(&account.compatize(), commit_seq) .modernize(), false, ), diff --git a/interface/src/instruction_builder/write_state_buffer.rs b/interface/src/instruction_builder/write_state_buffer.rs index 903b3f94..4c5417be 100644 --- a/interface/src/instruction_builder/write_state_buffer.rs +++ b/interface/src/instruction_builder/write_state_buffer.rs @@ -26,7 +26,7 @@ pub fn write_state_buffer( AccountMeta::new( state_buffer_pda( &account.compatize(), - args.commit_id, + args.commit_seq, &authority.compatize(), ) .modernize(), diff --git a/interface/src/pda.rs b/interface/src/pda.rs index c728409b..0bce2400 100644 --- a/interface/src/pda.rs +++ b/interface/src/pda.rs @@ -1,6 +1,7 @@ use crate::compat::Pubkey; pub const DELEGATION_CLUSTER_SEED: &[u8] = b"delegation-cluster"; +pub const DELEGATION_STATE_SEED: &[u8] = b"delegation-state"; pub const OPERATOR_BOND_SEED: &[u8] = b"operator-bond"; pub const VERIFIER_BOND_SEED: &[u8] = b"verifier-bond"; pub const VERIFIER_REGISTRY_SEED: &[u8] = b"verifier-registry"; @@ -18,6 +19,14 @@ pub fn delegation_cluster_pda(cluster_id: &Pubkey) -> Pubkey { .0 } +pub fn delegation_state_pda(delegated_account: &Pubkey) -> Pubkey { + Pubkey::find_program_address( + &[DELEGATION_STATE_SEED, delegated_account.as_ref()], + &crate::id(), + ) + .0 +} + pub fn verifier_registry_pda(delegation_cluster: &Pubkey) -> Pubkey { Pubkey::find_program_address( &[VERIFIER_REGISTRY_SEED, delegation_cluster.as_ref()], @@ -51,14 +60,14 @@ pub fn verifier_bond_pda( pub fn state_buffer_pda( account: &Pubkey, - commit_id: u64, + commit_seq: u32, authority: &Pubkey, ) -> Pubkey { Pubkey::find_program_address( &[ STATE_BUFFER_SEED, account.as_ref(), - &commit_id.to_le_bytes(), + &commit_seq.to_le_bytes(), authority.as_ref(), ], &crate::id(), @@ -66,12 +75,12 @@ pub fn state_buffer_pda( .0 } -pub fn pending_commitment_pda(account: &Pubkey, commit_id: u64) -> Pubkey { +pub fn pending_commitment_pda(account: &Pubkey, commit_seq: u32) -> Pubkey { Pubkey::find_program_address( &[ PENDING_COMMITMENT_SEED, account.as_ref(), - &commit_id.to_le_bytes(), + &commit_seq.to_le_bytes(), ], &crate::id(), ) @@ -80,14 +89,14 @@ pub fn pending_commitment_pda(account: &Pubkey, commit_id: u64) -> Pubkey { pub fn challenge_pda( account: &Pubkey, - commit_id: u64, + commit_seq: u32, challenger: &Pubkey, ) -> Pubkey { Pubkey::find_program_address( &[ CHALLENGE_SEED, account.as_ref(), - &commit_id.to_le_bytes(), + &commit_seq.to_le_bytes(), challenger.as_ref(), ], &crate::id(), diff --git a/interface/src/state/challenge.rs b/interface/src/state/challenge.rs index eb38d797..b44f8df4 100644 --- a/interface/src/state/challenge.rs +++ b/interface/src/state/challenge.rs @@ -12,7 +12,7 @@ pub const CHALLENGE_OUTCOME_MATCHING_STATE_CHALLENGER_PENALIZED: u8 = 2; pub const CHALLENGE_OUTCOME_OPERATOR_CORRECT_CHALLENGER_SLASHED: u8 = 3; pub const CHALLENGE_OUTCOME_CHALLENGER_CORRECT_OPERATOR_SLASHED: u8 = 4; -/// PDA: `["challenge", account, commit_id, challenger]`. +/// PDA: `["challenge", account, commit_seq, challenger]`. /// Created by `RaiseChallenge`. /// Closed by `CloseTerminalAccounts` after terminal challenge outcome. #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/interface/src/state/delegation_state.rs b/interface/src/state/delegation_state.rs new file mode 100644 index 00000000..0dcb0711 --- /dev/null +++ b/interface/src/state/delegation_state.rs @@ -0,0 +1,93 @@ +use wheels::{ + fixed_offset_layout, layout::VariableLayoutSlice, DataLayoutError, +}; + +use crate::{compat::Pubkey, error::DlpError, DelegationSeed}; + +/// PDA: `["delegation-state", delegated_account]`. +/// Created by `Delegate`; closed when the account is undelegated. +#[derive(Clone, Debug, PartialEq, Eq)] +#[fixed_offset_layout(buffer_offset = 0)] +pub struct DelegationState { + /// Account type marker. + pub discriminator: [u8; 8], + + /// Canonical PDA bump for this account. + pub bump: u8, + + /// Account controlled by this delegation state. + pub delegated_account: Pubkey, + + /// Stable cluster authority for this delegation. + pub delegation_cluster: Pubkey, + + /// Program that owned the account before delegation. + pub original_owner: Pubkey, + + /// Account that paid to create this delegation state. + pub rent_payer: Pubkey, + + /// Slot in which this delegation was created. + pub delegation_slot: u64, + + /// Account lamports at delegation or after the latest finalization. + pub lamports: u64, + + /// Latest finalized commit sequence number applied to the delegated account. + pub last_commit_seq: u32, + + /// Who requested undelegation, stored as `UndelegationRequester::value()`. + pub undelegation_requester: u8, + + /// Bump used with the stored seeds for an off-curve delegated account. + pub delegated_account_bump: u8, + + /// Number of owner-program seeds encoded in `encoded_seeds`. + pub seed_count: u8, + + /// Self-delimiting `DelegationSeed` values used to derive the account. + #[extendable = 2] + pub encoded_seeds: Vec, +} + +impl DelegationState { + pub const DISCRIMINATOR: [u8; 8] = *b"v2delst0"; + pub const MAX_SEEDS: usize = 8; + pub const MAX_SEED_LEN: usize = 32; + pub const ENCODED_SEEDS_LEN_HEADER_LEN: usize = 2; + + pub fn data_len_from_encoded_seeds_len( + encoded_seeds_len: usize, + ) -> Result { + Self::MIN_DATA_LEN + .checked_add(Self::ENCODED_SEEDS_LEN_HEADER_LEN) + .and_then(|len| len.checked_add(encoded_seeds_len)) + .ok_or(DlpError::Overflow) + } +} + +impl<'a> DelegationStateView<'a> { + pub fn seeds( + &self, + ) -> Result, DataLayoutError> { + VariableLayoutSlice::new( + self.encoded_seeds().as_slice(), + self.seed_count() as usize, + ) + } +} + +/// Identifies who requested that a delegated account be undelegated. +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UndelegationRequester { + None = 0, + Validator = 1, + OwnerProgram = 2, +} + +impl UndelegationRequester { + pub const fn value(self) -> u8 { + self as u8 + } +} diff --git a/interface/src/state/mod.rs b/interface/src/state/mod.rs index 9dfc0921..652e2755 100644 --- a/interface/src/state/mod.rs +++ b/interface/src/state/mod.rs @@ -1,5 +1,6 @@ mod challenge; mod delegation_cluster; +mod delegation_state; mod operator_bond; mod pending_commitment; mod state_buffer; @@ -8,6 +9,7 @@ mod verifier_registry; pub use challenge::*; pub use delegation_cluster::*; +pub use delegation_state::*; pub use operator_bond::*; pub use pending_commitment::*; pub use state_buffer::*; diff --git a/interface/src/state/pending_commitment.rs b/interface/src/state/pending_commitment.rs index d423e0c8..4dc95937 100644 --- a/interface/src/state/pending_commitment.rs +++ b/interface/src/state/pending_commitment.rs @@ -15,11 +15,11 @@ pub const PENDING_COMMITMENT_STATUS_CANCELLED: u8 = 9; pub const RESOLVED_STATE_SOURCE_OPERATOR_COMMITMENT: u8 = 1; pub const RESOLVED_STATE_SOURCE_CHALLENGER_REVEAL: u8 = 2; -/// PDA: `["pending-commitment", account, commit_id]`. +/// PDA: `["pending-commitment", account, commit_seq]`. /// Created by `ProposeCommitment`. /// Closed by `CloseTerminalAccounts` after finalize, cancel, or expiry. /// -/// One account per delegated account and commit id. +/// One account per delegated account and commit sequence number. #[derive(Clone, Debug, PartialEq, Eq)] #[fixed_offset_layout(buffer_offset = 0)] pub struct PendingCommitment { @@ -41,8 +41,8 @@ pub struct PendingCommitment { /// Delegated account whose base-layer state will be finalized. pub account_pubkey: Pubkey, - /// Operator-chosen nonce for this account commitment. - pub commit_id: u64, + /// Monotonically increasing sequence number for this account's commitments. + pub commit_seq: u32, /// Delegation record tying this account to the ER context. pub delegation_record: Pubkey, @@ -62,7 +62,8 @@ pub struct PendingCommitment { /// Owner committed by the operator. pub owner: Pubkey, - /// Hash binding operator, account, commit id, delegation, DA, and state. + /// Hash binding operator, account, commit sequence number, delegation, DA, + /// and state. pub state_commitment_hash: [u8; 32], /// Registry account used when this commitment was posted. @@ -108,7 +109,7 @@ pub struct PendingCommitment { pub er_slot: Option, /// Aligns selected verifier elements after the Vec length prefix. - pub _pad_before_selected_verifiers: [u8; 5], + pub _pad_before_selected_verifiers: [u8; 9], /// Verifiers selected by round-robin for this commitment. #[extendable = 2] diff --git a/interface/src/state/state_buffer.rs b/interface/src/state/state_buffer.rs index 6f97bb09..f375f9d8 100644 --- a/interface/src/state/state_buffer.rs +++ b/interface/src/state/state_buffer.rs @@ -2,7 +2,7 @@ use wheels::fixed_offset_layout; use crate::{compat::Pubkey, error::DlpError}; -/// PDA: `["state-buffer", account, commit_id, authority]`. +/// PDA: `["state-buffer", account, commit_seq, authority]`. /// Created by `WriteStateBuffer`. /// Closed by `CloseTerminalAccounts` after finalize, cancel, or expiry. #[derive(Clone, Debug, PartialEq, Eq)] @@ -20,8 +20,8 @@ pub struct StateBuffer { /// Delegated account whose payload is stored after this header. pub account_pubkey: Pubkey, - /// Flow-specific nonce that identifies this opened buffer. - pub commit_id: u64, + /// Commit sequence number that identifies this opened buffer. + pub commit_seq: u32, /// Hash of the finalized payload. Zero until finalized. pub data_hash: [u8; 32], diff --git a/program/src/processor/delegate.rs b/program/src/processor/delegate.rs new file mode 100644 index 00000000..63323a70 --- /dev/null +++ b/program/src/processor/delegate.rs @@ -0,0 +1,232 @@ +use delegation_program_interface as dpi; +use dpi::{ + error::DlpError, + legacy::pda::DELEGATE_BUFFER_TAG, + pda::{DELEGATION_CLUSTER_SEED, DELEGATION_STATE_SEED}, + DelegateArgs, DelegationCluster, DelegationState, UndelegationRequester, +}; +use pinocchio::{ + address::{address_eq, Address}, + cpi::{Seed, Signer}, + error::ProgramError, + sysvars::{clock::Clock, rent::Rent, Sysvar}, + AccountView, ProgramResult, +}; +use pinocchio_system::instructions as system; +use wheels::{ + layout::Decodable, require, require_eq, require_eq_keys, require_le, + require_n_accounts, require_owned_by, require_signer, +}; + +use crate::{ + legacy::processor::{ + fast::utils::pda::{create_pda, AccountSpace}, + utils::curve::is_on_curve_fast, + }, + requires::{require_uninitialized_account, StandardCtx}, +}; + +/// Delegate an account to one delegation cluster. +/// +/// Accounts: +/// 0: `[signer, writable]` delegation-state rent payer +/// 1: `[signer, writable]` delegated account, signed by its owner program +/// 2: `[]` original owner program +/// 3: `[writable]` owner-program delegate buffer PDA +/// 4: `[writable]` DelegationState PDA +/// 5: `[]` DelegationCluster PDA +/// 6: `[]` system program, required by system CPI +#[inline(never)] +pub fn process_delegate( + accounts: &[AccountView], + data: &[u8], +) -> ProgramResult { + let [ + payer, // force multi-line + delegated_account, + original_owner, + delegate_buffer, + delegation_state, + delegation_cluster, + _system_program, + ] = require_n_accounts!(accounts, 7); + + require_signer!(payer); + require_signer!(delegated_account); + require_owned_by!(delegated_account, &crate::fast::ID); + + let args = DelegateArgs::decode(data)?; + validate_delegated_account(delegated_account, original_owner, &args)?; + + let delegate_buffer_bump = [args.delegate_buffer_bump()]; + dpi::require_pda!( + delegate_buffer, + &[ + DELEGATE_BUFFER_TAG, + delegated_account.address().as_ref(), + &delegate_buffer_bump, + ], + original_owner.address(), + true + ); + + let delegation_state_bump = [args.delegation_state_bump()]; + dpi::require_pda!( + delegation_state, + &[ + DELEGATION_STATE_SEED, + delegated_account.address().as_ref(), + &delegation_state_bump, + ], + &crate::fast::ID, + true + ); + require_uninitialized_account( + delegation_state, + true, + StandardCtx::new("delegation state"), + )?; + + let delegation_cluster_data = delegation_cluster.try_borrow()?; + let delegation_cluster_state = + DelegationCluster::decode(delegation_cluster_data.as_ref())?; + let delegation_cluster_bump = [delegation_cluster_state.bump()]; + dpi::require_initialized_pda!( + delegation_cluster, + &[ + DELEGATION_CLUSTER_SEED, + delegation_cluster_state.cluster_id().as_ref(), + &delegation_cluster_bump, + ], + &crate::fast::ID, + false + ); + require_eq!( + &delegation_cluster_state.discriminator(), + &DelegationCluster::DISCRIMINATOR, + ProgramError::InvalidAccountData + ); + require!( + !delegation_cluster_state.paused(), + ProgramError::InvalidAccountData + ); + drop(delegation_cluster_data); + + let seeds = args.seeds(); + let encoded_seeds = seeds.as_bytes(); + let state_len = + DelegationState::data_len_from_encoded_seeds_len(encoded_seeds.len())?; + let delegated_account_lamports = delegated_account.lamports(); + + create_pda( + delegation_state, + &crate::fast::ID, + AccountSpace::CurrentRent(state_len), + &[Signer::from(&[ + Seed::from(DELEGATION_STATE_SEED), + Seed::from(delegated_account.address().as_ref()), + Seed::from(&delegation_state_bump), + ])], + payer, + )?; + + let mut state = DelegationState::decode_mut(delegation_state)?; + *state.discriminator_mut()? = DelegationState::DISCRIMINATOR; + *state.bump_mut()? = args.delegation_state_bump(); + *state.delegated_account_mut()? = *delegated_account.address(); + *state.delegation_cluster_mut()? = *delegation_cluster.address(); + *state.original_owner_mut()? = *original_owner.address(); + *state.rent_payer_mut()? = *payer.address(); + state.delegation_slot_mut()?.set(Clock::get()?.slot)?; + state.lamports_mut()?.set(delegated_account_lamports)?; + state.last_commit_seq_mut()?.set(0)?; + *state.undelegation_requester_mut()? = UndelegationRequester::None.value(); + *state.delegated_account_bump_mut()? = args.delegated_account_bump(); + *state.seed_count_mut()? = seeds.len() as u8; + state + .encoded_seeds_mut()? + .extend_from_slice(encoded_seeds)?; + + if !delegate_buffer.is_data_empty() { + require_eq!( + delegate_buffer.data_len(), + delegated_account.data_len(), + ProgramError::InvalidAccountData + ); + delegated_account + .try_borrow_mut()? + .copy_from_slice(delegate_buffer.try_borrow()?.as_ref()); + } + + // Preserve the original zero-lamport state in DelegationState, then fund + // an empty delegated account enough to keep it alive on the base layer. + if delegated_account_lamports == 0 && delegated_account.is_data_empty() { + system::Transfer { + from: payer, + to: delegated_account, + lamports: Rent::get()?.try_minimum_balance(0)?, + } + .invoke()?; + } + + Ok(()) +} + +fn validate_delegated_account( + delegated_account: &AccountView, + original_owner: &AccountView, + args: &dpi::DelegateArgsView<'_>, +) -> ProgramResult { + let seeds = args.seeds(); + require_le!( + seeds.len(), + DelegationState::MAX_SEEDS, + DlpError::TooManySeeds + ); + + if is_on_curve_fast(delegated_account.address()) { + require_eq_keys!( + original_owner.address(), + &pinocchio_system::ID, + ProgramError::IncorrectProgramId + ); + require_eq!(seeds.len(), 0, ProgramError::InvalidInstructionData); + require_eq!( + args.delegated_account_bump(), + 0, + ProgramError::InvalidInstructionData + ); + return Ok(()); + } + + require!(!seeds.is_empty(), ProgramError::InvalidInstructionData); + + let mut pda_seeds: [&[u8]; DelegationState::MAX_SEEDS + 1] = + [&[]; DelegationState::MAX_SEEDS + 1]; + for (index, seed) in seeds.iter().enumerate() { + require_le!( + seed.as_slice().len(), + DelegationState::MAX_SEED_LEN, + ProgramError::MaxSeedLengthExceeded + ); + pda_seeds[index] = seed.as_slice(); + } + let delegated_account_bump = [args.delegated_account_bump()]; + pda_seeds[seeds.len()] = &delegated_account_bump; + + let owner = if address_eq(original_owner.address(), &pinocchio_system::ID) { + &crate::fast::ID + } else { + original_owner.address() + }; + let derived_account = + Address::create_program_address(&pda_seeds[..=seeds.len()], owner) + .map_err(|_| ProgramError::InvalidSeeds)?; + require_eq_keys!( + &derived_account, + delegated_account.address(), + ProgramError::InvalidSeeds + ); + + Ok(()) +} diff --git a/program/src/processor/fraud_proofs/approve_commitment.rs b/program/src/processor/fraud_proofs/approve_commitment.rs index bf83c46e..1c586a03 100644 --- a/program/src/processor/fraud_proofs/approve_commitment.rs +++ b/program/src/processor/fraud_proofs/approve_commitment.rs @@ -149,13 +149,13 @@ fn validate_pending_commitment( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending_commitment.commit_id().to_le_bytes(); + let commit_seq_bytes = pending_commitment.commit_seq().to_le_bytes(); require_initialized_pda( pending_commitment_account, &[ PENDING_COMMITMENT_SEED, pending_commitment.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, ], &crate::fast::ID, true, diff --git a/program/src/processor/fraud_proofs/challenger_reveal.rs b/program/src/processor/fraud_proofs/challenger_reveal.rs index b36627cd..67e26212 100644 --- a/program/src/processor/fraud_proofs/challenger_reveal.rs +++ b/program/src/processor/fraud_proofs/challenger_reveal.rs @@ -99,7 +99,7 @@ pub fn process_challenger_reveal( operator_state_buffer, pending.operator_identity(), pending.account_pubkey(), - pending.commit_id(), + pending.commit_seq(), pending.data_hash(), )?; @@ -108,7 +108,7 @@ pub fn process_challenger_reveal( challenger_state_buffer, &challenger_identity, pending.account_pubkey(), - pending.commit_id(), + pending.commit_seq(), args.data_hash(), )?; @@ -229,13 +229,13 @@ fn validate_pending_commitment( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending.commit_id().to_le_bytes(); + let commit_seq_bytes = pending.commit_seq().to_le_bytes(); require_initialized_pda( pending_commitment, &[ PENDING_COMMITMENT_SEED, pending.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, ], &crate::fast::ID, true, @@ -276,13 +276,13 @@ fn validate_challenge( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending.commit_id().to_le_bytes(); + let commit_seq_bytes = pending.commit_seq().to_le_bytes(); require_initialized_pda( challenge, &[ CHALLENGE_SEED, pending.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, challenger.address().as_ref(), ], &crate::fast::ID, @@ -328,16 +328,16 @@ fn validate_state_buffer( state_buffer: &AccountView, authority: &dpi::compat::Pubkey, account_pubkey: &dpi::compat::Pubkey, - commit_id: u64, + commit_seq: u32, expected_data_hash: &[u8; 32], ) -> ProgramResult { - let commit_id_bytes = commit_id.to_le_bytes(); + let commit_seq_bytes = commit_seq.to_le_bytes(); require_initialized_pda( state_buffer, &[ STATE_BUFFER_SEED, account_pubkey.as_ref(), - &commit_id_bytes, + &commit_seq_bytes, authority.as_ref(), ], &crate::fast::ID, @@ -358,8 +358,8 @@ fn validate_state_buffer( ProgramError::InvalidAccountData ); require_eq!( - state.commit_id(), - commit_id, + state.commit_seq(), + commit_seq, ProgramError::InvalidInstructionData ); require_eq!( @@ -440,7 +440,7 @@ fn challenge_hash( pending.operator_identity().as_ref(), challenge.challenger_identity().as_ref(), pending.account_pubkey().as_ref(), - &pending.commit_id().to_le_bytes(), + &pending.commit_seq().to_le_bytes(), &args.lamports().to_le_bytes(), args.owner().as_ref(), args.data_hash(), diff --git a/program/src/processor/fraud_proofs/finalize_commitment.rs b/program/src/processor/fraud_proofs/finalize_commitment.rs index 4f7a6f33..ca5c4d19 100644 --- a/program/src/processor/fraud_proofs/finalize_commitment.rs +++ b/program/src/processor/fraud_proofs/finalize_commitment.rs @@ -198,13 +198,13 @@ pub fn process_finalize_commitment( true, )?; - let commit_id_bytes = pending.commit_id().to_le_bytes(); + let commit_seq_bytes = pending.commit_seq().to_le_bytes(); require_initialized_pda( state_buffer, &[ STATE_BUFFER_SEED, delegated_account.address().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, state_authority.address().as_ref(), ], &crate::fast::ID, @@ -217,14 +217,14 @@ pub fn process_finalize_commitment( delegation_cluster, selected_state.owner, )?; - validate_delegation_metadata(delegation_metadata, pending.commit_id())?; + validate_delegation_metadata(delegation_metadata, pending.commit_seq())?; let state_buffer_data = state_buffer.try_borrow()?; let raw_state = validate_state_buffer( state_buffer_data.as_ref(), state_authority, delegated_account, - pending.commit_id(), + pending.commit_seq(), selected_state.data_hash, )?; @@ -256,7 +256,7 @@ pub fn process_finalize_commitment( { let mut metadata = DelegationMetadataFast::from_account(delegation_metadata)?; - metadata.set_last_commit_id(pending.commit_id()); + metadata.set_last_commit_id(u64::from(pending.commit_seq())); } drop(delegation_cluster_data); @@ -276,13 +276,13 @@ fn validate_pending_commitment( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending.commit_id().to_le_bytes(); + let commit_seq_bytes = pending.commit_seq().to_le_bytes(); require_initialized_pda( pending_commitment, &[ PENDING_COMMITMENT_SEED, pending.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, ], &crate::fast::ID, true, @@ -395,14 +395,18 @@ fn validate_delegation_record( fn validate_delegation_metadata( delegation_metadata: &AccountView, - commit_id: u64, + commit_seq: u32, ) -> ProgramResult { let metadata = DelegationMetadataFast::from_account(delegation_metadata)?; - let expected_commit_id = metadata + let expected_commit_seq = metadata .last_commit_id() .checked_add(1) .ok_or(DlpError::Overflow)?; - require_eq!(commit_id, expected_commit_id, DlpError::NonceOutOfOrder); + require_eq!( + u64::from(commit_seq), + expected_commit_seq, + DlpError::NonceOutOfOrder + ); match metadata.undelegation_requester()? { UndelegationRequester::None => Ok(()), @@ -419,7 +423,7 @@ fn validate_state_buffer<'a>( data: &'a [u8], state_authority: &AccountView, delegated_account: &AccountView, - commit_id: u64, + commit_seq: u32, expected_data_hash: &[u8; 32], ) -> Result<&'a [u8], ProgramError> { let state = StateBuffer::decode(data)?; @@ -438,8 +442,8 @@ fn validate_state_buffer<'a>( DlpError::InvalidDelegatedAccount ); require_eq!( - state.commit_id(), - commit_id, + state.commit_seq(), + commit_seq, ProgramError::InvalidInstructionData ); require_eq!( @@ -533,13 +537,13 @@ fn validate_challenge( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending.commit_id().to_le_bytes(); + let commit_seq_bytes = pending.commit_seq().to_le_bytes(); require_initialized_pda( challenge_account, &[ CHALLENGE_SEED, pending.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, challenge.challenger_identity().as_ref(), ], &crate::fast::ID, diff --git a/program/src/processor/fraud_proofs/propose_commitment.rs b/program/src/processor/fraud_proofs/propose_commitment.rs index c977e743..7dc49c2a 100644 --- a/program/src/processor/fraud_proofs/propose_commitment.rs +++ b/program/src/processor/fraud_proofs/propose_commitment.rs @@ -102,13 +102,13 @@ pub fn process_propose_commitment( false, "operator bond", )?; - let commit_id_bytes = args.commit_id().to_le_bytes(); + let commit_seq_bytes = args.commit_seq().to_le_bytes(); require_initialized_pda( state_buffer, &[ STATE_BUFFER_SEED, delegated_account.address().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, current_validator.address().as_ref(), ], &crate::fast::ID, @@ -154,7 +154,7 @@ pub fn process_propose_commitment( &state_buffer_state, current_validator, delegated_account, - args.commit_id(), + args.commit_seq(), )?; drop(state_buffer_data); @@ -179,7 +179,7 @@ pub fn process_propose_commitment( let delegation_metadata = DelegationMetadataRef::from_account(delegation_metadata)?; require_eq!( - args.commit_id(), + u64::from(args.commit_seq()), delegation_metadata .last_commit_id() .checked_add(1) @@ -238,7 +238,7 @@ pub fn process_propose_commitment( delegation_cluster: delegation_cluster.address(), operator_identity: current_validator.address(), account_pubkey: delegated_account.address(), - commit_id: args.commit_id(), + commit_seq: args.commit_seq(), delegation_record: delegation_record.address(), da_pointer_hash: args.da_pointer_hash(), account_state_hash: &account_state_hash, @@ -253,7 +253,7 @@ pub fn process_propose_commitment( operator_identity: *current_validator.address(), operator_bond: operator_bond.address().to_bytes().into(), account_pubkey: delegated_account.address().to_bytes().into(), - commit_id: args.commit_id(), + commit_seq: args.commit_seq(), delegation_record: delegation_record.address().to_bytes().into(), da_pointer_hash: *args.da_pointer_hash(), account_state_hash, @@ -279,7 +279,7 @@ pub fn process_propose_commitment( active_challenge: None, resolved_state_source: None, er_slot: args.er_slot(), - _pad_before_selected_verifiers: [0; 5], + _pad_before_selected_verifiers: [0; 9], selected_verifiers, }; @@ -290,7 +290,7 @@ pub fn process_propose_commitment( &[ PENDING_COMMITMENT_SEED, delegated_account.address().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, ], &crate::fast::ID, true, @@ -304,7 +304,7 @@ pub fn process_propose_commitment( &[Signer::from(&[ Seed::from(PENDING_COMMITMENT_SEED), Seed::from(delegated_account.address().as_ref()), - Seed::from(&commit_id_bytes), + Seed::from(&commit_seq_bytes), Seed::from(&[pending_commitment_bump]), ])], current_validator, @@ -373,7 +373,7 @@ fn validate_state_buffer( state_buffer: &dpi::StateBufferView<'_>, operator: &AccountView, delegated_account: &AccountView, - commit_id: u64, + commit_seq: u32, ) -> Result<[u8; 32], ProgramError> { if state_buffer.discriminator() != StateBuffer::DISCRIMINATOR { return Err(ProgramError::InvalidAccountData); @@ -389,8 +389,8 @@ fn validate_state_buffer( ProgramError::InvalidAccountData ); require_eq!( - state_buffer.commit_id(), - commit_id, + state_buffer.commit_seq(), + commit_seq, ProgramError::InvalidInstructionData ); require_eq!( @@ -468,7 +468,7 @@ struct StateCommitmentHashInput<'a> { delegation_cluster: &'a Address, operator_identity: &'a Address, account_pubkey: &'a Address, - commit_id: u64, + commit_seq: u32, delegation_record: &'a Address, da_pointer_hash: &'a [u8; 32], account_state_hash: &'a [u8; 32], @@ -482,7 +482,7 @@ fn state_commitment_hash(input: StateCommitmentHashInput<'_>) -> [u8; 32] { input.delegation_cluster.as_ref(), input.operator_identity.as_ref(), input.account_pubkey.as_ref(), - &input.commit_id.to_le_bytes(), + &input.commit_seq.to_le_bytes(), input.delegation_record.as_ref(), input.da_pointer_hash, input.account_state_hash, diff --git a/program/src/processor/fraud_proofs/raise_challenge.rs b/program/src/processor/fraud_proofs/raise_challenge.rs index 5427ae2e..0849a78a 100644 --- a/program/src/processor/fraud_proofs/raise_challenge.rs +++ b/program/src/processor/fraud_proofs/raise_challenge.rs @@ -81,13 +81,13 @@ pub fn process_raise_challenge( )?; let pending_account_pubkey = *pending_state.account_pubkey(); - let commit_id_bytes = pending_state.commit_id().to_le_bytes(); + let commit_seq_bytes = pending_state.commit_seq().to_le_bytes(); let challenge_bump = require_uninitialized_pda( challenge, &[ CHALLENGE_SEED, pending_account_pubkey.as_ref(), - &commit_id_bytes, + &commit_seq_bytes, challenger.address().as_ref(), ], &crate::fast::ID, @@ -128,7 +128,7 @@ pub fn process_raise_challenge( &[Signer::from(&[ Seed::from(CHALLENGE_SEED), Seed::from(pending_account_pubkey.as_ref()), - Seed::from(&commit_id_bytes), + Seed::from(&commit_seq_bytes), Seed::from(challenger.address().as_ref()), Seed::from(&[challenge_bump]), ])], @@ -194,13 +194,13 @@ fn validate_pending_commitment( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending_commitment.commit_id().to_le_bytes(); + let commit_seq_bytes = pending_commitment.commit_seq().to_le_bytes(); require_initialized_pda( pending_commitment_account, &[ PENDING_COMMITMENT_SEED, pending_commitment.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, ], &crate::fast::ID, true, diff --git a/program/src/processor/fraud_proofs/resolve_dispute.rs b/program/src/processor/fraud_proofs/resolve_dispute.rs index 0862aa9c..fb6fad36 100644 --- a/program/src/processor/fraud_proofs/resolve_dispute.rs +++ b/program/src/processor/fraud_proofs/resolve_dispute.rs @@ -254,13 +254,13 @@ fn validate_pending_commitment( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending.commit_id().to_le_bytes(); + let commit_seq_bytes = pending.commit_seq().to_le_bytes(); require_initialized_pda( pending_commitment, &[ PENDING_COMMITMENT_SEED, pending.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, ], &crate::fast::ID, true, @@ -300,13 +300,13 @@ fn validate_challenge( return Err(ProgramError::InvalidAccountData); } - let commit_id_bytes = pending.commit_id().to_le_bytes(); + let commit_seq_bytes = pending.commit_seq().to_le_bytes(); require_initialized_pda( challenge, &[ CHALLENGE_SEED, pending.account_pubkey().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, challenger.address().as_ref(), ], &crate::fast::ID, diff --git a/program/src/processor/fraud_proofs/write_state_buffer.rs b/program/src/processor/fraud_proofs/write_state_buffer.rs index 7d025459..54363e59 100644 --- a/program/src/processor/fraud_proofs/write_state_buffer.rs +++ b/program/src/processor/fraud_proofs/write_state_buffer.rs @@ -50,11 +50,11 @@ pub fn process_write_state_buffer( let args = WriteStateBufferArgs::decode(data)?; let (offset, write_end) = validate_args(&args)?; - let commit_id_bytes = args.commit_id().to_le_bytes(); + let commit_seq_bytes = args.commit_seq().to_le_bytes(); let state_buffer_seeds = [ STATE_BUFFER_SEED, delegated_account.address().as_ref(), - &commit_id_bytes, + &commit_seq_bytes, authority.address().as_ref(), ]; @@ -98,7 +98,7 @@ pub fn process_write_state_buffer( &[Signer::from(&[ Seed::from(STATE_BUFFER_SEED), Seed::from(delegated_account.address().as_ref()), - Seed::from(&commit_id_bytes), + Seed::from(&commit_seq_bytes), Seed::from(authority.address().as_ref()), Seed::from(&[state_buffer_bump]), ])], @@ -109,7 +109,7 @@ pub fn process_write_state_buffer( discriminator: StateBuffer::DISCRIMINATOR, authority: *authority.address(), account_pubkey: *delegated_account.address(), - commit_id: args.commit_id(), + commit_seq: args.commit_seq(), data_hash: [0; 32], total_len: args.total_len(), finalized: false, @@ -274,8 +274,8 @@ fn validate_state_buffer( ProgramError::InvalidAccountData ); require_eq!( - state.commit_id(), - args.commit_id(), + state.commit_seq(), + args.commit_seq(), ProgramError::InvalidInstructionData ); require_eq!( diff --git a/program/src/processor/mod.rs b/program/src/processor/mod.rs index ed630dbb..11fbba97 100644 --- a/program/src/processor/mod.rs +++ b/program/src/processor/mod.rs @@ -1,8 +1,10 @@ use delegation_program_interface as dpi; mod bootstrap; +mod delegate; mod fraud_proofs; pub use bootstrap::*; +pub use delegate::*; use dpi::DlpV2Instruction; pub use fraud_proofs::*; use pinocchio::{AccountView, ProgramResult}; @@ -50,5 +52,6 @@ pub fn process_instruction( DlpV2Instruction::ResolveDispute => { process_resolve_dispute(accounts, data) } + DlpV2Instruction::Delegate => process_delegate(accounts, data), } } diff --git a/program/tests/test_approve_commitment.rs b/program/tests/test_approve_commitment.rs index 800195ba..c340eb79 100644 --- a/program/tests/test_approve_commitment.rs +++ b/program/tests/test_approve_commitment.rs @@ -56,7 +56,7 @@ async fn test_approve_commitment() { &env.verifiers[0], env.delegation_cluster, env.delegated_account, - args.commit_id, + args.commit_seq, ) .await .unwrap(); @@ -64,7 +64,7 @@ async fn test_approve_commitment() { let pending_commitment_data = read_pending_commitment_data( &mut env.banks, env.delegated_account, - args.commit_id, + args.commit_seq, ) .await; let pending_commitment = @@ -103,7 +103,7 @@ async fn test_approve_commitment_duplicate_is_noop() { &env.verifiers[0], env.delegation_cluster, env.delegated_account, - args.commit_id, + args.commit_seq, ) .await .unwrap(); @@ -113,7 +113,7 @@ async fn test_approve_commitment_duplicate_is_noop() { &env.verifiers[0], env.delegation_cluster, env.delegated_account, - args.commit_id, + args.commit_seq, ) .await .unwrap(); @@ -121,7 +121,7 @@ async fn test_approve_commitment_duplicate_is_noop() { let pending_commitment_data = read_pending_commitment_data( &mut env.banks, env.delegated_account, - args.commit_id, + args.commit_seq, ) .await; let pending_commitment = @@ -157,7 +157,7 @@ async fn test_approve_commitment_fails_with_wrong_verifier() { &env.verifiers[1], env.delegation_cluster, env.delegated_account, - args.commit_id, + args.commit_seq, ) .await .is_err()); @@ -183,7 +183,7 @@ async fn test_approve_commitment_fails_without_verifier_signature() { env.verifiers[0].pubkey(), env.delegation_cluster, env.delegated_account, - args.commit_id, + args.commit_seq, ); ix.accounts[0].is_signer = false; @@ -218,7 +218,7 @@ async fn test_approve_commitment_fails_with_instruction_data() { env.verifiers[0].pubkey(), env.delegation_cluster, env.delegated_account, - args.commit_id, + args.commit_seq, ); ix.data.push(1); @@ -355,7 +355,7 @@ async fn setup_approve_commitment_env( &operator, delegated_account, WriteStateBufferArgs { - commit_id: propose_commitment_args().commit_id, + commit_seq: propose_commitment_args().commit_seq, total_len: state_data().len() as u32, offset: 0, chunk: state_data(), @@ -456,7 +456,7 @@ async fn register_and_add_verifier( fn propose_commitment_args() -> ProposeCommitmentArgs { ProposeCommitmentArgs { - commit_id: 1, + commit_seq: 1, lamports: 1_000, owner: Pubkey::new_from_array([7; 32]), da_pointer_hash: [9; 32], @@ -527,7 +527,7 @@ async fn approve( verifier: &Keypair, delegation_cluster: Pubkey, delegated_account: Pubkey, - commit_id: u64, + commit_seq: u32, ) -> Result<(), solana_program_test::BanksClientError> { let latest_blockhash: Hash = banks.get_latest_blockhash().await.unwrap(); let blockhash = banks @@ -538,7 +538,7 @@ async fn approve( verifier.pubkey(), delegation_cluster, delegated_account, - commit_id, + commit_seq, ); let tx = Transaction::new_signed_with_payer( &[ix], @@ -553,10 +553,10 @@ async fn approve( async fn read_pending_commitment_data( banks: &mut BanksClient, delegated_account: Pubkey, - commit_id: u64, + commit_seq: u32, ) -> Vec { let pending_commitment_account = banks - .get_account(pending_commitment_pda(&delegated_account, commit_id)) + .get_account(pending_commitment_pda(&delegated_account, commit_seq)) .await .unwrap() .unwrap(); diff --git a/program/tests/test_challenger_reveal.rs b/program/tests/test_challenger_reveal.rs index 511cad62..63b7be46 100644 --- a/program/tests/test_challenger_reveal.rs +++ b/program/tests/test_challenger_reveal.rs @@ -102,7 +102,7 @@ async fn test_challenger_reveal_matching_state_penalizes_and_reopens_commitment( challenge.challenger_state_buffer, state_buffer_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, &env.challenger.pubkey() ) ); @@ -271,12 +271,12 @@ async fn test_challenger_reveal_fails_with_wrong_challenger_buffer() { env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, reveal_args, ); ix.accounts[4].pubkey = state_buffer_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, &env.operator.pubkey(), ); @@ -296,11 +296,11 @@ async fn test_challenger_reveal_fails_with_unfinalized_challenger_buffer() { env.committed_owner, account_data_hash(&expected_data), ); - let commit_id = env.commit_id; + let commit_seq = env.commit_seq; write_challenger_state_buffer_args( &mut env, WriteStateBufferArgs { - commit_id, + commit_seq, total_len: expected_data.len() as u32, offset: 0, chunk: expected_data[..2].to_vec(), @@ -339,7 +339,7 @@ async fn test_challenger_reveal_fails_with_wrong_fee_vault() { env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, reveal_args, ); ix.accounts[6].pubkey = env.challenger.pubkey(); @@ -358,7 +358,7 @@ struct ChallengerRevealEnv { delegated_account: Pubkey, committed_owner: Pubkey, committed_lamports: u64, - commit_id: u64, + commit_seq: u32, challenge_config: ChallengeConfig, operator_state_data: Vec, } @@ -378,7 +378,7 @@ async fn setup_challenger_reveal_env() -> ChallengerRevealEnv { let delegation_cluster = dpi::pda::delegation_cluster_pda(&cluster_identity.pubkey()); let committed_owner = Pubkey::new_unique(); - let commit_id = 1; + let commit_seq = 1; let operator_state_data = vec![9, 8, 7, 6, 5]; let record_lamports = LAMPORTS_PER_SOL; let committed_lamports = LAMPORTS_PER_SOL; @@ -464,7 +464,7 @@ async fn setup_challenger_reveal_env() -> ChallengerRevealEnv { &operator, delegated_account, WriteStateBufferArgs { - commit_id, + commit_seq, total_len: operator_state_data.len() as u32, offset: 0, chunk: operator_state_data.clone(), @@ -482,7 +482,7 @@ async fn setup_challenger_reveal_env() -> ChallengerRevealEnv { delegated_account, committed_owner, committed_lamports, - commit_id, + commit_seq, challenge_config, operator_state_data, } @@ -496,7 +496,7 @@ async fn propose( env.delegation_cluster, env.delegated_account, ProposeCommitmentArgs { - commit_id: env.commit_id, + commit_seq: env.commit_seq, lamports: env.committed_lamports, owner: env.committed_owner, da_pointer_hash: [9; 32], @@ -517,7 +517,7 @@ async fn raise_for_reveal( env.challenger.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, RaiseChallengeArgs { state_commitment_hash: pending.state_commitment_hash, challenge_hash: challenge_hash( @@ -525,7 +525,7 @@ async fn raise_for_reveal( env.operator.pubkey(), env.challenger.pubkey(), env.delegated_account, - env.commit_id, + env.commit_seq, reveal_args, ), stake_lamports, @@ -544,7 +544,7 @@ async fn reveal( env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, args, ); @@ -558,7 +558,7 @@ async fn write_challenger_state_buffer( write_challenger_state_buffer_args( env, WriteStateBufferArgs { - commit_id: env.commit_id, + commit_seq: env.commit_seq, total_len: data.len() as u32, offset: 0, chunk: data, @@ -684,7 +684,7 @@ async fn read_pending_commitment( .banks_client .get_account(pending_commitment_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, )) .await .unwrap() @@ -790,7 +790,7 @@ fn valid_reveal_args( fn challenge_address(env: &ChallengerRevealEnv) -> Pubkey { challenge_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, &env.challenger.pubkey(), ) } @@ -805,7 +805,7 @@ fn challenge_hash( operator: Pubkey, challenger: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, args: &ChallengerRevealArgs, ) -> [u8; 32] { solana_sha256_hasher::hashv(&[ @@ -814,7 +814,7 @@ fn challenge_hash( operator.as_ref(), challenger.as_ref(), account.as_ref(), - &commit_id.to_le_bytes(), + &commit_seq.to_le_bytes(), &args.lamports.to_le_bytes(), args.owner.as_ref(), &args.data_hash, diff --git a/program/tests/test_finalize_commitment.rs b/program/tests/test_finalize_commitment.rs index af254148..28d51c07 100644 --- a/program/tests/test_finalize_commitment.rs +++ b/program/tests/test_finalize_commitment.rs @@ -70,7 +70,10 @@ async fn test_finalize_commitment() { assert_eq!(delegation_record.lamports, env.committed_lamports); let delegation_metadata = read_delegation_metadata(&mut env).await; - assert_eq!(delegation_metadata.last_commit_id, env.commit_id); + assert_eq!( + delegation_metadata.last_commit_id, + u64::from(env.commit_seq) + ); } #[tokio::test] @@ -111,7 +114,7 @@ async fn test_finalize_commitment_fails_with_wrong_operator() { wrong_operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); assert!(process_ix(&mut env.context, ix, &[&wrong_operator]) @@ -226,7 +229,7 @@ struct FinalizeCommitmentEnv { delegated_account: Pubkey, committed_owner: Pubkey, committed_lamports: u64, - commit_id: u64, + commit_seq: u32, final_state_data: Vec, } @@ -248,7 +251,7 @@ async fn setup_finalize_commitment_env( dpi::pda::delegation_cluster_pda(&cluster_identity.pubkey()); let account_owner = Pubkey::new_unique(); let committed_owner = account_owner; - let commit_id = 1; + let commit_seq = 1; let final_state_data = vec![9, 8, 7, 6, 5]; add_lamport_account(&mut program_test, authority.pubkey()); @@ -330,7 +333,7 @@ async fn setup_finalize_commitment_env( &operator, delegated_account, WriteStateBufferArgs { - commit_id, + commit_seq, total_len: final_state_data.len() as u32, offset: 0, chunk: final_state_data.clone(), @@ -347,7 +350,7 @@ async fn setup_finalize_commitment_env( delegated_account, committed_owner, committed_lamports, - commit_id, + commit_seq, final_state_data, } } @@ -360,7 +363,7 @@ async fn propose( env.delegation_cluster, env.delegated_account, ProposeCommitmentArgs { - commit_id: env.commit_id, + commit_seq: env.commit_seq, lamports: env.committed_lamports, owner: env.committed_owner, da_pointer_hash: [9; 32], @@ -378,7 +381,7 @@ async fn approve( env.verifier.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); process_ix(&mut env.context, ix, &[&env.verifier]).await @@ -391,7 +394,7 @@ async fn finalize( env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); process_ix(&mut env.context, ix, &[&env.operator]).await @@ -506,7 +509,7 @@ async fn read_pending_commitment( .banks_client .get_account(pending_commitment_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, )) .await .unwrap() diff --git a/program/tests/test_primary_delegate.rs b/program/tests/test_primary_delegate.rs new file mode 100644 index 00000000..1ac4a668 --- /dev/null +++ b/program/tests/test_primary_delegate.rs @@ -0,0 +1,362 @@ +use delegation_program::solana_program; +use delegation_program_interface as dpi; +use dpi::{ + instruction_builder::delegate, + legacy::pda::DELEGATE_BUFFER_TAG, + pda::{delegation_state_pda, DELEGATION_STATE_SEED}, + DelegateArgs, DelegationSeed, DelegationState, UndelegationRequester, +}; +use solana_program::{ + account_info::AccountInfo, + entrypoint::ProgramResult, + instruction::{AccountMeta, Instruction}, + native_token::LAMPORTS_PER_SOL, + program::invoke_signed, + program_error::ProgramError, + pubkey::Pubkey, + rent::Rent, +}; +use solana_program_test::{ + processor, BanksClient, BanksClientError, ProgramTest, +}; +use solana_sdk::{ + account::Account, + instruction::InstructionError, + signature::{Keypair, Signer}, + transaction::{Transaction, TransactionError}, +}; +use solana_sdk_ids::system_program; +use wheels::layout::Decodable; + +#[allow(dead_code)] +mod fixtures; + +use crate::fixtures::v2::{direct_cluster_args, initialize_delegation_cluster}; + +const OWNER_PROGRAM_ID: Pubkey = + solana_sdk::pubkey!("4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4EVRtnYHk"); +const DELEGATED_ACCOUNT_SEED: &[u8] = b"primary-delegated"; +const DELEGATED_ACCOUNT_DATA: &[u8] = b"primary account data"; +const DELEGATE_BUFFER_DATA: &[u8] = b"delegate buffer data"; + +struct DelegateTestContext { + banks: BanksClient, + payer: Keypair, + delegated_account: Pubkey, + delegated_account_bump: u8, + delegation_cluster: Pubkey, +} + +#[tokio::test] +async fn test_delegate_creates_cluster_bound_delegation_state() { + let context = setup_delegate_test().await; + let delegation_state = + delegation_state_pda(&context.delegated_account.to_bytes().into()); + let (_, expected_state_bump) = Pubkey::find_program_address( + &[DELEGATION_STATE_SEED, context.delegated_account.as_ref()], + &dpi::id(), + ); + + let result = submit_delegate( + &context, + DelegateArgs::new( + context.delegated_account_bump, + vec![DelegationSeed { + value: DELEGATED_ACCOUNT_SEED.to_vec(), + }], + ), + ) + .await; + assert!(result.is_ok()); + + let delegated_account = context + .banks + .get_account(context.delegated_account) + .await + .unwrap() + .unwrap(); + assert_eq!(delegated_account.owner, dpi::id()); + assert_eq!(delegated_account.data, DELEGATE_BUFFER_DATA); + + let state_account = context + .banks + .get_account(delegation_state) + .await + .unwrap() + .unwrap(); + let state = DelegationState::decode(&state_account.data).unwrap(); + let seeds = state.seeds().unwrap(); + + assert_eq!(state.discriminator(), DelegationState::DISCRIMINATOR); + assert_eq!(state.bump(), expected_state_bump); + assert_eq!(*state.delegated_account(), context.delegated_account); + assert_eq!(*state.delegation_cluster(), context.delegation_cluster); + assert_eq!(*state.original_owner(), OWNER_PROGRAM_ID); + assert_eq!(*state.rent_payer(), context.payer.pubkey()); + assert_eq!(state.lamports(), delegated_account.lamports); + assert_eq!(state.last_commit_seq(), 0); + assert_eq!( + state.undelegation_requester(), + UndelegationRequester::None.value() + ); + assert_eq!( + state.delegated_account_bump(), + context.delegated_account_bump + ); + assert_eq!(seeds.len(), 1); + assert_eq!(seeds.get(0).unwrap().as_slice(), DELEGATED_ACCOUNT_SEED); + assert_eq!( + state_account.lamports, + Rent::default().minimum_balance(state_account.data.len()) + ); +} + +#[tokio::test] +async fn test_delegate_rejects_wrong_delegated_account_seeds() { + let context = setup_delegate_test().await; + let result = submit_delegate( + &context, + DelegateArgs::new( + context.delegated_account_bump, + vec![DelegationSeed { + value: b"wrong-seed".to_vec(), + }], + ), + ) + .await; + + assert!( + matches!( + &result, + Err(BanksClientError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::InvalidSeeds + ) + )) + ), + "unexpected result: {result:?}" + ); + assert!(context + .banks + .get_account(delegation_state_pda( + &context.delegated_account.to_bytes().into() + )) + .await + .unwrap() + .is_none()); +} + +#[tokio::test] +async fn test_delegate_rejects_non_system_owner_for_on_curve_account() { + let mut program_test = ProgramTest::default(); + program_test.prefer_bpf(true); + program_test.add_program("delegation_program", dpi::ID, None); + + let delegated_account = Keypair::new(); + let config_authority = Keypair::new(); + program_test.add_account( + delegated_account.pubkey(), + Account { + lamports: Rent::default().minimum_balance(0), + data: vec![], + owner: dpi::id(), + executable: false, + rent_epoch: 0, + }, + ); + program_test.add_account( + config_authority.pubkey(), + Account { + lamports: LAMPORTS_PER_SOL, + data: vec![], + owner: system_program::id(), + executable: false, + rent_epoch: 0, + }, + ); + + let (banks, payer, blockhash) = program_test.start().await; + let delegation_cluster = initialize_delegation_cluster( + &banks, + &payer, + &config_authority, + blockhash, + direct_cluster_args(Pubkey::new_unique()), + ) + .await; + let instruction = delegate( + payer.pubkey(), + delegated_account.pubkey(), + OWNER_PROGRAM_ID, + delegation_cluster, + DelegateArgs::new(0, vec![]), + ) + .unwrap(); + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&payer.pubkey()), + &[&payer, &delegated_account], + banks.get_latest_blockhash().await.unwrap(), + ); + let result = banks.process_transaction(transaction).await; + + assert!( + matches!( + &result, + Err(BanksClientError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::IncorrectProgramId + ) + )) + ), + "unexpected result: {result:?}" + ); +} + +async fn setup_delegate_test() -> DelegateTestContext { + let mut program_test = ProgramTest::default(); + program_test.prefer_bpf(true); + program_test.add_program("delegation_program", dpi::ID, None); + program_test.prefer_bpf(false); + program_test.add_program( + "primary-delegate-owner", + OWNER_PROGRAM_ID, + processor!(owner_program_processor), + ); + program_test.prefer_bpf(true); + + let (delegated_account, delegated_account_bump) = + Pubkey::find_program_address( + &[DELEGATED_ACCOUNT_SEED], + &OWNER_PROGRAM_ID, + ); + let delegate_buffer = Pubkey::find_program_address( + &[DELEGATE_BUFFER_TAG, delegated_account.as_ref()], + &OWNER_PROGRAM_ID, + ) + .0; + let config_authority = Keypair::new(); + + program_test.add_account( + delegated_account, + Account { + lamports: Rent::default() + .minimum_balance(DELEGATED_ACCOUNT_DATA.len()), + data: DELEGATED_ACCOUNT_DATA.to_vec(), + owner: dpi::id(), + executable: false, + rent_epoch: 0, + }, + ); + program_test.add_account( + delegate_buffer, + Account { + lamports: Rent::default() + .minimum_balance(DELEGATE_BUFFER_DATA.len()), + data: DELEGATE_BUFFER_DATA.to_vec(), + owner: OWNER_PROGRAM_ID, + executable: false, + rent_epoch: 0, + }, + ); + program_test.add_account( + config_authority.pubkey(), + Account { + lamports: LAMPORTS_PER_SOL, + data: vec![], + owner: system_program::id(), + executable: false, + rent_epoch: 0, + }, + ); + + let (banks, payer, blockhash) = program_test.start().await; + let delegation_cluster = initialize_delegation_cluster( + &banks, + &payer, + &config_authority, + blockhash, + direct_cluster_args(Pubkey::new_unique()), + ) + .await; + + DelegateTestContext { + banks, + payer, + delegated_account, + delegated_account_bump, + delegation_cluster, + } +} + +async fn submit_delegate( + context: &DelegateTestContext, + args: DelegateArgs, +) -> Result<(), solana_program_test::BanksClientError> { + let inner = delegate( + context.payer.pubkey(), + context.delegated_account, + OWNER_PROGRAM_ID, + context.delegation_cluster, + args, + ) + .unwrap(); + let mut accounts = inner.accounts; + accounts[1].is_signer = false; + accounts.push(AccountMeta::new_readonly(dpi::id(), false)); + let outer = Instruction { + program_id: OWNER_PROGRAM_ID, + accounts, + data: inner.data, + }; + let transaction = Transaction::new_signed_with_payer( + &[outer], + Some(&context.payer.pubkey()), + &[&context.payer], + context.banks.get_latest_blockhash().await.unwrap(), + ); + + context.banks.process_transaction(transaction).await +} + +fn owner_program_processor( + program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + let [payer, delegated_account, original_owner, delegate_buffer, delegation_state, delegation_cluster, system_program, dlp_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + if original_owner.key != program_id || dlp_program.key != &dpi::id() { + return Err(ProgramError::IncorrectProgramId); + } + + let instruction = Instruction { + program_id: dpi::id(), + accounts: vec![ + AccountMeta::new(*payer.key, true), + AccountMeta::new(*delegated_account.key, true), + AccountMeta::new_readonly(*original_owner.key, false), + AccountMeta::new(*delegate_buffer.key, false), + AccountMeta::new(*delegation_state.key, false), + AccountMeta::new_readonly(*delegation_cluster.key, false), + AccountMeta::new_readonly(*system_program.key, false), + ], + data: data.to_vec(), + }; + let delegated_account_bump = + [ + Pubkey::find_program_address(&[DELEGATED_ACCOUNT_SEED], program_id) + .1, + ]; + + invoke_signed( + &instruction, + accounts, + &[&[DELEGATED_ACCOUNT_SEED, &delegated_account_bump]], + ) +} diff --git a/program/tests/test_propose_commitment.rs b/program/tests/test_propose_commitment.rs index 8eedb7b9..d453f4f8 100644 --- a/program/tests/test_propose_commitment.rs +++ b/program/tests/test_propose_commitment.rs @@ -55,7 +55,7 @@ async fn test_propose_commitment() { .banks .get_account(pending_commitment_pda( &env.delegated_account, - args.commit_id, + args.commit_seq, )) .await .unwrap() @@ -82,7 +82,7 @@ async fn test_propose_commitment() { env.operator.pubkey() ); assert_eq!(*pending_commitment.account_pubkey(), env.delegated_account); - assert_eq!(pending_commitment.commit_id(), args.commit_id); + assert_eq!(pending_commitment.commit_seq(), args.commit_seq); assert_eq!(pending_commitment.lamports(), args.lamports); assert_eq!(*pending_commitment.owner(), args.owner); assert_eq!( @@ -231,7 +231,7 @@ async fn test_propose_commitment_fails_without_state_buffer() { }) .await; let mut args = propose_commitment_args(); - args.commit_id = 99; + args.commit_seq = 99; assert!(propose(&mut env, args).await.is_err()); } @@ -264,7 +264,7 @@ async fn test_propose_commitment_rejects_wrong_owner() { } #[tokio::test] -async fn test_propose_commitment_rejects_out_of_order_commit_id() { +async fn test_propose_commitment_rejects_out_of_order_commit_seq() { let mut env = setup_propose_commitment_env(ProposeCommitmentSetup { verifier_count: 2, register_operator_as_verifier: true, @@ -272,14 +272,14 @@ async fn test_propose_commitment_rejects_out_of_order_commit_id() { }) .await; let mut args = propose_commitment_args(); - args.commit_id = 2; + args.commit_seq = 2; write_state( &mut env.banks, &env.payer, &env.operator, env.delegated_account, WriteStateBufferArgs { - commit_id: args.commit_id, + commit_seq: args.commit_seq, total_len: state_data().len() as u32, offset: 0, chunk: state_data(), @@ -442,7 +442,7 @@ async fn setup_propose_commitment_env( &operator, delegated_account, WriteStateBufferArgs { - commit_id: propose_commitment_args().commit_id, + commit_seq: propose_commitment_args().commit_seq, total_len: state_data().len() as u32, offset: 0, chunk: state_data(), @@ -545,7 +545,7 @@ async fn register_and_add_verifier( fn propose_commitment_args() -> ProposeCommitmentArgs { ProposeCommitmentArgs { - commit_id: 1, + commit_seq: 1, lamports: 1_000, owner: Pubkey::new_from_array([7; 32]), da_pointer_hash: [9; 32], diff --git a/program/tests/test_raise_challenge.rs b/program/tests/test_raise_challenge.rs index 01593483..4c832e1f 100644 --- a/program/tests/test_raise_challenge.rs +++ b/program/tests/test_raise_challenge.rs @@ -50,7 +50,7 @@ async fn test_raise_challenge() { let args = valid_raise_challenge_args(pending_before.state_commitment_hash); let challenge_address = challenge_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, &env.challenger.pubkey(), ); @@ -71,7 +71,7 @@ async fn test_raise_challenge() { assert_eq!(challenge.outcome(), CHALLENGE_OUTCOME_NONE); assert_eq!( *challenge.pending_commitment(), - pending_commitment_pda(&env.delegated_account, env.commit_id) + pending_commitment_pda(&env.delegated_account, env.commit_seq) ); assert_eq!(*challenge.challenger_identity(), env.challenger.pubkey()); assert_eq!( @@ -114,7 +114,7 @@ async fn test_raise_challenge_fails_without_challenger_signature() { env.challenger.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, args, ); ix.accounts[0].is_signer = false; @@ -164,7 +164,7 @@ async fn test_raise_challenge_uses_submitted_policy() { .banks_client .get_account(challenge_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, &env.challenger.pubkey(), )) .await @@ -238,7 +238,7 @@ struct RaiseChallengeEnv { delegated_account: Pubkey, committed_owner: Pubkey, committed_lamports: u64, - commit_id: u64, + commit_seq: u32, challenge_config: ChallengeConfig, } @@ -257,7 +257,7 @@ async fn setup_raise_challenge_env() -> RaiseChallengeEnv { let delegation_cluster = dpi::pda::delegation_cluster_pda(&cluster_identity.pubkey()); let committed_owner = Pubkey::new_unique(); - let commit_id = 1; + let commit_seq = 1; let final_state_data = vec![9, 8, 7, 6, 5]; let record_lamports = LAMPORTS_PER_SOL; let committed_lamports = LAMPORTS_PER_SOL; @@ -342,7 +342,7 @@ async fn setup_raise_challenge_env() -> RaiseChallengeEnv { &operator, delegated_account, WriteStateBufferArgs { - commit_id, + commit_seq, total_len: final_state_data.len() as u32, offset: 0, chunk: final_state_data.clone(), @@ -361,7 +361,7 @@ async fn setup_raise_challenge_env() -> RaiseChallengeEnv { delegated_account, committed_owner, committed_lamports, - commit_id, + commit_seq, challenge_config, } } @@ -372,7 +372,7 @@ async fn propose(env: &mut RaiseChallengeEnv) -> Result<(), BanksClientError> { env.delegation_cluster, env.delegated_account, ProposeCommitmentArgs { - commit_id: env.commit_id, + commit_seq: env.commit_seq, lamports: env.committed_lamports, owner: env.committed_owner, da_pointer_hash: [9; 32], @@ -388,7 +388,7 @@ async fn approve(env: &mut RaiseChallengeEnv) -> Result<(), BanksClientError> { env.verifier.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); process_ix(&mut env.context, ix, &[&env.verifier]).await @@ -402,7 +402,7 @@ async fn raise( env.challenger.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, args, ); @@ -414,7 +414,7 @@ async fn finalize(env: &mut RaiseChallengeEnv) -> Result<(), BanksClientError> { env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); process_ix(&mut env.context, ix, &[&env.operator]).await @@ -531,7 +531,7 @@ async fn read_pending_commitment( .banks_client .get_account(pending_commitment_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, )) .await .unwrap() diff --git a/program/tests/test_resolve_dispute.rs b/program/tests/test_resolve_dispute.rs index 8f96de71..b865d8a1 100644 --- a/program/tests/test_resolve_dispute.rs +++ b/program/tests/test_resolve_dispute.rs @@ -131,7 +131,7 @@ async fn test_resolve_dispute_operator_correct_slashes_challenger_and_selects_op env.challenger.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); assert!(process_ix(&mut env.context, ix, &[&env.challenger]) .await @@ -141,7 +141,7 @@ async fn test_resolve_dispute_operator_correct_slashes_challenger_and_selects_op env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); process_ix(&mut env.context, ix, &[&env.operator]) .await @@ -227,7 +227,7 @@ async fn test_resolve_dispute_challenger_correct_refunds_challenger_and_slashes_ env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); assert!(process_ix(&mut env.context, ix, &[&env.operator]) .await @@ -237,7 +237,7 @@ async fn test_resolve_dispute_challenger_correct_refunds_challenger_and_slashes_ env.challenger.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, ); process_ix(&mut env.context, ix, &[&env.challenger]) .await @@ -269,7 +269,7 @@ async fn test_resolve_dispute_fails_with_wrong_resolver() { env.delegation_cluster, env.challenger.pubkey(), env.delegated_account, - env.commit_id, + env.commit_seq, ResolveDisputeArgs { decision: DISPUTE_DECISION_OPERATOR_STATE_CORRECT, }, @@ -321,7 +321,7 @@ async fn test_resolve_dispute_fails_with_wrong_operator_bond() { env.delegation_cluster, env.challenger.pubkey(), env.delegated_account, - env.commit_id, + env.commit_seq, ResolveDisputeArgs { decision: DISPUTE_DECISION_OPERATOR_STATE_CORRECT, }, @@ -343,7 +343,7 @@ async fn test_resolve_dispute_fails_with_wrong_fee_vault() { env.delegation_cluster, env.challenger.pubkey(), env.delegated_account, - env.commit_id, + env.commit_seq, ResolveDisputeArgs { decision: DISPUTE_DECISION_OPERATOR_STATE_CORRECT, }, @@ -375,7 +375,7 @@ struct ResolveDisputeEnv { delegated_account: Pubkey, committed_owner: Pubkey, committed_lamports: u64, - commit_id: u64, + commit_seq: u32, challenge_config: ChallengeConfig, operator_state_data: Vec, challenger_state_data: Vec, @@ -401,7 +401,7 @@ async fn setup_resolve_dispute_env() -> ResolveDisputeEnv { dpi::pda::delegation_cluster_pda(&cluster_identity.pubkey()); let challenge_config = cluster_args.challenge_config.clone().unwrap(); let committed_owner = Pubkey::new_unique(); - let commit_id = 1; + let commit_seq = 1; let operator_state_data = vec![9, 8, 7, 6, 5]; let challenger_state_data = vec![4, 3, 2, 1]; let record_lamports = LAMPORTS_PER_SOL; @@ -492,7 +492,7 @@ async fn setup_resolve_dispute_env() -> ResolveDisputeEnv { &operator, delegated_account, WriteStateBufferArgs { - commit_id, + commit_seq, total_len: operator_state_data.len() as u32, offset: 0, chunk: operator_state_data.clone(), @@ -511,7 +511,7 @@ async fn setup_resolve_dispute_env() -> ResolveDisputeEnv { delegated_account, committed_owner, committed_lamports, - commit_id, + commit_seq, challenge_config, operator_state_data, challenger_state_data, @@ -549,7 +549,7 @@ async fn propose(env: &mut ResolveDisputeEnv) -> Result<(), BanksClientError> { env.delegation_cluster, env.delegated_account, ProposeCommitmentArgs { - commit_id: env.commit_id, + commit_seq: env.commit_seq, lamports: env.committed_lamports, owner: env.committed_owner, da_pointer_hash: [9; 32], @@ -570,7 +570,7 @@ async fn raise_for_reveal( env.challenger.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, RaiseChallengeArgs { state_commitment_hash: pending.state_commitment_hash, challenge_hash: challenge_hash( @@ -578,7 +578,7 @@ async fn raise_for_reveal( env.operator.pubkey(), env.challenger.pubkey(), env.delegated_account, - env.commit_id, + env.commit_seq, reveal_args, ), stake_lamports, @@ -597,7 +597,7 @@ async fn reveal( env.operator.pubkey(), env.delegation_cluster, env.delegated_account, - env.commit_id, + env.commit_seq, args, ); @@ -613,7 +613,7 @@ async fn resolve( env.delegation_cluster, env.challenger.pubkey(), env.delegated_account, - env.commit_id, + env.commit_seq, args, ); @@ -629,7 +629,7 @@ async fn write_challenger_state_buffer( &env.challenger, env.delegated_account, WriteStateBufferArgs { - commit_id: env.commit_id, + commit_seq: env.commit_seq, total_len: data.len() as u32, offset: 0, chunk: data, @@ -742,7 +742,7 @@ async fn read_pending_commitment( .banks_client .get_account(pending_commitment_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, )) .await .unwrap() @@ -867,7 +867,7 @@ fn valid_reveal_args( fn challenge_address(env: &ResolveDisputeEnv) -> Pubkey { challenge_pda( &env.delegated_account, - env.commit_id, + env.commit_seq, &env.challenger.pubkey(), ) } @@ -882,7 +882,7 @@ fn challenge_hash( operator: Pubkey, challenger: Pubkey, account: Pubkey, - commit_id: u64, + commit_seq: u32, args: &ChallengerRevealArgs, ) -> [u8; 32] { solana_sha256_hasher::hashv(&[ @@ -891,7 +891,7 @@ fn challenge_hash( operator.as_ref(), challenger.as_ref(), account.as_ref(), - &commit_id.to_le_bytes(), + &commit_seq.to_le_bytes(), &args.lamports.to_le_bytes(), args.owner.as_ref(), &args.data_hash, diff --git a/program/tests/test_v2_write_state_buffer.rs b/program/tests/test_v2_write_state_buffer.rs index 96113fa2..6d6b99b0 100644 --- a/program/tests/test_v2_write_state_buffer.rs +++ b/program/tests/test_v2_write_state_buffer.rs @@ -23,7 +23,7 @@ use crate::fixtures::v2::setup_program_test_env; #[tokio::test] async fn test_write_state_buffer_unregistered_authority_one_chunk_finalizes() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 7; + let commit_seq = 7; let data = vec![1, 2, 3, 4]; write_buffer( @@ -32,7 +32,7 @@ async fn test_write_state_buffer_unregistered_authority_one_chunk_finalizes() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: data.len() as u32, offset: 0, chunk: data.clone(), @@ -43,7 +43,7 @@ async fn test_write_state_buffer_unregistered_authority_one_chunk_finalizes() { let state_buffer = state_buffer_pda( &env.delegated.pubkey(), - commit_id, + commit_seq, &env.writer.pubkey(), ); let state_buffer_account = @@ -53,7 +53,7 @@ async fn test_write_state_buffer_unregistered_authority_one_chunk_finalizes() { assert_eq!(state.discriminator(), StateBuffer::DISCRIMINATOR); assert_eq!(*state.authority(), env.writer.pubkey()); assert_eq!(*state.account_pubkey(), env.delegated.pubkey()); - assert_eq!(state.commit_id(), commit_id); + assert_eq!(state.commit_seq(), commit_seq); assert_eq!(state.total_len(), data.len() as u32); assert!(state.finalized()); assert_eq!(*state.data_hash(), account_data_hash(&data)); @@ -69,7 +69,7 @@ async fn test_write_state_buffer_unregistered_authority_one_chunk_finalizes() { #[tokio::test] async fn test_write_state_buffer_empty_payload_finalizes() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 16; + let commit_seq = 16; write_buffer( &mut env.banks, @@ -77,7 +77,7 @@ async fn test_write_state_buffer_empty_payload_finalizes() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: 0, offset: 0, chunk: vec![], @@ -88,7 +88,7 @@ async fn test_write_state_buffer_empty_payload_finalizes() { let state_buffer = state_buffer_pda( &env.delegated.pubkey(), - commit_id, + commit_seq, &env.writer.pubkey(), ); let state_buffer_account = @@ -109,11 +109,11 @@ async fn test_write_state_buffer_empty_payload_finalizes() { #[tokio::test] async fn test_write_state_buffer_multiple_chunks_finalize() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 8; + let commit_seq = 8; let total_len = 5; let state_buffer = state_buffer_pda( &env.delegated.pubkey(), - commit_id, + commit_seq, &env.writer.pubkey(), ); @@ -124,7 +124,7 @@ async fn test_write_state_buffer_multiple_chunks_finalize() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len, offset: 0, chunk: vec![1, 2], @@ -167,7 +167,7 @@ async fn test_write_state_buffer_multiple_chunks_finalize() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len, offset: 2, chunk: vec![3, 4, 5], @@ -198,10 +198,10 @@ async fn test_write_state_buffer_multiple_chunks_finalize() { #[tokio::test] async fn test_write_state_buffer_grows_payload_span_past_initial_capacity() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 15; + let commit_seq = 15; let state_buffer = state_buffer_pda( &env.delegated.pubkey(), - commit_id, + commit_seq, &env.writer.pubkey(), ); let initial_payload_capacity = StateBuffer::MAX_INITIAL_PAYLOAD_LEN; @@ -227,7 +227,7 @@ async fn test_write_state_buffer_grows_payload_span_past_initial_capacity() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: total_len as u32, offset: offset as u32, chunk, @@ -272,7 +272,7 @@ async fn test_write_state_buffer_grows_payload_span_past_initial_capacity() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: total_len as u32, offset: first_write_len as u32, chunk: crossing_chunk.clone(), @@ -306,7 +306,7 @@ async fn test_write_state_buffer_grows_payload_span_past_initial_capacity() { #[tokio::test] async fn test_write_state_buffer_duplicate_retry() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 9; + let commit_seq = 9; let first_chunk = vec![1, 2]; write_buffer( @@ -315,7 +315,7 @@ async fn test_write_state_buffer_duplicate_retry() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: 4, offset: 0, chunk: first_chunk.clone(), @@ -330,7 +330,7 @@ async fn test_write_state_buffer_duplicate_retry() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: 4, offset: 0, chunk: first_chunk, @@ -341,7 +341,7 @@ async fn test_write_state_buffer_duplicate_retry() { let state_buffer = state_buffer_pda( &env.delegated.pubkey(), - commit_id, + commit_seq, &env.writer.pubkey(), ); let state_buffer_account = @@ -368,7 +368,7 @@ async fn test_write_state_buffer_duplicate_retry() { #[tokio::test] async fn test_write_state_buffer_rejects_mismatched_duplicate_retry() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 10; + let commit_seq = 10; write_buffer( &mut env.banks, @@ -376,7 +376,7 @@ async fn test_write_state_buffer_rejects_mismatched_duplicate_retry() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: 4, offset: 0, chunk: vec![1, 2], @@ -391,7 +391,7 @@ async fn test_write_state_buffer_rejects_mismatched_duplicate_retry() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: 4, offset: 0, chunk: vec![1, 9], @@ -412,7 +412,7 @@ async fn test_write_state_buffer_rejects_wrong_offset() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id: 11, + commit_seq: 11, total_len: 4, offset: 1, chunk: vec![1, 2], @@ -433,7 +433,7 @@ async fn test_write_state_buffer_rejects_oversized_total_len() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id: 12, + commit_seq: 12, total_len: StateBuffer::MAX_TOTAL_PAYLOAD_LEN + 1, offset: 0, chunk: vec![1], @@ -447,7 +447,7 @@ async fn test_write_state_buffer_rejects_oversized_total_len() { #[tokio::test] async fn test_write_state_buffer_rejects_post_finalize_mutation() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 13; + let commit_seq = 13; let data = vec![1, 2, 3]; write_buffer( @@ -456,7 +456,7 @@ async fn test_write_state_buffer_rejects_post_finalize_mutation() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: data.len() as u32, offset: 0, chunk: data.clone(), @@ -471,7 +471,7 @@ async fn test_write_state_buffer_rejects_post_finalize_mutation() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: data.len() as u32, offset: 0, chunk: data, @@ -486,7 +486,7 @@ async fn test_write_state_buffer_rejects_post_finalize_mutation() { &env.writer, env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: 3, offset: 3, chunk: vec![4], @@ -500,7 +500,7 @@ async fn test_write_state_buffer_rejects_post_finalize_mutation() { #[tokio::test] async fn test_write_state_buffer_rejects_wrong_authority_for_buffer() { let mut env = setup_write_state_buffer_env().await; - let commit_id = 14; + let commit_seq = 14; let wrong_authority = Keypair::new(); fund_account(&mut env.banks, &env.payer, &wrong_authority.pubkey()).await; @@ -509,7 +509,7 @@ async fn test_write_state_buffer_rejects_wrong_authority_for_buffer() { env.writer.pubkey(), env.delegated.pubkey(), WriteStateBufferArgs { - commit_id, + commit_seq, total_len: 2, offset: 0, chunk: vec![1, 2],