OpenZeppelin
Skip to content

Particle Network BTC Smart Account Audit

Table of Contents

Summary

Type
Account Abstraction (BTC)
Timeline
From 2024-01-31
To 2024-02-06
Languages
Solidity
Total Issues
7 (0 resolved, 1 partially resolved)
Critical Severity Issues
0 (0 resolved)
High Severity Issues
1 (0 resolved, 1 partially resolved)
Medium Severity Issues
0 (0 resolved)
Low Severity Issues
3 (0 resolved)
Notes & Additional Information
3 (0 resolved)

Scope

We audited the Particle-Network/btc-smart-account repository at commit 61464ad.

In scope were the following files:

 contracts
├── CustomSlotInitializable.sol
├── LightAccount.sol
├── LightAccountFactory.sol
└── SHA256L.sol

System Overview

This project aims to provide a system for generating robust, ERC-4337-compliant smart contract accounts that will operate on Bitcoin EVM-compatible L2 networks. These are L2 networks that utilize the EVM for execution, use BTC as the native token, and settle on the Bitcoin network. ERC-4337 creates a method for achieving account abstraction, which in this case allows users to create transactions on behalf of an owned smart contract rather than from their own Externally Owned Account (EOA). To this end, the Particle network team has created:

  • LightAccount: An individually-upgradeable proxy contract that will validate user operations and execute calls.
  • CustomSlotInitializable: A contract that is inherited by LightAccount and specifies how LightAccount stores its proxy initialization data.
  • LightAccountFactory: A contract that will generate proxied LightAccount contracts as needed.
  • SHA256L: A library that implements SHA256 and aids BTC hashing and signature-related tasks.

All of this aims to allow Bitcoin EVM-compatible L2 users to interact with contracts using their smart wallets without needing to store new EVM keys.

Security Model and Trust Assumptions

Account Abstraction Functionality

ERC-4337 defines the architecture, participants, contracts, and restrictions associated with the account abstraction ecosystem. In this audit, we assumed this infrastructure is deployed and functioning correctly.

The Particle Network's LightAccount and LightAccountFactory contracts are based on the Ethereum Foundation's samples, which are designed to be compatible with the rest of the architecture with some configuration options.

In particular, the LightAccount contract makes the following choices:

  • The owner address can either be an EOA or a smart contract. In either case, it must endorse the user operation.
  • There are no restrictions on nonces other than the mandatory ones. This means that operations must be executed sequentially within a category (or "key", using the 4337 terminology), but can be executed in any order across categories. The category associated with each user operation is chosen by the owner.

External Ownership

Interestingly, EOAs sign messages that use the Bitcoin encoding envelope. This is because they function as proxies for Bitcoin addresses. Since Bitcoin and Ethereum use the same elliptic curve and signature scheme, the same ECDSA public key can be used to derive both Bitcoin and Ethereum addresses. After accounting for differences in encoding formats, this also implies that a Bitcoin signature is also a valid signature for the associated Ethereum address since they both correspond to the same private key. By setting this Ethereum address as the owner, the LightAccount can use existing Ethereum signature verification infrastructure to validate signatures that were created for the Bitcoin ecosystem.

Contract Ownership

Alternatively, the owner can be a contract that uses the ERC-1271 mechanism to authorize user operations. However, it must also obey the ERC-7562 opcode restrictions to be compliant with the ERC-4337 requirements. In this case, the user operation digest is not wrapped in a Bitcoin encoding envelope.

Contract Authentication Functionality

The LightAccount also supports ERC-1271 signature validation. This is independent of its account abstraction functionality and instead is a mechanism for this contract to endorse a generic message. It uses the same authorization mechanisms but the Bitcoin encoding envelope is not included whether or not the owner address is an EOA.

Privileged Roles

The owner address has complete control of the LightAccount contract. Either directly or using the account abstraction mechanism, the owner can:

  • Execute any message (or a batch of messages) from the account, including sending ETH.
  • Withdraw any ETH deposited with the EntryPoint on behalf of the account.
  • Transfer ownership to any non-zero address other than the account itself.
  • Upgrade the implementation of the account arbitrarily.
 

High Severity

Incorrect SHA256 Implementation

We identified the following invalid behaviors in the preprocess function of the SHA256L implementation.

Incorrect Implementation

The preprocessing step involves padding the input to ensure that, along with the final 8-byte length field, it occupies an integer number of 64-byte blocks. However, when an extra block is needed, only 56 extra zero bytes (rather than 64) are added, resulting in the incorrect hash output. This will occur whenever the length of the input (in bytes) is higher than 55 modulo 64. In the current codebase, neither use case triggers this error.

Unsafe Memory Assumptions

The function implicitly assumes the input was the last value allocated, but this is not necessarily true. As a result, the input padding will overwrite any memory that is saved after the input. The return values are also placed [number padding bytes] after the current free memory pointer, which is an arbitrary position if the free memory pointer did not start at the padding location.

