use std::path::Path;
use std::sync::Arc;
use unc_crypto::{InMemorySigner, KeyType, PublicKey, Signature, Signer};
use crate::block::{Approval, ApprovalInner, BlockHeader};
use crate::challenge::ChallengeBody;
use crate::chunk_validation::ChunkEndorsementInner;
use crate::hash::CryptoHash;
use crate::network::{AnnounceAccount, PeerId};
use crate::sharding::ChunkHash;
use crate::telemetry::TelemetryInfo;
use crate::types::{AccountId, BlockHeight, EpochId};
pub trait ValidatorSigner: Sync + Send {
fn validator_id(&self) -> &AccountId;
fn public_key(&self) -> PublicKey;
fn sign_telemetry(&self, info: &TelemetryInfo) -> serde_json::Value;
fn sign_block_header_parts(
&self,
prev_hash: CryptoHash,
inner_lite: &[u8],
inner_rest: &[u8],
) -> (CryptoHash, Signature);
fn sign_chunk_hash(&self, chunk_hash: &ChunkHash) -> Signature;
fn sign_approval(&self, inner: &ApprovalInner, target_height: BlockHeight) -> Signature;
fn sign_chunk_endorsement(&self, inner: &ChunkEndorsementInner) -> Signature;
fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature);
fn sign_account_announce(
&self,
account_id: &AccountId,
peer_id: &PeerId,
epoch_id: &EpochId,
) -> Signature;
fn sign_account_key_payload(&self, proto_bytes: &[u8]) -> Signature;
fn compute_vrf_with_proof(
&self,
data: &[u8],
) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof);
fn write_to_file(&self, path: &Path) -> std::io::Result<()>;
}
#[derive(smart_default::SmartDefault)]
pub struct EmptyValidatorSigner {
#[default("test".parse().unwrap())]
account_id: AccountId,
}
impl ValidatorSigner for EmptyValidatorSigner {
fn validator_id(&self) -> &AccountId {
&self.account_id
}
fn public_key(&self) -> PublicKey {
PublicKey::empty(KeyType::ED25519)
}
fn sign_telemetry(&self, _info: &TelemetryInfo) -> serde_json::Value {
serde_json::Value::default()
}
fn sign_block_header_parts(
&self,
prev_hash: CryptoHash,
inner_lite: &[u8],
inner_rest: &[u8],
) -> (CryptoHash, Signature) {
let hash = BlockHeader::compute_hash(prev_hash, inner_lite, inner_rest);
(hash, Signature::default())
}
fn sign_chunk_hash(&self, _chunk_hash: &ChunkHash) -> Signature {
Signature::default()
}
fn sign_approval(&self, _inner: &ApprovalInner, _target_height: BlockHeight) -> Signature {
Signature::default()
}
fn sign_chunk_endorsement(&self, _inner: &ChunkEndorsementInner) -> Signature {
Signature::default()
}
fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature) {
(CryptoHash::hash_borsh(challenge_body), Signature::default())
}
fn sign_account_announce(
&self,
_account_id: &AccountId,
_peer_id: &PeerId,
_epoch_id: &EpochId,
) -> Signature {
Signature::default()
}
fn sign_account_key_payload(&self, _proto_bytes: &[u8]) -> Signature {
Signature::default()
}
fn compute_vrf_with_proof(
&self,
_data: &[u8],
) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof) {
unimplemented!()
}
fn write_to_file(&self, _path: &Path) -> std::io::Result<()> {
unimplemented!()
}
}
#[derive(Clone)]
pub struct InMemoryValidatorSigner {
account_id: AccountId,
signer: Arc<dyn Signer>,
}
impl InMemoryValidatorSigner {
pub fn from_random(account_id: AccountId, key_type: KeyType) -> Self {
let signer = Arc::new(InMemorySigner::from_random(account_id.clone(), key_type));
Self { account_id, signer }
}
pub fn from_seed(account_id: AccountId, key_type: KeyType, seed: &str) -> Self {
let signer = Arc::new(InMemorySigner::from_seed(account_id.clone(), key_type, seed));
Self { account_id, signer }
}
pub fn public_key(&self) -> PublicKey {
self.signer.public_key()
}
pub fn from_file(path: &Path) -> std::io::Result<Self> {
let signer = InMemorySigner::from_file(path)?;
Ok(Self { account_id: signer.account_id.clone(), signer: Arc::new(signer) })
}
}
impl ValidatorSigner for InMemoryValidatorSigner {
fn validator_id(&self) -> &AccountId {
&self.account_id
}
fn public_key(&self) -> PublicKey {
self.signer.public_key()
}
fn sign_telemetry(&self, info: &TelemetryInfo) -> serde_json::Value {
let mut value = serde_json::to_value(info).expect("Telemetry must serialize to JSON");
let content = serde_json::to_string(&value).expect("Telemetry must serialize to JSON");
value["signature"] = self.signer.sign(content.as_bytes()).to_string().into();
value
}
fn sign_block_header_parts(
&self,
prev_hash: CryptoHash,
inner_lite: &[u8],
inner_rest: &[u8],
) -> (CryptoHash, Signature) {
let hash = BlockHeader::compute_hash(prev_hash, inner_lite, inner_rest);
(hash, self.signer.sign(hash.as_ref()))
}
fn sign_chunk_hash(&self, chunk_hash: &ChunkHash) -> Signature {
self.signer.sign(chunk_hash.as_ref())
}
fn sign_approval(&self, inner: &ApprovalInner, target_height: BlockHeight) -> Signature {
self.signer.sign(&Approval::get_data_for_sig(inner, target_height))
}
fn sign_chunk_endorsement(&self, inner: &ChunkEndorsementInner) -> Signature {
self.signer.sign(&borsh::to_vec(inner).unwrap())
}
fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature) {
let hash = CryptoHash::hash_borsh(challenge_body);
let signature = self.signer.sign(hash.as_ref());
(hash, signature)
}
fn sign_account_announce(
&self,
account_id: &AccountId,
peer_id: &PeerId,
epoch_id: &EpochId,
) -> Signature {
let hash = AnnounceAccount::build_header_hash(account_id, peer_id, epoch_id);
self.signer.sign(hash.as_ref())
}
fn sign_account_key_payload(&self, proto_bytes: &[u8]) -> Signature {
self.signer.sign(proto_bytes)
}
fn compute_vrf_with_proof(
&self,
data: &[u8],
) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof) {
self.signer.compute_vrf_with_proof(data)
}
fn write_to_file(&self, path: &Path) -> std::io::Result<()> {
self.signer.write_to_file(path)
}
}