unc_primitives/
validator_signer.rs

1use std::path::Path;
2use std::sync::Arc;
3
4use unc_crypto::{InMemorySigner, KeyType, PublicKey, Signature, Signer};
5
6use crate::block::{Approval, ApprovalInner, BlockHeader};
7use crate::challenge::ChallengeBody;
8use crate::chunk_validation::ChunkEndorsementInner;
9use crate::hash::CryptoHash;
10use crate::network::{AnnounceAccount, PeerId};
11use crate::sharding::ChunkHash;
12use crate::telemetry::TelemetryInfo;
13use crate::types::{AccountId, BlockHeight, EpochId};
14
15/// Validator signer that is used to sign blocks and approvals.
16pub trait ValidatorSigner: Sync + Send {
17    /// Account id of the given validator.
18    fn validator_id(&self) -> &AccountId;
19
20    /// Public key that identifies this validator.
21    fn public_key(&self) -> PublicKey;
22
23    /// Serializes telemetry info to JSON and signs it, returning JSON with "signature" field.
24    fn sign_telemetry(&self, info: &TelemetryInfo) -> serde_json::Value;
25
26    /// Signs given parts of the header.
27    fn sign_block_header_parts(
28        &self,
29        prev_hash: CryptoHash,
30        inner_lite: &[u8],
31        inner_rest: &[u8],
32    ) -> (CryptoHash, Signature);
33
34    /// Signs given inner of the chunk header.
35    fn sign_chunk_hash(&self, chunk_hash: &ChunkHash) -> Signature;
36
37    /// Signs approval of given parent hash and reference hash.
38    fn sign_approval(&self, inner: &ApprovalInner, target_height: BlockHeight) -> Signature;
39
40    /// Signs approval of the given chunk.
41    fn sign_chunk_endorsement(&self, inner: &ChunkEndorsementInner) -> Signature;
42
43    /// Signs challenge body.
44    fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature);
45
46    /// Signs account announce.
47    fn sign_account_announce(
48        &self,
49        account_id: &AccountId,
50        peer_id: &PeerId,
51        epoch_id: &EpochId,
52    ) -> Signature;
53
54    /// Signs a proto-serialized AccountKeyPayload (see
55    /// chain/network/src/network_protocol/network.proto).
56    /// Making it typesafe would require moving the definition of
57    /// AccountKeyPayload proto to this crate to avoid a dependency cycle,
58    /// so for now we are just signing an already-serialized byte sequence.
59    /// We are serializing a proto rather than borsh here (as an experiment,
60    /// to allow the network protocol to evolve faster than on-chain stuff),
61    /// but we can always revert that decision, because these signatures are
62    /// used only for networking purposes and are not persisted on chain.
63    /// Moving to proto serialization for stuff stored on chain would be way
64    /// harder.
65    fn sign_account_key_payload(&self, proto_bytes: &[u8]) -> Signature;
66
67    fn compute_vrf_with_proof(
68        &self,
69        data: &[u8],
70    ) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof);
71
72    /// Used by test unc-infra.tructure, only implement if make sense for testing otherwise raise `unimplemented`.
73    fn write_to_file(&self, path: &Path) -> std::io::Result<()>;
74}
75
76/// Test-only signer that "signs" everything with 0s.
77/// Don't use in any production or code that requires signature verification.
78#[derive(smart_default::SmartDefault)]
79pub struct EmptyValidatorSigner {
80    #[default("test".parse().unwrap())]
81    account_id: AccountId,
82}
83
84impl ValidatorSigner for EmptyValidatorSigner {
85    fn validator_id(&self) -> &AccountId {
86        &self.account_id
87    }
88
89    fn public_key(&self) -> PublicKey {
90        PublicKey::empty(KeyType::ED25519)
91    }
92
93    fn sign_telemetry(&self, _info: &TelemetryInfo) -> serde_json::Value {
94        serde_json::Value::default()
95    }
96
97    fn sign_block_header_parts(
98        &self,
99        prev_hash: CryptoHash,
100        inner_lite: &[u8],
101        inner_rest: &[u8],
102    ) -> (CryptoHash, Signature) {
103        let hash = BlockHeader::compute_hash(prev_hash, inner_lite, inner_rest);
104        (hash, Signature::default())
105    }
106
107    fn sign_chunk_hash(&self, _chunk_hash: &ChunkHash) -> Signature {
108        Signature::default()
109    }
110
111    fn sign_approval(&self, _inner: &ApprovalInner, _target_height: BlockHeight) -> Signature {
112        Signature::default()
113    }
114
115    fn sign_chunk_endorsement(&self, _inner: &ChunkEndorsementInner) -> Signature {
116        Signature::default()
117    }
118
119    fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature) {
120        (CryptoHash::hash_borsh(challenge_body), Signature::default())
121    }
122
123    fn sign_account_announce(
124        &self,
125        _account_id: &AccountId,
126        _peer_id: &PeerId,
127        _epoch_id: &EpochId,
128    ) -> Signature {
129        Signature::default()
130    }
131
132    fn sign_account_key_payload(&self, _proto_bytes: &[u8]) -> Signature {
133        Signature::default()
134    }
135
136    fn compute_vrf_with_proof(
137        &self,
138        _data: &[u8],
139    ) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof) {
140        unimplemented!()
141    }
142
143    fn write_to_file(&self, _path: &Path) -> std::io::Result<()> {
144        unimplemented!()
145    }
146}
147
148/// Signer that keeps secret key in memory and signs locally.
149#[derive(Clone)]
150pub struct InMemoryValidatorSigner {
151    account_id: AccountId,
152    signer: Arc<dyn Signer>,
153}
154
155impl InMemoryValidatorSigner {
156    pub fn from_random(account_id: AccountId, key_type: KeyType) -> Self {
157        let signer = Arc::new(InMemorySigner::from_random(account_id.clone(), key_type));
158        Self { account_id, signer }
159    }
160
161    pub fn from_seed(account_id: AccountId, key_type: KeyType, seed: &str) -> Self {
162        let signer = Arc::new(InMemorySigner::from_seed(account_id.clone(), key_type, seed));
163        Self { account_id, signer }
164    }
165
166    pub fn public_key(&self) -> PublicKey {
167        self.signer.public_key()
168    }
169
170    pub fn from_file(path: &Path) -> std::io::Result<Self> {
171        let signer = InMemorySigner::from_file(path)?;
172        Ok(Self { account_id: signer.account_id.clone(), signer: Arc::new(signer) })
173    }
174}
175
176impl ValidatorSigner for InMemoryValidatorSigner {
177    fn validator_id(&self) -> &AccountId {
178        &self.account_id
179    }
180
181    fn public_key(&self) -> PublicKey {
182        self.signer.public_key()
183    }
184
185    fn sign_telemetry(&self, info: &TelemetryInfo) -> serde_json::Value {
186        let mut value = serde_json::to_value(info).expect("Telemetry must serialize to JSON");
187        let content = serde_json::to_string(&value).expect("Telemetry must serialize to JSON");
188        value["signature"] = self.signer.sign(content.as_bytes()).to_string().into();
189        value
190    }
191
192    fn sign_block_header_parts(
193        &self,
194        prev_hash: CryptoHash,
195        inner_lite: &[u8],
196        inner_rest: &[u8],
197    ) -> (CryptoHash, Signature) {
198        let hash = BlockHeader::compute_hash(prev_hash, inner_lite, inner_rest);
199        (hash, self.signer.sign(hash.as_ref()))
200    }
201
202    fn sign_chunk_hash(&self, chunk_hash: &ChunkHash) -> Signature {
203        self.signer.sign(chunk_hash.as_ref())
204    }
205
206    fn sign_approval(&self, inner: &ApprovalInner, target_height: BlockHeight) -> Signature {
207        self.signer.sign(&Approval::get_data_for_sig(inner, target_height))
208    }
209
210    fn sign_chunk_endorsement(&self, inner: &ChunkEndorsementInner) -> Signature {
211        self.signer.sign(&borsh::to_vec(inner).unwrap())
212    }
213
214    fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature) {
215        let hash = CryptoHash::hash_borsh(challenge_body);
216        let signature = self.signer.sign(hash.as_ref());
217        (hash, signature)
218    }
219
220    fn sign_account_announce(
221        &self,
222        account_id: &AccountId,
223        peer_id: &PeerId,
224        epoch_id: &EpochId,
225    ) -> Signature {
226        let hash = AnnounceAccount::build_header_hash(account_id, peer_id, epoch_id);
227        self.signer.sign(hash.as_ref())
228    }
229
230    fn sign_account_key_payload(&self, proto_bytes: &[u8]) -> Signature {
231        self.signer.sign(proto_bytes)
232    }
233
234    fn compute_vrf_with_proof(
235        &self,
236        data: &[u8],
237    ) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof) {
238        self.signer.compute_vrf_with_proof(data)
239    }
240
241    fn write_to_file(&self, path: &Path) -> std::io::Result<()> {
242        self.signer.write_to_file(path)
243    }
244}