2424 context:: Context , effects:: Effects , metadata:: Metadata , proto:: InstrFixture as ProtoFixture ,
2525 } ,
2626 mollusk_svm_fuzz_fs:: { FsHandler , IntoSerializableFixture , SerializableFixture } ,
27+ solana_sdk:: keccak:: { Hash , Hasher } ,
2728} ;
2829
2930/// A fixture for invoking a single instruction against a simulated SVM
@@ -76,7 +77,22 @@ impl From<Fixture> for ProtoFixture {
7677 }
7778}
7879
79- impl SerializableFixture for ProtoFixture { }
80+ impl SerializableFixture for ProtoFixture {
81+ // Manually implemented for deterministic hashes.
82+ fn hash ( & self ) -> Hash {
83+ let mut hasher = Hasher :: default ( ) ;
84+ if let Some ( metadata) = & self . metadata {
85+ crate :: metadata:: hash_proto_metadata ( & mut hasher, metadata) ;
86+ }
87+ if let Some ( input) = & self . input {
88+ crate :: context:: hash_proto_context ( & mut hasher, input) ;
89+ }
90+ if let Some ( output) = & self . output {
91+ crate :: effects:: hash_proto_effects ( & mut hasher, output) ;
92+ }
93+ hasher. result ( )
94+ }
95+ }
8096
8197impl IntoSerializableFixture for Fixture {
8298 type Fixture = ProtoFixture ;
@@ -85,3 +101,97 @@ impl IntoSerializableFixture for Fixture {
85101 Into :: into ( self )
86102 }
87103}
104+
105+ #[ cfg( test) ]
106+ mod tests {
107+ use {
108+ super :: { proto:: InstrFixture , Fixture } ,
109+ crate :: {
110+ context:: { Context , EpochContext , SlotContext } ,
111+ effects:: Effects ,
112+ metadata:: Metadata ,
113+ } ,
114+ mollusk_svm_fuzz_fs:: SerializableFixture ,
115+ solana_sdk:: {
116+ account:: AccountSharedData , feature_set:: FeatureSet , keccak:: Hash , pubkey:: Pubkey ,
117+ transaction_context:: InstructionAccount ,
118+ } ,
119+ } ;
120+
121+ fn produce_hash ( fixture : & Fixture ) -> Hash {
122+ let proto_fixture: InstrFixture = fixture. clone ( ) . into ( ) ;
123+ proto_fixture. hash ( )
124+ }
125+
126+ #[ test]
127+ fn test_consistent_hashing ( ) {
128+ const ITERATIONS : usize = 1000 ;
129+
130+ let program_id = Pubkey :: default ( ) ;
131+ let accounts = vec ! [
132+ (
133+ Pubkey :: new_unique( ) ,
134+ AccountSharedData :: new( 42 , 42 , & Pubkey :: default ( ) ) ,
135+ None ,
136+ ) ,
137+ (
138+ Pubkey :: new_unique( ) ,
139+ AccountSharedData :: new( 42 , 42 , & Pubkey :: default ( ) ) ,
140+ None ,
141+ ) ,
142+ (
143+ Pubkey :: new_unique( ) ,
144+ AccountSharedData :: new( 42 , 42 , & Pubkey :: default ( ) ) ,
145+ None ,
146+ ) ,
147+ (
148+ Pubkey :: new_unique( ) ,
149+ AccountSharedData :: new( 42 , 42 , & Pubkey :: default ( ) ) ,
150+ None ,
151+ ) ,
152+ ] ;
153+ let instruction_accounts = accounts
154+ . iter ( )
155+ . enumerate ( )
156+ . map ( |( i, _) | InstructionAccount {
157+ index_in_transaction : i as u16 ,
158+ index_in_caller : i as u16 ,
159+ index_in_callee : i as u16 ,
160+ is_signer : false ,
161+ is_writable : true ,
162+ } )
163+ . collect :: < Vec < _ > > ( ) ;
164+ let instruction_data = vec ! [ 4 ; 24 ] ;
165+ let slot_context = SlotContext { slot : 42 } ;
166+ let epoch_context = EpochContext {
167+ feature_set : FeatureSet :: all_enabled ( ) ,
168+ } ;
169+
170+ let metadata = Metadata {
171+ entrypoint : String :: from ( "Hello, world!" ) ,
172+ } ;
173+ let context = Context {
174+ program_id,
175+ accounts,
176+ instruction_accounts,
177+ instruction_data,
178+ compute_units_available : 200_000 ,
179+ slot_context,
180+ epoch_context,
181+ } ;
182+ let effects = Effects :: default ( ) ;
183+
184+ let fixture = Fixture {
185+ metadata : Some ( metadata) ,
186+ input : context,
187+ output : effects,
188+ } ;
189+
190+ let mut last_hash = produce_hash ( & fixture) ;
191+ for _ in 0 ..ITERATIONS {
192+ let new_hash = produce_hash ( & fixture) ;
193+ assert_eq ! ( last_hash, new_hash) ;
194+ last_hash = new_hash;
195+ }
196+ }
197+ }
0 commit comments