TruffleSuiteで始めるDapps開発2

どうも、次世代デジタル基盤開発事業部(旧ブロックチェーン事業部)の土田です。
今回から前回つくった開発環境を使ってDappsを作っていきたいと思います。
tec.tecotec.co.jp

今回のゴール

コントラクトを書いて、プライベートチェーン上にデプロイし、実行するところまでをやっていきます。

Ganacheの永続化

まずはGanacheで立てたノードの永続化を行います。起動時にオプションを指定するだけです。

yarn ganache-cli --db 任意のパス --mnemonic "任意の文字" --networkId 任意の数字

各種オプションについては下記を参照して下さい。 github.com

ニーモニックとネットワークIDを指定することで、同じアカウントを使うことができます。また、この指定が無いと毎回アドレスを生成してしまいますので、注意が必要です。

ニーモニック(Mnemonic)

MetaMaskでアカウントを作成した際に12個の単語が表示されたかと思います。アレです。
Ganacheではアドレス生成やトランザクション送信の署名等に使用されるそうです。開発で使ったニーモニックは絶対に本番で使用しないでください。

Ganache起動(永続化)

というわけで、クライアントとGUIを起動します。 f:id:teco_tsuchida:20210827180146p:plain f:id:teco_tsuchida:20210827180216p:plain

RPCを試す

せっかくなのでRPCも試しておきます。Ganache-cliで実装されているメソッドは下記辺りを参考にしてください。

github.com github.com

まずはマイニング報酬を受け取るアカウントを確認します。

curl -X POST http://127.0.0.1:8545 --data '{"jsonrpc":"2.0","method":"eth_coinbase","id":0}'

{"id":0,"jsonrpc":"2.0","result":"0xffd2004aa4baa1b1498f290b4e483b6df6d06699"}

表示されたresultのアドレスと、クライアントを起動した際に表示されていたアドレスが一致していることを確認してください。
送金も試しておきます。RPCでのコールの際、valueには10進数を16進数に変換し、0xを追加したものを指定します。
今回は0.01ETHを送金するので、100000000000000002386F26FC10000になり、0xを追加して0x2386F26FC10000となります。

curl -X POST http://127.0.0.1:8545 --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xFfd2004aA4BAa1b1498F290B4E483B6Df6d06699","value":"0x2386F26FC10000","to":"0xd144FB12830e7e2C41a070D44fb2cAe24959585D"}],"id":0}'

{"id":0,"jsonrpc":"2.0","result":"0xaf24403ceceb055b97fb42bfb759763e0402118e901733b9dd521e8b1d7e5bdf"}

GUI上でもトランザクションが処理されたことを確認してみます。 f:id:teco_tsuchida:20210827180829p:plain f:id:teco_tsuchida:20210827180836p:plain

トランザクションも確認でき、ETHも減っていました。

いざ、コントラクト開発

諸々の準備が出来ました。コントラクトを開発していきたいと思いますが、良い題材が思い浮かばなかったので、クリプトゾンビをやっていこうと思います。(クリプトゾンビの後半にはTruffleを使ったチャプターもあるので、そちらを参考にしても良いかもしれません。)

cryptozombies.io

レッスン1のチャプター13まで進めると、シンプルなゾンビを生成することができるコントラクトが完成します。こちらをプライベートチェーンにデプロイしてみましょう!

前回からの続きになりますので、truffle-boxで準備したディレクトリで作業します。まずはデプロイ用のjsを用意し、コントラクトをsolcファイルに転記します。
転記したコントラクトファイルをcontractsディレクトリ配下に配置して下さい。

次に、migrationsディレクトリ配下に下記のデプロイ用ファイルを用意します。

var ZombieFactory = artifacts.require("./ZombieFactory.sol");

module.exports = function(deployer) {
  deployer.deploy(ZombieFactory);
};

最終的な構造は下記のようになると思います。
f:id:teco_tsuchida:20210827184335p:plain

準備ができましたので、コンソールにてtruffle consoleを実行し、いざcompile!

truffle console
truffle(development)> compile

すると、おそらく下記のようなエラーになるかと思います。

truffle(development)> compile

Compiling your contracts...
===========================
> Compiling ./contracts/ZombieFactory.sol

