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}