SCamlによるTezosプログラミング#3 ウォレット基本操作

Tezos ブロックチェーン のためのスマートコントラクト記述言語 SCaml、チュートリアルの第3回目です。

今回はTezosブロックチェーンにアクセスするコマンドラインウォレット tezos-client の実習です。

このブログではSCamlを使ったTezosスマートコントラクトプログラミングについて少しずつ日本語で連載をしていきたいと思います。

(この内容は 2020-03-21 にやった SCaml ハンズオンの資料を手直ししたものです。)

やっと実習

はいやっとです。でも SCaml には…まだ触れません。あれ〜。

まずは Tezos ブロックチェーンにアクセスするための基本プログラムであるコマンドラインウォレット、 tezos-client の使い方を理解しないといけません。

Tezos ネット

Tezos には使用目的によって複数の異なるネットワークがあります:

mainnet
換金性のあるトークンが扱われている本番環境です。このネットワークに書き込みを行いたい場合は mainnet トークンを所持している必要があります。持っていない場合は仮想通貨交換所で購入などしなければいけません。
現在(2022-05)の mainnet で動作している Tezos のプロトコルのバージョンは 012 Ithaca (イタカ)といいます。
テストネット
失敗しても金銭的損害を受けずに安心してテストできるネットワークとして、テストネットがあります。テストネットのトークンは無料で入手できます。(でもお金には変えられません。残念。)
現在の Mainnet と同じ 012 Ithaca プロトコルが動いているテストネットを ithacanet といいます。 このチュートリアルではこの ithacanet を使います。

このテストネットの名前はTezos プロトコルのアップデートに伴って変わります。

Tezos ノード

本来の Tezos 開発では手元でノード(ブロックチェーンサーバのインスタンス)を動かして、それとお話しするのですが、時間がかかるのでこのチュートリアルではしません。RPCポートを公開している node を使わせてもらいます。この設定は check スクリプトで自動的に行われているはずです。念のため、もう一度 check を走らせてみてください:

$ cd docker-tezos-hands-on   (Git clone したディレクトリで作業)
$ ./check
(以下は出力例です)
Checking network connectivity inside Docker... Ok
Finding an accessible public Tezos RPC server...
Checking https://ithacanet.smartpy.io: Ok
Using --endpoint https://ithacanet.smartpy.io
Updated the config of ./tezos-client:
{ "base_dir": "/root/.tezos-client",
  "endpoint": "https://ithacanet.smartpy.io", "web_port": 8080,
  "confirmations": 0 }

Ok

ここで問題が出る場合は、第1回の記事を確認してください。(もしかするとスクリプトがアップデートされているかもしれません)

上の例では https://ithacanet.smartpy.io にあるパブリックRPCノードと通信する設定になっています。この設定は $HOME/.tezos-handson-client に書き込まれます。気になる人は実習後消してください。

tezos-client

Tezosノード、つまり Tezos の分散データベースにアクセスする基本プログラムが tezos-client です。

これは仮想通貨的には「ウォレット」と呼ばれます。tezos-clientは普通のウォレットのようなカッコイイGUIなどはありませんので、コマンドライン・ウォレットと言われます。見た目は貧弱ですが、全ての機能にコマンドラインからアクセスできるようになっている Tezos 公式のウォレットです。

Ithacanet のアカウントを作る

Tezos ネットで作業をするためにはアカウントと暗号通貨トークンが必要です。Mainnet ではお金を出してトークンを買わなければいけませんが、テストネットではタダで手に入ります(ヤッター)。

Faucet(蛇口) https://teztnets.xyz/ithacanet-faucet でテストネットのアカウントを取得し、設定 JSON ファイル ithacanet.json を保存してください。

./tezos-client activate account «アカウント名» with «JSONファイル名» でJSON ファイルの情報からアカウントを有効化できます。たとえば:

$ ./tezos-client activate account myself with ithacanet.json 

ダウンロードした JSON ファイルは `tezos-client` スクリプトと同じ場所に置いて上のコマンドを実行してください。

うまくいくと次のような画面になる:

$ ./tezos-client activate account myself with ithacanet.json 
Waiting for the node to be bootstrapped...
Current head: BMaE7CSn8Dqs (timestamp: 2022-05-19T07:32:45.000-00:00, validation: 2022-05-19T07:32:58.922-00:00)
Node is bootstrapped.
Operation successfully injected in the node.
Operation hash is 'onh8b2Qp1axyLTeu9CoJmHaSivgCFJu4x6XfWQVFGKnKNa4HkJ2'
Waiting for the operation to be included...