project:/contracts/ZombieFactory.sol:18:28: TypeError: Data location must be "storage" or "memory" for parameter in function, but none was given.
    function _createZombie(string _name, uint _dna) private {
                           ^----------^
,project:/contracts/ZombieFactory.sol:23:33: TypeError: Data location must be "storage" or "memory" for parameter in function, but none was given.
    function _generateRandomDna(string _str) private view returns (uint) {
                                ^---------^
,project:/contracts/ZombieFactory.sol:28:33: TypeError: Data location must be "memory" for parameter in function, but none was given.
    function createRandomZombie(string _name) public {
                                ^----------^

Compilation failed. See above.

こちらはsolidityのバージョンの違いによるものです。ゾンビファクトリーのバージョンはpragma solidity ^0.4.19;となっていませんか?
truffleのsolidityのバージョンを確認してみましょう。

truffle(development)> version
Truffle v5.4.2 (core: 5.4.2)
Solidity v0.5.16 (solc-js)
Node v15.14.0
Web3.js v1.4.0

執筆時点で、v0.5.16でした。実はv0.5.0から破壊的変更がいくつかあるようで、その変更によりエラーとなっているようです。エラーの内容は、memoryを明示する必要があるとのことです。

docs.soliditylang.org

Explicit data location for all variables of struct, array or mapping types is now mandatory. This is also applied to function parameters and return variables. For example, change uint x = m_x to uint storage x = m_x, and function f(uint x) to function f(uint memory x) where memory is the data location and might be replaced by storage or calldata accordingly. Note that external functions require parameters with a data location of calldata.

コントラクトの一部を修正します。コンソールで指摘されている箇所をそれぞれstring memory _xxxxxと修正してください。
それでは、もう一度コンパイル!

truffle(development)> compile

Compiling your contracts...
===========================
> Compiling ./contracts/ZombieFactory.sol

project:/contracts/ZombieFactory.sol:20:9: TypeError: Event invocations have to be prefixed by "emit".
        NewZombie(id, _name, _dna);
        ^------------------------^
,project:/contracts/ZombieFactory.sol:24:36: TypeError: Invalid type for argument in function call. Invalid implicit conversion from string memory to bytes memory requested. 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.
        uint rand = uint(keccak256(_str));
                                   ^--^

Compilation failed. See above.

また違うエラーに・・・。一つずつやりましょう。

まずはTypeError: Event invocations have to be prefixed by "emit".です。
下記ドキュメントから、emitキーワードを明示的に付ける必要があるそうです。

docs.soliditylang.org

該当箇所をemit NewZombie(id, _name, _dna);と修正しましょう。

次にTypeError: Invalid type for argument in function call. 以下略 です。これは先程のv0.5.0の破壊的変更に記載があります。

docs.soliditylang.org

The functions .call(), .delegatecall(), staticcall(), keccak256(), sha256() and ripemd160() now accept only a single bytes argument. Moreover, the argument is not padded. This was changed to make more explicit and clear how the arguments are concatenated.

keccak256()にはbytes型のみ渡すことが出来るとのことで、修正していきます。bytesで渡せばいいので、uint rand = uint(keccak256(bytes(_str)));とすれば良いはずです。

いざ、コンパイル

truffle(development)> compile

Compiling your contracts...
===========================
> Compiling ./contracts/ZombieFactory.sol
> Artifacts written to /home/tsuchida/workspace/react-truffle-box/client/src/contracts
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang

できました!!まだまだ油断はできません。そのままデプロイします。

truffle(development)> migrate

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.

中略

3_deploy_contracts.js
=====================

   Deploying 'ZombieFactory'
   -------------------------
   > transaction hash:    0x3faf47ad2d2c9c080216665d6b696938c80c1ad1e47b5cc552681b9a60c996b6
   > Blocks: 0            Seconds: 0
   > contract address:    0x2E5F40D8e42c5c5795d63F70Ab744F814EbC3d43
   > block number:        6
   > block timestamp:     1629988251
   > account:             0xFfd2004aA4BAa1b1498F290B4E483B6Df6d06699
   > balance:             99.97594284
   > gas used:            351812 (0x55e44)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00703624 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00703624 ETH

Summary
=======
> Total deployments:   3
> Final cost:          0.01224352 ETH

- Blocks: 0            Seconds: 0
- Saving migration to chain.
- Blocks: 0            Seconds: 0
- Saving migration to chain.
- Blocks: 0            Seconds: 0
- Saving migration to chain.

成功しているようです!
トランザクションも確認してみます。コンソールに表示されたトランザクションハッシュは下記です。

0x3faf47ad2d2c9c080216665d6b696938c80c1ad1e47b5cc552681b9a60c996b6

GUIで検索してみましょう。

f:id:teco_tsuchida:20210827181804p:plain GUIでも確認できましたね!スクリーンショットをとっていて気が付いたのですが、イベントログとか取れてないようです。コントラクトの連携をしていないので当たり前なのですが、次回以降調べてみます。

ここまでで、実際にコントラクトを自分で書いて、デプロイするということが出来たと思います。

実行までやってしまえ!

ついでなので、実行もしてみます。truffle consoleにてそのまま実行できます。公式通りにやります。

www.trufflesuite.com

コンソールに表示されたコントラクトアドレス(0x2E5F40D8e42c5c5795d63F70Ab744F814EbC3d43)を指定します。

truffle(development)> let myZombieFactory = await ZombieFactory.at("0x2E5F40D8e42c5c5795d63F70Ab744F814EbC3d43")
undefined

undefinedと出力されますが、問題ありません。取得したオブジェクトを確認してみます。

truffle(development)> myZombieFactory 
TruffleContract {
  constructor: [Function: TruffleContract] {
略

いろいろ出力されたかと思いますが、定義したNewZombie: [Function (anonymous)],等が確認出来るはずです。
それでは、ゾンビを生成しましょう。

truffle(development)> let myZombie = myZombieFactory.createRandomZombie("tsuchida")
undefined
truffle(development)> myZombie
{
  tx: '0x772b89dd762f54bf0156800d6987b47ef0739e3ffef643904c98eb8650e1e9cf',
  receipt: {
    transactionHash: '0x772b89dd762f54bf0156800d6987b47ef0739e3ffef643904c98eb8650e1e9cf',
    transactionIndex: 0,
    blockHash: '0x0e90df84c217cfb7e9dfc8a6e52f8efd45b13ac4c2ed39113b24b0c873b51e72',
    blockNumber: 8,
    from: '0xffd2004aa4baa1b1498f290b4e483b6df6d06699',
    to: '0x2e5f40d8e42c5c5795d63f70ab744f814ebc3d43',
    gasUsed: 87786,
    cumulativeGasUsed: 87786,
    contractAddress: null,
    logs: [ [Object] ],
    status: true,
    logsBloom: '0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    rawLogs: [ [Object] ]
  },
  logs: [
    {
      logIndex: 0,
      transactionIndex: 0,
      transactionHash: '0x772b89dd762f54bf0156800d6987b47ef0739e3ffef643904c98eb8650e1e9cf',
      blockHash: '0x0e90df84c217cfb7e9dfc8a6e52f8efd45b13ac4c2ed39113b24b0c873b51e72',
      blockNumber: 8,
      address: '0x2E5F40D8e42c5c5795d63F70Ab744F814EbC3d43',
      type: 'mined',
      removed: false,
      id: 'log_dd10701a',
      event: 'NewZombie',
      args: [Result]
    }
  ]
}

なにやらトランザクション情報が取得できました。これもGUIで確認してみましょう。 f:id:teco_tsuchida:20210827181917p:plain

ここでスクショとってて気付いたんですが、イベントログとか取れてないですね。コントラクトの連携もしていないので当たり前なのですが、次回以降調べてみます。

より詳しくゾンビが出来ているか、コンソール上で確認してみます。トランザクションハッシュやコントラクトのABIもわかっているので、トランザクションログをデコードして調べることにします。ドンピシャなQiita記事を見つけましたので、そのまま参考にさせていただきます。

qiita.com

truffle(development)> let txReceipt = await web3.eth.getTransactionReceipt("0x772b89dd762f54bf0156800d6987b47ef0739e3ffef643904c98eb8650e1e9cf")
undefined
truffle(development)> txReceipt
{
  transactionHash: '0x772b89dd762f54bf0156800d6987b47ef0739e3ffef643904c98eb8650e1e9cf',
  transactionIndex: 0,
  blockHash: '0x0e90df84c217cfb7e9dfc8a6e52f8efd45b13ac4c2ed39113b24b0c873b51e72',
  blockNumber: 8,
  from: '0xffd2004aa4baa1b1498f290b4e483b6df6d06699',
  to: '0x2e5f40d8e42c5c5795d63f70ab744f814ebc3d43',
  gasUsed: '0x156ea',
  cumulativeGasUsed: 87786,
  contractAddress: null,
  logs: [
    {
      logIndex: 0,
      transactionIndex: 0,
      transactionHash: '0x772b89dd762f54bf0156800d6987b47ef0739e3ffef643904c98eb8650e1e9cf',
      blockHash: '0x0e90df84c217cfb7e9dfc8a6e52f8efd45b13ac4c2ed39113b24b0c873b51e72',
      blockNumber: 8,
      address: '0x2E5F40D8e42c5c5795d63F70Ab744F814EbC3d43',
      data: '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000181898d76b058c00000000000000000000000000000000000000000000000000000000000000087473756368696461000000000000000000000000000000000000000000000000',
      topics: [Array],
      type: 'mined',
      removed: false,
      id: 'log_dd10701a'
    }
  ],
  status: true,
  logsBloom: '0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
}
truffle(development)> let eventAbi = ZombieFactory.abi.filter(element => element.signature == txReceipt.logs[0].topics);
undefined
truffle(development)>  web3.eth.abi.decodeLog(eventAbi[0].inputs, txReceipt.logs[0].data, txReceipt.logs[0].topics)
Result {
  '0': '0',
  '1': 'tsuchida',
  '2': '6782444169266572',
  __length__: 3,
  zombieId: '0',
  name: 'tsuchida',
  dna: '6782444169266572'
}

回りくどいですが、しっかりname: 'tsuchida'で生成されているようです!プライベートチェーン上とはいえ、世界に一つの自分のゾンビです!(プライベートチェーン上なので、世に出ているわけではありませんが・・・)

終わり

というわけで、今回はここまでにしたいと思います。
GanacheGUIがトランザクション確認くらいしか出来ないので、本来の使いやすさを取り戻すべく調べつつ、コントラクト開発やフロントエンド開発を進めていきたいと思います。

ここまでご覧いただき、ありがとうございました。

tecotec.co.jp

その他参考

docs.soliditylang.org docs.soliditylang.org ethereum.stackexchange.com github.com

【社員紹介】PMって大変? テコテックのPMとして働く魅力とは

f:id:teco_kanri:20210730154112j:plain

こんにちは。株式会社テコテック 管理部の多田です。 今回は社員紹介をさせていただきます!

2020年10月に中途で入社し、現在決済認証システム開発事業部のプロジェクトマネージャー(以下、PM)として活躍している岡田さんにお話を伺いました。

■プロフィール■

出身地:兵庫県

好きな食べ物:蕎麦

最近ハマっていること:読書、アニメ、筋トレ、麻雀

自分を表す#:#子育てエンジニア

続きを読む

2021年上半期における量子コンピューターの動向

最近マーベル映画シリーズ沼にハマってしまった、投資戦略システム事業部の亀井です!

普段は投資を主に取り扱う業務のサーバーサイドエンジニアとしてお仕事をさせていただいております。

現状、通常業務で量子コンピューターを扱うことはないのですが、
個人的な好奇心で片足を突っ込んだので、せっかくならと記事にしてみました。

こんな人にオススメ

▶ これから社会に進出される学生様:業界分析や面接のネタに
▶ 投資家の方:来る量子技術イノベーションに向けた業界研究の入り口になるかも
▶ ニッチなもの好きなエンジニアの方:市場的人材価値向上に繋がるかも


この記事ゴール

▶ 量子コンピューターってわからないもんなんだな、と理解する(無知の知)


この時点で、

▶「量子」も「コンピューター」もわかんないのに、「量子コンピューター」なんて到底わかりっこない!
▶ それ学んでなんの役に立つの?
▶ お金にならないものなら見たくもないんだけど?


そんな抱きしめてあげたくなるような拒絶反応を示した方が、
「ねぇねぇ、量子コンピューターって知ってる?」とおちゃめに知識をお披露目する姿を想像ながら書きました。

よければ見ていってください。

注意
この記事は2021年8月時点のものとなります。
量子コンピューターを取り巻く状況は激しく移り変わっているため、
この記事の一部の内容が陳腐化する可能性がありますことをご了承ください。



  • 1. 総論
  • 2. 動機
    • 2.1. 人材としての陳腐化
    • 2.2 基本概念の登場から40年
    • 2.3 技術の見極め
  • 3. 量子コンピューターとは
    • 3.1 背景
    • 3.2 量子コンピューター
  • 4. 2021年上半期における動向と学ぶ価値
    • 4.1 量子コンピューターの動向
    • 4.2 メリット
    • 4.3 デメリット
  • 5. さいごに

続きを読む

TruffleSuiteで始めるDapps開発

どうも、ブロックチェーン事業部の土田です。
主にDappsゲームにおける管理画面やAPI開発に携わっています。

今回の記事ですが、Dapps開発のためのローカル開発環境を構築したいと思います。
TruffleSuiteを使ってみたところ、思った以上に簡単に構築出来たので紹介したいと思います。 N番煎じなネタですが、ちょっとハマったポイントもあったので備忘も兼ねて書いていきます。

今回構築する環境

Reactを扱った案件がありましたので、業務で馴染みのあるReactとEthereumを使用します。また、TruffleとGanacheを使ってローカルに作っていきます。

続きを読む

たまには息抜きが必要でしょう

テコテックで一番旅行が好きな人がまたブログ書きます。

 

学生時代から世界50ヵ国を巡り、社会人になってからも飛行機でどこかに出かけておりましたが、、、コロナで2020年の年始を最後に海外旅行に行けておらずウズウズしております。

 

そんな中で緊急事態宣言が明けた後に国内を巡っていました。

そこで感じたことを執筆します。

 

■日本にも絶景がいっぱい

世界中の絶景を情報収集するのが日課ですが、日本にも知られていない景色が数多くありとても興味がそそられ、即実行で絶景を求めて田舎や島巡りなど地方に足を伸ばしました。

その中でも鳥取県の隠岐諸島の景色はまさに芸術。その中でも特に西ノ島の摩天崖!天空の草原ともいわれ、一度肌で感じるべし。

www.e-oki.net

隠岐諸島は絶景の宝庫なので是非遊びに行ってみてください!

 

 

続いては、壱岐島からフェリーで数分の無人島・辰の島。

水は透明度が高く、日が差し込むと海の色はエメラルドと変わりまるで、フィリピンのパラワンに似た絶景です。

こんな絶景を見るために東京から駆け付けたのですが、長崎や福岡出身の同僚は全く知らないと、、、

www.ikikankou.com

 

 

■コロナによって壊れた僕の地元

Goto トラベルが実施されていたこともあり、月に2回程度飛行機に乗って旅行していました。田舎の地元に帰った時には賑やかだった商店街がシャッター通りになっており休業の影響はとても大きかったと痛感しました。

 

学生時代は貧乏旅行をしており、いかに安く旅行するかだけを考えていました。現地でお土産は買わないし、お邪魔だけして最低限の出費だけ。これだと観光業で生計を立てている人は追い詰められてしまう。

 

この光景を目の当たりにしてからは廃業されては困る、いつまでもこの事業を続けてほしいと感じるものには、いままで以上に観光地でお金を使うように努力しています。

 

外食産業でも時短営業やお酒の提供の制限もあり同様です。大手外食企業の決算を見ていても赤字で厳しい印象です。

 

平穏な日常になったらとりあえず、みんな今まで貯めたお金使おうぜ!!経済回そうぜ!!っていう結論です!!

 

■最後に

テコテックは一緒に働ける人材を募集しています。

忙しい時期もありますが、リフレッシュ休暇制度と言って年次有給休暇とは別に休暇を年3日取得できる環境でもあります。

興味がありましたら、下記リンクよりご応募ください。

www.tecotec.co.jp

Vue3 + WebRTC でブラウザからカメラを扱う簡単なデモの紹介

こんにちは。ブロックチェーン事業部の熊谷です。
普段はWebアプリケーションのフロントエンド開発に携わっています。

とあるプロジェクトでWebRTCという、Webブラウザからリアルタイム通信を行うことができる技術に触れる機会がありました。

webrtc.org

そこで今回はそれらの知識整理も兼ね、カメラやマイクを扱うWebRTCのAPIを使い、簡単なデモを作成してみました。

続きを読む

苦しんで覚えるフロント設計 of Nuxt

みなさま、こんばんみ。ブロックチェーン事業部の城市と申します。
フロントエンドエンジニア(多分)として、主にNuxt.js/TypeScriptを用いて開発を行っています。

【前回の反省】では初めてのNuxtと起こったことについて書きました。その反省でAtomicDesign/TypeScript/ElasticSearchを導入しました。テストを書きました。それでどうなったのか。何が変わったのか。今回はそんな話をしていきます。

続きを読む