本文將從帳號、Token、交易的角度討論 TON 的特性及用戶資產安全問題。

作者: Johan,慢霧安全團隊

封面: Photo by  Eyestetix Studio  on  Unsplash

背景

TON(The Open Network) 是一個去中心化的區塊鏈平台,由 Telegram 團隊最初設計和開發。 TON 的目標是提供一個高效能和可擴展的區塊鏈平台,以支援大規模的去中心化應用程式 (DApps) 和智慧合約。

TON 如此特殊,它是易用的,它與 Telegram 深度結合,使得普通人也能輕易使用代幣;它又是複雜的,它與其他區塊鏈有著截然不同的架構,而且使用非主流的 FunC 智能合約語言。今天我們從帳號、Token、交易的角度討論一下 TON 的特色及用戶資產安全問題。

TON 的特點

帳號生成

TON 帳號位址的產生方式與大多數區塊鏈都不同,它是一個智慧合約地址。首先,開局一個私鑰,TON 主要使用 Ed25519 演算法產生公鑰,產生流程如下:

圖片

這裡有兩種形式的公鑰,一種是由私鑰計算的原始公鑰,形如:

E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D

另一種是「美化」後的公鑰,它攜帶了公鑰的一些資訊和校驗位,形如:Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2

如果認為得到公鑰就能像以太坊一樣得到帳號地址就太天真了,光是用戶的公鑰還不足以計算出用戶的帳號位址。我們剛剛說了,用戶的帳號地址是一個智能合約地址,但是我們連帳號都沒有,怎麼部署智能合約?正確的順序是先計算地址,接收一點初始金額的代幣,然後就可以部署合約。帳號位址的計算過程如下圖所示:

圖片

使用者的地址也有多種形式,首先是原始形式,形如:

0:b4c1b2ede12aa76f4a44353944258bcc8f99e9c7c474711a152c78b43218e296

以及使用者友善形式,形如:

Mainnet:
  Bounceable:
    EQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0MhjilkPX
  Non-bounceable:
    UQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0Mhjilh4S
Testnet:
  Bounceable:
    kQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0Mhjilvhd
  Non-bounceable:
    0QC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0MhjilqWY

細心觀察這幾個位址就可以看出,它們只有首尾幾個字元存在差別,中間的`account_id` 是一樣的,但是我們還是無法看出公鑰和帳號位址之間的關係,其實玄機就藏在在開頭的`initial data` 中,它包含了一個使用者的公鑰,使用者就是透過它掌握錢包合約的所有權。 `workchainId` 很好理解,TON 並不是只有一條單鏈,它由非常多的分片組成,每個分片是整個網路的一部分,處理特定的一組帳號和交易。為了定位和管理智能合約,需要明確指出它們位於哪個分片中。 `Bounceable` 和`Non-bounceable` 有什麼不同?這和智能合約的工作機制有關,我們先繼續往下看。

錢包合約

以下是一個用戶錢包合約的一段原始碼,可以看到它在接收到用戶的訊息時讀取了 4 個參數 (stored_seqno, stored_subwallet, public_key, plugins):

wallet-v4-code.fc

() recv_external(slice in_msg) impure {
  var signature = in_msg~load_bits(512);
  var cs = in_msg;
  var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
  throw_if(36, valid_until <= now());
  var ds = get_data().begin_parse();
  var (stored_seqno, stored_subwallet, public_key, plugins) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256), ds~load_dict()); ;;##The Initial Data
  ds.end_parse();
  throw_unless(33, msg_seqno == stored_seqno);
  throw_unless(34, subwallet_id == stored_subwallet);
  throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
  //...
}

沒錯,這個用戶的錢包合約在部署的時候,需要傳入一些初始參數,其中就包含了一個 256 位元的 public_key 訊息,這樣就確保了每個用戶使用相同的合約程式碼時,有一個獨立的合約地址。用戶發起的一切交易都需要對`in_msg` 簽名,然後透過自己的錢包合約驗證簽名 (check_signature) 後,由合約去呼叫鏈上的一切操作。從這裡我們也可以推斷出,一個用戶的公鑰其實是可以對應無數錢包位址的,只需要部署不同原始碼的錢包或不同的初始化數據,就能得到完全不同的合約位址。

Jetton Token

Token 是資產在鏈上的表現形式,所以它是我們需要了解的一個基本元素。 Jetton 是 TON token 的標準形式,Jetton 由兩部分合約組成,Jetton-minter 和 Jetton-wallet:

圖片

代幣被發行時,會創建一個 Jetton-minter 合約,合約初始化記錄了代幣的總量、管理員、錢包代碼等資訊。

當代幣分發給用戶時,Minter 合約會為用戶部署一個錢包合約,並在合約初始化時記錄用戶的餘額、所有權、代幣 Minter 合約地址、用戶錢包代碼等信息,每個用戶都會獨立部署一個合約。注意,這裡創建的合約是用於管理特定 Jetton 代幣的錢包合約,與用戶的帳號錢包合約並不相同,這裡的 owner_address 記錄的是用戶的帳號錢包地址。

當用戶 Alice 給用戶 Bob 轉帳時,呼叫關係如下:

圖片

Alice 在鏈下的 APP 簽名,並透過呼叫她的錢包合約下達操作指令。這些指令會進一步呼叫她的代幣錢包來轉帳。當 Bob 的代幣錢包收到代幣後,它會通知 Bob 的錢包合約(即 Bob Jetton-wallet 的 Owner 地址)。如果交易過程中有剩餘的 Gas,也會回傳給回應位址,通常為 Alice 的帳號合約。

這是 Tonviewer 瀏覽器解析的一筆 Jetton 代幣轉帳:

圖片

一個 ERC20 轉帳最少只需要呼叫一個合約,而一筆 Jetton 代幣轉帳至少要呼叫四個合約,這麼做是為了讓轉帳可以在鏈上並發進行,提高交易效率。

交易

當 TON 中的某個帳戶發生某些事件時,它會引發一筆交易,最常見的事件是 “接收某個訊息”,交易包括以下內容:

  • 最初觸發合約的傳入訊息(有特殊的觸發方式)
  • 由傳入訊息引起的合約行動,例如更新合約的儲存(可選)
  • 發送給其他參與者的傳出訊息(可選)
圖片

交易需要注意幾個特性:

1. 非同步:TON 的交易並不是一次呼叫裡完成的,它可能需要透過訊息傳遞到多個不同的智能合約去執行一系列呼叫。由於分片鏈中的路由不同,TON 並不能保證多個智慧合約之間的訊息傳遞順序。

2. 手續費: 非同步的特性還會帶來一個問題,就是消耗的手續費難以預估。因此在發起交易時,錢包通常會多發送一些代幣作為手續費。如果調用的合約有良好的手續費處理機制,那麼剩餘的手續費最後會返還到用戶錢包。用戶可能會觀察到錢包代幣突然變少,過幾分鐘又變多,就是這個原因。

3. 反彈:反彈是合約的一種錯誤處理機制,當呼叫的合約不存在或拋出錯誤時,如果交易設定為可反彈的,那麼就會反彈回一個 bounced 訊息給發起呼叫的合約。例如:用戶發起一筆轉賬,如果呼叫過程出錯了,那麼就需要反彈訊息,這樣用戶的錢包合約才能將自己的餘額恢復。幾乎所有在智能合約之間發送的內部訊息都應該是可彈回的,即應該設置它們的 “bounce” 位元。

資產安全

TON 有許多功能會帶來安全問題,因此使用者也需要了解一些常見的陷阱。

手續費截留攻擊

上面說到錢包經常需要多發送一些手續費以防止交易執行失敗,這讓攻擊者找到了作惡的機會。如果你是 TON 錢包用戶,你可能碰到過這樣的情況,錢包裡總是會收到各種 NFT 或代幣,本來以為只是一些垃圾代幣空投,但是一查交易訊息,竟然可以賣不少錢?可是當啟動交易時,發現所需的手續費超高(1 TON),這時就需要注意了,這可能是手續費詐騙。

圖片

攻擊者利用精心建構的代幣合約,讓錢包的預估轉帳手續費超高,而實際執行時卻只是截留手續費,並未發送轉帳訊息。

首尾號釣魚

首尾號釣魚不是 TON 上才有,各大公鏈都存在這種釣魚攻擊。攻擊者會為全網每個用戶地址都產生一個首尾號相同的高仿帳號,當用戶發送一筆轉賬時,攻擊者用高仿帳號也尾隨發送一筆小額轉賬,目的是在用戶的收款記錄裡留下一張紀錄。當收款用戶想要轉回一筆代幣時,可能會從歷史記錄裡複製地址,這時很可能複製到攻擊者的地址,導致轉賬到錯誤地址,攻擊者可謂是精準拿捏用戶的行為了。

comment 釣魚

TON 在轉帳時可以添加一個 comment,用於備註交易訊息,這個功能在交易所充值時會頻繁用到,交易所通常會要求用戶在充值時備註一下用戶 ID。但這個功能經常會被惡意利用,攻擊者透過在備註裡寫入詐欺資訊來騙取用戶的資產。如圖所示:

圖片

使用者尤其需要注意 Anonymous Telegram Number 這個 NFT,如果使用者用 Anonymous Telegram Number 開通了 TG 號,但沒開 Two-Step Verification,一旦這個 NFT 被釣走,駭客就可以直接登入目標 TG 號,實施後續的資產盜取及欺騙行為。

智能合約漏洞

智能合約的安全漏洞會導致用戶放在智能合約的資金受損,用戶在選擇專案時需要選擇經過良好審計的專案。 TON 的智能合約主要使用 FunC 語言來編程,也有使用更高階的 Tact,或更底層的 Fift,都是原創程度很高的語言。新的程式語言會帶來新的安全風險,特別是對開發者而言,要有安全編程的良好習慣,掌握最佳安全實踐,並且在部署生產環境之前經過嚴格的安全審計,限於篇幅,本文暫不討論合約安全。慢霧安全團隊推出 TON 智慧合約安全審計服務,歡迎有審計需求的朋友一起探討。

假儲值攻擊

錢包或交易所用戶需要注意假儲值攻擊,通常有兩種類型的假儲值攻擊:

  • 假幣,攻擊者發行一個 metadata 和目標代幣相同的代幣,如果自動化入帳程式沒有檢查這是否是正確的 minter 合約,那麼就會導致錯誤入帳。
  • 反彈,TON 的轉帳過程需要在兩個用戶的錢包合約之間發生呼叫關係,如果接收方的錢包合約不存在,並且交易設定為 Bounceable,這時訊息會被反彈,原始資金在扣除手續費後將返還給發送方。對細節有興趣的朋友可以查看我們之前披露過的假儲值文章。

總結

本文從 TON 的公私鑰創作、錢包合約、Token 的形式、交易特性等角度介紹了 TON 的一些基礎的技術原理,同時也探討了使用 TON 的過程中可能存在的安全問題,希望能給大家的學習帶來啟發。

參考連結:

https://docs.ton.org/

https://github.com/ton-blockchain/wallet-contract

免責聲明:作為區塊鏈資訊平台,本站所發布文章僅代表作者及來賓個人觀點,與 Web3Caff 立場無關。文章內的資訊僅供參考,均不構成任何投資建議及要約,並請您遵守所在國家或地區的相關法律法規。