Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
9a0778cc
Unverified
Commit
9a0778cc
authored
Dec 09, 2022
by
mergify[bot]
Committed by
GitHub
Dec 09, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4225 from ethereum-optimism/jm/echidna-metering
ctb: Add echidna tests for metering
parents
08bc4633
b34d4ff1
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
174 additions
and
1 deletion
+174
-1
late-cameras-jam.md
.changeset/late-cameras-jam.md
+5
-0
config.yml
.circleci/config.yml
+8
-1
FuzzResourceMetering.sol
...tracts-bedrock/contracts/echidna/FuzzResourceMetering.sol
+158
-0
echidna.yaml
packages/contracts-bedrock/echidna.yaml
+3
-0
No files found.
.changeset/late-cameras-jam.md
0 → 100644
View file @
9a0778cc
---
'
@eth-optimism/contracts-bedrock'
:
patch
---
Add echidna tests for metering
.circleci/config.yml
View file @
9a0778cc
...
...
@@ -266,7 +266,6 @@ jobs:
bedrock-echidna-build
:
docker
:
-
image
:
ethereumoptimism/ci-builder:latest
resource_class
:
large
steps
:
-
checkout
-
attach_workspace
:
{
at
:
"
."
}
...
...
@@ -819,18 +818,26 @@ workflows:
requires
:
-
yarn-monorepo
-
bedrock-echidna-run
:
name
:
Bedrock Echidna Alias Test
echidna_target
:
aliasing
requires
:
-
bedrock-echidna-build
-
bedrock-echidna-run
:
name
:
Bedrock Echidna Eth Burn Test
echidna_target
:
"
burn:eth"
requires
:
-
bedrock-echidna-build
-
bedrock-echidna-run
:
name
:
Bedrock Echidna Gas Burn Test
echidna_target
:
"
burn:gas"
size
:
2xlarge
requires
:
-
bedrock-echidna-build
-
bedrock-echidna-run
:
name
:
Bedrock Echidna Metering Test
echidna_target
:
metering
requires
:
-
bedrock-echidna-build
-
op-bindings-build
:
requires
:
-
yarn-monorepo
...
...
packages/contracts-bedrock/contracts/echidna/FuzzResourceMetering.sol
0 → 100644
View file @
9a0778cc
pragma solidity 0.8.15;
import { ResourceMetering } from "../L1/ResourceMetering.sol";
import { Arithmetic } from "../libraries/Arithmetic.sol";
import { StdUtils } from "forge-std/Test.sol";
contract EchidnaFuzzResourceMetering is ResourceMetering, StdUtils {
bool failedMaxGasPerBlock;
bool failedRaiseBaseFee;
bool failedLowerBaseFee;
bool failedNeverBelowMinBaseFee;
bool failedMaxRaiseBaseFeePerBlock;
bool failedMaxLowerBaseFeePerBlock;
// Used as a special flag for the purpose of identifying unchecked math errors specifically
// in the test contracts, not the target contracts themselves.
bool underflow;
constructor() {
initialize();
}
function initialize() internal initializer {
__ResourceMetering_init();
}
/**
* @notice Takes the necessary parameters to allow us to burn arbitrary amounts of gas to test
* the underlying resource metering/gas market logic
*/
function testBurn(uint256 _gasToBurn, bool _raiseBaseFee) public {
// Part 1: we cache the current param values and do some basic checks on them.
uint256 cachedPrevBaseFee = uint256(params.prevBaseFee);
uint256 cachedPrevBoughtGas = uint256(params.prevBoughtGas);
uint256 cachedPrevBlockNum = uint256(params.prevBlockNum);
// check that the last block's base fee hasn't dropped below the minimum
if (cachedPrevBaseFee < uint256(MINIMUM_BASE_FEE)) {
failedNeverBelowMinBaseFee = true;
}
// check that the last block didn't consume more than the max amount of gas
if (cachedPrevBoughtGas > uint256(MAX_RESOURCE_LIMIT)) {
failedMaxGasPerBlock = true;
}
// Part2: we perform the gas burn
// force the gasToBurn into the correct range based on whether we intend to
// raise or lower the baseFee after this block, respectively
uint256 gasToBurn;
if (_raiseBaseFee) {
gasToBurn = bound(
_gasToBurn,
uint256(TARGET_RESOURCE_LIMIT),
uint256(MAX_RESOURCE_LIMIT)
);
} else {
gasToBurn = bound(_gasToBurn, 0, uint256(TARGET_RESOURCE_LIMIT));
}
_burnInternal(uint64(gasToBurn));
// Part 3: we run checks and modify our invariant flags based on the updated params values
// Calculate the maximum allowed baseFee change (per block)
uint256 maxBaseFeeChange = cachedPrevBaseFee / uint256(BASE_FEE_MAX_CHANGE_DENOMINATOR);
// If the last block used more than the target amount of gas (and there were no
// empty blocks in between), ensure this block's baseFee increased, but not by
// more than the max amount per block
if (
(cachedPrevBoughtGas > uint256(TARGET_RESOURCE_LIMIT)) &&
(uint256(params.prevBlockNum) - cachedPrevBlockNum == 1)
) {
failedRaiseBaseFee = failedRaiseBaseFee || (params.prevBaseFee <= cachedPrevBaseFee);
failedMaxRaiseBaseFeePerBlock =
failedMaxRaiseBaseFeePerBlock ||
((uint256(params.prevBaseFee) - cachedPrevBaseFee) < maxBaseFeeChange);
}
// If the last blocked used less than the target amount of gas, (or was empty),
// ensure that: this block's baseFee was decreased, but not by more than the max amount
if (
(cachedPrevBoughtGas < uint256(TARGET_RESOURCE_LIMIT)) ||
(uint256(params.prevBlockNum) - cachedPrevBlockNum > 1)
) {
// Invariant: baseFee should decrease
failedLowerBaseFee =
failedLowerBaseFee ||
(uint256(params.prevBaseFee) > cachedPrevBaseFee);
if (params.prevBlockNum - cachedPrevBlockNum == 1) {
// No empty blocks
// Invariant: baseFee should not have decreased by more than the maximum amount
failedMaxLowerBaseFeePerBlock =
failedMaxLowerBaseFeePerBlock ||
((cachedPrevBaseFee - uint256(params.prevBaseFee)) <= maxBaseFeeChange);
} else if (params.prevBlockNum - cachedPrevBlockNum > 1) {
// We have at least one empty block
// Update the maxBaseFeeChange to account for multiple blocks having passed
unchecked {
maxBaseFeeChange = uint256(
int256(cachedPrevBaseFee) -
Arithmetic.clamp(
Arithmetic.cdexp(
int256(cachedPrevBaseFee),
BASE_FEE_MAX_CHANGE_DENOMINATOR,
int256(uint256(params.prevBlockNum) - cachedPrevBlockNum)
),
MINIMUM_BASE_FEE,
MAXIMUM_BASE_FEE
)
);
}
// Detect an underflow in the previous calculation.
// Without using unchecked above, and detecting the underflow here, echidna would
// otherwise ignore the revert.
underflow = underflow || maxBaseFeeChange > cachedPrevBaseFee;
// Invariant: baseFee should not have decreased by more than the maximum amount
failedMaxLowerBaseFeePerBlock =
failedMaxLowerBaseFeePerBlock ||
((cachedPrevBaseFee - uint256(params.prevBaseFee)) <= maxBaseFeeChange);
}
}
}
function _burnInternal(uint64 _gasToBurn) private metered(_gasToBurn) {}
function echidna_high_usage_raise_baseFee() public view returns (bool) {
return !failedRaiseBaseFee;
}
function echidna_low_usage_lower_baseFee() public view returns (bool) {
return !failedLowerBaseFee;
}
function echidna_never_below_min_baseFee() public view returns (bool) {
return !failedNeverBelowMinBaseFee;
}
function echidna_never_above_max_gas_limit() public view returns (bool) {
return !failedMaxGasPerBlock;
}
function echidna_never_exceed_max_increase() public view returns (bool) {
return !failedMaxRaiseBaseFeePerBlock;
}
function echidna_never_exceed_max_decrease() public view returns (bool) {
return !failedMaxLowerBaseFeePerBlock;
}
function echidna_underflow() public view returns (bool) {
return !underflow;
}
}
packages/contracts-bedrock/echidna.yaml
View file @
9a0778cc
...
...
@@ -4,3 +4,6 @@ format: text
# Set the timeout to 3 minutes to keep CI from getting too long.
# The tool also adds 2 or 3 minutes before/after the actual timeout window.
timeout
:
180
# Prevent calls to the (non-existent) fallback function of EchidnaFuzzResourceMetering
filterFunctions
:
[
EchidnaFuzzResourceMetering.*fallback*()
]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment