ブロックチェーンの課題と可能性~BBc-1(Beyond Blockchain One)から学ぶブロックチェーン開発

第11回 BBc-1のアプリケーションプログラミング

この記事を読むのに必要な時間:およそ 5.5 分

トランザクション登録

いよいよトランザクションの作成と登録です。トランザクションに含めるアセットの内容はアプリケーションごとさまざまですが,トランザクションのデータ構造の作り方はどんなアプリケーションでも共通です。今回の記事ではその共通部分について説明します。

file_proof.pyの中ではいくつかの関数でトランザクションを作成していますが,基本は292行目のstore_proc()関数です。

file_proof.py 292~327行目

def store_proc(file, txid=None):
    with open(file, "rb") as fin:
        data = fin.read()
    bbc_app_client = setup_bbc_client()

    store_transaction = bbclib.make_transaction(relation_num=1, witness=True)
    user_info = "Owner is %s" % user_name
    bbclib.add_relation_asset(store_transaction, relation_idx=0, asset_group_id=asset_group_id,
                              user_id=user_id, asset_body=user_info, asset_file=data)
    store_transaction.witness.add_witness(user_id)

    if txid:
        bbc_app_client.search_transaction(txid)
        response_data = bbc_app_client.callback.synchronize()
        if response_data[KeyType.status] < ESUCCESS:
            print("ERROR: ", response_data[KeyType.reason].decode())
            sys.exit(0)
        prev_tx, fmt_type = bbclib.deserialize(response_data[KeyType.transaction_data])
        bbclib.add_relation_pointer(transaction=store_transaction, relation_idx=0,
                                    ref_transaction_id=prev_tx.transaction_id)
    sig = store_transaction.sign(private_key=key_pair.private_key,
                                 public_key=key_pair.public_key)
    store_transaction.get_sig_index(user_id)
    store_transaction.add_signature(user_id=user_id, signature=sig)
    store_transaction.digest()
    print(store_transaction)

    ret = bbc_app_client.insert_transaction(store_transaction)
    assert ret
    response_data = bbc_app_client.callback.synchronize()
    if response_data[KeyType.status] < ESUCCESS:
        print("ERROR: ", response_data[KeyType.reason].decode())
        sys.exit(0)

    store_id_mappings(os.path.basename(file),
asset_group_id, transaction_id=response_data[KeyType.transaction_id],
                          asset_ids=store_transaction.relations[0].asset.asset_id)

store_proc()関数は,ファイルと過去のトランザクションIDを引数に取ります。ファイルは登録したいファイルで,トランザクションIDはファイルに変更を加えるときに変更前の情報をもつトランザクションIDです。ファイルを読んでdata変数(294行目)に情報を格納します。つぎにbbc_app_client = setup_bbc_client()で(295行目)⁠coreノードに接続します。setup_bbc_client()という関数は140行目で定義されています。

トランザクションの作成

処理の流れの詳細を説明していきます。 次の部分がトランザクションを作成する部分です。

file_proof.py 297~301行目

store_transaction = bbclib.make_transaction(relation_num=1, witness=True)
user_info = "Owner is %s" % user_name
bbclib.add_relation_asset(store_transaction, relation_idx=0, asset_group_id=asset_group_id,
                          user_id=user_id, asset_body=user_info, asset_file=data)
store_transaction.witness.add_witness(user_id)

make_transaction()というのはユーティリティ関数で,relation_numで指定した数だけBBcRelationオブジェクトを含み,BBcWitnessオブジェクトも含むようなトランザクションオブジェクトを作成します。さらに,add_relation_asset()関数は,第1,第2引数でトランザクションオブジェクトとその中のrelationsリストの何番目のBBcRelationオブジェクトなのかを指定して,実際にその中身を設定します。上記コードではrelation_idx=0の部分です。add_relation_asset()関数はさらに,BBcRelationオブジェクトの中にBBcAssetオブジェクトを含めます。user_id,asset_body,asset_fileという引数は,BBcAssetオブジェクト内に設定されます。 そして,witness.add_witness()関数では,BBcWitnessオブジェクトに引数で指定したuser_idの署名をトランザクションに付加することを宣言します。第5回から第7回で解説したトランザクションのデータ構造はこのコードで作成されています。

トランザクションの検索

file_proof.py 303~311行目

