Slide 1

Slide 1 text

Flow のリソース指向プログラミング言語 Cadence 入門 2020.04.02(04.12 更新) @avcdsld


Slide 2

Slide 2 text

本資料は 2020年4月2日12日 時点の情報を元に作成しています。 Flow および Cadence 言語は現在開発中であるため 仕様が大きく変更される可能性があります。

Slide 3

Slide 3 text

Flow:Dapper Labs が開発中のブロックチェーン 3 www.onflow.org

Slide 4

Slide 4 text

Flow の特徴 4 ブロックチェーンの処理を 4種類のノードに分割する ことで、ネットワークの パフォーマンスを改善 • トランザクション収集 • コンセンサス(PoS) • 計算 • 検証 https://medium.com/dapperlabs/enter-the-octagon-ufc-on-flow-brings-mma-to-crypto-480618408510 ※上記に加えて Observer ノードも存在する

Slide 5

Slide 5 text

Cadence:Flow で使われるプログラミング言語 5 • クリプト対応アプリケーションの新しいパラダイムのために 特別に設計された新しいプログラミング言語 • コードを 安全、セキュア、親しみやすくするための機能がある: ○ リソースオブジェクト ○ 強力な静的型システム ○ 関数とトランザクションにおける 事前/事後条件

Slide 6

Slide 6 text

Libra の Move 言語に発想を得ている 6 • リソースオブジェクト ○ 線形型 = 宣言したものは必ず使わなければならない → 増えたり、なくなったりしない → ブロックチェーン上の所有権の管理に最適 • トランザクションにスクリプトを書ける

Slide 7

Slide 7 text

リソース:新しい所有モデル

Slide 8

Slide 8 text

従来の所有モデル 8 中央台帳で所有権を管理する 例:すべての CryptoKitties は1つのコントラクトで管理されている コントラクト #1
 #2
 mapping (uint256 => address) :

Slide 9

Slide 9 text

従来の所有モデルの課題 9 • スケールできない(Sharding のボトルネックになる) • バグが生まれる(マップの書き換えミスが起こりうる) コントラクト #1
 #2
 mapping (uint256 => address) : コントラクト EOA : コントラクト 参照 更新 更新

Slide 10

Slide 10 text

リソース:新しい所有モデル 10 アカウントのストレージにリソースオブジェクトを配置する アカウント1 リソース1 #1 アカウント2 リソース2 #2

Slide 11

Slide 11 text

リソースのルール 11 1. 各リソースは、常に1つの場所に存在する 実装ミスや悪意のあるコードによって、 リソースを複製したり、誤って削除したりすることはない 2. リソースの所有権は、保存場所によって定義される 所有権を変えるために相談しなければならない中央台帳はない 3. リソースのメソッドへのアクセスは所有者に制限される 例えば、CryptoKitty の所有者のみが繁殖操作を行える これらが、言語の仕組みとして保証されることが素晴らしい!

Slide 12

Slide 12 text

リソースの利点 12 • 言語レベルで制限されたセキュアなコード(最小権限の原則) • 副次的な利点: ○ 所有権のカスタマイズが容易 (例えば EIP-998 は既存 ERC-721 に適用できない) ○ ストレージ利用料の徴収が容易 (Ethereum の State Rent は既存 ERC-721 に適用できない) ○ 再入性バグが発生しない

Slide 13

Slide 13 text

実際に触れてみる

Slide 14

Slide 14 text

チュートリアル と プレイグラウンド が充実! 14 https://www.onflow.org/play

Slide 15

Slide 15 text

プレイグラウンドをナイトモードにするプラグイン 15 Chrome: https://chrome.google.com/webstore/detail/flow-playground-enhancer/agjkjdemgkkmgdmeobefbmfiakkgkkdh FireFox: https://addons.mozilla.org/en-US/firefox/addon/flow-playground-enhancer/ https://twitter.com/MaxStalker/status/1246167263226585088

Slide 16

Slide 16 text

Flow におけるプログラムの種類 16 1. コントラクト 実行したアカウントのストレージにデプロイされる 2. トランザクション 実行したアカウントで、デプロイ済みコントラクトを操作する 3. スクリプト ストレージの状態を読む (実行したアカウントの署名は不要、状態は変更できない)  すべて .cdc ファイル

