Wednesday, November 12, 2025
HomeBitcoinverification - Rust Confirm Signature

verification – Rust Confirm Signature

I am attempting to confirm signed messages utilizing Rust. lumo AI generated me this:

#[cfg(feature = "derive")]

use bitcoin::util::key::PublicKey;
use secp256k1::{Secp256k1};
pub mod messages {
use bitcoin::{
    Deal with, PublicKey, deal with::Payload, hashes::{Hash, sha256d::{self, Hash as Sha256dHash}}, secp256k1::{
        self,
        All,               // the context kind we’ll use
        Message
    }
};
use base64::decode;
use secp256k1::{Secp256k1, ecdsa::{RecoverableSignature, RecoveryId}}; // just for comfort; you would use any Base64 lib

/// Flip the primary byte of a legacy signature (27‑34) right into a `RecoveryId`.
fn decode_recovery_byte(byte: u8) -> Outcome {
    if !(27..=34).accommodates(&byte) {
        return Err(secp256k1::Error::InvalidRecoveryId);
    }
    // Low‑order two bits = actual restoration id (0‑3). The additional +4 (for compressed)
    // is stripped robotically by the modulo operation.
    RecoveryId::from_i32((byte % 4) as i32)
}

/// Compute the precise double‑SHA‑256 hash that Bitcoin‑CLI/Electrum signal.
///
/// The payload is:
///   <"Bitcoin Signed Message:n">
///   
fn bitcoin_message_hash(msg: &str) -> Sha256dHash {
    const MAGIC: &str = "Bitcoin Signed Message:n";

    // CompactSize (varint) encoder – an identical to Bitcoin Core.
    fn varint_len(s: &str) -> Vec {
        let mut v = Vec::new();
        let len = s.len() as u64;
        if len < 0xfd {
            v.push(len as u8);
        } else if len <= 0xffff {
            v.push(0xfd);
            v.extend_from_slice(&(len as u16).to_le_bytes());
        } else if len <= 0xffff_ffff {
            v.push(0xfe);
            v.extend_from_slice(&(len as u32).to_le_bytes());
        } else {
            v.push(0xff);
            v.extend_from_slice(&len.to_le_bytes());
        }
        v.extend_from_slice(s.as_bytes());
        v
    }

    let mut knowledge = Vec::new();
    knowledge.lengthen(varint_len(MAGIC));
    knowledge.lengthen(varint_len(msg));
    sha256d::Hash::hash(&knowledge)
}

/// Confirm a basic Bitcoin‑CLI / Electrum signed‑message **utilizing solely the `bitcoin` crate**.
///
/// * `address_str` – the deal with that allegedly signed the message (Base58 `1…` or Bech32 `bc1…`).  
/// * `message`     – the precise clear‑textual content that was signed.  
/// * `sig_base64`  – the Base64 string printed by the pockets.
///
/// Returns `Okay(true)` if the signature is legitimate for the equipped deal with,
/// `Okay(false)` whether it is syntactically right however does **not** match,
/// and `Err(_)` for malformed inputs (dangerous Base64, mistaken size, unsupported deal with kind, and so on.).
pub fn verify_signed_message(
    address_str: &str,
    message: &str,
    sig_base64: &str,
) -> Outcome> {

    
    // --------------------------------------------------------------
    // 1️⃣ Decode the Base64 signature (have to be precisely 65 bytes)
    // --------------------------------------------------------------
    let sig_bytes = decode(sig_base64.trim())?;
    if sig_bytes.len() != 65 {
        return Err(format!("Signature have to be 65 bytes (received {})", sig_bytes.len()).into());
    }
   println!("c111heckingadfdsads for deal with");
    // --------------------------------------------------------------
    // 2️⃣ Cut up restoration byte and compact (r|s) signature
    // --------------------------------------------------------------
    let rec_id = decode_recovery_byte(sig_bytes[0])?;
    let is_compressed = sig_bytes[0] >= 31; // true for 31‑34
    let compact_sig = &sig_bytes[1..]; // 64‑byte slice (r‖s)

    // --------------------------------------------------------------
    // 3️⃣ Construct a RecoverableSignature (bundles the rec_id)
    // --------------------------------------------------------------
    let recoverable = RecoverableSignature::from_compact(compact_sig, rec_id)?;

    // --------------------------------------------------------------
    // 4️⃣ Compute the double‑SHA‑256 hash of the message (magic prefix)
    // --------------------------------------------------------------
    let msg_hash = bitcoin_message_hash(message);
    let secp_msg = Message::from_slice(msg_hash.as_ref())?;

    // --------------------------------------------------------------
    // 5️⃣ Recuperate the general public key
    // --------------------------------------------------------------
    // `Secp256k1::verification_only()` provides us a learn‑solely context.
    let secp = Secp256k1::verification_only();
    let recovered_secp = secp.recover_ecdsa(&secp_msg, &recoverable)?;
    let recovered_pub = PublicKey::new(recovered_secp);
   println!("checkingadfdsads for deal with");
    // --------------------------------------------------------------
    // 6️⃣ Parse the equipped deal with (this additionally tells us the community)
    // --------------------------------------------------------------
    let supplied_addr: Deal with = address_str.parse::>().unwrap().assume_checked();
    println!("checking for deal with");
    // --------------------------------------------------------------
    // 7️⃣ Re‑derive the deal with from the recovered public key
    // --------------------------------------------------------------
    let derived_addr = match supplied_addr.payload {
        // ---------- Legacy Base58 (P2PKH) ----------
        Payload::PubkeyHash(_) => {
            // `p2pkh` robotically makes use of the compressed type if the secret is
            // compressed; the `is_compressed` flag we extracted earlier is just
            // wanted for sanity‑checking, not for deal with building.
            Deal with::p2pkh(&recovered_pub, supplied_addr.community)
        }

        // // ---------- Native SegWit v0 (bc1q…) ----------
        // Payload::WitnessProgram {
        //     model: 0,
        //     program: ref prog,
        // } if prog.len() == 20 => {
        //     // SegWit v0 all the time makes use of the **compressed** public key, regardless
        //     // of the flag within the signature.  The `is_compressed` boolean is
        //     // subsequently irrelevant for deal with reconstruction right here.
        //     Deal with::p2wpkh(&recovered_pub, supplied_addr.community)?
        // }

        // The rest (Taproot `bc1p…`, P2SH‑wrapped, multisig, and so on.)
        // shouldn't be supported by the legacy signed‑message format.
        _ => {
            return Err(format!(
                "Legacy verification solely helps P2PKH (1…) and native SegWit v0 (bc1q…) 
                 addresses. Deal with `{}` is of a distinct kind.",
                address_str
            )
            .into())
        }
    };
    println!("{:?}", derived_addr);
    println!("{:?}", supplied_addr);
    // --------------------------------------------------------------
    // 8️⃣ Evaluate the derived deal with with the equipped one
    // --------------------------------------------------------------
    Okay(derived_addr == supplied_addr)
}
}

Can anybody inform me why this code is not working? I’ve checked bitcoin-cli verifymessage and returned true. The Rust code is returning false. I am utilizing a P2PKH deal with. Signing with the Sparrow pockets.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments