extern crate alloc; use alloc::vec::Vec; use core::marker::PhantomData; use sp_core::sr25519; use sp_runtime::traits::Verify; use fp_evm::{ExitError, ExitSucceed, LinearCostPrecompile, PrecompileFailure}; use crate::{PrecompileExt, parse_slice}; pub struct Sr25519Verify(PhantomData); impl PrecompileExt for Sr25519Verify where A: From<[u8; 32]>, { const INDEX: u64 = 1027; } impl LinearCostPrecompile for Sr25519Verify where A: From<[u8; 32]>, { // There is no EIP for SR25519, so we use the same base cost as Ed25519. const BASE: u64 = 6000; const WORD: u64 = 0; fn execute(input: &[u8], _: u64) -> Result<(ExitSucceed, Vec), PrecompileFailure> { if input.len() < 132 { return Err(PrecompileFailure::Error { exit_status: ExitError::Other("input must contain 128 bytes".into()), }); }; let mut buf = [0u8; 32]; let msg = parse_slice(input, 4, 36)?; let pk = sr25519::Public::try_from(parse_slice(input, 36, 68)?).map_err(|_| { PrecompileFailure::Error { exit_status: ExitError::Other("Public key recover failed".into()), } })?; let sig = sr25519::Signature::try_from(parse_slice(input, 68, 132)?).map_err(|_| { PrecompileFailure::Error { exit_status: ExitError::Other("Signature recover failed".into()), } })?; let valid = sig.verify(msg, &pk); if valid { buf[31] = 1u8; } Ok((ExitSucceed::Returned, buf.to_vec())) } } #[cfg(test)] mod tests { #![allow(clippy::expect_used)] use super::*; use crate::mock::{ AccountId, abi_word, addr_from_index, new_test_ext, precompiles, selector_u32, }; use precompile_utils::solidity::encode_with_selector; use precompile_utils::testing::PrecompileTesterExt; use sp_core::{H256, Pair, U256, sr25519}; #[test] fn sr25519_precompile_verifies_valid_and_invalid_signatures() { new_test_ext().execute_with(|| { let caller = addr_from_index(1); let precompile_addr = addr_from_index(Sr25519Verify::::INDEX); let pair = sr25519::Pair::from_seed(&[1u8; 32]); let message = [7u8; 32]; let signature = pair.sign(&message); let public_key = pair.public(); let broken_message = [8u8; 32]; let mut broken_signature = signature.0; broken_signature[0] ^= 1; let broken_signature = sr25519::Signature::from_raw(broken_signature); precompiles::>() .prepare_test( caller, precompile_addr, encode_with_selector( selector_u32("verify(bytes32,bytes32,bytes32,bytes32)"), ( H256::from(message), H256::from(public_key.0), H256::from_slice(&signature.0[..32]), H256::from_slice(&signature.0[32..]), ), ), ) .with_static_call(true) .execute_returns_raw(abi_word(U256::one())); precompiles::>() .prepare_test( caller, precompile_addr, encode_with_selector( selector_u32("verify(bytes32,bytes32,bytes32,bytes32)"), ( H256::from(broken_message), H256::from(public_key.0), H256::from_slice(&signature.0[..32]), H256::from_slice(&signature.0[32..]), ), ), ) .with_static_call(true) .execute_returns_raw(abi_word(U256::zero())); precompiles::>() .prepare_test( caller, precompile_addr, encode_with_selector( selector_u32("verify(bytes32,bytes32,bytes32,bytes32)"), ( H256::from(message), H256::from(public_key.0), H256::from_slice(&broken_signature.0[..32]), H256::from_slice(&broken_signature.0[32..]), ), ), ) .with_static_call(true) .execute_returns_raw(abi_word(U256::zero())); }); } }