diem_types/
account_state_blob.rsuse crate::{
    account_address::{AccountAddress, HashAccountAddress},
    account_config::{AccountResource, BalanceResource},
    account_state::AccountState,
    ledger_info::LedgerInfo,
    proof::AccountStateProof,
    transaction::Version,
};
use anyhow::{anyhow, ensure, Error, Result};
use diem_crypto::{
    hash::{CryptoHash, CryptoHasher},
    HashValue,
};
use diem_crypto_derive::CryptoHasher;
#[cfg(any(test, feature = "fuzzing"))]
use proptest::{arbitrary::Arbitrary, prelude::*};
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt};
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, CryptoHasher)]
pub struct AccountStateBlob {
    blob: Vec<u8>,
}
impl fmt::Debug for AccountStateBlob {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let decoded = bcs::from_bytes(&self.blob)
            .map(|account_state: AccountState| format!("{:#?}", account_state))
            .unwrap_or_else(|_| String::from("[fail]"));
        write!(
            f,
            "AccountStateBlob {{ \n \
             Raw: 0x{} \n \
             Decoded: {} \n \
             }}",
            hex::encode(&self.blob),
            decoded,
        )
    }
}
impl AsRef<[u8]> for AccountStateBlob {
    fn as_ref(&self) -> &[u8] { &self.blob }
}
impl From<&AccountStateBlob> for Vec<u8> {
    fn from(account_state_blob: &AccountStateBlob) -> Vec<u8> {
        account_state_blob.blob.clone()
    }
}
impl From<AccountStateBlob> for Vec<u8> {
    fn from(account_state_blob: AccountStateBlob) -> Vec<u8> {
        Self::from(&account_state_blob)
    }
}
impl From<Vec<u8>> for AccountStateBlob {
    fn from(blob: Vec<u8>) -> AccountStateBlob { AccountStateBlob { blob } }
}
impl TryFrom<&AccountState> for AccountStateBlob {
    type Error = Error;
    fn try_from(account_state: &AccountState) -> Result<Self> {
        Ok(Self {
            blob: bcs::to_bytes(account_state)?,
        })
    }
}
impl TryFrom<&AccountStateBlob> for AccountState {
    type Error = Error;
    fn try_from(account_state_blob: &AccountStateBlob) -> Result<Self> {
        bcs::from_bytes(&account_state_blob.blob).map_err(Into::into)
    }
}
impl TryFrom<(&AccountResource, &BalanceResource)> for AccountStateBlob {
    type Error = Error;
    fn try_from(
        (account_resource, balance_resource): (
            &AccountResource,
            &BalanceResource,
        ),
    ) -> Result<Self> {
        Self::try_from(&AccountState::try_from((
            account_resource,
            balance_resource,
        ))?)
    }
}
impl TryFrom<&AccountStateBlob> for AccountResource {
    type Error = Error;
    fn try_from(account_state_blob: &AccountStateBlob) -> Result<Self> {
        AccountState::try_from(account_state_blob)?
            .get_account_resource()?
            .ok_or_else(|| anyhow!("AccountResource not found."))
    }
}
impl CryptoHash for AccountStateBlob {
    type Hasher = AccountStateBlobHasher;
    fn hash(&self) -> HashValue {
        let mut hasher = Self::Hasher::default();
        hasher.update(&self.blob);
        hasher.finish()
    }
}
#[cfg(any(test, feature = "fuzzing"))]
prop_compose! {
    fn account_state_blob_strategy()(account_resource in any::<AccountResource>(), balance_resource in any::<BalanceResource>()) -> AccountStateBlob {
        AccountStateBlob::try_from((&account_resource, &balance_resource)).unwrap()
    }
}
#[cfg(any(test, feature = "fuzzing"))]
impl Arbitrary for AccountStateBlob {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;
    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
        account_state_blob_strategy().boxed()
    }
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct AccountStateWithProof {
    pub version: Version,
    pub blob: Option<AccountStateBlob>,
    pub proof: AccountStateProof,
}
impl AccountStateWithProof {
    pub fn new(
        version: Version, blob: Option<AccountStateBlob>,
        proof: AccountStateProof,
    ) -> Self {
        Self {
            version,
            blob,
            proof,
        }
    }
    pub fn verify(
        &self, ledger_info: &LedgerInfo, version: Version,
        address: AccountAddress,
    ) -> Result<()> {
        ensure!(
            self.version == version,
            "State version ({}) is not expected ({}).",
            self.version,
            version,
        );
        self.proof.verify(
            ledger_info,
            version,
            address.hash(),
            self.blob.as_ref(),
        )
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use bcs::test_helpers::assert_canonical_encode_decode;
    use proptest::collection::vec;
    fn hash_blob(blob: &[u8]) -> HashValue {
        let mut hasher = AccountStateBlobHasher::default();
        hasher.update(blob);
        hasher.finish()
    }
    proptest! {
        #[test]
        fn account_state_blob_hash(blob in vec(any::<u8>(), 1..100)) {
            prop_assert_eq!(hash_blob(&blob), AccountStateBlob::from(blob).hash());
        }
        #[test]
        fn account_state_blob_bcs_roundtrip(account_state_blob in any::<AccountStateBlob>()) {
            assert_canonical_encode_decode(account_state_blob);
        }
        #[test]
        fn account_state_with_proof_bcs_roundtrip(account_state_with_proof in any::<AccountStateWithProof>()) {
            assert_canonical_encode_decode(account_state_with_proof);
        }
    }
    #[test]
    fn test_debug_does_not_panic() {
        let _ = format!("{:#?}", AccountStateBlob::from(vec![1u8, 2u8, 3u8]));
    }
}