Slide 17

Slide 17 text

Flow でコントラクトを実行する流れ 17 1. コントラクトのコードを書く 2. コントラクトをデプロイする → 実行したアカウントのストレージにデプロイされる 3. トランザクションのコードを書く 4. トランザクションを実行する (実行したアカウントで署名することになる) → 結果は、ログ記録 and/or アカウントのストレージに保管される

Slide 18

Slide 18 text

アカウントのストレージの構成 18 アカウント Code Storage Published デプロイされたコントラクトが入る リソースオブジェクトが入る 自分しか操作できない リソースオブジェクトの参照が入る 誰でもみられる&参照で定義された操作ができる

Slide 19

Slide 19 text

コントラクトの書き方

Slide 20

Slide 20 text

コントラクトの書き方 20 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } 最もシンプルな NFT のコントラクト

Slide 21

Slide 21 text

コントラクトの書き方 21 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } コントラクトの定義 アクセス修飾子 contract コントラクト名 { … }

Slide 22

Slide 22 text

コントラクトの書き方 22 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } コントラクトデプロイ時の初期処理

Slide 23

Slide 23 text

コントラクトの書き方 23 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } リソースの定義 アクセス修飾子 resource リソース名 { … }

Slide 24

Slide 24 text

コントラクトの書き方 24 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } リソースオブジェクト生成時の初期処理

Slide 25

Slide 25 text

コントラクトの書き方 25 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } アクセス修飾子 all ≒ public(他に account, contract, self がある)

Slide 26

Slide 26 text

コントラクトの書き方 26 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } 変数の宣言。Swift に似ている let はイミュータブル、var はミュータブル 型: - Bool - Int8, Int16 …, Int256 - UInt8, UInt16 …, UInt256 - Word8, Word16, Word32, Word64 - Fix64 - UFix64 - Address - AnyStruct - AnyResource

Slide 27

Slide 27 text

コントラクトの書き方 27 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } ここからが Cadence の特徴的なところ!

Slide 28

Slide 28 text

コントラクトの書き方 28 access(all) contract NonFungibleToken { access(all) resource NFT { access(all) let id: UInt64 access(all) var metadata: {String: String} init(initID: UInt64) { self.id = initID self.metadata = {} } } init() { let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT } } NFT リソースのオブジェクトを生成して、 実行しているアカウントのストレージに保存する このとき、もともとこの場所にあるかもしれない リソースオブジェクトを押し出して変数に移動する

Slide 29

Slide 29 text

コントラクトの書き方 29 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1)

Slide 30

Slide 30 text

コントラクトの書き方 30 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) リソースオブジェクトを代入する(=)ときは この特別な移動演算子を使わなければならない 移動演算子を複数回使った式は、 右から左に向かって解釈される

Slide 31

Slide 31 text

コントラクトの書き方 31 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) アカウントのストレージ(ストレージの storage 領域)には リソースの型をキーとしたマップが用意されている リソースの型に応じた場所に、リソースオブジェクトを格納できる

Slide 32

Slide 32 text

コントラクトの書き方 32 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) リソースオブジェクトは上書きできないため、 移動演算子を使うときは、移動先にある(かもしれない) リソースオブジェクトを、明示的に他の場所に移動しなければならない

Slide 33

Slide 33 text

コントラクトの書き方 33 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) destroy oldNFT リソースオブジェクトを削除する構文 リソースは線形論理と呼ばれる考え方で設計されており、 プログラム内で宣言したオブジェクトは、 プログラム終了までに必ず消費(どこかに格納 or 削除)する必要がある

Slide 34

Slide 34 text

Storage[NFT] コントラクトの書き方 34 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1)

Slide 35

Slide 35 text

Storage[NFT] コントラクトの書き方 35 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1)

Slide 36

Slide 36 text

コントラクトの書き方 36 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) Storage[NFT]

Slide 37

Slide 37 text

コントラクトの書き方 37 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1) Storage[NFT]

Slide 38

Slide 38 text

NFT コントラクトを拡張してみる

Slide 39

Slide 39 text

先ほどのコントラクトだと、1つしか NFT を保持できない 39 Storage[NFT]

Slide 40

Slide 40 text

そこで、NFT を複数保持できるリソースを用意する 40 Storage[Collection] #1
 #2
 : ディクショナリ変数

Slide 41

Slide 41 text

NFT を保持する Collection リソース 41 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource Collection { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } destroy() { destroy self.ownedNFTs } } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } }

Slide 42

Slide 42 text

NFT を保持する Collection リソース 42 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource Collection { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } destroy() { destroy self.ownedNFTs } } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } ID と NFT リソースオブジェクトを保持するマップ

Slide 43

Slide 43 text

NFT を保持する Collection リソース 43 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource Collection { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } destroy() { destroy self.ownedNFTs } } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } リソースオブジェクトを入れる変数の型を書くときは 「@」をつけなければならない(視覚化が目的) ID と NFT リソースオブジェクトを保持するマップ リソースオブジェクトを入れる変数に代入するときは 移動演算子「<-」を使わなければならない(視覚化が目的)

Slide 44

Slide 44 text

NFT を保持する Collection リソース 44 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource Collection { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } destroy() { destroy self.ownedNFTs } } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } この Collection リソースはリソースオブジェクトを保 持しているため、自身が削除されるときには、 保持しているリソースオブジェクトを 明示的に削除または移動しなければならない

Slide 45

Slide 45 text

NFT を保持する Collection リソース 45 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource Collection { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } destroy() { destroy self.ownedNFTs } } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } }

Slide 46

Slide 46 text

NFT を送受信する機能を提供する 46 Storage[Collection] ・ディクショナリ変数 #1
 #2
 : ・withdraw() 関数 ・deposit() 関数

Slide 47

Slide 47 text

NFT を送受信する機能を提供する 47 Storage[Collection] ・ディクショナリ変数 #1
 #2
 : ・withdraw() 関数 ・deposit() 関数 Storage[Collection] 他のアカウントの ストレージは直接 変更できない!

Slide 48

Slide 48 text

NFT を送受信する機能を提供する 48 Storage[Collection] ・マップ変数 #1
 #2
 : ・withdraw() 関数 ・deposit() 関数 Storage[Collection] 他のアカウントの ストレージは直接 変更できない! どうするか?      

Slide 49

Slide 49 text

NFT を送受信する機能を提供する 49 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 そもそも、送付先アカウントに Collection のリソースオブジェクト がないので、まずこれを作るための 関数を用意する Storage[Collection]

Slide 50

Slide 50 text

NFT を送受信する機能を提供する 50 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code 送付先アカウントにこれを 呼んでもらって、ストレージ に空のリソースオブジェクト を作ってもらう createEmptyCollection() 関数 Storage[Collection]

Slide 51

Slide 51 text

