use crate::solana_program::instruction::Instruction;
use crate::solana_compat::solana_sdk::secp256k1_program;
const SIGNATURE_SERIALIZED_SIZE: usize = 64;
const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20;
const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11;
#[derive(Clone)]
pub struct SecpSignature {
pub signature: [u8; SIGNATURE_SERIALIZED_SIZE],
pub recovery_id: u8,
pub eth_address: [u8; HASHED_PUBKEY_SERIALIZED_SIZE],
pub message: Vec<u8>,
}
pub struct Secp256k1InstructionUtils;
impl Secp256k1InstructionUtils {
pub fn build_secp256k1_instruction(
signatures: &[SecpSignature],
instruction_index: u8,
) -> Result<Instruction, Box<dyn std::error::Error>> {
let data = make_secp256k1_instruction_data_unique_message(signatures, instruction_index)?;
Ok(Instruction {
program_id: secp256k1_program::ID,
accounts: vec![],
data,
})
}
}
pub fn make_secp256k1_instruction_data_unique_message(
signatures: &[SecpSignature],
instruction_index: u8,
) -> std::result::Result<Vec<u8>, Box<dyn std::error::Error>> {
if signatures.is_empty() {
return Err("No signatures provided".into());
}
let common_message = &signatures[0].message;
for sig in signatures.iter() {
if sig.message != *common_message {
return Err("Not all signatures share the same message".into());
}
}
let message_size = common_message.len();
let signature_block_size = SIGNATURE_SERIALIZED_SIZE + 1 + HASHED_PUBKEY_SERIALIZED_SIZE;
let count = signatures.len();
let offsets_area_size = 1 + count * SIGNATURE_OFFSETS_SERIALIZED_SIZE;
let message_offset = offsets_area_size + count * signature_block_size;
let mut signature_offsets = Vec::with_capacity(count);
let mut signature_buffer = Vec::new();
for sig in signatures {
let current_offset = offsets_area_size + signature_buffer.len();
let signature_offset = current_offset; let eth_address_offset = current_offset + SIGNATURE_SERIALIZED_SIZE + 1;
let message_data_offset = message_offset;
let message_data_size = message_size;
let signature_offset = u16::try_from(signature_offset)?;
let eth_address_offset = u16::try_from(eth_address_offset)?;
let message_data_offset = u16::try_from(message_data_offset)?;
let message_data_size = u16::try_from(message_data_size)?;
let mut offsets_bytes = Vec::with_capacity(SIGNATURE_OFFSETS_SERIALIZED_SIZE);
offsets_bytes.extend(&signature_offset.to_le_bytes());
offsets_bytes.push(instruction_index); offsets_bytes.extend(ð_address_offset.to_le_bytes());
offsets_bytes.push(instruction_index); offsets_bytes.extend(&message_data_offset.to_le_bytes());
offsets_bytes.extend(&message_data_size.to_le_bytes());
offsets_bytes.push(instruction_index);
if offsets_bytes.len() != SIGNATURE_OFFSETS_SERIALIZED_SIZE {
return Err("Invalid offsets length".into());
}
signature_offsets.push(offsets_bytes);
signature_buffer.extend(&sig.signature);
signature_buffer.push(sig.recovery_id);
signature_buffer.extend(&sig.eth_address);
}
let mut instr_data = vec![count as u8];
for offs in signature_offsets {
instr_data.extend(offs);
}
instr_data.extend(signature_buffer);
instr_data.extend(common_message);
Ok(instr_data)
}