@@ -13,9 +13,11 @@ use fvm_shared::{
13
13
piece:: PieceInfo ,
14
14
sector:: { RegisteredSealProof , SectorNumber } ,
15
15
} ;
16
+ use fvm_shared:: address:: Address ;
16
17
use num_traits:: Zero ;
17
18
use vm_api:: VM ;
18
19
use vm_api:: util:: serialize_ok;
20
+ use cid:: Cid ;
19
21
20
22
use crate :: util:: {
21
23
PrecommitMetadata , advance_by_deadline_to_epoch, create_accounts, create_miner,
@@ -112,6 +114,9 @@ pub fn evm_receives_ddo_notifications_test(v: &dyn VM) {
112
114
113
115
precommit_sectors_v2 ( v, 1 , meta, & worker, & miner_addr, seal_proof, sector_number, true , None ) ;
114
116
117
+ // Before prove commit no notifications have been received
118
+ check_receiver_notification_count ( v, & worker, & evm_robust_addr, 0 ) ;
119
+
115
120
// Advance time to prove commit epoch
116
121
let prove_time = v. epoch ( ) + 151 ;
117
122
advance_by_deadline_to_epoch ( v, & miner_addr, prove_time) ;
@@ -140,118 +145,135 @@ pub fn evm_receives_ddo_notifications_test(v: &dyn VM) {
140
145
assert ! ( prove_result. code. is_success( ) , "ProveCommit failed: {}" , prove_result. message) ;
141
146
142
147
/* ***Verify that the EVM contract received the notifications correctly*** */
143
- // 1. Call totalNotifications() to verify only one notification witnessed
144
- {
145
- let call_params = NotificationReceiver :: totalNotificationsCall:: new ( ( ) ) . abi_encode ( ) ;
146
- let call_result = v
147
- . execute_message (
148
- & worker,
149
- & evm_robust_addr,
150
- & TokenAmount :: zero ( ) ,
151
- fil_actor_evm:: Method :: InvokeContract as u64 ,
152
- Some ( serialize_ok ( & ContractParams ( call_params. to_vec ( ) ) ) ) ,
153
- )
154
- . unwrap ( ) ;
155
-
156
- assert ! (
157
- call_result. code. is_success( ) ,
158
- "Failed to call totalNotifications: {}" ,
159
- call_result. message
160
- ) ;
161
-
162
- // Decode the return value
163
- let return_data: BytesDe = call_result. ret . unwrap ( ) . deserialize ( ) . unwrap ( ) ;
164
- let total_notifications = AlloyU256 :: abi_decode ( & return_data. 0 )
165
- . expect ( "Failed to decode totalNotifications return value" ) ;
166
- assert_eq ! (
167
- total_notifications,
168
- AlloyU256 :: from( 1 ) ,
169
- "Expected 1 notification, got {}" ,
170
- total_notifications
171
- ) ;
172
- }
173
-
174
- // 2. Call getNotification(0) to verify the notification contents match exactly what was sent
175
- {
176
- let call_params =
177
- NotificationReceiver :: getNotificationCall:: new ( ( AlloyU256 :: from ( 0 ) , ) ) . abi_encode ( ) ;
178
- let call_result = v
179
- . execute_message (
180
- & worker,
181
- & evm_robust_addr,
182
- & TokenAmount :: zero ( ) ,
183
- fil_actor_evm:: Method :: InvokeContract as u64 ,
184
- Some ( serialize_ok ( & ContractParams ( call_params. to_vec ( ) ) ) ) ,
185
- )
186
- . unwrap ( ) ;
187
- assert ! (
188
- call_result. code. is_success( ) ,
189
- "Failed to call getNotification: {}" ,
190
- call_result. message
191
- ) ;
192
-
193
- // Decode the return value - it returns a tuple of (uint64, int64, bytes, uint64, bytes)
194
- let return_data: BytesDe = call_result. ret . unwrap ( ) . deserialize ( ) . unwrap ( ) ;
195
-
196
- // Use the generated abi_decode_returns function
197
- let notification_result =
198
- NotificationReceiver :: getNotificationCall:: abi_decode_returns ( & return_data. 0 )
199
- . expect ( "Failed to decode getNotification return value" ) ;
200
-
201
- let received_sector = notification_result. sector ;
202
- let minimum_commitment_epoch = notification_result. minimumCommitmentEpoch ;
203
- let data_cid_bytes = notification_result. dataCid ;
204
- let received_piece_size = notification_result. pieceSize ;
205
- let received_payload = notification_result. payload ;
206
-
207
- assert_eq ! (
208
- received_sector, sector_number,
209
- "Sector number mismatch: expected {}, got {}" ,
210
- sector_number, received_sector
211
- ) ;
212
-
213
- assert_eq ! (
214
- received_piece_size, piece_size0. 0 ,
215
- "Piece size mismatch: expected {}, got {}" ,
216
- piece_size0. 0 , received_piece_size
217
- ) ;
218
-
219
- // Check payload matches exactly what we set in the manifest (hex "cafe")
220
- let expected_payload_bytes = notification_payload. to_vec ( ) ;
221
- assert_eq ! (
222
- received_payload. as_ref( ) ,
223
- expected_payload_bytes. as_slice( ) ,
224
- "Payload mismatch: expected 0x{}, got 0x{}" ,
225
- hex:: encode( & expected_payload_bytes) ,
226
- hex:: encode( & received_payload)
227
- ) ;
228
-
229
- // Check the piece CID data is present
230
- // The contract receives the CID with an extra leading byte from the CBOR encoding,
231
- // so we verify it contains the expected CID data after the first byte
232
- let expected_cid_bytes = piece_cid0. to_bytes ( ) ;
233
- assert ! ( !data_cid_bytes. is_empty( ) , "Data CID should not be empty" ) ;
234
- // Verify the CID data matches
235
- assert_eq ! ( data_cid_bytes[ 0 ] , 0 , "Data CID should start with 0x00 for ipld cbor reasons" ) ;
236
- assert_eq ! (
237
- & data_cid_bytes[ 1 ..] ,
238
- expected_cid_bytes,
239
- "Piece CID data mismatch: expected 0x{}, got 0x{}" ,
240
- hex:: encode( & expected_cid_bytes) ,
241
- hex:: encode( & data_cid_bytes[ 1 ..] )
242
- ) ;
243
-
244
- // Verify minimum_commitment_epoch matches the sector expiration
245
- // The sector expiration is set to: precommit_epoch + min_sector_expiration + max_prove_commit_duration
246
- let policy = Policy :: default ( ) ;
247
- let expected_expiration = precommit_epoch
248
- + policy. min_sector_expiration
249
- + max_prove_commit_duration ( & policy, seal_proof) . unwrap ( ) ;
250
-
251
- assert_eq ! (
252
- minimum_commitment_epoch, expected_expiration,
253
- "Minimum commitment epoch mismatch: expected {}, got {}" ,
254
- expected_expiration, minimum_commitment_epoch
255
- ) ;
256
- }
148
+ let policy = Policy :: default ( ) ;
149
+ let expected_notification = ExpectedNotification {
150
+ sector : sector_number,
151
+ minimum_commitment_epoch : precommit_epoch + policy. min_sector_expiration + max_prove_commit_duration ( & policy, seal_proof) . unwrap ( ) ,
152
+ piece_cid : piece_cid0,
153
+ piece_size : piece_size0. 0 ,
154
+ payload : notification_payload. to_vec ( ) ,
155
+ } ;
156
+
157
+ check_receiver_notification_count ( v, & worker, & evm_robust_addr, 1 ) ;
158
+ check_receiver_notification_at ( v, & worker, & evm_robust_addr, 0 , & expected_notification) ;
159
+ }
160
+
161
+ // Helper functions checking state of receiver contract
162
+
163
+ pub fn check_receiver_notification_count ( v : & dyn VM , sender_addr : & Address , receiver_addr : & Address , expected_count : u64 ) {
164
+ let call_params = NotificationReceiver :: totalNotificationsCall:: new ( ( ) ) . abi_encode ( ) ;
165
+ let call_result = v
166
+ . execute_message (
167
+ & sender_addr,
168
+ & receiver_addr,
169
+ & TokenAmount :: zero ( ) ,
170
+ fil_actor_evm:: Method :: InvokeContract as u64 ,
171
+ Some ( serialize_ok ( & ContractParams ( call_params. to_vec ( ) ) ) ) ,
172
+ )
173
+ . unwrap ( ) ;
174
+
175
+ assert ! (
176
+ call_result. code. is_success( ) ,
177
+ "Failed to call totalNotifications: {}" ,
178
+ call_result. message
179
+ ) ;
180
+
181
+ // Decode the return value
182
+ let return_data: BytesDe = call_result. ret . unwrap ( ) . deserialize ( ) . unwrap ( ) ;
183
+ let total_notifications = AlloyU256 :: abi_decode ( & return_data. 0 )
184
+ . expect ( "Failed to decode totalNotifications return value" ) ;
185
+ assert_eq ! (
186
+ total_notifications,
187
+ AlloyU256 :: from( expected_count) ,
188
+ "Expected {} notification(s), got {}" ,
189
+ expected_count,
190
+ total_notifications
191
+ ) ;
192
+ }
193
+
194
+ /// Struct to hold all notification values for checking against contract state.
195
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
196
+ pub struct ExpectedNotification {
197
+ pub sector : u64 ,
198
+ pub minimum_commitment_epoch : i64 ,
199
+ pub piece_cid : Cid ,
200
+ pub piece_size : u64 ,
201
+ pub payload : Vec < u8 > ,
202
+ }
203
+
204
+ pub fn check_receiver_notification_at ( v : & dyn VM , sender_addr : & Address , receiver_addr : & Address , index : u64 , expected : & ExpectedNotification ) {
205
+ let call_params =
206
+ NotificationReceiver :: getNotificationCall:: new ( ( AlloyU256 :: from ( index) , ) ) . abi_encode ( ) ;
207
+ let call_result = v
208
+ . execute_message (
209
+ & sender_addr,
210
+ & receiver_addr,
211
+ & TokenAmount :: zero ( ) ,
212
+ fil_actor_evm:: Method :: InvokeContract as u64 ,
213
+ Some ( serialize_ok ( & ContractParams ( call_params. to_vec ( ) ) ) ) ,
214
+ )
215
+ . unwrap ( ) ;
216
+ assert ! (
217
+ call_result. code. is_success( ) ,
218
+ "Failed to call getNotification: {}" ,
219
+ call_result. message
220
+ ) ;
221
+
222
+ // Decode the return value - it returns a tuple of (uint64, int64, bytes, uint64, bytes)
223
+ let return_data: BytesDe = call_result. ret . unwrap ( ) . deserialize ( ) . unwrap ( ) ;
224
+
225
+ // Use the generated abi_decode_returns function
226
+ let notification_result =
227
+ NotificationReceiver :: getNotificationCall:: abi_decode_returns ( & return_data. 0 )
228
+ . expect ( "Failed to decode getNotification return value" ) ;
229
+
230
+ let received_sector = notification_result. sector ;
231
+ let minimum_commitment_epoch = notification_result. minimumCommitmentEpoch ;
232
+ let data_cid_bytes = notification_result. dataCid ;
233
+ let received_piece_size = notification_result. pieceSize ;
234
+ let received_payload = notification_result. payload ;
235
+
236
+ assert_eq ! (
237
+ received_sector, expected. sector,
238
+ "Sector number mismatch: expected {}, got {}" ,
239
+ expected. sector, received_sector
240
+ ) ;
241
+
242
+ assert_eq ! (
243
+ received_piece_size, expected. piece_size,
244
+ "Piece size mismatch: expected {}, got {}" ,
245
+ expected. piece_size, received_piece_size
246
+ ) ;
247
+
248
+ let expected_payload_bytes = expected. payload . to_vec ( ) ;
249
+ assert_eq ! (
250
+ received_payload. as_ref( ) ,
251
+ expected_payload_bytes. as_slice( ) ,
252
+ "Payload mismatch: expected 0x{:x?}, got 0x{:x?}" ,
253
+ & expected_payload_bytes,
254
+ & received_payload. as_ref( )
255
+ ) ;
256
+
257
+ // Check the piece CID data is present
258
+ // The contract receives the CID with an extra leading byte from the CBOR encoding,
259
+ // so we verify it contains the expected CID data after the first byte
260
+ let expected_cid_bytes = expected. piece_cid . to_bytes ( ) ;
261
+ assert ! ( !data_cid_bytes. is_empty( ) , "Data CID should not be empty" ) ;
262
+ // Verify the CID data matches
263
+ assert_eq ! ( data_cid_bytes[ 0 ] , 0 , "Data CID should start with 0x00 for ipld cbor reasons" ) ;
264
+ assert_eq ! (
265
+ & data_cid_bytes[ 1 ..] ,
266
+ expected_cid_bytes,
267
+ "Piece CID data mismatch: expected {:x?}, got {:x?}" ,
268
+ & expected_cid_bytes,
269
+ & data_cid_bytes[ 1 ..]
270
+ ) ;
271
+
272
+ assert_eq ! (
273
+ minimum_commitment_epoch, expected. minimum_commitment_epoch,
274
+ "Minimum commitment epoch mismatch: expected {}, got {}" ,
275
+ expected. minimum_commitment_epoch, minimum_commitment_epoch
276
+ ) ;
257
277
}
278
+
279
+
0 commit comments