Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions docs/design/mimd-0024-fraud-proofs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -177,7 +177,7 @@ challenge_hash = H(
operator_identity,
challenger_identity,
account_pubkey,
commit_id,
commit_seq,
challenger_lamports,
challenger_owner,
challenger_data_hash,
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -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`.
Expand All @@ -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],
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -596,8 +596,8 @@ Commitment, approval, challenge, resolution, and finalization instructions.

| Instruction | Expected invoker | Description |
| --- | --- | --- |
| `WriteStateBuffer`<ul><li>ix-data: <code>commit_id, total_len, offset, chunk</code></li><li>accounts: <strong>payer signer, authority signer, StateBuffer, delegated account, system program</strong></li></ul> | 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`<ul><li>ix-data: <code>commit_id, lamports, owner, da_pointer_hash, er_slot</code></li><li>accounts: <strong>current validator signer, OperatorBond, PendingCommitment, finalized operator StateBuffer, delegated account, DelegationRecord, DelegationMetadata, DelegationCluster, VerifierRegistry, system program</strong></li></ul> | 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`<ul><li>ix-data: <code>commit_seq, total_len, offset, chunk</code></li><li>accounts: <strong>payer signer, authority signer, StateBuffer, delegated account, system program</strong></li></ul> | 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`<ul><li>ix-data: <code>commit_seq, lamports, owner, da_pointer_hash, er_slot</code></li><li>accounts: <strong>current validator signer, OperatorBond, PendingCommitment, finalized operator StateBuffer, delegated account, DelegationRecord, DelegationMetadata, DelegationCluster, VerifierRegistry, system program</strong></li></ul> | 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`<ul><li>ix-data: <code>empty</code></li><li>accounts: <strong>verifier signer, VerifierBond, PendingCommitment</strong></li></ul> | 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`<ul><li>ix-data: <code>state_commitment_hash, challenge_hash, stake_lamports</code></li><li>accounts: <strong>challenger signer, Challenge, PendingCommitment, DelegationCluster, system program</strong></li></ul> | 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`<ul><li>ix-data: <code>lamports, owner, data_hash, salt</code></li><li>accounts: <strong>challenger signer, Challenge, PendingCommitment, finalized operator StateBuffer, finalized challenger StateBuffer, DelegationCluster, protocol fee vault</strong></li></ul> | 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. |
Expand Down Expand Up @@ -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;
Expand All @@ -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.

Expand All @@ -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
Expand All @@ -692,15 +692,15 @@ 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,
pub chunk: &'a [u8],
}

pub struct ProposeCommitmentArgs {
pub commit_id: u64,
pub commit_seq: u32,
pub lamports: u64,
pub owner: Pubkey,
pub da_pointer_hash: [u8; 32],
Expand Down Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions interface/src/args/delegate.rs
Original file line number Diff line number Diff line change
@@ -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<u8>,
}

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<DelegationSeed>,
}

impl DelegateArgs {
pub fn new(delegated_account_bump: u8, seeds: Vec<DelegationSeed>) -> Self {
Self {
delegate_buffer_bump: 0,
delegation_state_bump: 0,
delegated_account_bump,
seeds,
}
}
}
2 changes: 2 additions & 0 deletions interface/src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;
Expand Down
2 changes: 1 addition & 1 deletion interface/src/args/propose_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
2 changes: 1 addition & 1 deletion interface/src/args/write_state_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
2 changes: 2 additions & 0 deletions interface/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions interface/src/instruction_builder/approve_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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,
),
Expand Down
10 changes: 5 additions & 5 deletions interface/src/instruction_builder/challenger_reveal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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(),
Expand All @@ -50,7 +50,7 @@ pub fn challenger_reveal(
AccountMeta::new_readonly(
state_buffer_pda(
&account.compatize(),
commit_id,
commit_seq,
&challenger.compatize(),
)
.modernize(),
Expand Down
52 changes: 52 additions & 0 deletions interface/src/instruction_builder/delegate.rs
Original file line number Diff line number Diff line change
@@ -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<Instruction, DataLayoutError> {
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,
})
}
Loading
Loading