Skip to content

Commit cab55f0

Browse files
major change to tracking sums page
Added motivation, and specific CVL specs examples.
1 parent 63ac056 commit cab55f0

File tree

2 files changed

+62
-17
lines changed

2 files changed

+62
-17
lines changed

docs/user-guide/patterns/sums.rst

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,51 @@ namely the :clink:`ERC20</DEFI/ERC20/contracts/ERC20.sol>` contract from the
1111
:clink:`Examples</>` repository.
1212

1313

14+
Motivating rule
15+
---------------
16+
The motivating example is the following rule, asserting that the transfer recipient's
17+
balance is updated correctly.
18+
19+
.. _transferIntegrity_bad_rule:
20+
21+
.. cvlinclude:: ../../../Examples/DEFI/ERC20/certora/specs/ERC20SumsBad.spec
22+
:cvlobject: transferIntegrity
23+
:caption: :clink:`transferIntegrity rule</DEFI/ERC20/certora/specs/ERC20SumsBad.spec>`
24+
25+
The Prover will find a counter-example for this rule, in which the recipient's balance
26+
overflows. This overflow can occur, because the recipient's balance update is inside
27+
an :solidity:`unchecked` clause, as shown below in :ref:`erc20_transfer_func`.
28+
In practice this violation cannot occur, since the balance of :cvl:`recipient` is
29+
limited by :solidity:`totalSupply`. The Prover, however, is not aware of this fact.
30+
31+
Hence, to verify the :cvl:`transferIntegrity` rule we must make the Prover aware that
32+
the :solidity:`totalSupply()` is the sum of all balances.
33+
34+
.. _erc20_transfer_func:
35+
36+
.. literalinclude:: ../../../Examples/DEFI/ERC20/contracts/ERC20.sol
37+
:language: solidity
38+
:start-at: function transfer
39+
:end-before: function transferFrom
40+
:emphasize-lines: 6-8
41+
:caption: :clink:`transfer function</DEFI/ERC20/contracts/ERC20.sol>`
42+
43+
1444
Trying to verify the sum of two balances
1545
----------------------------------------
16-
Often one needs a invariant like :cvl:`sumOfTwo` below:
17-
18-
.. code-block:: cvl
46+
The naive solution is to prove the following :cvl:`sumOfTwo` invariant, and
47+
add :cvl:`requireInvariant sumOfTwo(e.msg.sender, recipient)` to
48+
:ref:`transferIntegrity_bad_rule`.
1949

20-
invariant sumOfTwo(address a, address b)
21-
(a != b) => (balanceOf(a) + balanceOf(b) <= to_mathint(totalSupply()));
50+
.. cvlinclude:: ../../../Examples/DEFI/ERC20/certora/specs/ERC20SumsBad.spec
51+
:cvlobject: sumOfTwo
52+
:caption: :clink:`sumOfTwo invariant</DEFI/ERC20/certora/specs/ERC20SumsBad.spec>`
2253

23-
If we run this invariant, the Prover will find a violations.
24-
An example of such a violation is summarized in the table below.
25-
In this example the function called was :cvl:`tranferFrom(c, a, 2)`,
26-
where :solidity:`c` is an address different from :solidity:`a` and :solidity:`b`.
54+
This solution fails however, since the Prover finds counter-examples for this invariant.
55+
Here is one possible counter-example, where the function called is
56+
:cvl:`tranferFrom(c, a, 2)`. The states before and after the function call
57+
are summarized in the table below, where :solidity:`c` is an address different
58+
from :solidity:`a` and :solidity:`b`.
2759

2860
.. list-table:: Counter example
2961
:header-rows: 1
@@ -53,17 +85,30 @@ where :solidity:`c` is an address different from :solidity:`a` and :solidity:`b`
5385
- **true** (3 >= 1+2)
5486
- **false** (3 < 3+2)
5587

56-
We see that the Prover cannot verify :cvl:`sumOfTwo` invariant without us adding unsound
57-
assumptions. So instead, we shall prove a stronger property, as explained next.
88+
Hence, the Prover cannot verify :cvl:`sumOfTwo` invariant without additional assumptions.
89+
So instead, we shall prove a stronger property, as explained next.
5890

5991

6092
Equality of sum of balances and total supply
6193
--------------------------------------------
6294
The preferred solution to tracking sums is using a hook and a ghost, as shown below.
6395

64-
.. cvlinclude:: ../../../Examples/DEFI/ERC20/certora/specs/ERC20Fixed.spec
65-
:lines: 98-106, 115-116
66-
:caption: :clink:`Total supply is sum of balances</DEFI/ERC20/certora/specs/ERC20Fixed.spec>`
96+
.. literalinclude:: ../../../Examples/DEFI/ERC20/certora/specs/ERC20SumsGood.spec
97+
:language: cvl
98+
:start-at: A ghost variable to track the sum of all balances
99+
:end-before: Partial transfer integrity rule
100+
:caption: :clink:`Sum of balances</DEFI/ERC20/certora/specs/ERC20SumsGood.spec>`
101+
102+
Once this invariant is proved, we can require this invariant. For example,
103+
we can add such a requirement to fix :ref:`transferIntegrity_bad_rule` from before,
104+
as shown below.
105+
106+
.. cvlinclude:: ../../../Examples/DEFI/ERC20/certora/specs/ERC20SumsGood.spec
107+
:cvlobject: transferIntegrity
108+
:emphasize-lines: 3
109+
:caption: :clink:`transferIntegrity rule corrected</DEFI/ERC20/certora/specs/ERC20SumsGood.spec>`
110+
111+
.. warning::
67112

68-
Once this invariant is proved, we can require properties like the one in
69-
:cvl:`sumOfTwo` above.
113+
Note that the :cvl:`Sload` hook adds a require statement for every balance read.
114+
One should always be cautious with such require statements, as they can be unsound.

0 commit comments

Comments
 (0)