Repository: aptos-labs/aptos-core
File: aptos-move/framework/src/natives/cryptography/secp256k1.rs (Rust)

Code before:
    let signature = safely_pop_arg!(arguments, Vec<u8>);
    let recovery_id = safely_pop_arg!(arguments, u8);
    let msg = safely_pop_arg!(arguments, Vec<u8>);

    context.charge(SECP256K1_BASE)?;

    // NOTE(Gas): O(1) cost
    // (In reality, O(|msg|) deserialization cost, with |msg| < libsecp256k1_core::util::MESSAGE_SIZE
    // which seems to be 32 bytes, so O(1) cost for all intents and purposes.)
    let msg = match libsecp256k1::Message::parse_slice(&msg) {
        Ok(msg) => msg,
        Err(err) => {
            return Err(SafeNativeError::abort_with_message(
                abort_codes::NFE_DESERIALIZE,
                format!("Failed to parse message: {}", err),
            ));
        },
    };

    // NOTE(Gas): O(1) cost
    let rid = match libsecp256k1::RecoveryId::parse(recovery_id) {
        Ok(rid) => rid,
        Err(err) => {
            return Err(SafeNativeError::abort_with_message(
                abort_codes::NFE_DESERIALIZE,
                format!("Failed to parse recovery ID: {}", err),
            ));
        },
    };

    // NOTE(Gas): O(1) deserialization cost
    // which seems to be 64 bytes, so O(1) cost for all intents and purposes.
    let sig = match libsecp256k1::Signature::parse_standard_slice(&signature) {
        Ok(sig) => sig,
        Err(err) => {
            return Err(SafeNativeError::abort_with_message(
                abort_codes::NFE_DESERIALIZE,
                format!("Failed to parse signature: {}", err),
            ));
        },
    };

    context.charge(SECP256K1_ECDSA_RECOVER * NumArgs::one())?;

    // NOTE(Gas): O(1) cost: a size-2 multi-scalar multiplication
    match libsecp256k1::recover(&msg, &sig, &rid) {
        Ok(pk) => Ok(smallvec![
            Value::vector_u8(pk.serialize()[1..].to_vec()),
            Value::bool(true)
        ]),
        Err(_) => Ok(smallvec![Value::vector_u8([0u8; 0]), Value::bool(false)]),

Review comment (suggestion):
Same note here. I think the recovery IDs can only take a few values. I don't recall what they are, but look in the corresponding Move file. 

And/or see screenshot below from [here](https://alinush.github.io/ecdsa#recoverable-algorithms).

And/or look at the `parse` code.

You may be able to give a better message here too.

<img width="1776" height="456" alt="Image" src="https://github.com/user-attachments/assets/3b44346c-d399-4369-af5c-445d3f5aa242" />

Code after:
    let signature = safely_pop_arg!(arguments, Vec<u8>);
    let recovery_id = safely_pop_arg!(arguments, u8);
    let msg = safely_pop_arg!(arguments, Vec<u8>);

    context.charge(SECP256K1_BASE)?;

    // NOTE(Gas): O(1) cost
    // (In reality, O(|msg|) deserialization cost, with |msg| < libsecp256k1_core::util::MESSAGE_SIZE
    // which seems to be 32 bytes, so O(1) cost for all intents and purposes.)
    let msg = match libsecp256k1::Message::parse_slice(&msg) {
        Ok(msg) => msg,
        Err(_) => {
            return Err(SafeNativeError::abort_with_message(
                abort_codes::NFE_DESERIALIZE,
                "Message must be exactly 32 bytes",
            ));
        },
    };

    // NOTE(Gas): O(1) cost
    let rid = match libsecp256k1::RecoveryId::parse(recovery_id) {
        Ok(rid) => rid,
        Err(_) => {
            return Err(SafeNativeError::abort_with_message(
                abort_codes::NFE_DESERIALIZE,
                "Recovery ID must be 0, 1, 2, or 3",
            ));
        },
    };

    // NOTE(Gas): O(1) deserialization cost
    // which seems to be 64 bytes, so O(1) cost for all intents and purposes.
    let sig = match libsecp256k1::Signature::parse_standard_slice(&signature) {
        Ok(sig) => sig,
        Err(_) => {
            return Err(SafeNativeError::abort_with_message(
                abort_codes::NFE_DESERIALIZE,
                "Signature must be exactly 64 bytes",
            ));
        },
    };

    context.charge(SECP256K1_ECDSA_RECOVER * NumArgs::one())?;

    // NOTE(Gas): O(1) cost: a size-2 multi-scalar multiplication
    match libsecp256k1::recover(&msg, &sig, &rid) {
        Ok(pk) => Ok(smallvec![
            Value::vector_u8(pk.serialize()[1..].to_vec()),
            Value::bool(true)
        ]),
        Err(_) => Ok(smallvec![Value::vector_u8([0u8; 0]), Value::bool(false)]),