Skip to main content

alea_verifier/instructions/
initialize.rs

1use anchor_lang::prelude::*;
2
3use crate::crypto::constants::{
4    EXPECTED_EVMNET_CHAIN_HASH, EXPECTED_EVMNET_GENESIS_TIME, EXPECTED_EVMNET_PERIOD,
5    EXPECTED_EVMNET_PUBKEY,
6};
7use crate::errors::AleaError;
8use crate::state::Config;
9
10/// Accounts for the `initialize` instruction.
11///
12/// Creates the singleton Config PDA at seeds `["config"]` with the
13/// program's on-chain upgrade authority as authority. The `init`
14/// constraint returns Anchor's built-in "account already in use"
15/// runtime error on duplicate initialization (T3.22).
16///
17/// Wave X+1 / FENDER-002 hardening (Codex Session C, 2026-04-17):
18/// `program_data` is the BPFLoaderUpgradeable ProgramData account
19/// derived at `[crate::ID]` seeds in the loader program. It exposes
20/// the program's `upgrade_authority_address` field. The handler
21/// requires `authority.key() == program_data.upgrade_authority_address`
22/// so only the entity that deployed the program (or holds the current
23/// upgrade authority, e.g. Squads multisig post-transition per ADR 0009)
24/// can initialize. This closes the deploy-to-init front-run window
25/// physically at the program level; `scripts/initialize.ts` becomes
26/// a convenience wrapper rather than the sole mitigation.
27#[derive(Accounts)]
28pub struct Initialize<'info> {
29    #[account(
30        init,
31        payer = authority,
32        space = Config::LEN,
33        seeds = [b"config"],
34        bump,
35    )]
36    pub config: Account<'info, Config>,
37    #[account(mut)]
38    pub authority: Signer<'info>,
39    /// ProgramData account for the Alea program. Anchor derives the PDA
40    /// from `[crate::ID]` under the BPFLoaderUpgradeable program and
41    /// deserializes the `upgrade_authority_address` field. Any caller
42    /// whose `authority` pubkey does not match that field is rejected
43    /// with `AleaError::UnauthorizedInit` (6012).
44    #[account(
45        seeds = [crate::ID.as_ref()],
46        bump,
47        seeds::program = anchor_lang::solana_program::bpf_loader_upgradeable::ID,
48    )]
49    pub program_data: Account<'info, ProgramData>,
50    pub system_program: Program<'info, System>,
51}
52
53/// `initialize` handler — sets up the Config PDA with evmnet parameters.
54///
55/// Guards (ADR 0031 + ADR 0027 fallback + Wave X+1 FENDER-002 hardening):
56/// - `authority == program_data.upgrade_authority_address` (6012) —
57///   closes the deploy-to-init front-run window. Only the entity that
58///   controls the program's upgrade authority (the deployer pre-transition,
59///   the Squads 2-of-3 multisig post-transition per ADR 0009) can
60///   initialize. Without this check, anyone watching the mempool between
61///   `solana program deploy` confirmation and the initialize tx could
62///   submit their own initialize with the correct (public) evmnet
63///   constants and capture permanent authority (ADR 0009 forbids
64///   rotation in `update_config`).
65/// - `chain_hash == EXPECTED_EVMNET_CHAIN_HASH` (6007) — prevents
66///   wrong-chain deployment.
67/// - `pubkey_g2 == EXPECTED_EVMNET_PUBKEY` (6008) — OPEN-ITEMS #4
68///   RESOLVED 2026-04-16: fallback path chosen because the primary
69///   `is_in_correct_subgroup_assuming_on_curve` exceeds 1.4M CU on BPF.
70///   Key rotation requires a program upgrade.
71pub fn initialize_handler(
72    ctx: Context<Initialize>,
73    pubkey_g2: [u8; 128],
74    genesis_time: u64,
75    period: u64,
76    chain_hash: [u8; 32],
77) -> Result<()> {
78    // Wave X+1 (Codex C HIGH, 2026-04-17) — upgrade-authority gate.
79    // MUST be the first check so no state writes occur on unauthorized
80    // calls. An immutable program (ADR 0009 Phase 5+) would have
81    // `upgrade_authority_address == None`, in which case nobody can
82    // initialize via this code path — that is the correct semantics
83    // (immutable program's Config must be set before authority is
84    // revoked).
85    let upgrade_authority = ctx.accounts.program_data.upgrade_authority_address;
86    require!(
87        upgrade_authority == Some(ctx.accounts.authority.key()),
88        AleaError::UnauthorizedInit,
89    );
90
91    require!(
92        chain_hash == EXPECTED_EVMNET_CHAIN_HASH,
93        AleaError::WrongChainHash
94    );
95    require!(pubkey_g2 == EXPECTED_EVMNET_PUBKEY, AleaError::WrongPubkey);
96    // T2.E — byte-equality guards for all four Config fields. Prevents
97    // genesis=0 / period=0 / wrong-constant attacks even if authority is
98    // ever compromised. ADR 0031 extended.
99    require!(
100        genesis_time == EXPECTED_EVMNET_GENESIS_TIME,
101        AleaError::InvalidGenesisTime
102    );
103    require!(period == EXPECTED_EVMNET_PERIOD, AleaError::InvalidPeriod);
104
105    let config = &mut ctx.accounts.config;
106    config.pubkey_g2 = pubkey_g2;
107    config.genesis_time = genesis_time;
108    config.period = period;
109    config.chain_hash = chain_hash;
110    config.authority = ctx.accounts.authority.key();
111    config.bump = ctx.bumps.config;
112    Ok(())
113}