use crate::{
account_address::AccountAddress,
block_info::{BlockInfo, Round},
epoch_state::EpochState,
on_chain_config::ValidatorSet,
transaction::Version,
validator_verifier::{ValidatorVerifier, VerifyError},
};
use aptos_crypto::{bls12381, hash::HashValue};
use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher};
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
fmt::{Display, Formatter},
ops::{Deref, DerefMut},
};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, BCSCryptoHash)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct LedgerInfo {
commit_info: BlockInfo,
consensus_data_hash: HashValue,
}
impl Display for LedgerInfo {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "LedgerInfo: [commit_info: {}]", self.commit_info())
}
}
impl LedgerInfo {
pub fn new(commit_info: BlockInfo, consensus_data_hash: HashValue) -> Self {
Self {
commit_info,
consensus_data_hash,
}
}
pub fn genesis(genesis_state_root_hash: HashValue, validator_set: ValidatorSet) -> Self {
Self::new(
BlockInfo::genesis(genesis_state_root_hash, validator_set),
HashValue::zero(),
)
}
#[cfg(any(test, feature = "fuzzing"))]
pub fn mock_genesis(validator_set: Option<ValidatorSet>) -> Self {
Self::new(BlockInfo::mock_genesis(validator_set), HashValue::zero())
}
pub fn commit_info(&self) -> &BlockInfo {
&self.commit_info
}
pub fn epoch(&self) -> u64 {
self.commit_info.epoch()
}
pub fn next_block_epoch(&self) -> u64 {
self.commit_info.next_block_epoch()
}
pub fn round(&self) -> Round {
self.commit_info.round()
}
pub fn consensus_block_id(&self) -> HashValue {
self.commit_info.id()
}
pub fn transaction_accumulator_hash(&self) -> HashValue {
self.commit_info.executed_state_id()
}
pub fn version(&self) -> Version {
self.commit_info.version()
}
pub fn timestamp_usecs(&self) -> u64 {
self.commit_info.timestamp_usecs()
}
pub fn next_epoch_state(&self) -> Option<&EpochState> {
self.commit_info.next_epoch_state()
}
pub fn ends_epoch(&self) -> bool {
self.next_epoch_state().is_some()
}
pub fn consensus_data_hash(&self) -> HashValue {
self.consensus_data_hash
}
pub fn set_consensus_data_hash(&mut self, consensus_data_hash: HashValue) {
self.consensus_data_hash = consensus_data_hash;
}
#[cfg(any(test, feature = "fuzzing"))]
pub fn set_executed_state_id(&mut self, id: HashValue) {
self.commit_info.set_executed_state_id(id)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum LedgerInfoWithSignatures {
V0(LedgerInfoWithV0),
}
impl Display for LedgerInfoWithSignatures {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
LedgerInfoWithSignatures::V0(ledger) => write!(f, "{}", ledger),
}
}
}
impl LedgerInfoWithSignatures {
pub fn new(
ledger_info: LedgerInfo,
signatures: BTreeMap<AccountAddress, bls12381::Signature>,
) -> Self {
LedgerInfoWithSignatures::V0(LedgerInfoWithV0::new(ledger_info, signatures))
}
pub fn genesis(genesis_state_root_hash: HashValue, validator_set: ValidatorSet) -> Self {
LedgerInfoWithSignatures::V0(LedgerInfoWithV0::genesis(
genesis_state_root_hash,
validator_set,
))
}
}
impl Deref for LedgerInfoWithSignatures {
type Target = LedgerInfoWithV0;
fn deref(&self) -> &LedgerInfoWithV0 {
match &self {
LedgerInfoWithSignatures::V0(ledger) => ledger,
}
}
}
impl DerefMut for LedgerInfoWithSignatures {
fn deref_mut(&mut self) -> &mut LedgerInfoWithV0 {
match self {
LedgerInfoWithSignatures::V0(ref mut ledger) => ledger,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct LedgerInfoWithV0 {
ledger_info: LedgerInfo,
signatures: BTreeMap<AccountAddress, bls12381::Signature>,
}
impl Display for LedgerInfoWithV0 {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self.ledger_info)
}
}
impl LedgerInfoWithV0 {
pub fn new(
ledger_info: LedgerInfo,
signatures: BTreeMap<AccountAddress, bls12381::Signature>,
) -> Self {
LedgerInfoWithV0 {
ledger_info,
signatures,
}
}
pub fn genesis(genesis_state_root_hash: HashValue, validator_set: ValidatorSet) -> Self {
Self::new(
LedgerInfo::genesis(genesis_state_root_hash, validator_set),
BTreeMap::new(),
)
}
pub fn ledger_info(&self) -> &LedgerInfo {
&self.ledger_info
}
pub fn commit_info(&self) -> &BlockInfo {
self.ledger_info.commit_info()
}
pub fn add_signature(&mut self, validator: AccountAddress, signature: bls12381::Signature) {
self.signatures.entry(validator).or_insert(signature);
}
pub fn remove_signature(&mut self, validator: AccountAddress) {
self.signatures.remove(&validator);
}
pub fn signatures(&self) -> &BTreeMap<AccountAddress, bls12381::Signature> {
&self.signatures
}
pub fn verify_signatures(
&self,
validator: &ValidatorVerifier,
) -> ::std::result::Result<(), VerifyError> {
validator.batch_verify_aggregated_signatures(self.ledger_info(), self.signatures())
}
pub fn check_voting_power(
&self,
validator: &ValidatorVerifier,
) -> ::std::result::Result<(), VerifyError> {
validator.check_voting_power(self.signatures.keys())
}
}
#[cfg(any(test, feature = "fuzzing"))]
use ::proptest::prelude::*;
#[cfg(any(test, feature = "fuzzing"))]
impl Arbitrary for LedgerInfoWithV0 {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
let dummy_signature = bls12381::Signature::dummy_signature();
(
proptest::arbitrary::any::<LedgerInfo>(),
proptest::collection::vec(proptest::arbitrary::any::<AccountAddress>(), 0..100),
)
.prop_map(move |(ledger_info, addresses)| {
let mut signatures = BTreeMap::new();
for address in addresses {
let signature = dummy_signature.clone();
signatures.insert(address, signature);
}
Self {
ledger_info,
signatures,
}
})
.boxed()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::validator_signer::ValidatorSigner;
#[test]
fn test_signatures_hash() {
let ledger_info = LedgerInfo::new(BlockInfo::empty(), HashValue::random());
const NUM_SIGNERS: u8 = 7;
let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
.map(|i| ValidatorSigner::random([i; 32]))
.collect();
let mut author_to_signature_map = BTreeMap::new();
for validator in validator_signers.iter() {
author_to_signature_map.insert(validator.author(), validator.sign(&ledger_info));
}
let ledger_info_with_signatures =
LedgerInfoWithV0::new(ledger_info.clone(), author_to_signature_map);
let mut author_to_signature_map = BTreeMap::new();
for validator in validator_signers.iter().rev() {
author_to_signature_map.insert(validator.author(), validator.sign(&ledger_info));
}
let ledger_info_with_signatures_reversed =
LedgerInfoWithV0::new(ledger_info, author_to_signature_map);
let ledger_info_with_signatures_bytes =
bcs::to_bytes(&ledger_info_with_signatures).expect("block serialization failed");
let ledger_info_with_signatures_reversed_bytes =
bcs::to_bytes(&ledger_info_with_signatures_reversed)
.expect("block serialization failed");
assert_eq!(
ledger_info_with_signatures_bytes,
ledger_info_with_signatures_reversed_bytes
);
}
}