このエントリーをはてなブックマークに追加

solidity勉強

細かい話はドキュメントを参照してもらうのが一番正確で早い。

メッセージ

ethreumにはトランザクションとメッセージがある。詳細は以下。

ちなみに、solidityのドキュメントで言うトランザクションはethreumのホワイトペーパのtransactionを含むメッセージ全般のことを言っている。 solidityのコード上msgという特別な変数が出てくるがこれは、このメッセージ全般を指す。

gas

contractのdeploy(トランザクション)やcontract内の関数の実行(メッセージ)にはノードのリソースを消費するためgas料金が発生する。つまり、リアルにお金がかかるということ。ちなみにgas料金は変動していて、今のgas料金がどれぐらいかは、以下からわかる。

gas limit

どれだけgasを使うかを指定する。gasが足りない場合は処理が中断される。トランザクションは巻き戻る。 無限ループ防止のために必要。

単位

一般的にethの単位がよく知られているが、一番最小の単位はwei。solidityのmsg.valueもweiを基準にしている。 そのほかにもbabbageとかlovelaceとかshannonとかszaboとかfinneyとかある。

var unitMap = {
    'noether':      '0',
    'wei':          '1',
    'kwei':         '1000',
    'Kwei':         '1000',
    'babbage':      '1000',
    'femtoether':   '1000',
    'mwei':         '1000000',
    'Mwei':         '1000000',
    'lovelace':     '1000000',
    'picoether':    '1000000',
    'gwei':         '1000000000',
    'Gwei':         '1000000000',
    'shannon':      '1000000000',
    'nanoether':    '1000000000',
    'nano':         '1000000000',
    'szabo':        '1000000000000',
    'microether':   '1000000000000',
    'micro':        '1000000000000',
    'finney':       '1000000000000000',
    'milliether':   '1000000000000000',
    'milli':        '1000000000000000',
    'ether':        '1000000000000000000',
    'kether':       '1000000000000000000000',
    'grand':        '1000000000000000000000',
    'mether':       '1000000000000000000000000',
    'gether':       '1000000000000000000000000000',
    'tether':       '1000000000000000000000000000000'
};

変数の格納場所

変数の格納場所はstorage, memory stackの3種類がある。

  • storage
    • 永続化される高価な領域
  • memory
    • storageより安い揮発性領域
  • stack
    • memoryより安い
    • 1関数に16変数しか定義できないほど小さな領域

  • address
    • ethreumのアドレスを入れる型
  • int, uint
    • int256のalias
    • uint256のalias
  • int8,..(8bit増やし)..,int256
  • byte
    • bytes1のalias
  • bytes1,..(1バイト増やし)..,bytes32
  • string
  • bytes
    • byte[]と同じようなもの
  • array
    • <型>[]で表す
    • storage領域であればpushを使って動的にサイズを拡張できる
  • map
    • mapping(<型> => <型>) で表す

msg

メッセージの情報が格納された変数。

  • msg.sender
    • メッセージ送信者のアドレス
  • msg.value
    • メッセージと一緒に送ったwei数

より詳細は、以下参照

block

ブロックチェーンのブロックにまつわる情報が格納された変数。

  • block.coinbase
    • コインベースを返す
  • block.number
    • 現在のブロック番号
  • block.timestamp
    • 現在のブロックのタイムスタンプ

より詳細は、以下参照

sha3()

keccak-256ダイジェストを取る関数。bytes32が返ってくる。

revert()

状態変更を巻き戻して実行を止める。

require()/assert()

どちらも条件がfalseの場合にrevert()を行うものだが、requireは入力が不正な場合、assertは内部的にあり得ない場合に使う。

Contract

classのようなもの。deployすると、deployしたcontractに対してアドレスが割り当てられる。 継承可能。contract名と同じ関数がコンストラクタ。

pragma solidity ^0.4.11;

contract test {
    string message;
    string name;

    function test() {
        message = "hello";
        name = "bob";
    }

    function getMessage() returns (string) {
        return message;
    }

    function getName() returns (string) {
        return name;
    }
}

contract test2 is test {
    string test;

    function test2() {
        test = "test";
        name = "alice";
    }

    function getAll() returns (string, string, string) {
        return (test, message, name);
    }
}

// getAll
//   Transaction cost: 25430 gas.
//   Execution cost: 4158 gas.
//   Decoded:
//     string: test
//     string: hello
//     string: alice
// getMessage
//   Transaction cost: 22780 gas.
//   Execution cost: 1508 gas.
//   Decoded:
//     string: hello
// getName
//   Transaction cost: 22736 gas.
//   Execution cost: 1464 gas.
//   Decoded:
//     string: alice

Modifier

pythonのデコレータのようなもの。”_”にラップした関数が挿入されているイメージ。 主に事前の条件チェックに使われる。継承で上書することもできる。

pragma solidity ^0.4.11;

contract test {
    string message;
    string name;
    bool ok = true;

    function test() {
        message = "hello";
        name = "bob";
    }

    modifier isOK() {
        assert(ok);
        _; // ここに関数本体が挿入されている
        // _ の後ろに何か書いてもOK
    }

    function getMessage() isOK returns (string) {
        return message;
    }

    function getName() isOK returns (string) {
        return name;
    }
}

contract test2 is test {
    string t;

    function test2() {
        t = "test";
        name = "alice";
        ok = false;
    }

    function getAll() isOK returns (string, string, string) {
        return (t, message, name);
    }
}

// getAll
//   Exception during execution.
// getMessage
//   Exception during execution.
// getName
//   Exception during execution.

address.call()

低レベルな呼び出し。別のコントラクトの関数を呼び出したりできる。 特に使う必要がないなら使わないのが無難。

  • 危険ポイント

    • msg.senderが変わる
    • reentrantが発生する可能性がある
      • 同じ契約の関数を呼び出した場合等
    • 呼び出された側でrevert()しても呼び出し側は自動でrevert処理をしない
      • 例外を伝播できなため
pragma solidity ^0.4.11;

contract test {
    string attr;
    string message;
    string name;
    address sender;

    function test() {
        attr = "hehehe";
        message = "hello";
        name = "alice";
    }

    function first() returns (string, string, string, string, address, address) {
        attr = "hahaha";
        if (this.call(bytes4(sha3("second()")))) {
            return ("success", attr, message, name, msg.sender, sender);
        } else {
            return ("error", attr, message, name, msg.sender, sender);
        }
    }

    function second()  {
        name = "bob";
        message = "zzz";
        sender = msg.sender;
    }

}

// first
// Transaction cost: 79543 gas.
// Execution cost: 58271 gas.
// Decoded:
//   string: success
//   string: hahaha
//   string: zzz
//   string: bob
//   address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
//   address: 0xdc544654fefd1a458eb24064a6c958b14e579154

返却された値のaddressが変わっていることが確認できる。 次にsecond()内でrevert()をしてみる。

pragma solidity ^0.4.11;

contract test {
    string attr;
    string message;
    string name;
    address sender;

    function test() {
        attr = "hehehe";
        message = "hello";
        name = "alice";
    }

    function first() returns (string, string, string, string, address, address) {
        attr = "hahaha";
        if (this.call(bytes4(sha3("second()")))) {
            return ("success", attr, message, name, msg.sender, sender);
        } else {
            return ("error", attr, message, name, msg.sender, sender);
        }
    }

    function second()  {
        name = "bob";
        message = "zzz";
        sender = msg.sender;
        revert();
    }

}

// first
// Transaction cost: 2958284 gas.
// Execution cost: 2937012 gas.
// Decoded:
//   string: error
//   string: hahaha
//   string: hello
//   string: alice
//   address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
//   address: 0x0

firstで変更したattrの値は変わっていないことが確認できる。

address.delegatecall()

address.call()との違いはコンテキストの違いにある。 delegatecallは呼び出し元の契約がコンテキストとなる。 callは呼び出された契約がコンテキストとなる。 つまり、別のコントラクトの関数を借りてきて使うことができる。

  • call()の場合
pragma solidity ^0.4.11;

contract a {
    string name;
    address self;
    address sender;

    function a() {
        name = "bob";
    }

    function update() {
        name = "alice";
        self = this;
        sender = msg.sender;
    }

    function getA() returns (string, address, address) {
        return (name, self, sender);
    }
}

contract b {
    string name;
    address contractA;

    function b(address _contractA) {
        contractA = _contractA;
        name = "jon";
    }

    function call() returns (string, string, address, address, address) {
        name = "smith";
        if (contractA.call(bytes4(sha3("update()")))) {
            return ("success", name, this, contractA, msg.sender);
        } else {
            return ("error", name, this, contractA, msg.sender);
        }
    }
}

