https://github.com/sri-csl/smart-contract-explorer
https://github.com/sri-csl/smart-contract-explorer
Last synced: 10 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/sri-csl/smart-contract-explorer
- Owner: SRI-CSL
- License: gpl-3.0
- Created: 2019-06-14T02:02:14.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2023-04-27T15:33:46.000Z (about 3 years ago)
- Last Synced: 2025-04-13T14:44:37.627Z (about 1 year ago)
- Language: Solidity
- Size: 749 KB
- Stars: 0
- Watchers: 3
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Smart Contract Explorer
Exploring behaviors of Solidity smart contracts.
## Requirements
* [solc-verify](https://github.com/SRI-CSL/solidity)
* [Node.js](https://nodejs.org/en/)
* [Ocaml](https://ocaml.org)
* [OCaml Package Manager](https://opam.ocaml.org)
While `solc-verify` must currently be built from source, the remainder are available from various package managers, e.g., from [Homebrew](https://brew.sh):
```
$ brew install node opam
$ opam init
```
## Setup
```
$ git clone git@github.com:SRI-CSL/smart-contract-explorer.git
$ git submodule init
$ git submodule update
$ npm link
```
and optionally:
```
$ npm run test
```
## Usage
```bash
$ sc-simulation --help
```
```
usage: sc-simulation --source --target
Options:
--version Show version number [boolean]
--verbose output verbosity [boolean] [default: false]
--output output directory [string] [default: ".sc-simulation.ignore"]
--source source smart contract [string] [required]
--target target smart contract [string] [required]
--states number of states to explore [number] [default: 5]
--relation append to the simulation relation [string]
--examples generate examples for synthesis [boolean] [default: true]
--synthesize synthesize the simulation relation [boolean] [default: true]
--verify verify the simulation contract [boolean] [default: true]
--help Show help [boolean]
```
### Generating examples for synthesis
```bash
$ sc-simulation --source resources/contracts/Token/ERC20-molochventures/ERC20.impl.sol --target resources/contracts/Token/ERC20-openzeppelin/ERC20.spec.sol --no-synthesize --no-verify && cat .sc-simulation.ignore/SimulationExamples.sol
```
```
sc-simulation version 0.0.1
---
Generating examples
---
Warning: did not generate accessor for mapping: _allowed
Warning: did not generate accessor for mapping: allowances
```
```solidity
pragma solidity ^0.5.0;
import "/Users/mje/Code/smart-contract-explorer/.sc-simulation.ignore/ERC20.impl.exemplified.sol";
import "/Users/mje/Code/smart-contract-explorer/.sc-simulation.ignore/ERC20.spec.exemplified.sol";
contract SimulationExamples {
Token impl;
ERC20 spec;
function positiveExample0() public {
impl = (new Token)();
spec = (new ERC20)();
}
function positiveExample1() public {
impl = (new Token)();
impl.transfer(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 0);
spec = (new ERC20)();
spec.transfer(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 0);
}
function positiveExample2() public {
impl = (new Token)();
impl.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 0);
spec = (new ERC20)();
spec.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 0);
}
function negativeExample0() public {
impl = (new Token)();
spec = (new ERC20)();
spec.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 1);
}
function negativeExample1() public {
impl = (new Token)();
impl.transfer(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 0);
spec = (new ERC20)();
spec.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 1);
}
function negativeExample2() public {
impl = (new Token)();
impl.transfer(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 1);
spec = (new ERC20)();
spec.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 1);
}
function negativeExample3() public {
impl = (new Token)();
impl.transfer(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 2);
spec = (new ERC20)();
spec.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 1);
}
function negativeExample4() public {
impl = (new Token)();
impl.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 0);
spec = (new ERC20)();
spec.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 1);
}
function negativeExample5() public {
impl = (new Token)();
impl.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 1);
spec = (new ERC20)();
spec.transfer(0x6999c8a988a86C52297D9351235644e5768c28e6, 1);
}
function impl$_balances() public view returns (bytes32) {
return keccak256(abi.encode(
impl._balances(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
impl._balances(0x6999c8a988a86C52297D9351235644e5768c28e6)
));
}
function impl$_allowed() public view returns (bytes32) {
return keccak256(abi.encode(
impl._allowed(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
impl._allowed(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 0x6999c8a988a86C52297D9351235644e5768c28e6),
impl._allowed(0x6999c8a988a86C52297D9351235644e5768c28e6, 0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
impl._allowed(0x6999c8a988a86C52297D9351235644e5768c28e6, 0x6999c8a988a86C52297D9351235644e5768c28e6)
));
}
function impl$_totalSupply() public view returns (uint) {
impl._totalSupply();
}
function spec$balances() public view returns (bytes32) {
return keccak256(abi.encode(
spec.balances(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
spec.balances(0x6999c8a988a86C52297D9351235644e5768c28e6)
));
}
function spec$allowances() public view returns (bytes32) {
return keccak256(abi.encode(
spec.allowances(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
spec.allowances(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09, 0x6999c8a988a86C52297D9351235644e5768c28e6),
spec.allowances(0x6999c8a988a86C52297D9351235644e5768c28e6, 0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
spec.allowances(0x6999c8a988a86C52297D9351235644e5768c28e6, 0x6999c8a988a86C52297D9351235644e5768c28e6)
));
}
function spec$minters() public view returns (bytes32) {
return keccak256(abi.encode(
spec.minters(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
spec.minters(0x6999c8a988a86C52297D9351235644e5768c28e6)
));
}
function spec$burners() public view returns (bytes32) {
return keccak256(abi.encode(
spec.burners(0xab7A7Cb3E460a4ec0B73e07dA54F4173d9bB0c09),
spec.burners(0x6999c8a988a86C52297D9351235644e5768c28e6)
));
}
}
```
### Synthesizing simulation relations
```bash
$ sc-simulation --source resources/contracts/Token/BasicToken-impl/BasicToken.impl.sol --target resources/contracts/Token/BasicToken-spec/BasicToken.spec.sol --no-verify
```
```
sc-simulation version 0.0.1
---
Generating examples
---
Warning: did not generate accessor for mapping: allowances
Warning: did not generate accessor for mapping: allowances
---
Synthesizing simulation relation
---
---
Computed simulation relation:
spec$balances == impl$balances
---
```
### Verifying simulation relations
```bash
$ DEBUG=*:evaluate sc-simulation --source resources/contracts/Token/ERC20-molochventures/ERC20.impl.sol --target resources/contracts/Token/ERC20-openzeppelin/ERC20.spec.sol --no-synthesize && cat .sc-simulation.ignore/SimulationCheck.sol
```
```
Keccak bindings are not compiled. Pure JS implementation will be used.
sc-simulation version 0.0.1
---
Generating examples
---
Warning: did not generate accessor for mapping: _allowed
Warning: did not generate accessor for mapping: allowances
---
Checking simulation relation
---
---
Verified simulation relation
---
```
```solidity
pragma solidity ^0.5.0;
import "/Users/mje/Code/smart-contract-explorer/.sc-simulation.ignore/ERC20.impl.internalized.sol";
import "/Users/mje/Code/smart-contract-explorer/.sc-simulation.ignore/ERC20.spec.internalized.sol";
/**
* @notice invariant __verifier_eq(Token._balances, ERC20.balances)
* @notice invariant __verifier_eq(Token._allowed, ERC20.allowances)
*/
contract SimulationCheck is Token, ERC20 {
/**
* @notice postcondition impl_ret_0 == spec_ret_0
*/
function check$balance(address account) public view returns (uint256 impl_ret_0, uint256 spec_ret_0) {
impl_ret_0 = Token.balance(account);
spec_ret_0 = ERC20.balance(account);
return (impl_ret_0, spec_ret_0);
}
/**
* @notice postcondition impl_ret_0 == spec_ret_0
*/
function check$allowance(address owner, address beneficiary) public view returns (uint256 impl_ret_0, uint256 spec_ret_0) {
impl_ret_0 = Token.allowance(owner, beneficiary);
spec_ret_0 = ERC20.allowance(owner, beneficiary);
return (impl_ret_0, spec_ret_0);
}
/**
* @notice modifies Token._balances[msg.sender]
* @notice modifies Token._balances[to]
* @notice modifies ERC20.balances[msg.sender]
* @notice modifies ERC20.balances[to]
* @notice precondition to != address(0)
* @notice precondition to != msg.sender
* @notice precondition ERC20.balances[to] + val >= ERC20.balances[to]
* @notice precondition ERC20.balances[msg.sender] - val >= 0
*/
function check$transfer(address to, uint256 val) public {
Token.transfer(to, val);
ERC20.transfer(to, val);
}
/**
* @notice modifies Token._allowed[msg.sender]
* @notice modifies ERC20.allowances[msg.sender]
* @notice precondition to != address(0)
*/
function check$approve(address to, uint256 val) public {
Token.approve(to, val);
ERC20.approve(to, val);
}
/**
* @notice modifies Token._balances[to]
* @notice modifies Token._balances[from]
* @notice modifies Token._allowed[from]
* @notice modifies ERC20.balances[to]
* @notice modifies ERC20.balances[from]
* @notice modifies ERC20.allowances[from]
* @notice precondition to != address(0)
* @notice precondition to != from
* @notice precondition ERC20.allowances[from][msg.sender] - val >= 0
* @notice precondition ERC20.balances[to] + val >= ERC20.balances[to]
* @notice precondition ERC20.balances[from] - val >= 0
*/
function check$transferFrom(address from, address to, uint256 val) public {
Token.transferFrom(from, to, val);
ERC20.transferFrom(from, to, val);
}
/**
* @notice modifies Token._allowed[msg.sender]
* @notice modifies ERC20.allowances[msg.sender]
* @notice precondition spender != address(0)
* @notice precondition ERC20.allowances[msg.sender][spender] + val >= ERC20.allowances[msg.sender][spender]
*/
function check$increaseAllowance(address spender, uint256 val) public {
Token.increaseAllowance(spender, val);
ERC20.increaseAllowance(spender, val);
}
/**
* @notice modifies Token._allowed[msg.sender]
* @notice modifies ERC20.allowances[msg.sender]
* @notice precondition spender != address(0)
* @notice precondition ERC20.allowances[msg.sender][spender] - val >= 0
*/
function check$decreaseAllowance(address spender, uint256 val) public {
Token.decreaseAllowance(spender, val);
ERC20.decreaseAllowance(spender, val);
}
/**
* @notice modifies Token._balances[to]
* @notice modifies Token._totalSupply
* @notice modifies ERC20.balances[to]
* @notice precondition to != address(0)
* @notice precondition ERC20.minters[msg.sender]
* @notice precondition ERC20.balances[to] + val >= ERC20.balances[to]
*/
function check$mint(address to, uint256 val) public {
Token.mint(to, val);
ERC20.mint(to, val);
}
/**
* @notice modifies Token._balances[from]
* @notice modifies Token._totalSupply
* @notice modifies ERC20.balances[from]
* @notice precondition from != address(0)
* @notice precondition ERC20.burners[msg.sender]
* @notice precondition ERC20.balances[from] - val >= 0
*/
function check$burn(address from, uint256 val) public {
Token.burn(from, val);
ERC20.burn(from, val);
}
}
```