Flow Go SDK と Cadence で署名検証する

Ara
Flow Japan
Published in
10 min readJun 15, 2021

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_256crypto.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(...)

--

--

Ara
Flow Japan

ソフトウェアエンジニア。生物学、民俗学、仏教、神道、メディアアート、博物学、フォント、ブロックチェーンなどに興味あり。