Common pitfalls when handling records in Aleo: Validations, parity, and privacy | Smart contract audits from Veridise

Common pitfalls when handling records in Aleo: Validations, parity, and privacy

Jan 20

| 15 read

This is the second article in our series exploring records in Leo, the programming language of the Aleo Network.

Records in Leo blog series:
📚 Part I: How records work in Aleo? The foundation of private state in Leo
📚 Part II: Common Pitfalls handling records in Aleo – Validations, parity, and privacy

Common pitfalls when handling records in Aleo: Validations, parity, and privacy

In the last blog, we examined the Aleo record model, how records are defined and used, and some examples of how records are consumed in practice. Now let’s have a look at some common issues that arise when building on Aleo.

These typically cluster into three categories. We will look at these in detail to understand how some important validations can be easily overlooked, and what to be careful about when handling records on Aleo.

Weak binding or missing input invalidations when consuming records

Whenever a record is consumed, the code must verify that the private record actually matches the state the transition intends to consume, including the correct token identifier and any relevant program fields.

To see this in context, let us take an example of a token record from the token registry. Tokens on Aleo are generally implemented through the token registry. The token registry program serves as a single hub for issuing and managing tokens. Aleo does not yet support dynamic cross program calls. As a result a protocol cannot ship once and later integrate with arbitrary new token programs without recompilation and redeployment. The registry provides a stable interface that both tokens and applications can depend on. Tokens register with it and use it for minting and transfers. Applications can then integrate only with the registry rather than with each token, and new tokens can appear without forcing protocols to recompile/redeploy to support them.

When integrating a token from the token registry, or operating on any similar record, it is important to validate that the private record being consumed is bound to the state that the transition intends to consume. For example, each record produced by the token registry carries the following fields that include a token identifier.

A Token record defined within token_registry.aleo

Therefore transitions consuming a token record must validate that the token record being consumed corresponds to the expected token_id. If this validation is not performed, an attacker can pass as input an unrelated token record and indicate that the “expected” token record has been consumed.

The example below implements a cross-chain private transfer of tokens, where the tokens are first burnt on Aleo, and then minted on the corresponding chain. But, as can be seen the token_id within the record is not validated against the token id passed as input. Therefore, a user can burn a record for a worthless token while specifying the token_id of a valuable token such as ETH in the function parameters. The function will then proceed to publish a bridge message that authorizes the release of the valuable token on the destination chain, even though only the worthless token was actually burned on Aleo.

Another example of such a bug arises when information within the record fields is not fully validated before consuming the record. Let’s look at a record that implements a compliance token as an example. The following record implements a compliance policy where the minted tokens cannot be spent until a specified locking period passes.

A compliant token record that defines a locking period:

The locking period is recorded within the record, and the information recorded can be different for different compliance tokens that deal with different token policies. When such a record is to be consumed, it is important to ensure that the specified locking period has passed and the compliant tokens specified within the record are no longer locked. If such a validation is missed, then users can skirt compliance boundaries by spending tokens that are still within the lock-in period.

This is an example of how important validations can be missed. Since records maintain application state, they can hold a variety of fields that represent different states and conditions for the application. On consumption, it is imperative to validate the necessary fields as per the application specifications, to ensure discordant records cannot be consumed unknowingly. In summary, every field in the record should be appropriately validated before it is consumed.

Losing parity between private records and on-chain state

The second category where issues can be easily overlooked is parity with public state. Whenever a record is spent, the relevant state should be updated for the corresponding on-chain state. Its important to remember that records are single use, so once a record is spent it cannot be reused, which we cover in our earlier post on the record lifecycle. These state updates are performed within the finalize() portion of a function.

Care should be taken when private record is consumed and converted into a public state. During such a state transition, all relevant state from the private record should be moved to some public state since the private record will be consumed, and any residual if it exists must be dealt with by producing another record.

We had a look at the example below when understanding how a private record can be consumed and converted into public tokens. But, in this context, finalize_transfer_priv_to_pub() does not update the public user balance. And therefore, although the private record is consumed, the users tokens are now effectively lost.

transfer_private_to_public consumes a private record, but does not update public balance

The finalize function must apply the required updates in a complete and consistent way. If consistency in updating state is not maintained then it risks a loss of information and holes in bookkeeping.

Leaking private information

The Aleo record model is built around supporting privacy. But, in certain cases, developers may intend to expose private state and make it public. Therefore, Aleo allows developers to specify whether certain inputs provided to the function are public or private. Keeping this in mind, there will be certain fields, which if exposed would compromise the privacy of a user. So, it is important to ensure that no private field is unintentionally exposed or it will end up leaking private information.

The following program implements a sealed auction, where each user submits a sealed bid, and the bids are revealed at the end of the auction with the item being sold to the highest bidder. As can be inferred, privacy is of the utmost importance in such an application since having knowledge of the sealed bids provides an unfair advantage to any user.

An example Sealed auction program in Aleo

Unfortunately, the function bid_auction leaks sensitive bid amounts by declaring bid_amount as public. Moreover, since the bid amount is also forwarded to finalize_bid_auction(), it is exposed on-chain, and the privacy of the application is therefore compromised.

These are a few simple examples where privacy features are explicitly compromised by leaking sensitive information. Sometimes information leakage can be much more subtle, therefore we should always be careful about leaking any kind of sensitive information when developing an application with privacy features.

Conclusion

In conclusion, the issues covered here can serve as a simple outline for avoiding problematic patterns in Aleo that arise from mishandling records.

Later, we’ll dig deeper into some more of Aleo’s feature set, with particular attention to issues that can arise in practice.

Records in Leo blog series:
📚 Part I: How records work in Aleo? The foundation of private state in Leo
📚 Part II: Common Pitfalls handling records in Aleo – Validations, parity, and privacy

More by Veridise

Subscribe to our blog

Be the first to get the latest from Veridise — including educational articles on ZK and smart contracts, audit case studies, and updates on our tool development. Delivered twice a month.

smart contract audit cloud

Contact us for a security audit quote

Secure an earlier audit slot by reaching out early.