// call
// Transaction cost: 86749 gas.
// Execution cost: 65477 gas.
// Decoded:
//   string: success
//   string: smith
//   address: 0x375eae23b65feb1833072328647902f1fe9afa61
//   address: 0xa36c60652b04ec2c04221eab0401fb91132f7a06
//   address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
// getA
// Transaction cost: 23266 gas.
// Execution cost: 2058 gas.
// Decoded:
//   string: alice
//   address: 0xa36c60652b04ec2c04221eab0401fb91132f7a06
//   address: 0x375eae23b65feb1833072328647902f1fe9afa61

selfがcontractAを指していて、msg.senderが呼び出し元コントラクトになっていることがわかる。

  • delegatecallの場合
pragma solidity ^0.4.11;

contract a {
    string name;
    address self;
    address sender;

    function a() {
        name = "bob";
    }

    function update() {
        name = "alice";
        self = this;
        sender = msg.sender;
    }
}

contract b {
    string name;
    address self;
    address sender;
    address contractA;

    function b(address _contractA) {
        contractA = _contractA;
        name = "jon";
    }

    function call() returns (string, string, address, address, address, address, address) {
        name = "smith";
        if (contractA.delegatecall(bytes4(sha3("update()")))) {
            return ("success", name, this, contractA, msg.sender, self, sender);
        } else {
            return ("error", name, this, contractA, msg.sender, self, sender);
        }
    }
}

// call
// Transaction cost: 87368 gas.
// Execution cost: 66096 gas.
// Decoded:
//   string: success
//   string: alice
//   address: 0xe37538be3aee714cef0e8a11c0227d2240fec16f
//   address: 0x22e37c29ad8303c6b58d3cea5a3f86160278af01
//   address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
//   address: 0xe37538be3aee714cef0e8a11c0227d2240fec16f
//   address: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c

thisやmsg.senderが変わっていないことがわかる。 次に、delegatecallする関数にrevert()を仕込んでみる。

pragma solidity ^0.4.11;

contract a {
    string name;
    address self;
    address sender;

    function a() {
        name = "bob";
    }

    function update() {
        name = "alice";
        self = this;
        sender = msg.sender;
        revert();
    }
}

contract b {
    string name;
    address self;
    address sender;
    address contractA;

    function b(address _contractA) {
        contractA = _contractA;
        name = "jon";
    }

    function call() returns (string, string, address, address, address, address, address) {
        name = "smith";
        if (contractA.delegatecall(bytes4(sha3("update()")))) {
            return ("success", name, this, contractA, msg.sender, self, sender);
        } else {
            return ("error", name, this, contractA, msg.sender, self, sender);
        }
    }
}

// call
// Exception during execution.

関数を借りてきているだけなので、ちゃんとexceptionで中止される。

Library

前に説明したdelegatecallやJUMP命令が内部的に使われている。 主にロジックを外に出すときに使う。 internalがついていればJUMP命令が実行され、ついてなければDELEGATECALL命令が実行される。

usingで型にライブラリ関数をバインドできる。バインドするとpythonのselfのように扱えるようになる。 <型の変数>.<関数>()で呼び出しができるようになり、ライブラリ関数の一つ目の引数が”型の変数”になる。

pragma solidity ^0.4.11;

library who {
    struct person {
        string name;
    }

    function init (string _name) internal returns (person) {
        person memory _person;
        _person.name = _name;
        return _person;
    }

    function getName (person _person) internal returns (string) {
        return _person.name;
    }

    function echoBool (bool b) returns (bool) {
        return b;
    }

}

contract test {
    using who for who.person;

    who.person person;

    function test() {
        person = who.init("hoge");
    }

    function getName() returns (string) {
        // JUMP命令
        return person.getName();
    }

    function echoBool () returns (bool) {
        // DELEGATECALL命令
        return who.echoBool(true);
    }
}

// echoBool
// Transaction cost: 23267 gas.
// Execution cost: 1995 gas.
// Decoded:
//   bool: true
// getName
// Transaction cost: 22928 gas.
// Execution cost: 1656 gas.
// Decoded:
//  string: hoge

gas代検証

  • まとめ
    • ストレージの使用は必要最低限にする
    • データ量は必要最低限にする
    • structやarrayを使うよりもmappingをうまく使った方がリーズナブル
      • 割り切って mapping(byte32 => string) を一つ用意してsha3()でkeyを作って入れていく方がリーズナブル
    • loopは重いし、高価だから極力使わない
    • a[x][y]みたいな呼び出しを何度もやるより、var v = a[x][y]で変数化してvを使い回す方がリーズナブル
  • データ量
