契約アカウントには 3 つのサブタスクがあり、これらは bad account シリーズを構成しています。この記事はシリーズの 2 番目のタスクである Bendy Signatures です。
分析#
問題には 2 つの契約があり、sphinx は抽象アカウント契約であり、アカウントが gates 契約の open メソッドを呼び出します。sphinx には多くの制限があり、3 つの公開鍵が与えられますが、そのうちの 1 つは秘密鍵を持っています。問題は 2/3 マルチサインの問題をシミュレートしており、2 つの署名が認証に成功すると検証トランザクションを送信する必要があります。
アカウント契約の__validate__
では、starknet::get_tx_info().unbox()
を使用してトランザクションの署名を取得し、長さ 6 の raw_sig を構築する必要があります。raw_sig [0] と raw_sig [3] は公開鍵、raw_sig [1] と raw_sig [4] は署名の r、raw_sig [2] と raw_sig [5] は署名の s です。公開鍵は事前に指定された 3 つの公開鍵のいずれかである必要があり、r と s は重複してはなりません。同時に、__execute__
ではcalls.len() == 1
が制限されているため、唯一の秘密鍵で 2 つのトランザクションを生成して解決することはできません。
1 つの秘密鍵しかないため、この秘密鍵を使用して 2 つの r と s の組を生成し、両方が検証に合格し、重複しないようにする必要があります。問題は、SDK を使用してトランザクションを送信する方法であり、有効な署名が 2 つあることです。
手順#
最初に、Starknet.js で 2 つの異なる maxFee を使用して 2 つの open トランザクションを生成し、それぞれに署名を付けてもテストネットで検証できないことがわかりました。
チューターに問い合わせた後、この問題を解決するための実際の攻略方法は ECDSA の可変性の問題であることがわかりました。詳細については、ZK bookを参照してください。
私は ECDSA の学習に時間を費やしましたが、それには多くの数学的な知識が関わっています。
ECDSA の原理を知らない場合は、深く学習することをお勧めします。上記の ZK Book の内容は素晴らしいです。
StarkNet も ECDSA を使用して署名を生成するため、ChatGPT の出力する ECDSA の署名プロセスは次のとおりです:
1. 最初に、ランダムに選択された整数であるプライベートキーが必要です。同時に、公開鍵も必要であり、これはプライベートキーと基準点Gの積であるQ = dGとして表されます。ここで、dはプライベートキーであり、Gは基準点です。
2. メッセージに署名する場合、まずメッセージをハッシュ関数を使用して整数に変換します。これをzとします。
3. 次に、ランダムな整数kを選択し、点R = kGを計算します。Rのx座標は署名の一部であり、rとして表されます。
4. 次に、s = (z + r * d) / kを計算します。このsは署名のもう一つの部分です。
最終的な署名は(r, s)です。
それぞれのステップに対応する Starknet.js のコードは次のとおりです:
ec.starkCurve.getStarkKey(privateKey)
を使用して公開鍵を取得します。公開鍵は既知のため、このステップをスキップできます。hash.calculateTransactionHash
を使用してトランザクションの詳細を messageHash に変換します。トランザクションに必要な 6 つのパラメータは自分で設定する必要がありますが、calldata はtransaction.getExecuteCalldata
を使用して取得できます。- r はランダムな数値であり、自分で設定する必要はありません。次のステップで直接取得できます。
ec.starkCurve.sign(msgHash, privateKey)
またはsigner.signTransaction
を使用して r と s を取得します。
上記の ECDSA の可変性の参考資料によれば、対称性のため、1 つの r には 2 つの s が対応し、両方の署名が有効になります。2 番目の s の計算は非常に簡単で、必要なもう 1 つのパラメータは SDK のソースコードで見つけることができます。最終的に生成される r1 == r2、s1 != s2 です。
もう 1 つの方法は、異なるシードを使用して 2 つの r を生成することです。Python ライブラリには対応するメソッドがあります。最終的に生成される r1 != r2、s1 != s2 です。
問題の要件に従って、長さ 6 のリストを構築し、account.invokeFunction
の signature に渡し、他の必要なパラメータを埋めて送信することで、タスクを完了することができます。
結論#
この問題は、マルチシグウォレット契約の動作方法を紹介する一方で、ECDSA の可変性の問題も関連しており、トランザクションの構成にも触れています。解決することができれば、非常に有益な経験になるでしょう。