code/precompiles/src/sr25519.rs
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<A>(PhantomData<A>);
impl<A> PrecompileExt<A> for Sr25519Verify<A>
where
A: From<[u8; 32]>,
{
const INDEX: u64 = 1027;
}
impl<A> LinearCostPrecompile for Sr25519Verify<A>
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<u8>), 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::<AccountId>::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::<Sr25519Verify<AccountId>>()
.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::<Sr25519Verify<AccountId>>()
.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::<Sr25519Verify<AccountId>>()
.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()));
});
}
}