if txid:
    bbc_app_client.search_transaction(txid)
    response_data = bbc_app_client.callback.synchronize()
    if response_data[KeyType.status] < ESUCCESS:
        print("ERROR: ", response_data[KeyType.reason].decode())
        sys.exit(0)
    prev_tx, fmt_type = bbclib.deserialize(response_data[KeyType.transaction_data])
    bbclib.add_relation_pointer(transaction=store_transaction, relation_idx=0,
                                ref_transaction_id=prev_tx.transaction_id)

if txid:の条件分岐で,過去のトランザクションIDが指定されているときは,bbc_app_client.search_transaction(txid)によってcoreノードから過去のトランザクションを取得します。bbc_app_client.callback.synchronize()でcoreノードからの応答を待ち受けています。 この段階ではまだオブジェクトではなくデータの塊しか取得できていないため,bbclib.deserialize()関数を呼んでBBcTransactionオブジェクトに変換します。そして,得られた過去トランザクションを,bbclib.add_relation_pointer()関数を使って,新しいトランザクションのBBcRelationオブジェクト内にBBcPointerオブジェクトを追加します。このBBcPointerオブジェクトは,関連する過去のトランザクションIDやアセットIDを参照するためのものです。

ここまでで,トランザクションのうち署名以外の部分が完成します。

トランザクションへの署名付与

file_proof.py 312~313行目

sig = store_transaction.sign(private_key=key_pair.private_key,
                             public_key=key_pair.public_key)

あとは,sig = store_transaction.sign()を用いて署名オブジェクトsigを作成します。そのとき必ず自分の秘密鍵を指定するようにしてください。

file_proof.py 315行目

store_transaction.add_signature(user_id=user_id, signature=sig)

先のwitness.add_witness(user_id)という行(301行目)で,user_idというユーザの署名を付与する宣言をしていましたので,作成したsigオブジェクトをそのuser_idというユーザの署名としてトランザクションに付与します。なお,この例では,署名は1つしか付与していませんが,複数付与することも可能ですし,他のユーザの署名を付けても構いません。

file_proof.py 316~317行目

store_transaction.digest()
print(store_transaction)

の部分は,このトランザクションのトランザクションIDを計算したり,トランザクションの内容をテキストで表示したりするためのものです(デバッグ用出力です)⁠

トランザクションの登録と後処理

file_proof.py 319~327行目

ret = bbc_app_client.insert_transaction(store_transaction)
assert ret
response_data = bbc_app_client.callback.synchronize()
if response_data[KeyType.status] < ESUCCESS:
    print("ERROR: ", response_data[KeyType.reason].decode())
    sys.exit(0)

store_id_mappings(os.path.basename(file),
asset_group_id, transaction_id=response_data[KeyType.transaction_id],
                      asset_ids=store_transaction.relations[0].asset.asset_id)

完成したトランザクションは,ret = bbc_app_client.insert_transaction(store_transaction)によってcoreノードに登録されます。 if response_data[KeyType.status] < ESUCCESS:という条件分でエラー処理を行っています。

最後のstore_id_mappings()は,このfile_proof.py独自のやり方で,ファイル名やパスと,トランザクションIDをファイルにjson形式で書き出すユーティリティです。過去のトランザクションを取得するためには,トランザクションIDまたはアセットIDを指定する必要があるため,ファイル名とそれを登録したトランザクションのトランザクションIDのマッピングを記録しています。目的とする情報を含むトランザクションをどのように特定するかは,さまざまなアプリケーションに共通の課題です。file_proof.pyはサンプルアプリケーションであることから,store_id_mappings()で記録したマッピング情報を,get_id_from_mappings()関数で読み出すという簡易的な実装になっています。

まとめ

今回は,file_proof.pyに沿ってBBc-1プログラミングの概要を説明しました。トランザクションの検証方法,およびBBc-1において最も重要な「相手との合意」を実現する方法や「どのような内容のトランザクションを作成するか」については次回の記事で説明する予定です。

著者プロフィール

久保健(くぼたけし)

1977年生まれ,大阪出身。

株式会社ゼタント代表取締役/一般社団法人ビヨンドブロックチェーン理事。大手通信会社の研究所での研究,企画部門でのプロジェクトマネジメントなどを経て,独立。気がつけばBeyond Blockchain One(BBc-1)のメイン開発者になっていたが,あくまで趣味の位置づけ。本業では,技術者・研究者も報われる会社を目指す。