- 1 - Table of Contents
- 2 - Introduction
- 2.1 - Audit Goals
- 2.1.1 - Sound Architecture
- 2.1.2 - Code Correctness and Quality
- 2.1.3 - Security
- 2.2 - Source Code
- 2.2.1 - Initial Review
- 2.2.2 - File List
- 2.3 - Quick Left and Piper Merriam
- 2.4 - Terminology
- 2.1 - Audit Goals
- 3 - Main Findings
- 3.1 - Primary Issues
- 3.2 - Test Coverage
- 3.3 - General Thoughts
- 4 - Detailed Findings
- 4.1 - Source File:
contracts/Token.sol
- 4.1.1 -
Token.approve
andBadge.approve
only allow for allowance up to current account balance - 4.1.2 -
Token.allowance
andBadge.allowance
do not return the actual approved amount - 4.1.3 - The authority to mint both tokens is handled by the same mechanism
- 4.1.4 - The Events from the
Token
andBadge
contracts do not conform to ERC20 - 4.1.5 - The
Mint
event from theToken
andBadge
contracts do not follow the pattern established in ERC20 - 4.1.6 - The
Token.locked
andBadge.locked
could removed - 4.1.7 - The
Token
andBadge
contracts do not implement a mechanism for transferring ownership
- 4.1.1 -
- 4.2 - Contract:
contracts/Token.sol:Badge
- 4.3 - Contract:
contracts/Token.sol:Token
- 4.1 - Source File:
- 5 - Resolution
- 5.1 - Terminology
- 5.1.1 - Status: Resolved
- 5.1.2 - Status: Unchanged
- 5.1.3 - Status: Needs Work
- 5.2 - Primary Findings
- 5.3 - Test Coverage
- 5.4 - Minor Issues
- 5.4.1 - The
Mint
event does not follow the ERC20 pattern - 5.4.2 -
Token.locked
andBadge.locked
mechanism could be removed - 5.4.3 -
Badge
contract has unused constructor argument - 5.4.4 -
Badge
contract has unused mappingseller
- 5.4.5 - Unused
noEther
modifier - 5.4.6 - No way to query whether an address is a
seller
- 5.4.7 - The
dao
andowner
variables are not public - 5.4.8 - The
config
variable is not used outside of the constructor
- 5.4.1 - The
- 5.5 - Medium Issues
- 5.6 - Major Issues
- 5.6.1 - ERC20 Conformant Events
- 5.6.2 - No mechanism to transfer ownership
- 5.6.3 - No mechanism for unregistering sellers
- 5.7 - Critical Issues
- 5.1 - Terminology
From April 5th through April 14th of 2016, Piper Merriam via Quick Left conducted an audit of the token and badge contracts that will be used with the Digix DAO.
This audit evaluates the architecture of these two contracts, focusing on whether they conform to established smart contract best practices. Additionally, they will be evaluated on their ability to provide a future proof platform for managing these token based assets.
This audit includes a full review of the contract source code. The primary areas of focus include:
- Correctness (does it do was it is supposed to do)
- Easy to read and/or understand
- Sections of code with high complexity.
- Antipatterns
- Adequate test coverage.
This audit focused on identifying security related issues.
The code being audited is from the digixdao-contracts
repository under the
DigixGlobal github account.
https://github.com/DigixGlobal/digixdao-contracts
The initial review of this source code was performed at commit sha
089ec81b319b423d13575b9cc7fb82b8c1139d17
.
The following source files were included in the audit.
contracts/Interfaces.sol
contracts/Token.sol
The SHA256 of these files at the time of the audit was as follows:
$ shasum -a 256 contracts/Interfaces.sol contracts/Token.sol
12ef2f5bf41d68551b76a1db01ac498c2614649da8a6ddc8662aa13d471ebae8 contracts/Interfaces.sol
5dfda3c5d009073ec1d25c7fdbb287c0a21990223826006effec2c0d20854f97 contracts/Token.sol
Quick Left is a web consultancy located in Boulder Colorado, USA.
I, Piper Merriam, am a full time Senior Engineer employed by Quick Left. I have been developing software as my full time profession since 2011.
Evidence of my expertise in this area can be found in the open source work available on my github profile (https://github.com/pipermerriam).
The following terminology is used within this audit to categorize findings.
Measurement of the testing code coverage. This measurement was done via inspection of the code.
No tests.
The tests do not cover some set of non-trivial functionality.
The tests cover all major functionality.
The tests cover all code paths.
Measurement of magnitude of an issue..
Minor issues are generally subjective in nature, or potentially deal with topics like "best practices" or "readability". Minor issues in general will not indicate an actual problem or bug in code.
The maintainers should use their own judgement as to whether addressing these issues improves the codebase.
Medium issues are generally objective in nature but do not represent actual bugs or security problems.
These issues should be addressed unless there is a clear reason not to.
Major issues will be things like bugs or security vulnerabilities. These issues may not be directly exploitable, or may require a certain condition to arise in order to be exploited.
Left unaddressed these issues are highly likely to cause problems with the operation of the contract or lead to a situation which allows the system to be exploited in some way.
Critical issues are directly exploitable bugs or security vulnerabilities.
Left unaddressed these issues are highly likely or guaranteed to cause major problems or potentially a full failure in the operations of the contract.
Some issues may be entirely subjective, as they are the opinion of the reviewer and the choice between the current code and the suggested change does not have a clear objective improvement (eg. naming conventions, tabs vs spaces).
In these cases, they will be marked as follows.
- Severity: minor
- Subjective Issue
- Severity: medium
The Token
contract is written such that it is the owner of the Badge
contract.
Recommendation: restructure these contracts such that both the Badge
and
Token
contracts are directly owned by the DAO.
- Severity: major
There is no overflow protection or checking implemented in any of the addition operations. Combined with the ability for these contracts to mint new tokens, this presents an attack surface that would allow an attacker with access to the minting API to zero out the account balances of other users.
- Bob has an account balance of
100
tokens. - Attacker mints
2**256 - 100
tokens into Bob's account. - Bob's account balance will overflow resulting in a zero balance.
- The
totalSupply
value will likely overflow as well.
Recommendation: implement overflow protection for all addition operations.
- Severity: major
The architecture of these two contracts has certain properties that are entirely immutable once initialized.
- The
owner
address - The
dao
address
Not providing an API for changing these values may be a desired property. It will also restrict the ability for the current contracts to be carried forward as the DAO evolves.
Recommendation: implement a mechanism for updating these values.
- Coverage: untested
- Severity: major
The Token
and Badge
contracts do not have any test coverage.
Recommendation: add tests. The benefits of testing in software are well documented.
- Severity: minor
- Subjective Issue
It has improved the readability of contract source code to explicitly mark
functions as public
. While this is the default, it helps those reading the
contract source code to more easily differentiate between what functions are
exposed publicly and which ones are not.
- Severity: medium
- Subjective Issue
Each subtraction operation implements its own underflow protection.
It may be prudent to write this code once and reuse it in all places where this protection is needed. The following code could be used as either an abstract base contract or a library to accomplish this.
function safeToAdd(uint a, uint b) returns (bool) {
return (a + b >= a);
}
function addSafely(uint a, uint b) returns (uint) {
if (!safeToAdd(a, b)) throw;
return a + b;
}
function safeToSubtract(uint a, uint b) returns (bool) {
return (b <= a);
}
function subtractSafely(uint a, uint b) returns (uint) {
if (!safeToSubtract(a, b)) throw;
return a - b;
}
- Severity: medium
The approve
function of both the Token
and Badge
contracts only allow
approvals up to the current account balance. This restriction may not be
desired as it limits how users can use the approval/allowance API.
Consider a user who plans to have any tokens they acquire be immediately sold by a 3rd party. Without the ability to approve above the current account balance, each time new tokens are acquired, the user would need to re-approve the 3rd party in order for them to have access to the new tokens.
Recommendation: remove this logic in favor of allowing allowances to be set to any value.
- Severity: medium
The allowance
function of both the Token
and Badge
contracts does not
return the actual approval value. Instead, it computes the value that could be
currently transferred based on the current account balance and returns this
amount. This logic may not be desirable as it limits the way that users can
use the approval/allowance API.
See the Token.approve
and Token.allowance
issue for an example use case.
The current functionality can still be achieved by querying both the
allowance
and balanceOf
values instead of implementing the logic within the
allowance
method.
Recommendation: remove this logic in favor of directly returning the allowance value.
- Severity: medium
Both Token
and Badge
implement privileged APIs for minting new tokens.
The mechanism that controls authorization for minting is contained in the
Token
contract. Any address that is set as a seller may mint tokens for
either contract.
Coupling these two mechanisms into a single access control mechanism may end up problematic. By having the authorization for both contracts controlled by a single mechanism it is not possible to grant authority to mint only badges or only tokens.
Recommendation: decouple this into two separate authorization mechanisms.
** Severity major
ERC20 specifies the following two events.
event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
The Token
and Badge
implement the following versions of these events.
event Transfer(address indexed _from, address indexed _to, uint256 indexed _value)
event Approval(address indexed _owner, address indexed _spender, uint256 indexed _value)
In the Token
and Badge
implementations the _value
argument is set as
indexed
. This may cause compatibility issues with 3rd party integrations
which interact with contracts according to the official ERC20 specification.
Recommendation: remove the indexed
attribute to be conformant with ERC20.
4.1.5 The Mint
event from the Token
and Badge
contracts do not follow the pattern established in ERC20
** Severity minor
The Mint
event is declared as:
event Mint(address indexed _recipient, uint256 indexed _amount)
While ERC20 does not specify a Mint
event, the expected format can be
extrapolated from the Transfer
and Approve
events which do not have the
_amount
value set as indexed
.
Recommendation: remove the indexed
attribute from these events.
- Severity: minor
The locked
variable on both the Token
and Badge
contracts is only used in
a single code path to ensure that once the address of the DAO has been set,
it cannot be changed to another value.
Replacing the locked
mechanism with if (dao == 0x0)
would reduce the
overall complexity of these contracts.
Recommendation: remove this variable in favor of if (dao == 0x0)
until a separate
code path requires a generic locking mechanism to be put into place.
- Severity: major
The Token
and Badge
contracts do not implement any mechanism for
transferring ownership. This eliminates any upgrade path in the event that the
owner needs to be updates.
Consider the situation where a bug is found in the DAO code. Ideally a new DAO contract could be deployed with this issue fixed, and the ownership of the token contracts could be transferred to the new DAO. This is not possible with the current code.
Recommendation: add an API for transferring ownership.
- Severity: minor
The constructor for this contract takes an argument _config
of type address
which is not used or stored.
Recommendation: remove this argument.
This contract does not make use of the sellers
mapping.
Recommendation: remove this mapping.
- Severity: major
The Token
contract presents the function registerSeller
for adding
addresses to the sellers
mapping. Any address present in this mapping is
granted permission to mint new tokens.
The Token
contract does not present any API for unregistering addresses to
remove them from this mapping. In the event that an address ends up in this
mapping that is controlled by an attacker, the integrity of the the tokens
controlled by this contract could be compromised.
Recommendation: add a mechanism for removing addresses from this mapping.
- Severity: minor
The modifier noEther
is declared but not used anywhere in the contract.
Recommendation: remove this modifier.
- Severity: minor
The sellers
mapping is not public
and the contract does not implement any
getter function for querying whether an address is set as a seller.
Recommendation: make the sellers
mapping public
or implementing an API for
querying whether an address is a seller.
- Severity: minor
The dao
and owner
variables are not marked as public
. This makes
auditing these contracts more difficult.
Recommendation: make these variables public
.
- Severity: minor
- Subjective Issue
The config
variable is used to query certain values in the constructor
function, but is not used in any of the the other contract logic.
Recommendation: remove this variable and/or modifying the constructor to
explicitly take constructor arguments for the values queried from the config
contract. Doing so simplifies this contract at no cost to functionality.
This audit was performed on the codebase at commit sha
936cf20a6a06031f52c27b0a89b23f880b09b43e
.
Issues that are noted with the resolved status have been adequately addressed.
Issues that are noted with the unchanged status have not been addressed.
Something was done in response to this issue but it does not fully address the problem.
- Status: unchanged
- Issue 3.1.1
Per a conversation with the Digix developers this is an intentional design decision.
- Status: resolved
- Issue 3.1.2
This issue has been resolved within the Token
contract's mint
function.
This function now uses a addSafely
function to explicitly that the addition
operation cannot overflow.
This was the only location in the code where this attack vector was
found to be exploitable. This effectively restricts the totalSupply
from
ever exceeding 2**256
which in turn should make it impossible for any other
addition operations to overflow.
There are other locations in the code where an additional level of safety could
be attained by using these new addSafely
and subtractSafely
functions but
given the simplicity of the code it is suspect as to whether this safety is
worth the tradeoff in code complexity.
- Status: resolved
- Issue 3.1.3
- Issue 4.1.7
Both Token
and Badge
contracts now implement a setOwner
function which
allows for transferring ownership of these contracts, allowing an upgrade path
in the event this is necessary.
- Status: unchanged
- Issue 3.2
The codebase remains without tests. Per discussion with the Digix developers they will be executing manual tests against these contracts.
- Status: unchanged
- Issue 4.1.5
The _amount
argument for this event is still set to be indexed
in both the
Token
and Badge
contract.
- Status: unchanged
- Issue 4.1.6
This mechanism is still in place. This is assumed to be an intentional design decision.
- Status: unchanged
- Issue 4.2.1
The constructor still receives the _config
argument and does not use it.
- Status: unchanged
- Issue 4.2.2
This variable is still in place on the Badge
contract and is not used
anywhere in the contract source code.
- Status: resolved
- Issue 4.3.2
This modifier has been removed.
- Status: unchanged
- Issue 4.3.3
No mechanism for querying whether an address is in the seller
mapping has
been implemented.
- Status: resolved
- Issue 4.3.4
These variables are now marked as public
.
- Status: unchanged
- Issue 4.3.5
The use of this value
remains contained to the constructor.
- Status: unchanged
- Issue 4.1.3
This is unchanged, and assumed to be an intentional design decision.
- Status: resolved
- Issue 4.1.1
- Issue 4.1.2
These functions have been modified to remove the account balance restrictions.
- Status: resolved
- Issue 4.1.4
The ERC20 events in both the Token
and Badge
contracts now conform to the
ERC20 specification.
- Status: resolved
- Issue 4.1.7
See notes in Primary Findings section.
- Status: resolved
- Issue 4.3.1
The Token
contract now implements a unregisterSeller
function which can be
used to remove addresses from the seller
mapping.
No critical issues were found in this audit.