Performance tuning smart contracts for fun and profit (with some horror).

makoto_inoue
BlockParty
Published in
6 min readSep 18, 2017
At CodeUp22 where Simply Business sponsored beers and pizzas

Introduction

In this blog post, I will share my experience of doing some performance tuning on existing smart contract and its associated security risks of doing so.

This is a technical blog post and I assume that readers have some understanding of Solidity, a programming language to write smart contracts on Ethereum.

Stories so far

(You can skip this part if you already know BlockParty)

I have been working on an event management DApp (Distributed App) called “BlockParty”. You pay a small deposit when you register. You lose your deposit if you do not turn up. You will get your deposit back + we split the deposit of whom did not turn up. Simple! I’ve been using the dapp on the mainnet (with real ether at stake) since DevCon2 to host small parties (we used to put ETH1 per person as a deposit!).

Back in June, while Ether price was going wild with various famous ICOs, I piloted to use BlockParty to manage a monthly Ethereum coding group I organise called “London Ethereum Codeup” and wrote a blog post called “Running everyday dapp when Ethereum is under pressure”.

Solving scalability issues

With the success of piloting a meetup, now it’s time to change gears.

My goals for this time was to actually use BlockParty as a main registration tool for CodeUp as well as piloting at London Ethereum Meetup.

The first problem I wanted to tackle was a scalability issue. When you write normal web app, that’s one of YAGNI(You aren’t gonna need it) until you have millions of users. That’s not the same when writing smart contract as every operation costs Ether in the form of “gas”.

Since Ether price went up x30 for the last 6 month, I became very cost conscious and started monitoring the gas usage with a script and I’ve been aware that the cost for the contract owner to execute payback function (which determines the final payback cost and allows users to withdraw their payout) has a linear gas increase.

Gas estimate of payback function depending on the number of participants

As you can see, the cost of payback increases roughly $0.03 per user. This leads to nearly $3 for hosting over 200 participants event. You may think it's not that expensive but high gas cost has another implication.

The gas cost at 200 participants was about 4.5 million. This was the almost close to the upper limit of GasLimit back in June. Even though the gas limit is now increased to 6.7 million, this means that you cannot execute payback function of an event more than around 300 people.

Average gasLimit chart from etherscan

If you cannot run the function, it gets to the situation that no one can withdraw their deposit (unless I call selfdestruct function and payback to each participant manually).

So this is the fix I made.

As you can see, payback and cancel function both iterate through each participant and set their payback amount. The change is to have a global payback value payoutAmount as we know that everybody either receives the same amount (or not at all if you do not attend).

When you withdraw, it sends the amount specified at a global variablepayoutAmount instead of paticipant.payout which is set per each participant, but it was actually same across everybody.

With this relatively simple change, the gas growth was changed from linear to constant.

Saving few more gases.

Just about the time I started on tackling the scalability issues, I started reading @hayeah’s Diving into The Ethereum VMseries.

I had a basic idea of how Ethereum VM works but didn’t know enough to reflect into my solidity coding. It’s surprising to see that some basic stuff like not setting zero variable can actually save a few gas cost.

This is the overall result of my performance tuning work.

PR for performance optimisation.

The biggest improvement was payback function but most function also saved a few cents which made me very happy.

Then the horror begins…

I used the contract to host not only CodeUp (I will write another blog post about how it went on) but also to pilot at Ethereum London meetup which is one of the biggest Ethereum meetups on earth.

I did a quick demo on stage about how things work and the response seemed positive. A couple of friends talked to me afterword at pub saying that we should use BlockParty to host half of the capacity (130). The highest number of people registered so far was 13 so it’s almost 10x capacity with ETH 6.5 at steak. That sounds a bit too risky for me so I suggested them to have a look into codes for pair review and they said they will.

And the following day….

Bug report on github issues

This was the first bug on my smart contract that anyone, whether they have registered the event or not, could drain money (yes I had some minor ones before).

Fortunately this could be exploited only if I decided to cancel the event and let all participants to withdraw their deposit equally.

So how did this happen? Let’s go back to the change I made during the performance optimisation.

Prior to the change, I was sending amount paticipant.payout so that non registered people could not receive anything. Now I changed to payoutAmount so that even non participants could receive the same amount. In the normal payout situation, it would not have reached at this point except it's canceled which allow anyone to send deposit to themselves multiple times, allowing them to drain the entire deposit.

The change I made was relatively easy.

PR to fix the bug

I made sure to check that participant address and sender address matches. I also send to participant.addr instead of msg.sender. Though I did not do any emergency upgrade(and to be honest there’s no emergency upgrade in my contracts) now that the meetups were over and the majority of deposits were withdrawn so there were less likely that people could have lost any deposits. Phew..

Conclusion

Every change on smart contract may inject new security vulnerabilities. Are there any ways to increase more external pair of eyes to spot any flows for each release? When I first hosted BlockParty, I actually created a security bounty smart contract and put 1ETH but that has never claimed. It may probably because 1ETH was too small to get enough attention of security researchers and still not a lot compared to bug bounties set on other crowdsale contracts.

I’ve been thinking about some sort of smart contract developer mutuals where each developer audit each other’s code and verify it by betting some sort of assurance. If you are interested in the idea, please join the reddit discussion below.

--

--