I followed the answer of "how to verify a signature in a pallet", and try to verify some data signed by a private key on chain logic.

    pub fn my_verify(origin: OriginFor<T>, data: Vec<u8>, sig: T::Signature, from: T::PublicKey) -> DispatchResult{
        log::info!("--------public key is {:?}", from.clone().into_account());
        log::info!("--------signature is {:?}", sig.clone());
        log::info!("--------data is {:?}", data.clone());

        let ok = sig.verify(&data[..], &from.into_account());
        // `ok` is a bool. Use in an `if` or `ensure!`.
        ensure!(ok, <Error<T>>::SignatureVerifyError);

but I got false from sig.verify(). My config is exactly the same as the question's answer.

pub trait Config: frame_system::Config {
    /// A Signature can be verified with a specific `PublicKey`.
    /// The additional traits are boilerplate.
    type Signature: Verify<Signer = Self::PublicKey> + Encode + Decode + Member + TypeInfo;

    /// A PublicKey can be converted into an `AccountId`. This is required by the
    /// `Signature` type.
    /// The additional traits are boilerplate.
    type PublicKey: IdentifyAccount<AccountId = Self::PublicKey> + Encode + Decode + Member + TypeInfo;

    type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;

    type MaxMinersOwned: Get<u32>;

    type EpochCycle: Get<u32>;

and runtime config is like this:

impl pallet_xenon::Config for Runtime {
type Signature = sp_core::sr25519::Signature;
type PublicKey = sp_core::sr25519::Public;

type Event = Event;
type MaxMinersOwned = MaxMinersOwned;
type EpochCycle = EpochCycle;


So what mistake did I make? Help!!! Please!!!

my console shows the log output like this: loginfo

  • What is the problem then? Aug 10, 2022 at 2:23
  • I got false from "sig.verify(&data[..], &from.into_account());" So the verification is failed. But I can verify the signature by polkadot.js UI.
    – kugool
    Aug 10, 2022 at 2:37

2 Answers 2


How did you create the signature that you are testing with?
I assume you used PolkadotJS? That will not work per default, but can easily be fixed.
PolkadotJS has the quirk of wrapping everything in <Bytes></Bytes> tags.
You can update your Rust code to expect this wrapping, or don't. Depending on your use-case.

Lets write some tests to verify the assumption. First do a test where we sign with Rust and verify with Rust:

fn sig_verify_rust_works() {
    use sp_core::Pair;

    new_test_ext().execute_with(|| {
        let alice = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
        let msg: Vec<u8> = b"The actual message".to_vec();
        let sig = alice.sign(&msg[..]);

        // Works as expected - no magic involved.
        assert_ok!(Example::my_verify(Origin::root(), msg, sig, alice.public()));

Now a test where we try to verify a signature that was created by PolkadotJS:

fn sig_verify_polkadotjs_works() {
    use sp_core::Pair;

    new_test_ext().execute_with(|| {
        let alice = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
        // Signature on "The actual message" by Alice via PolkadotJS.
        let sig = b"860ab35af395c6cc989b0498269d26c13d488431f8ceac89ed82744eb84361162ce7f6c817575e46c07287f000397a0c3d5521577ac63e20ce1d0b3ab158cd88";
        let sig = hex::decode(sig).unwrap();
        let sig = sp_core::sr25519::Signature::from_slice(&sig[..]).unwrap();
        // This will not work since it's missing the wrapping:
        let msg: Vec<u8> = b"The actual message".to_vec();
        assert!(Example::my_verify(Origin::root(), msg, sig.clone(), alice.public()).is_err());

        // This will work since it's wrapped:
        let msg: Vec<u8> = b"<Bytes>The actual message</Bytes>".to_vec();
        assert!(Example::my_verify(Origin::root(), msg, sig, alice.public()).is_ok());

As you can see: The <Bytes></Bytes> makes the difference.
Interestingly enough, PolkadotJS does accepts both as valid, with and without the wrapping.

If you are using something besides PolkadotJS, you need to pay attention to the Signing context as mentioned in the original post: How to verify a signature in a Pallet?.

  • Thank you very much! I got a correct verification result with your help. Thank you! @Oliver Tale-Yazdi
    – kugool
    Aug 17, 2022 at 3:21
  • The <Bytes> is a bit of a hack, but is there a way to tell the polkadot api to not use the <Bytes>, reason being that the payload is changed from the original and submitting the derived extrinsic gets rejected by the substrate-node.
    – ivanceras
    Sep 6, 2022 at 17:03
  • Well, if you just get a PolkadotJS signature, there is no way to remove the <Bytes>. The <Bytes> was added to prevent someone using a Polkadot wallet as signing oracle; its a necessary security measurement. But particularly using Bytes instead of something like Polkadot is questionable indeed. Where is the extrinsic getting rejected by the substrate-node? Sep 6, 2022 at 18:24
  • 1
    Damn! Why? Can Jaco answer this? <Bytes> is really really tricky. Jan 19 at 8:37

I have met a similar(on ECDSA) problem before. But I'm not sure if this could help you. That takes me a whole night to debug(I think there are some bugs on my code before).

Finally, I found the substrate's signing & verification are a little bit different. For SR25519, the signing context is 'substrate', check sign & verify.

After I switch to https://github.com/paritytech/libsecp256k1, the issues were gone.

Your Answer

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

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