ここで止まりますが慌てないで。しばらく待っていると:

Operation found in block: BLT5MPJzySFVegM2SkzRSJJNcSpMxmmF5As2XJFmGnWdsPGGEgY (pass: 2, offset: 0)
This sequence of operations was run:
  Genesis account activation:
    Account: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
    Balance updates:
      commitment(btz1RY54ZjohSe73o8boe94L2GfXLXTeiNdaC) ... -ꜩ8029.299319
      tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ................ +ꜩ8029.299319

The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for onh8b2Qp1axyLTeu9CoJmHaSivgCFJu4x6XfWQVFGKnKNa4HkJ2 to be included --confirmations 1 --branch BLtjJjoYxEzgmo3FpKmfhaqQZMd44FWfMUSDnuKiqGf5dExsxE4
and/or an external block explorer.
Account myself (tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez) activated with ꜩ8029.299319.
$

何が起こったの?

接続先ノードがネットワーク上で最新の状態になっているか確認:

Waiting for the node to be bootstrapped...
Current head: BMaE7CSn8Dqs (timestamp: 2022-05-19T07:32:45.000-00:00, validation: 2022-05-19T07:32:58.922-00:00)
Node is bootstrapped.

アカウント有効化の命令(operation)をノードに送付。Operation識別子であるハッシュを得る:

Operation successfully injected in the node.
Operation hash is 'onh8b2Qp1axyLTeu9CoJmHaSivgCFJu4x6XfWQVFGKnKNa4HkJ2'
Waiting for the operation to be included...

この operation は Tezos ブロックチェーンの P2P ネットワークに広がっていきます:

https://gitlab.com/dailambda/images/-/raw/master/consensus3.svg

Operation \(O_1, O_2\) がユーザによりネットワークに放流され、P2Pにより伝搬されていく。

ネットワーク上にはブロック生成者V(Tezos では Baker といいます)がいて、operation を取り上げてブロックを作り、それを P2P ネットワークに放流します:

https://gitlab.com/dailambda/images/-/raw/master/consensus4.svg

\(V\) は \(O_1, O_2\) を使ってブロック B を作り、ネットワークに提出する。

https://gitlab.com/dailambda/images/-/raw/master/consensus5.svg

ブロック B は P2P によりネットワークに広がっていく。

このブロックがあなたが使ったノードに到達します:

Operation found in block: BLT5MPJzySFVegM2SkzRSJJNcSpMxmmF5As2XJFmGnWdsPGGEgY (pass: 2, offset: 0)

ここから先はレシート(receipt)といい、実際にデータベースにどんな変化があったのかを表示します。プロだと端から端まで読まないとダメなんだけど、まあ今回は要点だけ:

This sequence of operations was run:
  Genesis account activation:
    Account: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
    Balance updates:
      commitment(btz1RY54ZjohSe73o8boe94L2GfXLXTeiNdaC) ... -ꜩ8029.299319
      tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ................ +ꜩ8029.299319
...

この faucetアカウントには ꜩ8029 が割り当てられていて、activation によりこの金額が入ったアカウント tz1Uuf.. が利用できるようになりました。

Tezos ブロックチェーンは現在 BFT コンセンサスである Tendermint アルゴリズムを採用しています。このアルゴリズムでは、作られた直後のブロックは他のブロックに取って変わられる可能性がわずかながらあります。ブロックが完全に確定されるにはさらにもう一ブロック待つ必要があります。待つのであれば tezos-client wait for ... コマンドを使うようにとの情報が出ています:

The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for onh8b2Qp1axyLTeu9CoJmHaSivgCFJu4x6XfWQVFGKnKNa4HkJ2 to be included --confirmations 1 --branch BLtjJjoYxEzgmo3FpKmfhaqQZMd44FWfMUSDnuKiqGf5dExsxE4
and/or an external block explorer.

今回は価値のないテストトークンなので、気にせず先に進みましょう。最終的に "myself" という名前でアカウントがウォレットに登録されます:

Account myself (tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez) activated with ꜩ8029.299319.

Tezos ブロックチェーンで起こること

