Hyperledger Fabric上での実践的chaincodeの書き方

Blockchain Strategy Centerの中村です。いきなりステーキダイヤモンドカードを目指しています。(現在約35,000g)

2回目の投稿ですが、今回こそBlockchainに関する記事です。

(前回投稿 React/Reduxで学ぶTypeScript応用編 )

TL;DR

  • Channel分割や、ユーザ証明書に属性を付与してACLを実現する

実践的なchaincodeとは

Hyperledger Fabricは複数社による共同運営を想定した、"コンソーシアム型" に分類されるブロックチェーンです。 chaincodeは、Hyperledger Fabric上におけるいわゆるSmartContractのことで、通常は台帳へのRead/Write操作が主要な処理になります。

公式の Tutorial にSampleなどまとまっておりますが、 台帳上に書かれたデータは基本的には参加する全ての組織・全てのユーザで共有されます。 一方で、実際のビジネスに適用する際には細かなデータへのアクセス制御を行いたいケースが発生します。

  • 本部 - 部 - 課など階層構造を持った組織において、上位の組織が保有するデータへのアクセスはできないようにしたい
  • ユーザの個人情報など秘匿すべきセキュアな情報は自社のみで管理したい
  • 特約を締結した組織間のみでのデータ共有を実現したい

上記の様なアクセス制御を実現するために外部システムに別のデータベースを用意するケースも存在しますが、 Hyperledger Fabricのアーキテクチャを最大限活用し、デフォルトの機能を用いて実現する事も可能です。 以下、本記事ではその方法を解説します。

ユーザ証明書に属性を付与してACLを実現する

Hyperledger Fabricでは各ユーザが台帳にアクセスするための証明書を、CAと呼ばれる認証局が発行しており、 権威の源泉が参加組織間の合意にある、という点が主要なパブリック型チェーンとの大きな違いです。

この証明書は X.509 の形式で発行され、Hyperledger Fabricでは、X.509のv3から用意された証明書の拡張領域に任意の属性を付与する事が可能です。 例として、一般ユーザ、中間管理者、管理者などユーザの権限にレベル設定を持たせるような運用が想定されます。

証明書への属性付与

コードはかなり端折りますが、SDKのユーザを登録する register methodには attrというフィールドが存在し、ここに任意のユーザ属性を指定する事が可能です。 下記は Node.js SDK (TypeScript) の例です。

const fabricCAClient = new FabricCAClient(
  caUrl,
  tlsOptions,
  caName,
  cryptoSuite
)

const secret = await fabricCAClient.register({
  enrollmentID: enrollId,
  affiliation: affiliation,
  role: 'client',
  attrs: [
    {name: 'userType', value: "user"}
  ]
}, certificatorContext);

この結果CAが発行した証明書を user1.cert という名前で保存していると仮定します。

cat user1.cert | openssl x509 -text -fingerprint -noout コマンドでdecodeすると、確かに拡張領域に属性が付与されています。

...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                14:61:8B:B1:6C:66:CB:63:7C:FE:99:B6:26:A0:27:AA:05:C2:CC:55
            X509v3 Authority Key Identifier:
                keyid:88:46:01:DB:D5:83:FC:D7:4E:8F:FC:72:B9:B6:78:5F:EE:29:CF:41:0F:26:6F:C6:AE:5A:D6:79:DE:F5:0D:87

            1.2.3.4.5.6.7.8.1:
                {"attrs":{"hf.Affiliation":"org1.department1","hf.EnrollmentID":"user1","hf.Type":"client","userType":"user"}}
...

さてこのユーザ属性をchaincodeから参照します。 ちなみにchaincodeはNode.js / Goで書くのが一般的ですが、Node.jsで記述した場合はchaincodeのinstantiate(≒deploy) 時に、 packageのinstallがノード内で実行される一方、goの場合はvendoringにより依存ファイルを固定して完全にコントロール可能なため、 Goで記述しています。

github.com/hyperledger/fabric/core/chaincode/lib/cid packageにある AssertAttributeValueGetAttributeValue を利用することで、chaincode内から証明書の属性を参照する事が可能です。 以下のように属性・ユーザに応じて自由にデータ参照を制御する事が可能です。

...
const LOGGER_NAME = "logger"

type CCImpl struct {
}

func (this *CCImpl) MethodForAdmin(stub shim.ChaincodeStubInterface) ([]*api.SomeData, error) {
    logger := shim.NewLogger(LOGGER_NAME)

    err := cid.AssertAttributeValue(stub, "userType", "administrator")
    if err != nil {
        logger.Error(err)
        return nil, errors.New("this user is NOT authorized to execute requested method.")
    }
        ...
}

証明書の属性による権限管理以外にも、自社だけにしか参照できない領域にデータを保存する PrivateData や, 台帳に記帳されない処理のための一時的なデータを渡す TransientMap などの機能も存在するため、 実現したい内容に応じて使い分けるのが好ましいかと思います。

Chaincodeから別のChannelのChaincodeを呼び出す

Hyperledger Fabricにはそもそも Channel というchaincodeと台帳の共有範囲を規定する管理単位があり、 特定組織間でのみ共有したいデータが存在する場合は、Channelの単位で分割することが望ましいです。

一方で、その様なChannel分割を行なった場合は、個々のChannel内で個別にデータを管理する事になるのですが、 丁度RDBでのトランザクションの様にチェーンコードから別のチェーンコードを呼び出したいケースも存在します。 SDKにも InvokeChaincode という、chaincode内から別のchaincodeを呼び出すメソッドが用意されており、channel間の相互運用が図られています。

func (this *CCImpl) InvokeAcrossChannel(stub shim.ChaincodeStubInterface) ([]byte, error) {
    args := []string{"MethodName", "Payload"}

    chaincodeName := "ccOnAnotherChannel"
    queryArgs := asBytes(args)
    channelName := "anotherChannel"

    response := stub.InvokeChaincode(chaincodeName, queryArgs, channelName)

    return response.GetPayload(), nil
}

おわりに

Getting Startedの様な位置付けの、初めの一歩のサンプルではあまり触れられていない、 実践的な使い方を解説しました。

Blockchain Strategy Centerではエンジニアを大絶賛募集中です。 Blockchain技術を利用したプロダクト開発にご興味を持たれた方は、ぜひ下記からご応募ください! その他の職種でもエンジニアを募集中ですので、どうぞよろしくおねがいします。

GA technologies 新卒・中途採用サイト