1use solana_program::account_info::AccountInfo;
4use solana_program::ed25519_program;
5use solana_program::pubkey::Pubkey;
6use solana_program::instruction::Instruction;
7use solana_program::program_error::ProgramError;
8use solana_program::sysvar::instructions::{load_current_index_checked, load_instruction_at_checked};
9
10use crate::consts::{
11 ADMIN_ADDRESS, CLAIM_TASK_PREFIX, ED25519_DATA_START, ED25519_PUBKEY_SIZE, ED25519_SIGNATURE_SIZE,
12 TASK_VERIFIER,
13};
14use crate::error::DojosError;
15
16pub fn new_ed25519_instruction_with_signature(
19 message: &[u8],
20 signature: &[u8; 64],
21 pubkey: &[u8; 32],
22) -> Instruction {
23 let public_key_offset = ED25519_DATA_START;
24 let signature_offset = public_key_offset + ED25519_PUBKEY_SIZE;
25 let message_data_offset = signature_offset + ED25519_SIGNATURE_SIZE;
26
27 let mut data = Vec::with_capacity(message_data_offset + message.len());
28 data.extend_from_slice(&[1u8, 0u8]); data.extend_from_slice(&(signature_offset as u16).to_le_bytes());
30 data.extend_from_slice(&u16::MAX.to_le_bytes()); data.extend_from_slice(&(public_key_offset as u16).to_le_bytes());
32 data.extend_from_slice(&u16::MAX.to_le_bytes()); data.extend_from_slice(&(message_data_offset as u16).to_le_bytes());
34 data.extend_from_slice(&(message.len() as u16).to_le_bytes());
35 data.extend_from_slice(&u16::MAX.to_le_bytes()); data.extend_from_slice(pubkey);
37 data.extend_from_slice(signature);
38 data.extend_from_slice(message);
39
40 Instruction {
41 program_id: ed25519_program::id(),
42 accounts: vec![],
43 data,
44 }
45}
46
47pub fn verify_signed_task_via_introspection(
51 dojo_pda: Pubkey,
52 task_id: u64,
53 signature: &[u8; 64],
54 instructions_sysvar_info: &AccountInfo,
55) -> Result<(), ProgramError> {
56 let current_index = load_current_index_checked(instructions_sysvar_info)?;
57 let prev_index = current_index.checked_sub(1).ok_or(ProgramError::InvalidInstructionData)?;
58 let prev_ix = load_instruction_at_checked(prev_index as usize, instructions_sysvar_info)?;
59
60 if prev_ix.program_id != ed25519_program::id() {
61 return Err(ProgramError::InvalidInstructionData);
62 }
63
64 let data = &prev_ix.data;
65 if data.len() < ED25519_DATA_START + ED25519_PUBKEY_SIZE + ED25519_SIGNATURE_SIZE {
66 return Err(ProgramError::InvalidInstructionData);
67 }
68
69 let verifier_bytes: [u8; 32] = TASK_VERIFIER.to_bytes();
70 if data[ED25519_DATA_START..ED25519_DATA_START + ED25519_PUBKEY_SIZE] != verifier_bytes[..] {
71 return Err(ProgramError::InvalidInstructionData);
72 }
73 let sig_start = ED25519_DATA_START + ED25519_PUBKEY_SIZE;
74 if data[sig_start..sig_start + ED25519_SIGNATURE_SIZE] != signature[..] {
75 return Err(ProgramError::InvalidInstructionData);
76 }
77
78 let mut expected_message = Vec::with_capacity(CLAIM_TASK_PREFIX.len() + 32 + 8);
79 expected_message.extend_from_slice(CLAIM_TASK_PREFIX);
80 expected_message.extend_from_slice(dojo_pda.as_ref());
81 expected_message.extend_from_slice(&task_id.to_le_bytes());
82
83 let msg_start = sig_start + ED25519_SIGNATURE_SIZE;
84 if data.len() < msg_start + expected_message.len() || data[msg_start..msg_start + expected_message.len()] != expected_message[..] {
85 return Err(ProgramError::InvalidInstructionData);
86 }
87
88 Ok(())
89}
90
91pub fn assert_key(info: &AccountInfo, expected: &Pubkey) -> Result<(), ProgramError> {
93 if info.key != expected {
94 return Err(ProgramError::InvalidAccountData);
95 }
96 Ok(())
97}
98
99pub fn assert_admin(authority: &AccountInfo) -> Result<(), ProgramError> {
101 if authority.key != &ADMIN_ADDRESS {
102 return Err(DojosError::UnauthorizedAdmin.into());
103 }
104 Ok(())
105}
106