I've been following the how-to guide to implementing unsigned extrinsic call with a signed payload.
My pallet defines the following KeyTypeId
:
pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"ver!");
And the code that uses the local accounts looks like this:
let signer = Signer::<T, T::AuthorityId>::any_account();
if let Some((_, res)) = signer.send_unsigned_transaction(
// this line is to prepare and return payload
|acct| {
// Update payload with status and signature
...
},
|signed_payload, signature| Call::submit_unsigned {
signed_payload,
signature,
},
) {
res.map_err(|_| Error::<T>::OffChainVerificationError)
} else {
// The case of `None`: no account is available for sending
error!("No local accounts available. Consider adding one via `author_insertKey` RPC.");
Err(Error::<T>::OffChainVerificationError)
}
At this time, if I run the OCW, I get the following error from the code above:
ERROR offchain-worker verification::offchain: No local accounts available. Consider adding one via `author_insertKey` RPC.
I read that we can either specify a local account in the service.rs
:
let keystore = keystore_container.sync_keystore();
if config.offchain_worker.enabled {
sc_service::build_offchain_workers(&config, task_manager.spawn_handle(), client.clone(), network.clone());
sp_keystore::SyncCryptoStore::sr25519_generate_new(
&*keystore,
sp_runtime::KeyTypeId(*b"ver!"),
Some("//Alice"),
).expect("Creating key with local account Alice should work");
}
Or either add the account through RPC call:
curl --location --request POST 'http://localhost:9933' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"method": "author_insertKey",
"params": ["ver!","clip organ olive upper oak void inject side suit toilet stick narrow","0xb48004c6e1625282313b07d1c9950935e86894a2e4f21fb1ffee9854d180c781"],
"id": 1
}'
But in both cases the execution of the code inside submit_unsigned
extrinsic, that validates the signature:
// Ensure the payload of the unsigned transaction was signed by a local account and that the
// signature is valid
ensure_none(origin)?;
let signer = Signer::<T, T::AuthorityId>::all_accounts();
ensure!(
// panics in the next line
signer.with_filter(vec![payload.public.clone()]).can_sign(),
Error::<T>::OffChainVerificationError
);
let signature_valid = SignedPayload::<T>::verify::<T::AuthorityId>(&payload, signature);
ensure!(signature_valid, Error::<T>::OffChainVerificationError);
panics with the following error:
Thread 'tokio-runtime-worker' panicked at 'No `keystore` associated for the current context!', /workspace/.cargo/git/checkouts/substrate-7e08433d4c370a21/e8a7d16/primitives/io/src/lib.rs:832
This is a bug. Please report it at:
So, there is no keystore available at this time. Turns out from the example that there is no need to explicitly ensure that the account that signed the payload is a local account.
Is the SignedPayload::verify()
method already validating this fact?
Out of curiosity, what is the reason why the keystore is not available in extrinsic invocation?