villagerHの日記

勉強したことや苦労した事などを書き綴ります

ブロックチェーンアプリケーション

いつかの記事で軽く触れました、

ブロックチェーンアプリケーション開発の教科書

ブロックチェーンアプリケーション開発の教科書

こちらの本を読み終わりました。
ブロックチェーン関連の本としては3冊目ですかね。

スマートコントラクト周りについて読みたくて購入していたのでブロックチェーン全体の概要や技術に関しての箇所は軽く流し読みで飛ばしました;

肝心のスマートコントラクト関連はsolidityについてと環境構築に関してです。

solidityの文法とかについてはCryptoZombiesである程度把握できますが
CryptoZombiesでは紹介しきれていない箇所もあったりでよかったです。

それとCryptoZombiesだと文法はわかっても実際の環境構築に関しては触れられてませんがこちらの本は環境構築に関して触れられております!

truffleとMetamaskを使用してスマートコントラクトを作って動かしてみるといった感じですかね。

こちらの本を読む前に他の方がまとめてくれていたブログなどを必死に探して環境構築していたのでもっと早く読んでいればという感じでした(笑)

vue学習しながら作成中

コントラクタメソッドを呼ぶのに各メソッドに対応したボタンを用意して順番に押して確かめるということをしていましたが
少しはゲームらしいページ遷移をしようと思いvueの遷移方法に関して調べました。

しっかりとしたものだとちゃんとした機能があるみたいですが、
簡単なページ遷移で十分なのでコンポーネントの管理でなんとか出来そうなのでそれで作り始めました。

コンポーネントコントラクタへのアクセスは全て管理
コンポーネントは必要な要素を受け取り各ページの描画を行う

作りとしてこれが正しいのかどうかはわかりませんが、とりあえずこんな形で進めていってます。
見た目とかは全く凝っていないのであれですが、近日中にとりあえず最低限の形にはなりそうです!

Solidityの乱数生成

フロントエンド側からmethodが呼べるようになったのでコントラクト側に戻り
ゲームのロジック部分を作り始めて乱数が欲しくなったところ軽く詰まったので記録。

簡単な乱数が取得できれば良いのでCryptoZombiesに習いハッシュ値から乱数を取得する形で行きます。

CryptoZombiesは以下のような形になっています。

function randMod(uint _modulus) internal returns(uint) {
    randNonce++;
    return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
}

now(現在日時),msg.sender(アドレス),randNonce(使い捨て数値)からハッシュ値を求めてその値から必要な範囲の数値を取り出して乱数値として生成していますね。

基本的にはこれを使いまわして以下の形で実装しました。

function randMod(uint _randNonce, uint _modulus) internal returns(uint) {
    return uint(keccak256(now, msg.sender, _randNonce)) % _modulus;
}

randNonceの使い方だけちょっと変えましたがロジック的には全く変わっていません。

solidityの0.5.0からkeccak256の仕様変更が入ったらしく、上記のコードだとコンパイル時に怒られます。

TypeError: Wrong argument count for function call: 3 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding.

パラメータはbytes型しか受け付けなくなったらしく上記のコードはもちろん、uint投げたりしても怒られました。

エラー文にもありますがabi.encodePakedを通してやるとbytesに変換してくれるようです。
なのでコードを以下に変更。

function randMod(uint _randNonce, uint _modulus) internal returns(uint) {
    return uint(keccak256(abi.encodePaked(now, msg.sender, _randNonce))) % _modulus;
}

コンパイルが通って無事に乱数取得処理が完成しました!

前進

結構前進した気がします!

前回記載した問題点
・msg.senderでアドレスを保存しているはずなのに保存されない
実際のコードより

uint id = units.push(Unit(pow, 1/*level*/));
unitToOwner[id] = msg.sender;

mappingにpushした時に返ってくるindexは
挿入したindexの次のindexです!!!!!

正しくはこう

uint id = units.push(Unit(pow, 1/*level*/)) - 1;
unitToOwner[id] = msg.sender;

アドレスと保存位置がずれてたらそりゃ正しく取得できないですよ;


これで正しく動いてくれるようになったのですが
上記のUnitを生成してるmethodのretrunでidを返してしまえばいいと思って作っていたのですが
フロントエンド側で呼び出した時にどうもうまく数値が取得できない。

