moonbeam_vrf/
lib.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17//! VRF client primitives for client-side verification
18
19use 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
28/// Uses the runtime API to get the VRF inputs and sign them with the VRF key that
29/// corresponds to the authoring NimbusId.
30pub 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	// first ? for runtime API, second ? for if last vrf output is not available
44	let last_vrf_output = runtime_api.get_last_vrf_output(parent).ok()??;
45	// first ? for runtime API, second ? for not VRF key associated with NimbusId
46	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
51/// Signs the VrfInput using the private key corresponding to the input `key` public key
52/// to be found in the input keystore
53fn 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			// VRF signature cannot be validated using key and transcript
70			return None;
71		}
72		Some(PreDigest {
73			vrf_output: signature.pre_output,
74			vrf_proof: signature.proof,
75		})
76	} else {
77		// VRF key not found in keystore or VRF signing failed
78		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}