1use std::fmt::Debug;
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
13pub struct BankHashProof {
14 pub parent_bankhash: solana_sdk::hash::Hash,
17
18 pub(crate) accounts_delta_hash: solana_sdk::hash::Hash,
20
21 pub(crate) signature_count: u64,
23
24 pub blockhash: solana_sdk::hash::Hash,
27}
28
29impl Debug for BankHashProof {
30 #[cfg_attr(test, mutants::skip)]
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.debug_struct("Proof")
33 .field("parent_bankhash", &self.parent_bankhash)
34 .field("accounts_delta_hash", &self.accounts_delta_hash)
35 .field("signature_count", &self.signature_count)
36 .field("blockhash", &self.blockhash)
37 .field("bank_hash()", &self.hash())
38 .finish()
39 }
40}
41
42impl BankHashProof {
43 pub fn new(
45 parent_bankhash: solana_sdk::hash::Hash,
46 accounts_delta_hash: solana_sdk::hash::Hash,
47 signature_count: u64,
48 blockhash: solana_sdk::hash::Hash,
49 ) -> Self {
50 Self {
51 parent_bankhash,
52 accounts_delta_hash,
53 signature_count,
54 blockhash,
55 }
56 }
57
58 pub fn verify(&self, bank_hash: solana_sdk::hash::Hash) -> bool {
60 self.hash() == bank_hash
61 }
62
63 pub fn hash(&self) -> solana_sdk::hash::Hash {
65 solana_sdk::hash::hashv(&[
67 self.parent_bankhash.as_ref(),
68 self.accounts_delta_hash.as_ref(),
69 self.signature_count.to_le_bytes().as_ref(),
70 self.blockhash.as_ref(),
71 ])
72 }
73}
74
75#[cfg(test)]
76impl<'a> arbitrary::Arbitrary<'a> for BankHashProof {
77 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> Result<Self, arbitrary::Error> {
78 use crate::testing::arbitrary_hash;
79 Ok(Self {
80 parent_bankhash: arbitrary_hash(u)?,
81 accounts_delta_hash: arbitrary_hash(u)?,
82 signature_count: u.arbitrary()?,
83 blockhash: arbitrary_hash(u)?,
84 })
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use std::str::FromStr;
91
92 use arbitrary::Arbitrary;
93 use arbtest::arbtest;
94
95 use super::*;
96 use crate::testing::arbitrary_hash;
97
98 #[test]
99 fn known_values_from_solana() {
100 let test_cases = vec![
102 (
103 "11111111111111111111111111111111",
104 "AAH4XpMn5FrdDoCwaTXKY8Cz3hmeQKbeZFt8S44XYuYi",
105 1,
106 "J4UmrMsC4pE4GKEgrbyegswSfMopxs38zg1xb7abVnfa",
107 "acuaeWZsRuYjSq8Z3t7BbRB2TnF538yrMZENofNQ3A9",
108 ),
109 (
110 "11111111111111111111111111111111",
111 "3e1WeddAcM6joht3K69aUzvYj4peMbWepYatDKYt9sNC",
112 0,
113 "2B7nfm7qU7z2n1LsXhYNsy1SgUrQytK2aW7HTRiKTSur",
114 "ARQDUtGsw7DPFmiw7AWvHgpxib1E3V15jp91U25wkFD",
115 ),
116 (
117 "11111111111111111111111111111111",
118 "9KMQVuq5rUG2ji36GfvoxZCTHrJEvuH9YJyfFjFZ6DDx",
119 0,
120 "2B7nfm7qU7z2n1LsXhYNsy1SgUrQytK2aW7HTRiKTSur",
121 "8uqjLNiXSkyg99dxRXMTJPN2Xz9xn6KvKkkNwMpPTLt4",
122 ),
123 (
124 "8uqjLNiXSkyg99dxRXMTJPN2Xz9xn6KvKkkNwMpPTLt4",
125 "EtDCV1MSqhvL8sfmwhxuVB3eC53KGJVT4NDVU5CBhaPD",
126 0,
127 "2B7nfm7qU7z2n1LsXhYNsy1SgUrQytK2aW7HTRiKTSur",
128 "2Pj5sRN85mVTqk4hiri9SgPQ7VZ7t8ki4AiXciKsdqzi",
129 ),
130 (
131 "8uqjLNiXSkyg99dxRXMTJPN2Xz9xn6KvKkkNwMpPTLt4",
132 "Fpi4tGcNqSmJxVc1F2A63v3gqTmahkjmF9D7qLxiKQAn",
133 1,
134 "2B7nfm7qU7z2n1LsXhYNsy1SgUrQytK2aW7HTRiKTSur",
135 "2WxpWuxFHLMKjVQDQSBznBbJNjZrxMiAEdPB4s6hTG2G",
136 ),
137 (
138 "11111111111111111111111111111111",
139 "3d8fqmw2aGfek4c5ZgTDnojF8UjQKxfmCS1sdRbUutZb",
140 0,
141 "GBXGZS579k5B7eqrGELiDkiP7vvQyB4bZ93u8cgaYFeJ",
142 "6ustQtaSzYzaMZbAVsNTdtj8RMgMeE83PDy1DdVV6N6U",
143 ),
144 (
145 "11111111111111111111111111111111",
146 "cLUFixMKDgFbNcWxLab3sJZsEaYPs41ZGTEoJbUPfX9",
147 2,
148 "GBXGZS579k5B7eqrGELiDkiP7vvQyB4bZ93u8cgaYFeJ",
149 "Chz6wPtQvcehwJemAvhP39xTPYB2BWPaa4F458Sohhjw",
150 ),
151 (
152 "FKv2WQG68Fj3MU5MVPTNzj4SaUq6heCgReMdMnvQW4Sy",
153 "CbYAUEJFYsCSF4ySAeUB71WzpTFW5bdu6Jp4zu3TLfMi",
154 2,
155 "JAu73Nm8wtCVEihHFitDMVQ2W3jRGuA19ZeshTp4gYvj",
156 "81CMWBfTbpRuX2zQbRyzgbfGBJoX7dMTUfMFkK6gs75v",
157 ),
158 ];
159
160 use solana_sdk::hash::Hash;
161
162 for (parent_bankhash, accounts_delta_hash, signature_count, blockhash, expected) in
163 test_cases
164 {
165 let proof = BankHashProof {
166 parent_bankhash: Hash::from_str(parent_bankhash).unwrap(),
167 accounts_delta_hash: Hash::from_str(accounts_delta_hash).unwrap(),
168 signature_count,
169 blockhash: Hash::from_str(blockhash).unwrap(),
170 };
171 assert!(proof.verify(Hash::from_str(expected).unwrap()));
172 }
173 }
174
175 #[test]
176 fn bank_hash_construction() {
177 arbtest(move |u| {
178 let mut proof = BankHashProof::arbitrary(u)?;
179 let hash_before = proof.hash();
180
181 let mut unmodified = true;
182 if u.ratio(1, 10)? {
183 let new_parent_bankhash = arbitrary_hash(u)?;
184 unmodified = new_parent_bankhash == proof.parent_bankhash;
185 proof.parent_bankhash = new_parent_bankhash;
186 } else if u.ratio(1, 10)? {
187 let new_accounts_delta_hash = arbitrary_hash(u)?;
188 unmodified = new_accounts_delta_hash == proof.accounts_delta_hash;
189 proof.accounts_delta_hash = new_accounts_delta_hash;
190 } else if u.ratio(1, 10)? {
191 let new_signature_count = u.arbitrary()?;
192 unmodified = new_signature_count == proof.signature_count;
193 proof.signature_count = new_signature_count;
194 } else if u.ratio(1, 10)? {
195 let new_blockhash = arbitrary_hash(u)?;
196 unmodified = new_blockhash == proof.blockhash;
197 proof.blockhash = new_blockhash;
198 }
199
200 if unmodified {
201 assert!(proof.verify(hash_before));
202 } else {
203 assert!(!proof.verify(hash_before));
204 }
205
206 Ok(())
207 });
208 }
209}