NFT を送受信する機能を提供する 51 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code 送付先アカウントにこれを 呼んでもらって、ストレージ に空のリソースオブジェクト を作ってもらう (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 createEmptyCollection() 関数 Storage[Collection]

Slide 52

Slide 52 text

NFT を送受信する機能を提供する 52 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 ストレージのNFTを操作する ためのインターフェースを 用意する (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 Storage[Collection]

Slide 53

Slide 53 text

NFT を送受信する機能を提供する 53 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 このインターフェースを、 送付先アカウントの Published に入れてもらう Storage[Collection] Published 参照

Slide 54

Slide 54 text

NFT を送受信する機能を提供する 54 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 これで ようやく 準備完了 Storage[Collection] Published

Slide 55

Slide 55 text

NFT を送受信する機能を提供する 55 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 Storage[Collection] Published まず withdraw で NFT を引き出して…

Slide 56

Slide 56 text

NFT を送受信する機能を提供する 56 Storage[Collection] #1
 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 まず withdraw で NFT を引き出して… Storage[Collection] Published

Slide 57

Slide 57 text

NFT を送受信する機能を提供する 57 Storage[Collection] ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 送信先アカウントの Published にある deposit を実行する (空) Storage[Collection] Published

Slide 58

Slide 58 text

NFT を送受信する機能を提供する 58 Storage[Collection] ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 送信先アカウントの Published にある deposit を実行する (空) (空) Storage[Collection] Published

Slide 59

Slide 59 text

NFT を送受信する機能を提供する 59 Storage[Collection] ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code createEmptyCollection() 関数 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 Published NFTReceiver インターフェース - deposit() 関数 (空) 無事にNFTを送信できた

Slide 60

Slide 60 text

拡張した NFT コントラクトの全体 60 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource interface NFTReceiver { access(all) fun deposit(token: @NFT) } access(all) resource Collection: AnyResource{NFTReceiver} { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } access(all) fun withdraw(withdrawID: UInt64): @NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") return <-token } access(all) fun deposit(token: @NFT) { let oldToken <- self.ownedNFTs[token.id] <- token destroy oldToken } destroy() { destroy self.ownedNFTs } } access(all) fun createEmptyCollection(): @Collection { return <- create Collection() } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } }

Slide 61

Slide 61 text

61 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource interface NFTReceiver { access(all) fun deposit(token: @NFT) } access(all) resource Collection: AnyResource{NFTReceiver} { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } access(all) fun withdraw(withdrawID: UInt64): @NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") return <-token } access(all) fun deposit(token: @NFT) { let oldToken <- self.ownedNFTs[token.id] <- token destroy oldToken } destroy() { destroy self.ownedNFTs } } access(all) fun createEmptyCollection(): @Collection { return <- create Collection() } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } リソースのインターフェースの定義 アクセス修飾子 resource interface インターフェース名 { … } 拡張した NFT コントラクトの全体

Slide 62

Slide 62 text

62 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource interface NFTReceiver { access(all) fun deposit(token: @NFT) } access(all) resource Collection: AnyResource{NFTReceiver} { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } access(all) fun withdraw(withdrawID: UInt64): @NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") return <-token } access(all) fun deposit(token: @NFT) { let oldToken <- self.ownedNFTs[token.id] <- token destroy oldToken } destroy() { destroy self.ownedNFTs } } access(all) fun createEmptyCollection(): @Collection { return <- create Collection() } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } リソースのインターフェースの定義 アクセス修飾子 resource interface インターフェース名 { … } 関数の定義(インターフェース内は実装はなし) アクセス修飾子 fun 関数名 { … } 拡張した NFT コントラクトの全体

Slide 63

Slide 63 text

63 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource interface NFTReceiver { access(all) fun deposit(token: @NFT) } access(all) resource Collection: AnyResource{NFTReceiver} { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } access(all) fun withdraw(withdrawID: UInt64): @NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") return <-token } access(all) fun deposit(token: @NFT) { let oldToken <- self.ownedNFTs[token.id] <- token destroy oldToken } destroy() { destroy self.ownedNFTs } } access(all) fun createEmptyCollection(): @Collection { return <- create Collection() } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } リソース内の withdraw 関数と deposit 関数 拡張した NFT コントラクトの全体

Slide 64

Slide 64 text

64 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource interface NFTReceiver { access(all) fun deposit(token: @NFT) } access(all) resource Collection: AnyResource{NFTReceiver} { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } access(all) fun withdraw(withdrawID: UInt64): @NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") return <-token } access(all) fun deposit(token: @NFT) { let oldToken <- self.ownedNFTs[token.id] <- token destroy oldToken } destroy() { destroy self.ownedNFTs } } access(all) fun createEmptyCollection(): @Collection { return <- create Collection() } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } コントラクト内の createEmptyCollection 関数 (アクセス修飾子が access(all) なので、デプロイされて  いるアカウントから読み込むと誰でも実行できる) 拡張した NFT コントラクトの全体

Slide 65

Slide 65 text

65 access(all) contract NonFungibleToken { access(all) resource NFT { … } access(all) resource interface NFTReceiver { access(all) fun deposit(token: @NFT) } access(all) resource Collection: AnyResource{NFTReceiver} { access(all) var ownedNFTs: @{UInt64: NFT} init () { self.ownedNFTs <- {} } access(all) fun withdraw(withdrawID: UInt64): @NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") return <-token } access(all) fun deposit(token: @NFT) { let oldToken <- self.ownedNFTs[token.id] <- token destroy oldToken } destroy() { destroy self.ownedNFTs } } access(all) fun createEmptyCollection(): @Collection { return <- create Collection() } init() { let oldCollection <- self.account.storage[Collection] <- create Collection() destroy oldCollection } } 結果が空だったときにエラーにするための構文 拡張した NFT コントラクトの全体

Slide 66

Slide 66 text

トランザクションの書き方

Slide 67

Slide 67 text

トランザクションの書き方 67 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } 先ほどの NFT コントラクトで、送付先アカウントが最初に実行するトランザクション

Slide 68

Slide 68 text

トランザクションの書き方 68 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } はじめに、デプロイ済みコントラクトを、 デプロイされているアカウントから読み込む import コントラクト名 from アカウントのアドレス

Slide 69

Slide 69 text

トランザクションの書き方 69 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } トランザクションの定義 transaction { … } トランザクションには下記を含められる - prepare(acct: AuthAccount) { … }  トランザクションを実行したアカウント  のストレージを参照&変更できる - execute { … }  基本的にメインの処理をここに書く.  引数はなく、トランザクション内に  宣言した変数のみ参照・変更できる  (変数は prepare で中身を入れる) - post { … }  事後の確認処理をここに書ける

Slide 70

Slide 70 text

トランザクションの書き方 70 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } 空の Collection リソースオブジェクトを作って、 実行アカウントのストレージに入れる

