code/precompiles/src/sr25519.rs

sr25519.rs

136 lines · 4,788 bytes · 19a6485969RawGitHub
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()));
        });
    }
}