Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ testMatrix.json

# E2E broadcast logs
/broadcast-e2e

# Recon
medusa
echidna
crytic-export
39 changes: 39 additions & 0 deletions contracts/echidna.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

testMode: "property"
prefix: "invariant_"
coverage: true
corpusDir: "echidna"
balanceAddr: 0x1043561a8829300000
balanceContract: 0x1043561a8829300000
filterFunctions: []
cryticArgs: ["--foundry-compile-all","--compile-libraries=(CallUtils,0xf01),(CallbackUtils,0xf02),(BaseRelayRecipient,0xf03),(SuperfluidPoolDeployerLibrary,0xf04),(SolvencyHelperLibrary,0xf06),(SlotsBitmapLibrary,0xf08),(SuperfluidGovDeployerLibrary,0xf09),(SuperfluidHostDeployerLibrary,0xf0a),(SuperfluidCFAv1DeployerLibrary,0xf0b),(SuperfluidIDAv1DeployerLibrary,0xf0c),(SuperfluidPoolLogicDeployerLibrary,0xf0d),(SuperfluidGDAv1DeployerLibrary,0xf0e),(CFAv1ForwarderDeployerLibrary,0xf0f),(GDAv1ForwarderDeployerLibrary,0xf10),(SuperTokenDeployerLibrary,0xf11),(SuperfluidPoolNFTLogicDeployerLibrary,0xf12), (ProxyDeployerLibrary,0xf13),(TokenDeployerLibrary,0xf14),(SuperTokenFactoryDeployerLibrary,0xf15),(SuperfluidPeripheryDeployerLibrary,0xf16)"]
deployContracts: [
["0xf01", "CallUtils"],
["0xf02", "CallbackUtils"],
["0xf03", "BaseRelayRecipient"],
["0xf04", "SuperfluidPoolDeployerLibrary"],
["0xf06", "SolvencyHelperLibrary"],
["0xf08", "SlotsBitmapLibrary"],

["0xf09", "SuperfluidGovDeployerLibrary"],
["0xf0a", "SuperfluidHostDeployerLibrary"],
["0xf0b", "SuperfluidCFAv1DeployerLibrary"],
["0xf0c", "SuperfluidIDAv1DeployerLibrary"],
["0xf0d", "SuperfluidPoolLogicDeployerLibrary"],
["0xf0e", "SuperfluidGDAv1DeployerLibrary"],
["0xf0f", "CFAv1ForwarderDeployerLibrary"],
["0xf10", "GDAv1ForwarderDeployerLibrary"],
["0xf11", "SuperTokenDeployerLibrary"],
["0xf12", "SuperfluidPoolNFTLogicDeployerLibrary"],
["0xf13", "ProxyDeployerLibrary"],
["0xf14", "TokenDeployerLibrary"],
["0xf15", "SuperTokenFactoryDeployerLibrary"],
["0xf16", "SuperfluidPeripheryDeployerLibrary"]

]
deployer: "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496"
contractAddr: "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496"
shrinkLimit: 100000
timeout: 14400
## Deploy ERC1820RegistryCompiled as we don't have access to vm.etch
deployBytecodes: [["0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24", "608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061086057508015155b15610870576000925050506106e8565b61087a858561094c565b909250905060018214801561088f5750806001145b1561089f576001925050506106e8565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108f2576108eb83836107ef565b90506106e8565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160248189617530fa90519096909550935050505056fea165627a7a72305820377f4a2d4301ede9949f163f319021a6e9c687c292a5e2b2c4734c126b524e6c0029"]]
12 changes: 9 additions & 3 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ src = "src"
out = "out"
libs = ["lib"]
evm_version = 'cancun'
solc = "0.8.24"
optimizer = true
optimizer_runs = 200
ignored_error_codes = [3860, 5574] # contract-size
Expand All @@ -14,9 +15,14 @@ fs_permissions = [

[invariant]
call_override = false
fail_on_revert = true
runs = 500
depth = 50
fail_on_revert = false
runs = 500000000
depth = 100
include_storage = true
show_solidity = true
show_metrics = true
continuous_run = true
corpus_dir = "corpus/foundry"
# failure_persist_dir = "/dev/null" # XXX circumvent this half-baked Foundry feature
shrink_run_limit = 0 # XXX shrinking is super broken, results in completely wrong repro sequence

Expand Down
89 changes: 89 additions & 0 deletions contracts/medusa.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"fuzzing": {
"workers": 16,
"workerResetLimit": 50,
"timeout": 0,
"testLimit": 0,
"callSequenceLength": 100,
"corpusDirectory": "medusa",
"coverageEnabled": true,
"deploymentOrder": [
"CryticTester"
],
"targetContracts": [
"CryticTester"
],
"predeployedContracts": {
"ERC1820RegistryRuntime": "0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24"
},
"targetContractsBalances": [
"0x27b46536c66c8e3000000"
],
"constructorArgs": {},
"deployerAddress": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496",
"senderAddresses": [
"0x10000"
],
"blockNumberDelayMax": 60480,
"blockTimestampDelayMax": 604800,
"blockGasLimit": 125000000,
"transactionGasLimit": 12500000,
"testing": {
"stopOnFailedTest": false,
"stopOnFailedContractMatching": false,
"stopOnNoTests": true,
"testAllContracts": false,
"traceAll": false,
"assertionTesting": {
"enabled": true,
"testViewMethods": true,
"panicCodeConfig": {
"failOnCompilerInsertedPanic": false,
"failOnAssertion": true,
"failOnArithmeticUnderflow": false,
"failOnDivideByZero": false,
"failOnEnumTypeConversionOutOfBounds": false,
"failOnIncorrectStorageAccess": false,
"failOnPopEmptyArray": false,
"failOnOutOfBoundsArrayAccess": false,
"failOnAllocateTooMuchMemory": false,
"failOnCallUninitializedVariable": false
}
},
"propertyTesting": {
"enabled": true,
"testPrefixes": [
"invariant_"
]
},
"optimizationTesting": {
"enabled": true,
"testPrefixes": [
"optimize_"
]
}
},
"chainConfig": {
"codeSizeCheckDisabled": true,
"cheatCodes": {
"cheatCodesEnabled": true,
"enableFFI": false
}
}
},
"compilation": {
"platform": "crytic-compile",
"platformConfig": {
"target": "test/recon/CryticTester.sol",
"solcVersion": "",
"exportDirectory": "",
"args": [
"--foundry-compile-all"
]
}
},
"logging": {
"level": "info",
"logDirectory": ""
}
}
51 changes: 51 additions & 0 deletions contracts/test/recon/BeforeAfter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0
pragma solidity ^0.8.0;

