chore(certora): contract balance increases when new storage requests are added
This commit is contained in:
parent
6d319c76b9
commit
1e5c8df2fb
|
@ -7,9 +7,12 @@ import {IGroth16Verifier} from "../../contracts/Groth16.sol";
|
|||
import {MarketplaceConfig} from "../../contracts/Configuration.sol";
|
||||
import {Marketplace} from "../../contracts/Marketplace.sol";
|
||||
import {RequestId, SlotId} from "../../contracts/Requests.sol";
|
||||
import {Request} from "../../contracts/Requests.sol";
|
||||
|
||||
contract MarketplaceHarness is Marketplace {
|
||||
constructor(MarketplaceConfig memory config, IERC20 token, IGroth16Verifier verifier) Marketplace(config, token, verifier) {}
|
||||
constructor(MarketplaceConfig memory config, IERC20 token, IGroth16Verifier verifier)
|
||||
Marketplace(config, token, verifier)
|
||||
{}
|
||||
|
||||
function requestContext(RequestId requestId) public returns (Marketplace.RequestContext memory) {
|
||||
return _requestContexts[requestId];
|
||||
|
@ -23,4 +26,3 @@ contract MarketplaceHarness is Marketplace {
|
|||
return _periodEnd(period);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,14 @@ ghost mathint sumOfBalances {
|
|||
init_state axiom sumOfBalances == 0;
|
||||
}
|
||||
|
||||
ghost mathint requestsCount {
|
||||
init_state axiom requestsCount == 0;
|
||||
}
|
||||
|
||||
ghost mathint sumOfAllRequestPrices {
|
||||
init_state axiom sumOfAllRequestPrices == 0;
|
||||
}
|
||||
|
||||
hook Sload uint256 balance Token._balances[KEY address addr] {
|
||||
require sumOfBalances >= to_mathint(balance);
|
||||
}
|
||||
|
@ -95,8 +103,22 @@ hook Sstore _requestContexts[KEY Marketplace.RequestId requestId].state Marketpl
|
|||
if (oldState != newState) {
|
||||
requestStateChangesCount = requestStateChangesCount + 1;
|
||||
}
|
||||
|
||||
if (oldState == Marketplace.RequestState.New && newState == Marketplace.RequestState.Cancelled) {
|
||||
uint256 duration = currentContract._requests[requestId].ask.duration;
|
||||
uint256 slots = currentContract._requests[requestId].ask.slots;
|
||||
uint256 reward = currentContract._requests[requestId].ask.reward;
|
||||
sumOfAllRequestPrices = sumOfAllRequestPrices - reward * duration * slots;
|
||||
}
|
||||
}
|
||||
|
||||
hook Sstore _requests[KEY Marketplace.RequestId requestId].ask.reward uint256 value (uint256 oldValue) {
|
||||
uint256 duration = currentContract._requests[requestId].ask.duration;
|
||||
uint256 slots = currentContract._requests[requestId].ask.slots;
|
||||
sumOfAllRequestPrices = sumOfAllRequestPrices + value * duration * slots;
|
||||
}
|
||||
|
||||
|
||||
ghost mathint slotStateChangesCount {
|
||||
init_state axiom slotStateChangesCount == 0;
|
||||
}
|
||||
|
@ -107,6 +129,22 @@ hook Sstore _slots[KEY Marketplace.SlotId slotId].state Marketplace.SlotState ne
|
|||
}
|
||||
}
|
||||
|
||||
// hook Sstore _requests[KEY Marketplace.RequestId requestId].client address value (address oldValue) {
|
||||
// if (oldValue == 0 && value != 0) {
|
||||
// requestsCount = requestsCount + 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
hook Sstore _requestsPerClient[KEY address client]._inner._values[INDEX uint i] bytes32 value (bytes32 oldValue) {
|
||||
bytes32 zero = to_bytes32(0);
|
||||
if (value != zero && oldValue == value) {
|
||||
requestsCount = requestsCount + 1;
|
||||
} else {
|
||||
// commented out for now that we are only checking balance/requests increase
|
||||
// requestsCount = requestsCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------
|
||||
| Helper functions |
|
||||
--------------------------------------------*/
|
||||
|
@ -191,6 +229,9 @@ invariant cancelledSlotAlwaysHasCancelledRequest(env e, Marketplace.SlotId slotI
|
|||
currentContract.slotState(e, slotId) == Marketplace.SlotState.Cancelled =>
|
||||
currentContract.requestState(e, currentContract.slots(e, slotId).requestId) == Marketplace.RequestState.Cancelled;
|
||||
|
||||
invariant requestsCountIsGreaterOrEqualToZero()
|
||||
requestsCount >= 0;
|
||||
|
||||
/*--------------------------------------------
|
||||
| Properties |
|
||||
--------------------------------------------*/
|
||||
|
@ -244,7 +285,7 @@ rule allowedRequestStateChanges(env e, method f) {
|
|||
|
||||
Marketplace.RequestId requestId = currentContract.slots(e, slotId).requestId;
|
||||
|
||||
// needed, otherwise it finds counter examples where
|
||||
// needed, otherwise it finds counter examples where
|
||||
// `SlotState.Cancelled` and `RequestState.New`
|
||||
requireInvariant cancelledSlotAlwaysHasCancelledRequest(e, slotId);
|
||||
// needed, otherwise it finds counter example where
|
||||
|
@ -256,11 +297,11 @@ rule allowedRequestStateChanges(env e, method f) {
|
|||
// However, `requestId == 0` enforces `SlotState.Free` in the `fillSlot` function regardless,
|
||||
// which ultimately results in counter examples where we have a state change
|
||||
// RequestState.Cancelled -> RequestState.Finished, which is forbidden.
|
||||
//
|
||||
//
|
||||
// COUNTER EXAMPLE: https://prover.certora.com/output/6199/3a4f410e6367422ba60b218a08c04fae?anonymousKey=0d7003af4ee9bc18c0da0c80a216a6815d397370
|
||||
//
|
||||
// The `require` below is a hack to ensure we exclude such cases as the code
|
||||
// reverts in `requestIsKnown()` modifier (simply `require requestId != 0` isn't
|
||||
//
|
||||
// The `require` below is a hack to ensure we exclude such cases as the code
|
||||
// reverts in `requestIsKnown()` modifier (simply `require requestId != 0` isn't
|
||||
// sufficient here)
|
||||
require requestId == to_bytes32(0) => currentContract._requests[requestId].client == 0;
|
||||
|
||||
|
@ -374,3 +415,25 @@ rule slotStateChangesOnlyOncePerFunctionCall(env e, method f) {
|
|||
|
||||
assert slotStateChangesCountAfter <= slotStateChangesCountBefore + 1;
|
||||
}
|
||||
|
||||
rule contractBalanceIncreasesWhenRequestsAmountIncreases(env e, method f) {
|
||||
requireInvariant totalSupplyIsSumOfBalances();
|
||||
requireInvariant requestsCountIsGreaterOrEqualToZero();
|
||||
|
||||
calldataarg args;
|
||||
|
||||
mathint balanceBefore = Token.balanceOf(e, currentContract);
|
||||
mathint requestsCountBefore = requestsCount;
|
||||
mathint sumOfAllRequestPricesBefore = sumOfAllRequestPrices;
|
||||
|
||||
require e.msg.sender != currentContract;
|
||||
require e.msg.sender != 0;
|
||||
f(e, args);
|
||||
|
||||
mathint balanceAfter = Token.balanceOf(e, currentContract);
|
||||
mathint requestsCountAfter = requestsCount;
|
||||
mathint sumOfAllRequestPricesAfter = sumOfAllRequestPrices;
|
||||
|
||||
assert requestsCountAfter > requestsCountBefore => sumOfAllRequestPricesAfter >= sumOfAllRequestPricesBefore;
|
||||
assert requestsCountAfter > requestsCountBefore => balanceAfter >= balanceBefore + (sumOfAllRequestPricesAfter - sumOfAllRequestPricesBefore);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue