Skip to content

Commit 330b8ee

Browse files
committed
fix this
1 parent 52e680a commit 330b8ee

File tree

5 files changed

+210
-44
lines changed

5 files changed

+210
-44
lines changed

target_chains/ethereum/contracts/contracts/entropy/Entropy.sol

Lines changed: 125 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol";
88
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
99
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
1010
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
11+
import "ExcessivelySafeCall/ExcessivelySafeCall.sol";
1112
import "./EntropyState.sol";
1213

1314
// Entropy implements a secure 2-party random number generation procedure. The protocol
@@ -76,6 +77,8 @@ import "./EntropyState.sol";
7677
// the user is always incentivized to reveal their random number, and that the protocol has an escape hatch for
7778
// cases where the user chooses not to reveal.
7879
abstract contract Entropy is IEntropy, EntropyState {
80+
using ExcessivelySafeCall for address;
81+
7982
function _initialize(
8083
address admin,
8184
uint128 pythFeeInWei,
@@ -203,7 +206,8 @@ abstract contract Entropy is IEntropy, EntropyState {
203206
address provider,
204207
bytes32 userCommitment,
205208
bool useBlockhash,
206-
bool isRequestWithCallback
209+
bool isRequestWithCallback,
210+
uint64 callbackGasLimit
207211
) internal returns (EntropyStructs.Request storage req) {
208212
EntropyStructs.ProviderInfo storage providerInfo = _state.providers[
209213
provider
@@ -220,7 +224,10 @@ abstract contract Entropy is IEntropy, EntropyState {
220224
// Check that fees were paid and increment the pyth / provider balances.
221225
uint128 requiredFee = getFee(provider);
222226
if (msg.value < requiredFee) revert EntropyErrors.InsufficientFee();
223-
providerInfo.accruedFeesInWei += providerInfo.feeInWei;
227+
providerInfo.accruedFeesInWei += getProviderFee(
228+
provider,
229+
callbackGasLimit
230+
);
224231
_state.accruedPythFeesInWei += (SafeCast.toUint128(msg.value) -
225232
providerInfo.feeInWei);
226233

@@ -245,9 +252,21 @@ abstract contract Entropy is IEntropy, EntropyState {
245252
);
246253
req.requester = msg.sender;
247254

248-
req.blockNumber = SafeCast.toUint64(block.number);
249-
req.useBlockhash = useBlockhash;
250-
req.isRequestWithCallback = isRequestWithCallback;
255+
if (useBlockhash && isRequestWithCallback) {
256+
revert EntropyErrors.AssertionFailure();
257+
} else if (isRequestWithCallback) {
258+
req.isRequestWithCallback = isRequestWithCallback;
259+
if (callbackGasLimit == 0) {
260+
req.blockNumber = providerInfo.defaultGasLimit;
261+
} else {
262+
req.blockNumber = callbackGasLimit;
263+
}
264+
req.useBlockhash = false;
265+
} else {
266+
req.isRequestWithCallback = false;
267+
req.blockNumber = SafeCast.toUint64(block.number);
268+
req.useBlockhash = useBlockhash;
269+
}
251270
}
252271

253272
// As a user, request a random number from `provider`. Prior to calling this method, the user should
@@ -269,7 +288,8 @@ abstract contract Entropy is IEntropy, EntropyState {
269288
provider,
270289
userCommitment,
271290
useBlockHash,
272-
false
291+
false,
292+
0
273293
);
274294
assignedSequenceNumber = req.sequenceNumber;
275295
emit Requested(req);
@@ -294,7 +314,35 @@ abstract contract Entropy is IEntropy, EntropyState {
294314
// If we remove the blockHash from this, the provider would have no choice but to provide its committed
295315
// random number. Hence, useBlockHash is set to false.
296316
false,
297-
true
317+
true,
318+
0
319+
);
320+
321+
emit RequestedWithCallback(
322+
provider,
323+
req.requester,
324+
req.sequenceNumber,
325+
userRandomNumber,
326+
req
327+
);
328+
329+
return req.sequenceNumber;
330+
}
331+
332+
function requestWithCallbackAndGas(
333+
address provider,
334+
bytes32 userRandomNumber,
335+
uint64 gasLimit
336+
) public payable override returns (uint64) {
337+
EntropyStructs.Request storage req = requestHelper(
338+
provider,
339+
constructUserCommitment(userRandomNumber),
340+
// If useBlockHash is set to true, it allows a scenario in which the provider and miner can collude.
341+
// If we remove the blockHash from this, the provider would have no choice but to provide its committed
342+
// random number. Hence, useBlockHash is set to false.
343+
false,
344+
true,
345+
gasLimit
298346
);
299347

300348
emit RequestedWithCallback(
@@ -489,26 +537,21 @@ abstract contract Entropy is IEntropy, EntropyState {
489537

490538
address callAddress = req.requester;
491539

492-
// Check if the callAddress is a contract account.
493-
// TODO: this can probably be deleted.
494-
uint len;
495-
assembly {
496-
len := extcodesize(callAddress)
497-
}
540+
if (req.blockNumber != 0 && !req.callbackAttempted) {
541+
// TODO: need to validate that we have enough gas left to forward (?)
542+
// Or at least that we forwarded enough gas before marking the callback as failed
543+
/*
544+
if (gasleft() < req.blockNumber) {
498545
499-
bool success;
500-
bytes memory ret;
501-
if (len != 0) {
502-
uint64 gas;
503-
if (req.blockNumber != 0) {
504-
gas = req.blockNumber;
505-
} else {
506-
gas = gasLeft();
507546
}
547+
*/
508548

509549
req.reentryGuard = true;
510-
(success, ret) == callAddress.excessivelySafeCall(
511-
gas,
550+
bool success;
551+
bytes memory ret;
552+
(success, ret) = callAddress.excessivelySafeCall(
553+
req.blockNumber,
554+
0,
512555
32,
513556
abi.encodeWithSelector(
514557
IEntropyConsumer._entropyCallback.selector,
@@ -518,31 +561,52 @@ abstract contract Entropy is IEntropy, EntropyState {
518561
)
519562
);
520563
req.reentryGuard = false;
521-
}
522564

523-
if (success) {
565+
if (success) {
566+
emit RevealedWithCallback(
567+
req,
568+
userRandomNumber,
569+
providerRevelation,
570+
randomNumber
571+
);
572+
clearRequest(provider, sequenceNumber);
573+
} else {
574+
bytes32 errorReason;
575+
assembly {
576+
errorReason := mload(add(ret, 32))
577+
}
578+
579+
emit CallbackFailed(
580+
provider,
581+
req.requester,
582+
sequenceNumber,
583+
errorReason
584+
);
585+
req.callbackAttempted = true;
586+
}
587+
} else {
524588
emit RevealedWithCallback(
525589
req,
526590
userRandomNumber,
527591
providerRevelation,
528592
randomNumber
529593
);
530594
clearRequest(provider, sequenceNumber);
531-
} else {
532-
bytes32 errorReason;
595+
596+
// Check if the callAddress is a contract account.
597+
uint len;
533598
assembly {
534-
errorReason := mload(add(ret, 32));
599+
len := extcodesize(callAddress)
535600
}
536601

537-
emit CallbackFailed(
538-
provider,
539-
req.requester,
540-
sequenceNumber,
541-
errorReason
542-
)
543-
req.callbackAttempted = true;
602+
if (len != 0) {
603+
IEntropyConsumer(callAddress)._entropyCallback(
604+
sequenceNumber,
605+
provider,
606+
randomNumber
607+
);
608+
}
544609
}
545-
546610
}
547611

548612
function getProviderInfo(
@@ -570,7 +634,30 @@ abstract contract Entropy is IEntropy, EntropyState {
570634
function getFee(
571635
address provider
572636
) public view override returns (uint128 feeAmount) {
573-
return _state.providers[provider].feeInWei + _state.pythFeeInWei;
637+
return getFeeForGas(provider, 0);
638+
}
639+
640+
function getFeeForGas(
641+
address provider,
642+
uint64 gasLimit
643+
) public view override returns (uint128 feeAmount) {
644+
return getProviderFee(provider, gasLimit) + _state.pythFeeInWei;
645+
}
646+
647+
function getProviderFee(
648+
address providerAddr,
649+
uint64 gasLimit
650+
) internal view returns (uint128 feeAmount) {
651+
EntropyStructs.ProviderInfo memory provider = _state.providers[
652+
providerAddr
653+
];
654+
if (gasLimit > provider.defaultGasLimit) {
655+
uint128 additionalFee = ((gasLimit - provider.defaultGasLimit) *
656+
provider.feeInWei) / provider.defaultGasLimit;
657+
return provider.feeInWei + additionalFee;
658+
} else {
659+
return provider.feeInWei;
660+
}
574661
}
575662

576663
function getPythFee() public view returns (uint128 feeAmount) {
@@ -678,11 +765,7 @@ abstract contract Entropy is IEntropy, EntropyState {
678765

679766
uint64 oldGasLimit = provider.defaultGasLimit;
680767
provider.defaultGasLimit = gasLimit;
681-
emit ProviderDefaultGasLimitUpdated(
682-
msg.sender,
683-
oldGasLimit,
684-
gasLimit
685-
);
768+
emit ProviderDefaultGasLimitUpdated(msg.sender, oldGasLimit, gasLimit);
686769
}
687770

688771
function constructUserCommitment(

target_chains/ethereum/contracts/forge-test/Entropy.t.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,10 +801,12 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents {
801801
providerInfo.currentCommitment
802802
)
803803
),
804-
blockNumber: 1234,
804+
blockNumber: 0,
805805
requester: user1,
806806
useBlockhash: false,
807-
isRequestWithCallback: true
807+
isRequestWithCallback: true,
808+
callbackAttempted: false,
809+
reentryGuard: false
808810
})
809811
);
810812
vm.roll(1234);

target_chains/ethereum/contracts/remappings.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
@pythnetwork/=./node_modules/@pythnetwork/
44
ds-test/=lib/forge-std/lib/ds-test/src/
55
forge-std/=lib/forge-std/src/
6+
ExcessivelySafeCall=lib/ExcessivelySafeCall/src/
67
truffle/=./node_modules/truffle/

target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ interface IEntropy is EntropyEvents {
5656
bytes32 userRandomNumber
5757
) external payable returns (uint64 assignedSequenceNumber);
5858

59+
function requestWithCallbackAndGas(
60+
address provider,
61+
bytes32 userRandomNumber,
62+
uint64 gasLimit
63+
) external payable returns (uint64 assignedSequenceNumber);
64+
5965
// Fulfill a request for a random number. This method validates the provided userRandomness and provider's proof
6066
// against the corresponding commitments in the in-flight request. If both values are validated, this function returns
6167
// the corresponding random number.
@@ -100,6 +106,11 @@ interface IEntropy is EntropyEvents {
100106

101107
function getFee(address provider) external view returns (uint128 feeAmount);
102108

109+
function getFeeForGas(
110+
address provider,
111+
uint64 gasLimit
112+
) external view returns (uint128 feeAmount);
113+
103114
function getAccruedPythFees()
104115
external
105116
view
@@ -124,6 +135,9 @@ interface IEntropy is EntropyEvents {
124135
// the provider supports for callbacks.
125136
function setMaxNumHashes(uint32 maxNumHashes) external;
126137

138+
// Set the default gas limit for a request. If 0, no
139+
function setDefaultGasLimit(uint64 gasLimit) external;
140+
127141
// Advance the provider commitment and increase the sequence number.
128142
// This is used to reduce the `numHashes` required for future requests which leads to reduced gas usage.
129143
function advanceProviderCommitment(

0 commit comments

Comments
 (0)