import {Setup} from "./Setup.sol";

import {LatestTroveData} from "../../src/Types/LatestTroveData.sol";
import {LiquityMath} from "../../src/Dependencies/LiquityMath.sol";
import {TroveManager} from "../../src/TroveManager.sol";
import {MIN_DEBT} from "../../src/Dependencies/Constants.sol";

// ghost variables for tracking state variable values before and after function calls
abstract contract BeforeAfter is Setup {
struct Vars {
mapping(uint256 => LatestTroveData troveData) dataForTroves; //maps troveId to its given data
mapping(address => TroveManager.Batch) batches;
uint256 collSurplusBalance;
uint256 ghostDebtAccumulator;
uint256 entireSystemDebt;
uint256 ghostWeightedRecordedDebtAccumulator;
uint256 weightedRecordedDebtAccumulator;
uint256 price;
}

Vars internal _before;
Vars internal _after;

modifier updateGhosts {
__before();
_;
__after();
}

function __before() internal {
_before.collSurplusBalance = collSurplusPool.getCollateral(_getActor());
// always zero accumulators at start for clean summation
_before.ghostDebtAccumulator = 0;
_before.ghostWeightedRecordedDebtAccumulator = 0;
_before.weightedRecordedDebtAccumulator = 0;
_before.price = priceFeed.getPrice();

}

function __after() internal {
_after.collSurplusBalance = collSurplusPool.getCollateral(_getActor());
// always zero accumulators at start for clean summation
_after.ghostDebtAccumulator = 0;
_after.ghostWeightedRecordedDebtAccumulator = 0;
_after.weightedRecordedDebtAccumulator = 0;
_after.price = priceFeed.getPrice();
}
}
29 changes: 29 additions & 0 deletions contracts/test/recon/CryticTester.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
pragma solidity ^0.8.0;

import {TargetFunctions} from "./TargetFunctions.sol";
import {CryticAsserts} from "@chimera/CryticAsserts.sol";
import {ERC1820RegistryCompiled} from
"@superfluid-finance/ethereum-contracts/contracts/libs/ERC1820RegistryCompiled.sol";

// echidna . --contract CryticTester --config echidna.yaml --format text --workers 16 --test-limit 10000000 --test-mode exploration
// medusa fuzz
contract ERC1820RegistryRuntime {
constructor() {
bytes memory runtime = ERC1820RegistryCompiled.bin;
assembly {
return(add(runtime, 0x20), mload(runtime))
}
}
}