pragma solidity ^0.4.11;

contract gasTestInputChange {
    string value;

    function setString(string _value){
        value = _value;
    }

    function getString() returns (string) {
        return value;
    }
}

// input "aaa"
// setString
//   Transaction cost: 42677 gas.
//   Execution cost: 20701 gas.
// getString
//   Transaction cost: 22268 gas.
//   Execution cost: 996 gas.

// input "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
// setString
//   Transaction cost: 710010 gas.
//   Execution cost: 623010 gas.
// getString
//   Transaction cost: 27568 gas.
//   Execution cost: 6296 gas.
  • ストレージとストラクとマッピング
pragma solidity ^0.4.11;

contract gasTestMultistorage {
    string value1;
    string value2;
    string value3;

    function setString(string _value1, string _value2, string _value3){
        value1 = _value1;
        value2 = _value2;
        value3 = _value3;
    }

    function getString() returns (string, string, string) {
        return (value1, value2, value3);
    }
}

// input "aaa", "bbb", "ccc"
// setString
//   Transaction cost: 85273 gas.
//   Execution cost: 61889 gas.
// getString
//   Transaction cost: 23938 gas.
//   Execution cost: 2666 gas.

contract gasTestStruct1 {
    struct value {
        string value1;
        string value2;
        string value3;
    }

    value v;

    function setString(string _value1, string _value2, string _value3){
        v = value({
            value1: _value1,
            value2: _value2,
            value3: _value3
        });
    }

    function getString() returns (string, string, string) {
        return (v.value1, v.value2, v.value3);
    }
}

// input "aaa", "bbb", "ccc"
// setString
//   Transaction cost: 85405 gas.
//   Execution cost: 62021 gas.
// getString
//   Transaction cost: 23956 gas.
//   Execution cost: 2684 gas.

contract gasTestStruct2 {
    struct value {
        string value1;
        string value2;
        string value3;
    }

    value v;

    function setString(string _value1, string _value2, string _value3){
        v.value1 = _value1;
        v.value2 = _value2;
        v.value3 = _value3;
    }

    function getString() returns (string, string, string) {
        return (v.value1, v.value2, v.value3);
    }
}

// input "aaa", "bbb", "ccc"
// setString
//   Transaction cost: 85291 gas.
//   Execution cost: 61907 gas.
// getString
//   Transaction cost: 23956 gas.
//   Execution cost: 2684 gas.

contract getTestStatuctMapping {
    struct value {
        string value1;
        string value2;
        string value3;
    }
    value sv;
    mapping (string => string) mv;

    function setStruct(string _value1, string _value2, string _value3) {
        sv.value1 = _value1;
        sv.value2 = _value2;
        sv.value3 = _value3;
    }

    function getStruct() returns (string _value1, string _value2, string _value3) {
        return (sv.value1, sv.value2 = _value2, sv.value3);
    }

    function setMapping(string _value1, string _value2, string _value3) {
        mv["value1"] = _value1;
        mv["value2"] = _value2;
        mv["value3"] = _value3;
    }

    function getMapping() returns (string _value1, string _value2, string _value3) {
        return (mv["value1"], mv["value2"], mv["value2"]);
    }

}

// input "aaa", "bbb", "ccc"
// setStruct
//    Transaction cost: 85291 gas.
//    Execution cost: 61907 gas.
// setMapping
//    Transaction cost: 85628 gas.
//    Execution cost: 62244 gas.
// getStruct
//    Transaction cost: 29070 gas.
//    Execution cost: 7798 gas.
// getMapping
//    Transaction cost: 24331 gas.
//    Execution cost: 3059 gas.
  • もう少し難しい構造
pragma solidity ^0.4.11;

