This article will analyze in detail some features related to smart contracts on the TON blockchain, as well as the vulnerability points of smart contracts on TON that are easily overlooked.
Author: Beosin
In today's rapidly developing blockchain technology, TON (The Open Network) is attracting more and more attention from developers as an efficient and flexible blockchain platform. TON's unique architecture and features provide powerful tools and rich possibilities for the development of decentralized applications.
However, with the increase in functionality and complexity, the security of smart contracts has become increasingly important. FunC, as the smart contract programming language on TON, is known for its flexibility and efficiency, but it also brings many potential risks and challenges. Writing secure and reliable smart contracts requires developers to have a deep understanding of the features of the FunC language and the potential risks that may exist.
This article will analyze in detail some features related to smart contracts on the TON blockchain, as well as the vulnerability points of smart contracts on TON that are easily overlooked.
Analysis of Ton's Asynchronous Features and Account Mechanism
Asynchronous Invocation of Smart Contracts
Network Sharding and Asynchronous Communication
The TON blockchain is designed with three types of chains: the master chain, working chains, and shard chains.
The master chain is the core of the entire network, responsible for storing the metadata and consensus mechanism of the entire network. It records the state of all working chains and shard chains, and ensures the consistency and security of the entire network. Working chains are independent blockchains, with a maximum of 2^32, responsible for processing specific types of transactions and smart contracts. Each working chain can have its own rules and characteristics to meet different application requirements. Shard chains are sub-chains of working chains, used to further divide the workload of working chains, improve processing capacity, and scalability. Each working chain can be split into a maximum of 2^60 shard chains, and shard chains independently process part of the transactions, achieving efficient parallel processing.
In theory, each account can occupy a shard chain, and each account independently maintains its COIN/TOKEN balance. Transactions between accounts can be completely parallel. Messages are transmitted asynchronously between shard chains, with the path of message transmission between shard chains being log_16(N) - 1, where N is the number of shard chains.
Source: https://frontierlabzh.medium.com/ton-web3 世界的 weixin-e1d3ae3b3574
In Ton, smart contracts interact by sending and receiving messages. These messages can be internal messages (generally messages sent by smart contracts to interact with each other) or external messages (messages sent by external sources). The process of message delivery does not require waiting for an immediate response from the target contract, and the sender can continue executing the remaining logic code. This asynchronous message delivery mechanism, compared to Ethereum's synchronous calls, provides greater flexibility and scalability, reduces performance bottlenecks caused by waiting for responses, and also brings challenges in handling concurrency and race conditions.
Message Format and Structure
In Ton, messages typically contain sender, recipient, amount, message body, and other information. The message body can be a function call, data transfer, or other custom content. The message format used by Ton can be flexibly defined and extended, allowing different contracts to efficiently transmit various types of information.
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_coins(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_slice(message_body)
.end_cell();
Message Queue and State Processing
Each contract maintains a message queue to store unprocessed messages. During execution, the contract processes messages one by one according to the queue. Since message processing is asynchronous, the contract's state is not immediately updated before receiving messages.
Advantages of Asynchronous Message Delivery
- Efficient sharding mechanism: Ton's asynchronous mechanism is highly compatible with its sharding design. Each shard independently processes contract messages and state changes, avoiding the latency issues caused by cross-shard synchronous communication. This design enhances the throughput and scalability of the entire network.
- Reduced resource consumption: As asynchronous messages do not require immediate responses, Ton's contract execution can be spread across multiple blocks, avoiding excessive resource consumption within a single block. This allows Ton to support more complex and resource-intensive smart contracts.
- Fault tolerance and reliability: The asynchronous message delivery mechanism makes the system more fault-tolerant. For example, if a contract cannot respond to a message in a timely manner due to resource constraints or other reasons, the sender can continue processing other logic, and the system will not stall due to the delay of a single contract.
Challenges of Asynchronous Contract Design
- State consistency issues: Due to the asynchronous nature of message delivery, a contract's state may receive different messages at different times, requiring developers to pay special attention to state consistency issues. When designing contracts, it is necessary to consider the potential state changes caused by different message sequences to ensure that the system can maintain consistency under any circumstances.
- Race conditions and protection: Asynchronous message processing brings potential race condition issues, where multiple messages may attempt to modify the contract state simultaneously. Developers need to introduce appropriate locking mechanisms or use transactional operations to prevent state conflicts.
- Security considerations: Asynchronous contracts are susceptible to man-in-the-middle attacks or replay attacks when handling cross-contract communication. Therefore, when designing asynchronous contracts, it is necessary to consider these potential security risks and take measures to prevent them, such as using timestamps, random numbers, or multi-signatures.
Account Model
When designing its blockchain infrastructure, Ton (The Open Network) adopted a unique account abstraction and account model. The flexibility of this model is reflected in how it handles account states, message delivery, and contract execution.
Account Abstraction
Ton's account model adopts a contract-based abstraction, where each account can be viewed as a contract. This has some similarities to Ethereum's account abstraction model, but is more flexible and general. In Ton, accounts are not just containers for holding assets; they also contain contract code and state data. Each account consists of its code, data, and message handling logic.
Account structure: Each Ton account has a unique address, which is composed of the hash value of the account code, the initial data deployed, and some other parameters. This means that the same code and initial data deployed in different environments (e.g., different blockchains or shards) may generate different addresses.
Flexibility: Since each account can run its own contract code, Ton's accounts can implement very complex logic. Accounts are not just simple balance holders; they can also handle complex state transitions, cross-account message communication, and even automated operations based on specific conditions. This makes Ton's account model more scalable and flexible than the account models on traditional blockchains.
Account Book Structure
Ton's account book structure is designed to efficiently process large-scale concurrent transactions, support asynchronous message delivery, and multi-shard operations. The state of each account is stored in a Merkle tree structure, giving Ton's account book efficient state verification capabilities.
State Storage
Account state information is stored in persistent storage and organized through a Merkle tree to ensure the integrity and security of the state. This design also supports efficient querying and verification of state, especially in scenarios involving cross-shard transactions.
The account or smart contract state typically includes the following:
- Balance of the base currency
- Balance of other currencies
- Smart contract code (or its hash)
- Persistent data of the smart contract (or its Merkle hash)
- Statistics on the number of persistent storage units and the original number of bytes used
- The most recent time of payment for smart contract persistent storage (actually the block number of the master chain)
- Public key required to transfer currency and send messages from this account (optional; by default, it is equal to the accountid itself). In some cases, more complex signature check code can be found here, similar to what is done in Bitcoin transaction outputs; then accountid will be equal to the hash value of this code.
Not all information is required for each account. For example, smart contract code is only applicable to smart contracts, not to "simple" accounts. Additionally, while any account must have a non-zero balance of the primary currency (e.g., Gram for the main chain of the basic working chain and shard chains), the balance of other currencies may be zero. To avoid retaining unused data, a sum-product type was defined during the creation of the working chain, which uses different token bytes to distinguish different "constructor functions". Ultimately, the account state itself is saved as a collection of TVM persistent storage units.
Message Delivery and Processing
Ton's account book structure inherently supports asynchronous message delivery, allowing each account to independently process received messages and update its state. This asynchronous message mechanism enables complex interactions between accounts without affecting the normal operation of other accounts due to the delay of a particular operation.
Gas Model
The Ton (The Open Network) blockchain significantly optimizes the efficiency of smart contract execution through its unique Gas fee model. The Gas fee model is used in the blockchain to measure and limit the resources consumed during smart contract execution. Compared to the Gas model of traditional blockchains (such as Ethereum), Ton's model is more complex and efficient, allowing for more precise management of resource consumption during contract execution.
Refined Gas Consumption Measurement
Ton's Gas model can accurately measure the computational resources, storage operations, and message delivery costs consumed during smart contract execution. By refining the measurement of resources such as computation, storage, and message delivery, Ton's Gas model prevents operations with excessive complexity from consuming too many resources. By limiting Gas consumption, Ton ensures that each node in the network can fairly allocate computational resources and avoids excessive consumption of network resources by a single contract or operation.
Parallel Processing and Gas Optimization
Ton supports parallel processing of smart contracts, allowing multiple contracts to run simultaneously on different shards without blocking each other. In this design, its Gas model is closely integrated with its parallel execution and sharding mechanism. By parallel processing contracts on multiple shards, Ton can distribute Gas computation and payment across different nodes and chains, avoiding network congestion and maximizing resource utilization.
Dynamic Gas Adjustment Mechanism
Ton's Gas model includes a dynamic adjustment mechanism that allows Gas fees to be adjusted based on the real-time load of the network. This means that when the network load is low, users can execute contracts at lower Gas fees, encouraging operations during low-load periods and balancing the network's resource usage. This mechanism not only improves user experience but also controls resource usage peaks in a market-oriented manner.
Vulnerabilities in Ton Smart Contracts
In our previous security analysis article on TON, we detailed the common security vulnerabilities in the Ton ecosystem, which can also be referred to in the following table:
This article will focus on the vulnerability points in TON contracts that our team has identified as being easily overlooked:
(1) Optimization of Code Readability
In TON smart contracts, numbers are used to store data related to message sending. For example, in the code below, numbers are used multiple times to represent corresponding identifiers and data storage lengths, significantly reducing the readability and maintainability of the code. Other developers may find it difficult to understand the meaning and purpose of these numbers when reading this code. To improve code readability and maintainability, it is recommended to define key numerical values as named constants, for example: defining 0x18 as NON_BOUNCEABLE.
check_std_addr(address);
var msg = begin_cell()
.store_uint(0x18, 6) ;; nobounce
.store_slice(address)
.store_coins(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.end_cell();
send_raw_message(msg, 1);
Additionally, in error message prompts in contract conditional statements, it is also recommended to define corresponding variables to replace error codes.
throw_unless(705, equal_slices(owner_address, sender_address));
(2) Use of end_parse() to Ensure Data Integrity
In TON contracts, data parsing follows a fixed order, loading specified types of data from the raw data step by step. This parsing method ensures the consistency and accuracy of the data. As shown below:
() load_data() impure {
slice ds = get_data().begin_parse();
storage::owner = ds~load_msg_addr();
storage::amount = ds~load_uint(256);
storage::data = ds~load_ref();
storage::api_data = ds~load_ref();
ds.end_parse();
}
Note that endparse() is used to check if the data slice is empty. If the slice is not empty, the function will throw an exception. This ensures that the data format and content are as expected. If endparse() finds that there is still remaining data in the data slice, it may indicate that the data parsing did not fully follow the expected process, or there may be issues with the data format. Therefore, by calling end_parse(), it is possible to check for omissions or anomalies in the parsing process.
(3) Exceptions Caused by Mismatched Data Loading and Storage Types
Here, it is important to note the matching of int and uint storage types. In the code below, storeint() is used to store the value of type int as -42, but loaduint() is used to load this value, which may lead to an exception.
() Test_Fuction() {
var cell = begin_cell();
cell = cell.store_int(-42, 32);
var my_cell = cell.end_cell();
slice s = my_cell.begin_parse();
var result = s.load_uint(32);
}
(4) Reasonable Use of inline_ref and inline Modifiers
First, it is necessary to explain the difference between inline_ref and inline modifiers:
Inline: Functions decorated with the inline modifier have their code directly inserted at the call site every time they are called. This means that the actual code of the function is copied to the call location each time the function is called, rather than being executed by jumping to the function body as with regular functions.
Inlineref: Functions decorated with the inlineref modifier have their code stored in a separate cell. Each time the function is called, TVM executes the code stored in the cell using the CALLREF command, instead of inserting the function code at the call location.
Therefore, the inline modifier is suitable for simple functions to reduce function call overhead but may lead to code duplication. On the other hand, the inlineref modifier is suitable for complex or frequently called functions to improve efficiency by storing the function code in a separate cell, avoiding code duplication. In summary, when the function is large or called from multiple places, it is recommended to use inlineref; otherwise, inline is recommended.
(5) Determining the Correct Working Chain
TON allows the creation of up to 2^32 working chains, each of which can be subdivided into up to 2^60 shards. Currently, there are only two working chains: the master chain (-1) and the base chain (0). When calculating the target address in a contract, the chain ID to which the target address belongs must be explicitly specified to ensure that the generated wallet address is on the correct working chain. To avoid generating incorrect addresses, it is recommended to use force_chain() to forcibly specify the chain ID.
(6) Avoiding Error Code Conflicts
In contract design, managing error codes is crucial to ensure compliance and avoid confusion. For TON smart contracts, it is important to ensure that each error code is unique within the contract to prevent the definition of duplicate error codes within the same contract, which could lead to confusion and unclear information. Additionally, TON platform or underlying systems have defined some standard error codes, and it is advisable to avoid conflicts with these system error codes. For example, error code 333 represents a mismatched chain ID. Therefore, it is recommended that the error codes for contracts be preferably between 400 and 1000.
(7) Storing Data and Calling return() After Operation Completion
In TON smart contracts, message processing selects different logic based on op-codes. After completing the corresponding business logic, two operations must be completed: first, if there are data changes, save_data() must be called to ensure that the data is stored, otherwise the changes will be invalid; second, return() must be called to indicate the completion of the operation, otherwise it will trigger the throw(0xffff) exception.
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
int flags = cs~load_uint(4);
if (flags & 1) {
;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
load_data();
int op = in_msg_body~load_op();
if ((op == op::op_1())) {
handle_op1();
save_data();
return ();
}
if ((op == op::op_2())) {
handle_op2();
save_data();
return ();
}
if ((op == op::op_3())) {
handle_op3();
save_data();
return ();
}
throw(0xffff);
}
In conclusion, with its innovative architecture and flexible development environment, the TON blockchain is gradually becoming the ideal platform for decentralized application developers. However, as smart contracts play an increasingly important role in the TON ecosystem, contract security issues cannot be ignored. Developers should have a deep understanding of the characteristics of the TON ecosystem, strictly adhere to best practices, strengthen security audit processes, and ensure the robustness and security of contracts. Only in this way can the advantages of the TON platform be fully utilized to build more secure and reliable decentralized applications, ensuring the healthy development of the entire ecosystem.
The TON ecosystem is currently experiencing rapid development, attracting a large amount of funds and active users. However, the associated security issues cannot be overlooked.
免责声明:本文章仅代表作者个人观点,不代表本平台的立场和观点。本文章仅供信息分享,不构成对任何人的任何投资建议。用户与作者之间的任何争议,与本平台无关。如网页中刊载的文章或图片涉及侵权,请提供相关的权利证明和身份证明发送邮件到support@aicoin.com,本平台相关工作人员将会进行核查。