@@ -8,6 +8,7 @@ import "./utils/AddressArrayUtils.sol";
88import "./libraries/operator/GroupSelection.sol " ;
99import "./libraries/operator/Groups.sol " ;
1010import "./libraries/operator/DKGResultVerification.sol " ;
11+ import "./libraries/operator/Reimbursements.sol " ;
1112
1213interface ServiceContract {
1314 function entryCreated (uint256 requestId , bytes calldata entry , address payable submitter ) external ;
@@ -95,8 +96,9 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
9596 uint256 public relayEntryTimeout = relayEntryGenerationTime.add (groupSize.mul (resultPublicationBlockStep));
9697
9798 // Gas required to verify BLS signature and produce successful relay
98- // entry. Excludes callback and DKG gas.
99- uint256 public entryVerificationGasEstimate = 240000 ;
99+ // entry. Excludes callback and DKG gas. The worst case (most expensive)
100+ // scenario.
101+ uint256 public entryVerificationGasEstimate = 280000 ;
100102
101103 // Gas required to submit DKG result. Excludes initiation of group selection.
102104 uint256 public dkgGasEstimate = 1740000 ;
@@ -118,6 +120,7 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
118120 struct SigningRequest {
119121 uint256 relayRequestId;
120122 uint256 entryVerificationAndProfitFee;
123+ uint256 callbackFee;
121124 uint256 groupIndex;
122125 bytes previousEntry;
123126 address serviceContract;
@@ -371,17 +374,15 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
371374 uint256 surplus = dkgSubmitterReimbursementFee.sub (reimbursementFee);
372375 dkgSubmitterReimbursementFee = 0 ;
373376 // Reimburse submitter with actual DKG cost.
374- (bool success , ) = magpie.call.value (reimbursementFee)("" );
375- require (success, "Failed reimburse actual DKG cost " );
377+ magpie.call.value (reimbursementFee)("" );
376378
377379 // Return surplus to the contract that started DKG.
378380 groupSelectionStarterContract.fundDkgFeePool.value (surplus)();
379381 } else {
380382 // If submitter used higher gas price reimburse only dkgSubmitterReimbursementFee max.
381383 reimbursementFee = dkgSubmitterReimbursementFee;
382384 dkgSubmitterReimbursementFee = 0 ;
383- (bool success , ) = magpie.call.value (reimbursementFee)("" );
384- require (success, "Failed reimburse DKG fee " );
385+ magpie.call.value (reimbursementFee)("" );
385386 }
386387 }
387388
@@ -403,18 +404,26 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
403404 uint256 requestId ,
404405 bytes memory previousEntry
405406 ) public payable onlyServiceContract {
407+ uint256 entryVerificationAndProfitFee = groupProfitFee ().add (
408+ entryVerificationGasEstimate.mul (gasPriceWithFluctuationMargin (priceFeedEstimate))
409+ );
406410 require (
407- msg .value >= groupProfitFee (). add (entryVerificationGasEstimate. mul ( gasPriceWithFluctuationMargin (priceFeedEstimate))) ,
411+ msg .value >= entryVerificationAndProfitFee ,
408412 "Insufficient new entry fee "
409413 );
410- signRelayEntry (requestId, previousEntry, msg .sender , msg .value );
414+ uint256 callbackFee = msg .value .sub (entryVerificationAndProfitFee);
415+ signRelayEntry (
416+ requestId, previousEntry, msg .sender ,
417+ entryVerificationAndProfitFee, callbackFee
418+ );
411419 }
412420
413421 function signRelayEntry (
414422 uint256 requestId ,
415423 bytes memory previousEntry ,
416424 address serviceContract ,
417- uint256 entryVerificationAndProfitFee
425+ uint256 entryVerificationAndProfitFee ,
426+ uint256 callbackFee
418427 ) internal {
419428 require (! isEntryInProgress () || hasEntryTimedOut (), "Beacon is busy " );
420429
@@ -424,6 +433,7 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
424433 signingRequest = SigningRequest (
425434 requestId,
426435 entryVerificationAndProfitFee,
436+ callbackFee,
427437 groupIndex,
428438 previousEntry,
429439 serviceContract
@@ -455,25 +465,60 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
455465
456466 emit RelayEntrySubmitted ();
457467
458- ServiceContract (signingRequest.serviceContract).entryCreated (
459- signingRequest.relayRequestId,
460- _groupSignature,
461- msg .sender
468+ // Spend no more than groupSelectionGasEstimate + 40000 gas max
469+ // This will prevent relayEntry failure in case the service contract is compromised
470+ signingRequest.serviceContract.call.gas (groupSelectionGasEstimate.add (40000 ))(
471+ abi.encodeWithSignature (
472+ "entryCreated(uint256,bytes,address) " ,
473+ signingRequest.relayRequestId,
474+ _groupSignature,
475+ msg .sender
476+ )
462477 );
463478
479+ if (signingRequest.callbackFee > 0 ) {
480+ executeCallback (signingRequest, uint256 (keccak256 (_groupSignature)));
481+ }
482+
464483 (uint256 groupMemberReward , uint256 submitterReward , uint256 subsidy ) = newEntryRewardsBreakdown ();
465484 groups.addGroupMemberReward (groupPubKey, groupMemberReward);
466485
467- (bool success , ) = stakingContract.magpieOf (msg .sender ).call.value (submitterReward)("" );
468- require (success, "Failed send relay submitter reward " );
486+ stakingContract.magpieOf (msg .sender ).call.value (submitterReward)("" );
469487
470488 if (subsidy > 0 ) {
471- ServiceContract ( signingRequest.serviceContract).fundRequestSubsidyFeePool. value (subsidy)();
489+ signingRequest.serviceContract.call. gas ( 35000 ). value (subsidy)(abi.encodeWithSignature ( " fundRequestSubsidyFeePool() " ) );
472490 }
473491
474492 currentEntryStartBlock = 0 ;
475493 }
476494
495+ /**
496+ * @dev Executes customer specified callback for the relay entry request.
497+ * @param signingRequest Request data tracked internally by this contract.
498+ * @param entry The generated random number.
499+ */
500+ function executeCallback (SigningRequest memory signingRequest , uint256 entry ) internal {
501+ uint256 callbackFee = signingRequest.callbackFee;
502+
503+ // Make sure not to spend more than what was received from the service contract for the callback
504+ uint256 gasLimit = callbackFee.div (gasPriceWithFluctuationMargin (priceFeedEstimate));
505+
506+ bytes memory callbackReturnData;
507+ uint256 gasBeforeCallback = gasleft ();
508+ (, callbackReturnData) = signingRequest.serviceContract.call.gas (gasLimit)(abi.encodeWithSignature ("executeCallback(uint256,uint256) " , signingRequest.relayRequestId, entry));
509+ uint256 gasAfterCallback = gasleft ();
510+ uint256 gasSpent = gasBeforeCallback.sub (gasAfterCallback);
511+
512+ Reimbursements.reimburseCallback (
513+ stakingContract,
514+ priceFeedEstimate,
515+ gasLimit,
516+ gasSpent,
517+ callbackFee,
518+ callbackReturnData
519+ );
520+ }
521+
477522 /**
478523 * @dev Get rewards breakdown in wei for successful entry for the current signing request.
479524 */
@@ -574,7 +619,8 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
574619 signingRequest.relayRequestId,
575620 signingRequest.previousEntry,
576621 signingRequest.serviceContract,
577- signingRequest.entryVerificationAndProfitFee
622+ signingRequest.entryVerificationAndProfitFee,
623+ signingRequest.callbackFee
578624 );
579625 }
580626 }
@@ -659,8 +705,9 @@ contract KeepRandomBeaconOperator is ReentrancyGuard {
659705 function withdrawGroupMemberRewards (address operator , uint256 groupIndex , uint256 [] memory groupMemberIndices ) public nonReentrant {
660706 uint256 accumulatedRewards = groups.withdrawFromGroup (operator, groupIndex, groupMemberIndices);
661707 (bool success , ) = stakingContract.magpieOf (operator).call.value (accumulatedRewards)("" );
662- require (success, "Failed withdraw rewards " );
663- emit GroupMemberRewardsWithdrawn (stakingContract.magpieOf (operator), operator, accumulatedRewards, groupIndex);
708+ if (success) {
709+ emit GroupMemberRewardsWithdrawn (stakingContract.magpieOf (operator), operator, accumulatedRewards, groupIndex);
710+ }
664711 }
665712
666713 /**
0 commit comments