Closed reekamaharaj closed 10 months ago
A proving/debugging process:
no-break-on-calls
, --max-depth 10000
and --max-iterations 5
or 10
kcfg
for branching. true
or false
:
runLemma => doneLemma
style claim that demonstrably simplifies the branching conditionkcfg
using foundry-remove-node
From @lucasmt Notes
kontrol foundry-prove
hangs during an execute step (after hours without a response) or crashes because kore-rpc
returned an empty response, it could be due to the configuration becoming too large. To troubleshoot, inspect the node that was being extended using foundry-show
or foundry-view-kcfg
. Check if there are any unusually large expressions in any of the cells and consider writing lemmas to simplify them.runLemma-doneLemma
pattern to check if an expression simplifies, it's important to remember that running the claim includes an implication check
. This means that if your claim is in the form of runLemma(A) => doneLemma(B)
and it passes, it doesn't guarantee that A
fully simplifies to B
. Instead, it might simplify to an expression B'
that implies B
.infiniteGas
cheatcode and the expression in the <gas>
or <callGas>
cell is growing out of control without being simplified, you can call kontrol foundry-prove
with the --auto-abstract-gas
option. This will automatically abstract the gas expression into a symbolic variable. Only do this if you are not concerned with measuring gas consumption, as you will lose that information. If possible, write lemmas to simplify the gas expression instead.KEVM
will cause branching based on whether a symbolic address already exists in the <accounts>
cell or if it is a new address. This situation arises when the prank
cheatcode is called with a symbolic address (refer to this issue for an example in foundry-show
). To resolve this, the usual solution is to add one vm.assume(symbolicAddress != ...)
for each of the preexisting addresses. These addresses should correspond to:
&&
and ||
operators are short-circuit operators, meaning they introduce branching when evaluated. This means that if you have a line like vm.assume(p && q)
or vm.assume(p || q)
, it will introduce branching in the execution, which might not be immediately apparent.split
node from the KCFG
before continuing. Otherwise, both branches will still exist, but the unnecessary one will simplify to #Bottom
.From @lucasmt Notes
maxUInt160 &Int X
, to extract a variable with a specific number of bits from a larger word. The number of bits can often provide a clue about the type of the variable. For example, maxUInt160
typically represents an address, while maxUInt8
represents a boolean
value.symbolicStorage
cheatcode, you may encounter expressions like #lookup(?STORAGE0:Map, 6)
. This expression accesses storage slot 6 of the symbolic storage represented by the STORAGE0
variable. If you want to determine which storage variable this expression corresponds to, you can follow these steps:
STORAGE0
corresponds to.
symbolicStorage
creates the symbolic variable STORAGE
, followed by STORAGE0
, STORAGE1
, STORAGE2
, STORAGE3
, and so on. Therefore, you can use the order in which symbolicStorage
was called in each contract to map each variable to its contract.<accounts>
cell in the KEVM
configuration. In each <account>
, the <acctId>
cell contains the address of the contract, and the <storage>
cell contains the storage. If you know the address of each contract, you can map it to the storage variable.ContractName
storage, where ContractName
represents the contract identified in the previous step. This command will output a JSON
result, with the storage field containing a list of all storage slots in the contract. The label of each slot corresponds to the name of the storage variable.More tips for debugging:
Things to add to an FAQ page