Securing Smart Contracts on the Blockchain: A Technical Overview of the Quantstamp Betanet Protocol

By Nadir Akhtar

This document is an overview of the Betanet release — version 0.1.0 of the Quantstamp protocol. Future iterations of the protocol will include more focus on decentralization and handling private audits. However, in the meantime, this iteration is a first step towards achieving those goals.

Table of Contents

Introduction

  • The Need for Smart Contract Security
  • Smart Contract Audits
  • The Quantstamp Protocol
  • FAQ

Design Overview

  • Protocol Summary
  • Protocol Process
  • Protocol Breakdown

Contract Architecture

  • QuantstampAudit
  • QuantstampAuditData
  • QuantstampAuditView

Introduction

What is the goal that the Quantstamp protocol accomplishes?

The aim of the Quantstamp protocol is to create a decentralized platform for automated smart contract security audits.

The Need for Smart Contract Security

Smart contracts, enabled by blockchain technology such as Ethereum, have the potential to revolutionize whole industries. However, their immutable nature and automatic execution mean security is essential. This was made clear a year after Ethereum was released by a hack of The DAO.

The DAO was a decentralized autonomous organization implemented through smart contracts which formed an investor-directed venture capital fund. In June 2016, a hacker exploited a vulnerability in The DAO’s smart contracts to siphon off a third of its funds — more than $50 million USD at the time. The DAO hack impacted a large number of people in the Ethereum community and led to a growing realization of the importance of smart contract security.

Smart contract development is considerably different from traditional software development. Rather than working with a database under your own control, decentralized applications (DApps) deployed on the blockchain are immutable, visible to everyone with access to the blockchain network, usually have control over significant amounts of capital, and present an immense attack surface. Even the smallest errors in the contract code can result in catastrophes. It’s clear that security is of the utmost priority when developing and deploying smart contracts. To achieve security guarantees, it’s necessary to perform an audit of the project before launch.

Smart Contract Audits

A smart contract audit is a thorough inspection of an individual smart contract or smart contract project to help ensure that the code cannot misbehave in any way and cannot be misused by an attacker. This means looking not only for common vulnerabilities such as integer overflow and memory mismanagement, but also more involved vulnerabilities usually encountered in systems programming, such as race conditions. In addition to software vulnerabilities, smart contract audits must also investigate game theoretical security, avoiding misalignment of incentives which could allow an actor to gain an unfair economic advantage even though they’re technically following the contract logic.

Audits as part of the quality assurance process for software are not a new concept, but the budding blockchain landscape has relatively little experience analyzing the security of these types of distributed, highly adversarial systems. Security experts in the blockchain space often learn about the best security measures ad-hoc, either as a painful lesson after a hack, or from benevolent white-hat hackers who publish vulnerabilities they discover.

Because of the complexity of performing thorough smart contract audits, most audits are done by security specialists. These specialists manually analyze smart contracts for any bugs, issues, or other unexpected behavior. Given the depth of examination required for even a single project, these manual audits are often difficult to scale.

After years of manual audits and many devastating hacks, researchers began to discover common patterns to smart contract vulnerabilities. To quicken and secure the audit process, they began to create automated tools to assist manual auditors. These tools automate the detection of well-known vulnerabilities, giving human auditors the opportunity to focus on more complex security issues. Given the fledgling status of many of these tools, however, errors are common. Manual inspection is still necessary to help detect and eliminate false positive or negatives, and correctly interpret the findings of these automated tools.

As automated tools get smarter, manual audits may become a smaller part of the audit process. With automating audits, you might be able to detect most known vulnerabilities without much human inspection. In that case, all you need to do is run the tool, or ask someone else to run it for you, and read the automatically generated report. You can then show that report to the world to prove the security of your smart contract project.

However, one major problem arises: how do we ensure the tool is run correctly? How do we prove the authenticity and trustworthiness of the report itself? A malicious actor could decide to hide certain vulnerabilities with the hopes that no one else will investigate further. To increase the legitimacy of the actor’s claim that all vulnerabilities detected by the tool have been revealed, a trusted third party must run the tool and report the findings.

Unfortunately, third parties are subject to similar incentive misalignments — they may have an incentive to keep vulnerabilities secret or may collude with the audit requestor. Someone serving as a white knight, auditing contracts to confirm the findings of these third parties, will not get compensated for volunteering to keep everyone safe. Is there a way to ensure that these automated tools can be run securely while still compensating the auditors?