Slide 71

Slide 71 text

トランザクションの書き方 71 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } 空の Collection リソースオブジェクトを作って、 実行アカウントのストレージに入れる トランザクション内でリソースの型を指定する際は コントラクト名.リソース名 と記述する (おそらく名前の競合を避けるため)

Slide 72

Slide 72 text

トランザクションの書き方 72 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } トランザクション実行のログ(これはオフチェーン)

Slide 73

Slide 73 text

トランザクションの書き方 73 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } 実行アカウントの Published 領域に、 自分の Collection リソースへの参照を入れる

Slide 74

Slide 74 text

トランザクションの書き方 74 import NonFungibleToken from 0x01 transaction { prepare(acct: AuthAccount) { let collection <- NonFungibleToken.createEmptyCollection() let oldCollection <- acct.storage[NonFungibleToken.Collection] <- collection destroy oldCollection log("Collection created for account 1") acct.published[&AnyResource{NonFungibleToken.NFTReceiver}] = &acct.storage[NonFungibleToken.Collection] as &AnyResource{NonFungibleToken.NFTReceiver} log("Reference published") } } Published 領域には、参照のみ入れられる 参照は「&」をつけて明示する 上の処理で作った自分の Collection リソースオブジェクトの 参照を、特定の関数だけにしたインターフェースで公開する

Slide 75

Slide 75 text

もっと詳しい情報は https://docs.onflow.org/docs 言語リファレンスなど日々増強中 ※言語仕様がいま結構変わっていってるので注意

Slide 76

Slide 76 text

所感など

Slide 77

Slide 77 text

これはパラダイム・シフト 77 • 手続き型からオブジェクト指向に変わったくらい大きな変化を感じる • 言語の仕組みとして、リソースの所有によるアクセス制御が備わって いるので、セキュリティのリスクとコストが大幅に削減される • Ethereum など既存ブロックチェーンの良くない点・使いにくい点がこ まごまと解消されている(コントラクト実行の分離、中央台帳を使わ ないトークン、ビルトインのログ、柔軟なトランザクション、マルチ シグなど)

Slide 78

Slide 78 text

気になる点 78 • トークンを受け取るアカウントが事前に準備しないといけない仕様の 良し悪しがまだ読めない • いろいろ作り込もうとすると、けっこうすぐにコードが複雑になる (別のセキュリティホールが出てくるかも) • トランザクションのコードのセキュリティ担保も課題になりそう • トランザクションに何でもかけるので、実際の性能がどのくらいにな るか不明 • 今後出てくるであろうクライアントライブラリの使い勝手が気になる • PoS の分権性

Slide 79

Slide 79 text

参考 79 • Introducing Flow, a new blockchain from the creators of CryptoKitties https://medium.com/dapperlabs/introducing-flow-a-new-blockchain-from-the-creators-of-cr yptokitties-d291282732f5 • Resources: Programming Ownership on The Blockchain - By Dieter Shirley https://hackernoon.com/resources-programming-ownership-on-the-blockchain-lzb832d1 • Flow Tutorials https://docs.onflow.org/docs • What is Move language - Speaker Deck https://speakerdeck.com/nakajo2011/what-is-move-language