Contract Security, Design Patterns and Reputation
Since I published my last post, custodial models and governance structures for DAOs have become hot topics for discussion. In that post I recommended constitutional governing documents spelling out intent and constitutional governance mechanisms to reduce fragmentation and to achieve greater aligned incentives among token holders, recommendations that have since been echoed by Vitalik Buterin and Gavin Wood.
In this post, I will focus on very recent best practices and secure design patterns available for Solidity smart contracts. These best practices and secure design patterns are the first steps in resolving vulnerabilities that have been revealed to be far too prevalent in currently deployed Ethereum smart contracts.
Since my previous post, I have become involved in the creation of additional DAO governance structures. Working with additional DAO entities allows me the opportunity to enthusiastically disseminate the legal and technical lessons I have learned, and to gain new experience with varying needs of a wide range of DAOs [and how these needs are met legally and technically].
Structural variances and legal issues aside for the moment, two primary themes have come up during the creation of the code bases composing the resulting DAOs: (1) the identification and elimination of attack surfaces, and (2) rigorous analysis and testing frameworks to assist in the first task.
Fundamentally, there is a trade-off between complexity and the ability to identify and eliminate all attack surfaces. As a result, while fulfilling the requirements of any DAO via source code the goal will be to minimize the contract interfaces, simplify the control flow and architecture, and employ coding, analysis and testing best practices available for the platform. These requirements are met under a test-driven development framework.
Coupled with test-driven development is a focus on defensive testing: identifying the largest risks and eliminating them in an iterative fashion. Defensive testing is quite different than nominal path testing. Defensive testing approaches, in aspiration, exhaustive testing under an unlimited set of circumstances. Solidity as a programming language makes exhaustive testing and rigorous analysis difficult. As a result of the inherent weaknesses in the primary language in use and the associated published best practices, there exists a real threat to Ethereum as a healthy ecosystem. The goal of this piece is to educate participants in the ecosystem to minimize the threat going forward.
Knowledge of smart contract attack vectors illuminates smart contract implementation patterns that are incorrect or incomplete. Design patterns are commonly used in software to facilitate architectural and source code best practices - security or otherwise. In this case, as a first step, let’s focus on design patterns to counteract known Ethereum Solidity attack vectors.
Below is a brief history of the most recently revealed Solidity attack vectors. This is a non-exhaustive history (for example, this list does not include the previously disclosed call stack depth limit vulnerability). Presented alongside each attack vector is a design pattern or two provided to counteract, eliminating or mitigating the attack vector. The resulting design patterns can be considered best practices for Solidity secure implementations.
From external references, here are the vulnerabilities subject to attack and the design patterns to counteract them:
June 4: Send with Throw
June 9: Race to Empty
June 16: Unchecked Send
June 17: Re-entrancy
June 19: Loss of State
Adoption of these design patterns should be incorporated into Solidity analysis and testing tools that by definition reveal weaknesses in smart contract source code. In this case, source code best practices — as well as analysis and testing tools — should be updated to assess known attack vectors and design patterns to best counteract them in smart contract source code. The Solidity browser is an example of a tool that sets a good standard for this. Formal software testing for Solidity will require new building blocks as well.
Contract source code verification tools can provide a measure of risk and as such may provide assessments prior to the engagement with a smart contract. These assessments are a rational basis on which to establish a relative smart contract trust score. Additionally, these assessments serve — among other factors — as a basis for contract deployment reputation.