The Quantstamp Protocol

The issue of providing trustworthy, automated third party audits is the very problem the Quantstamp protocol seeks to solve. It leverages blockchain technology to create a platform for both audit requestors and auditors, providing trusted vulnerability detection for audit requestors while simultaneously giving proper compensation to auditors.

Requestors can submit audits to this decentralized platform and get a reliable audit report without having to know or trust the auditors directly. Auditors running automated software can earn compensation for performing reliable audits. This decentralized platform reduces the chances of censorship and collusion significantly by removing the need to trust in any individual entity.

FAQ

What is the benefit of using the Quantstamp protocol to audit my contract versus running automated smart contract analyzers myself?

Automated smart contract security analyzers are currently relatively difficult to setup and use correctly. Those with the ability can indeed perform their own automated smart contract audits using these tools or even install the Quantstamp protocol software and spin up their own private blockchain. However, you will be missing out on the crucial aspect of the protocol: the validation and consensus around the results of your audit. Sure, you can claim that your automated analysis of your own contract showed zero vulnerabilities, but to anyone else who you’re trying to convince, that would sound a tad… fishy. And even if you report that your own analysis has discovered some vulnerabilities, who’s to say you’ve shared them all?

While you can run your own automated audit or even spin up your own version of the protocol to prove to yourself your contracts are bug-free, others may not trust your findings. To prove to the world the security of your contract, it’s necessary for a third party to independently verify that your contract is free of vulnerabilities. Blockchain networks, in particular, earn additional trust by making it difficult to impossible for any two parties to collude.

Aside from the trust issues with running your own automated audit, there are technical barriers as well. You’ll need to understand how to install and operate these tools — no trivial feat. Also, you will need sufficient computational power to run the auditing tools or protocol.

Are there any benefits of an audit done through the Quantstamp protocol compared to a manual audit?

With a manual audit, a small team of auditors is looking for vulnerabilities by hand. What’s to say that these auditors aren’t bribed into keeping some vulnerabilities secret? Their reputation would be at stake, but perhaps they’re confident enough the vulnerability won’t be detected by anyone else.

While corruption within reputable auditing companies is highly unlikely — a threshold low enough for most project teams, exchanges, observers, and so on — there still does not exist a 100% guarantee of integrity.

On the other hand, an audit done through the Quantstamp protocol is a set of automated tools running on several different devices across the world. What we want the most out of an automated, decentralized audit is not just the results themselves since we could get the results by running the tools on a private network, but rather the results along with proof that the results are valid and not manipulated. (A side benefit to automated audits through the protocol is its reduced cost relative to manual audits.)

Won’t performing an audit on my smart contract through a public protocol expose my code and intellectual property to the world?

In order to use the Quantstamp protocol, you have to submit your code to a publicly facing network. Your smart contract code will, therefore, be exposed for analysis by third parties. This will provide a highly trustworthy audit of your code and will also allow others to verify that the audit was of your code in particular. However, it does expose your code to the world.

If privacy and IP protection is a concern, you may choose to do an audit on a private network or hire security experts to examine your code privately. However, you will miss out on the ability to prove to the world that your code has gone through an automated and decentralized smart contract audit.

Design Overview

The following section will go over the design of the Quantstamp protocol. While this is accessible to all readers, it will only be of interest to those looking to understand the lifecycle of an audit as it moves through the protocol.

Protocol Summary

The QSP protocol consists of three main participants: the Requestor; the Contract, also known as the Protocol; and the Audit Node.

A Requestor, looking to request an audit, sends the smart contract to audit and payment to the Contract. An Audit Node accepts that request and generates a vulnerability report using automated tools. The Audit Node sends back to the Contract the report and receives compensation. The Requestor now has access to the vulnerability report.

Here’s a closer look at what the process looks like.

Protocol Process

The audit goes through a few possible states, as demonstrated by the state machine diagram below. (A state machine is a way to describe the transformation of something through a process. In this case, it describes an audit. Each circle represents a possible state of the audit, and each arrow represents a function of the contract.)

Figure 1: QSP State Machine, by John Bender

Here’s a breakdown of the possible states of the audit:

  • None: The audit does not yet exist.
  • Queued: The audit has been requested by a Requestor.
  • Expired: The audit timeout period has been passed.
  • Assigned: An Audit Node has successfully picked up the audit.
  • Completed: the report was submitted in time with no error.
  • Refunded: The Requestor has received a refund for a service not performed in the alloted time.
  • Error: There was some issue with producing the report for the user.
  • Resolved: The owner of the contract has resolved the error manually.

