來源:Beosin
隨着區塊鏈技術的飛速發展,TON生態系統逐漸嶄露頭角,吸引了大量开發者和投資者的關注。然而,隨之而來的安全問題也不容忽視。爲了確保TON生態的安全性和可靠性,專業的安全審計成爲不可或缺的一環。本文將深入探討Beosin如何對TON生態進行全方位的安全審計,護航其全生態安全。
權限校驗
由於TON區塊鏈在一定程度上實現了账本和邏輯的分離,TON合約的調用通常比以太坊合約更加復雜。以Jetton錢包爲例,一個Jetton Master合約管理着多個用戶錢包,每個用戶的錢包合約記錄各自的余額。在進行鑄幣操作時,通常涉及多個合約的調用,因此需要特別注意合約之間的權限校驗。
這裏舉兩個例子:
第一種是Master合約調用Wallet合約的情況。由於一個Master合約對應多個Wallet合約,可以直接在Wallet合約中用Master地址進行判斷。例如,要求sender_address等於master_address,如下代碼所示:
throw_unless(error::unauthorized_transfer, equal_slice_bits(master_address, sender_address));
第二種是Wallet合約調用Master合約的情況。由於一個Master合約對應多個Wallet合約,不能直接使用上述方法進行鑑權。因此,Wallet合約調用Master合約時,通過from_address、my_address和jetton_wallet_code計算出Wallet合約地址,然後判斷sender_address是否等於計算出的Wallet合約地址。
throw_unless(error::unauthorized_burn_request, equal_slice_bits(calc_user_wallet(from_address, my_address(), jetton_wallet_code), sender_address) );
溢出
在TON區塊鏈的Func語言中,鑑於其僅支持 int 類型,在審計過程中務必考慮這一點。通過審計,我們發現大多數代碼都是在轉账後進行檢查,因此我們建議在轉账前進行檢查。下面是我們編寫的一個簡單示例。
() send_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure inline_ref {int query_id = in_msg_body~load_query_id();int jetton_amount = in_msg_body~load_coins();slice to_owner_address = in_msg_body~load_msg_addr();force_chain(to_owner_address); (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();throw_unless(error::not_enough_jettons, balance >= jetton_amount);throw_unless(error::not_enough_jettons, jetton_amount >= 0);balance -= jetton_amount;
當然,還可以使用反序列化中的 store_coins 來保存 amount。store_coins 要求輸入的 amount 必須爲正數,這也可以有效防止溢出。
並發消息調用和鎖的安全性
在TON區塊鏈上支持消息的並發調用。並發調用指的是存在多個用戶同時對账本進行操作,而不受先後順序的限制。然而,在某些情況下,如果存在多個用戶同時對账本進行操作,可能會出現問題。這時需要使用鎖機制,以確保用戶的調用有先後順序。
簡單展示了TON區塊鏈中並發消息調用和鎖機制防範的流程:
1.User A 和 User B 分別發送消息到 Master Contract。
2.Master Contract 管理多個錢包合約(Wallet Contract A 和 Wallet Contract B),並包含鎖機制邏輯以確保順序性。
3.Wallet Contract A 和 Wallet Contract B 分別處理交易。
箭頭表示消息和調用的流程,通過在Master Contract中實現的鎖機制,確保了安全的並發操作。
cell 數據解析
在TON區塊鏈中,Cell是一種靈活且高效的樹狀數據結構,用於存儲和傳輸數據,是構建和解析TON區塊鏈數據的基本單位。因此,在審計中需要特別注意Cell數據解析的正確性。在Func語言中,解析Cell數據是順序進行的。常用的解析操作和函數包括:讀取位(Bits)操作,如 load_bits(n) 從Slice中讀取n位數據和 skip_bits(n) 跳過n位數據;讀取整數(Integers)操作,如 load_uint(n) 讀取n位無符號整數和 load_int(n) 讀取n位有符號整數;讀取字節(Bytes)操作,如 load_bytes(n) 從Slice中讀取n個字節;讀取引用(References)操作,如 load_ref() 讀取一個引用並返回一個新的Cell對象;讀取地址(Address)操作,如 load_msg_addr() 讀取一個消息地址;讀取代幣(Coins)操作,如 load_coins() 讀取一個代幣值。此外,還包括Slice操作,如 slice_empty?() 檢查Slice是否爲空和 begin_parse() 創建一個新的Slice用於解析引用的Cell。
下面舉個例子:
;}
开始解析:調用 message.begin_parse() 方法創建一個用於解析的 slice 對象 cs。
讀取標志位:使用 cs~load_uint(4) 讀取4位無符號整數,並將其存儲在 flags 變量中。
讀取發送者地址:使用 cs~load_msg_addr() 讀取消息發送者的地址,並將其存儲在 sender_address 變量中。
跳過目的地址:再次調用 cs~load_msg_addr() 讀取但忽略目的地址。
跳過消息值:使用 cs~load_coins() 讀取但忽略消息值。
跳過額外貨幣收集標志:使用 cs~skip_bits(1) 跳過1位的額外貨幣收集標志。
跳過 IHR 費用:使用 cs~load_coins() 讀取但忽略IHR費用。
讀取並計算轉發費用:使用 cs~load_coins() 讀取硬幣值,然後通過 muldiv(cs~load_coins(), 3, 2) 計算轉發費用 fwd_fee。
費用回收
在 TON 區塊鏈中,"excesses" 指的是在消息處理過程中產生的未使用的費用。用戶在發送交易時,會預留一定量的 gas 費用用於支付計算資源,以及可能的存儲費用。如果實際消耗的費用低於預留的金額,那么未使用的部分就會成爲 excesses。這些 excesses 可能來源於未使用完的 gas 費用或低於預留的存儲費用。在審計過程中,需要特別關注 excesses 的處理,確保多余的費用能夠正確退還給用戶。
消息手續費
Ton中的手續費取決於許多因素,並且最終的手續費是由多種不同的交易費構成.在審計過程中,應特別注意手續費計算的准確性,以避免因 gas 不足導致消息失敗。
transaction_fee = storage_fees + in_fwd_fees + computation_fees + action_fees + out_fwd_fees
名稱 | 介紹 | 示例 |
storage_fees | 區塊鏈上存儲智能合約而支付的金額 | TON錢包也是一個智能合約,每次接收或發送交易時都會支付存儲費用 |
in_fwd_fees | 區塊鏈外部導入消息的費用。每次進行交易時,都必須將其傳送給將處理它的驗證者 | 每個錢包應用程序(如Tonkeeper)進行的每筆交易都需要首先在驗證節點之間分發,支付這筆費用 |
computation_fees | 在虛擬機中執行代碼而支付的金額。代碼越大,必須支付的費用就越多。 | 每次使用錢包(即智能合約)發送交易時,都會執行錢包合約的代碼並爲此付費。 |
action_fees | 智能合約發送外部消息所收取的費用。 | 智能合約間的相互調用需要爲此付費。 |
out_fwd_fees | 從TON區塊鏈發送消息到外部服務(例如,日志)和外部區塊鏈的費用。 | 由於尚未實施,因此目前未使用。因此目前等於0。 |
以下是常見費用的參考:
發送任何數量的TON的平均費用爲0.0055 TON。發送任何數量的自定義Jettons的平均費用爲0.037 TON。鑄造一個NFT的平均費用爲0.08 TON。在TON上保存1 MB數據一年的成本爲6.01 TON。
關於每種費用的詳細計算公式可以參考:
https://docs.ton.org/mandarin/develop/smart-contracts/fee-calculation
考慮消息失敗的情況
在TON的函數調用中,一個操作可能會涉及多個消息的發送。當某個消息失敗時,之前執行的消息結果不會受到影響。如果不回滾之前消息更新的數據,可能會導致數據不一致問題。例如,在抵押操作中,用戶首先將資金轉入合約,但由於後續消息的失敗且代碼未考慮消息失敗的情況,資金未能返還給用戶。與EVM不同的是,TON的函數調用不會在失敗時自動回滾到最初狀態,這需要开發人員手動處理回滾邏輯,以確保數據一致性。
數據導入順序
在TON環境中,由於消息傳輸的異步特性,如果沒有考慮數據導入的順序,這會導致數據不一致的情況。例如,當用戶執行贖回操作時,如果其他用戶在此之前執行了類似的資產操作,可能會導入新的資產地址數據。如果在執行贖回時未及時更新相關的地址信息,那么可能導致贖回的資產與實際情況不符。
消息反彈
在TON中,處理消息反彈(bounce)是一項重要任務,因爲消息傳遞失敗會影響智能合約的正常運行和業務邏輯。對於什么時候該反彈什么時候不反彈的處理需要根據當前業務邏輯進行仔細審計。
幾乎所有在智能合約之間發送的內部消息都應該是可彈回的,即應該設置它們的“bounce”位。所有智能合約都應檢查所有入站消息的“bounced”標志,並且要么默默接受它們(通過立即以exit code 0終止),要么執行一些特殊處理來檢測哪個出站查詢失敗了。彈回消息主體中包含的查詢永遠不應執行。
但是在某些情況下,必須使用不可彈回內部消息。例如,沒有發送至少一條不可彈回內部消息給它們,就無法創建新账戶。除非這條消息包含一個帶有新智能合約的代碼和數據的StateInit,否則在不可彈回內部消息中擁有非空主體是沒有意義的。non-bounce消息的設計目的是爲了避免某些情況下的資源浪費和處理復雜性。
TON生態系統作爲一個开放且去中心化的平台,吸引了大量开發者和用戶。然而,其復雜的技術架構和廣泛的應用場景也帶來了潛在的安全風險。
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。