Smart Contract : Security Patterns

Seungwon Go
Dec 20, 2018 · 4 min read

By Seungwon Go, CEO & Founder at ReturnValues (seungwon.go@returnvalues.com)

Image for post
Image for post

Checks Effects Interaction Patern

The following code is an example where Checks Effects Interaction pattern.

function auctionEnd() public { 
// 1. Checks require(now >= auctionEnd);
require(!ended);
// 2. Effects
ended = true;
// 3. Interaction
beneficiary.transfer(highestBid);
}

The code below is vulnerable to re-entrancy attack since Checks Effects Interaction pattern is not used.

// INSECURE
mapping (address => uint) private userBalances;
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
if (!(msg.sender.call.value(amountToWithdraw)())) { throw; } // At this point, the caller's code is executed, and can call withdrawBalance again
userBalances[msg.sender] = 0;
}

In the example above, you can withdraw more amount than there is left by calling withdrawBalance() recursively before userBalances[msg.sender]=0; gets run.

You can prevent reculsive calling of withdrawBalance() by managing the reentrancy state as in the code below.

bool private reentrancyLock = false;modifier nonReentrancy() {
require(!reentrancyLock);
reentrancyLock = true;
_;
reentrancyLock = false;
}
mapping (address => uint) private userBalances;function withdrawBalance() public nonReentrancy() {
uint amountToWithdraw = userBalances[msg.sender];
if (!(msg.sender.call.value(amountToWithdraw)())) { throw; } // At this point, the caller's code is executed, and can call withdrawBalance again
userBalances[msg.sender] = 0;
}

Emergency Stop Pattern

You might be familiar with Pausable contracts but it is more useful to use halt, which is much stronger and permanent. Pausable provides a feature that stops the contract from executing according to the intention of the contract owner for a while. The reason doesn’t have to be a bug.

contract EmergencyStop is Owned { 
bool public contractStopped = false;
modifier haltInEmergency {
require(contractStopped)
_;
}
modifier enableInEmergency {
require(!contractStopped)
_;
}
function toggleContractStopped() public onlyOwner {
contractStopped = !contractStopped;
}
function deposit() public payable haltInEmergency {
// some code
}
function withdraw() public view enableInEmergency {
// some code
}
}

Speed Bump Pattern

Speed Bump pattern is a programming pattern where you set a limit to withdrawal by setting a time interval or a maximum amount. It is for preventing many users from calling a certain function endless causing malfunction of the contract.

In the code below, we call a requestWithdrawal function to save how much has been withdrawn before actual withdrawal. And the actual withdrawal is executed through the withdraw function. And when withdraw function is called, it checks if 7 days have been passed since the withdrawal was requested by calling requestWithdrawal.

contract SpeedBump { 
struct Withdrawal {
uint amount;
uint requestedAt;
}
mapping (address => uint) private balances;
mapping (address => Withdrawal) private withdrawals;
uint constant WAIT_PERIOD = 7 days;
function deposit() public payable {
if(!(withdrawals[msg.sender].amount > 0)) {
balances[msg.sender] += msg.value;
}
}
function requestWithdrawal() public {
if (balances[msg.sender] > 0) {
uint amountToWithdraw = balances[msg.sender];
balances[msg.sender] = 0;
withdrawals[msg.sender] = Withdrawal({ amount: amountToWithdraw, requestedAt: now });
}
}
function withdraw() public {
if(withdrawals[msg.sender].amount > 0 && now > withdrawals[msg.sender].requestedAt + WAIT_PERIOD) {
uint amount = withdrawals[msg.sender].amount;
withdrawals[msg.sender].amount = 0;
msg.sender.transfer(amount);
}
}
}

Rate Limit Pattern

contract RateLimit { 
uint enabledAt = now;
modifier enabledEvery(uint t) {
if (now >= enabledAt) {
enabledAt = now + t;
_;
}
}
function f() public enabledEvery(1 minutes) {
// some code
}
}

Mutex Pattern

contract Mutex { 
bool locked;
modifier noReentrancy() {
require(!locked);
locked = true;
_;
locked = false;
}
// f is protected by a mutex, thus reentrant calls
// from within msg.sender.call cannot call f again
function f() noReentrancy public returns (uint) {
require(msg.sender.call());
return 1;
}
}

Balance Limit Pattern

contract LimitBalance { 
uint256 public limit;
function LimitBalance(uint256 value) public {
limit = value;
}
modifier limitedPayable() {
require(this.balance <= limit);
_;
}
function deposit() public payable limitedPayable {
// some code
}
}

ReturnValues

ReturnValues Blogs

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store