How to Test Ethereum Smart Contracts for Access Restriction
Photo by Kelli McClintock on Unsplash

How to Test Ethereum Smart Contracts for Access Restriction

By alexroan | Blockchain Developer | 11 Jun 2020


Prerequisite: Ths article assumes an understanding of Solidity and Ethereum smart contracts.

Controlling access to smart contracts is vital to ensuring security. Common patterns, like OpenZeppelin’s Ownable and AccessControl contracts, enable developers to easily implement various levels of access to functions.

It’s great using these libraries because they’re audited and reviewed by a large community of developers, minimising the risk and maximising their utility. But how do you know you’re using them correctly?

We all make mistakes, even the best of us. What if you miss an onlyOwner declaration in one of your functions? Without tests, you could be deploying code with a huge vulnerability.

How can we ensure that functions are restricted correctly?


Testing ‘OnlyOwner’ Access With Solidity

Let’s assume we have a contract called ExampleContract that implements OpenZeppelin’s Ownable contract. It has many functions, some of which can be called by any address and some of which are only executable from the owner of the contract. This is where the onlyOwner modifier from Ownable is used:

function somethingSomethingTheOwnerCanDo() public onlyOwner {
...
}

If our contract has lots of functions, it’s very easy to miss one simple modifier. So we need to write tests for every successful path through the code and every unsuccessful path.


Truffle Suite

The Truffle Suite allows us to write tests in both JavaScript and Solidity. It’s easy to test the onlyOwner modifier exists using JavaScript tests because multiple accounts can be injected into the test runner. Send the message from the wrong account, and you’ll get a revert.

In Solidity tests, this is more difficult since all the tests are being run from a single address: the test contract. So how can we imitate this in Solidity tests?

The answer: By using a proxy contract

Using our somethingSomethingTheOwnerCanDo() function as the target of our test, we need a test that ensures it reverts if someone other than the owner calls it.

Let’s start by creating the ProxyContract, which makes the call that we expect to fail because it’s not the owner.

pragma solidity ^0.6.7;
import "./ExampleContract.sol";

contract ProxyContract {
function attemptNonOwnerCall(ExampleContract _contract) public {
_contract.somethingSomethingTheOwnerCanDo();
}
}

In our tests, we need to initialize this ProxyContract and call attemptNonOwnerCall with the instance of ExampleContract being tested as the parameter. This test needs to assert the call will fail. We can do that with try-catch. Here’s our test:

function testNonOwnerFails() public {
ExampleContract testTarget = new ExampleContract();
ProxyContract proxy = new ProxyContract();
string expectedReason = "Owner: caller is not the owner";
try proxy.attemptNonOwnerCall(testTarget) {
revert("It should fail");
} catch Error(string memory reason) {
Assert.equal(reason, expectedReason, "It should fail");
}
}

Let’s walk through this line by line:

  • testTarget is the contract we’re testing, with the restricted function
  • We create a new proxy contract
  • expectedReason is the message that Ownable returns when a call violates the onlyOwner modifier
  • We try to call the restricted function through our proxy, who isn’t the owner, so we expect to catch an error
  • If there’s no error, the revert command is run inside the try block, causing our test to fail
  • If there’s an error, the catch block is invoked, and we assert that it’s the error we were expecting: expectedReason

This only works because the owner of ExampleContract is our test contract (the contract which initialized ExampleContract), and the msg.sender of the call to somethingSomethingTheOwnerCanDo() is the ProxyContract, which has a different address.


Conclusion

It’s easy to assume that because we use well-tested libraries that our code is safe. While that’s a fair assumption, the only way to know you’re using them correctly is by thoroughly testing each function and ensuring your expectations of what they do line up with reality.


Further Reading

If you’re interested in Blockchain Development, I write tutorials, walkthroughs, hints, and tips on how to get started and build a portfolio. Check out this evolving list of Blockchain Development Resources.

If you enjoyed this post and want to learn more about Blockchain Development or the Blockchain Space in general, I highly recommend signing up to the Blockgeeks platform. They have courses on a wide range of topics in the industry, from Coding to Marketing to Trading. It has proven to be an invaluable tool for my development in the Blockchain space.


alexroan
alexroan

Blockchain Developer


Blockchain Developer
Blockchain Developer

Tutorials, walkthrough, hints and tips on Blockchain Development for all levels of expertise.

Send a $0.01 microtip in crypto to the author, and earn yourself as you read!

20% to author / 80% to me.
We pay the tips from our rewards pool.