ログを覗いて見ると意図しているreturnの値でなくトランザクションのハッシュやら何やらが入っていました。
どういうことだ?と思いweb3のドキュメントとか覗いてみてました。

コントラクタの状態を変化させるmethodの呼び出しはsend
変化させずに取得のみ行うようなものに関してはcallが呼び出されます

なるほど。sendのドキュメントを見てみたら先ほどのハッシュ値とかが確かに定義してありました。
でも自分で定義したreturnもどこかで返ってきてるんじゃないの?とか思いつつさらに調べました。


「状態変更関数(別名トランザクション)は、ブロックチェーンの状態を変更する前にマイニングする必要があるため、非同期にマイニングされる関数からの戻り結果は期待できません」Chrome翻訳)

な、なるほどー!
確かにいつマイニングされて初めて数値が変わるものの返りは期待できないですね。
妙に納得してスッキリしたのとブロックチェーンならではの問題だなという感じでちょっと感動しました(笑)


これにより現在できてるコントラクタのmethodは全て呼び出すことに成功しました!
少しずつではありますが前進してますね!


・eventでログを残しているはずなのに保存されない

こちらはまだ不明です。
Ganache上では確認できないですがtruffleのテストでは確認できてるのでとりあえずは後回しにしておきます。

コントラクトの呼び出しに苦戦中③

牛歩ですが少しずつは進んでます。(多分)

コントラクトの呼び出しはできるようになってきました

call時は問題なく行くもののsend時がエラーになる...

コンソールに

Error: Internal JSON-RPC error.

と表示されます。

調べた感じだとMetaMaskがおかしくなっているらしいです。
とりあえずの解決手段。

>アカウントの設定
>Advanced
>アカウントをリセット

で解決しました。
それでもダメな時はGanacheも再起動させてリフレッシュしてます。(強引)


・現状でわかってる問題点
msg.senderでアドレスを保存しているはずなのに保存されない
eventでログを残しているはずなのに保存されない

少しずつ進めていきましょう;

コントラクトの呼び出しに苦戦中②

villagerh.hatenablog.com
前回の続き。

こんな感じで呼び出してますが怒られてます。

instance.getUnitNum()
Error: The send transactions "from" field must be defined!

from fieldを指定しろということなので指定してみます。

instance.getUnitNum({from:this.account})

動きました!!!

でも毎度毎度fromフィールドを指定するのは気持ち悪いなー、デフォルトで入るようにならないのかな?
と思って調べました。

web3のドキュメントによるとweb3.eth.defaultAccountにアカウントを指定するとfromが指定されていない時にこのアドレスが自動で挿入されるとのこと。なるほど。

指定してみます。

web3.eth.defaultAccount = this.account

accountにはweb3.eth.getAccountsで取得してきたアカウントが入ってます。

これで実行してみたところ

Error: The send transactions "from" field must be defined!

あれー?また怒られる???

数時間これと格闘してましたが結局原因と明確な対策方法が見つからず;;

ここでずっと詰まっているのも勿体無いのでとりあえず直接fromを指定する形で先に進めようかと思いますが、なんで動かないんだろう・・・

コントラクトの呼び出しに苦戦中

いくつかのサイトさんを参考にApp.vueを編集し、最低限の感じはできたので動作させてみて確認しようと思ったところコントラクトの関数をうまく呼び出せず苦戦中です…

ログを出しつつ色々試行錯誤してみたところMetaMaskのアカウントリストの取得でempty状態で返ってきてしまっているようなので原因を調査。


調べた結果MetaMaskのアップデートでセキュリティリスク回避のためにプライベートモードなるものが新しく追加されており
web3.eth.getAccountsの呼び出し前にethereum.enable()の呼び出しが必要になった模様。なるほど。

ethereum.enable()を入れて試してみたところ「許可しますか?」的な小ウインドウが出てきて許可したところgetAccountsで無事アカウントが取れるようになりました。



しかし未だにコントラクトの関数呼び出しは失敗します。
instanceの中身を見てみるとアドレスとかは問題なく取得できている模様。

ちなみに
The send transactions "from" field must be defined!
とコンソール画面にはエラー表示が。

fromフィールドを用意しろってことのようですが・・・時間になってしまったので明日また調べます。