1use std::sync::Arc;
20
21use nimbus_primitives::{DigestsProvider, NimbusId};
22use schnorrkel::PublicKey;
23use session_keys_primitives::{make_vrf_transcript, PreDigest, VrfApi, VrfId};
24use sp_application_crypto::{AppCrypto, ByteArray};
25use sp_core::H256;
26use sp_keystore::{Keystore, KeystorePtr};
27
28pub fn vrf_pre_digest<B, C>(
31 client: &C,
32 keystore: &KeystorePtr,
33 nimbus_id: NimbusId,
34 parent: H256,
35) -> Option<sp_runtime::generic::DigestItem>
36where
37 B: sp_runtime::traits::Block<Hash = sp_core::H256>,
38 C: sp_api::ProvideRuntimeApi<B>,
39 C::Api: VrfApi<B>,
40{
41 let runtime_api = client.runtime_api();
42
43 let last_vrf_output = runtime_api.get_last_vrf_output(parent).ok()??;
45 let key: VrfId = runtime_api.vrf_key_lookup(parent, nimbus_id).ok()??;
47 let vrf_pre_digest = sign_vrf(last_vrf_output, key, &keystore)?;
48 Some(session_keys_primitives::digest::CompatibleDigestItem::vrf_pre_digest(vrf_pre_digest))
49}
50
51fn sign_vrf(last_vrf_output: H256, key: VrfId, keystore: &KeystorePtr) -> Option<PreDigest> {
54 let transcript = make_vrf_transcript(last_vrf_output);
55 let try_sign = Keystore::sr25519_vrf_sign(
56 &**keystore,
57 VrfId::ID,
58 key.as_ref(),
59 &transcript.clone().into_sign_data(),
60 );
61 if let Ok(Some(signature)) = try_sign {
62 let public = PublicKey::from_bytes(&key.to_raw_vec()).ok()?;
63 if signature
64 .pre_output
65 .0
66 .attach_input_hash(&public, transcript.0.clone())
67 .is_err()
68 {
69 return None;
71 }
72 Some(PreDigest {
73 vrf_output: signature.pre_output,
74 vrf_proof: signature.proof,
75 })
76 } else {
77 None
79 }
80}
81
82pub struct VrfDigestsProvider<B, C> {
83 client: Arc<C>,
84 keystore: Arc<dyn Keystore>,
85 _marker: std::marker::PhantomData<B>,
86}
87
88impl<B, C> VrfDigestsProvider<B, C> {
89 pub fn new(client: Arc<C>, keystore: Arc<dyn Keystore>) -> Self {
90 Self {
91 client,
92 keystore,
93 _marker: Default::default(),
94 }
95 }
96}
97
98impl<B, C> DigestsProvider<NimbusId, H256> for VrfDigestsProvider<B, C>
99where
100 B: sp_runtime::traits::Block<Hash = sp_core::H256>,
101 C: sp_api::ProvideRuntimeApi<B>,
102 C::Api: VrfApi<B>,
103{
104 type Digests = Option<sp_runtime::generic::DigestItem>;
105
106 fn provide_digests(&self, nimbus_id: NimbusId, parent: H256) -> Self::Digests {
107 vrf_pre_digest::<B, C>(&self.client, &self.keystore, nimbus_id, parent)
108 }
109}