villagerHの日記

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

コントラクト自身のアドレス

まだ完全じゃないですが体調がよくなり始めたので作業の方も再開し始めました。

フロント側を作っている最中にコントラクトの拡張を何度か行ったのですが
そういえばtruffleでのテスト用ファイルを全く更新していなかったことを思い出して更新しました。

ちなみにですが
Truffleにはtestフォルダにテスト用のsolidityファイルを用意するとテストを行える機能があるようです。
解説してくださってるサイトがあるのでそれを参考にしつつ作ってます。


メインの方のコントラクトですがCryptoZombiesを参考にしつつ作っており

  • 任意のワードからユニットを生成してmsg.senderで呼び出し元のアドレスを保存
  • 保存されたユニットのIDを取得するメソッドにアドレスを渡すとそのアドレスのIDが取得できる

という形になってます。

このIDを取得するメソッドへのアドレスをテスト側のコントラクトで以下の形で呼び出してました。

uint[] memory unitids = unitFactory.getUnitIdByOwner(msg.sender);

しかしユニットを作ってからやっているのに何度やってもunitidsの中身が0でうまく取得できません。

eventでログを出しながらやったところCreate時に保存されているアドレスとgetUnitIdByOwnerで渡した時のアドレスが違っていることがわかりました。

よくよく考えれば当たり前なのですがテスト側でmsg.senderしてしまうとテスト側の呼び出し元のアドレスになってしまうんですね。

テストコントラクト(msg.sender=アドレスA)⇨メインコントラクト(msg.sender=アドレスA)

こうなるのかと思って実装してましたが正しくは以下の形です。

テスト呼び出し元(自身のアドレス=アドレスB)⇨テストコントラクト(自身のアドレス=アドレスA, msg.sender=アドレスB)⇨メインコントラクト(msg.sender=アドレスA)

わかりづらい図で申し訳ないですがざっくりこんな感じです。

で、テストのコントラクト自身のアドレスを取得するにはどうすればいいのかというとthisを使えばいいようです。

uint[] memory unitids = unitFactory.getUnitIdByOwner(address(this));

これで無事に意図している挙動になってテストが無事にできました。

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

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

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

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

こちらの本を読み終わりました。
ブロックチェーン関連の本としては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でログを残しているはずなのに保存されない

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