このタスクでは、StarkNet の calldata に関するものです。calldata とは、関数呼び出し時に関数に渡されるデータを指します。StarkNet では、渡されるデータはさまざまなタイプになる可能性がありますが、最終的には複数の felt252 で構成されるスナップショットに変換されます。詳細はcall_contract_syscallを参照してください。
2 つの契約があり、PortalSpell 契約の cast はArray<PortalData>
を受け入れます。DrunkenMage 契約の cast は 2 つのArray<felt252>
を受け入れます。2 つの Array で構成される calldata とArray<PortalData>
の最初の 2 つの PortalData が同じである必要があります(契約コードで検証されています)。
struct PortalData {
location: felt252,
details: Array<felt252>
}
// 期待される関数のシグネチャ
cast(portal_data: Array<PortalData>)
// Mageの関数のシグネチャ
cast(origin: Array<felt252>, destination: Array<felt252>)
上記の cast はディスパッチャを介してクロスコントラクト呼び出しを行い、引数の一致を検証する必要はありません。
アプローチ#
まず、StarkNet 契約が calldata をどのように解析するかを理解する必要があります。ウォークスルーには非常に明確に説明されています。
エンコードする目標は Array<PortalData>
であり、値は
[ 0: { location: 'TAVERN', details: [ 'OPEN', 'PORTAL' ] }, 1: { location: 'HOME', details: [ 'CLOSE', 'PORTAL' ] } ]
Serde が実装されているため、struct をシリアライズできます。テストで印刷した結果、単一の struct のエンコードは次のようになります。
[DEBUG] TAVERN (raw: 0x54415645524e
[DEBUG] (raw: 0x2
[DEBUG] OPEN (raw: 0x4f50454e
[DEBUG] PORTAL (raw: 0x504f5254414c
2 つの PortalData を合わせて、前に 2 を追加すると、最終的なエンコードになります。
drunk_spell.cast(origin, destination)
は分けて渡されるため、 origin
と destination
の値を見つける必要があります。これにより、 Array<PortalData>
として解釈されると、必要な calldata を反映することができます。
検証する必要があるのは最初の 2 つの Portal のみであり、 ['TAVERN', 2, 'OPEN', 'PORTAL', 'HOME', 2, 'CLOSE', 'PORTAL']
である必要があります。
もし、 PortalData {location: 'VOID', details: ['ANY']}
を追加すると、エンコードは [3, 'TAVERN', 2, 'OPEN', 'PORTAL', 'HOME', 2, 'CLOSE', 'PORTAL','VOID', 1 , 'ANY']
になりますが、これも検証に合格します。
しかし、2 つの array に分割して渡す場合、 [3, 'TAVERN', 2, 'OPEN', 'PORTAL']
と ['PORTAL', 'HOME', 2, 'CLOSE', 'PORTAL','VOID', 1 , 'ANY']
になります。 'PORTAL' を felt の 10 進数に変換すると非常に大きな数値になり、details の長さを満たすために details を長くするか、さらに多くの portal を追加する必要があります。テスト実行時にエラーが発生します。
そのため、第二のパラメータの array の長さが非常に小さい数字になるまで、Portal の数を増やすことで、ガスをあまり消費せずに済むようにします。これで書く方法がわかるはずです。
テストに合格した後、ハック契約をデプロイして解読する準備をします。starkli invoke を使用しても配列を渡すことはできませんが、snforge invoke --calldata
を使用すると calldata を渡すことができますが、アカウントなどの追加のパラメータをどのように渡すかわかりません。最終的には、Voyager ブラウザを使用して 2 つの calldata を直接送信する方法が最も簡単だとわかりました。
結論#
StarkNet の calldata の構成ルールは非常にシンプルです。この問題の難しいところは、特定の calldata を構成するためにさらに多くの Portal を追加する必要があることです。非常に興味深い CTF 問題です。