Imprecise Return Values

The output array has an entry for every 4-byte value in the input, but its length is set to 32 regardless of the input size. Its first record is set to 16 * rounds, and then immediately overwritten by the loop. The function also allocates an extra word for the returned array.

Inefficient Algorithm

The k value is the size of the whole input including the padding, but it is also used as the amount of zeroes to add. The function only needs to pad to the nearest 64-byte block but it could end up padding for several blocks.

Consider updating the algorithm accordingly. In addition, consider using the Solidity sha256 function for networks that support it.

Update: Partially resolved in pull request #1. The Particle team stated:

Indeed, according to the current implementation, if the user enters more than 55 bytes, there will be an erroneous result. However, we do not believe that it will happen in our scenario. As such, we have only fixed the following high-severity sub-issues:

  1. Incorrect Implementation
  2. Imprecise Return Values

However, we are not addressing following sub-issues for now:
1. Unsafe Memory Assumptions
2. Inefficient Algorithm

These are primarily optimization issues so we are not in a hurry to solve them at the moment. May be we will fix them someday in the future or switch to the native SHA256 implementation by the layer 2.

Low Severity

Misleading Comments

We identified the following misleading comments:

Consider updating these comments accordingly.

Update: Acknowledged, will resolve. The Particle team stated:

This issue is not urgent and will be resolved in the future.

Incomplete Docstrings

Throughout the codebase, there are multiple components that do not have docstrings:

Moreover, the Initialized event does not document the version parameter, while the _transferOwnership function and the hash function do not document their parameters and return values.

Consider thoroughly documenting all events and functions (and their parameters) that are part of any contract's public API. Functions implementing sensitive functionality, even if not public, should be clearly documented as well. When writing docstrings, consider following the Ethereum Natural Specification Format (NatSpec).

Update: Acknowledged, will resolve. The Particle team stated:

This issue is not urgent and will be resolved in the future.

Lack of Tests

Consider introducing a testing suite, particularly for a complex system like a smart account. It should cover functional tests of the main use cases as well as security tests for operations that should be rejected.

Update: Acknowledged, will resolve. The Particle team stated:

This issue is not urgent and will be resolved in the future.

Notes & Additional Information

Lack of Security Contact

Providing a specific security contact (such as an email or ENS name) within a smart contract significantly simplifies the process for individuals to communicate if they identify a vulnerability in the code. This practice is quite beneficial as it permits the code owners to dictate the communication channel for vulnerability disclosure, eliminating the risk of miscommunication or failure to report due to a lack of knowledge on how to do so. Furthermore, if the contract incorporates third-party libraries and a bug surfaces in those, it becomes easier for the maintainers of those libraries to make contact with the appropriate person about the problem and provide mitigation instructions.

Throughout the codebase, there are contracts that do not have a security contact:

Consider adding a NatSpec comment containing a security contact above the contract definitions. Using the @custom:security-contact convention is recommended as it has been adopted by the OpenZeppelin Wizard and the ethereum-lists.

Update: Acknowledged, will resolve. The Particle team stated:

This issue is not urgent and will be resolved in the future.

Code Simplifications

The isValidSignature function of the LightAccount contract could use the getMessageHash function instead of reimplementing the same logic. Moreover, it appears to use an unnecessary layer of indirection. The encodeMessageData function function creates an EIP-712 encoding of an arbitrary message wrapped in a LightAccountMessage struct, but the message is always a bytes32 digest.

For simplicity, consider defining the LightAccountMessage struct as directly containing the bytes32 digest.

Update: Acknowledged, will resolve. The Particle team stated:

This issue is not urgent and will be resolved in the future.

Missing Namespaced Storage NatSpec Tag

ERC-7201 specifies that namespaced storage structs be annotated with the NatSpec tag @custom:storage-location <FORMULA_ID>:<NAMESPACE_ID>, where <FORMULA_ID> identifies a formula used to compute the storage location where the namespace is rooted, based on the namespace id. There are multiple namespaced storage structs that do not have these tags:

However, it is clear from the code that they follow ERC-7201's formula for computing storage locations.

For improved code clarity and for tooling integration, consider adding ERC-7201 annotations to these storage structs.

Update: Acknowledged, will resolve. The Particle team stated:

This issue is not urgent and will be resolved in the future.

 
 

Conclusion

Particle Network has built contracts to facilitate account abstraction on Bitcoin EVM-compatible L2 networks. These contracts rely heavily on the ERC-4337 contracts developed by the Ethereum foundation and with BTC wallets in mind. We identified some issues in the implementation of the SHA256 algorithm. Nevertheless, overall, we found the codebase to be well-reasoned, well-documented, and understandable. The Particle Network team was available and responsive throughout the audit period to answer any questions we had.