Starknet 署名に関するガイド
抽象的な
この記事では、Starknet での署名と署名の検証のプロセスの概要を説明します。まず、アカウントの抽象化を紹介し、イーサリアムなどの従来のブロックチェーンと比較して署名検証がどのように変更されるかについて説明します。次に、Starknet で利用可能な 2 つの方法 (ユーザーの公開キーを使用する方法とユーザーのアカウント アドレスを使用する方法) を使用してメッセージに署名し、署名を検証するための TypeScript と Go の包括的なコード例を示します。
ライブ署名プレイグラウンドは https://signatures.felts.xyz で利用できます
この記事に記載されているすべてのコード例は、関連する GitHub リポジトリで入手できます。コード スニペットに関して協力してくれた Thiago に感謝します。
アカウントの抽象化
イーサリアムでは、外部所有アカウント (EOA) として知られる個々のユーザー アカウントは、秘密鍵と公開鍵のペアによって制御されます。トランザクションでは、アカウントの状態を変更するために秘密キーからの署名が必要です。このシステムは安全ではありますが、秘密鍵が紛失または盗難された場合に資産が元に戻せないこと、ウォレットの機能が制限されていること、ユーザーフレンドリーな鍵やアカウントの回復オプションが欠如していることなど、重大な欠点があります。
Starknet は、秘密キーの代わりにスマート コントラクトを通じてアカウントを管理するアカウント抽象化 (AA) を通じてこれらの制限に対処します。このアプローチにより、スマート コントラクトがトランザクションを検証できるようになり、スマート コントラクトでカバーされるガス料金、単一アカウントの複数の署名者、さまざまな暗号署名などの機能が有効になります。 AA は、開発者が日常的な高額取引用の異なるキーやセキュリティを強化するための生体認証などのカスタム セキュリティ モデルを設計できるようにすることで、セキュリティとユーザー エクスペリエンスを強化します。また、ソーシャル リカバリやハードウェア ベースのトランザクション署名などの方法を使用して、キーのリカバリと管理を簡素化します。さらに、AA はキーのローテーション、Web3 アプリケーションのセッション キー、さまざまな署名と検証スキームをサポートしており、カスタマイズされたセキュリティ対策を可能にします。イーサリアムの EOA モデルに固有の制限に対処することで、Starknet の AA は、アカウント管理に対するより柔軟で安全かつユーザーフレンドリーなアプローチを提供し、ブロックチェーンの相互作用を大幅に改善します。
サイン
アカウントの抽象化を理解したので、それが署名の検証にどのように変化するかを調べることができます。まず、署名の構成を理解することが重要です。 STARK 曲線は楕円曲線であり、その署名は ECDSA 署名であり、r と s の 2 つの値で構成されます。署名は、秘密キーを使用してメッセージに署名することによって生成され、公開キーを使用して検証できます。 ECDSA 署名の詳細については、Wikipedia のページを参照してください。
メッセージに署名する
Starknet では、署名されるメッセージは通常、EIP-712 形式に従います。このメッセージ形式には、types、primaryType、domain、message の 4 つの必須フィールドが含まれます。 type フィールドは、型名を対応する型定義にマップします。 PrimaryType フィールドは、メッセージの主タイプを指定します。ドメイン フィールドには、チェーン コンテキストを指定するキーと値のペアが含まれます。メッセージ フィールドには、メッセージを説明するキーと値のペアが含まれます。通常、メッセージは JSON オブジェクトとして表されます:
{ types: { StarkNetDomain: [ { name: "name", type: "felt" }, { name: "chainId", type: "felt" }, { name: "version", type: "felt" }, ], Message: [{ name: "message", type: "felt" }], }, primaryType: "Message", domain: { name: "MyDapp", chainId: "SN_MAIN", version: "0.0.1", }, message: { message: "hello world!", }, }
メッセージに署名するには、秘密キーが必要です。署名プロセスを詳しく理解するには、ECDSA 署名アルゴリズムを参照してください。以下はメッセージに署名するコードです。
TypeScript:
import { ec, encode, TypedData, Signer, typedData, WeierstrassSignatureType } from 'starknet'; //-------------------------------------------------------------------------- // Account //-------------------------------------------------------------------------- const privateKey = '0x1234567890987654321'; const starknetPublicKey = ec.starkCurve.getStarkKey(privateKey); const fullPublicKey = encode.addHexPrefix( encode.buf2hex(ec.starkCurve.getPublicKey(privateKey, false)) ); const pubX = starknetPublicKey const pubY = encode.addHexPrefix(fullPublicKey.slice(68)) //-------------------------------------------------------------------------- // Message //-------------------------------------------------------------------------- const messageStructure: TypedData = { types: { StarkNetDomain: [ { name: "name", type: "felt" }, { name: "chainId", type: "felt" }, { name: "version", type: "felt" }, ], Message: [{ name: "message", type: "felt" }], }, primaryType: "Message", domain: { name: "MyDapp", chainId: "SN_MAIN", version: "0.0.1", }, message: { message: "hello world!", }, }; const messageHash = typedData.getMessageHash(messageStructure, BigInt(starknetPublicKey)) //-------------------------------------------------------------------------- // Signature //-------------------------------------------------------------------------- const signer = new Signer(privateKey) let signature: WeierstrassSignatureType; try { signature = (await signer.signMessage(messageStructure, starknetPublicKey)) as WeierstrassSignatureType } catch (error) { console.error("Error signing the message:", error); } // signature has properties r and s
行きます:
package main import ( "fmt" "math/big" "strconv" "github.com/NethermindEth/starknet.go/curve" "github.com/NethermindEth/starknet.go/typed" "github.com/NethermindEth/starknet.go/utils" ) // NOTE: at the time of writing, starknet.go forces us to create a custom // message type as well as a method to format the message encoding since // there is no built-in generic way to encode messages. type MessageType struct { Message string } // FmtDefinitionEncoding is a method that formats the encoding of the message func (m MessageType) FmtDefinitionEncoding(field string) (fmtEnc []*big.Int) { if field == "message" { if v, err := strconv.Atoi(m.Message); err == nil { fmtEnc = append(fmtEnc, big.NewInt(int64(v))) } else { fmtEnc = append(fmtEnc, utils.UTF8StrToBig(m.Message)) } } return fmtEnc } func main() { //-------------------------------------------------------------------------- // Account //-------------------------------------------------------------------------- privateKey, _ := new(big.Int).SetString("1234567890987654321", 16) pubX, pubY, err := curve.Curve.PrivateToPoint(privateKey) if err != nil { fmt.Printf("Error: %s\n", err) return } if !curve.Curve.IsOnCurve(pubX, pubY) { fmt.Printf("Point is not on curve\n") return } starknetPublicKey := pubX // IMPORTANT: this is not a standard way to retrieve the full public key, it // is just for demonstration purposes as starknet.go does not provide a way // to retrieve the full public key at the time of writing. // Rule of thumb: never write your own cryptography code! fullPublicKey := new(big.Int).SetBytes(append(append( []byte{0x04}, // 0x04 is the prefix for uncompressed public keys pubX.Bytes()...), pubY.Bytes()...), // concatenate x and y coordinates ) //-------------------------------------------------------------------------- // Message //-------------------------------------------------------------------------- types := map[string]typed.TypeDef{ "StarkNetDomain": { Definitions: []typed.Definition{ {Name: "name", Type: "felt"}, {Name: "chainId", Type: "felt"}, {Name: "version", Type: "felt"}, }, }, "Message": { Definitions: []typed.Definition{ {Name: "message", Type: "felt"}, }, }, } primaryType := "Message" domain := typed.Domain{ Name: "MyDapp", ChainId: "SN_MAIN", Version: "0.0.1", } message := MessageType{ Message: "hello world!", } td, err := typed.NewTypedData(types, primaryType, domain) if err != nil { fmt.Println("Error creating TypedData:", err) return } hash, err := td.GetMessageHash(starknetPublicKey, message, curve.Curve) if err != nil { fmt.Println("Error getting message hash:", err) return } //-------------------------------------------------------------------------- // Signature //-------------------------------------------------------------------------- r, s, err := curve.Curve.Sign(hash, privateKey) if err != nil { fmt.Println("Error signing message:", err) return } }
dApp を開発している場合は、ユーザーの秘密キーにアクセスできません。代わりに、starknet.js ライブラリを使用してメッセージに署名できます。コードはブラウザ ウォレット (通常は ArgentX または Braavos) と対話して、メッセージに署名します。ライブデモは https://signatures.felts.xyz でご覧いただけます。以下は、ブラウザーウォレットを使用して TypeScript でメッセージに署名するための簡略化されたコードです (完全なコードは GitHub リポジトリで入手可能です):
import { connect } from "get-starknet"; const starknet = await connect(); // Connect to the browser wallet const messageStructure: TypedData = { types: { StarkNetDomain: [ { name: "name", type: "felt" }, { name: "chainId", type: "felt" }, { name: "version", type: "felt" }, ], Message: [{ name: "message", type: "felt" }], }, primaryType: "Message", domain: { name: "MyDapp", chainId: "SN_MAIN", version: "0.0.1", }, message: { message: "hello world!", }, }; // skipDeploy allows not-deployed accounts to sign messages const signature = await starknet.account.signMessage(messageStructure, { skipDeploy: true });
メッセージが署名されると、r、s、v の形式で署名が取得されます。v の値は回復 ID であり、署名から公開キーを回復するために使用できます (詳細については Wikipedia を参照してください) )。ただし、署名者の公開鍵が事前にわかっていない限り、この回復プロセスは署名の検証として完全に信頼することはできません。 r と s の値は、署名を検証するために使用される署名値です。
重要: ブラウザーのウォレットによっては、署名は r 値と s 値のみを返す場合があります。 v 値は常に提供されるわけではありません。
Verifying a Signature
To verify a signature, the public key is required from a cryptographic perspective. However, due to Account Abstraction in Starknet, access to the public key is not always available. Currently, the public key cannot be retrieved through the browser wallet. Therefore, two methods are distinguished for verifying a signature: using the user's public key (if available) or using the user's address (i.e., account smart contract address).
Using the User's Public Key
If the user's public key is available, the signature can be verified using the public key. Here is the code to verify a signature.
TypeScript:
// following the previous code const isValid = ec.starkCurve.verify(signature, messageHash, fullPublicKey)
Go:
// following the previous code isValid := curve.Curve.Verify(hash, r, s, starknetPublicKey, pubY)
Using the User's Address
NOTE: This method works only if the user's account smart contract has been deployed (activated) on the Starknet network. This deployment is typically done through the browser wallet when the user creates an account and requires some gas fees. The skipDeploy parameter is specified in the JavaScript code when signing with the browser wallet. The example code provided earlier will not work with signatures different from the browser wallet since a sample private key was used to sign the message.
IMPORTANT: Avoid using your own private key when experimenting with the code. Always sign transactions with the browser wallet.
If the user's public key is not available, the signature can be verified using the user's account smart contract. By the standard SRC-6, the user account smart contract has a function fn is_valid_signature(hash: felt252, signature: Array
TypeScript (simplified for readability):
import { Account, RpcProvider } from "starknet"; const provider = new RpcProvider({ nodeUrl: "https://your-rpc-provider-url" }); // '0x123' is a placeholder for the user's private key since we don't have access to it const account = new Account(provider, address, '0x123') try { // messageStructure and signature are obtained from the previous code when signing the message with the browser wallet const isValid = account.verifyMessage(messageStructure, signature) console.log("Signature is valid:", isValid) } catch (error) { console.error("Error verifying the signature:", error); }
Go (simplified for readability):
import ( "context" "encoding/hex" "fmt" "math/big" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/starknet.go/curve" "github.com/NethermindEth/starknet.go/rpc" "github.com/NethermindEth/starknet.go/utils" ) ... provider, err := rpc.NewProvider("https://your-rpc-provider-url") if err != nil { // handle error } // we import the account address, r, and s values from the frontend (typescript) accountAddress, _ := new(big.Int).SetString("0xabc123", 16) r, _ := new(big.Int).SetString("0xabc123", 16) s, _ := new(big.Int).SetString("0xabc123", 16) // we need to get the message hash, but, this time, we use the account address instead of the public key. `message` is the same as the in the previous Go code hash, err := td.GetMessageHash(accountAddress, message, curve.Curve) if err != nil { // handle error } callData := []*felt.Felt{ utils.BigIntToFelt(hash), (&felt.Felt{}).SetUint64(2), // size of the array [r, s] utils.BigIntToFelt(r), utils.BigIntToFelt(s), } tx := rpc.FunctionCall{ ContractAddress: utils.BigIntToFelt(accountAddress), EntryPointSelector: utils.GetSelectorFromNameFelt( "is_valid_signature", ), Calldata: callData, } result, err := provider.Call(context.Background(), tx, rpc.BlockID{Tag: "latest"}) if err != nil { // handle error } isValid, err := hex.DecodeString(result[0].Text(16)) if err != nil { // handle error } fmt.Println("Signature is valid:", string(isValid) == "VALID")
Usage
Signatures can be used in various applications, with user authentication in web3 dApps being a primary use case. To achieve this, use the structure provided above for signature verification using the user's account address. Here is the complete workflow:
- The user signs a message with the browser wallet.
- Send the user address, message, and signature (r, s) to the backend.
- The backend verifies the signature using the user's account smart contract.
Make sure that the message structure is the same on the frontend and backend to ensure the signature is verified correctly.
Conclusion
I hope that this article provided you with a comprehensive understanding of the signatures on Starknet and helped you implement it in your applications. If you have any questions or feedback, feel free to comment or reach out to me on Twitter or GitHub. Thank you for reading!
Sources:
- https://book.starknet.io/ch04-00-account-abstraction.html
- https://www.starknetjs.com/docs/guides/signature/
- https://docs.starknet.io/architecture-and-concepts/accounts/introduction/
- https://docs.openzeppelin.com/contracts-cairo/0.4.0/accounts#isvalidsignature
- https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
- https://eips.ethereum.org/EIPS/eip-712
以上がStarknet 署名に関するガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











Golangは、パフォーマンスとスケーラビリティの点でPythonよりも優れています。 1)Golangのコンピレーションタイプの特性と効率的な並行性モデルにより、高い並行性シナリオでうまく機能します。 2)Pythonは解釈された言語として、ゆっくりと実行されますが、Cythonなどのツールを介してパフォーマンスを最適化できます。

