Share
Embed
Flow のリソース指向プログラミング言語 Cadence 入門/Flow Cadence Introduction
■ Google Slide 版
https://docs.google.com/presentation/d/15rak1MNkzSIpC0Z5ConxsLuT5kBJKtwCbcO1NM5F8Pc/edit?usp=sharing
■ 参考
Introducing Flow, a new blockchain from the creators of CryptoKitties
https://medium.com/dapperlabs/introducing-flow-a-new-blockchain-from-the-creators-of-cryptokitties-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
Ara
April 12, 2020
Transcript
79 slides
-
Flow の特徴 4 ブロックチェーンの処理を 4種類のノードに分割する ことで、ネットワークの パフォーマンスを改善 • トランザクション収集 •
コンセンサス(PoS) • 計算 • 検証 https://medium.com/dapperlabs/enter-the-octagon-ufc-on-flow-brings-mma-to-crypto-480618408510 ※上記に加えて Observer ノードも存在する -
Cadence:Flow で使われるプログラミング言語 5 • クリプト対応アプリケーションの新しいパラダイムのために 特別に設計された新しいプログラミング言語 • コードを 安全、セキュア、親しみやすくするための機能がある: ◦
リソースオブジェクト ◦ 強力な静的型システム ◦ 関数とトランザクションにおける 事前/事後条件 -
Libra の Move 言語に発想を得ている 6 • リソースオブジェクト ◦ 線形型 =
宣言したものは必ず使わなければならない → 増えたり、なくなったりしない → ブロックチェーン上の所有権の管理に最適 • トランザクションにスクリプトを書ける -
従来の所有モデル 8 中央台帳で所有権を管理する 例:すべての CryptoKitties は1つのコントラクトで管理されている コントラクト #1 #2 mapping
(uint256 => address) : -
従来の所有モデルの課題 9 • スケールできない(Sharding のボトルネックになる) • バグが生まれる(マップの書き換えミスが起こりうる) コントラクト #1 #2
mapping (uint256 => address) : コントラクト EOA : コントラクト 参照 更新 更新 -
リソースのルール 11 1. 各リソースは、常に1つの場所に存在する 実装ミスや悪意のあるコードによって、 リソースを複製したり、誤って削除したりすることはない 2. リソースの所有権は、保存場所によって定義される 所有権を変えるために相談しなければならない中央台帳はない 3.
リソースのメソッドへのアクセスは所有者に制限される 例えば、CryptoKitty の所有者のみが繁殖操作を行える これらが、言語の仕組みとして保証されることが素晴らしい! -
リソースの利点 12 • 言語レベルで制限されたセキュアなコード(最小権限の原則) • 副次的な利点: ◦ 所有権のカスタマイズが容易 (例えば EIP-998
は既存 ERC-721 に適用できない) ◦ ストレージ利用料の徴収が容易 (Ethereum の State Rent は既存 ERC-721 に適用できない) ◦ 再入性バグが発生しない -
Flow におけるプログラムの種類 16 1. コントラクト 実行したアカウントのストレージにデプロイされる 2. トランザクション 実行したアカウントで、デプロイ済みコントラクトを操作する 3.
スクリプト ストレージの状態を読む (実行したアカウントの署名は不要、状態は変更できない) すべて .cdc ファイル -
Flow でコントラクトを実行する流れ 17 1. コントラクトのコードを書く 2. コントラクトをデプロイする → 実行したアカウントのストレージにデプロイされる 3.
トランザクションのコードを書く 4. トランザクションを実行する (実行したアカウントで署名することになる) → 結果は、ログ記録 and/or アカウントのストレージに保管される -
コントラクトの書き方 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 のコントラクト -
コントラクトの書き方 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 コントラクト名 { … } -
コントラクトの書き方 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 } } コントラクトデプロイ時の初期処理 -
コントラクトの書き方 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 リソース名 { … } -
コントラクトの書き方 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 } } リソースオブジェクト生成時の初期処理 -
コントラクトの書き方 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 がある) -
コントラクトの書き方 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 -
コントラクトの書き方 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 の特徴的なところ! -
コントラクトの書き方 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 リソースのオブジェクトを生成して、 実行しているアカウントのストレージに保存する このとき、もともとこの場所にあるかもしれない リソースオブジェクトを押し出して変数に移動する -
コントラクトの書き方 30 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1)
リソースオブジェクトを代入する(=)ときは この特別な移動演算子を使わなければならない 移動演算子を複数回使った式は、 右から左に向かって解釈される -
コントラクトの書き方 31 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1)
アカウントのストレージ(ストレージの storage 領域)には リソースの型をキーとしたマップが用意されている リソースの型に応じた場所に、リソースオブジェクトを格納できる -
コントラクトの書き方 32 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1)
リソースオブジェクトは上書きできないため、 移動演算子を使うときは、移動先にある(かもしれない) リソースオブジェクトを、明示的に他の場所に移動しなければならない -
コントラクトの書き方 33 let oldNFT <- self.account.storage[NFT] <- create NFT(initID: 1)
destroy oldNFT リソースオブジェクトを削除する構文 リソースは線形論理と呼ばれる考え方で設計されており、 プログラム内で宣言したオブジェクトは、 プログラム終了までに必ず消費(どこかに格納 or 削除)する必要がある -
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 } } -
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 リソースオブジェクトを保持するマップ -
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 リソースオブジェクトを保持するマップ リソースオブジェクトを入れる変数に代入するときは 移動演算子「<-」を使わなければならない(視覚化が目的) -
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 リソースはリソースオブジェクトを保 持しているため、自身が削除されるときには、 保持しているリソースオブジェクトを 明示的に削除または移動しなければならない -
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 } } -
NFT を送受信する機能を提供する 47 Storage[Collection] ・ディクショナリ変数 #1 #2 : ・withdraw() 関数
・deposit() 関数 Storage[Collection] 他のアカウントの ストレージは直接 変更できない! -
NFT を送受信する機能を提供する 48 Storage[Collection] ・マップ変数 #1 #2 : ・withdraw() 関数
・deposit() 関数 Storage[Collection] 他のアカウントの ストレージは直接 変更できない! どうするか? -
NFT を送受信する機能を提供する 49 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code createEmptyCollection() 関数 そもそも、送付先アカウントに Collection のリソースオブジェクト がないので、まずこれを作るための 関数を用意する Storage[Collection] -
NFT を送受信する機能を提供する 50 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code 送付先アカウントにこれを 呼んでもらって、ストレージ に空のリソースオブジェクト を作ってもらう createEmptyCollection() 関数 Storage[Collection] -
NFT を送受信する機能を提供する 51 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code 送付先アカウントにこれを 呼んでもらって、ストレージ に空のリソースオブジェクト を作ってもらう (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 createEmptyCollection() 関数 Storage[Collection] -
NFT を送受信する機能を提供する 52 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code createEmptyCollection() 関数 ストレージのNFTを操作する ためのインターフェースを 用意する (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 Storage[Collection] -
NFT を送受信する機能を提供する 53 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 このインターフェースを、 送付先アカウントの Published に入れてもらう Storage[Collection] Published 参照 -
NFT を送受信する機能を提供する 54 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 これで ようやく 準備完了 Storage[Collection] Published -
NFT を送受信する機能を提供する 55 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 Storage[Collection] Published まず withdraw で NFT を引き出して… -
NFT を送受信する機能を提供する 56 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数
Code createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 まず withdraw で NFT を引き出して… Storage[Collection] Published -
NFT を送受信する機能を提供する 57 Storage[Collection] ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code
createEmptyCollection() 関数 (空) ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 送信先アカウントの Published にある deposit を実行する (空) Storage[Collection] Published -
NFT を送受信する機能を提供する 58 Storage[Collection] ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code
createEmptyCollection() 関数 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 NFTReceiver インターフェース - deposit() 関数 送信先アカウントの Published にある deposit を実行する (空) (空) Storage[Collection] Published -
NFT を送受信する機能を提供する 59 Storage[Collection] ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 Code
createEmptyCollection() 関数 Storage[Collection] #1 ・withdraw() 関数 ・deposit() 関数 ・ディクショナリ変数 NFTReceiver インターフェース - deposit() 関数 Published NFTReceiver インターフェース - deposit() 関数 (空) 無事にNFTを送信できた -
拡張した 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 } } -
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 コントラクトの全体 -
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 コントラクトの全体 -
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 コントラクトの全体 -
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 コントラクトの全体 -
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 コントラクトの全体 -
トランザクションの書き方 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 コントラクトで、送付先アカウントが最初に実行するトランザクション -
トランザクションの書き方 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 アカウントのアドレス -
トランザクションの書き方 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 { … } 事後の確認処理をここに書ける -
トランザクションの書き方 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 リソースオブジェクトを作って、 実行アカウントのストレージに入れる -
トランザクションの書き方 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 リソースオブジェクトを作って、 実行アカウントのストレージに入れる トランザクション内でリソースの型を指定する際は コントラクト名.リソース名 と記述する (おそらく名前の競合を避けるため) -
トランザクションの書き方 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") } } トランザクション実行のログ(これはオフチェーン) -
トランザクションの書き方 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 リソースへの参照を入れる -
トランザクションの書き方 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 リソースオブジェクトの 参照を、特定の関数だけにしたインターフェースで公開する -
これはパラダイム・シフト 77 • 手続き型からオブジェクト指向に変わったくらい大きな変化を感じる • 言語の仕組みとして、リソースの所有によるアクセス制御が備わって いるので、セキュリティのリスクとコストが大幅に削減される • Ethereum など既存ブロックチェーンの良くない点・使いにくい点がこ
まごまと解消されている(コントラクト実行の分離、中央台帳を使わ ないトークン、ビルトインのログ、柔軟なトランザクション、マルチ シグなど) -
気になる点 78 • トークンを受け取るアカウントが事前に準備しないといけない仕様の 良し悪しがまだ読めない • いろいろ作り込もうとすると、けっこうすぐにコードが複雑になる (別のセキュリティホールが出てくるかも) • トランザクションのコードのセキュリティ担保も課題になりそう
• トランザクションに何でもかけるので、実際の性能がどのくらいにな るか不明 • 今後出てくるであろうクライアントライブラリの使い勝手が気になる • PoS の分権性 -
参考 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