Skip to content

Commit 80fc09a

Browse files
committed
more stuff
1 parent 0c12829 commit 80fc09a

File tree

3 files changed

+23
-86
lines changed

3 files changed

+23
-86
lines changed

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,11 @@ abstract contract Entropy is IEntropy, EntropyState {
521521
bytes memory ret;
522522
uint256 startingGas = gasleft();
523523
(success, ret) = callAddress.excessivelySafeCall(
524+
// Warning: the provided gas limit below is only an *upper bound* on the gas provided to the call.
525+
// If the current context has less gas the desired value, 63/64ths of the current context's gas will
526+
// be provided instead. (See CALL opcode docs here https://www.evm.codes/?fork=cancun#f1)
527+
// Consequently, out-of-gas reverts need to be handled carefully to ensure that the callback
528+
// was truly provided with a sufficient amount of gas.
524529
uint256(req.gasLimit10k) * 10000,
525530
256, // copy at most 256 bytes of the return value into ret.
526531
abi.encodeWithSelector(
@@ -543,7 +548,9 @@ abstract contract Entropy is IEntropy, EntropyState {
543548
);
544549
clearRequest(provider, sequenceNumber);
545550
} else if (ret.length > 0 || startingGas - endingGas >= uint256(req.gasLimit10k) * 10000) {
546-
// Callback reverted for some reason. Note ret.length == 0 implies out of gas.
551+
// The callback reverted for some reason.
552+
// If ret.length == 0, then the callback ran out of gas. In this case, ensure that the
553+
// callback was provided with sufficient gas.
547554
emit CallbackFailed(
548555
provider,
549556
req.requester,
@@ -555,7 +562,13 @@ abstract contract Entropy is IEntropy, EntropyState {
555562
);
556563
req.callbackStatus = EntropyStatusConstants.CALLBACK_FAILED;
557564
} else {
558-
require(false, "provider needs to send more gas");
565+
// Callback reverted by running out of gas, but the calling context did not have enough gas
566+
// to run the callback. This is a corner case that can happen due to the nuances of gas passing
567+
// in calls (see the comment on the call above).
568+
//
569+
// (Note that reverting here plays nicely with the estimateGas RPC method, which binary searches for
570+
// the smallest gas value that causes the transaction to *succeed*. See https://github.com/ethereum/go-ethereum/pull/3587 )
571+
revert EntropyErrors.InsufficientGas();
559572
}
560573
} else {
561574
// This case uses the checks-effects-interactions pattern to avoid reentry attacks

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

Lines changed: 6 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,12 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents {
12261226
assertEq(reqAfterReveal.sequenceNumber, 0);
12271227
}
12281228

1229+
// Test the corner case caused by the CALL opcode passing at most 63/64ths of the current gas
1230+
// to the sub-call.
12291231
function testRequestWithCallbackUsingTooMuchGas2() public {
1232+
// With a 64M gas limit, we will pass ~63M gas to the callback (which is insufficient), but still
1233+
// have ~1M gas to execute code within the revealWithCallback method, which should be enough to
1234+
// run all of the logic subsequent to the excessivelySafeCall.
12301235
uint32 defaultGasLimit = 64000000;
12311236
vm.prank(provider1);
12321237
random.setDefaultGasLimit(defaultGasLimit);
@@ -1246,98 +1251,15 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents {
12461251
assignedSequenceNumber
12471252
);
12481253

1249-
// Verify the gas limit was set correctly
1250-
// assertEq(req.gasLimit10k, 10);
1251-
12521254
// The transaction reverts if the provider does not provide enough gas to forward
12531255
// the gasLimit to the callback transaction.
1254-
vm.expectRevert();
1256+
vm.expectRevert(EntropyErrors.InsufficientGas.selector);
12551257
random.revealWithCallback{gas: defaultGasLimit - 10000}(
12561258
provider1,
12571259
assignedSequenceNumber,
12581260
userRandomNumber,
12591261
provider1Proofs[assignedSequenceNumber]
12601262
);
1261-
1262-
/*
1263-
// If called with enough gas, the transaction should succeed, but the callback should fail because
1264-
// it uses too much gas.
1265-
vm.expectEmit(false, false, false, true, address(random));
1266-
emit CallbackFailed(
1267-
provider1,
1268-
address(consumer),
1269-
assignedSequenceNumber,
1270-
userRandomNumber,
1271-
provider1Proofs[assignedSequenceNumber],
1272-
random.combineRandomValues(
1273-
userRandomNumber,
1274-
provider1Proofs[assignedSequenceNumber],
1275-
0
1276-
),
1277-
// out-of-gas reverts have an empty bytes array as the return value.
1278-
""
1279-
);
1280-
random.revealWithCallback(
1281-
provider1,
1282-
assignedSequenceNumber,
1283-
userRandomNumber,
1284-
provider1Proofs[assignedSequenceNumber]
1285-
);
1286-
1287-
// Verify request is still active after failure
1288-
EntropyStructs.Request memory reqAfterFailure = random.getRequest(
1289-
provider1,
1290-
assignedSequenceNumber
1291-
);
1292-
assertEq(reqAfterFailure.sequenceNumber, assignedSequenceNumber);
1293-
assertEq(
1294-
reqAfterFailure.status,
1295-
EntropyConstants.STATUS_CALLBACK_FAILED
1296-
);
1297-
1298-
// A subsequent attempt passing insufficient gas should also revert
1299-
vm.expectRevert();
1300-
random.revealWithCallback{gas: defaultGasLimit - 10000}(
1301-
provider1,
1302-
assignedSequenceNumber,
1303-
userRandomNumber,
1304-
provider1Proofs[assignedSequenceNumber]
1305-
);
1306-
1307-
// Again, request stays active after failure
1308-
reqAfterFailure = random.getRequest(provider1, assignedSequenceNumber);
1309-
assertEq(reqAfterFailure.sequenceNumber, assignedSequenceNumber);
1310-
assertEq(
1311-
reqAfterFailure.status,
1312-
EntropyConstants.STATUS_CALLBACK_FAILED
1313-
);
1314-
1315-
// Calling without a gas limit should succeed
1316-
vm.expectEmit(false, false, false, true, address(random));
1317-
emit RevealedWithCallback(
1318-
reqAfterFailure,
1319-
userRandomNumber,
1320-
provider1Proofs[assignedSequenceNumber],
1321-
random.combineRandomValues(
1322-
userRandomNumber,
1323-
provider1Proofs[assignedSequenceNumber],
1324-
0
1325-
)
1326-
);
1327-
random.revealWithCallback(
1328-
provider1,
1329-
assignedSequenceNumber,
1330-
userRandomNumber,
1331-
provider1Proofs[assignedSequenceNumber]
1332-
);
1333-
1334-
// Verify request is cleared after successful reveal
1335-
EntropyStructs.Request memory reqAfterReveal = random.getRequest(
1336-
provider1,
1337-
assignedSequenceNumber
1338-
);
1339-
assertEq(reqAfterReveal.sequenceNumber, 0);
1340-
*/
13411263
}
13421264

13431265
function testLastRevealedTooOld() public {

target_chains/ethereum/entropy_sdk/solidity/EntropyErrors.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ library EntropyErrors {
4343
error LastRevealedTooOld();
4444
// A more recent commitment is already revealed on-chain
4545
error UpdateTooOld();
46+
// Not enough gas was provided to the function to execute the callback with the desired amount of gas.
47+
error InsufficientGas();
4648
}

0 commit comments

Comments
 (0)