diem_types/
account_state_blob.rs1use crate::{
9 account_address::{AccountAddress, HashAccountAddress},
10 account_config::{AccountResource, BalanceResource},
11 account_state::AccountState,
12 ledger_info::LedgerInfo,
13 proof::AccountStateProof,
14 transaction::Version,
15};
16use anyhow::{anyhow, ensure, Error, Result};
17use diem_crypto::{
18 hash::{CryptoHash, CryptoHasher},
19 HashValue,
20};
21use diem_crypto_derive::CryptoHasher;
22#[cfg(any(test, feature = "fuzzing"))]
23use proptest::{arbitrary::Arbitrary, prelude::*};
24#[cfg(any(test, feature = "fuzzing"))]
25use proptest_derive::Arbitrary;
26use serde::{Deserialize, Serialize};
27use std::{convert::TryFrom, fmt};
28
29#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, CryptoHasher)]
30pub struct AccountStateBlob {
31 blob: Vec<u8>,
32}
33
34impl fmt::Debug for AccountStateBlob {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 let decoded = bcs::from_bytes(&self.blob)
37 .map(|account_state: AccountState| format!("{:#?}", account_state))
38 .unwrap_or_else(|_| String::from("[fail]"));
39
40 write!(
41 f,
42 "AccountStateBlob {{ \n \
43 Raw: 0x{} \n \
44 Decoded: {} \n \
45 }}",
46 hex::encode(&self.blob),
47 decoded,
48 )
49 }
50}
51
52impl AsRef<[u8]> for AccountStateBlob {
53 fn as_ref(&self) -> &[u8] { &self.blob }
54}
55
56impl From<&AccountStateBlob> for Vec<u8> {
57 fn from(account_state_blob: &AccountStateBlob) -> Vec<u8> {
58 account_state_blob.blob.clone()
59 }
60}
61
62impl From<AccountStateBlob> for Vec<u8> {
63 fn from(account_state_blob: AccountStateBlob) -> Vec<u8> {
64 Self::from(&account_state_blob)
65 }
66}
67
68impl From<Vec<u8>> for AccountStateBlob {
69 fn from(blob: Vec<u8>) -> AccountStateBlob { AccountStateBlob { blob } }
70}
71
72impl TryFrom<&AccountState> for AccountStateBlob {
73 type Error = Error;
74
75 fn try_from(account_state: &AccountState) -> Result<Self> {
76 Ok(Self {
77 blob: bcs::to_bytes(account_state)?,
78 })
79 }
80}
81
82impl TryFrom<&AccountStateBlob> for AccountState {
83 type Error = Error;
84
85 fn try_from(account_state_blob: &AccountStateBlob) -> Result<Self> {
86 bcs::from_bytes(&account_state_blob.blob).map_err(Into::into)
87 }
88}
89
90impl TryFrom<(&AccountResource, &BalanceResource)> for AccountStateBlob {
91 type Error = Error;
92
93 fn try_from(
94 (account_resource, balance_resource): (
95 &AccountResource,
96 &BalanceResource,
97 ),
98 ) -> Result<Self> {
99 Self::try_from(&AccountState::try_from((
100 account_resource,
101 balance_resource,
102 ))?)
103 }
104}
105
106impl TryFrom<&AccountStateBlob> for AccountResource {
107 type Error = Error;
108
109 fn try_from(account_state_blob: &AccountStateBlob) -> Result<Self> {
110 AccountState::try_from(account_state_blob)?
111 .get_account_resource()?
112 .ok_or_else(|| anyhow!("AccountResource not found."))
113 }
114}
115
116impl CryptoHash for AccountStateBlob {
117 type Hasher = AccountStateBlobHasher;
118
119 fn hash(&self) -> HashValue {
120 let mut hasher = Self::Hasher::default();
121 hasher.update(&self.blob);
122 hasher.finish()
123 }
124}
125
126#[cfg(any(test, feature = "fuzzing"))]
127prop_compose! {
128 fn account_state_blob_strategy()(account_resource in any::<AccountResource>(), balance_resource in any::<BalanceResource>()) -> AccountStateBlob {
129 AccountStateBlob::try_from((&account_resource, &balance_resource)).unwrap()
130 }
131}
132
133#[cfg(any(test, feature = "fuzzing"))]
134impl Arbitrary for AccountStateBlob {
135 type Parameters = ();
136 type Strategy = BoxedStrategy<Self>;
137
138 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
139 account_state_blob_strategy().boxed()
140 }
141}
142
143#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
144#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
145pub struct AccountStateWithProof {
146 pub version: Version,
148 pub blob: Option<AccountStateBlob>,
151 pub proof: AccountStateProof,
153}
154
155impl AccountStateWithProof {
156 pub fn new(
158 version: Version, blob: Option<AccountStateBlob>,
159 proof: AccountStateProof,
160 ) -> Self {
161 Self {
162 version,
163 blob,
164 proof,
165 }
166 }
167
168 pub fn verify(
178 &self, ledger_info: &LedgerInfo, version: Version,
179 address: AccountAddress,
180 ) -> Result<()> {
181 ensure!(
182 self.version == version,
183 "State version ({}) is not expected ({}).",
184 self.version,
185 version,
186 );
187
188 self.proof.verify(
189 ledger_info,
190 version,
191 address.hash(),
192 self.blob.as_ref(),
193 )
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use bcs::test_helpers::assert_canonical_encode_decode;
201 use proptest::collection::vec;
202
203 fn hash_blob(blob: &[u8]) -> HashValue {
204 let mut hasher = AccountStateBlobHasher::default();
205 hasher.update(blob);
206 hasher.finish()
207 }
208
209 proptest! {
210 #[test]
211 fn account_state_blob_hash(blob in vec(any::<u8>(), 1..100)) {
212 prop_assert_eq!(hash_blob(&blob), AccountStateBlob::from(blob).hash());
213 }
214
215 #[test]
216 fn account_state_blob_bcs_roundtrip(account_state_blob in any::<AccountStateBlob>()) {
217 assert_canonical_encode_decode(account_state_blob);
218 }
219
220 #[test]
221 fn account_state_with_proof_bcs_roundtrip(account_state_with_proof in any::<AccountStateWithProof>()) {
222 assert_canonical_encode_decode(account_state_with_proof);
223 }
224 }
225
226 #[test]
227 fn test_debug_does_not_panic() {
228 let _ = format!("{:#?}", AccountStateBlob::from(vec![1u8, 2u8, 3u8]));
229 }
230}