Cordaの入門資料が網羅的に知ることができます。
はじめに
本稿はCordaのオープンソースライブラリであるToken SDKの基本コンセプトと基本的な使用方法について説明します。本稿はCorda Trainingの内容をベースに公式DocやGithubから補記したものになります。
Token SDKとは
Token SDKはCordaネットワーク上で様々な資産を表現するTokenの作成を支援するためのライブラリです。Tokenで表現できる資産は、物理的なもの、概念的なもの、実世界の価値に基づくもの、ブロックチェーンネイティブなもの多岐に渡ります。Token SDKを利用することで、それらのTokenの発行・移転・償還をレディメイドのFlowを使って容易に実行することができます。
Token SDKはCorda4.3から導入されましたが、それまでCordaではTokenが作成できなかったわけではありません。結局のところ、TokenはContractState、LinearState、OwnableState、FungibleAssetのいずれか、またはその組み合わせであり、発行、移転、償還の3つのコマンドがあります。そこでR3は、新しいState、Contract、Flow、Service、Utilityおよび多くのサンプルを含む包括的なSDKを構築しました。
Token SDKではFungibleToken、NonFungibleTokenの作成をサポートしています。作成したいTokenが分割・再統合可能ならFungibleTokenを、分割できないCordaネットワーク上の唯一のTokenならNonFungibleTokenのデザインをご参照ください。
ここでFungibleTokenとNonFungibleTokenのデザインについて説明する前に、TokenTypeとEvolvableTokenTypeというクラスを説明します。FungibleTokenとNonFungibleTokenはこれらのクラスのいずれかをラップする形で実現されており、これらのクラスがそれをラップするFungibleTokenとNonFungibleTokenの性質を決定します。
TokenType
TokenTypeはTokenの固定単位(ユニット)であり、発行しようとしているTokenの識別子や最小単位を指定することができます。
例えば、あなたがCordaネットワーク上で、USDを発行したいなら、tokenidentifier="USD"、fractionDigits=2(USDの最小単位は1セント=0.01ドルであり、小数点以下2桁となるためです)とする必要があります。また、XRPを発行したとするならば、tokenidentifier="XRP"、fractionDigits=6(1 drop = 0.000,001 XRPのため)となります。
また、TokenTypeを拡張して、独自のカスタム属性を追加することができます。ただし、追加したカスタム属性はTokenのライフサイクルにおいて変更不可であること注意が必要です。例えば、猫を Token化したCryptoKittenTokenTypeを作成し、カスタム属性として品種を追加した場合、そのような属性は猫の寿命が尽きるまで、本来は変更不可能なものです。
カスタム属性は、設計上変更できないので、不変であるべきプロパティに使用します。
EvolvableTokenType
Tokenのライフサイクルにおいて決して変化しないTokenTypeインスタンスとは異なり、EvolvableTokenTypeインスタンスはLinearStateインスタンスであり時間とともに進化することができます。他のStateと同様に、ルールに従ってのみ作成および更新できます。この場合、そのStateのプロパティで指定されたメンテナのリストから署名、つまり承認が必要となります。さらに、この移行を容易にするためのFlowも提供されています。
例えば、EvolvableTokenType を使って自動車を表現することを考えてみます。自動車には、不変のプロパティと、ライフサイクルの中で進化する変更可能なプロパティがあります。メーカー(ブランド)、モデル、車体番号は変更できませんが、走行距離と価格は時間とともに変化します。
進化可能なユニットを作るには、EvolvableTokenTypeを拡張し、カスタム属性を追加する必要があります。この例では、車のユニットを CarTokenType として表現することができます。覚えておくべきは、まずユニットを定義する必要があるということです。そうして初めて、そのユニットのTokenを発行することができます。CarTokenTypeは、更新可能な属性である価格と走行距離、および固定属性であるメーカーと 車体番号を持ちます。
また追加したプロパティに対し、EvolvableTokenContractを継承したCarTokenCntractで追加の制約を設けることができます。(例:価格は0以上、車体番号は変更不可など)
この例ではDMV(陸運局)がこのCarTokenTypeの管理者であり、DMVのみがCarTokenTypeのプロパティを変更することができます。
FungibleToken
Fungibleとは、個々の単位が他と区別できない特性のことを言います。例えば、1gの純金はが他の1gの純金と物質的に区別がつかないので、Fungibleであると言えます。貨幣、通貨、多くの証券のようにFungibleの重要な特性は、量だけで十分に意味を持つというところにあります。誰かが「米ドル100ドル」と言えば、誰もがそれが何を意味するのかわかります。なぜなら、米ドルはFungibleだからです。「どの米ドルか ?」と尋ねる人はいないでしょう。
FungibleTokenは、発行者、保有者、量の間の関係を組み立てるStateです。それをネストした構成で実現します。
- FungibleTokenは所有者とAmount型のTokenを持ちます。
- Amountは数量とIssuedTokenType型のTokenを持ちます。
- IssuedTokenTypeは、発行者とTokenType型のTokenを持っています。
図で表すとそれは以下の構造になります。
Tokenの種類が同じでも、発行者が異なるTokenは別物とみなされ、混在させることはできませんので、ご注意ください。例えば、US Mintが発行したUSDと、Monopolyが発行したUSDは別物であることは明白でしょう。
Fungibleは、分割と統合が可能です。つまり、ある量のTokenを複数のTokenに分割したり、また統合して複数のTokenを1つにまとめることができます。
NonFungibleToken
NonFungibleToken(a.k.a NFT)も発行者と保有者の関係を定義しますが、分割や統合はできず、数量もありません。2つのNFTインスタンスが同じオフチェーンまたはオンチェーンのオブジェクトを参照しないようにすることは開発者の責任です。
NFTの良い例が家です。家はユニークな財産であり、この一意性は不動産IDや住所といったメタ情報を使って表現できます。不動産開発者は、同じ家に対して2つのHouseTokenを発行しないように発行Flowにおいて細心の注意を払います。技術的には同じ住所を2回割り当てることは可能ですが、それはおそらく欠陥であり、問題を引き起こす可能性があります。
進化可能なCarTokenTypeの例に戻ると、車もまたNFTの一例と言えます。悲しいことに、2台のエコノミーセダンから高級スポーツカーを作ることはできません。
NonFungibleTokenもFungibleToken同様にネストされた構成によって関係が実現されています。
NonFungibleToken は所有者と IssuedTokenType型のtokenを持っています。
さらに、IssuedTokenType は発行者と TokenType 型のtokenを持っています。
つまり図式的には以下のようになります。
上記の例では、issuerとmaintainersが異なるクラスで宣言された異なるエンティティであることに注目してください。以下の例を見るとその理由がよくわかります。
- 自動車が販売店に納車されると、DMVに登録されます。
- DMVはmaintainersであり、そのmaintainersは個々の車に対してCarTokenTypeのインスタンスを作成します。
- ディーラーがその車を売るとき、その車のCarTokenTypeインスタンスに対してNonFungibleTokenインスタンスを作成し、Tokenのholderとして新しい所有者に発行します。
- 車が1年ごとの車検を受けると、DMVは新しい関連情報でCarTokenTypeインスタンスを進化させます。
- 車の所有者がそれを売却すると、所有者はそのインスタンスを、新しい所有者をholderとする新しいNonFungibleTokenインスタンスに置き換えます。
補足
実生活でNonFungibleだからといって、台帳上ではNonFungibleTokenとして扱わなければならないわけではありません。
例えば、家はNonfungibleなので、物理的に家を分割することはできません。でも、例えばとあるCorDapp上では、複数の人が投資して一つの物件を買うことができるとします。そして、それを貸し出して、投資に対するリターンを分配します。この場合、HouseTokenというTokenTypeを作り、同じHouseTokenのFungibleTokenインスタンスを複数発行し、複数の所有者がHouseTokenのFungibleTokenを所有します。こうすることで、誰でも自分のシェアを複数の買い手に売ったり、複数の売り手から買ったりすることができ、CordaではHouseTokenのFungibleTokenを分割・併合することになります。
EvolvableTokenTypeの誤解
上の図では、あたかもIssuedTokenTypeがCarTokenTypeを直接保持しているように見えますが、実際はそうではありません。なぜならこれまでに説明したように、CarTokenTypeは進化可能であり、そのライフサイクルの中で何度も進化する可能性があります。その進化可能なTokenTypeをIssuedTokenTypeが直接保持してしまうと、IssuedTokenTypeの中のCarTokenTypeは最新のCarTokenTypeと乖離してしまいます。
それを解決するために、IssuedTokenTypeはCarTokenTypeの代わりにTokenTypeを継承したTokenPointerを保持することで、CarTokenTypeの進化を追うことができます。TokenPointerはプロパティにLinearPointerを持ち、LinearPointerは参照先のUniqueIdentifierを持っているため、最新の参照先のインスタンを取得することができるのです。したがって上の図は正しくは以下のように表現されます。
Token SDKのデザインの説明は以上です。Cordaにおけるどのような思想に基づいで設計されているか理解する助けとなれば幸いです。
CordappにToken SDKを追加する
ここからはいよいよ自分のCordappにToken SDKを導入する方法を説明していきます。
CordappにToken SDKのライブラリを追加します。
まず、プロジェクトルートのbuild.gradleに以下を追加します:
そして、contract/build.gradleとworkflows/build.gradleにもこれらを追加します:
実際に使ってみる
ここではToken SDKを使って実際に、Tokenの発行・移転・償還の手順を説明します。この章を読み終わるころには、Token SDKを使用すると色々なCordappでTokenを容易に扱うことができることがわかると思います。
FungibleToken
ここではFungibleTokenの発行から、移転、償還の方法について実際のコードをお見せしならが説明していきます。
FungibleTokenを発行する
下のコードではUS MintがUSDを$100発行しています。
発行の流れは以下の通りです:
・発行するTokenの単位(ユニット)を生成します。今回は米ドルを発行するので、記号は"USD"、最小単位は少数第二位としています。
・Tokenの単位に発行者を追加し、IssuedTokenTypeを生成します。今回はUS Mintを発行体とするusMintUSDを作成しました。
・発行するTokenの数量を指定します。今回は100とします。
・発行するFungibleTokenのインスタンスを生成します。IssuedTokenTypeに加え、FTの所有者となるAliceを引数に指定します。
・Token SDKが提供するFlowを用いてTokenを発行します。Tokenの発行ではIssueTokensというFlowを使用します。引数に発行したいTokenのリスト、オブザーバー(今回は空にします)を指定してFlowを実行します。
以上でAliceに$100のUSDを発行することができました。
FungibleTokenを移転する
続いて上記で発行したFTをAliceからBobに移転します。
移転の流れは以下の通りです:
・移転したいTokenのTokenTypeを指定します。
・移転したいTokenの数量を指定します。
・移転するTokenの検索条件を指定します。ここでは発行者がUS Mintであるという条件を指定しています。
・移転するTokenの検索条件を指定します。ここでは保有者がAliceであるという条件を指定しています。
・Token SDKが提供するFlowを用いてTokenを移転します。FTの移転ではMoveFungibleTokensというFlowを使用します。引数に移転後の(Outputになる)Tokenインスタンス、オブザーバー(今回は空にします)、移転する(Inputになる)Tokenの検索条件、お釣りの返却先を指定してFlowを実行します。
以上でAliceからBobに$50のUSDを移転することができました。
FungibleTokenを償還する
続いてBobがAliceからもらったUSDを償還します。チェーン上の価値を実世界の価値に変換する場合など、償還が必要になります。
償還の流れは以下の通りです:
・償還したいTokenのTokenTypeを指定します。
・償還するTokenの検索条件を指定します。ここでは保有者がBobであるという条件を指定しています。
・償還したいTokenの数量を指定しています。
・Token SDKが提供するFlowを用いてTokenを償還します。FTの償還ではRedeemFungibleTokensというFlowを使用します。引数に償還する(Inputになる)Tokenの数量、発行者、オブザーバー(今回は空にします)、Tokenの検索条件、お釣りの返却先を指定してFlowを実行します。
以上でBobから$25のUSDを償還することができました。
NonFungibleToken
ここではNonFungibleTokenの発行と移転の方法について実際のコードをお見せしならが説明していきます。
NonFungibleTokenを発行する
NFTの発行は基本的にFTと同じです。ただし、NFTではそのTokenの性質を表すEvolvableTokenTypeを定義する必要があります。(FTの発行においてもそのFTが何を表現するのかTokenTypeを定義しましたが、EvolvableTokenTypeの方が定義が複雑です。)
ここでは「デザイン」の章で扱ったCarTokenType作成していきます。
「デザイン」でも説明した通り、EvolvableTokenTypeには追加の制約を設けることができます。ここでは追加の制約はEvolvableTokenContractを継承したCarTokenContractに記述していきます。ここでは追加で定義した車体番号、メーカー、走行距離、価格に対し制約を付与します。
このCarTokentypeを元にNFTを発行します。注意すべきは、CarTokenTypeのmaintainerとNFTのissuerが異なる主体のため、issuerはNFTを発行する前にmaintainerからCarTokentypeを共有してもらう必要があるということです。以下はCarTokenTypeのmaintainerであるDMV側のコードです。
10行目のCreateEvolvableTokensでは4行目で作成したCarTokentypeを関係者に伝播させています。これによりissuerはCarfTokenTypeを知り、NFTを発行することができるようになります。
以下のコードはissuerであるBMWディーラー側のコードです。
発行の流れは以下の通りです:
・CarTokenTypeに発行者情報を追加し、IssuedTokenTypeを生成します。発行者はFlowの実行者であるBMWディーラーです。
・発行するNFTのインスタンスを生成します。IssuedTokenTypeに加え、NFTの所有者となるAliceを引数に指定します。
・Token SDKが提供するFlowを用いてTokenを発行します。Tokenの発行ではIssueTokensというFlowを使用します。引数に発行したいTokenのリストを指定してFlowを実行します。
以上でAliceにCarTokenTypeのNFTを発行することができました。
NonFungibleTokenを移転する
続いて発行したNFTをAliceからBobに移転します。これは発行と比べるととてもシンプルなFlowになります。
移転の流れは以下の通りです:
・移転したいTokenのTokenPointerを取得します。
・移転したいTokenと移転先情報を持つPartyAndTokenインスタンスを生成します。
・Token SDKが提供するFlowを用いてTokenを移転します。NFTの移転ではMoveNonFungibleTokensというFlowを使用します。引数に上で生成したPartyAndToken、オブザーバー(今回は空にします)Flowを実行します。
以上でAliceからBobにNFTを移転することができました。
もっと詳しく知る
Corda Training Webサイト: https://training.corda.net/libraries/token-sdk/token-introduction/
R3 公式Doc: https://docs.r3.com/en/platform/corda/4.9/community/token-sdk-introduction.html
Token SDK Github: https://github.com/corda/token-sdk
おわりに
いいかがでしたでしょうか。自分で一からTokenを発行・移転・償還しようとした場合、Tokenの検索から残高の確認、残高のロックからお釣りの計算など考えなければならないことがたくさんあります。しかし、Token SDKを用いれば上記のように、発行・移転・償還がわずか10数行で記述することができます。
また、上記で紹介したToken SDKの使用方法はほんの一部であり、その他にも便利なFlowやメソッドが多く用意されています。より詳しく知りたい方は「もっと詳しく知る」で紹介している資料をご覧いただければ思います。
<ご質問・ご要望の例>
- Corda Portalの記事について質問したい
- ブロックチェーンを活用した新規事業を相談したい
- 企業でのブロックチェーン活用方法を教えて欲しい 等々