Flow Go SDK と Cadence で署名検証する
Go 言語ビギナーの私ですが、Flow の SDK のうち、Go SDK は一番メンテがされていて機能も多いので、いよいよ触らなきゃなと思い、手を動かし始めた最近です。DJT 満足さんにオススメされた本を読んだりして勉強中です。
ということで今回は、Flow Go SDK の導入方法と、署名および署名検証のやり方について紹介します! Cadence を使って任意の文字列の署名を検証するスクリプトは、まとまった情報として出るのは初めてだと思います。
目次
- Flow Go SDK について
- Flow Go SDK のインストール
- 任意の文字列に署名する
- Cadence で署名を検証する
- Flow Go SDK で署名検証の Cadence スクリプトを実行する
- おわりに
Flow Go SDK について
Flow の SDK は、公式だと JavaScript と Go の 2 つが開発されています。Dapper Labs の開発者は Gopher が多い印象です(Cadence を除いて)。だいたい、必要な機能は最初に Go SDK に実装されて、そのあと JavaScript SDK に来てる感じがします。
Flow Go SDK を使うと、Flow のアクセスノードとのやり取りを簡単に行えます。この通信には gRPC プロトコルが使われおり、Flow アクセスノードの gRPC の API 仕様はここに書かれています。
Flow Go SDK の使い方は、公式ドキュメントを参照するのがよいです。ただし、事前に Cadence 言語の理解が必要だったり、説明が足りていなかったりはします。Flow Go SDK の GitHub リポジトリにあるサンプルコードはとても役に立ちます。
※ 一部、Cadence の破壊的変更の影響を受けている部分があるので注意
Flow Go SDK のインストール
Go 1.13 以上の環境にて(私は Go 1.16 を使っています)、下記コマンドでインストールできます。
# 注記: 2021.06.13 時点では、エラーでインストールできない. 回避策は後述go get github.com/onflow/flow-go-sdk
ただし、2021 年 6 月 13 日 現在、Cadence の仕様変更により正しくインストールができないので(詳細はこちらの PR 参照)、下記のように修正済みコミットからインストールする必要があります。
※ GOPATH モードではなくモジュールモードを使用してください。
go get github.com/onflow/flow-go-sdk@958fc05a220c18276590aa1848907776d0fe24a1
加えて、利用する機能にもよりますが、今回の署名検証の場合は、以下も追加でインストールが必要です。
go get github.com/onflow/flow/protobuf/go/flow
go get github.com/golang/protobuf
go get google.golang.org/grpc
任意の文字列に署名する
Flow Go SDK では、秘密鍵を使って署名する機能が提供されています。
コードはとてもシンプルで、下記のようになります。
ここに書いてある秘密鍵は、Flow CLI を使ってランダムに生成したものです。実際には、秘密鍵の管理には十分に注意を払ってください。
上記のコードには、次の import が必要です。
重要なのは、 flow.SignUserMessage(signer, messageHex)
の部分で、第 1 引数に signer オブジェクト、第 2 引数に署名対象のバイト列を渡します。ここで、 内部の処理としては、署名対象のバイト列の先頭には、32 バイトのタグが付与されます(詳細はこのあたりのコードを参照)。結果は、バイト列で返ってきます。
Flow は、署名アルゴリズム ECDSA P-256 または ECDSA secp256k1、ハッシュアルゴリズム SHA-3 256 または SHA-2 256 の組み合わせをサポートしています(詳細)。上記コードの秘密鍵は、Flow CLI を使って生成しました(詳細)。デフォルトでは、ECDSA P-256 と SHA-3 256 が使われますが、Flow CLI のキーペア生成コマンドの引数指定で変更することもできます。AWS KMS を使って鍵管理したい場合、現在のところ署名アルゴリズムには、AWS と Flow 両方がサポートする secp256k を使う必要があります。その場合は上記コードの crypto.ECDSA_256
をcrypto.ECDSA_secp256k1
に変更します(この場合、署名検証時の署名アルゴリズムにも secp256k を使う必要があります)。
Cadence で署名を検証する
Cadence のコードで署名検証できる、というのは、オンチェーンでアカウント本人の意思確認ができるということです。これは様々なユースケースに活用できるため、非常に重要です。
Flow では、Cadence 言語は、コントラクトを書くだけでなく、それを操作・参照するトランザクションとスクリプト(読み取り専用のトランザクション)を書くことにも使われます。今回の例では、署名検証を行うスクリプトを作成します。このスクリプトのコードを、署名メッセージとともに、 Flow アクセスノードに対して送信・実行して、署名検証を行います。
さっそく、コードを紹介します。
Cadence には、署名検証など暗号関連の処理を行うのための Crypto
という組み込みコントラクトが用意されています。詳細はここを参照。
Flow はネイティブの機能としてマルチシグに対応しています。アカウントには複数のキーペアを登録でき、署名および署名検証も、それら複数のキーペアで行うことができます。各キーの重み付けは、登録時に 0〜1000 の間の値で設定します。
マルチシグを考慮して、このスクリプトの引数は、公開鍵の配列、重み付の配列、署名の配列、署名対象のデータとなっています(ただ、今回の例はマルチシグではなく、1 つの秘密鍵だけで署名しているので、値が 1 つだけの配列を渡します)。
処理を説明すると、まず Crypto.KeyList()
で keyList のオブジェクトを作り、そこに、公開鍵・署名アルゴリズム・ハッシュアルゴリズム・重み付けのセットを複数登録します。次に Crypto.KeyListSignature
型の配列に、署名と重み付けのセットを登録し、最後に keyList.verify(<署名セット>, <署名対象のデータ>)
で、正しい署名かどうかを検証します。
署名および署名対象のデータは、バイト列(Cadence の UInt8
型の配列)で渡します。署名対象のデータは文字列であることが多いと思いますが、文字列からバイト列へは、Go の標準関数で変換します。また、Cadence の型への変換には、Flow Go SDK を利用します。
Flow Go SDK で署名検証の Cadence スクリプトを実行する
先ほど Flow Go SDK を使って署名した結果を、Cadence のスクリプトとともに、Flow アクセスノードに送信・実行します。
まず引数を用意します。先ほどの Cadence スクリプトの引数は、以下のようになっていました。
pub fun main(
rawPublicKeys: [String],
weights: [UFix64],
signatures: [String],
signedData: [UInt8]
): Bool
これらを、Go のコードの中で、以下のように用意します。
まず公開鍵です。privateKey
は先ほど秘密鍵の文字列から生成したオブジェクトで、これが持つ PublicKey()
関数を使って公開鍵(バイト列)を取得できます。Cadence スクリプトの引数には String
型の配列で渡すので、少々わずらわしいですが何度か変換して、最終的に Go SDK の cadence.Value
型の配列の中に、 cadence.String
型の値を 1 つ入れます。
次に重み付けですが、秘密鍵 1 つだけの場合は 1.0
を配列に 1 つだけ入れて渡せばよいです。こちらは、cadence.Value
型の配列の中に、 cadence.UFix64
型の値を 1 つ入れます。
次に署名です。先ほど取得したバイト列型の署名結果 signature
を文字列に変換します。cadence.Value
型の配列の中に、cadence.String
型の値を 1 つ入れます。
最後に、署名対象のデータです。 toUInt8Array()
という関数を作りました。以下のように、Go のバイト列を、Flow Go SDK で定義された Cadence 用 UInt8 型の配列に変換します(なんだか長ったらしいコードにみえるので、もっといいやり方があるかもしれません)。
長くなってきましたが・・・
いよいよ、これらの引数を使って、Cadence のスクリプトを実行します
以下のコードで、スクリプトを Flow アクセスノードに送信します。
ノードのホスト名・ポート番号を指定してクライアントを生成したのち、それを使って flowClient.ExecuteScriptAtLatestBlock(...)
でスクリプトを実行できます。ここで、script
は、Cadence スクリプトの文字列をバイト列にしたもので、var script = []byte(`import Crypto ...(略)...`)
で宣言しています(改行が含まれていても問題ありません)。今回実行するスクリプトの返り値は Bool
型なので、結果は Cadence の Bool 型で返ってきます。
今回は、ノードに Flow エミュレータを指定していますが、テストネット・メインネットのノードを指定しても同じ結果が返るはずです。Flow エミュレータ(Flow CLI に統合されています)の使い方については割愛します。ドキュメントはこのあたりです。別途、記事を書こうかなとは思ってます。
なお、これらのコードには、以下の import が必要となります。
おわりに
この記事では、Flow Go SDK を使って署名した結果を、Cadence のコードで検証する方法を紹介しました。
今回は、Cadence のスクリプトで検証を行いましたが、同じコードは、コントラクトにも記述できます。特定のアカウントの合意があれば処理を行わせるなど、実用的なユースケースがいろいろ考えられます。
これらのコードは GitHub で公開しています。ご参照ください。
今回と同じ処理の JavaScript SDK 版も、コードを書いたりしています。こちらも近々、記事を書こうと思ってます。
2021/06/30 追記:
Cadence の破壊的変更に伴い、コードを一部修正しました。
keyList.isValid(...) → keyList.verify(...)