# Problems with signature verification on chain

**URL:** <https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389>\
**Category:** Sway\
**Created:** [May 28, 2024, 3:01pm UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389 "2024-05-28T15:01:51Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![lokesh-lync](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/lokesh-lync/32/2902_2.png) [@lokesh-lync](https://forum.fuel.network/u/lokesh-lync)\
**Post date:** [May 28, 2024, 3:01pm UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/1 "2024-05-28T15:01:51Z")

</div>

I have this contract and I am trying to do signature verification on chain.  
I have this contract:

```auto
contract;

use std::{b512::B512, ecr::{ec_recover, ec_recover_address, EcRecoverError}};
use std::hash::*;

abi MyContract {

    #[storage(read)]
    fn generate_msg_hash(txt: str) -> b256;

    #[storage(read)]
    fn recover_signer(
        signature: B512,
        msg_hash: b256
    ) -> Address;
}

storage {
    nonce: u256 = 0
}

impl MyContract for Contract {

    #[storage(read)]
    fn generate_msg_hash(txt: str) -> b256 {
        let nonce = storage.nonce.read();
        sha256((txt, nonce))
    }

    #[storage(read)]
    fn recover_signer(
        signature: B512,
        msg_hash: b256
    ) -> Address {
        // A recovered Fuel address.
        let result_address: Result<Address, EcRecoverError> = ec_recover_address(signature, msg_hash);
        if let Ok(address) = result_address {
            return address;
        } else {
            revert(0);
        }
    }

}

```

I did types generation and this is some ts code I am using to check if signature verification works on chain:

```auto
import { Provider, WalletUnlocked } from "fuels";
import { SignatureValidationAbi__factory } from "./types";

const FUEL_RPC_URL = "https://testnet.fuel.network/v1/graphql";
const CONTRACT_ID =
  "0xa1286882be906caa819dfde46879f5cb80874210bd2328e139476a4437928703";

(async () => {
  const provider = await Provider.create(FUEL_RPC_URL);
  const mnemonic =
    "---------- YOUR MNEMONIC HERE ----------";

  const wallet = WalletUnlocked.fromMnemonic(
    mnemonic,
    undefined,
    undefined,
    provider,
  );

  console.log("Address: ", wallet.address);
  console.log("Address b256: ", wallet.address.toB256());

  const contract = SignatureValidationAbi__factory.connect(CONTRACT_ID, wallet);

  const msg_hash = await contract.functions.generate_msg_hash("abc").get();
  console.log("msg_hash: ", msg_hash.value);

  const signature = await wallet.signMessage(msg_hash.value);
  console.log("signature: ", signature);

  const recover = await contract.functions
    .recover_signer(signature, msg_hash.value)
    .get();

  console.log("recover: ", recover.value);
})();

```

This is what I am getting as output if I pass in my mnemonic:

```shell
Address: Address {
  bech32Address: 'fuel1yyvjlxdss79nklvt9cfuzthctr9fqj4ph5gu2hpwptcp7mjzex4skvqlsf'
}
Address b256: 0x21192f99b0878b3b7d8b2e13c12ef858ca904aa1bd11c55c2e0af01f6e42c9ab
msg_hash: 0x1b23f860db9bd9eccc26b36eaa3b6c223ef34539cdef01b240ad76ee45e176c1
signature: 0x39f13fa3bbe334d2925666fffeebefafdaa5ec96093f2179135ded28038d8c07943043cd57ff099625df2c5c3d53546d4b0139a02543fcb9072b9bacf78ff073
recover: {
  bits: '0xefe9068f319a0704531fa7e20fe8322af4b66ed2c13a88ace32136444eafad01'
}

```

As you can see I am not getting correct recovered address. What am I doing wrong here?

---

<div class="post-metadata">

**Author:** ![naz3eh](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/naz3eh/32/2025_2.png) [@naz3eh](https://forum.fuel.network/u/naz3eh)\
**Post date:** [May 29, 2024, 5:01am UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/2 "2024-05-29T05:01:11Z")

</div>

will look into it and get back to you.

---

<div class="post-metadata">

**Author:** ![shivamlync](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/shivamlync/32/2807_2.png) [@shivamlync](https://forum.fuel.network/u/shivamlync)\
**Post date:** [May 29, 2024, 11:27am UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/3 "2024-05-29T11:27:34Z")

</div>

Any update @naz3eh

---

<div class="post-metadata">

**Author:** ![naz3eh](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/naz3eh/32/2025_2.png) [@naz3eh](https://forum.fuel.network/u/naz3eh)\
**Post date:** [May 29, 2024, 11:33am UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/4 "2024-05-29T11:33:30Z")

</div>

I am working on it and i will update you on this by tonight

---

<div class="post-metadata">

**Author:** ![calldelegation](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/calldelegation/32/1484_2.png) [@calldelegation](https://forum.fuel.network/u/calldelegation)\
**Post date:** [May 29, 2024, 10:45pm UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/5 "2024-05-29T22:45:52Z")

</div>

Hey @shivamlync @lokesh-lync

The contract looks correct to me the problem seems to be how you are doing verifications.

Take a look at this [portion of the documentation](https://docs.fuel.network/docs/fuels-ts/wallets/signing/#signing-messages). You skipped using the `hashMessage` method to get the hash of the original message after signing.

Typescript

```auto
   // Some message
    const msg_hash = await contract.functions.generate_msg_hash("abc").get();
    console.log("msg_hash: ", msg_hash.value);

    // Signing message
    const signedMessage = await wallet?.signMessage(msg_hash);
    console.log("signed message: ", signedMessage);

    // Hash of some message
    const hashedMessage = hashMessage(msg_hash);
    console.log("hashed message: ", hashedMessage);

    // TS SDK ec recover for sanity
    const recoveredAddress = Signer.recoverAddress(hashedMessage, signedMessage);
    console.log("TS SDK EC Recover: ", recoveredAddress.toB256())

    // Smart Contract ec recover
    const recover = await contract.functions
    .recover_signer(signedMessage, hashedMessage)
    .get();
    console.log("Smart Contract EC Recover: ", recover.value);

```

Console output

 ![Screenshot 2024-05-29 at 6.41.52 PM](https://us1.discourse-cdn.com/flex022/uploads/fuel/original/2X/2/290b795e3b19a91cebbd77832bdc6b8f72e7eeaa.png)

Like the documentation says `wallet.signMessage` uses sha256 again on the message automatically so essentially you are doing sha256(sha256(“abc”)). This is why you cannot use the `msg_hash` directly from the contract to recover after since it is only hashed once.

Next time I would suggest simplifying your contract example to help debug. It was very helpful to only hashing the string in the contract to rule out any misunderstandings.

```auto
  fn generate_msg_hash(txt: str) -> b256 {
      sha256(txt)
  }

```

 ![Screenshot 2024-05-29 at 6.43.09 PM](https://us1.discourse-cdn.com/flex022/uploads/fuel/original/2X/9/91a61e23bcb3db1a64d49d793bb06414aa628e13.png)

I hope this helps! 🙂

---

<div class="post-metadata">

**Author:** ![lokesh-lync](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/lokesh-lync/32/2902_2.png) [@lokesh-lync](https://forum.fuel.network/u/lokesh-lync)\
**Post date:** [May 31, 2024, 5:58am UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/6 "2024-05-31T05:58:06Z")

</div>

What if I have to create the hash before recovering on chain? Something like this:

```auto
    #[storage(read)]
    fn recover_signer(
        signature: B512
    ) -> Address {
        let nonce = storage.nonce.read();
        let msg_hash = sha256((txt, nonce));
        // A recovered Fuel address.
        let result_address: Result<Address, EcRecoverError> = ec_recover_address(signature, msg_hash);
        if let Ok(address) = result_address {
            return address;
        } else {
            revert(0);
        }
    }

```

I am not able to create the hash on chain. I tried to do sha256 again and it did not work. I tried this:

```auto
    #[storage(read)]
    fn recover_signer(
        signature: B512
    ) -> Address {
        let nonce = storage.nonce.read();
        let msg_hash = sha256(sha256((txt, nonce)));
        // A recovered Fuel address.
        let result_address: Result<Address, EcRecoverError> = ec_recover_address(signature, msg_hash);
        if let Ok(address) = result_address {
            return address;
        } else {
            revert(0);
        }
    }

```

Let me know if you need more details I can create an end to end example script to test it out.

---

<div class="post-metadata">

**Author:** ![calldelegation](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/calldelegation/32/1484_2.png) [@calldelegation](https://forum.fuel.network/u/calldelegation)\
**Post date:** [May 31, 2024, 3:45pm UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/7 "2024-05-31T15:45:25Z")

</div>

Hey @lokesh-lync I think you’re misunderstanding here. It is not a matter of hashing the message again. The following will still work.

```auto
    fn recover_signer(
        signature: B512
    ) -> Address {
        let nonce = storage.nonce.read();
        let msg_hash = sha256((txt, nonce));
        // A recovered Fuel address.
        let result_address: Result<Address, EcRecoverError> = ec_recover_address(signature, msg_hash);
        if let Ok(address) = result_address {
            return address;
        } else {
            revert(0);
        }
    }

```

Lets break down this line

```auto
let result_address: Result<Address, EcRecoverError> = ec_recover_address(signature, msg_hash);

```

we know that `msg_hash` is `sha256((txt, nonce))` and thats fine

the problem arises when you have to pass in the correct signature over that `msg_hash` in order to recover the correct address.

If you are using the TS SDK  
Instead signing `msg_hash` you need to ONLY sign the tuple `(txt, nonce)` to avoid double hashing.

Typescript

```auto
const signedMessage = await wallet?.signMessage(msg_hash)

```

This is what youre passing in the as the signature `sig(sha256(sha256(txt, nonce)))` which is wrong

should actually be

```auto
const signedMessage = await wallet?.signMessage((txt, nonce))

```

to get this as the signature `sig(sha256(txt, nonce)))`

---

<div class="post-metadata">

**Author:** ![lokesh-lync](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/lokesh-lync/32/2902_2.png) [@lokesh-lync](https://forum.fuel.network/u/lokesh-lync)\
**Post date:** [June 3, 2024, 8:06am UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/8 "2024-06-03T08:06:40Z")

</div>

got it.  
But how do I sign a tuple using the ts sdk. If I try

```auto
const signedMessage = await wallet?.signMessage((txt, nonce))

```

I get type errors. I guess because `signMessage` takes a single string. So how do I pass in a tuple here?

---

<div class="post-metadata">

**Author:** ![calldelegation](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/calldelegation/32/1484_2.png) [@calldelegation](https://forum.fuel.network/u/calldelegation)\
**Post date:** [June 3, 2024, 6:55pm UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/9 "2024-06-03T18:55:37Z")

</div>

Hey looks like you’re right. `signMessage` only takes a single string. Fuel doesn’t have an [EIP 712](https://eips.ethereum.org/EIPS/eip-712) or equivalent yet. Let me talk to the SDK team and get back to you. You might need to find a workaround.

---

<div class="post-metadata">

**Author:** ![calldelegation](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.fuel.network/calldelegation/32/1484_2.png) [@calldelegation](https://forum.fuel.network/u/calldelegation)\
**Post date:** [June 3, 2024, 7:41pm UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/10 "2024-06-03T19:41:24Z")

</div>

We don’t have a limitation on the size of the payload to be signed therefore you can combine both things into a string:

`${txt} ${nonce}`

Make sure this is the same way you are doing it onchain as well.

You might find this helpful

> [@Convert u64 to String for concatenation](https://forum.fuel.network/t/convert-u64-to-string-for-concatenation/5254/2):
>
> EDIT/SOLUTION: the to\_le\_bytes() returns 8 bytes for num.to\_le\_bytes() which is totally fine and thus it might concatenate 8 symbols to the string which is not what we want. I tried several available built-in methods without any luck and came up with my own solution which works but might be unoptimal/not super efficient: fn convert\_num\_to\_ascii\_bytes(num: u64) -\> Bytes { let mut bytes = Bytes::new(); let mut n = num; if n == 0 { bytes.push(48); return bytes; } …

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex022/uploads/fuel/original/2X/5/57d5a345cc15a64b636e0d56e042857f8a0e80b1.png) [@system](https://forum.fuel.network/u/system)\
**Post date:** [June 24, 2024, 3:41pm UTC](https://forum.fuel.network/t/problems-with-signature-verification-on-chain/5389/11 "2024-06-24T15:41:34Z")

</div>

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.
