use crate::{
account_address::AccountAddress,
transaction::Version,
validator_set::ValidatorSet,
validator_verifier::{ValidatorVerifier, VerifyError},
};
use failure::prelude::*;
#[cfg(any(test, feature = "testing"))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use solana_libra_canonical_serialization::{
CanonicalSerialize, CanonicalSerializer, SimpleSerializer,
};
use solana_libra_crypto::{
hash::{CryptoHash, CryptoHasher, LedgerInfoHasher},
HashValue, *,
};
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
fmt::{Display, Formatter},
};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
pub struct LedgerInfo {
version: Version,
transaction_accumulator_hash: HashValue,
consensus_data_hash: HashValue,
consensus_block_id: HashValue,
epoch_num: u64,
timestamp_usecs: u64,
next_validator_set: Option<ValidatorSet>,
}
impl Display for LedgerInfo {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(
f,
"LedgerInfo: [committed_block_id: {}, version: {}, epoch_num: {}, timestamp (us): {}, next_validator_set: {}]",
self.consensus_block_id(),
self.version(),
self.epoch_num(),
self.timestamp_usecs(),
self.next_validator_set.as_ref().map_or("None".to_string(), |validator_set| format!("{}", validator_set)),
)
}
}
impl LedgerInfo {
pub fn new(
version: Version,
transaction_accumulator_hash: HashValue,
consensus_data_hash: HashValue,
consensus_block_id: HashValue,
epoch_num: u64,
timestamp_usecs: u64,
next_validator_set: Option<ValidatorSet>,
) -> Self {
LedgerInfo {
version,
transaction_accumulator_hash,
consensus_data_hash,
consensus_block_id,
epoch_num,
timestamp_usecs,
next_validator_set,
}
}
pub fn version(&self) -> Version {
self.version
}
pub fn transaction_accumulator_hash(&self) -> HashValue {
self.transaction_accumulator_hash
}
pub fn consensus_data_hash(&self) -> HashValue {
self.consensus_data_hash
}
pub fn consensus_block_id(&self) -> HashValue {
self.consensus_block_id
}
pub fn set_consensus_data_hash(&mut self, consensus_data_hash: HashValue) {
self.consensus_data_hash = consensus_data_hash;
}
pub fn epoch_num(&self) -> u64 {
self.epoch_num
}
pub fn timestamp_usecs(&self) -> u64 {
self.timestamp_usecs
}
pub fn is_zero(&self) -> bool {
self.version == 0
}
pub fn next_validator_set(&self) -> Option<&ValidatorSet> {
self.next_validator_set.as_ref()
}
}
impl TryFrom<crate::proto::types::LedgerInfo> for LedgerInfo {
type Error = Error;
fn try_from(proto: crate::proto::types::LedgerInfo) -> Result<Self> {
let version = proto.version;
let transaction_accumulator_hash =
HashValue::from_slice(&proto.transaction_accumulator_hash)?;
let consensus_data_hash = HashValue::from_slice(&proto.consensus_data_hash)?;
let consensus_block_id = HashValue::from_slice(&proto.consensus_block_id)?;
let epoch_num = proto.epoch_num;
let timestamp_usecs = proto.timestamp_usecs;
let next_validator_set = if let Some(validator_set_proto) = proto.next_validator_set {
Some(ValidatorSet::try_from(validator_set_proto)?)
} else {
None
};
Ok(LedgerInfo::new(
version,
transaction_accumulator_hash,
consensus_data_hash,
consensus_block_id,
epoch_num,
timestamp_usecs,
next_validator_set,
))
}
}
impl From<LedgerInfo> for crate::proto::types::LedgerInfo {
fn from(ledger_info: LedgerInfo) -> Self {
Self {
version: ledger_info.version,
transaction_accumulator_hash: ledger_info.transaction_accumulator_hash.to_vec(),
consensus_data_hash: ledger_info.consensus_data_hash.to_vec(),
consensus_block_id: ledger_info.consensus_block_id.to_vec(),
epoch_num: ledger_info.epoch_num,
timestamp_usecs: ledger_info.timestamp_usecs,
next_validator_set: ledger_info.next_validator_set.map(Into::into),
}
}
}
impl CanonicalSerialize for LedgerInfo {
fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> {
serializer
.encode_u64(self.version)?
.encode_bytes(self.transaction_accumulator_hash.as_ref())?
.encode_bytes(self.consensus_data_hash.as_ref())?
.encode_bytes(self.consensus_block_id.as_ref())?
.encode_u64(self.epoch_num)?
.encode_u64(self.timestamp_usecs)?
.encode_optional(&self.next_validator_set)?;
Ok(())
}
}
impl CryptoHash for LedgerInfo {
type Hasher = LedgerInfoHasher;
fn hash(&self) -> HashValue {
let mut state = Self::Hasher::default();
state.write(
&SimpleSerializer::<Vec<u8>>::serialize(self).expect("Serialization should work."),
);
state.finish()
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct LedgerInfoWithSignatures<Sig> {
ledger_info: LedgerInfo,
signatures: HashMap<AccountAddress, Sig>,
}
impl<Sig> Display for LedgerInfoWithSignatures<Sig> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self.ledger_info)
}
}
impl<Sig: Signature> LedgerInfoWithSignatures<Sig> {
pub fn new(ledger_info: LedgerInfo, signatures: HashMap<AccountAddress, Sig>) -> Self {
LedgerInfoWithSignatures {
ledger_info,
signatures,
}
}
pub fn ledger_info(&self) -> &LedgerInfo {
&self.ledger_info
}
pub fn add_signature(&mut self, validator: AccountAddress, signature: Sig) {
self.signatures.entry(validator).or_insert(signature);
}
pub fn remove_signature(&mut self, validator: AccountAddress) {
self.signatures.remove(&validator);
}
pub fn signatures(&self) -> &HashMap<AccountAddress, Sig> {
&self.signatures
}
pub fn verify(
&self,
validator: &ValidatorVerifier<Sig::VerifyingKeyMaterial>,
) -> ::std::result::Result<(), VerifyError> {
if self.ledger_info.is_zero() {
return Ok(());
}
let ledger_hash = self.ledger_info().hash();
validator.batch_verify_aggregated_signature(ledger_hash, self.signatures())
}
}
impl<Sig: Signature> TryFrom<crate::proto::types::LedgerInfoWithSignatures>
for LedgerInfoWithSignatures<Sig>
{
type Error = Error;
fn try_from(proto: crate::proto::types::LedgerInfoWithSignatures) -> Result<Self> {
let ledger_info = proto
.ledger_info
.ok_or_else(|| format_err!("Missing ledger_info"))?
.try_into()?;
let signatures_proto = proto.signatures;
let num_signatures = signatures_proto.len();
let signatures = signatures_proto
.into_iter()
.map(|proto| {
let validator_id = AccountAddress::try_from(proto.validator_id)?;
let signature_bytes: &[u8] = proto.signature.as_ref();
let signature = Sig::try_from(signature_bytes)?;
Ok((validator_id, signature))
})
.collect::<Result<HashMap<_, _>>>()?;
ensure!(
signatures.len() == num_signatures,
"Signatures should be from different validators."
);
Ok(LedgerInfoWithSignatures {
ledger_info,
signatures,
})
}
}
impl<Sig: Signature> From<LedgerInfoWithSignatures<Sig>>
for crate::proto::types::LedgerInfoWithSignatures
{
fn from(ledger_info_with_sigs: LedgerInfoWithSignatures<Sig>) -> Self {
let ledger_info = Some(ledger_info_with_sigs.ledger_info.into());
let signatures = ledger_info_with_sigs
.signatures
.into_iter()
.map(
|(validator_id, signature)| crate::proto::types::ValidatorSignature {
validator_id: validator_id.to_vec(),
signature: signature.to_bytes().to_vec(),
},
)
.collect();
Self {
signatures,
ledger_info,
}
}
}