自己紹介
古瀬 淳 @camlspotter / @camloeba
ダイラムダ株式会社CEO
25年くらい OCaml を書いている
会社紹介: ダイラムダ株式会社
Tezosブロックチェーン研究開発
- コア開発: ノードやプロトコルの実装
- アプリケーションへのコンサル業務
相場はやりません。
Agenda
Tezosとは
Tezosの歴史
Edoプロトコル技術紹介
- Ticket
- BLS12-381
- Sapling
- 匿名送金
Tezosとは
Tezos
第三世代パブリックブロックチェーン
Proof of Stake
オンチェーンガバナンス
形式検証を重視
関数型言語 OCaml による実装
スマートコントラクト
Proof of Stake
トークン保有量に比例したブロック生成権
Proof of Work のようなマイニング資源を必要としない
Tezos の LPoS: 「ベーキング」
Rasberry Pi 4 でさえ可能
生成権を委託し報酬を得ることもできる
手数料が高騰しない
オンチェーンガバナンス
オンチェーン投票により信任を得たハードフォーク
新技術をスムーズに投入する
プロトコル変更をコードとして提案
自動的にP2P配布
プロトコル上で採択するか投票
採択されれば自動的に移行
形式検証
ソフトウェアテストは重要だが完璧ではない
形式検証を使って数理的に安全性を証明していく
- 最新のソフトウェア検証技術
- 型システム、モデル検査、定理証明
- 多方面に検証を適用
- プロトコル、ノード、、コンパイラ、
スマートコントラクト…
OCaml
関数型プログラミング言語
スマートコントラクト
ブロックチェーン上でのプログラムの自動実行
Michelson: TezosのスマートコントラクトVM
「高レベル」スタックマシン
- 可変長整数
- No overflow
- 静的型
- “Never goes wrong” ™️
- データ型
- pair, sum, option, list, map
Tezos スマートコントラクト
マルチシグコントラクト in Michelson
parameter (or (unit %default)
(pair %main
(pair :payload
(nat %counter) # counter, used to prevent replay attacks
(or :action # payload to sign, represents the requested action
(lambda %operation unit (list operation))
(pair %change_keys # change the keys controlling the multisig
(nat %threshold) # new threshold
(list %keys key)))) # new list of keys
(list %sigs (option signature)))); # signatures
storage (pair (nat %stored_counter) (pair (nat %threshold) (list %keys key))) ;
code
{
UNPAIR ;
IF_LEFT
{ # Default entry point: do nothing
# This entry point can be used to send tokens to this contract
DROP ; NIL operation ; PAIR }
{ # Main entry point
# Assert no token was sent:
# to send tokens, the default entry point should be used
PUSH mutez 0 ; AMOUNT ; ASSERT_CMPEQ ;
SWAP ; DUP ; DIP { SWAP } ;
DIP
{
UNPAIR ;
# pair the payload with the current contract address, to ensure signatures
# can't be replayed accross different contracts if a key is reused.
DUP ; SELF ; ADDRESS ; CHAIN_ID ; PAIR ; PAIR ;
PACK ; # form the binary payload that we expect to be signed
DIP { UNPAIR @counter ; DIP { SWAP } } ; SWAP
} ;
# Check that the counters match
UNPAIR @stored_counter; DIP { SWAP };
ASSERT_CMPEQ ;
# Compute the number of valid signatures
DIP { SWAP } ; UNPAIR @threshold @keys;
DIP
{
# Running count of valid signatures
PUSH @valid nat 0; SWAP ;
ITER
{
DIP { SWAP } ; SWAP ;
IF_CONS
{
IF_SOME
{ SWAP ;
DIP
{
SWAP ; DIIP { DUUP } ;
# Checks signatures, fails if invalid
{ DUUUP; DIP {CHECK_SIGNATURE}; SWAP; IF {DROP} {FAILWITH} };
PUSH nat 1 ; ADD @valid } }
{ SWAP ; DROP }
}
{
# There were fewer signatures in the list
# than keys. Not all signatures must be present, but
# they should be marked as absent using the option type.
FAIL
} ;
SWAP
}
} ;
# Assert that the threshold is less than or equal to the
# number of valid signatures.
ASSERT_CMPLE ;
# Assert no unchecked signature remains
IF_CONS {FAIL} {} ;
DROP ;
# Increment counter and place in storage
DIP { UNPAIR ; PUSH nat 1 ; ADD @new_counter ; PAIR} ;
# We have now handled the signature verification part,
# produce the operation requested by the signers.
IF_LEFT
{ # Get operation
UNIT ; EXEC
}
{
# Change set of signatures
DIP { CAR } ; SWAP ; PAIR ; NIL operation
};
PAIR }
}
型があるので手書きできる
コントラクト記述高級言語
Michelsonへとコンパイルする高級言語(たち)
SCaml
弊社が開発したコンパイラ
- OCaml 完全下位互換
- OCaml がわかる人なら書ける
dune, Merlin, ocaml-lsp が使える
Tezosの新機能をすぐ足せる
- SCaml. Not Scam.
- https://scamlang.dev/
SCaml
マルチシグコントラクト in SCaml
open SCaml
type storage =
{ stored_counter : nat
; threshold : nat
; keys : key list
}
type parameter =
{ payload : payload
; sigs : signature option list
}
and payload =
{ counter : nat
; action : action
}
and action =
| Transfer of transfer
| Delegate of key_hash option
| Change_keys of change_keys
and transfer =
{ amount : tz
; dest : unit contract
}
and change_keys =
{ threshold : nat
; keys : key list
}
let main parameter storage =
(* pair the payload with the current contract address, to ensure signatures
can't be replayed accross different contracts if a key is reused. *)
let signature_target =
Obj.pack ( parameter.payload
, Contract.address Contract.self
, (match (Obj.unpack (Obj.pack (Chain_id "NetXdQprcVkpaWU")) : SCaml.chain_id option) with None -> failwith "failed" | Some x -> x)
(* Global.get_chain_id () varies in different networks *)
)
in
(* Check that the counters *)
assert (storage.stored_counter = parameter.payload.counter);
(* Compute the number of valid signatures *)
let nsigs = Loop.left (fun (nsigs, keys, sigs) ->
match keys, sigs with
| [], [] -> Right nsigs
| key::keys, Some sig_::sigs ->
(* Checks signatures, fails if invalid *)
assert (Crypto.check_signature key sig_ signature_target);
Left (nsigs +^ Nat 1, keys, sigs)
| key::keys, None::sigs ->
Left (nsigs, keys, sigs)
| _ ->
(* There were fewer signatures in the list
than keys. Not all signatures must be present, but
they should be marked as absent using the option type.
*)
assert false
) (Nat 0, storage.keys, parameter.sigs)
in
(* Assert that the threshold is less than or equal to the
number of valid signatures.
*)
assert (storage.threshold <= nsigs);
(* Increment counter and place in storage *)
let storage = { storage with stored_counter = storage.stored_counter +^ Nat 1 } in
(* We have now handled the signature verification part,
produce the operation requested by the signers. *)
match parameter.payload.action with
| Transfer { amount ; dest } -> (* Transfer tokens *)
[ Operation.transfer_tokens () amount dest ], storage
| Delegate key_hash_opt -> (* Change delegate *)
[ Operation.set_delegate key_hash_opt], storage
| Change_keys { threshold ; keys } -> (* Change set of signatures *)
[], { storage with threshold; keys }
普通に副作用のないOCamlのコード。
Tezosの歴史
Tezosの歴史
- 2014-08
- ホワイトペーパー発表
- 2017-03
- Tezos財団創設
- 2017-07
- ICOで250億円調達
- 2018-09
- Mainnet開始
Tezos プロトコルの歴史
2018-09: 003: 開始バージョン
2019-05: 004 Athens: PoS参加条件引下げ、ガスリミット引上げ
2019-10: 005 Babylon: 合意アルゴリズム Emmy+、投票方式の改良
2020-03: 006 Carthge: スマコンVM強化
2020-11: 007 Delphi: ガスコスト大幅削減
2021-02: 008 Edo: Ticket, BLS12_381, Sapling, 匿名送金
2021-0?: 009 F???: ???
Tezos 008 Edo
2021-02-13 (日本時間14日早朝)に移行
- Ticket
- BLS12-381 (ゼロ知識証明)
- Sapling と匿名送金
Ticket
Ticket
偽造できない「保存量」の発行が可能
- スマートコントラクトが自由に発行できる
- Ticket には発行者情報が付属、他人は偽造できない
- 量を保存する分割(split)、統合(join)しかできない
通貨や認証システムを簡単に間違いなく作れる
よくある Fungible Token 実装
- 各ユーザーの所持量を管理テーブル
- 発行総量を変更する特殊操作: Mint/Burn
- 所有者移転操作: 発行総量の保存チェック
Ticket による Fungible Token
- 所持量管理テーブルは不要
- Mint: ticket 値を生成するだけ
- Burn: もらった ticket を破棄するだけ
- 発行総量を保存しない移転操作は自動的に失敗する
Ticket による Fungible Token
- Mint: ticket を発行しユーザーコントラクトに送付
- ユーザーコントラクトはもらった ticket を保存
- ユーザーコントラクトは使用したい分をSPLITし発行者に送る
手元には残高のticketが残る。偽造できない - Burn: 発行者は受け取った ticket を破棄して
額面分のサービスをユーザーに対して行う - ユーザーコントラクトは ticket の一部を他人に譲渡できる
Ticket 応用
量保存性が必要な数値ならいろんなものに使える
- FT
- UTXO 的なものを実装できる
- NFT
- NFT の ID を乗せることができる。別ID の ticket は join できない。
- 投票
- 投票用紙の ticket を有権者に配って、返事付きで返してもらう
Ticket型と関連命令の追加
線形性がある型: α ticket
TICKET : α -> nat -> α ticket
(** Ticket の複製は不可能 (型エラーになる) *)
READ_TICKET : α ticket -> (address * α * nat) * α ticket
(** 発行者のアドレスを返す *)
SPLIT_TICKET : α ticket -> (nat * nat)
-> (α ticket * α ticket) option
(** 分割が正しくない場合は失敗する (None) *)
JOIN_TICKET : α ticket -> α ticket -> α ticket option
(** 発行者が異なると失敗する (None) *)
Ticket型の線型性
線形型: 一度しか使用できない値を表す型
Michelsonはスタック言語なので線形型そのものでは無い
- スタック上で
DUP
できない TICKET
命令でしか生成できない
定数は無い (定数をPUSH
できると偽造可能)- ブロックチェーン外にやり取りできない
高級言語では線形型が現れてくる:
BLS12-381 とゼロ知識証明
楕円曲線
楕円曲線上の格子点を「数」(素体 \(G\))として使う
整数\(a\) と\(G\)の生成元 \(P\) から点 \(aP\) は簡単に計算できるが、\(aP\) から \(a\) を計算するのは困難。
双線形写像
二つの楕円曲線上の点を取る「都合のいい」関数 \(e(aP, bQ)\)
\[e(aP, bQ) = e(P,Q)^{ab}\]
係数を左右に振り替えることができる。ペアリング検査
ペアリング検査による公開鍵署名
秘密鍵: \(\color{red}s\)
公開鍵: \({\color{blue}p} = {\color{red}{s}}P\)
メッセージ: \(\color{blue}m\)
署名: \({\color{blue}{\sigma}} = {\color{red}s}{\color{blue}m}Q\) ← \(\color{red}s\) を知っていないと作れない
\(e(P, {\color{blue}\sigma})\) と \(e({\color{blue}p}, {\color{blue}{m}}Q)\) を比較して同じなら:
\[e(P, {\color{blue}\sigma}) = e(P, {\color{red}s}{\color{blue}m}Q) = e({\color{red}s}P, {\color{blue}m}Q) = e({\color{blue}p}, {\color{blue}{m}}Q)\]
\(\color{red}s\)を知っている人の署名だとわかる
BLS12-381
匿名コイン Zcash の Sapling に使われている楕円曲線と
双線形写像の実装
二つの素体 \(G_1\) と \(G_2\) を使う (\(\#G_1 \ll \#G_2\))
009 Edo から Tezos/Michelson で使用可能に
BLS12-381 in Tezos
- 型
bls12_381_g1
: \(G_1\) の型
bls12_381_g2
: \(G_2\) の型
bls12_381_fr
: 係数の型- 計算
INT
:bls12_381_fr
からint
への型変換ADD
MUL
NEG
: \(G_1, G_2\) と係数の計算に対応- ペアリング検査命令
PAIRING_CHECK
: (bls12_381_g1 * bls_12_381_g2) list -> bool
\(e(p_1,q_1)*e(p_2,q_2)*..*e(p_n,q_n) \stackrel{?}= 1\)
\(e(p_1,q_1) = e(p_2,q_2) ~~\Leftrightarrow~~ e(p_1,q_q) * e(-p_2,q_2) = 1\)
BLS12-381 による署名検査
open SCaml
open BLS12_381
let [@entry] main _param _storage =
let g1_one (* G1 の生成元 P *) = G1Bytes "0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1" in
let g2_one (* G2 の生成元 Q *) = G2Bytes "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801" in
let s (* 秘密鍵 *) = FrBytes "0x012345" in
let p (* 公開鍵 = sP *) = let open G1 in g1_one * s in
let m (* メッセージ *) = FrBytes "0x789012" in
let mQ = let open G2 in g2_one * m in
let sg (* 署名 = smQ *) = let open G2 in mQ * s in
let a1 (* (P, sg) *) = (g1_one, sg) in
let a2 (* (-p, mQ) *) = (G1.(~-) p, mQ) in
(* ペアリング検査 e(P,-sg) & e(-p, mQ) = 1 *)
assert (BLS12_381.pairing_check [a1; a2]);
[], ()
チェーンは検査に特化している。データ作成の便利な命令はない。 本来はオフチェーンでやるべきこと。
BLS12-381 とゼロ知識証明
ゼロ知識証明
ある知識を知っていることを、
その知識そのものを公開せずに証明する。
例: 公開鍵署名
「自分はメッセージ \(m\) に対してこの署名 \(\sigma\) を
作成できる秘密鍵 \(s\) を知っている」
例: 匿名通貨
「自分は残高が \(n\) 以上あるUTXOの秘密鍵 \(s\) を持っている」
のでその一部 \(n\) を送金してほしい
BLS12-381 とゼロ知識証明
Tezos/Michelsonは証明の検証のみ提供
検証器とそのパラメータ、そこに提供する証明データの
作成は提供されていない
ZoKrates を利用可能
ゼロ知識証明ツールキット
- 証明したい性質を Python 風プログラムとして入力、
検証器コード(Solidity)とパラメータを得る - 性質を満たすデータを入力、証明データを得る
簡単な変更で Tezos/Michelson に使用可能
ZoKrates for Tezos
「ある数の平方根を知っている」
# ZoKrates での仕様
def main(private field a, field b) -> bool:
return a * a == b
ZoKrates -c bls12_381 -s g16
使ってコンパイル
$ zokrates compile -c bls12_381 -i root.zok
$ zokrates setup -s g16
$ zokrates export-verifier -s g16
\(337^2 = 113569\) の証明を作る
$ zokrates compute-witness -a 337 113569
$ zokrates generate-proof -s g16
出力データを Tezos/Michelson 向けに変換
ZoKrates for Tezos
「ある数の平方根を知っている」証明検証器
(* Tezos/Michelson *)
open SCaml
let [@entry] main
( ( ( input_x : bls12_381_fr), ( input_y : bls12_381_fr) ),
( ( proof_a : bls12_381_g1 ), ( proof_b : bls12_381_g2 ), ( proof_c : bls12_381_g1 ) ) )
()
=
let vk_a = G1Point ( "0x169d7633e3da4d413bf1918c412fc54c548ddf641a423f47b61ca883c0ba1b85f5ee13dd63d7c1661cc4fe2ca38f00e1", "0x065dbcfb2123f8258ae2b3cf92035485f621e55d433b1f251ad37c02ae2b3ec6a1658ae23bbc77649878ec0871a6d8f1" ) in
let vk_b = G2Point (( "0x1116f52efd0f128a0bd6be9042bec761332408e765609caae2b6f7805ab3287143a9bafeb94a7cdd6635fd1ee293c2cf", "0x16c58ffdfec9c8b7a4d3826e32a40f99e97bd237067971e474438078e8bca6ccbbee0870bef905972fe879030273dd67" ), ( "0x0dd3791f5adb64150c2e7082f23a214b7ef5aa97fd903e648637deb13c156061788756f74b75545c3453e10822012e6a", "0x037bef7f92e32e639cd632611077b121db404705da6a507b9b8d8adc08a38eba27b2d31b7b22e95a96e22e26660162d3" )) in
let vk_gamma = G2Point (( "0x1889d7bcf405c1932c7cc66ea8d353b34844769b50da863b3f0f11079371d2e6b7c10031e270761f136e2f4e52f16224", "0x00e3114ca5fa1c0af741154f059c94a4d2647481ba6071b97317587a937e21f048e5f85191ba2fbd0f3929121485f4e2" ), ( "0x16872517c4811f11bba424dc64fdb3e6f27455e736d369d04de53a3e51eb36d4be6686a07f8be5352c138aceea7c6357", "0x1182c943f4672b1293f79699f6747d874e805f57e8211aae940d3e31693619054e2be22cb99b177cfc092db80c1f7896")) in
let vk_delta = G2Point ( ( "0x095f3f088b18f2ab4e44df666560e101f4c83da739ce074527deb3fde2fcb25e2a19fe2a0f7c4f546ca7f904575875b9", "0x1394d62459f5e72373706504085111cae6afb88121e8f5707a9ff82daf97d300b9a20fc52a6ac7149dac1626d1e4d0ae" ), ( "0x0d108a1585684a6ccbc94bdcb37c133579866e5dabb75c77178927d38f59a687015995d7c6d7fb536ee9f76f7e8081b2", "0x16ec4958b2b639d08f0c1bbd4af8e9e9289280e84b7308d790da4707ceefe61c78b661fbd76f8be9bd365ddd9a5f0b25" )) in
let vk_gamma_a = G1Point ( "0x07f1710668da99b2b45e6392d5cd89db7a1d34eff180ead6c886d51bca063f8111a179640d5d2dfc209e0a92783fea07", "0x0b5a9436d5c1be84f88a4127c9a02d307c5858df52ce76ae0b6fbb5c0d855438419e06a1c37f633b306e9da44eaf345e" ) in
let vk_gamma_b = G1Point ( "0x18c1bf32977afa48e224b2209e9df000ea072af1cf06efc9e83cf9b156ff577e154da015b077626e2bab7a8300a087ea", "0x18dbc6bb77f98da0fc7e492c0624fd30b513ef5cfb54f2ed052216485052ba8a117f83a63e925b735fb6fe7c4c88e364") in
let vk_gamma_c = G1Point ( "0x102bae3361383e2cef7d4aed348743201f6fd5fd25f4400d0888451a5556cd980f311510e6457e36fe8f330947269539", "0x0cf8968b8d779e53c04632588e402ae5884cc49ee3988159ede6a292fb3b9cd1071eec4bf3fffe51b7325184cadeab1d") in
(* Compute vk_x as
(vk_gamma_b * input_x) + (vk_gamma_c * input_y) + vk_gamma_a
*)
let vk_x =
let open BLS12_381.G1 in
vk_gamma_b * input_x + vk_gamma_c * input_y + vk_gamma_a
in
let list =
let (~-) = BLS12_381.G1.(~-) in
[ (proof_a, proof_b);
(~- vk_x, vk_gamma);
(~- proof_c, vk_delta);
(~- vk_a, vk_b) ]
in
assert (BLS12_381.pairing_check list);
[], ()
詳しくは 記事 を。
Sapling
Sapling
Zcash の Sapling protocol を導入
スマートコントラクトでゼロ知識証明を使った
匿名トランザクションが可能に。
Tezos本体が匿名コインになるわけでは無い!!
Shielded tez
sapling_contract.tz
: 1 shielded tez = 1 tez の匿名コイン
コントラクト内のトランザクションは匿名化
このコントラクトをデプロイすればオレオレ匿名 tez を作れる
tezos-client sapling
コマンド
Sapling トランザクションを作成してコントラクトに適用
# 匿名アカウント作成
$ tezos-client sapling gen key <zname>
# 使用するスマートコントラクトを指定
$ tezos-client sapling use key <zname> for contract <contract>
# 匿名アドレスを作成
$ tezos-client sapling gen address <zname>
# 通常アカウントから匿名アドレスへの送金 1 tez = 1 shielded tez
$ tezos-client sapling shield <amount> from <name> to <zaddress>
# 匿名アドレスの残高照会
$ tezos-client sapling get balance for <zname> in contract <contract>
# 匿名送金の作成
$ tezos-client sapling forge transaction <amount> from <zname> to <zaddress> \
using <contract>
# 送金命令の送信
$ tezos-client sapling submit <file> from <account> using <contract>
GUIウォレットへの対応が待たれる
Sapling in Michelson
type n sapling_state (* 状態。 n は整数定数 *)
type n sapling_transaction (* 状態変更 *)
val SAPLING_EMPTY_STATE n : n sapling_state
(** 空の状態 *)
val SAPLING_VERIFY_UPDATE n :
n sapling_transaction -> n sapling_state -> (int * n sapling_state) option
(** State の元で transaction が valid であれば、
transaction を適用して新しい state を返す *)
Michelson レベルでは Sapling 関連データは抽象化され、
検証命令しか存在しない。
Transaction のためのゼロ知識証明はオフチェーンで作成。
ex. tezos-client sapling
コマンド
Sapling の応用
Sapling 命令は Shielded tez のためだけではなく、
匿名トランザクションを使った FT dApps の実現に利用できる
Sapling in SCaml
module Sapling : sig
type 'sz state = 'sz sapling_state
type 'sz transaction = 'sz sapling_transaction
val empty_state : 'sz -> 'sz state
val verify_update : 'sz transaction -> 'sz state -> (int * 'sz state) option
end
Michelson の型では 'sz
には整数が来る。
でも、定数だし、依存型というわけでもない。
多層バリアントを使って数値から型への変換を行う。
サイズ 8 の空 state を作るには:
Saplint.empty_state `n8 : [> `n8] state
Tezos 008 Edo
主な新機能:
- Ticket
- BLS12-381
- Sapling
他にも Keccak, SHA3 命令の追加、 PVSS などある
Tezos 009 F???
ベーキングアカウント
コントラクト呼び出し規約変更
ガス量最適化
などなど。現在プロトコル改定提案に向けてテスト中。
求人情報(予定)
プロジェクトマネージメントできる方
PM経験 + 英語 + OCaml + 関西圏
詳細は近日中に https://dailambda.jp で