Golangは並行性がCよりも優れていますが、Cは生の速度ではGolangよりも優れています。 1)Golangは、GoroutineとChannelを通じて効率的な並行性を達成します。これは、多数の同時タスクの処理に適しています。 2)Cコンパイラの最適化と標準ライブラリを介して、極端な最適化を必要とするアプリケーションに適したハードウェアに近い高性能を提供します。

goisidealforforbeginnersandsutable forcloudnetworkservicesduetoitssimplicity、andconcurrencyfeatures.1)installgofromtheofficialwebsiteandverify with'goversion'.2)

Golangは迅速な発展と同時シナリオに適しており、Cは極端なパフォーマンスと低レベルの制御が必要なシナリオに適しています。 1)Golangは、ごみ収集と並行機関のメカニズムを通じてパフォーマンスを向上させ、高配列Webサービス開発に適しています。 2)Cは、手動のメモリ管理とコンパイラの最適化を通じて究極のパフォーマンスを実現し、埋め込みシステム開発に適しています。

speed、効率、およびシンプル性をspeedsped.1)speed:gocompilesquilesquicklyandrunseffictient、理想的なlargeprojects.2)効率:等系dribribraryreducesexexternaldedenciess、開発効果を高める3)シンプルさ:

GolangとPythonにはそれぞれ独自の利点があります。Golangは高性能と同時プログラミングに適していますが、PythonはデータサイエンスとWeb開発に適しています。 Golangは同時性モデルと効率的なパフォーマンスで知られていますが、Pythonは簡潔な構文とリッチライブラリエコシステムで知られています。

GolangとCのパフォーマンスの違いは、主にメモリ管理、コンピレーションの最適化、ランタイム効率に反映されています。 1)Golangのゴミ収集メカニズムは便利ですが、パフォーマンスに影響を与える可能性があります。

GolangとCにはそれぞれパフォーマンス競争において独自の利点があります。1)Golangは、高い並行性と迅速な発展に適しており、2)Cはより高いパフォーマンスと微細な制御を提供します。選択は、プロジェクトの要件とチームテクノロジースタックに基づいている必要があります。
