Parse, don't validate — correctness in smart contract development
Tags: rust, security, programming
Published: 2022-12-05 09:40:53

Just couple of days ago, there was a discussion on the [Rust in Blockchain](https://rib.rs/) group. It was about another smart contract exploited by a hacker. Some missing role checks allowed arbitrary token minting.

I've been a software engineer for multiple years, and I know that it's easy to forget about an important line in the code.

However, there are techniques which make it harder to forget! That's what, dear reader, I'd like you share with you about 🙃

## Ensuring input correctness

Accepting input from outside world requires checking if this input is actually what we need it to be. There is one common way of achieving this. It's called _validation_.

### Validation

It is about taking the input and checking if it _does include_ expected values and _does not include_ harmful values.

If the check is successful, the input is considered valid by the internal part of our system. Otherwise, the input is rejected as invalid and is not further processed.

However, validation does not require using new types to capture information about the input correctness. 

Have a glance at [this Rust playground example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0b85758a7eb9ef3a91f63b01749c1d8b).

It is covered by unit tests, which definitely help, but the same `Addr` type is shared by both, the valid and invalid address. That's where the real danger is! 🙊

What can be done to make a mistake even harder to make? We can use... types!

### Parse, don't validate

I'll start by linking to [an awesome article by Alexis King](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) which   includes all details about this technique.

In short, we want to parse an input into a known set of internal types. These internal types can only be constructed via functions that include validation.

The main difference is creating a wrapping type for a given valid value. Now, the valid admin address can be expressed by using the `AdminAddr`. All other addresses are still expressed with our basic `Addr` type.

Take a look at [the playground here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=108a50a21930cab90043d22ee51f8177) — it's a modification of the previous code example.

## Security

According to the [NIST Cybersecurity Framework](https://www.cybersaint.io/blog/nist-cybersecurity-framework-core-explained), there are five main areas of focus: Identify, Protect, Detect, Respond, and Recover.

A simple technique like using types to ensure software correctness can be helpful with the following security areas:
- Identify
  - assessing risk is easier if values can carry information about their own correctness
- Protect
  - it's harder to forget using a correct type in an authenticated method [[1]](#auth-ref-link)
  - the compiler helps with catching the mistakes 🚀

## Summary

All in all, mistakes are unavoidable. They are part of the risk. Keep in mind:
> The risk can't be avoided — it has to be managed.

Strong types are one of the ways to manage risk in software development. Smart contracts included.

---

<ol>
<li><a id="auth-ref-link" href="https://www.nicholashairs.com/posts/musings-on-auth-definitions/">Musings on Auth: Definitions</a> by Nicholas Hairs</li></ol>