Verifies Polkadot's SR25519 signatures on Solana.
To use, add the following into Cargo.toml:
[dependencies]
schnorrkel = { git = "https://github.com/mrkobold/schnorrkel" }
Solana limits ComputeUnit usage per transaction to 1.4 million. Verifying SR25519 signatures don't fit into this limit.
The solution: do signature verification in steps/batches, saving and loading progress into a PDA between steps.
Create the following struct, which will be the data of the progress-saving-PDA
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct DotSigCheckStruct {
pub i: u64,
pub projective_point: [u64; 15],
pub signing_key: [u8; 32],
pub message: [u8; 32],
pub sig: [u8; 64],
}
Entering a signature verification step/batch from your code looks something like this:
pub fn verify<M: AsRef<[u8]>, F: Fn(usize, [u64; 15])>(
sig: &[u8], // from PDA above
message: M, // from PDA above
pubkey: &[u8], // from PDA above
update_pda_fn: F,
i: usize,
projective_point: [u64; 15],
) -> ProgramResult {
msg!("sr25519.verify: {} more steps", i);
let Ok(signature) = schnorrkel::Signature::from_bytes(sig) else {
return Err(ERR_SIGNATURE_BYTES_INCORRECT);
};
let Ok(public) = schnorrkel::PublicKey::from_bytes(pubkey) else {
return Err(ERR_PUB_KEY_BYTES_INCORRECT);
};
const SIGNING_CTX: &[u8] = b"substrate";
let t = SigningContext::new(SIGNING_CTX).bytes(message.as_ref());
let verify_res = public.step_verify(
t,
&signature,
update_pda_fn,
i,
projective_point,
);
Ok(())
}
The update_pda_fn used in the flow looks like this:
fn update_pda_fn(i: usize, projective_point: [u64; 15], pda: AccountInfo) {
let mut pda_bytes = pda.data.borrow_mut();
let mut pda_struct: DotSigCheckStruct = DotSigCheckStruct::try_from_slice(&pda_bytes).unwrap();
pda_struct.dot_sig_check_struct.i = i as u64;
pda_struct.dot_sig_check_struct.projective_point = projective_point;
pda_struct.serialize(&mut &mut srw_bytes[..]).unwrap();
}