8

I am trying to run a benchmark for one of the test cases where I need to have some AccountId in storage to check and verify that the origin of the caller exists in storage

I there any way to convert sr25519::Public to AccountId.

use sp_io::crypto::{sr25519_generate}

benchmark! {
   set_and_verify {
        let pub_key = sr25519_generate(AURA,None);
        let msg = b"hello";
        let signature:sr25519::Signature= sr25519_sign(AURA,&pub_key,&msg).unwrap();
        let accounts:T::AccounId = vec![pub_key]
         StorageAccounts::<T>::put(&accounts);
    }: _(RawOrigin::Root,(msg.encode(),signature.encode()))
    verify {
        
    }
}

3 Answers 3

15

In general you can't unless AccountId is bound to a trait which actually provides you with a conversion function which does what you want.

The frame_system::Config::AccountId which I'd presume you're using could easily be a type into which it makes no sense to convert from a S/R25519 public key. For example, in test runtimes, AccountId is a u64, in a Polkadot runtime, it is MultiSigner.

If you don't really care about how it is converted, but you just want some AccountId value vaguely associated with the public key, then you might do something like:

(0u8, pub_key).using_encoded(|entropy| {
    AccountId::decode(&mut TrailingZeroInput::new(entropy))
        .expect("infinite input; no invalid input; qed")
})

This won't necessarily give any kind of sensible value of AccountId, but should work ok for the most common of them. It works by ensuring the first byte is zero, which is guaranteed to convert into a sensible enum variant in the case of something like a MultiSigner and won't do any harm with something like a u64. Be warned though: it won't work in general and it will result in a completely arbitrary and meaningless value of AccountId.

The correct ways of doing it are a bit more involved and may not be needed if this is just test/benchmarking code. One way involves introducing a more bounded version of AccountId into your pallet's Config trait, a little bit like the Event type which pops up everywhere.

It would be bounded similarly:

type AccountId: From<sr25519::Public> + IsType<<Self as frame_system::Config>::AccountId>;

Anything of this type (which because of the IsType bound now includes the System's AccountId) is convertible from an Sr25519 public key.

Another way would be to introduce another type item in the Config trait which implements the conversion. It would look something like:

type ConvertIntoAccountId: Convert<sr25519::Public, Self::AccountId>;

This has the advantage that you don't need to implement From<sr25519::Public> for your AccountId, which might be difficult or impossible.

1
  • 1
    Thank you, @Gav for the detailed answer. It was very helpful and solved my problem.
    – salman01z
    Apr 1, 2022 at 19:52
2

You can decode the bytes into an accountId using something like this:

let accounts = T::AccountId::decode(&mut &pub_key.encode()[..]).unwrap()
1
  • 3
    This might not work as expected if the AccountId is a MultiSigner (or indeed anything except sr25519::Public).
    – Gav
    Apr 1, 2022 at 11:13
2

You can also put a where clause into your benchmark and change your mocked runtime to use real crypto. Bit ugly but works.

benchmark! {
    where_clause {
        where T::AccountId: From<sp_core::sr25519::Public>,
    }

    …
}

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.