これはアカウントアクティベーションでしたが、基本的にどんなTezos DB書き込み操作も同じことが起こります:

  • 行いたい操作を operation にして放流
  • Baker が流れてきた operation を取り上げ、ブロックを作成、放流
  • ブロックが元のノードに伝わってきて operation が DBに書き込まれたことが確認できる
  • 合意形成によってブロックが覆される可能性はあるが、可能性はどんどん少なくなる

アカウントを確認

アカウントを確認しましょう。ウォレットが知っているコントラクト(アカウント)を表示します:

$ ./tezos-client list known contracts
myself: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez

myself は別名です。ウォレット内でしか通用しません。もしあなたが myself 以外の別名を使ったのならそれが出てくるはず。

残高は?

$ ./tezos-client get balance for myself
8029.299319 ꜩ

ブロックエクスプローラを使う

GUIを使ってアカウントやブロックチェーンの状態を調べることもできます。いくつかありますが、今回は TzStat を使ってみましょう。URL に ithacanet を忘れずに。忘れるとメインネットの情報になるよ:

https://gitlab.com/dailambda/images/-/raw/master/tzstats.png

一番上のエントリにアカウント名を入れて残高を確認してください。下の Other タブから関連した operation を眺めることもできます。今はアカウント有効化の operation があるはず。

新しいアカウントを作る

Faucet から得たのはトークンが予約されていた(擬似)ICOアカウントです。残高0の新しいアカウントを作ることもできます:

$ ./tezos-client gen keys myself2
$ ./tezos-client list known contracts
myself2: tz1h1CEBP7nYP8Rdt8Ahkni8qmCb3SrF5mGY
myself: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
$ ./tezos-client get balance for myself2
0 ꜩ

ブロックエクスプローラでこの新しいアカウント tz1.. を確認すると… 出ません。このアカウントはウォレット内で作られただけで、まだブロックチェーンには存在していないんです。このアカウントに対して送金を行うと初めてブロックチェーン上に出現します。

送金してみる

はじめに作ったアカウント myself から新しいアカウント myself2 に送金をしてみましょう:

$ ./tezos-client transfer 1 from myself to myself2 
Waiting for the node to be bootstrapped...
Current head: BKskcVZUySiA (timestamp: 2022-05-19T07:47:40.000-00:00, validation: 2022-05-19T07:47:47.244-00:00)
Node is bootstrapped.
Fatal error:
  The operation will burn ꜩ0.06425 which is higher than the configured burn cap (ꜩ0).
   Use `--burn-cap 0.06425` to emit this operation.

エラーが出ました。これは新しいアカウント myself2 をブロックチェーンに登録するためにはディスク領域が必要で、その費用、storage burnを支払う必要があります。Burn したいときは、その上限を --burn-cap で明示する必要があります。

--burn-cap は 0.06425 あれば良いらしいですが、上限値なので、テストネットだし、適当な大きい値(たとえば100)で構いません:

$ ./tezos-client transfer 1 from myself to myself2 --burn-cap 100
Waiting for the node to be bootstrapped...
Current head: BLuRDaj4tN82 (timestamp: 2022-05-19T07:48:55.000-00:00, validation: 2022-05-19T07:48:56.374-00:00)
Node is bootstrapped.
Estimated storage: no bytes added
Estimated gas: 1420.040 units (will add 100 for safety)
Estimated storage: 257 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'opR8qFcmiNsL1bYEstHYaKnZJAq9CKS38QdxiTq7FDxD859843V'
Waiting for the operation to be included...
Operation found in block: BLzjioteCmCY7Rxh7csbbp32AroijqqkJhH54qjY7h9oTtXGxVx (pass: 3, offset: 0)
This sequence of operations was run:
  Manager signed operations:
    From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
    Fee to the baker: ꜩ0.00036
    Expected counter: 10593029
    Gas limit: 1000
    Storage limit: 0 bytes
    Balance updates:
      tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ0.00036
      payload fees(the block proposer) ....... +ꜩ0.00036
    Revelation of manager public key:
      Contract: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
      Key: edpkvUCk1egM2Mn9aiR6TTMu9Yo3YBTusBKsrvw2scjAVFZS6NK3tV
      This revelation was successfully applied
      Consumed gas: 1000
  Manager signed operations:
    From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
    Fee to the baker: ꜩ0.000311
    Expected counter: 10593030
    Gas limit: 1521
    Storage limit: 277 bytes
    Balance updates:
      tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ0.000311
      payload fees(the block proposer) ....... +ꜩ0.000311
    Transaction:
      Amount: ꜩ1
      From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
      To: tz1h1CEBP7nYP8Rdt8Ahkni8qmCb3SrF5mGY
      This transaction was successfully applied
      Consumed gas: 1420.040
      Balance updates:
        tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ1
        tz1h1CEBP7nYP8Rdt8Ahkni8qmCb3SrF5mGY ... +ꜩ1
        tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ0.06425
        storage fees ........................... +ꜩ0.06425

