6

I'd like to sign a message via Phantom (How to verify a signature from the Phantom wallet?), and then have a smart contract verify the signature of the message to be from the correct public key.

From searches, I found there is an ed25519 signature program (https://github.com/solana-labs/solana/blob/master/core/src/sigverify.rs), but I have no idea how to use it. And no idea if this is how one would verify a signature in a rust onchain program.

Ethereum has an method ecrecover in Solidity that allows one to verify a signature given the message and public address. I'm hoping for something similar.

Thanks

Edit: Still no solution or recommendations, despite my best efforts to search around outside of Stake Exchange.

3
  • 1
    Have you tried native program Ed25519SigVerify111111111111111111111111111? I am trying that, but not successful.
    – qian
    Commented Oct 20, 2022 at 3:10
  • @qian see my new answer if you never found a solution for this
    – Adé
    Commented Nov 18, 2022 at 16:43
  • Hi @britt, how do you solve this problem?
    – marethyu
    Commented Jan 27, 2023 at 10:27

2 Answers 2

3

Seems there is no direct ecrecover equivalent and that using the Ed25519 signature verification program is the best route. And it seems the way to use that program is to include it as an instruction in the transaction that calls your smart contract (instead of making a cross-program invocation to it, which you can't do as of the time of writing this). And you must design your smart contract in such a way that it assumes that an instruction before it would have been the Ed25519 sig verification instruction. Here's some code I made based off the only public library I could find that does this (it's Anchor based):

let ix = instructions_sysvar_module::load_instruction_at_checked(
    EXPECTED_IX_SYSVAR_INDEX,
    &ctx.accounts.instructions_sysvar,
)?;
if !validate_ed25519_ix(&ix) {
    return err!(ErrorCode::InstructionMissing);
}
let pub_key = Pubkey::new(&ix.data[EXPECTED_PUBLIC_KEY_RANGE]);
let order = &ix.data[112..];
return Ok((pub_key, order.to_vec()));

based on: https://github.com/UnCaged-Studios/solana-program-library/blob/main/programs/kaching-cash-register/src/settle_order_payment.rs#L30

EXPECTED_IX_SYSVAR_INDEX would be the expected index of the Ed25519 verification instruction in your transaction.

validate_ed25519_ix(&ix) makes sure that the instruction is actually a Ed25519 sig verify instruction (checking the instruction's program ID among other things)

The instruction's data will include the public key used to sign, the message signed, and the actual signature.

The way I understand this approach is that the fact that your smart contract's instruction is reached means the Ed25519 sig verify instruction succeeded. If the sig verify instruction fails because of an invalid signature that causes an error to be thrown, then your smart contract wouldn't be reached since the entire transaction would fail.

I first gleaned this is the approach to take from a comment on github

Apparently this program doesn't work for CPI for GPU reasons: Program failed to complete: Program Ed25519SigVerify111111111111111111111111111 not supported by inner instructions

solana-labs/solana#19843

Apparently the correct approach is to verify the signature in a separate instruction, then walk through the instructions in your program code using Sysvar1nstructions1111111111111111111111111 to confirm the signature value.

source: https://github.com/solana-developers/solana-cookbook/issues/211

So overall what does this all mean? It means that when you are creating a transaction that you'll send to the Solana network, whether you generate that transaction using JavaScript, Rust, Go, or Python code, you must include a Ed25519 sig verification instruction in your transaction at the index your smart contract (or program as they're called in Solana) expects.

2
  • 1
    This saved me, thank you sir!
    – Proph3t
    Commented Sep 14, 2023 at 20:48
  • I wonder why is it so complicated while for secp256k1 one can simply use solana_program::secp256k1_recover? What "GPU reasons" may that be? Commented Oct 23, 2023 at 16:09
1

I highly recommend that you use the Anchor Framework for smart contract development on Solana. In the anchor framework, when you define a struct that represents the context in which an instruction executes, you can declare an account as the signer

#[derive(Accounts)]
pub struct CreateUserStats<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
    // space: 8 discriminator + 2 level + 4 name length + 200 name + 1 bump
    #[account(
        init,
        payer = user,
        space = 8 + 2 + 4 + 200 + 1, seeds = [b"user-stats", user.key().as_ref()], bump
    )]
    pub user_stats: Account<'info, UserStats>,
    pub system_program: Program<'info, System>,
}

In this code (taken from here), the user is declared as a type "Signer". The #[account] macro takes care of verifying the signature. You don't need to do anything extra! You can go through the example on that page for the full program.

2
  • 2
    Thanks for the comment! It's unclear to me how this could be used to verify an offchain signature that is submitted to a contract along with the message that was signed. After looking at the example you provided, it seems to me that the account maco is verifying the signature of the transaction, not a submitted signature as data. The account that signed the transaction isn't important to me. I want to submit, as data, a signature to a Smart Contract and have it verified. Hopefully, that better explains what I am trying to accomplish.
    – britt
    Commented Aug 17, 2022 at 12:10
  • Hi @britt , did you find the solution?
    – marethyu
    Commented May 11, 2023 at 7:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.