contract gasTestMapping {
    struct value {
        string value1;
        string value2;
        string value3;
    }

    mapping(address => value) values;

    function setString1(string _value1, string _value2, string _value3){
        values[msg.sender].value1 = _value1;
        values[msg.sender].value2 = _value2;
        values[msg.sender].value3 = _value3;
    }

    function setString2(string _value1, string _value2, string _value3){
        var v = values[msg.sender];
        v.value1 = _value1;
        v.value2 = _value2;
        v.value3 = _value3;
    }

    function setString3(string _value1, string _value2, string _value3){
        values[msg.sender] = value({
           value1: _value1,
           value2: _value2,
           value3: _value3
        });
    }

    function getString1() returns (string, string, string) {
        return (values[msg.sender].value1,  values[msg.sender].value2, values[msg.sender].value3);
    }

    function getString2() returns (string, string, string) {
        var v = values[msg.sender];
        return (v.value1, v.value2, v.value3);
    }
}

// input "aaa", "bbb", "ccc"
// setString1
//     Transaction cost: 85558 gas.
//     Execution cost: 62174 gas.
// setString2
//     Transaction cost: 55608 gas.
//     Execution cost: 32224 gas.
// setString3
//     Transaction cost: 55731 gas.
//     Execution cost: 32347 gas.
// getString1
//     Transaction cost: 24267 gas.
//     Execution cost: 2995 gas.
// getString2
//     Transaction cost: 24058 gas.
//     Execution cost: 2786 gas.

contract gasTestArray {
    struct value {
        string value1;
        string value2;
        string value3;
    }
    value[] values;
    uint valuesIndex;

    function getInitLength() returns (uint) {
        return values.length;
    }

    function setString1(string _value1, string _value2, string _value3){
        values.push(value({
           value1: _value1,
           value2: _value2,
           value3: _value3
        }));
    }

    function getString1() returns (string, string, string) {
        return (values[0].value1,  values[0].value2, values[0].value3);
    }

    function getString2() returns (string, string, string) {
        var v = values[0];
        return (v.value1, v.value2, v.value3);
    }
}

// input "aaa", "bbb", "ccc"
// setString1
//    Transaction cost: 105844 gas.
//    Execution cost: 82460 gas.
// getString1
//    Transaction cost: 24459 gas.
//    Execution cost: 3187 gas.
// getString2
//    Transaction cost: 24122 gas.
//    Execution cost: 2850 gas.

contract gasTestMappingIndex {
    struct value {
        string value1;
        string value2;
        string value3;
    }

    mapping(uint => value) values;
    uint valuesIndex;

    function preSetString1(string _value1, string _value2, string _value3){
        values[valuesIndex].value1 = _value1;
        values[valuesIndex].value2 = _value2;
        values[valuesIndex].value3 = _value3;
    }

    function setString1(string _value1, string _value2, string _value3){
        values[valuesIndex].value1 = _value1;
        values[valuesIndex].value2 = _value2;
        values[valuesIndex].value3 = _value3;
        valuesIndex++;
    }

    function setString2(string _value1, string _value2, string _value3){
        var v = values[valuesIndex];
        v.value1 = _value1;
        v.value2 = _value2;
        v.value3 = _value3;
        valuesIndex++;
    }

    function setString3(string _value1, string _value2, string _value3){
        values[valuesIndex] = value({
           value1: _value1,
           value2: _value2,
           value3: _value3
        });
        valuesIndex++;
    }

    function getString1() returns (string, string, string) {
        return (values[valuesIndex].value1,  values[valuesIndex].value2, values[valuesIndex].value3);
    }

    function getString2() returns (string, string, string) {
        var v = values[valuesIndex];
        return (v.value1, v.value2, v.value3);
    }
}

// input "aaa", "bbb", "ccc"
// setString1
//    Transaction cost: 105762 gas.
//    Execution cost: 82378 gas.
// setString2
//    Transaction cost: 90563 gas.
//    Execution cost: 67179 gas.
// setString3
//    Transaction cost: 90686 gas.
//    Execution cost: 67302 gas.
// getString1
//    Transaction cost: 24424 gas.
//    Execution cost: 3152 gas.
// getString2
//    Transaction cost: 24115 gas.
//    Execution cost: 2843 gas.


contract gasTestMappingSha3 {

    mapping(bytes32 => string) values;
    uint valuesIndex;

    function preSetString1(string _value1, string _value2, string _value3){
        values[sha3(valuesIndex, "value1")] = _value1;
        values[sha3(valuesIndex, "value2")] = _value2;
        values[sha3(valuesIndex, "value3")] = _value3;
    }

    function setString1(string _value1, string _value2, string _value3){
        values[sha3(valuesIndex, "value1")] = _value1;
        values[sha3(valuesIndex, "value2")] = _value2;
        values[sha3(valuesIndex, "value3")] = _value3;
        valuesIndex++;
    }

    function getString1() returns (string, string, string) {
        return (values[sha3(valuesIndex, "value1")],  values[sha3(valuesIndex, "value2")] , values[sha3(valuesIndex, "value3")] );
    }
}

