use std::sync::Arc;
use nimbus_primitives::{DigestsProvider, NimbusId};
use schnorrkel::PublicKey;
use session_keys_primitives::{make_vrf_transcript, PreDigest, VrfApi, VrfId};
use sp_application_crypto::{AppCrypto, ByteArray};
use sp_core::H256;
use sp_keystore::{Keystore, KeystorePtr};
pub fn vrf_pre_digest<B, C>(
client: &C,
keystore: &KeystorePtr,
nimbus_id: NimbusId,
parent: H256,
) -> Option<sp_runtime::generic::DigestItem>
where
B: sp_runtime::traits::Block<Hash = sp_core::H256>,
C: sp_api::ProvideRuntimeApi<B>,
C::Api: VrfApi<B>,
{
let runtime_api = client.runtime_api();
let last_vrf_output = runtime_api.get_last_vrf_output(parent).ok()??;
let key: VrfId = runtime_api.vrf_key_lookup(parent, nimbus_id).ok()??;
let vrf_pre_digest = sign_vrf(last_vrf_output, key, &keystore)?;
Some(session_keys_primitives::digest::CompatibleDigestItem::vrf_pre_digest(vrf_pre_digest))
}
fn sign_vrf(last_vrf_output: H256, key: VrfId, keystore: &KeystorePtr) -> Option<PreDigest> {
let transcript = make_vrf_transcript(last_vrf_output);
let try_sign = Keystore::sr25519_vrf_sign(
&**keystore,
VrfId::ID,
key.as_ref(),
&transcript.clone().into_sign_data(),
);
if let Ok(Some(signature)) = try_sign {
let public = PublicKey::from_bytes(&key.to_raw_vec()).ok()?;
if signature
.pre_output
.0
.attach_input_hash(&public, transcript.0.clone())
.is_err()
{
return None;
}
Some(PreDigest {
vrf_output: signature.pre_output,
vrf_proof: signature.proof,
})
} else {
None
}
}
pub struct VrfDigestsProvider<B, C> {
client: Arc<C>,
keystore: Arc<dyn Keystore>,
_marker: std::marker::PhantomData<B>,
}
impl<B, C> VrfDigestsProvider<B, C> {
pub fn new(client: Arc<C>, keystore: Arc<dyn Keystore>) -> Self {
Self {
client,
keystore,
_marker: Default::default(),
}
}
}
impl<B, C> DigestsProvider<NimbusId, H256> for VrfDigestsProvider<B, C>
where
B: sp_runtime::traits::Block<Hash = sp_core::H256>,
C: sp_api::ProvideRuntimeApi<B>,
C::Api: VrfApi<B>,
{
type Digests = Option<sp_runtime::generic::DigestItem>;
fn provide_digests(&self, nimbus_id: NimbusId, parent: H256) -> Self::Digests {
vrf_pre_digest::<B, C>(&self.client, &self.keystore, nimbus_id, parent)
}
}