1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^ 0.8.19 ;
3
+
4
+ import "./FEVM.sol " ;
5
+
6
+ contract NotificationReceiver {
7
+ // Events to track notifications
8
+ event SectorContentChanged (uint64 indexed method , bytes params );
9
+ event NotificationReceived (uint64 indexed sector , uint64 indexed minimumCommitmentEpoch , bytes32 indexed dataCid );
10
+
11
+ // State variables to track received notifications
12
+ struct SectorNotification {
13
+ uint64 sector;
14
+ uint64 minimumCommitmentEpoch;
15
+ bytes32 dataCid;
16
+ uint256 pieceSize;
17
+ bytes payload;
18
+ uint256 timestamp;
19
+ }
20
+
21
+ SectorNotification[] public notifications;
22
+ mapping (uint64 => uint256 []) public sectorNotificationIndices;
23
+
24
+ // Counter for total notifications received
25
+ uint256 public totalNotifications;
26
+
27
+ // Flag to test different response behaviors
28
+ bool public shouldRejectNotifications = false ;
29
+
30
+ // Method selector for handle_filecoin_method
31
+ bytes4 constant NATIVE_METHOD_SELECTOR = 0x868e10c4 ;
32
+
33
+ // Sector content changed method number
34
+ uint64 constant SECTOR_CONTENT_CHANGED = 86399155 ;
35
+
36
+ /**
37
+ * @dev Toggle whether to reject notifications (for testing)
38
+ */
39
+ function setRejectNotifications (bool _reject ) public {
40
+ shouldRejectNotifications = _reject;
41
+ }
42
+
43
+ /**
44
+ * @dev Get the count of notifications for a specific sector
45
+ */
46
+ function getNotificationCount (uint64 sector ) public view returns (uint256 ) {
47
+ return sectorNotificationIndices[sector].length ;
48
+ }
49
+
50
+ /**
51
+ * @dev Get all notification indices for a sector
52
+ */
53
+ function getSectorNotifications (uint64 sector ) public view returns (uint256 [] memory ) {
54
+ return sectorNotificationIndices[sector];
55
+ }
56
+
57
+ /**
58
+ * @dev Get a specific notification by index
59
+ */
60
+ function getNotification (uint256 index ) public view returns (
61
+ uint64 sector ,
62
+ uint64 minimumCommitmentEpoch ,
63
+ bytes32 dataCid ,
64
+ uint256 pieceSize ,
65
+ bytes memory payload ,
66
+ uint256 timestamp
67
+ ) {
68
+ require (index < notifications.length , "Invalid notification index " );
69
+ SectorNotification memory notif = notifications[index];
70
+ return (
71
+ notif.sector,
72
+ notif.minimumCommitmentEpoch,
73
+ notif.dataCid,
74
+ notif.pieceSize,
75
+ notif.payload,
76
+ notif.timestamp
77
+ );
78
+ }
79
+
80
+ /**
81
+ * @dev Handle incoming Filecoin method calls
82
+ * This is the main entry point for receiving notifications from the miner actor
83
+ */
84
+ function handle_filecoin_method (uint64 method , uint64 , bytes memory params ) public returns (bytes memory ) {
85
+ emit SectorContentChanged (method, params);
86
+
87
+ // Check if this is a sector content changed notification
88
+ if (method == SECTOR_CONTENT_CHANGED) {
89
+ return processSectorContentChanged (params);
90
+ }
91
+
92
+ // For other methods, just acknowledge receipt
93
+ return abi.encode (true );
94
+ }
95
+
96
+ /**
97
+ * @dev Process sector content changed notification
98
+ * Expected params structure (CBOR encoded):
99
+ * {
100
+ * sectors: [{
101
+ * sector: uint64,
102
+ * minimum_commitment_epoch: int64,
103
+ * added: [{
104
+ * data: Cid,
105
+ * size: uint64,
106
+ * payload: bytes
107
+ * }]
108
+ * }]
109
+ * }
110
+ */
111
+ function processSectorContentChanged (bytes memory params ) internal returns (bytes memory ) {
112
+ // In a real implementation, we would decode CBOR here
113
+ // For testing, we'll process the raw bytes and extract key information
114
+
115
+ // Check if we should reject this notification
116
+ if (shouldRejectNotifications) {
117
+ // Return a rejection response
118
+ return encodeRejectionResponse ();
119
+ }
120
+
121
+ // For this test contract, we'll store a simplified version of the notification
122
+ // In production, you would properly decode the CBOR data
123
+
124
+ // Extract some basic info from the params (simplified for testing)
125
+ uint64 sector = extractSectorNumber (params);
126
+ uint64 minimumCommitmentEpoch = extractMinimumCommitmentEpoch (params);
127
+ bytes32 dataCid = extractDataCid (params);
128
+ uint256 pieceSize = extractPieceSize (params);
129
+ bytes memory payload = extractPayload (params);
130
+
131
+ // Store the notification
132
+ uint256 notificationIndex = notifications.length ;
133
+ notifications.push (SectorNotification ({
134
+ sector: sector,
135
+ minimumCommitmentEpoch: minimumCommitmentEpoch,
136
+ dataCid: dataCid,
137
+ pieceSize: pieceSize,
138
+ payload: payload,
139
+ timestamp: block .timestamp
140
+ }));
141
+
142
+ sectorNotificationIndices[sector].push (notificationIndex);
143
+ totalNotifications++ ;
144
+
145
+ emit NotificationReceived (sector, minimumCommitmentEpoch, dataCid);
146
+
147
+ // Return acceptance response
148
+ return encodeAcceptanceResponse ();
149
+ }
150
+
151
+ /**
152
+ * @dev Extract sector number from params (simplified for testing)
153
+ */
154
+ function extractSectorNumber (bytes memory params ) internal pure returns (uint64 ) {
155
+ // In a real implementation, this would properly decode CBOR
156
+ // For testing, return a dummy value or extract from known position
157
+ if (params.length >= 8 ) {
158
+ return uint64 (uint8 (params[7 ])) |
159
+ (uint64 (uint8 (params[6 ])) << 8 ) |
160
+ (uint64 (uint8 (params[5 ])) << 16 ) |
161
+ (uint64 (uint8 (params[4 ])) << 24 );
162
+ }
163
+ return 0 ;
164
+ }
165
+
166
+ /**
167
+ * @dev Extract minimum commitment epoch from params (simplified)
168
+ */
169
+ function extractMinimumCommitmentEpoch (bytes memory params ) internal pure returns (uint64 ) {
170
+ // Simplified extraction for testing
171
+ if (params.length >= 16 ) {
172
+ return uint64 (uint8 (params[15 ])) |
173
+ (uint64 (uint8 (params[14 ])) << 8 ) |
174
+ (uint64 (uint8 (params[13 ])) << 16 ) |
175
+ (uint64 (uint8 (params[12 ])) << 24 );
176
+ }
177
+ return 0 ;
178
+ }
179
+
180
+ /**
181
+ * @dev Extract data CID from params (simplified)
182
+ */
183
+ function extractDataCid (bytes memory params ) internal pure returns (bytes32 ) {
184
+ // Simplified extraction for testing
185
+ if (params.length >= 48 ) {
186
+ bytes32 cid;
187
+ assembly {
188
+ cid := mload (add (params, 48 ))
189
+ }
190
+ return cid;
191
+ }
192
+ return bytes32 (0 );
193
+ }
194
+
195
+ /**
196
+ * @dev Extract piece size from params (simplified)
197
+ */
198
+ function extractPieceSize (bytes memory params ) internal pure returns (uint256 ) {
199
+ // Simplified extraction for testing
200
+ if (params.length >= 24 ) {
201
+ return uint256 (uint64 (uint8 (params[23 ])) |
202
+ (uint64 (uint8 (params[22 ])) << 8 ) |
203
+ (uint64 (uint8 (params[21 ])) << 16 ) |
204
+ (uint64 (uint8 (params[20 ])) << 24 ));
205
+ }
206
+ return 0 ;
207
+ }
208
+
209
+ /**
210
+ * @dev Extract payload from params (simplified)
211
+ */
212
+ function extractPayload (bytes memory params ) internal pure returns (bytes memory ) {
213
+ // For testing, return a portion of the params as payload
214
+ if (params.length > 64 ) {
215
+ bytes memory payload = new bytes (params.length - 64 );
216
+ for (uint i = 0 ; i < payload.length ; i++ ) {
217
+ payload[i] = params[i + 64 ];
218
+ }
219
+ return payload;
220
+ }
221
+ return "" ;
222
+ }
223
+
224
+ /**
225
+ * @dev Encode an acceptance response for the notification
226
+ * The response should match SectorContentChangedReturn structure
227
+ */
228
+ function encodeAcceptanceResponse () internal pure returns (bytes memory ) {
229
+ // Return a properly formatted response indicating acceptance
230
+ // Structure: { sectors: [{ added: [{ accepted: true }] }] }
231
+ // For simplified testing, return a basic acceptance
232
+ return abi.encode (true );
233
+ }
234
+
235
+ /**
236
+ * @dev Encode a rejection response for the notification
237
+ */
238
+ function encodeRejectionResponse () internal pure returns (bytes memory ) {
239
+ // Return a properly formatted response indicating rejection
240
+ return abi.encode (false );
241
+ }
242
+
243
+ /**
244
+ * @dev Fallback function to handle direct calls
245
+ */
246
+ fallback () external payable {
247
+ // Check if this is a handle_filecoin_method call
248
+ if (msg .data .length >= 4 && bytes4 (msg .data [0 :4 ]) == NATIVE_METHOD_SELECTOR) {
249
+ // Decode the parameters
250
+ (uint64 method , uint64 codec , bytes memory params ) = abi.decode (msg .data [4 :], (uint64 , uint64 , bytes ));
251
+ bytes memory result = handle_filecoin_method (method, codec, params);
252
+
253
+ // Return the result
254
+ assembly {
255
+ return (add (result, 0x20 ), mload (result))
256
+ }
257
+ }
258
+ }
259
+
260
+ receive () external payable {}
261
+ }
0 commit comments