スマートコントラクトの概要

 

シンプルなスマートコントラクト

最も基本的な例から始めましょう。

ストレージ

pragma solidity ^0.4.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public constant returns (uint) {
        return storedData;
    }
}

 

最初の行は、ソースコードがSolidityバージョン0.4.0または機能を破ることのない新しいバージョン(バージョン0.5.0まで)を記述していることを示しています。 これは新しいコンパイラバージョンでコントラクトが異なる動作をしないようにするためです。pragmaはソースコードをどのように扱うかといったコンパイラへの命令であるためそのように呼び出されます。(プラグマを1回など)

 

Solinityでのコントラクトは、Ethereumブロックチェーンの特定のアドレスにあるコード(とその機能)とデータ(とその状態)の集合です。

 

line uint storedData; uint型のstoredDataと呼ばれる状態変数を宣言します。 データベースを管理するコードの関数を呼び出して照会および変更できるデータベース内の単一のスロットと考えることができます。 Ethereumの場合、これは常に所有コントラクトです。 この場合、関数setとgetを使用して、変数の値を変更または取得することができます。

状態変数にアクセスするには、他の言語では一般的な接頭辞「this.」は必要ありません。

※uint = 256ビットの符号なし整数

 

このコントラクトは、誰もがこの番号を公開しないように(実行可能な)方法なしで世界中の誰かがアクセスできる単一の番号を格納できるようにすることを除けば(Ethereumによって構築されたインフラストラクチャのため) 誰でもsetagainを別の値で呼び出して番号を上書きすることはできますが、番号はブロックチェーンの履歴に保存されます。

 

後で、あなただけが番号を変更できるようにアクセス制限を課す方法を見ていきます。

 

Note

すべての識別子(契約名、関数名、変数名)はASCII文字セットに制限されています。 UTF-8でエンコードされたデータを文字列変数に格納することは可能です。

 

Warning

同じように見える(または同じ文字さえも)文字が異なるコードポイントを持つ可能性があり、異なるバイト配列としてエンコードされるため、Unicodeテキストの使用には注意が必要です。

 

サブ通貨の例

次のコントラクトは、最も単純な形式の暗号化を実装します。 細い空気からコインを生成することは可能ですが、コントラクトを結んだ人だけがそれを行うことができます(異なる発行体系を実装するのは簡単です)。 さらに、誰でもあなたのユーザー名とパスワードを登録する必要なしに、お互いにコインを送ることができます。あなたが必要とするのは、Ethereum鍵ペアだけです。

 

pragma solidity ^0.4.21;

