Description
Background
Presently, the CSE initiates symbolic execution from the call graph's leaf nodes progressing towards the root (enabled by the --cse
option). The contract execution commences either from the symbolic values of its parameters or from the state post-execution of the constructor
(using --run-constructor
). This approach results in overly abstract initial states for the functions, making it exceedingly time-consuming to determine the final states of the functions. Consequently, this prevents us from synthesizing reusable function summaries.
Running Example
Using DualGovernance.activateNextState()
as an example, the ActivateNextStateTest.testActivateNextStateTermination
serves as a test harness for this function. The current situation is as follows:
kontrol prove ActivateNextStateTest.testActivateNextStateTermination
: The proof is successful. The process starts from the root of the call graph, executing the constructor andsetUp
before runningtestActivateNextStateTermination
, and finally invokingactivateNextState
.- Adding
cse
withkontrol prove ActivateNextStateTest.testActivateNextStateTermination --cse
: The proof fails. This failure occurs becauseDualGovernance.activateNextState()
is symbolically executed without calling the constructor andsetUp
, resulting in a PROOF-FAILED outcome forDualGovernance.activateNextState()
. Consequently,ActivateNextStateTest.testActivateNextStateTermination
inherits this failure when it uses the summary, leading to its own failure. This indicates that we should not executeactivateNextState
from the most abstract state to obtain its summary, i.e.,kontrol prove DualGovernance.activateNextState
. - Adding
--run-constructor
withkontrol prove DualGovernance.activateNextState
: Verification out-of-time or out-of-memory. While the function inputs are correctly configured by the constructor, the state includes improbable and unreasonable conditions that can arise during function calls. The excessive branching states prevent termination. Even if termination is achieved, the final state obtained throughminimize_proof
and node merging remains too abstract, causing the verification to fail.
Solution
To address the above problem, precondition refinement is necessary: more reasonable constraints should be provided for the initial state of the function. The following approaches can be considered. Given the simplicity of the activateNextState
example, the first approach should suffice to resolve the issue:
- Modify the current
--cse
option to run theconstructor
andsetUp
before executing functions from the leaf to the root of the call graph. - Introduce a new method to specify preconditions for each contract function, where the initial state is derived from these preconditions.
Additionally, function summaries should do the following before being included as lemmas:
- Ensure the summary can be used by removing any context unrelated to the callee contract and function, such as the test contract loaded during setUp.
- Improve rewrite efficiency by minimizing summaries using
minimize_proof
and node merging.
Result / Expected
Upon the successful implementation of minimize_proof
with node merging (issue #703), all tests within ActivateNextStateTest
are expected to be verified within seconds or minutes.