In this article, we will delve into the security pitfalls of Substrate blockchains and Polkadot parachains, exploring common vulnerabilities and ways to mitigate them. Whether you are a developer building on these platforms or a user looking to understand the risks, this article provides a valuable resource for staying informed about the security of Substrate and Polkadot.
Before we dive into how to keep a Substrate chain secure, we will quickly go over some technical aspects of Polkadot and what the difference to Substrate is.
Polkadot is a blockchain platform which is designed to support a wide range of decentralized applications and use cases. It uses a unique design called "parachains," which are essentially independent blockchains that are connected to the Polkadot relay chain and can rely on the security provided by the relay chain. Parachains can also communicate amongst each other using the XCM protocol which also allows tokens from one parachain to exist on and be transferred to other parachains. Polkadot parachains are usually built with Substrate.
Substrate is an open-source framework for building blockchains. It allows developers to easily create custom blockchains by providing a modular, flexible architecture and a number of pre-built modules. One of the key features of Substrate is its support for "pallets," which are modular pieces of code that can be easily added or removed from a Substrate-based blockchain. Pallets allow developers to add functionality to their blockchain, such as different consensus mechanisms, smart contract execution environments, and of course custom functionality. A Substrate blockchain can easily be turned into a parachain on Polkadot, by using the relevant finality pallet. Polkadot parachain slots are limited and are leased to the projects that win in the crowdfunded auctions.
One of the main challenges of Substrate development is ensuring the security and reliability of the custom pallets that are added to the blockchain. Pallets are essentially standalone pieces of code that interact with the core Substrate framework, and any vulnerabilities or bugs in these pallets can have serious consequences for the entire blockchain. For example, a poorly designed pallet could allow attackers to gain unauthorized access to sensitive data, perform arbitrary state transitions, or even disrupt the entire blockchain. We will now explore some of the key considerations for Substrate development and the potential security pitfalls that developers should be aware of when working with Substrate pallets.
Special care must be given to user input values. Always make sure that the values are sensible. A user may, for instance, call an extrinsic function with 0 amounts or other empty inputs. Therefore, when declaring a function, always consider every possible value that can be submitted and ask yourself which of those make no sense and exclude them early on. Let's say you have a function that takes a token amount that the user sets to 0 - even if all the calculations end up okay, maybe the program stores some data which is at best useless and at worst, if the user keeps sending transactions, fills up the node's storage and makes it infeasible to keep operating a node.
This brings us to the next point: every storage operation must have some cost to the user and that cost must be appropriate for the amount of data stored. Otherwise, as just stated, it might become impossible to keep running the blockchain node. This also applies to the configuration of pallets where for instance, the ExistentialDeposit must be large enough so users can not create billions of accounts gobbling up the storage space.
Related to that are the weights of the extrinsic functions. They can be static but more often than not, you'd want to benchmark and calculate them. Especially if your functions iterates over dynamic arrays causing many storage reads and writes, using a static weight would be inappropriate and could potentially cause issues, as Substrate also uses this information to decide which transactions can be included in the next block within the allotted time.
Unlike in smart contracts, where a panic can be a legit way to stop the execution, you do want to make sure that your pallet does not cause panics in the runtime. This applies to unsafe code, unwraps and any other operation that may cause a panic. Such as a user inputting 2 vectors of different lengths in this example:
fn send(amounts: Vec<u32>, targets: Vec<u32>) {
for (amount, target) in amounts.iter().zip_eq(targets.iter()) {
doSomething(target, amount);
}
}
Dealing with integers is commonplace in pallet code and any calculation, especially involving user input, can cause over- or underflows. These could cause potential havoc and it is therefore best to only rely on checked math by using the appropriate functions that rust provides. For instance: checked_add(), wrapping_sub() and so on.
While working with XCM and tokens from other parachains, keep in mind that those can have various amounts of decimal places. Acala's aUSD for example uses 12 decimals and therefore 1$ is equal to 1,000,000,000,000 tokens - and when dealing with large numbers of decimals, you'll need to choose integer types of an appropriate size to operate on these.
In conclusion, Substrate development and the use of Substrate pallets offer a powerful and flexible way to build custom blockchains and decentralized applications. However, it is important for developers to carefully consider the security risks and challenges that are inherent in this process. By following best practices such as thorough testing, code review, and careful upgrade planning, developers can help to ensure that their Substrate-based projects are secure and reliable.
Comments