牛の個体識別情報とは
牛の個体識別番号とは牛の個体識別情報を照会するための番号で、牛が誕生してから流通するまでの履歴を管理するために使用される10桁の数字です。 番号は(独)家畜改良センターのサイト上で入力することで情報照会が可能になっています。[1]
前回登場した牛さんの情報を再度確認してみます。以下がサイト上で 『0840701938』を入力して得られた牛さんの情報です。北海道で育ち茨城県下妻に引っ越した、2010年7月15日生まれの黒毛和種の女性でお母さんは『1242756502』さんであったという、個体情報と異動情報がそれぞれ分かるようになっています。今回は異動情報に注目して異動で行われている内容をERC721を継承して実装してみたいと思います。
異動内容のうち出生とと畜を除いた変更はざっくりと以下のように考えられると思います。
- 転出(取引)は『所有者から次の所有者への移転の許可を発生させる』
- 転入(搬入)は『次の所有者は転出(取引)で許可された移転を行う』
代替不可能なトークンを提供するERC721は Non-Fungible Token(NFT)を使用することができ、資産などの所有権や移動を基本的な機能として定義しています。CryptoKittiesやCryptoZombiesなどで既に利用実績があり、メタデータを含むことで所有者名などのデータも保持することができるトークン規格です。この規格に準拠することで牛さんを交換所などの市場において取り扱いができるようになります。[2]
The following standard allows for the implementation of a standard API for NFTs within smart contracts. This standard provides basic functionality to track and transfer NFTs.
We considered use cases of NFTs being owned and transacted by individuals as well as consignment to third party brokers/wallets/auctioneers (“operators”). NFTs can represent ownership over digital or physical assets. We considered a diverse universe of assets, and we know you will dream up many more:
Physical property — houses, unique artwork
Virtual collectables — unique pictures of kittens, collectable cards
“Negative value” assets — loans, burdens and other responsibilities
In general, all houses are distinct and no two kittens are alike. NFTs are distinguishable and you must track the ownership of each one separately.
ERC721の実装にはセキュアなスマートコントラクト開発のライブラリであるOpenZeppelinを使います。[3]
まずnpmでOpenZeppelinを導入します。最新のopenzeppelin-solidity@2.1.1のコードがpragma solidity ^0.5.0としてコンパイラバージョンが上位向けに定義されていたためpragma solidity ^0.4.23として同じバージョン向けに書き換えました。※ローカルにインストールしたものを変更している
$ npm install openzeppelin-solidity
+ openzeppelin-solidity@2.1.1
added 1 package from 1 contributor in 2.281s
基本インターフェースはIERC721.solに定義されているので先に確認します。ここに定義されているイベントやファンクションを実装していきます。
https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC721/IERC721.sol
例えば、所有者のアドレスからトークン保有量(所有する牛さんの頭数がトークン数)を返すbalanceOfと個体識別番号からその所有者を返すownerOfは以下のようにオーバーライドできます。CowBreeding.solで定義したownerCowCountやcowToOwnerを使用してbalanceOfやownerOfを表現します。
function balanceOf(address _owner) public view returns (uint256 _balance) {
require(_owner != address(0));
return ownerCowCount[_owner];
}function ownerOf(uint256 _tokenId) public view returns (address _owner) {
address owner = cowToOwner[_tokenId];
require(owner != address(0));
return owner;
}
同じ要領で基本インターフェースの各イベントおよびファンクションをOpenZeppelinを参考に実装したサンプルコードCowOwnership.solが以下です。
テストするためにマイグレーションファイルの/migrations/2_deploy_contracts.jsにCowOwnershipのデプロイを追加します。
var CowBreeding = artifacts.require("./CowBreeding.sol");
var CowOwnership = artifacts.require("./CowOwnership.sol");module.exports = function(deployer) {
deployer.deploy(CowBreeding);
deployer.deploy(CowOwnership);
};
Part 1と同様にtruffle developのプライベートチェーン環境で動作を確認しました。以下の(0)と(1)の2つのアカウント間で牛さんの移転を試してみます。
Truffle Develop started at http://127.0.0.1:9545/Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
登場した個体番号『0840701938』の母牛から計4頭、accounts[0]から2頭、accounts[1]から2頭生まれたとします。以下のトランザクションを発行して生産された子牛を確認します。
- accounts[0]からcowBirth(0840701938, “BrownSwiss”, “Female”, {from: web3.eth.accounts[0]})を2回発行
- accounts[1]からcowBirth(0840701938, “Jersey”, “Male”, {from: web3.eth.accounts[1]})を2回発行
truffle(develop)> migrate --reset
Using network 'develop'.Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x6f5a5c9a547eb3694208ec3c801af097bd3caabc80352dc750e8ac9fe28c51e0
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing CowBreeding...
... 0x1ef6dc8d9817d1a33bbbcb1ab3e9332dab61a8782f3ac81919d5d5406fc4784c
CowBreeding: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Replacing CowOwnership...
... 0x34c641d8deacf43db8b36464b3acbcc8e14aa8e6754710a53c27a15242ed23a0
CowOwnership: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf
Saving successful migration to network...
... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db
Saving artifacts...
truffle(develop)> c = CowOwnership.at("0xf25186b5081ff5ce73482ad761db0eb0d25abfbf")
...truffle(develop)> c.cowBirth(0840701938, "BrownSwiss", "Female", {from: web3.eth.accounts[0]})
truffle(develop)> c.cowBirth(0840701938, "BrownSwiss", "Female", {from: web3.eth.accounts[0]})
truffle(develop)> c.cowBirth(0840701938, "Jersey", "Male", {from: web3.eth.accounts[1]})
truffle(develop)> c.cowBirth(0840701938, "Jersey", "Male", {from: web3.eth.accounts[1]})
truffle(develop)> c.getCowsByOwner(web3.eth.accounts[0], {from: web3.eth.accounts[0]})
[ BigNumber { s: 1, e: 9, c: [ 2082887295 ] },
BigNumber { s: 1, e: 9, c: [ 9713811174 ] } ]
truffle(develop)> c.getCowsByOwner(web3.eth.accounts[1], {from: web3.eth.accounts[1]})
[ BigNumber { s: 1, e: 9, c: [ 8842890973 ] },
BigNumber { s: 1, e: 9, c: [ 4180188598 ] } ]
個体番号『9713811174』の子牛をaccounts[0]からaccounts[1]へ移転してみます。approveで個体番号『9713811174』の子牛をaccounts[1]へ移転する許可をして、transferFromでaccounts[0]からaccounts[1]へ実際に移転します。
truffle(develop)> c.approve(web3.eth.accounts[1], 9713811174, {from: web3.eth.accounts[0]})
truffle(develop)> c.getApproved(9713811174)
'0xf17f52151ebef6c7334fad080c5704d77216b732'
truffle(develop)> c.transferFrom(web3.eth.accounts[0], web3.eth.accounts[1], 9713811174)
個体番号『9713811174』の子牛がaccounts[1]に無事移転されました。
truffle(develop)> c.getCowsByOwner(web3.eth.accounts[0], {from: web3.eth.accounts[0]})
[ BigNumber { s: 1, e: 9, c: [ 2082887295 ] } ]
truffle(develop)> c.getCowsByOwner(web3.eth.accounts[1], {from: web3.eth.accounts[1]})
[ BigNumber { s: 1, e: 9, c: [ 9713811174 ] },
BigNumber { s: 1, e: 9, c: [ 8842890973 ] },
BigNumber { s: 1, e: 9, c: [ 4180188598 ] } ]
転入(搬入)やと畜のタイミングでは支払いが発生すると思いますのでEtherの移転や小切手・手形などの証券で処理する必要があります。と畜自体はburnで処理できそうです。[4]
フロントエンドはVue.jsかReactを試したいです、Part 3があったら。最後にIZAM氏といったらカルチャー・クラブということで『カーマは気まぐれ』をどうぞ。あとは『Do you really want to hurt me』。
実行環境
Truffle v4.1.14はバグがあるためv4.1.15かより上位のバージョンを利用したほうが良さそうです。v4.1.14ではtruffle initで以下のエラーが発生していました。ganacheやMetaMaskもバージョンによって複数バグが報告されています。
Error: Truffle Box at URL https://github.com/truffle-box/bare-box.git doesn’t exist.
$ truffle version
Truffle v4.1.15 (core: 4.1.15)
Solidity v0.4.25 (solc-js)$ npm --version
6.4.1$ node --version
v10.13.0$ ganache-cli --version
Ganache CLI v6.2.5 (ganache-core: 2.3.3)
まとめ
- 牛の個体識別番号から個体情報および異動情報が閲覧できるようになっている
- Non-Fungible Token(NFT)であるERC721を用いることで牛さんの各個体を非代替性のあるトークンとして実装できた
- OpenZeppelinを利用して牛の個体識別番号と異動情報を管理できるアプリをプライベートチェーン上に構築した