// input "aaa", "bbb", "ccc"
// setString1
//    Transaction cost: 76308 gas.
//    Execution cost: 52924 gas.
// getString1
//    Transaction cost: 24753 gas.
//    Execution cost: 3481 gas.

contract gasTestMappingSha3Struct {

    struct value {
        string value1;
        string value2;
        string value3;
    }

    mapping(bytes32 => value) values;
    uint valuesIndex;

    function preSetString1(string _value1, string _value2, string _value3){
        values[sha3(valuesIndex)].value1 = _value1;
        values[sha3(valuesIndex)].value2 = _value2;
        values[sha3(valuesIndex)].value3 = _value3;
    }

    function setString1(string _value1, string _value2, string _value3){
        values[sha3(valuesIndex)].value1 = _value1;
        values[sha3(valuesIndex)].value2 = _value2;
        values[sha3(valuesIndex)].value3 = _value3;
        valuesIndex++;
    }

    function setString2(string _value1, string _value2, string _value3){
        values[sha3(valuesIndex)] = value({
            value1 : _value1,
            value2 : _value2,
            value3 : _value3
        });
        valuesIndex++;
    }

    function getString1() returns (string, string, string) {
        return (values[sha3(valuesIndex)].value1,  values[sha3(valuesIndex)].value2 , values[sha3(valuesIndex)].value3 );
    }

    function getString2() returns (string, string, string) {
        var v = values[sha3(valuesIndex)];
        return (v.value1,  v.value2, v.value3);
    }
}

// input "aaa", "bbb", "ccc"
// setString1
//    Transaction cost: 106096 gas.
//    Execution cost: 82712 gas.
// setString2
//    Transaction cost: 105792 gas.
//    Execution cost: 82408 gas.
// getString1
//     Transaction cost: 24715 gas.
//     Execution cost: 3443 gas.
// getString2
//     Transaction cost: 24200 gas.
//     Execution cost: 2928 gas.
  • storage/memory/stack
pragma solidity ^0.4.11;

contract gasTestLocation {

    byte strg;

    function writeStorage(uint8 _value) {
        strg = byte(_value);
    }

    function writeMem(uint8 _value) {
        bytes memory mmry = new bytes(1);
        mmry[0] = byte(_value);
    }

    function writeStack(uint8 _value) {
        byte stck;
        stck = byte(_value);
    }

    function ReadStorage() returns (uint8) {
        return uint8(strg);
    }

    function writeReadStorage(uint8 _value) returns (uint8) {
        strg = byte(_value);
        return uint8(strg);
    }

    function writeReadMem(uint8 _value) returns (uint8) {
        bytes memory mmry = new bytes(1);
        mmry[0] = byte(_value);
        return uint8(mmry[0]);
    }

    function writeReadStack(uint8 _value) returns (uint8) {
        byte stck;
        stck = byte(_value);
        return uint8(stck);
    }

}

// input 1
// writeStorage
//    Transaction cost: 41788 gas.
//    Execution cost: 20324 gas.
// writeMem
//    Transaction cost: 21867 gas.
//    Execution cost: 403 gas.
// writeStack
//    Transaction cost: 21657 gas.
//    Execution cost: 193 gas.
// readStorage
//    Transaction cost: 21685 gas.
//    Execution cost: 413 gas.
// writeReadStorage
//    Transaction cost: 42008 gas.
//    Execution cost: 20544 gas.
// writeReadMem
//    Transaction cost: 22090 gas.
//    Execution cost: 626 gas.
// writeReadStack
//    Transaction cost: 21767 gas.
//    Execution cost: 303 gas.
  • ループ検証
pragma solidity ^0.4.11;

