[certora] allocation at groupId is greater than equal leafId#905
[certora] allocation at groupId is greater than equal leafId#905
Conversation
…of allocations at MarketId
renamed Signed-off-by: Bhargav Bhatt <40268131+bhargavbh@users.noreply.github.com>
renamed Signed-off-by: Bhargav Bhatt <40268131+bhargavbh@users.noreply.github.com>
|
|
||
| // Require change values to be bounded, else the sum exceeds max_uint256. | ||
| // Note that the implicit cast from uint256 to int256 is saf here, see allocationIsInt256 in Invariants.spec | ||
| require allocation(ids[0]) + change >= 0, "group level allocation + change is non-negative"; |
There was a problem hiding this comment.
maybe worth redeclaring :
strong invariant allocationIsInt256(bytes32 id)
allocation(id) <= max_int256();
like in AllocationMorphoMarketV1AdapterV2 and require them directly ?
There was a problem hiding this comment.
maybe not finally , these lines are stronger than these invariants
| "-destructiveOptimizations twostage", | ||
| "-s [z3:def{randomSeed=1},z3:def{randomSeed=2},z3:def{randomSeed=3},z3:def{randomSeed=4},z3:def{randomSeed=5},z3:def{randomSeed=6},z3:def{randomSeed=7},z3:def{randomSeed=8},z3:def{randomSeed=9},z3:def{randomSeed=10}]" | ||
| ], | ||
| "global_timeout": "7200", |
There was a problem hiding this comment.
just curious , is it intentional to have both global and smt timeout set to 7200 ?
default smt is 300 but maybe this is wanted
There was a problem hiding this comment.
just used the same timeout as a similar spec, but i can try reducing it. here is the prover link.
In general, it is not a very heavy spec.
|
|
||
| // assume the following functions return arbitrary value but do not modify the relevant storage for this rule. | ||
| function _.balanceOf(address) external => NONDET; | ||
| function _.realAssets() external => NONDET; |
There was a problem hiding this comment.
| function _.realAssets() external => NONDET; |
it's unused because accrueInterestView is summarized
| function _.transfer(address, uint256) external => NONDET; | ||
| function _.transferFrom(address, address, uint256) external => NONDET; |
There was a problem hiding this comment.
The comment above assume the following functions return arbitrary value but do not modify the relevant storage for this rule. only really applies to the summary of the transfer functions (and accrueInterest), because the other functions are view so NONDET is an over-approximation since we are not looking at reverts
There was a problem hiding this comment.
Btw, wondering if we cannot remove those 2 summaries, since we are proving strong invariants, which models reentrancy
| function _.transfer(address, uint256) external => NONDET; | |
| function _.transferFrom(address, address, uint256) external => NONDET; |
| function _.canSendShares(address) external => NONDET; | ||
| function _.canReceiveAssets(address) external => NONDET; | ||
| function _.canSendAssets(address) external => NONDET; | ||
| function accrueInterest() internal => NONDET; |
There was a problem hiding this comment.
| function accrueInterest() internal => NONDET; |
would this be possible, since we are already summarizing accrueInterestView ? It would be really nice, because then it removes part of the assumption "the following functions return arbitrary value but do not modify the relevant storage for this rule" which is not trivial to check
| // - groupId and leafId are distinct | ||
| // - groupId is never itself a leaf in any other adapter | ||
| // - leafId is never itself a group in any other adapter | ||
| // - a leaf's parent group never changes across calls |
There was a problem hiding this comment.
| // - a leaf's parent group never changes across calls | |
| // - a leaf has a unique parent group, and that group never changes across calls |
because the existence of the group is already an assumption in itself
| // Require change values to be bounded, else the sum exceeds max_uint256. | ||
| // Note that the implicit cast from uint256 to int256 is saf here, see allocationIsInt256 in Invariants.spec | ||
| require allocation(ids[0]) + change >= 0, "group level allocation + change is non-negative"; | ||
| require allocation(ids[0]) + change <= max_uint256, "group level allocation + change is bounded"; | ||
| require allocation(ids[1]) + change >= 0, "leaf level allocation + change is non-negative"; | ||
| require allocation(ids[1]) + change <= max_uint256, "leaf level allocation + change is bounded"; |
There was a problem hiding this comment.
Not clear to me why we need those. If an adapter doesn't have the property then it might revert, but this isn't a path that the prover would consider.
Also note that the property allocation(ids[0]) + change <= max_uint256 is precisely the reason why we are trying to prove the whole spec, so that we "prove" why the requires of this form in the other specs are sound to make
| // Every ghost cell stays within uint256 bounds. | ||
| strong invariant ghostAllocationBounded(bytes32 g, bytes32 l) | ||
| ghostAllocationByGroupId[g][l] >= 0 && ghostAllocationByGroupId[g][l] <= max_uint256; |
There was a problem hiding this comment.
| // Every ghost cell stays within uint256 bounds. | |
| strong invariant ghostAllocationBounded(bytes32 g, bytes32 l) | |
| ghostAllocationByGroupId[g][l] >= 0 && ghostAllocationByGroupId[g][l] <= max_uint256; |
let's just define ghostAllocationByGroupId as a uint256 instead of as a mathint
| !(ghostIsLeafId[id] && ghostIsGroupId[id]); | ||
|
|
||
| // A ghost cell is non-zero only if its leaf is registered and maps to that group. | ||
| strong invariant ghostGroupConsistency(bytes32 groupId, bytes32 leafId) |
There was a problem hiding this comment.
| strong invariant ghostGroupConsistency(bytes32 groupId, bytes32 leafId) | |
| strong invariant nonMatchingLeafAndGroupHasZeroGhostAllocation(bytes32 groupId, bytes32 leafId) |
| "compiler_map": { | ||
| "VaultV2": "solc-0.8.28" | ||
| }, |
There was a problem hiding this comment.
| "compiler_map": { | |
| "VaultV2": "solc-0.8.28" | |
| }, | |
| "solc": "solc-0.8.28", |
| ghostIsGroupId[groupId] => to_mathint(allocation(groupId)) == (usum bytes32 leafId. ghostAllocationByGroupId[groupId][leafId]) | ||
| { | ||
| preserved with (env e) { | ||
| bytes32 anyLeafId; |
There was a problem hiding this comment.
I don't think that this does anything, the anyLeafId can be chosen arbitrarily by the prover, so it could chose nonsensical value for it which would add a useless property to the context. Here is a run that goes through without it
adds invariants that allocation at AdapterId and CollateralId is sum of MarketId allocations.as a result, proves:- adapter allocation >= any individual market allocation- collateral allocation >= any individual market allocationFor a generic adapter following a hierarchical model of leafId (e.g. marketId) and groupId (e.g. collateralId), proves that the allocation at the group-level >= allocation at leaf-level.