@@ -1457,6 +1457,12 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents {
14571457 provider1
14581458 );
14591459 assertEq (info.defaultGasLimit, newGasLimit);
1460+
1461+ // Can reset back to 0.
1462+ vm.prank (provider1);
1463+ random.setDefaultGasLimit (0 );
1464+ info = random.getProviderInfo (provider1);
1465+ assertEq (info.defaultGasLimit, 0 );
14601466 }
14611467
14621468 function testSetDefaultGasLimitRevertIfNotFromProvider () public {
@@ -1509,136 +1515,210 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents {
15091515 assertEq (req.gasLimit10k, 20 );
15101516 }
15111517
1512- function testRequestWithCallbackAndGasLimitFeeScaling () public {
1513- uint32 defaultGasLimit = 100000 ;
1514- uint32 doubleGasLimit = 200000 ;
1515-
1518+ function testGasLimitsAndFeeRounding () public {
15161519 vm.prank (provider1);
1517- random.setDefaultGasLimit (defaultGasLimit);
1520+ random.setDefaultGasLimit (20000 );
1521+ vm.prank (provider1);
1522+ random.setProviderFee (1 );
15181523
1519- uint baseFee = random.getFee (provider1);
1520- assertEq (baseFee, provider1FeeInWei + pythFeeInWei);
1524+ // Test exact multiples of 10,000
1525+ assertGasLimitAndFee (0 , 2 , 1 );
1526+ assertGasLimitAndFee (10000 , 2 , 1 );
1527+ assertGasLimitAndFee (20000 , 2 , 1 );
1528+ assertGasLimitAndFee (100000 , 10 , 5 );
15211529
1522- // Fee scales proportionally with gas limit
1523- uint scaledFee = random.getFeeForGas (provider1, doubleGasLimit);
1524- assertEq (scaledFee, 2 * provider1FeeInWei + pythFeeInWei);
1525- }
1530+ // Test values just below multiples of 10,000
1531+ assertGasLimitAndFee (9999 , 2 , 1 );
1532+ assertGasLimitAndFee (19999 , 2 , 1 );
1533+ assertGasLimitAndFee (39999 , 4 , 2 );
1534+ assertGasLimitAndFee (99999 , 10 , 5 );
15261535
1527- function testRequestWithCallbackAndGasLimitInsufficientFee () public {
1528- uint32 defaultGasLimit = 100000 ;
1529- uint32 doubleGasLimit = 200000 ;
1536+ // Test values just above multiples of 10,000
1537+ assertGasLimitAndFee (10001 , 2 , 1 );
1538+ assertGasLimitAndFee (20001 , 3 , 1 );
1539+ assertGasLimitAndFee (100001 , 11 , 5 );
1540+ assertGasLimitAndFee (110001 , 12 , 6 );
1541+
1542+ // Test middle values
1543+ assertGasLimitAndFee (5000 , 2 , 1 );
1544+ assertGasLimitAndFee (15000 , 2 , 1 );
1545+ assertGasLimitAndFee (25000 , 3 , 1 );
1546+
1547+ // Test maximum value
1548+ assertGasLimitAndFee (
1549+ uint32 (type (uint16 ).max) * 10000 ,
1550+ type (uint16 ).max,
1551+ uint128 (type (uint16 ).max) / 2
1552+ );
15301553
1554+ // A provider with a 0 gas limit is opted-out of the failure state flow, indicated by
1555+ // a 0 gas limit on all requests.
15311556 vm.prank (provider1);
1532- random.setDefaultGasLimit (defaultGasLimit );
1557+ random.setDefaultGasLimit (0 );
15331558
1559+ assertGasLimitAndFee (0 , 0 , 1 );
1560+ assertGasLimitAndFee (10000 , 0 , 1 );
1561+ assertGasLimitAndFee (20000 , 0 , 1 );
1562+ assertGasLimitAndFee (100000 , 0 , 1 );
1563+ }
1564+
1565+ // Helper method to create a request with a specific gas limit and check the gasLimit10k / provider fees
1566+ function assertGasLimitAndFee (
1567+ uint32 gasLimit ,
1568+ uint16 expectedGasLimit10k ,
1569+ uint128 expectedProviderFee
1570+ ) internal {
1571+ // Create a request with callback
15341572 bytes32 userRandomNumber = bytes32 (uint (42 ));
1535- uint baseFee = random.getFee (provider1); // This is insufficient for double gas
1573+ uint fee = random.getFeeForGas (provider1, gasLimit);
1574+ assertEq (fee - random.getPythFee (), expectedProviderFee);
15361575
1537- vm.deal (user1, baseFee);
1576+ // Passing 1 wei less than the expected fee causes a revert.
1577+ vm.deal (user1, fee);
15381578 vm.prank (user1);
15391579 vm.expectRevert (EntropyErrors.InsufficientFee.selector );
1540- random.requestWithCallbackAndGasLimit {value: baseFee }(
1580+ random.requestWithCallbackAndGasLimit {value: fee - 1 }(
15411581 provider1,
15421582 userRandomNumber,
1543- doubleGasLimit
1583+ gasLimit
15441584 );
1545- }
15461585
1547- function testRequestWithCallbackAndGasLimitLowerThanDefault () public {
1548- uint32 defaultGasLimit = 100000 ;
1549- uint32 lowerGasLimit = 50000 ;
1550-
1551- vm.prank (provider1);
1552- random.setDefaultGasLimit (defaultGasLimit);
1553-
1554- bytes32 userRandomNumber = bytes32 (uint (42 ));
1555- uint fee = random.getFeeForGas (provider1, lowerGasLimit);
1556-
1557- vm.deal (user1, fee);
1586+ uint128 startingAccruedProviderFee = random
1587+ .getProviderInfo (provider1)
1588+ .accruedFeesInWei;
15581589 vm.prank (user1);
15591590 uint64 sequenceNumber = random.requestWithCallbackAndGasLimit {
15601591 value: fee
1561- }(provider1, userRandomNumber, lowerGasLimit );
1592+ }(provider1, userRandomNumber, gasLimit );
15621593
1594+ assertEq (
1595+ random.getProviderInfo (provider1).accruedFeesInWei -
1596+ startingAccruedProviderFee,
1597+ expectedProviderFee
1598+ );
1599+
1600+ // Check the gasLimit10k field in the request
15631601 EntropyStructs.Request memory req = random.getRequest (
15641602 provider1,
15651603 sequenceNumber
15661604 );
1567- assertEq (req.gasLimit10k, 5 );
1568- // Fee should be the same as base fee since we're using less gas than default
1569- assertEq (fee, random.getFee (provider1));
1605+ assertEq (req.gasLimit10k, expectedGasLimit10k);
15701606 }
15711607
1572- function testGasLimitsAndFeeRounding () public {
1573- vm.prank (provider1);
1574- random.setDefaultGasLimit (20000 );
1608+ function testCallbackProvidedGas () public {
15751609 vm.prank (provider1);
1576- random.setProviderFee (1 );
1577-
1578- // Test exact multiples of 10,000
1579- assertGasLimit10k (0 , 0 , 1 );
1580- assertGasLimit10k (10000 , 1 , 1 );
1581- assertGasLimit10k (20000 , 2 , 1 );
1582- assertGasLimit10k (100000 , 10 , 5 );
1610+ random.setDefaultGasLimit (200000 );
15831611
1584- // Test values just below multiples of 10,000
1585- assertGasLimit10k (9999 , 1 , 1 );
1586- assertGasLimit10k (19999 , 2 , 1 );
1587- assertGasLimit10k (39999 , 4 , 2 );
1588- assertGasLimit10k (99999 , 10 , 5 );
1612+ assertCallbackResult (0 , 190000 , true );
1613+ assertCallbackResult (0 , 210000 , false );
1614+ assertCallbackResult (300000 , 290000 , true );
1615+ assertCallbackResult (300000 , 310000 , false );
15891616
1590- // Test values just above multiples of 10,000
1591- assertGasLimit10k (10001 , 2 , 1 );
1592- assertGasLimit10k (20001 , 3 , 1 );
1593- assertGasLimit10k (100001 , 11 , 5 );
1594- assertGasLimit10k (110001 , 12 , 6 );
1595-
1596- // Test middle values
1597- assertGasLimit10k (5000 , 1 , 1 );
1598- assertGasLimit10k (15000 , 2 , 1 );
1599- assertGasLimit10k (25000 , 3 , 1 );
1617+ // A provider that hasn't upgraded to the callback failure flow
1618+ // can never cause a callback to fail because it runs out of gas.
1619+ vm.prank (provider1);
1620+ random.setDefaultGasLimit (0 );
16001621
1601- // Test maximum value
1602- assertGasLimit10k (
1603- uint32 (type (uint16 ).max) * 10000 ,
1604- type (uint16 ).max,
1605- uint128 (type (uint16 ).max) / 2
1606- );
1622+ assertCallbackResult (0 , 190000 , true );
1623+ assertCallbackResult (0 , 210000 , true );
1624+ assertCallbackResult (300000 , 290000 , true );
1625+ assertCallbackResult (300000 , 310000 , true );
16071626 }
16081627
1609- // Helper method to create a request with a specific gas limit and check the gasLimit10k field
1610- function assertGasLimit10k (
1628+ // Helper method to assert whether a request with a specific gas limit / a callback with a specific gas cost
1629+ // should be successful or not.
1630+ function assertCallbackResult (
16111631 uint32 gasLimit ,
1612- uint16 expectedGasLimit10k ,
1613- uint128 expectedProviderFee
1632+ uint32 callbackGasUsage ,
1633+ bool expectSuccess
16141634 ) internal {
16151635 // Create a request with callback
16161636 bytes32 userRandomNumber = bytes32 (uint (42 ));
16171637 uint fee = random.getFeeForGas (provider1, gasLimit);
1618- assertEq (fee - random.getPythFee (), expectedProviderFee);
16191638
16201639 vm.deal (user1, fee);
16211640 vm.prank (user1);
1622- uint64 sequenceNumber = random.requestWithCallbackAndGasLimit {
1623- value: fee
1624- }(provider1, userRandomNumber, gasLimit);
1641+ EntropyConsumer consumer = new EntropyConsumer (address (random), false );
1642+ uint64 sequenceNumber = consumer.requestEntropyWithGasLimit {value: fee}(
1643+ userRandomNumber,
1644+ gasLimit
1645+ );
1646+
1647+ consumer.setTargetGasUsage (callbackGasUsage);
16251648
1626- // Check the gasLimit10k field in the request
16271649 EntropyStructs.Request memory req = random.getRequest (
16281650 provider1,
16291651 sequenceNumber
16301652 );
1631- assertEq (req.gasLimit10k, expectedGasLimit10k);
1653+
1654+ if (! expectSuccess) {
1655+ vm.expectEmit (false , false , false , true , address (random));
1656+ emit CallbackFailed (
1657+ provider1,
1658+ address (consumer),
1659+ sequenceNumber,
1660+ userRandomNumber,
1661+ provider1Proofs[sequenceNumber],
1662+ random.combineRandomValues (
1663+ userRandomNumber,
1664+ provider1Proofs[sequenceNumber],
1665+ 0
1666+ ),
1667+ // out-of-gas reverts have an empty bytes array as the return value.
1668+ ""
1669+ );
1670+ random.revealWithCallback (
1671+ provider1,
1672+ sequenceNumber,
1673+ userRandomNumber,
1674+ provider1Proofs[sequenceNumber]
1675+ );
1676+
1677+ // Verify request is still active after failure
1678+ EntropyStructs.Request memory reqAfterFailure = random.getRequest (
1679+ provider1,
1680+ sequenceNumber
1681+ );
1682+ assertEq (reqAfterFailure.sequenceNumber, sequenceNumber);
1683+ assertEq (
1684+ reqAfterFailure.callbackStatus,
1685+ EntropyStatusConstants.CALLBACK_FAILED
1686+ );
1687+ } else {
1688+ vm.expectEmit (false , false , false , true , address (random));
1689+ emit RevealedWithCallback (
1690+ req,
1691+ userRandomNumber,
1692+ provider1Proofs[sequenceNumber],
1693+ random.combineRandomValues (
1694+ userRandomNumber,
1695+ provider1Proofs[sequenceNumber],
1696+ 0
1697+ )
1698+ );
1699+ random.revealWithCallback (
1700+ provider1,
1701+ sequenceNumber,
1702+ userRandomNumber,
1703+ provider1Proofs[sequenceNumber]
1704+ );
1705+
1706+ // Verify request is cleared after successful callback
1707+ EntropyStructs.Request memory reqAfterSuccess = random.getRequest (
1708+ provider1,
1709+ sequenceNumber
1710+ );
1711+ assertEq (reqAfterSuccess.sequenceNumber, 0 );
1712+ }
16321713 }
16331714}
16341715
16351716contract EntropyConsumer is IEntropyConsumer {
16361717 uint64 public sequence;
16371718 bytes32 public randomness;
1638- address public entropy;
16391719 address public provider;
1720+ address public entropy;
16401721 bool public reverts;
1641- uint256 public gasUsed;
16421722 uint256 public targetGasUsage;
16431723
16441724 constructor (address _entropy , bool _reverts ) {
@@ -1656,6 +1736,16 @@ contract EntropyConsumer is IEntropyConsumer {
16561736 }(_provider, randomNumber);
16571737 }
16581738
1739+ function requestEntropyWithGasLimit (
1740+ bytes32 randomNumber ,
1741+ uint32 gasLimit
1742+ ) public payable returns (uint64 sequenceNumber ) {
1743+ address _provider = IEntropy (entropy).getDefaultProvider ();
1744+ sequenceNumber = IEntropy (entropy).requestWithCallbackAndGasLimit {
1745+ value: msg .value
1746+ }(_provider, randomNumber, gasLimit);
1747+ }
1748+
16591749 function getEntropy () internal view override returns (address ) {
16601750 return entropy;
16611751 }
@@ -1665,6 +1755,10 @@ contract EntropyConsumer is IEntropyConsumer {
16651755 }
16661756
16671757 function setTargetGasUsage (uint256 _targetGasUsage ) public {
1758+ require (
1759+ _targetGasUsage > 60000 ,
1760+ "Target gas usage cannot be below 60k (~the cost of storing callback results) "
1761+ );
16681762 targetGasUsage = _targetGasUsage;
16691763 }
16701764
@@ -1674,22 +1768,20 @@ contract EntropyConsumer is IEntropyConsumer {
16741768 bytes32 _randomness
16751769 ) internal override {
16761770 uint256 startGas = gasleft ();
1677- uint256 currentGasUsed = 0 ;
1771+
1772+ sequence = _sequence;
1773+ provider = _provider;
1774+ randomness = _randomness;
16781775
16791776 // Keep consuming gas until we reach our target
1777+ uint256 currentGasUsed = startGas - gasleft ();
16801778 while (currentGasUsed < targetGasUsage) {
16811779 // Consume gas with a hash operation
16821780 keccak256 (abi.encodePacked (currentGasUsed, _randomness));
16831781 currentGasUsed = startGas - gasleft ();
16841782 }
16851783
1686- gasUsed = currentGasUsed;
1687-
1688- if (! reverts) {
1689- sequence = _sequence;
1690- provider = _provider;
1691- randomness = _randomness;
1692- } else {
1784+ if (reverts) {
16931785 revert ("Callback failed " );
16941786 }
16951787 }
0 commit comments