@@ -17,10 +17,10 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
1717 bytes32 public constant ADMIN = keccak256 ('ADMIN ' );
1818 bytes32 public constant DEPLOYER_ROLE = keccak256 ('DEPLOYER_ROLE ' );
1919 uint8 public constant PROTOCOL_VERSION = 0 ;
20- uint64 public constant APPLICATION_ID = 1 ;
21-
2220 //state variables
23- bytes32 public stateRoot;
21+ mapping (uint64 => bytes32 ) public applicationStateRoots;
22+ uint256 public maxNumOfApplications = 10 ;
23+ uint256 public availableDeploySlots = maxNumOfApplications;
2424
2525 mapping (bytes32 => Structs.PendingRequest) public requestById;
2626 mapping (uint256 => bytes32 ) private _requestIdByOrder;
@@ -44,7 +44,7 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
4444 }
4545
4646 modifier validApplicationId (uint64 applicationId ) {
47- if (applicationId != APPLICATION_ID ) revert InvalidApplicationId ();
47+ if (applicationStateRoots[ applicationId] == bytes32 ( 0 ) ) revert InvalidApplicationId ();
4848 _;
4949 }
5050
@@ -84,7 +84,7 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
8484 Structs.RequestType requestType ,
8585 bytes calldata payload ,
8686 uint256 depositAmount , // part of the sent value forwarded to the application, for app logic
87- uint256 maxFeeValue // part ot the sent value reserved for fee payment
87+ uint256 maxFeeValue // part of the sent value reserved for fee payment
8888 )
8989 external
9090 payable
@@ -93,16 +93,16 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
9393 nonReentrant
9494 returns (bytes32 )
9595 {
96+ if (requestType == Structs.RequestType.DEPLOYAPP) revert InvalidRequestType ();
97+
9698 //check values
9799 if (msg .value != depositAmount + maxFeeValue) revert InvalidValue ();
98100 if (maxFeeValue < minFeePerRequest) revert FeeValueBelowMinimum ();
99101
100102 //check queue size
101103 if (getPendingRequestsSize () >= maxQueueSize) revert QueueThresholdExceeded ();
102104
103- if (requestType == Structs.RequestType.DEPLOYAPP) {
104- if (! hasRole (DEPLOYER_ROLE, msg .sender )) revert DeployerNotAllowed ();
105- } else if (requestType == Structs.RequestType.ASSOCIATEKEY) {
105+ if (requestType == Structs.RequestType.ASSOCIATEKEY) {
106106 //if requestype is associatekey, the payload must be 133 bytes long (contains a Secp521r1_PubKey)
107107 if (payload.length != 133 ) revert InvalidPayload ();
108108 } else if (requestType == Structs.RequestType.DEANONYMIZATION) {
@@ -139,7 +139,55 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
139139 }
140140
141141 //emit event
142- emit RequestSubmitted (requestId, msg .sender );
142+ emit RequestSubmitted (applicationId, requestId, msg .sender );
143+
144+ return requestId;
145+ }
146+
147+ /// @inheritdoc IProcessorEndpoint
148+ function submitDeployRequest (
149+ uint8 protocolVersion ,
150+ bytes calldata payload
151+ ) external payable validProtocolVersion (protocolVersion) nonReentrant returns (bytes32 ) {
152+ if (! hasRole (DEPLOYER_ROLE, msg .sender )) revert DeployerNotAllowed ();
153+ if (availableDeploySlots == 0 ) revert MaxNumOfApplicationsExceeded ();
154+ //check queue size
155+ if (getPendingRequestsSize () >= maxQueueSize) revert QueueThresholdExceeded ();
156+ if (msg .value < minFeePerRequest) revert FeeValueBelowMinimum ();
157+
158+ -- availableDeploySlots;
159+
160+ Structs.RequestType requestType = Structs.RequestType.DEPLOYAPP;
161+ //create request
162+ bytes32 requestId = generateRequestId (
163+ msg .sender ,
164+ 0 , // deploy requests have applicationId 0, a unique applicationId will be derived from the requestId for each deploy request to avoid collisions with regular requests and to group deploy requests together
165+ requestType,
166+ payload,
167+ 0 ,
168+ _tail
169+ );
170+
171+ uint64 applicationId = uint64 (bytes8 (requestId)); // Derive a unique application ID from the request ID for deploy requests
172+ requestById[requestId] = Structs.PendingRequest ({
173+ timestamp: block .timestamp ,
174+ depositAmount: 0 ,
175+ maxFeeValue: msg .value ,
176+ requestId: requestId,
177+ payload: payload,
178+ sender: msg .sender ,
179+ applicationId: applicationId,
180+ protocolVersion: protocolVersion,
181+ requestType: requestType
182+ });
183+ _requestIdByOrder[_tail] = requestId;
184+
185+ unchecked {
186+ ++ _tail;
187+ }
188+
189+ //emit event
190+ emit DeployRequestSubmitted (applicationId, requestId, msg .sender );
143191
144192 return requestId;
145193 }
@@ -153,15 +201,28 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
153201 }
154202
155203 function _markRequestCompleted (
204+ uint64 applicationId ,
156205 bytes32 requestId ,
157206 uint256 applicationFees ,
158207 Structs.RequestResult result ,
159208 Structs.ErrorCode errCode ,
160- string memory errorMsg
209+ string memory errorMsg ,
210+ Structs.RequestType requestType
161211 ) private {
162212 _removeRequest ();
163213
164- emit RequestCompleted (requestId, applicationFees, result, errCode, errorMsg);
214+ if (requestType == Structs.RequestType.DEPLOYAPP) {
215+ emit DeployRequestCompleted (
216+ applicationId,
217+ requestId,
218+ applicationFees,
219+ result,
220+ errCode,
221+ errorMsg
222+ );
223+ } else {
224+ emit RequestCompleted (applicationId, requestId, applicationFees, result, errCode, errorMsg);
225+ }
165226
166227 _asyncTransfer (feeCollector, applicationFees);
167228 }
@@ -251,7 +312,7 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
251312 if (applicationId != requestInfo.applicationId) revert InvalidApplicationId ();
252313
253314 //check prev state root
254- if (prevStateRoot != stateRoot ) revert InvalidStateRoot ();
315+ if (prevStateRoot != applicationStateRoots[applicationId] ) revert InvalidStateRoot ();
255316
256317 uint256 eventsLength = events.length ;
257318 uint256 eventSubTypesLength = eventSubTypes.length ;
@@ -283,7 +344,7 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
283344 // For errors: state unchanged (prevStateRoot == newStateRoot), no events, no withdrawals
284345 // Refund user (minus minimum fee) and collect minimum fee
285346 if (eventsLength != 0 || withdrawalRequests.length != 0 ) revert InvalidPayload ();
286- if (stateRoot != newStateRoot) revert InvalidStateRoot ();
347+ if (applicationStateRoots[applicationId] != newStateRoot) revert InvalidStateRoot ();
287348
288349 if (requestInfo.depositAmount + requestInfo.maxFeeValue > _getAvailableBalance ())
289350 revert InsufficientBalance ();
@@ -298,20 +359,28 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
298359 emit Refund (applicationId, processedRequestId, sender, totalRefund);
299360 }
300361
362+ if (requestInfo.requestType == Structs.RequestType.DEPLOYAPP) {
363+ unchecked {
364+ ++ availableDeploySlots;
365+ }
366+ }
367+
301368 _markRequestCompleted (
369+ applicationId,
302370 processedRequestId,
303371 minFeePerRequest,
304372 Structs.RequestResult.FAILED,
305373 Structs.ErrorCode (errorCode),
306- errorMsg
374+ errorMsg,
375+ requestInfo.requestType
307376 );
308377
309378 return ;
310379 }
311380
312381 // Handle success case
313382 // State cannot remain the same
314- if (stateRoot == newStateRoot) revert InvalidStateRoot ();
383+ if (applicationStateRoots[applicationId] == newStateRoot) revert InvalidStateRoot ();
315384
316385 if (refund + applicationFees != maxFeeValue) revert InvalidValue ();
317386 if (applicationFees < minFeePerRequest) {
@@ -348,7 +417,7 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
348417 }
349418
350419 //update state root and request
351- stateRoot = newStateRoot;
420+ applicationStateRoots[applicationId] = newStateRoot;
352421 emit StateRootUpdate (applicationId, processedRequestId, prevStateRoot, newStateRoot);
353422
354423 //credit refund to sender's pending balance (pull pattern)
@@ -359,7 +428,7 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
359428
360429 //credit withdrawals to receivers' pending balances
361430 i = 0 ;
362- while (i < withdrawalRequests. length ) {
431+ while (i < withdrawalsLength ) {
363432 _asyncTransfer (withdrawalRequests[i].receiver, withdrawalRequests[i].amount);
364433 emit Withdrawal (
365434 applicationId,
@@ -374,11 +443,13 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
374443
375444 //set requests as completed
376445 _markRequestCompleted (
446+ applicationId,
377447 processedRequestId,
378448 applicationFees,
379449 Structs.RequestResult.COMPLETED,
380450 Structs.ErrorCode.NO_ERROR,
381- ''
451+ '' ,
452+ reqType
382453 );
383454 }
384455
@@ -389,6 +460,17 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
389460 emit QueueThresholdUpdated (newThreshold);
390461 }
391462
463+ /// @inheritdoc IProcessorEndpoint
464+ function updateMaxNumOfApplications (uint256 newMax ) external onlyRole (ADMIN) {
465+ if (newMax == 0 ) revert InvalidValue ();
466+ uint256 deployedApps = maxNumOfApplications - availableDeploySlots;
467+ if (newMax < deployedApps) revert InvalidValue ();
468+ uint256 oldMax = maxNumOfApplications;
469+ maxNumOfApplications = newMax;
470+ availableDeploySlots = newMax - deployedApps;
471+ emit MaxNumberOfAppUpdated (oldMax, newMax);
472+ }
473+
392474 /// @inheritdoc IProcessorEndpoint
393475 function updateFeeCollector (address payable newFeeCollector ) external onlyRole (ADMIN) {
394476 if (newFeeCollector == address (0 )) revert AddressCantBeZero ();
@@ -422,11 +504,12 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
422504 uint256 numOfRequests = getPendingRequestsSize ();
423505 if (numOfRequests > 0 ) {
424506 bytes32 requestId = _requestIdByOrder[_head];
425- return (requestById[requestId], stateRoot, true );
507+ Structs.PendingRequest storage req = requestById[requestId];
508+ return (req, applicationStateRoots[req.applicationId], true );
426509 }
427510
428511 Structs.PendingRequest memory emptyReq;
429- return (emptyReq, stateRoot , false );
512+ return (emptyReq, bytes32 ( 0 ) , false );
430513 }
431514
432515 /// @inheritdoc IProcessorEndpoint
@@ -468,7 +551,7 @@ contract ProcessorEndpoint is AccessControl, IProcessorEndpoint, ReentrancyGuard
468551 uint256 idx
469552 ) public pure returns (bytes32 ) {
470553 bytes32 requestId = keccak256 (
471- abi.encodePacked (sender, applicationId, requestType, payload, depositAmount, idx)
554+ abi.encode (sender, applicationId, requestType, payload, depositAmount, idx)
472555 );
473556
474557 return requestId;
0 commit comments