このタスクでは、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 つの配列に分割して渡す場合、[3, 'TAVERN', 2, 'OPEN', 'PORTAL']
と ['PORTAL', 'HOME', 2, 'CLOSE', 'PORTAL','VOID', 1 , 'ANY']
になります。'PORTAL' を felt の 10 進数に変換すると非常に大きな数になり、details の長さを満たすために details を長くするか、さらに多くの portal を追加する必要があります。テスト実行時にエラーが発生します。
2 番目の配列の長さが非常に小さい数字になるまで、Portal の数を増やし続けると、多くの Gas を消費せずに済みます。次の手順はわかるはずです。
テストに合格した後、ハックコントラクトをデプロイして解読する準備をします。starkli invoke を使用しても配列を渡すことはできませんが、snforge invoke --calldata
を使用すると calldata を渡すことができますが、アカウントなどの追加のパラメータをどのように渡すかわかりません。最終的には、2 つの calldata を直接 Voyager ブラウザで送信する方法が最も簡単だとわかりました。
結論#
StarkNet の calldata の構成ルールは非常にシンプルです。この問題の難しいところは、特定の calldata を構成するためにさらに多くの Portal を追加する必要があることです。非常に興味深い CTF 問題です。