@@ -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+
1444Trying 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
6092Equality of sum of balances and total supply
6193--------------------------------------------
6294The 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