Updates to the VariableDebtToken spec

This commit is contained in:
pistiner 2020-11-22 22:20:43 +02:00
parent 5ad4182508
commit dd0e3064e3
2 changed files with 88 additions and 87 deletions

View File

@ -1,15 +1,12 @@
using LendingPoolHarnessForVariableDebtToken as POOL using LendingPoolHarnessForVariableDebtToken as POOL
/** /**
TotalSupply is the sum of all users balances Checks that each possible opertaion changes the balance of at most one user.
totalSupply(t) = Σaddress u. balanceOf(u,t)
Check that each possible opertaion changes the balance of at most one user
*/ */
rule balanceOfChange(address a, address b, method f) rule balanceOfChange(address a, address b, method f)
{ {
env e; env e;
require a!=b ; require a != b;
uint256 balanceABefore = sinvoke balanceOf(e, a); uint256 balanceABefore = sinvoke balanceOf(e, a);
uint256 balanceBBefore = sinvoke balanceOf(e, b); uint256 balanceBBefore = sinvoke balanceOf(e, b);
@ -19,14 +16,14 @@ rule balanceOfChange(address a, address b, method f)
uint256 balanceAAfter = sinvoke balanceOf(e, a); uint256 balanceAAfter = sinvoke balanceOf(e, a);
uint256 balanceBAfter = sinvoke balanceOf(e, b); uint256 balanceBAfter = sinvoke balanceOf(e, b);
assert ( balanceABefore == balanceAAfter || balanceBBefore == balanceBAfter ); assert (balanceABefore == balanceAAfter || balanceBBefore == balanceBAfter);
} }
/* /**
Check that the changed to total supply is coherent with the changes to balance Checks that the change to total supply is coherent with the change to the balance due to an operation
(which is neither a burn nor a mint).
*/ */
rule integirtyBalanceOfTotalSupply(address a, method f)
rule integirtyBalanceOfTotalSupply(address a, method f )
{ {
env e; env e;
@ -36,17 +33,17 @@ rule integirtyBalanceOfTotalSupply(address a, method f )
calldataarg arg; calldataarg arg;
sinvoke f(e, arg); sinvoke f(e, arg);
require (f.selector != burn(address, uint256, uint256).selector && require (f.selector != burn(address, uint256, uint256).selector &&
f.selector != mint(address, uint256, uint256).selector ) ; f.selector != mint(address, address, uint256, uint256).selector);
uint256 balanceAAfter = balanceOf(e, a); uint256 balanceAAfter = balanceOf(e, a);
uint256 totalSupplyAfter = totalSupply(e); uint256 totalSupplyAfter = totalSupply(e);
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore)); assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore));
} }
/* Burn behaves deferently and due to accumulation errors might hace less total supply then the balance /**
Checks that the change to total supply is coherent with the change to the balance due to a burn operation.
*/ */
rule integirtyBalanceOfTotalSupplyOnBurn(address a)
rule integirtyBalanceOfTotalSupplyOnBurn(address a, method f )
{ {
env e; env e;
@ -59,67 +56,75 @@ rule integirtyBalanceOfTotalSupplyOnBurn(address a, method f )
sinvoke burn(e, a, x, index); sinvoke burn(e, a, x, index);
uint256 balanceAAfter = balanceOf(e, a); uint256 balanceAAfter = balanceOf(e, a);
uint256 totalSupplyAfter = totalSupply(e); uint256 totalSupplyAfter = totalSupply(e);
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore)); assert (balanceAAfter != balanceABefore => (balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore));
} }
rule integirtyBalanceOfTotalSupplyOnMint(address a, method f ) /**
Checks that the change to total supply is coherent with the change to the balance due to a mint operation.
*/
rule integirtyBalanceOfTotalSupplyOnMint(address u, address delegatedUser)
{ {
env e; env e;
uint256 balanceABefore = balanceOf(e, a); uint256 balanceUBefore = balanceOf(e, u);
uint256 totalSupplyBefore = totalSupply(e); uint256 totalSupplyBefore = totalSupply(e);
uint256 x; uint256 x;
address asset; address asset;
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset); uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
sinvoke mint(e, a, x, index); sinvoke mint(e, delegatedUser, u, x, index);
uint256 balanceAAfter = balanceOf(e, a); uint256 balanceUAfter = balanceOf(e, u);
uint256 totalSupplyAfter = totalSupply(e); uint256 totalSupplyAfter = totalSupply(e);
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore)); assert (balanceUAfter != balanceUBefore => (balanceUAfter - balanceUBefore == totalSupplyAfter - totalSupplyBefore));
} }
/** /**
Minting an amount of x tokens for user u increases their balance by x, up to rounding errors. Minting an amount of x tokens for user u increases her balance by x, up to rounding errors.
{ b= balanceOf(u,t) } { b = balanceOf(u,t) }
mint(u,x,index) mint(delegatedUser, u, x, index)
{ balanceOf(u,t) = b + x } { balanceOf(u,t) = b + x }.
Also, if the minting is done by a user delegatedUser different than u, the balance of delegatedUser
remains unchanged.
*/ */
rule integrityMint(address a, uint256 x) { rule integrityMint(address u, address delegatedUser, uint256 x) {
env e; env e;
address asset; address asset;
uint256 index = POOL.getReserveNormalizedVariableDebt(e,asset); uint256 index = POOL.getReserveNormalizedVariableDebt(e,asset);
uint256 balancebefore = balanceOf(e, a); uint256 balanceUBefore = balanceOf(e, u);
sinvoke mint(e, a, x, index); uint256 balanceDelegatedUBefore = balanceOf(e, delegatedUser);
sinvoke mint(e, delegatedUser, u, x, index);
uint256 balanceAfter = balanceOf(e, a); uint256 balanceUAfter = balanceOf(e, u);
assert balanceAfter == balancebefore+x; uint256 balanceDelegatedUAfter = balanceOf(e, delegatedUser);
assert balanceUAfter == balanceUBefore + x && (u != delegatedUser => (balanceDelegatedUAfter == balanceDelegatedUBefore));
} }
/** /**
Mint is additive, can performed either all at once or gradually Mint is additive, namely it can performed either all at once or gradually:
mint(u,x); mint(u,y) ~ mint(u,x+y) at the same timestamp mint(delegatedUser, u, x, index); mint(delegatedUser, u, y, index) ~ mint(delegatedUser, u, x+y, index) at the same timestamp.
*/ */
rule additiveMint(address a, uint256 x, uint256 y) { rule additiveMint(address a, address delegatedUser, uint256 x, uint256 y) {
env e; env e;
address asset; address asset;
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset); uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
storage initialStorage = lastStorage; storage initialStorage = lastStorage;
sinvoke mint(e, a, x, index); sinvoke mint(e, delegatedUser, a, x, index);
sinvoke mint(e, a, y, index); sinvoke mint(e, delegatedUser, a, y, index);
uint256 balanceScenario1 = balanceOf(e, a); uint256 balanceScenario1 = balanceOf(e, a);
uint t = x + y; uint256 t = x + y;
sinvoke mint(e, a, t ,index) at initialStorage; sinvoke mint(e, delegatedUser, a, t ,index) at initialStorage;
uint256 balanceScenario2 = balanceOf(e, a); uint256 balanceScenario2 = balanceOf(e, a);
assert balanceScenario1 == balanceScenario2, "mint is not additive"; assert balanceScenario1 == balanceScenario2, "mint is not additive";
} }
/** /**
Transfer of x amount of tokens from user u where receiver is user u Burning an amount of x tokens for user u decreases her balance by x, up to rounding errors.
{bu = balanceOf(u) } { bu = balanceOf(u) }
burn(u, u, x) burn(u, x)
{balanceOf(u) = bu - x } { balanceOf(u) = bu - x }.
*/ */
rule integrityBurn(address a, uint256 x) { rule integrityBurn(address a, uint256 x) {
env e; env e;
@ -132,9 +137,8 @@ rule integrityBurn(address a, uint256 x) {
assert balanceAfter == balancebefore - x; assert balanceAfter == balancebefore - x;
} }
/** /**
Minting is additive, i.e., it can be performed either all at once or in steps. Minting is additive, i.e., it can be performed either all at once or in steps:
burn(u, x); burn(u, y) ~ burn(u, x+y) at the same timestamp.
burn(u, u, x); burn(u, u, y) ~ burn(u, u, x+y)
*/ */
rule additiveBurn(address a, uint256 x, uint256 y) { rule additiveBurn(address a, uint256 x, uint256 y) {
env e; env e;
@ -144,7 +148,7 @@ rule additiveBurn(address a, uint256 x, uint256 y) {
sinvoke burn(e, a, x, index); sinvoke burn(e, a, x, index);
sinvoke burn(e, a, y, index); sinvoke burn(e, a, y, index);
uint256 balanceScenario1 = balanceOf(e, a); uint256 balanceScenario1 = balanceOf(e, a);
uint t = x + y; uint256 t = x + y;
sinvoke burn(e, a, t ,index) at initialStorage; sinvoke burn(e, a, t ,index) at initialStorage;
uint256 balanceScenario2 = balanceOf(e, a); uint256 balanceScenario2 = balanceOf(e, a);
@ -152,22 +156,19 @@ rule additiveBurn(address a, uint256 x, uint256 y) {
} }
/** /**
Minting and burning are inverse operations. Minting and burning are inverse operations:
{ bu = balanceOf(u) }
{bu = balanceOf(u) }
mint(u,x); burn(u, u, x) mint(u,x); burn(u, u, x)
{balanceOf(u) = bu } { balanceOf(u) = bu }.
*/ */
rule inverseMintBurn(address a, uint256 x) { rule inverseMintBurn(address a, uint256 x) {
env e; env e;
address asset; address asset;
address delegatedUser;
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset); uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
uint256 balancebefore = balanceOf(e, a); uint256 balancebefore = balanceOf(e, a);
sinvoke mint(e, a, x, index); sinvoke mint(e, delegatedUser, a, x, index);
sinvoke burn(e, a, x, index); sinvoke burn(e, a, x, index);
uint256 balanceAfter = balanceOf(e, a); uint256 balanceAfter = balanceOf(e, a);
assert balancebefore == balanceAfter, "burn is not inverse of mint"; assert balancebefore == balanceAfter, "burn is not the inverse of mint";
} }

View File

@ -2,14 +2,14 @@ pragma solidity ^0.6.8;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import { import {
ReserveConfiguration ReserveConfiguration
} from '../../contracts/libraries/configuration/ReserveConfiguration.sol'; } from '../../contracts/libraries/configuration/ReserveConfiguration.sol';
import {UserConfiguration} from '../../contracts/libraries/configuration/UserConfiguration.sol'; import {UserConfiguration} from '../../contracts/libraries/configuration/UserConfiguration.sol';
import {ReserveLogic} from '../../contracts/libraries/logic/ReserveLogic.sol'; import {ReserveLogic} from '../../contracts/libraries/logic/ReserveLogic.sol';
import {ILendingPool} from '../../contracts/interfaces/ILendingPool.sol'; import {ILendingPool} from '../../contracts/interfaces/ILendingPool.sol';
import {LendingPool} from '../../contracts/lendingpool/LendingPool.sol'; import {LendingPool} from '../../contracts/lendingpool/LendingPool.sol';
import { import {
ILendingPoolAddressesProvider ILendingPoolAddressesProvider
} from '../../contracts/interfaces/ILendingPoolAddressesProvider.sol'; } from '../../contracts/interfaces/ILendingPoolAddressesProvider.sol';
/* /*
@ -82,35 +82,35 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
} }
function getReserveData(address asset) function getReserveData(address asset)
external external
override override
view view
returns (ReserveLogic.ReserveData memory) returns (ReserveLogic.ReserveData memory)
{ {
return originalPool.getReserveData(asset); return originalPool.getReserveData(asset);
} }
function getUserConfiguration(address user) function getUserConfiguration(address user)
external external
override override
view view
returns (UserConfiguration.Map memory) returns (UserConfiguration.Map memory)
{ {
return originalPool.getUserConfiguration(user); return originalPool.getUserConfiguration(user);
} }
function getUserAccountData(address user) function getUserAccountData(address user)
external external
override override
view view
returns ( returns (
uint256 totalCollateralETH, uint256 totalCollateralETH,
uint256 totalBorrowsETH, uint256 totalBorrowsETH,
uint256 availableBorrowsETH, uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold, uint256 currentLiquidationThreshold,
uint256 ltv, uint256 ltv,
uint256 healthFactor uint256 healthFactor
) )
{ {
return originalPool.getUserAccountData(user); return originalPool.getUserAccountData(user);
} }
@ -132,8 +132,8 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
} }
function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
external external
override override
{ {
originalPool.setReserveInterestRateStrategyAddress(reserve, rateStrategyAddress); originalPool.setReserveInterestRateStrategyAddress(reserve, rateStrategyAddress);
} }
@ -143,10 +143,10 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
} }
function getConfiguration(address reserve) function getConfiguration(address reserve)
external external
override override
view view
returns (ReserveConfiguration.Map memory) returns (ReserveConfiguration.Map memory)
{ {
return originalPool.getConfiguration(reserve); return originalPool.getConfiguration(reserve);
} }
@ -161,10 +161,10 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
mapping(uint256 => uint256) private reserveNormalizedVariableDebt; mapping(uint256 => uint256) private reserveNormalizedVariableDebt;
function getReserveNormalizedVariableDebt(address asset) function getReserveNormalizedVariableDebt(address asset)
external external
override override
view view
returns (uint256) returns (uint256)
{ {
require(reserveNormalizedVariableDebt[block.timestamp] == 1e27); require(reserveNormalizedVariableDebt[block.timestamp] == 1e27);
return reserveNormalizedVariableDebt[block.timestamp]; return reserveNormalizedVariableDebt[block.timestamp];