contract Coin {
    // The keyword "public" makes those variables
    // readable from outside.
    address public minter;
    mapping (address => uint) public balances;

    // Events allow light clients to react on
    // changes efficiently.
    event Sent(address from, address to, uint amount);

    // This is the constructor whose code is
    // run only when the contract is created.
    function Coin() public {
        minter = msg.sender;
    }

    function mint(address receiver, uint amount) public {
        if (msg.sender != minter) return;
        balances[receiver] += amount;
    }

    function send(address receiver, uint amount) public {
        if (balances[msg.sender] < amount) return;
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}

 

このコントラクトは新しい概念をいくつかの導入していきます。

The line address public minter; 的にアクセス可能なアドレス型の状態変数を宣言します。 アドレスタイプは、算術演算を許可しない160ビットの値です。

外部の人に属するコントラクトや鍵ペアのアドレスを格納するのに適しています。 publicはコントラクト外から状態変数の現在の値にアクセスするための関数を自動的に生成します。 このキーワードがなければ、他のコントラクトは変数にアクセスできません。

 

コンパイラによって生成される関数のコードは、次のコードとほぼ同じです。

 

function minter() returns (address) { return minter; }

 

もちろん、それとまったく同じような関数を追加することはできません。同じ名前の関数と状態変数を持つことになるからです。しかしうまくいけばコンパイラはそれを理解します。

 

次の行は、mapping(address => uint)public balancesです。 パブリックステート変数も作成されますがより複雑なデータ型です。 型はアドレスを符号なし整数にマップします。 マッピングは、すべての可能なキーが存在し、バイト表現がすべてゼロである値にマッピングされるように、事実上初期化されるハッシュテーブルとして見ることができる。 ただしマッピングのすべてのキーのリストやすべての値のリストを取得することはできないため、あまりにも遠すぎるわけではありません。 したがってマッピングに追加したものや、これが必要でないコンテキストで使用することを心がけてください。(このリストのように、リストを保持するか、より高度なデータ型を使用するか)

 

この場合、publicキーワードによって作成されるgetter関数は少し複雑です。 おおよそ次のようになります。

function balances(address _account) public view returns (uint) {
    return balances[_account];
}

 

ご覧のとおり、このbalances関数を使用すると、単一のアカウントの残高を簡単に照会することができます。

 

The line event Sent(address from, address to, uint amount);

関数sendの最後の行に出力される、いわゆる “eventト”を宣言します。 ユーザーインターフェイス(サーバーアプリケーション)は、あまりコストをかけずにブロックチェーン上で発行されたイベントを聞くことができます。 それが発行されるとすぐに、リスナーはfrom、to、およびamountを受け取るので、トランザクションの追跡が容易になります。 このイベントを聞くためには、

Coin.Sent().watch({}, '', function(error, result) {
    if (!error) {
        console.log("Coin transfer: " + result.args.amount +
            " coins were sent from " + result.args.from +
            " to " + result.args.to + ".");
        console.log("Balances now:\n" +
            "Sender: " + Coin.balances.call(result.args.from) +
            "Receiver: " + Coin.balances.call(result.args.to));
    }
})

 

自動的に生成されたbalances関数がユーザインタフェースからどのように呼び出されるかに注意してください。

 

特殊Coin関数は、コントラクトの作成中に実行されるコンストラクタであり、後で呼び出すことはできません。コントラクト作成者のアドレスを永久に保存します。msg (together with tx and block)は、ブロックチェーンへのアクセスを可能にするいくつかのプロパティを含む魔法のグローバル変数です。 msg.senderは常に現在の(external)関数呼び出しがどこから来たかのアドレスです。

 

最後に、実際にコントラクトを結び、ユーザーと契約者が同じように呼び出すことができる関数は、mintとsendです。 mintがコントラクトを作成したアカウント以外の誰かによって呼び出された場合、何も起こりません。 一方sendは、コインを持っていればコインを誰かに送るために誰でも使うことができます。 このコントラクトを使用して住所にコインを送ると、ブロックチェーンエクスプローラでその住所を見ると何も表示されません。あなたがコインを送ったという事実と変更された残高は、この特定のコイン契約のデータストレージにのみ保存されるからです。 eventを使用することで、新しいコインの取引と残高を追跡する「ブロックチェーンエクスプローラ」を作成するのは比較的簡単です。

 

ブロックチェーンの基本

ブロックチェーンの概念は、プログラマーにとって理解しにくいものではありません。 その理由は、合併症(マイニング、ハッシュ、楕円曲線暗号、ピアツーピアネットワークなど)の大部分が、ある種の機能と約束を提供するためだけにあるからです。 これらの機能を指定されたとおりに受け入れると、基盤技術について心配する必要はありません。また、AmazonのAWSの使用方法を知る必要がありますか?

 

 

トランザクション

ブロックチェーンは、グローバルに共有されるトランザクションデータベースです。 つまり、誰もがネットワークに参加するだけでデータベースのエントリを読み取ることができます。 データベース内の何かを変更したい場合は、他のすべてが受け入れなければならない、いわゆるトランザクションを作成する必要があります。 トランザクションという言葉は、あなたがしたい変更(同時に2つの値を変更したいと仮定)が全く行われていないか、完全に適用されていることを意味します。 さらに、トランザクションがデータベースに適用されている間は、他のトランザクションで変更することはできません。

 

例として、すべての口座の残高を電子通貨で一覧表示する表を想像してみてください。 あるアカウントから別のアカウントへの転送が要求された場合、データベースのトランザクション特性により、あるアカウントから差し引かれた金額が常に他のアカウントに追加されます。 何らかの理由により、対象口座に金額を加算することができない場合、元口座も変更されません。

 

さらに、トランザクションは常に送信者(作成者)によって暗号署名されます。 これにより、データベースの特定の変更に対するアクセスを保護することが容易になります。 電子通貨の例では、簡単なチェックで、口座に鍵を持っている人だけがその口座から資金を移すことができます。

 

ブロック

克服するための大きな障害は、Bitcoinの言葉では「double-spend attack」と呼ばれるものです。ネットワーク内に2つのトランザクションが存在し、両方がアカウントを空にしたい、いわゆる紛争の場合はどうなりますか?

これに対する抽象的な答えは、気にする必要がないということです。トランザクションの順序が選択され、トランザクションは「ブロック」と呼ばれるものにバンドルされ、実行され、参加しているすべてのノードに分散されます。 2つのトランザクションが互いに矛盾する場合、2番目のトランザクションは拒否され、ブロックの一部にはなりません。

これらのブロックは時間的に線形シーケンスを形成し、それは「ブロックチェーン」という単語が由来している場所です。ブロックはかなり規則的な間隔でチェーンに追加されます – Ethereumの場合、これはおよそ17秒ごとです。

「order selection mechanism(注文選択メカニズム)」(これは「マイニング」と呼ばれます)の一環として、ブロックが時折復帰することがありますが、チェーンの「先端」でのみ発生します。上に追加されるブロックが多いほど、そのブロックは少なくなります。したがって、取引が元に戻ったりブロックチェーンから取り除かれたりする可能性もありますが、待つ時間が長くなるほど取引の可能性は低くなります。

 

 

The Ethereum Virtual Machine

概要

Ethereum仮想マシンまたはEVMは、Ethereumのスマートコントラクトの実行環境です。 サンドボックス化されているだけでなく、実際には完全に分離されているため、EVM内で実行されているコードではネットワーク、ファイルシステムなどのプロセスにアクセスできません。 スマートコントラクトは、他のスマートコントラクトへのアクセスが制限されています。

 

アカウント

同じアドレス空間を共有するEthereumには、公開鍵と秘密鍵のペア(つまり人間)によって制御される外部アカウントと、アカウントと共に格納されるコードによって制御されるコントラクトアカウントの2種類があります。

外部アカウントのアドレスは、公開鍵から決定され、コントラクトのアドレスは、コントラクトが作成された時点で決定される(クリエイターのアドレスおよびそのアドレスから送信されたトランザクションの数、 ノンス “)。

アカウントにコードが格納されているかどうかにかかわらず、2つのタイプはEVMによって等しく扱われます。

すべてのアカウントには、256ビットのワードをストレージと呼ばれる256ビットのワードにマッピングする永続的なキー値ストアがあります。

さらに、すべてのアカウントは、Etherを含むトランザクションを送信することによって変更することができるEtherのバランス(正確には「Wei」)を持っています。

 

トランザクション

トランザクションとは、あるアカウントから別のアカウント(同じまたは特別なゼロアカウント、以下を参照)に送信されるメッセージのことです。バイナリデータ(ペイロード)とEtherを含むことができます。

ターゲットアカウントにコードが含まれている場合、そのコードが実行され、ペイロードが入力データとして提供されます。

ターゲットアカウントがゼロアカウント(アドレス0のアカウント)である場合、トランザクションは新しいコントラクトを作成します。すでに言及したように、そのコントラクトのアドレスはゼロアドレスではなく、送信者から得られたアドレスと送信されたトランザクションの数(「ナンス」)です。このようなコントラクト作成トランザクションのペイロードは、EVMバイトコードとみなされて実行される。この実行の出力は契約のコードとして永久に保存されます。つまり、コントラクトを作成するには、コントラクトの実際のコードを送信するのではなく、実際に実行したときにそのコードを返すコードを送信します。

 

注意

コントラクトが作成されている間、そのコードはまだ空です。そのため、コンストラクターが実行を終了するまで、建設中のコントラクトにコールバックするべきではありません。

 

Gas

作成時には、各取引には一定量のガスが請求されます。その目的は、取引を実行するために必要な作業量を制限し、この実行に対して支払うことです。 EVMが取引を実行している間、ガスは特定のルールに従って徐々に消耗します。

gas priceは、取引の作成者によって設定された値で、送金口座からgas_price * gasを前払いしなければなりません。 実行後にガスが残っている場合も同様に払い戻されます。

ガスが任意の点で使い果たされた場合(それが負である場合)、ガス外の例外がトリガされ、これは現在のコールフレームにおける状態に加えられた全ての変更を元に戻す。

 

 

ストレージ、メモリ、スタック

各アカウントには、ストレージと呼ばれる永続メモリ領域があります。 Storageは、256ビットワードを256ビットワードにマップするキーバリューストアです。コントラクト内からストレージを列挙することは不可能であり、ストレージを変更するためには比較的高価です。コントラクトは、それ自身のものから離れて、いかなる記憶装置にも読み書きすることができません。

 

第2のメモリ領域はメモリと呼ばれ、コントラクトでは各メッセージ呼び出しに対して新たにクリアされたインスタンスが取得されます。メモリは線形であり、バイトレベルでアドレス指定することができますが、読み取りは256ビット幅に制限され、書き込みは8ビットまたは256ビット幅に制限されます。メモリは、以前に触れられていないメモリワード(すなわち、ワード内のオフセット)にアクセス(読み出しまたは書き込みのいずれか)するとき、ワード(256ビット)によって拡張される。拡張時には、ガスのコストを支払わなければならない。メモリは、大きくなるにつれてコストがかかります(2次的に縮尺が変わります)。

 

EVMはレジスタマシンではなくスタックマシンであるため、すべての計算はスタックと呼ばれる領域で実行されます。それは1024要素の最大サイズを持ち、256ビットのワードを含んでいます。スタックへのアクセスは、次のように最上位に限定されています。最上位の16要素の1つをスタックの先頭にコピーするか、最上位の要素をその下の16要素の1つと入れ替えることができます。他のすべての操作は、スタックから最上位2つ(または操作に応じて1つ以上)の要素を取り、その結果をスタックにプッシュします。もちろん、スタック要素をストレージまたはメモリに移動することは可能ですが、最初にスタックの先頭を削除することなく、スタック内のより深い任意の要素にアクセスすることはできません。

 

Instruction Set(指図書)

EVMの命令セットは、コンセンサスの問題を引き起こす可能性がある誤った実装を避けるため、最小限に抑えられています。 すべての命令は基本データ型の256ビットワードで動作します。 通常の算術演算、ビット演算、論理演算、比較演算が存在します。 条件付きおよび無条件のジャンプが可能です。 さらに、コントラクトは、現在のブロックの関連プロパティに、その番号とタイムスタンプのようにアクセスできます。

 

Message Calls(メッセージ通話)

コントラクトは、他のコントラクトを呼び出すことも、Etherコールを非コントラクトアカウントに送ることもできます。メッセージ・コールは、ソース、ターゲット、データ・ペイロード、Ether、ガス、およびリターン・データを持っている点で、トランザクションに似ています。実際、すべてのトランザクションは最上位のメッセージ・コールで構成され、さらにメッセージ・コールを作成できます。

コントラクトでは、内部のメッセージ呼び出しで残っているガスの量と保持する量を決めることができます。内部コール(または他の例外)でアウト・オブ・ガス例外が発生した場合、これはスタックに置かれたエラー値によって通知されます。この場合、コールと一緒に送信されたガスだけが使い切られます。 Solinityでは、呼び出し側のコントラクトによって、このような状況では手動で例外が発生するため、例外が呼び出しスタックを「バブルアップ」します。

すでに述べたように、呼び出されたコントラクト(発信者と同じ)は、新たにクリアされたメモリのインスタンスを受信し、コールペイロードにアクセスします。コールペイロードは、コールデータと呼ばれる別の領域で提供されます。実行が終了すると、呼び出し元のメモリーに格納されたデータを呼び出し元のメモリーに戻すことができます。

コールは深さが1024に制限されています。これは、より複雑な操作では、再帰呼び出しよりもループを優先する必要があることを意味します。

 

Delegatecall / Callcode and Libraries(デリゲートコール/コールコードとライブラリ)

delegatecallという名前の特殊なメッセージ・コールの変種が存在します。これは、ターゲット・アドレスのコードが呼び出し側のコントラクトのコンテキストで実行され、msg.senderとmsg.valueは変更されないという事実以外はメッセージ・コールと同じです その値。

これは、コントラクトが実行時に異なるアドレスから動的にコードをロードできることを意味します。 ストレージ、現在のアドレスと残高は依然として呼び出し元のコントラクトを参照しているため、呼び出されたアドレスからコードが取得されます。

これにより、Solidity:コントラクトのストレージに適用可能な再利用可能なライブラリコードに「ライブラリ」機能を実装することができます。 複雑なデータ構造を実装することができます。

 

Logs(ログ)

すべての方法でブロックレベルまでマップする特別にインデックスされたデータ構造にデータを格納することは可能です。 ログと呼ばれるこの機能は、イベントを実装するためにSolidityによって使用されます。 コントラクトはログデータが作成された後はアクセスできませんが、ブロックチェーンの外部から効率的にアクセスできます。 ログデータの一部はブルームフィルタに格納されるため、効率的かつ暗号的に安全な方法でこのデータを検索することができます。したがって、ブロックチェーン全体をダウンロードしないネットワークピア(「ライトクライアント」)は、これらのログを引き続き見つけることができます。

 

Create(作成する)

契約では、特別なオペコードを使用して他のコントラクトを作成することもできます(つまり、ゼロアドレスを呼び出すだけではありません)。 これらの作成呼び出しと通常のメッセージ呼び出しとの唯一の違いは、ペイロードデータが実行され、結果がコードとして保存され、呼び出し元/作成者がスタック上の新しいコントラクトアドレスを受け取ることだけです。

 

Self-destruct(自己破壊)

コードがブロックチェーンから削除される唯一の可能性は、そのアドレスでのコントラクトがselfdestruct操作を実行する場合です。 そのアドレスに格納されている残りのEtherが指定されたターゲットに送信され、ストレージとコードがその状態から削除されます。

 

警告

コントラクトのコードにselfdestructの呼び出しが含まれていなくても、委任コールまたはcallcodeを使用してその操作を実行できます。

注意

古いコントラクトの枝刈りは、Ethereumのクライアントによって実装される場合とされない場合があります。 さらに、アーカイブノードは、コントラクトストレージとコードを無期限に保つこともできます。

注意

現在、外部アカウントを状態から削除することはできません。