The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for opR8qFcmiNsL1bYEstHYaKnZJAq9CKS38QdxiTq7FDxD859843V to be included --confirmations 1 --branch BLA3chcnErYg5BPcD6JPkN6WjfED6ekm2Hi2BLwgiuJv3tp1hGH
and/or an external block explorer.

クッソ長いレシートを貰いました。細かい説明を省いてまとめるとブロック生成者(the block proposer)に報酬を支払っています:

  • アカウント公開operationの報酬: the block proposer へ ꜩ0.00036
  • 送金operationの報酬: the block proposer へ ꜩ0.000311
  • 新アカウント保存領域費用: ꜩ0.06425 (これは燃やされる)
  • 元アカウントから新アカウントへの送金: myself2 へ ꜩ1

新アカウント保存領域にかかる storage 費用はネットワーク全体にかかる費用なので、特定bakerへの報酬ではなく、「燃やされて」しまいます。

https://i.pinimg.com/originals/97/62/ca/9762ca8d6c96a4ea940127e0c631091e.jpg

テレビで金を燃やすと言えば日本では赤瀬川原平だが、あれは偽札だった。フランスではセルジュ・ゲンズブール が生放送で本物の500フラン札を燃やした。

さてこれで新アカウントがブロックエクスプローラでも見れるはずです。元アカウントと新アカウントに関する operation も見てみるとちゃんと上の operation も見えるはず。

なお Faucetアカウントの有効化に fee や burn は必要ありません。Faucet アカウントはICOに参加した人のためのアカウント。すでに有効化などの費用は払っているとみなされます。

コマンドが長い…

./tezos-client ではコマンドの打ち間違いで損することを避けるためにわざとコマンドを冗長に英語っぽくしてあります。長くて覚えられないのは当然です。私もよく忘れます。忘れた時は、

$ ./tezos-client man

で全コマンドリストが、各コマンドについてだけ知りたければ、例えば、

$ ./tezos-client transfer --help
...
Block contextual commands (see option -block):
  transfer <qty> from <src> to <dst> [--fee <amount>] [-D --dry-run]
      [--verbose-signing] [--simulation] [--force] [-G --gas-limit <amount>]
      [-S --storage-limit <amount>] [-C --counter <counter>] [--arg <data>]
      [-q --no-print-source] [--minimal-fees <amount>]
      [--minimal-nanotez-per-byte <amount>]
      [--minimal-nanotez-per-gas-unit <amount>] [--force-low-fee]
      [--fee-cap <amount>] [--burn-cap <amount>] [--entrypoint <name>]
    Transfer tokens / call a smart contract.
    <qty>: amount taken from source in ꜩ
    <src>: name of the source contract
    <dst>: name/literal of the destination contract
    --fee <amount>: fee in ꜩ to pay to the baker
    -D --dry-run: don't inject the operation, just display it
    --verbose-signing: display extra information before signing the operation
    --simulation: Simulate the execution of the command, without needing any signatures.
    --force: Inject the operation even if the simulation results in a failure.
    -G --gas-limit <amount>: Set the gas limit of the transaction instead of letting the client decide based on a simulation
    -S --storage-limit <amount>: Set the storage limit of the transaction instead of letting the client decide based on a simulation
    -C --counter <counter>: Set the counter to be used by the transaction
    --arg <data>: argument passed to the contract's script, if needed
    -q --no-print-source: don't print the source code
    --minimal-fees <amount>: exclude operations with fees lower than this threshold (in tez)
    --minimal-nanotez-per-byte <amount>: exclude operations with fees per byte lower than this threshold (in nanotez)
    --minimal-nanotez-per-gas-unit <amount>: exclude operations with fees per gas lower than this threshold (in nanotez)
    --force-low-fee: Don't check that the fee is lower than the estimated default value
    --fee-cap <amount>: Set the fee cap
    --burn-cap <amount>: Set the burn cap
    --entrypoint <name>: entrypoint of the smart contract

とすればヘルプが表示されます。

演習

myself2 から myself へ ꜩ0.5 送ってみてください