contract gasTestMemory {

    function mem10() {
        bytes memory v = new bytes(10);
    }

    function mem100() {
        bytes memory v = new bytes(100);
    }

    function mem1000() {
        bytes memory v = new bytes(1000);
    }

    function mem10000() {
        bytes memory v = new bytes(10000);
    }

    function mem100000() {
        bytes memory v = new bytes(100000);
    }

    function mem1000000() {
        bytes memory v = new bytes(1000000);
    }

    function mem10000000() {
        bytes memory v = new bytes(10000000);
    }

    function mem100000000() {
        bytes memory v = new bytes(100000000);
    }

    function init(bytes b, uint size) private {
        for (uint i = 0; i < size; i++) {
            b[i] = byte(0);
        }
    }

    function memInit10() {
        bytes memory v = new bytes(10);
        init(v, 10);
    }

    function memInit100() {
        bytes memory v = new bytes(100);
        init(v, 100);
    }

    function memInit1000() {
        bytes memory v = new bytes(1000);
        init(v, 1000);
    }

}

// mem10
//    Transaction cost: 21665 gas.
//    Execution cost: 393 gas.
// mem100
//    Transaction cost: 21555 gas.
//    Execution cost: 283 gas.
// mem1000
//    Transaction cost: 21599 gas.
//    Execution cost: 327 gas.
// mem10000
//    Transaction cost: 21687 gas.
//    Execution cost: 415 gas.
// mem100000
//    Transaction cost: 21577 gas.
//    Execution cost: 305 gas.
// mem1000000
//    Transaction cost: 21643 gas.
//    Execution cost: 371 gas.
// mem10000000
//    Transaction cost: 21621 gas.
//    Execution cost: 349 gas.
// mem100000000
//    Transaction cost: 21709 gas.
//    Execution cost: 437 gas.
// memInit10
//    Transaction cost: 23331 gas.
//    Execution cost: 2059 gas.
// memInit100
//    Transaction cost: 36796 gas.
//    Execution cost: 15524 gas.
// memInit1000
//    Transaction cost: 171970 gas.
//    Execution cost: 150698 gas.

stack検証

  • まとめ
    • スタックサイズで怒られた場合は関数を小分けにする。
    • modifierもスタックを消費する場合がある
      • この仕様が良くわからない。
pragma solidity ^0.4.11;

contract gasTestStack {

    function args(
        int _a1,
        int _a2,
        int _a3,
        int _a4,
        int _a5,
        int _a6,
        int _a7,
        int _a8,
        int _a9,
        int _a10,
        int _a11,
        int _a12,
        int _a13,
        int _a14,
        int _a15,
        int _a16,
        int _a17) {
        // error
    }

    function vars() {
        int _a1;
        int _a2;
        int _a3;
        int _a4;
        int _a5;
        int _a6;
        int _a7;
        int _a8;
        int _a9;
        int _a10;
        int _a11;
        int _a12;
        int _a13;
        int _a14;
        int _a15;
        int _a16;
        int _a17;
        // error
    }

    function rets() returns(
        int _a1,
        int _a2,
        int _a3,
        int _a4,
        int _a5,
        int _a6,
        int _a7,
        int _a8,
        int _a9,
        int _a10,
        int _a11,
        int _a12,
        int _a13,
        int _a14,
        int _a15,
        int _a16,
        int _a17) {
        // error
    }


    function argsRets(
        int _a1,
        int _a2,
        int _a3,
        int _a4,
        int _a5,
        int _a6,
        int _a7,
        int _a8
        ) returns(
        int,
        int,
        int,
        int,
        int,
        int,
        int,
        int) {
            var i = 1;
            // error
    }

    modifier m1(int _ma1) {
        int _l = 0;
        _;
    }

    function mod1(
        int _a1,
        int _a2,
        int _a3,
        int _a4,
        int _a5,
        int _a6,
        int _a7,
        int _a8
        ) m1(_a1) returns(
        int,
        int,
        int,
        int,
        int,
        int,
        int,
        int) {
            // OK
    }

    modifier m2(int _ma1) {
        int _v = 1;
        _;
    }

    function mod2(
        int _a1,
        int _a2,
        int _a3,
        int _a4,
        int _a5,
        int _a6,
        int _a7,
        int _a8
        ) m1(_a1) m2(_a2) returns(
        int,
        int,
        int,
        int,
        int,
        int,
        int,
        int) {
            // error
    }

    modifier m3(int _ma1) {
        int _i = 1;
        int _j = 1;
        _;
    }

    function mod3(
        int _a1,
        int _a2,
        int _a3,
        int _a4,
        int _a5,
        int _a6,
        int _a7,
        int _a8
        ) m3(_a1) returns(
        int,
        int,
        int,
        int,
        int,
        int,
        int,
        int) {
            // ok
    }
}
name:
email:
comment: