背景
TON(The Open Network)は、当初Telegramチームによって設計・開発された分散型ブロックチェーン・プラットフォームである。TONは、大規模な分散型アプリケーション(DApps)やスマートコントラクトをサポートする、高性能でスケーラブルなブロックチェーンプラットフォームを提供することを目指している。
TONのユニークな点は、使いやすく、Telegramと深く統合されており、一般人がトークンを簡単に利用できることだ。同時に複雑で、他のブロックチェーンとは一線を画すアーキテクチャを持ち、FunCと呼ばれる非主流のスマートコントラクト言語を使用している。
本日は、アカウント、トークン、トランザクションの観点から、TONの特徴とユーザー資産のセキュリティについて解説する。
TONの特徴
アカウント・ジェネレーション
TONのアカウントアドレスの生成方法は、多くのブロックチェーンとは異なり、スマートコントラクトのアドレスとなる。まず、すべては秘密鍵から始まる。TONは主にEd25519 アルゴリズムを使って公開鍵を生成しており、そのプロセスは以下のようになっている:
1つは秘密鍵から計算された生の公開鍵で、次のようになる:
E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D
もうひとつは「美化」された公開鍵で、いくつかの情報とチェック・ビットを含み、次のようになる:
Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2
イーサリアムのように公開鍵を入手するだけでアカウントアドレスが取得できると思ったら大間違いだ。ユーザーの公開鍵を持っているだけでは、ユーザーのアカウントアドレスを計算することはできない。
前述したように、ユーザーのアカウント・アドレスはスマート・コントラクトのアドレスであるが、アカウントを持たずにどうやってスマート・コントラクトをデプロイできるのだろうか?
正しい順序は、まずアドレスを計算し、いくらかの初期トークンを受け取り、それからコントラクトをデプロイする。口座アドレスの計算プロセスを下図に示す:
ユーザーのアドレスも複数の形式がある。まず、生のフォームがある:
0:b4c1b2ede12aa76f4a44353944258bcc8f99e9c7c474711a152c78b43218e296
そして、ユーザーフレンドリーなフォームがある:
メインネット
- バウンス可能:
EQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0MhjilkPX
- バウンス不可:
UQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0Mhjilh4S
テストネット
- バウンス可能:
kQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0Mhjilvhd
- バウンス不可:
0QC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0MhjilqWY
これらのアドレスを注意深く観察すると、最初と最後の数文字だけが異なり、真ん中のaccount_id
文字は同じであることがわかる。
しかし、公開鍵とアカウント・アドレスの関係はまだわからない。
その秘密は冒頭のinitial data
にあり、この211にはユーザーの公開鍵が含まれており、ユーザーはウォレットのコントラクトをコントロールすることができる。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はTONトークンの標準的な形態であり、2つのコントラクトで構成されています:Jetton-minterとJetton-walletです。
トークンが発行されると、Jetton-minterコントラクトが作成される。このコントラクトの初期化により、トークンの総供給量、管理者、ウォレットコード、その他の詳細などの情報が記録される。
トークンがユーザーに配布されると、Minter契約は各ユーザーのウォレット契約を展開する。コントラクトの初期化中に、ユーザーの残高、所有権、トークンのMinterコントラクトアドレス、ユーザーウォレットコード、その他の関連情報が記録される。各ユーザーは個別にデプロイされたコントラクトを持つ。
ここで作成されるコントラクトは、特定のJettonトークンを管理するためのウォレットコントラクトであり、ユーザーのアカウントのウォレットコントラクトとは異なることに注意することが重要です。ここで記録されるowner_address
は、ユーザーのアカウントウォレットのアドレスです。
ユーザーAliceがユーザーBobにトークンを転送するとき、呼び出しの 関係は以下のようになる:
アリスはオフチェーンアプリを通じて取引に署名し、彼女のウォレットコントラクトを通じて操作コマンドを送信する。これらのコマンドはさらに彼女のトークンウォレットを呼び出して送金を実行する。Bobのトークンウォレットがトークンを受け取ると、Bobのウォレットコントラクト(BobのJettonウォレットのオーナーアドレス)に通知する。
もし取引中にガスが残った場合、そのガスは適切な住所(通常はアリスの口座契約)に返却されます。
以下は、Tonviewerブラウザで解析されたJettonトークン転送の例です:
ERC20の送金ではコントラクトの呼び出しが1回で済むのに対し、Jettonトークンの送金では少なくとも4回のコントラクトの呼び出しが必要です。この方法は、オンチェーン同時実行を可能にし、トランザクション効率を向上させるように設計されています。
トランザクション
TONアカウントで特定のイベントが発生すると、トランザクションがトリガーされる。最も一般的なイベントは “メッセージの受信 “である。トランザクションには以下の要素が含まれる:
- コントラクトを最初にトリガーする着信メッセージ(特別なトリガーメソッドが利用可能)。
- コントラクトのストレージの更新など、受信メッセージに起因するコントラクトのアクション(オプション)。
- 他の参加者に送信するメッセージ(オプション)。
取引に関しては、いくつかの重要な特徴がある:
- 非同期: TONトランザクションは1回のコールで完了するのではなく、一連のコールを実行するために複数の異なるスマートコントラクトへのメッセージ受け渡しが必要になる場合がある。シャード化されたチェーンではルーティングが異なるため、TONは複数のスマート・コントラクト間のメッセージ配信順序を保証できない。
- 料金 トランザクションの非同期性は、消費される手数料を見積もる上で課題をもたらす。そのため、ウォレットは取引開始時に手数料として少し余分なトークンを送ることが多い。呼び出されたコントラクトに優れた手数料処理メカニズムがあれば、残りの手数料はユーザーのウォレットに戻される。ユーザーは自分のウォレットトークンが突然減少し、数分後に再び増加することに気づくかもしれないが、これはこのメカニズムによるものである。
- バウンス: バウンスはコントラクトのエラー処理メカニズムである。呼び出されたコントラクトが存在しないか、エラーをスローした場合、トランザクションがバウンス可能に設定されていると、バウンスメッセージが呼び出されたコントラクトに返される。例えば、ユーザーが送金を開始し、その処理中にエラーが発生した場合、ユーザーのウォレットコントラクトが残高を復元できるように、バウンスメッセージが必要になる。スマートコントラクト間で送信されるほとんど全ての内部メッセージはバウンス可能であるべきです。
資産セキュリティ
TONにはセキュリティ上の問題につながりかねない機能がたくさんあるので、ユーザーはよくある落とし穴に注意する必要がある。
フィー源泉徴収攻撃
前述したように、ウォレットは取引失敗を防ぐために少し余分な手数料を送信する必要があることが多く、攻撃者に機会を提供しています。TONウォレットのユーザーであれば、ウォレットが様々なNFTやトークンを頻繁に受け取る状況に遭遇したことがあるかもしれません。
当初、これらはゴミのようなトークンのエアドロップに見えるかもしれないが、取引の詳細を確認すると、かなりの金額で売却できることがわかる。しかし、取引を開始しようとすると、必要な手数料が非常に高い(1トン)ことに気づく。この時点で、手数料詐欺の可能性があるため、注意が必要である。
攻撃者は注意深く作られたトークン契約を作成し、ウォレットに過剰に高い送金手数料を見積もらせますが、実際には手数料だけが保留され、送金メッセージは送信されません。
最初と最後の数字フィッシング
このフィッシング攻撃は多くの主要なブロックチェーンに存在する。攻撃者は、ネットワーク上のすべてのユーザー・アドレスに対して、同じ最初と最後の数字を持つ偽造アカウントを生成する。
ユーザーが送金を開始すると、攻撃者はユーザーの取引履歴に記録を残すために、ユーザーの取引に続いて少額の送金も行う。受信側のユーザがトークンを送り返そうとすると、攻撃者のアドレスである可能性のある取引履歴のアドレスをコピーしてしまい、間違ったアドレスに送金してしまう可能性がある。攻撃者はユーザーの行動を正確に利用している。
コメント フィッシング
TONでトークンを送金する際、ユーザーは取引に注釈を付けるためにコメントを追加できます。この機能は取引所への入金の際によく利用され、取引所では通常、入金のコメントにユーザーIDを含めるようユーザーに要求します。しかし、この機能は悪意のある行為者に悪用されることが多く、コメントにはユーザーの資産を盗むための不正な情報が書き込まれる。例えば
ユーザーは「Anonymous Telegram Number」NFTに特に注意する必要があります。ユーザーが「匿名Telegram番号」でTelegramアカウントをアクティブ化したものの、2段階認証を有効にしておらず、このNFTがフィッシングされた場合、ハッカーはターゲットのTelegramアカウントに直接ログインし、その後の資産窃盗や詐欺行為を進めることができます。
スマート・コントラクトの脆弱性
スマート・コントラクトのセキュリティの脆弱性は、コントラクトに保管された資金の損失につながる可能性がある。ユーザーは、徹底的な監査を受けたプロジェクトを選ぶべきである。TONのスマートコントラクトは主にFunC言語を使ってプログラムされているが、より高度なTactや低レベルのFiftを使っているものもある。
これらの言語はすべて、非常に独創的である。特に開発者は、安全なコーディング習慣を実践し、ベスト・セキュリティ・プラクティスを習得し、本番環境にデプロイする前に厳しいセキュリティ監査を受ける必要がある。紙面の都合上、この記事ではコントラクト・セキュリティについては詳しく触れない。
偽預金攻撃
ウォレットや取引所のユーザーは、通常2つの形態でやってくる偽預金攻撃に注意すべきである:
- 偽のトークン: 攻撃者がターゲットトークンと同一のメタデータを持つトークンを発行する。自動入金プログラムがトークンが正しい採掘者コントラクトのものかどうかをチェックしない場合、不正な入金が行われる可能性がある。
- バウンス: TONの送金プロセスには、2人のユーザーのウォレット契約間の呼び出し関係が必要です。受信者のウォレットコントラクトが存在せず、トランザクションがバウンス可能に設定されている場合、メッセージはバウンスバックされ、手数料を差し引いた元の資金が送信者に返される。詳細に興味のある方は、偽の入金に関する以前の記事を参照してください。
結論
この記事では、鍵やウォレットの作成、トークンの形態、取引の特徴といった観点から、TONの基本的な技術原則を紹介した。また、TONを使用する際の潜在的なセキュリティ上の問題点についても検討し、皆さんの学習の旅を刺激することを期待している。