Naturally, the desired flow is None ==> Queued ==> Assigned ==> Completed. This is the “happy path” in which the Requestor’s audit is performed and the Audit Node receives compensation with no issues. Issues regarding timeouts or processing errors are undesirable but handled by the protocol in the other paths.

In the next section, we’ll take a deep dive into each step of the protocol.

Protocol Breakdown

The first step to requesting an audit is for the Requestor to acquire QSP, the Quantstamp protocol token, to purchase services from the Protocol.

Once the Requestor has QSP, the next step is giving the Contract an allowance of tokens. This is not a transfer of tokens; instead, the Requestor is allowing the Contract to transfer some amount of tokens out of the Requestor’s account at its discretion. This is standard ERC20 token functionality built precisely for this purpose. There is no fear of tokens being transferred out of the account unexpectedly, as the Requestor can examine the contract’s functionality before giving the Contract an allowance. Giving an allowance is necessary because it is not presently possible to securely call a function with a token transfer() call. This particular contract will only draw the tokens from the account when an audit is being requested.

The third step is requesting an audit, providing the contract code to the Quantstamp website. From there, the website will compile the code, turn it into a link, and pass that link back to the Requestor. (A note: while directly interacting with the protocol contract is an option, the web interface provides a comfortable UI for interacting with the contract and will be highly preferable for most users.) The Requestor can then submit that link back to the contract via requestAudit(). From here, a requestID will be generated for the Requestor, and the audit request will be Queued. During this time, the QSP will be held in escrow by the contract, awaiting either a completed report or an error resolution before paying out.

From there, the Requestor will be waiting until the state of the audit changes. An Audit Node will ideally pick up the audit, at which point the state of the audit will become Assigned. If the audit is not picked up in time, it will then become Expired, allowing the Requestor to call refund() to reclaim their QSP.

If the audit is in the Assigned phase, the assigned Audit Node is expected to process the audit before the timeout. If the report is submitted in time, then the state of the audit becomes Completed. Else, if the report is submitted too late, then the audit will be Expired and redeemable for a refund.

Though it is not expected to be a common occurrence, there is the possibility of an Audit Node experiencing an error. In this scenario, the Audit Node will submit an Error status to the Contract, and Quantstamp will manually resolve the issue to decide whether the Requestor receives a refund or the Audit Node receives compensation for the audit.

Contract Architecture

The protocol is designed to meet the following goals:

  • Requestors submit audits with payment and receive reports in return, or a refund if a report is not submitted in time.
  • Auditors receive audits with the promise of payment in the scenario where a report is properly reported.
  • All this functionality is handled securely and efficiently.

To achieve this, the project logic and data is split between three contracts:

  • QuantstampAudit.sol: The logic portion of the project. This contract contains all logic and data relevant to the particular decisions of the protocol.
  • QuantstampAuditData.sol: The storage portion of the project. This contract contains all data meant to persist between all versions of the protocol, such as audit requests.
  • QuantstampAuditView.sol: The data interface portion of the project. This contract serves only to give access to information about the protocol’s data, primarily meant to be accessed by a web application through web3.

Splitting this logic between three contracts facilitates upgrades to the protocol. All information on a blockchain is immutable, meaning that it’s impossible for anyone, including the contract deployer, to modify a smart contract once on the blockchain. What should be done if an upgrade is needed?

This question brings up several more. If we create a new contract with our desired capabilities, what happens to the previous contract? And what happens to any information stored within the previous contract? Is it lost? Must the new contract start from scratch?

With separate contracts for logic and data in our protocol, we make it possible to smoothly upgrade both logic and data. When the protocol’s logic is upgraded in one contract, the data will persist in a separate, untouched contract. The previous contract will be manually shut down, and all web applications and users will be redirected to the address of the new contract. Via this process, the functionality of the protocol can be switched out without losing the previous data.

The next section is a breakdown of each individual contract, along with their functions. This section is primarily for anyone interested in the specifics of the contracts and is not intended for a non-technical audience.

QuantstampAudit

QuantstampAudit.sol in this first iteration stores a few different pieces of information in order to accomplish its goals:

  • The price buckets available to auditors
  • A queue of the requested audits within each price bucket
  • A pointer to the QuantstampAuditData contract

