牛の個体識別情報とは
IZAM氏こだわりの牛の個体識別番号とは牛の個体識別情報を照会するための番号で、牛が誕生してから流通するまでの履歴を管理するために使用される10桁の数字です。2001年に千葉県でBSE発症の牛が見つかったことに端を発し、牛トレーサビリティ法が施行され、その番号は(独)家畜改良センター(すごい名前だがw)のサイト上で入力することで情報照会が可能になっています。[1]
サイトによると情報としてわかるのは出生の年月日・雄雌の区別・母牛の個体識別番号・種別(品種)・飼養場所の履歴で、以下が試しに『0840701938』を入力して得られた牛さんの情報です。
この牛さんは北海道で育ち茨城県下妻に引っ越した、2010年7月15日生まれの黒毛和種の女性でお母さんは『1242756502』さんであったという、個体情報と異動情報がそれぞれ分かるようになっています。個体情報・異動情報をブロックチェーン上で管理することで照会を中央管理者不在で可能とし、また異動については各所有者(ブロックチェーン上のアドレス所有者)間でトランザクションを発行することで低コストに運用することが可能なのではないでしょうか。平成31年1月からは管理者側も費用負担が発生することが決定しており、ブロックチェーンの適用は検討できそうです。Part 1ではCryptoZombiesのゾンビ実装を参考にTruffle/Solidityを使用して牛さんの個体情報を管理することを考えてみます。[2]
牛さんの個体情報モデル
これは予想ですが子牛の情報登録は母牛を所有する生産者に権利があるような気がします。本来であれば誰でも子牛を登録できるものではないと思うのですが、ここではEthereumのアカウントからは誰でも子牛を初期登録できるものとします。
- 子牛の個体番号(ランダム10桁の数字)
- 母牛の個体番号(ユーザ入力)
- 出生の年月日(登録時の年月日)
- 品種(ユーザ入力)
- 性別(ユーザ入力)
個別の牛さんは構造体で定義することを考え、子牛の個体番号はランダムな10桁の番号が割り振られるようにします。またSolidityに決まったDate型がないため、一旦block.timestampを出生日の代わりに記録することにしました。block.timestampはUNIX時間でnowはそのエイリアスとして使用されています。
struct Cow {
uint cowNum;
uint cowMom;
uint birthDate;
string types;
string sex;
}
上記構造体の配列をcowsとして保持し、cowToOwnerで個体番号と所有者、ownerCowCountで所有者の牛さん保有数、itToCowNumで個体番号を紐づけるmapping型の変数をそれぞれ定義します。
Cow[] public cows;
mapping (uint => address) public cowToOwner;
mapping (address => uint) ownerCowCount;
mapping (uint => uint) idToCowNum;
ここでは個体情報を管理するだけですので、子牛の個体情報登録および所有者の所有する牛さんの個体番号を返す関数のみを考えました。cowBirthで子牛を登録してgetCowsByOwnerでは所有者のアドレスを渡すことで所有する牛さんを配列で返すものとします。その他に所有頭数を確認できるgetCountbyOwnerおよび牛さんの個体番号からその所有者を確認できるgetOwnerbyCowを定義しています。
function _cowBirth(uint _cowNum, uint _cowMom, string _types, string _sex) internal { ... }
function cowBirth(uint _cowMom, string _types, string _sex) public { ... }
function getCowsByOwner(address _owner) external ownerOf(_owner) view returns(uint[]) { ... }
function getCountByOwner(address _owner) external ownerOf(_owner) view returns (uint) { ... }
function getOwnerbyCow(uint _cowNum) public view returns (address) { ...}
バックエンド実装
作成してみたサンプルコードCowBreeding.solです。品種や性別を文字列型にしていますがバリデーションは行っていません。またownable.solのコンストラクタなどをバージョンの記法に合うように少し書き換えて使用しています。文字列の比較などのバリデーションに使えそうなライブラリとしてはstringutilsがあります。[3]
truffle developのプライベートチェーン環境で動作を確認しました。先に登場した個体番号『0840701938』の母牛から2頭生まれたとしてcowBirth(0840701938, “BrownSwiss”, “Female”)のトランザクションを2回発行します。頭数の確認や個体番号から所有者を返す関数もテストできました。
truffle(develop)> migrate --reset
Compiling ./contracts/CowBreeding.sol...
Compiling ./contracts/ownable.sol...
Compiling ./contracts/safemath.sol...
Writing artifacts to ./build/contractsUsing network 'develop'.Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x6f5a5c9a547eb3694208ec3c801af097bd3caabc80352dc750e8ac9fe28c51e0
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0x254571c1c0a9dbc7031f00abf40bf463731080a6145ecaf39fd7ce13bdfccd33
CowBreeding: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
...
truffle(develop)> c = CowBreeding.at("0x345ca3e014aaf5dca488057592ee47305d9b3e10")
... truffle(develop)> c.cowBirth(0840701938, "BrownSwiss", "Female")
truffle(develop)> c.cowBirth(0840701938, "BrownSwiss", "Female")
truffle(develop)> c.getCowsByOwner(web3.eth.accounts[0])
[ BigNumber { s: 1, e: 9, c: [ 4342171360 ] },
BigNumber { s: 1, e: 9, c: [ 4692134363 ] } ]
truffle(develop)> c.getCountByOwner(web3.eth.accounts[0])
BigNumber { s: 1, e: 0, c: [ 2 ] }
truffle(develop)> c.getOwnerByCow(4342171360)
'0x627306090abab3a6e1400e9345bc60c78a8bef57'
フロントエンド実装
Web上でサービス提供するために、以下のようなシナリオを考えました。個体情報の管理という視点では最低限の機能です。
- 母牛の個体番号、子牛の品種および性別を入力して子牛を登録できる
- 所有者が保持する牛さんの個体番号を表示できる
- 所有者が保持する牛さんの頭数を表示できる
簡易的にlive-serverを使用し、リモートのUbuntuサーバで子牛の登録と所有している牛さんの情報確認などを表示できるようにしたいと思います(npmのパッケージはすべてグローバルにインストール)。
$ sudo npm install -g live-server
+ live-server@1.2.1
added 6 packages from 13 contributors, removed 52 packages, updated 24 packages and moved 13 packages in 30.674s
$ live-server . --host=X.X.X.X --port=3000 --no-browser
Serving "." at http://X.X.X.X:3000
Ready for changes
srcというフォルダとその配下にindex.htmlを作成します。jQueryとweb3はCDNから読み込み、/build/contract/CowBreeding.jsonからabi部分を抜き出しcowbreeding_abi.jsとして同フォルダへ保存しました。abiをファイル内で変数へ格納し、new web3.eth.Contract(ABI, Address)とすることでコントラクトをインスタンス化できます。live-serverはファイルを更新するとすぐにweb上に反映されるのでお手軽便利です。
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js@1.0.0-beta.36/dist/web3.min.js" integrity="sha256-nWBTbvxhJgjslRyuAKJHK+XcZPlCnmIAAMixz6EefVk=" crossorigin="anonymous"></script>
<script language="javascript" type="text/javascript" src="cowbreeding_abi.js"></script>
ganache-cli host=X.X.X.X &としてバックグラウンドでプライベートチェーンを立ち上げます(ポートはデフォルトの8545)。MetaMask上でこのプライベートチェーンのアカウントを1つインポートしてブラウザ上から接続しておきます。[4]
Ganache CLI v6.1.8 (ganache-core: 2.2.1)Available Accounts
==================
(0) 0x022ea08c553f776adfe41aea4e4b79ce90303cca (~100 ETH)
...Private Keys
==================
...HD Wallet
==================
Mnemonic: affair island magnet usage ancient foot wagon anxiety bronze keep sure moment
Base HD Path: m/44'/60'/0'/0/{account_index}Gas Price
==================
20000000000Gas Limit
==================
6721975Listening on X.X.X.X:8545
このganacheのブロックチェーン上にtruffleで作成したサンプルコードをデプロイします。truffle.jsをサーバのIPに合わせて設定してください。
module.exports = {
networks: {
live: {
host: "X.X.X.X",
port: 8545,
network_id: "*"
}
}
};
設定したliveネットワークに接続して、migrateを実施。
$ truffle console --network live
truffle(live)> web3.eth.accounts[0]
'0x022ea08c553f776adfe41aea4e4b79ce90303cca'
truffle(live)> migrate
Using network 'live'.
Running migration: 1_initial_migration.js
Deploying Migrations...
Transaction: 0x2a63ac9e5d155b7f6e9fbf65c7e4eb593df03fd0392a29556356ef18f7c986ee
Contract created: 0x48d75cbd8d878ce74a502e369edc168c156cd2aa
Gas usage: 277398
Block Number: 1
Running migration: 2_deploy_contracts.js
Deploying CowBreeding...
Transaction: 0xbfecb3815f46599e1169de240e2c2de6e4a3ea4f5f82df1bf9fdd18dce79798d
Contract created: 0xbee7cc6b579ec0fd7db20e09a8a4ce4e9191384d
Gas usage: 1143590
Block Number: 2
CowBreeding: 0x28103exxxxxxx15c8e2d95b1376adb967e598537
Saving successful migration to network...
Saving artifacts...
これでアプリを動作させる準備ができましたので、jQueryとweb3でフロントエンドを記述していきます。基本的にCryptoZombiesの実装を参考に、子牛の登録のところは入力された情報をjQueryで渡せるようにフォームを使用して書きました(Githubにコードあり)。”YOUR CONTRACT ADDRESS”にmigrateした際のコントラクトのアドレスを記載する必要があります。
以上でフォームによる子牛の登録と、所有する牛さんの個体番号表示ができるようになりました。
動作の確認にRopstenなどのテストネットにデプロイしている方もいますが、簡単なテストであればtruffle deployやGanacheなどローカルマシンやリモートサーバ上のプライベートチェーンでテストすることをおすすめします。以上、Part 1では個体情報を取り扱いましたが、Part 2で異動情報を処理できるように改良したいです。最後にSHAZNAの『Melty love』をどうぞ。
https://github.com/yuyasugano/cow-traceability
実行環境
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)
まとめ
- SHAZNAのIZAM氏は肉マニアで妻吉岡美穂もその異常さに呆れている
- 牛の個体識別番号から個体情報および異動情報が閲覧できるようになっている
- CryptoZombiesの実装を参考に簡易な牛個体識別管理のアプリをプライベートチェーン上に構築できた
References
- [1] 独立行政法人 家畜改良センター
- [2] 牛個体識別情報の提供に必要となる費用負担のお願い
- [3] solidity-stringutils
- [4] MetaMask
- [5] CryptoZombies