Skip to content

Commit

Permalink
Merge branch 'master' into nssc
Browse files Browse the repository at this point in the history
  • Loading branch information
bohendo committed Oct 3, 2022
2 parents 7d19b4a + 7762922 commit 80badac
Show file tree
Hide file tree
Showing 119 changed files with 5,624 additions and 580 deletions.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
13 changes: 7 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ on:

jobs:
tests:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
type: ["slither", "echidna", "manticore"]
type: ["slither", "manticore"]
steps:
- uses: actions/checkout@v1
- name: Set up Python 3.6
uses: actions/setup-python@v1
- uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:
python-version: 3.6
python-version: 3.8
- name: Install dependencies
run: |
sudo wget -O /usr/bin/solc https://github.com/ethereum/solidity/releases/download/v0.5.11/solc-static-linux
Expand Down
128 changes: 128 additions & 0 deletions .github/workflows/echidna.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
name: Echidna

on:
push:
branches:
- master
- dev
pull_request:
schedule:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'

jobs:
tests:
name: ${{ matrix.name }}
continue-on-error: ${{ matrix.flaky == true }}
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- name: Exercise 1
workdir: program-analysis/echidna/exercises/exercise1/
files: solution.sol
contract: TestToken
outcome: failure
expected: 'echidna_test_balance:\s*failed'
- name: Exercise 2
workdir: program-analysis/echidna/exercises/exercise2/
files: solution.sol
contract: TestToken
outcome: failure
expected: 'echidna_no_transfer:\s*failed'
- name: Exercise 3
workdir: program-analysis/echidna/exercises/exercise3/
files: solution.sol
contract: TestToken
outcome: failure
expected: 'echidna_test_balance:\s*failed'
- name: Exercise 4
workdir: program-analysis/echidna/exercises/exercise4/
files: solution.sol
config: config.yaml
contract: Token
outcome: failure
expected: 'transfer(address,uint256):\s*failed'
- name: TestToken
workdir: program-analysis/echidna/example/
files: testtoken.sol
contract: TestToken
outcome: failure
expected: 'echidna_balance_under_1000:\s*failed'
- name: Gas estimation
workdir: program-analysis/echidna/example/
files: gas.sol
config: gas.yaml
outcome: success
expected: 'f(42,123,'
flaky: true
- name: Multi
workdir: program-analysis/echidna/example/
files: multi.sol
config: filter.yaml
outcome: failure
expected: 'echidna_state4:\s*failed'
- name: Assert
workdir: program-analysis/echidna/example/
files: assert.sol
config: assert.yaml
outcome: failure
expected: 'inc(uint256):\s*failed'
- name: PopsicleBroken
workdir: program-analysis/echidna/example/
files: PopsicleBroken.sol
solc-version: 0.8.4
config: Popsicle.yaml
contract: PopsicleBroken
outcome: failure
expected: 'totalBalanceAfterTransferIsPreserved(address,uint256):\s*failed'
- name: PopsicleFixed
workdir: program-analysis/echidna/example/
files: PopsicleFixed.sol
solc-version: 0.8.4
config: Popsicle.yaml
contract: PopsicleFixed
outcome: success
expected: 'totalBalanceAfterTransferIsPreserved(address,uint256):\s*passed'
- name: TestDepositWithPermit
workdir: program-analysis/echidna/example/
files: TestDepositWithPermit.sol
solc-version: 0.8.0
config: testdeposit.yaml
contract: TestDepositWithPermit
outcome: success
expected: 'testERC20PermitDeposit(uint256):\s*passed'

steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Run Echidna
uses: crytic/echidna-action@v2
id: echidna
continue-on-error: true
with:
files: ${{ matrix.files }}
contract: ${{ matrix.contract }}
config: ${{ matrix.config }}
output-file: ${{ matrix.files }}.out
solc-version: ${{ matrix.solc-version || '0.5.11' }}
echidna-workdir: ${{ matrix.workdir }}
echidna-version: edge
- name: Verify that the exit code is correct
run: |
if [[ ${{ steps.echidna.outcome }} = ${{ matrix.outcome }} ]]; then
echo "Outcome matches"
else
echo "Outcome mismatch. Expected ${{ matrix.outcome }} but got ${{ steps.echidna.outcome }}"
exit 1
fi
- name: Verify that the output is correct
run: |
if grep -q "${{ matrix.expected }}" "${{ steps.echidna.outputs.output-file }}"; then
echo "Output matches"
else
echo "Output mismatch. Expected something matching '${{ matrix.expected }}'. Got the following:"
cat "${{ steps.echidna.outputs.output-file }}"
exit 1
fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Building Secure Smart Contracts

![](https://github.com/crytic/building-secure-contracts/workflows/CI/badge.svg) ![](https://github.com/crytic/building-secure-contracts/workflows/Echidna/badge.svg)

Follow our guidelines and best practices to write secure smart contracts.

**Table of contents:**

- [Development guidelines](./development-guidelines)
- [High-level best practices](./development-guidelines/guidelines.md): High-level best-practices for all smart contracts
- [Incident Response Recommendations](./development-guidelines/incident_response.md): Guidelines on how to formulate an incident response plan
- [Secure development workflow](./development-guidelines/workflow.md): A rough, high-level process to follow while you write code
- [Token integration checklist](./development-guidelines/token_integration.md): What to check when interacting with arbitrary token
- [Learn EVM](./learn_evm): EVM technical knowledge
- [EIPs - forks](./learn_evm/eips_forks.md): summarize the EIPs included in each fork
- [Program analysis](./program-analysis): How to use automated tools to secure contracts
- [Echidna](./program-analysis/echidna): a fuzzer that will check your contract's properties.
- [Slither](./program-analysis/slither): a static analyzer avaialable through a CLI and scriptable interface.
- [Manticore](./program-analysis/manticore): a symbolic execution engine that can prove the correctness properties.
- For each tool, this training material will provide:
- a theoretical introduction, a walkthrough of its API, and a set of exercises.
- exercises expected to require ~two hours to practically learn its operation.
100 changes: 5 additions & 95 deletions development-guidelines/README.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,6 @@
# Development Guidelines
List of smart contract development best practices

The following recommendations describe high-level best pratices.

## Design Guidelines

The design of the contract should be discussed ahead of time, prior writing any line of code.

### Documentation and specification

Documentation can be written at different levels, and should be updated while implementing the contracts:

- **A plain English description of the system**, describing what the contracts do and any assumptions on the codebase.

- **Schema and architectural diagrams**, including the contracts interactions, and the state machine of the system. [Slither printers](https://github.com/crytic/slither/wiki/Printer-documentation) will help to generate these schemas.

- **Thorough code documentation**, the [Natspec format](https://solidity.readthedocs.io/en/develop/natspec-format.html) can be used for Solidity.

## Onchain vs Offchain computation

- **Put as much code as you can offchain.** Keep the onchain layer small. Rely on offchain code for any data pre-processing for which the result can be easily verified onchain. You need an ordered list? Sort the list offchain, and check only its order onchain.

## Upgradeability

We discussed the different upgradeability solutions in [our blogpost](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/). The choice of upgradeable contract or not must be deliberate prior coding. The decision will influence how you structure our code. The summary is:

- **Favor [contract’s migration](https://blog.trailofbits.com/2018/10/29/how-contract-migration-works/) over upgradeability.** Migrations system have the same advantages than upgradeable, without their drawbacks.

- **Use the data separation pattern over the delegatecallproxy one.** If your project has a clear abstraction separation, upgradeability using the data separation pattern will necessitate only a few adjustments. The delegatecallproxy requires EVM expertise and is highly error-prone.

- **Document the migration/upgrade procedure before the deployment.** If you have to react under stress without any guidelines, you will make mistakes. Write the procedure to follow ahead of time. It should include:
- What are the calls to make to initiate the new contracts.
- Where are stored the keys, and how to access them.
- How to check the deployment. A post-deployment script should be prepared, and fully tested.

# Implementation Guidelines

The main advice to keep in mind is to **strive for simplicity.** Always use the simplest solution that fits your purpose. Always keep in mind that anyone should be able to understand your solution.

## Function Composition

The architecture of your codebase will also heavily influence the ease to review your code.

- **Split the logic of your system**, either through multiple contracts or by grouping similar functions together (ex: authentification, arithmetic, …). It will ease targeting the features of the code

- **Write small functions, with a clear purpose.** Small code is simpler to review and allow the testing of individual components.

## Inheritance

- **Keep the inheritance manageable.** While inheritance should be used to divide the logic, your project should aim to minimize the depth and the width of the inheritance tree.

- **Use Slither’s [inheritance printer](https://github.com/crytic/slither/wiki/Printer-documentation#inheritance-graph) to check the contracts’ hierarchy.** The inheritance printer will help you to watch the hierarchy grows.

## Events

- **Log any crucial operation.** Events will help to debug the contract during the development, and monitor it after its deployment.

## Blockchain pitfalls

- **Be aware of the most common vulnerabilities patterns.** There are many online resources to learn about common issues, such as [Ethernaut CTF](https://ethernaut.openzeppelin.com/), [Capture the Ether](https://capturetheether.com/), or [Not so smart contract](https://github.com/crytic/not-so-smart-contracts/).

- **Be aware of the warnings sections in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/).** The warnings sections will inform you about not obvious behavior of the language.

## Dependencies

- **Use well-tested libraries.** Importing code from well-tested libraries will reduce the likelihood for you to write buggy code. If you want to write an ERC20 contract, use [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20).

- **Use a dependencies manager, avoid copy-and-paste of code.** If you rely on an external source, be sure to be up to date with the original codebase.

## Testing and Verifying

- **Write thorough unit-tests.** An extensive test suite is crucial to build high-quality software.

- **Write [Slither](https://github.com/crytic/slither), [Echidna](https://github.com/crytic/echidna) and [Manticore](https://github.com/trailofbits/manticore) custom checks and properties.** Automated tools will help you to ensure your contracts is secure. The content of this repository will help you to write efficient checks and properties.

- **Use [crytic.io](https://crytic.io/).** Crytic will give you access to Slither’s private detectors, and will provide github integration.

## Solidity

- **Favor Solidity 0.5 over 0.4 and 0.6.** Solidity 0.5 is overall more secure and has better inbuilt practices and 0.4. Solidity 0.6 is too young to be used in production.

- **Use a stable compiler’s version to compile, use the latest to check for warnings.** You should check that your code leads to no warning with the latest compiler version. Solidity compiler has a fast release cycle, and has a history of compiler bugs, as a result, we recommend to not use the latest version for deployment (see Slither’s [solc version recommendation](https://github.com/crytic/slither/wiki/Detector-Documentation#recommendation-33)).

- **Do not use inline assembly.** Assembly requires EVM expertise, if you do not master the yellow paper, do not write EVM code.

## Post development Guidelines

Once the contract has been developed, consider to:

- **Monitor your contracts.** Watch the contracts’ logs, and be ready to react in case of contracts or wallet compromise.

- **Add your contact info to [blockchain-security-contacts](https://github.com/crytic/blockchain-security-contacts).** The list will help third-parties to contact and coordinate with you if any security flaw is discovered.

- **Secure the wallets of privileged users.** Follow the [best practices](https://blog.trailofbits.com/2018/11/27/10-rules-for-the-secure-use-of-cryptocurrency-hardware-wallets/) to store the wallets.

- **Have a response to incident plan.** Take in consideration that you can be compromised. Even if your contracts are free of bugs, an attacker can take control of the contract’s owner keys.
- [High-level best practices](./guidelines.md): High-level best-practices for all smart contracts
- [Token integration checklist](./token_integration.md): What to check when interacting with arbitrary tokens
- [Incident Response Recommendations](./incident_response.md): Guidelines on how to formulate an incident response plan
- [Secure development workflow](./workflow.md): A rough, high-level process to follow while you write code
Loading

0 comments on commit 80badac

Please sign in to comment.