The contract uses linked lists to hold onto and navigate this data. Upon receiving an audit submission from a Requestor, the Contract looks for a price bucket at the requested audit’s price. If the price bucket does not exist, it is created. The audit is then stored within the bucket at the end of its queue.

When an Audit Node seeks to perform an audit, the contract goes to the highest price bucket available. If the price bucket is above the minimum price the Audit Node is willing to accept, the contract removes the oldest audit in that price bucket from the linked list (essentially popping from the front of the price bucket’s queue) and assigns it to the Audit Node. Else, no audit is assigned. If no audits are left in the bucket’s queue after assignment, then the bucket is also removed from the price bucket list.

If the audit is not picked up for some period of time, the Requestor can call for a refund. Again, the audit will be removed from the price bucket’s queue, and the price bucket will also be removed from the list of buckets if no more audits remain within it.

Here’s a breakdown of the public functions currently within the logic contract:

requestAudit()

Inputs:

  • URI pointing to the smart contract (string)
  • Price to be offered in the event of a completed report (uint256)

Output:

  • An ID pointing to the generated request of the Requestor (uint256)

Effects:

  • Deduct the price from the Requestor’s account.
  • Add the request to the audit data contract.

Conditions:

  • Contract is not paused

getNextAuditRequest():

Inputs:

  • None

Outputs:

  • None

Effects:

  • Check whether the oldest audit in the assigned audit list is expired. If so, it sets the state of that audit to Expired.
  • If no failures, add the request to the audit data contract, including the auditor and timestamp.
  • Increment the number of assigned requests for the auditor.

Conditions:

  • Can only be called by whitelisted auditing nodes
  • The node must not exceed its max cap for audits

submitReport()

Inputs

  • ID of the request (uint256)
  • State of the audit (enum)
  • Hash of the report (string)

Outputs

  • None

Effects:

  • Update the state of the audit
  • Transfer compensation to the auditing node as a reward

Conditions:

  • Will only succeed if called by the Auditor of the request

refund()

Inputs:

  • ID of the request (uint256)

Outputs:

  • True if the token was transferred back to the Requestor, false otherwise (bool)

Effects:

  • Remove the ID from the queue if already in the queue
  • Set the state in the audit data contract to Refunded
  • Send the price back to the Requestor

Conditions:

  • Cannot succeed if audit has already been Assigned and the timestamp is not yet past expiration

resolveErrorReport()

Inputs:

  • ID of the request (uint256)
  • Recipient of compensation, true if Requestor and false if Auditor (bool)

Outputs:

  • None

Effects:

  • Transfer the price to either the Requestor or the Auditor, depending on Quantstamp’s decision
  • Set the audit’s state to Resolved

Conditions:

  • Can only be called by the owner, Quantstamp

QuantstampAuditData

QuantstampAuditData.sol can only be modified by QuantstampAudit.sol and the owner of the project, Quantstamp. Its logical functionality is limited to avoid constraining future forms of the logic contract, primarily filled with getters and setters, functions to retrieve and assign variable values

Within the contract is a custom data structure, or struct, called Audit containing all the relevant information about an audit, including the requestor, price, state, and timestamp. The contract stores the following pieces of data across all versions of the protocol:

  • A mapping from data type uint256 to Audit to associate IDs with audits
  • The address of the QSP token contract
  • The timeout (in blocks) of audits
  • The maximum number of requests any audit node can have assigned to itself at any given point in time
  • The minimum price each audit node is willing to take for an audit
  • The list of whitelisted audit nodes
  • The most recent ID assigned to an audit

No matter the logic of the contracts in future iterations of the protocol, they will always use these pieces of information in some way.

Here’s a breakdown of the public functions:

addAuditRequest()

Inputs:

  • Audit requestor (address)
  • Contract URI (string)
  • Price (uint256)

Outputs:

  • ID of the request (uint256)

Effects:

  • Request is stored at the given ID within the contract
  • The request ID counter is incremented by 1

Conditions:

  • Can only be called by the QuantstampAudit contract or by the contract owner

getAuditContractUri(), getAuditRequestorAuditPrice(), getAuditState(), getAuditRequestTimestamp(), getAuditAuditor(), getAuditAssignTimestamp():

Inputs:

  • ID of request (uint256)

Outputs:

  • Relevant value

Effects:

  • None

Conditions:

  • None

setAuditAuditor(), setAuditAssignTimestamp(), setAuditReportHash(), setAuditReportTimestamp():

