アルゴリズムとかオーダーとか

仕事で勉強したことなどをまとめてます

Libraryを利用したContractをTruffleでデプロイする方法

Libraryを利用したContractをデプロイする時はLibraryのアドレスをリンカーに教えないとContractがデプロイできません。(できても正常に動きません)

TruffleでLibraryとContractをlinkしてデプロイする方法はTruffleの公式ドキュメントにさらーっと書いてるだけなのでcontractの作成からtestまでの流れをまとめました。

今回作るContractはSolidityのlibraryにサンプルとして載ってるlibrary Setとcontract Cです。contract Cは名前が一文字だけだとうまくTruffleでコンパイルできなかったので、UseLibという名前に変更してます。

library Setとそれを使うcontract UseLibのソースは次の通りです。これをcontracts/以下に別々のファイルSet.solとUseLib.solとしてそれぞれ作成します。

  • Set.sol
pragma solidity ^0.4.18;

library Set {
  // We define a new struct datatype that will be used to
  // hold its data in the calling contract.
  struct Data {
  mapping(uint => bool) flags;
  }

  // Note that the first parameter is of type "storage
  // reference" and thus only its storage address and not
  // its contents is passed as part of the call.  This is a
  // special feature of library functions.  It is idiomatic
  // to call the first parameter 'self', if the function can
  // be seen as a method of that object.
  function insert(Data storage self, uint value) public
  returns (bool)
  {
    if (self.flags[value])
    return false; // already there
    self.flags[value] = true;
    return true;
  }

  function remove(Data storage self, uint value) public
  returns (bool)
  {
    if (!self.flags[value])
    return false; // not there
    self.flags[value] = false;
    return true;
  }

  function contains(Data storage self, uint value) public
  constant returns (bool)
  {
    return self.flags[value];
  }
}
  • UseLib.sol
pragma solidity ^0.4.18;

import 'contracts/Set.sol';

contract UseLib {
  Set.Data knownValues;

  function register(uint value) public {
    // The library functions can be called without a
    // specific instance of the library, since the
    // "instance" will be the current contract.
    require(Set.insert(knownValues, value));
  }
  // In this contract, we can also directly access knownValues.flags, if we want.

  function contains(uint value) public constant returns(bool) {
    return Set.contains(knownValues, value);
  }
}

そして、これらをデプロイするためのmigrationのソースです。linkerへの指示はこのmigrationファイルで設定します。

  • 2_deploy_library_set.js
var Set = artifacts.require("Set");
var UseLib = artifacts.require("UseLib");

module.exports = function(deployer) {
  // Use deployer to state migration tasks.
  deployer.deploy(Set);
  deployer.link(Set, UseLib)
  deployer.deploy(UseLib);
};

最後は正常にデプロイできたか確認するためのテストです。

  • test_uselib.js
var UseLib = artifacts.require("UseLib");

contract('UseLibTest', function(accounts) {
  it("Set value and contains check.", function() {
    var strage;
    return UseLib.deployed().then(function(instance) {
      strage = instance;
      return strage.register(5, {from: accounts[0]});
    }).then(function () {
      return strage.contains(5);
    }).then(function(message) {
      assert.equal(message, true, "value 5 is contains");
      return strage.contains(3);
    }).then(function(message) {
      assert.equal(message, false, "value 3 is not contains");
    });
  });
});

Truffleではコンパイル後、contractをデプロイする直前にlibrary addressの識別子(今回だと__Set__________)を実アドレスに置き換えてるみたいです。