use anchor_lang::prelude::*;
use solana_program::{ed25519_program, instruction::Instruction, sysvar};
pub struct Annotation {
pub signer: Pubkey,
pub data: Vec<u8>,
}
#[derive(Debug)]
pub enum ParseError {
Ed25519InstructionNotFound,
Ed25519InstructionInvalidProgramId,
Ed25519InstructionInvalidData,
Ed25519InstructionInvalidSigCount,
}
impl Annotation {
pub fn parse(
instructions: &AccountInfo<'_>,
ed25519_instruction_index: u16,
) -> std::result::Result<Annotation, ParseError> {
let ed25519_ix = sysvar::instructions::load_instruction_at_checked(
ed25519_instruction_index.into(),
instructions,
)
.map_err(|_| ParseError::Ed25519InstructionNotFound)?;
if ed25519_ix.program_id != ed25519_program::ID {
return Err(ParseError::Ed25519InstructionInvalidProgramId);
}
const STRUCT_SIZE: usize = std::mem::size_of::<Ed25519InstructionData>();
let ed25519_data_len = ed25519_ix.data.len();
if ed25519_data_len < STRUCT_SIZE {
return Err(ParseError::Ed25519InstructionInvalidData);
}
let ed25519_data_slice = &ed25519_ix.data[..STRUCT_SIZE];
let ed25519_data: Ed25519InstructionData = unsafe {
(ed25519_data_slice.as_ptr() as *const Ed25519InstructionData).read_unaligned()
};
if ed25519_data.signatures_count != 1 {
return Err(ParseError::Ed25519InstructionInvalidSigCount);
}
if ed25519_data.public_key_instruction_index == u16::MAX
&& ed25519_data.message_instruction_index == u16::MAX
{
return Ok(Annotation {
signer: parse_public_key_may_panic(&ed25519_ix, &ed25519_data),
data: parse_message(&ed25519_ix, &ed25519_data),
});
}
if ed25519_data.public_key_instruction_index != ed25519_data.message_instruction_index {
if ed25519_data.public_key_instruction_index != u16::MAX {
let other_ix = sysvar::instructions::load_instruction_at_checked(
ed25519_data.public_key_instruction_index.into(),
instructions,
)
.unwrap(); return Ok(Annotation {
signer: parse_public_key_may_panic(&other_ix, &ed25519_data),
data: parse_message(&ed25519_ix, &ed25519_data),
});
} else {
let other_ix = sysvar::instructions::load_instruction_at_checked(
ed25519_data.message_instruction_index.into(),
instructions,
)
.unwrap(); return Ok(Annotation {
signer: parse_public_key_may_panic(&ed25519_ix, &ed25519_data),
data: parse_message(&other_ix, &ed25519_data),
});
}
}
let other_ix = sysvar::instructions::load_instruction_at_checked(
ed25519_data.public_key_instruction_index.into(),
instructions,
)
.unwrap(); Ok(Annotation {
signer: parse_public_key_may_panic(&other_ix, &ed25519_data),
data: parse_message(&other_ix, &ed25519_data),
})
}
}
#[repr(packed)]
struct Ed25519InstructionData {
signatures_count: u8,
_padding_byte: u8,
_signature_offset: u16,
_signature_instruction_index: u16,
public_key_offset: u16,
public_key_instruction_index: u16,
message_data_offset: u16,
message_data_size: u16,
message_instruction_index: u16,
}
fn parse_public_key_may_panic(
instruction: &Instruction,
ed25519_data: &Ed25519InstructionData,
) -> Pubkey {
const PUBKEY_LEN: usize = 32;
let offset = ed25519_data.public_key_offset as usize;
Pubkey::try_from(instruction.data[offset..offset + PUBKEY_LEN].to_vec()).unwrap()
}
fn parse_message(instruction: &Instruction, ed25519_data: &Ed25519InstructionData) -> Vec<u8> {
let offset = ed25519_data.message_data_offset as usize;
let size = ed25519_data.message_data_size as usize;
instruction.data[offset..offset + size].to_vec()
}