Inputs:

  • ID of request (uint256)
  • Relevant data

Outputs:

  • None

Effects:

  • Sets relevant value to data passed as argument

Conditions:

  • Can only be called by the QuantstampAudit contract or by the contract owner

setAuditTimeout()

Inputs:

  • Timeout in blocks (uint256)

Outputs:

  • None

Effects:

  • Assign the new value to the timeout in blocks variable

Conditions:

  • Can only be called by the contract owner, Quantstamp

setMaxAssignedRequests():

Inputs:

  • Max assignments for any node (uint256)

Outputs:

  • None

Effects:

  • Assign the new value to the max requests variable

Conditions:

  • Can only be called by the contract owner, Quantstamp

getMinAuditPrice()

Inputs:

  • The auditor address (address)

Outputs:

  • The auditor’s min price (uint256)

Effects:

  • None

Conditions:

  • None

setMinAuditPrice()

Inputs:

  • Auditor address (address)
  • The new min price (uint256)

Outputs:

  • None

Effects:

  • Set the auditor’s address min price to the new value

Conditions:

  • Can only be called by the QuantstampAudit contract or by the contract owner
  • isWhitelisted():

Inputs:

  • Node address (address)

Outputs:

  • True if whitelisted, false otherwise (bool)

Effects:

  • None

Conditions:

  • None

addNodeToWhitelist()

Inputs:

  • Address to whitelist (address)

Outputs:

  • True if successful, false otherwise (bool)

Effects:

  • Add the given address to the list of whitelisted nodes

Conditions:

  • Can only be called by the owner

removeNodeFromWhitelist()

Inputs:

  • Address to remove from whitelist (address)

Outputs:

  • True if successful, false otherwise (bool)

Effects:

  • Remove the address from the whitelist

Conditions:

  • Can only be called by the owner

getNextWhitelistedNode()

Inputs:

  • Whitelisted address (address)

Outputs:

  • Address next to input address in whitelist (address)

Effects:

  • None

Conditions:

  • The input address must be a whitelisted node, otherwise the call will fail

QuantstampAuditView

QuantstampAuditView.sol is meant primarily as a way to easily access data about the contract, therefore it stores little to no information itself. The only meaningful information it stores is the address of the QuantstampAudit contract so that any users or interfaces will know which contract to look to even in the event of an upgrade.

setQuantstampAudit()

Inputs:

  • The new address of the Quantstamp Audit (address)

Outputs:

  • None

Effects:

  • The audit contract is updated to the new address

Conditions:

  • Can only be called by the contract owner
  • The address cannot be null

getMinAuditPriceSum()

Inputs:

  • None

Outputs:

  • The sum of all the minimum prices (uint256)

Effects:

  • None

Conditions:

  • None

getMinAuditPriceCount():

Inputs:

  • None

Outputs:

  • The number of minimum audit prices — should equal the number of whitelisted nodes, as there will be exactly one minimum audit price for each node (uint256)

Effects:

  • None

Conditions:

  • None

getMinAuditPriceMax()

Inputs:

  • None

Outputs:

  • The highest minimum price that a whitelisted node is willing to do an audit for (uint256)

Effects:

  • None

Conditions:

  • None

getMinAuditPriceMin()

Inputs:

  • None

Outputs:

  • The lowest minimum price that a whitelisted node is willing to do an audit for (uint256)

Effects:

  • None

Conditions:

  • None

Author:

Nadir Akhtar, UC Berkeley, Computer Science

Nadir supports the Quantstamp auditing team and has also served as President and Head of Education at Blockchain at Berkeley, a student-run organization which does education, consulting and research into blockchain. He is currently completing his Bachelor’s in Computer Science at UC Berkeley.

© 2018 Quantstamp, Inc. All rights reserved. This content shall not be used, copied, modified, redistributed or otherwise disseminated except to the extent expressly permitted by Quantstamp.

Notice: This information may include non-functional, illustrative descriptions or concepts provided for demonstration purposes only and detailed implementation is subject to continuing development, changes, or cancellation in Quantstamp’s sole discretion. This content is provided for your personal, non-commercial use for informational purposes only and should not be relied upon for tax, financial, legal, regulatory, or other advice. Use of Quantstamp materials is subject to participation terms.

Like this content? Subscribe to our newsletter at Quantstamp.com or follow our Twitter, Facebook, or Telegram channel to stay up to date with Quantstamp.