contract CryticTester is TargetFunctions, CryticAsserts {
function _requireERC1820() internal view {
require(ERC1820RegistryCompiled.at.code.length > 0, "ERC1820 registry missing");
}

constructor() payable {
_requireERC1820();
setup();
}
}
124 changes: 124 additions & 0 deletions contracts/test/recon/CryticToFoundry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0
pragma solidity ^0.8.0;

import {Test} from "forge-std/Test.sol";
import {TargetFunctions} from "./TargetFunctions.sol";
import {FoundryAsserts} from "@chimera/FoundryAsserts.sol";
import {Asserts} from "@chimera/Asserts.sol";
import "forge-std/console2.sol";

import {ERC1820RegistryCompiled} from
"@superfluid-finance/ethereum-contracts/contracts/libs/ERC1820RegistryCompiled.sol";

// forge test --match-contract CryticToFoundry -vv
contract CryticToFoundry is Test, TargetFunctions, FoundryAsserts {
mapping(string => bool) private assertionFailures;

function setUp() public {
vm.etch(ERC1820RegistryCompiled.at, ERC1820RegistryCompiled.bin); // TODO: Deploy at a new address

setup();

targetContract(address(this));
targetSender(address(0x10000));
targetSender(address(0x20000));
targetSender(address(0x30000));
}

function _isAssertion(string memory reason) internal pure returns (bool) {
bytes memory reasonBytes = bytes(reason);
return
reasonBytes.length >= 3
&& reasonBytes[0] == '!'
&& reasonBytes[1] == '!'
&& reasonBytes[2] == '!';
}

function gt(uint256 a, uint256 b, string memory reason)
internal
virtual
override(FoundryAsserts, Asserts)
{
if (_isAssertion(reason)) {
_recordAssertion(a > b, reason);
} else {
super.gt(a, b, reason);
}
}

function gte(uint256 a, uint256 b, string memory reason)
internal
virtual
override(FoundryAsserts, Asserts)
{
if (_isAssertion(reason)) {
_recordAssertion(a >= b, reason);
} else {
super.gte(a, b, reason);
}
}

function lt(uint256 a, uint256 b, string memory reason)
internal
virtual
override(FoundryAsserts, Asserts)
{
if (_isAssertion(reason)) {
_recordAssertion(a < b, reason);
} else {
super.lt(a, b, reason);
}
}

function lte(uint256 a, uint256 b, string memory reason)
internal
virtual
override(FoundryAsserts, Asserts)
{
if (_isAssertion(reason)) {
_recordAssertion(a <= b, reason);
} else {
super.lte(a, b, reason);
}
}

function eq(uint256 a, uint256 b, string memory reason)
internal
virtual
override(FoundryAsserts, Asserts)
{
if (_isAssertion(reason)) {
_recordAssertion(a == b, reason);
} else {
super.eq(a, b, reason);
}
}

function t(bool b, string memory reason)
internal
virtual
override(FoundryAsserts, Asserts)
{
if (_isAssertion(reason)) {
_recordAssertion(b, reason);
} else {
super.t(b, reason);
}
}

function _recordAssertion(bool ok, string memory reason) internal {
if (ok) {
return;
}

assertionFailures[reason] = true;
}

function invariant_assertion_failure_CANARY() public returns (bool) {
assertTrue(
!assertionFailures[ASSERTION_CANARY],
ASSERTION_CANARY
);
return true;
}
}
24 changes: 24 additions & 0 deletions contracts/test/recon/Properties.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
pragma solidity ^0.8.0;

import {Asserts} from "@chimera/Asserts.sol";
import {BeforeAfter} from "./BeforeAfter.sol";

import {MIN_DEBT} from "../../src/Dependencies/Constants.sol";
import {LatestBatchData} from "../../src/Types/LatestBatchData.sol";
import {BatchId} from "../../src/Types/BatchId.sol";
import {SortedTroves} from "../../src/SortedTroves.sol";
import {LatestTroveData} from "../../src/Types/LatestTroveData.sol";


abstract contract Properties is BeforeAfter, Asserts {
string internal constant ASSERTION_CANARY = "!!! canary assertion";

function invariant_canary() public pure returns (bool) {
revert("Canary invariant");
}

function assert_canary(uint256 entropy) public {
t(entropy > 0, ASSERTION_CANARY);
}
}
Loading
Loading