kakts-log

programming について調べたことを整理していきます

What is Ethereum? を読む

概要

この記事はブロックチェーン Advent Calendar 2017 - Qiita 9日目の記事です。

Ethereumについての勉強のため、公式ドキュメントの「What is Ethereum?」を翻訳しました。
What is Ethereum? — Ethereum Homestead 0.1 documentation

ところどころ解釈が不明瞭な箇所のもありますが、ご了承ください。

What is Ethereum?

Ethereumはオープンブロックチェーンネットワークであり、ブロックチェーンテクノロジによる非中央集権的なアプリケーションを誰でも構築・利用することを可能にします。
Bitcoinのように、世界中の多くのユーザによって作られたOSSプロジェクトであり、誰もEthereumをコントロールすることはできません。
しかし、Bitcoinとは違って、Ethereumはよりフレキシブルになっています。Ethereumプラットフォーム上で新たなアプリケーションの構築は非常に容易で、Homesteadリリース(Ethereum platformの2番目のメジャーバージョン)により、 誰でもこのアプリケーションを安全に使えます。

次世代のブロックチェーン

ブロックチェーンテクノロジはBitcoinの技術基盤で、2008年に謎の多い著者であるSatoshi Namamotoによるホワイトペーパー「Bitcoin: A Peer-to-Peer Electronic Cash System」で最初にコンセプトが著されました。
他の分野におけるブロックチェーンユースケースはこのホワイトペーパーで議論されていましたが、数年後にブロックチェーンは一般的な用語として登場しました。
ブロックチェーンは全てのネットワークノードが同一のトランザクションを実行し、記録する分散型コンピューティングアーキテクチャで、トランザクションはブロックとして扱われます。
一度に1ブロックだけ追加され、全てのブロックは前のブロックとつながっていることが数学的に証明するデータを保持しています。この方法で、ブロックチェーンの分散データベースはネットワーク全体のコンセンサスのもとに保持されます。

各ユーザのトランザクションは強固な暗号により守られています。ネットワークを維持し、トランザクションを処理するノードはプロトコル内に実装された数学的に証明されたインセンティブ方式によってインセンティブが付与されます。

Bitcoinにおけるケースでは、分散型データベースは各アカウントの残高、トランザクションを表したテーブルとして使われ、トランザクションBitcoinトークンの転送を意味し、信頼できない個人間でのファイナンスを促進します。
しかしBitcoinが技術者の注目を集める中、新規のプロジェクトでBitcoinを価値の転送以外の用途で使いはじめました。
これらの多くはBitcoinプロトコルの改良と、新機能を追加したalt-coinとして登場しました。
2013年後半、Ethereumの発明者であるVitalik Buterinは、任意の複雑な計算を実行するために再プログラムする機能を備えた単一のブロックチェーンが、これらの多くの他のプロジェクトを包含することを提案しました。

2014年、EthereumのファウンダーであるVitalik Buterin, Gavin Wood, Jeffrey Wilckeがより一般的であり、fully trustlessなスマートコントラクトプラットフォームを実装するという野望をもった次世代ブロックチェーンの開発に取り掛かりました。

Ethereum Virtual Machine

Ethreumはプログラム可能なブロックチェーンです。ユーザに最初から定義されたいくつかのオペレーションセットも提供し(例として、Bitcoinトランザクション)、Ethereumはユーザが独自のオペレーションを作ることを可能にします。
これにより、Ethereumは暗号通貨の他に、様々なタイプの非中央集権的ブロックチェーンアプリケーションのプラットフォームとなります。

Ethereumは狭義では、非中央集権的なアプリケーションのプラットフォームを定義する一連のプロトコルを意味します。 その中心たるものはEthereum Virtual Machine(EVM)と呼ばれ、任意の計算量をもつアルゴリズムを実行できます。
コンピュータサイエンスの用語でいうと、Ethereumは「チューリング完全」です。
開発者はJavascriptPythonをモデルとしたプログラミング言語でEVM上で動くアプリケーションを作ることができます。

他のブロックチェーンと同様に、Ethereumはpeer-to-peerのネットワークプロトコルを含みます。
Ethereumブロックチェーンデータベースはネットワークに接続された複数のノードにより、維持・アップデートされます。
ネットワーク内の全てのノードはEVMを実行し、同一のコードを実行します。これにより、Ethereumはときおり、"World Computer"として表現されます。

このEthereumネットワーク全体を通じて行われる巨大な並列計算は、計算を効率化されていません。
実際には、このプロセスはEthereumにおける計算をさらに遅くさせ、従来のコンピュータに比べてより高負荷になります。
むしろ、すべてのEthereumノードはブロックチェーンを通じたコンセンサスの維持のためにEVMを動かします。
非中央集権的コンサンサスは、Ethereumシステムの究極的なレベルの耐障害性、ゼロダウンタイムを保証し、ブロックチェーンに保存されたデータを永久に変更不可にし、検閲への耐久性も実現します。

Ethereumプラットフォームそれ自体は機能のない、価値のないものです。 プログラミング言語と同様に、そのシステムをどう使われるかは起業家や開発者に委ねられています。
しかし、1つ明らかなことは、あるタイプのアプリケーションはEthereumの機能によって、他のタイプよりも利益を得ることができるということです。
特に、Ethereumはネットワーク内のpeer間同士の直接的なやりとりと、グループ内の協調的行動の促進を自動化するようなアプリケーションに適しています。
例えば、peet-to-peerでの市場を運用したり、複雑な金融契約の自動化するアプリケーションなどがあげられます。
Bitcoinは個人間での通貨のやり取りを銀行や証券会社などの中間業者抜きで行えるようにしました。Ethereumの影響は、もっと大きなものになりえます。
理論的に、金融上のやりとりや計算処理は、Ehereum上で実行されているコードにより自動的かつ確実に実行できます。
金融系のアプリケーションだけでなく、信頼、セキュリティー、永続性が重要とされる全ての分野でEthereumプラットフォームは大きな影響を及ぼします。

How does Ethereum work?

Ethereumは、Bitcoinユーザに馴染みのある多くの機能を組み込んでおり、それらの機能に修正を加えたり、独自機能もあります。

Bitcoinブロックチェーンが純粋にトランザクションのリストである一方、Ethereumの基本単位はアカウントになります。
Ethereumブロックチェーンは全てのアカウントの状態をトラックしており、すべての状態遷移はアカウント間での価値と情報の移転を意味します。

外部アカウント(Externally owned accounts - EOSs) これらはプライベートキーによってコントロールされます

symbolic link (soft link)についてメモ

概要

Linuxプログラミングインタフェース18章2節の シンボリックリンクについて、学んだことのメモ

Linuxプログラミングインタフェース

Linuxプログラミングインタフェース

シンボリックリンクとは

シンボリックリンクとは、他のファイルのファイル名を自身のデータとして持つ特殊なファイル

シンボリックリンクは、ハードリンクとは異なるもので、参照先のファイルのリンクカウントには影響を与えない。
このため、参照先のファイルの情報を ls -liで確認しても、リンクカウントが増えない。

参照先ファイルが削除された場合、シンボリックリンク自体は残るが、参照先をたどることができなくなる。
このようなシンボリックリンクを danglink linkという。
また、最初から存在しないファイルを参照するようなシンボリックリンクも作成可能。

異なるファイルシステム間でのリンクについて

i-nodeは、同一ファイルシステム内のみ一意である。ハードリンクは、i-nodeを参照するため、異なるファイルシステム間のハードリンクはできないようになっている。 シンボリックリンクでは、参照するのはi-nodeでなくファイル名であるため、異なるファイルシステム上のファイルも参照できる。
さらに、ハードリンクではディレクトリに対する参照はできないが、シンボリックリンクではディレクトリを参照することができる。

【メモ】 node8.xにおける async/awaitでtry/catch構文を使ったときのパフォーマンスはどうなるか

メモ

node8.x系においてasync/awaitにといてtry/catchを使う場合のパフォーマンスに関する議論 stackoverflow.com

ざっくりいうと

V8の最新版においては、optimizer compilerであるturbofanによってtry catchの処理の最適化がされているため、V8(5.3以下 ) に比べて try/catchのパフォーマンスが向上している

V8 5.3時点(node.jsでいうと7.x系)でのtry catch周りの処理

https://github.com/v8/v8/blob/5.3-lkgr/src/ast/ast-numbering.cc#L306-L319
DisableCrankshaftによって、Crankshaftによる最適化が無効にされていることがわかる

void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) {
  IncrementNodeCount();
  DisableCrankshaft(kTryCatchStatement);
  Visit(node->try_block());
  Visit(node->catch_block());
}


void AstNumberingVisitor::VisitTryFinallyStatement(TryFinallyStatement* node) {
  IncrementNodeCount();
  DisableCrankshaft(kTryFinallyStatement);
  Visit(node->try_block());
  Visit(node->finally_block());
}

V8 2017/11/23時点でのtry catch周りの処理

最適化の無効処理は消されていて、最適化が走っていると思われる(実際にどう最適化されているかまでは追えていない)
https://github.com/v8/v8/blob/master/src/ast/ast-numbering.cc#L193-L202

void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) {
  DCHECK(node->scope() == nullptr || !node->scope()->HasBeenRemoved());
  Visit(node->try_block());
  Visit(node->catch_block());
}

void AstNumberingVisitor::VisitTryFinallyStatement(TryFinallyStatement* node) {
  Visit(node->try_block());
  Visit(node->finally_block());
}

C言語 crypt()を使ったソースコンパイル時に undefined reference to `crypt' が出たときの対処法

概要

Linuxプログラミングインターフェース の8.5章 「パスワードの暗号化とユーザ認証」のサンプルコードをコンパイルしたときにエラーがでて躓いたので 対処法をメモする

前提

まず最初に、今回コンパイルをした環境は以下のとおりです。

$ cat /etc/redhat-release 
CentOS Linux release 7.4.1708 (Core)

$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)

cryptについて

GNU Cライブラリの一つであるcryptを使って、ユーザから入力した平文パスワードを暗号化できる http://www.gnu.org/software/libc/manual/html_node/crypt.htmlwww.gnu.org

char * crypt (const char *key, const char *salt)

暗号化する際には、変換対象の平文と、ソルトを指定する

Linuxプログラミングインターフェース の8.5章 「パスワードの暗号化とユーザ認証」に示されているサンプルコードは 以下になります。

#define _BSD_SOURCE // <unistd.h>内のgetpass()の宣言を有効にする

#define _XOPEN_SOURCE // <unistd.h>内のcrypt()の宣言を有効にする

#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <shadow.h>
#include "../tlpi_hdr.h"

int main(int argc, char *argv[])
{
  char *username, *password, *encrypted, *p;

  struct passwd *pwd;
  struct spwd *spwd;
  Boolean authOk;
  size_t len;
  long lnmax;

  lnmax = sysconf(_SC_LOGIN_NAME_MAX);
  if (lnmax == -1) {
    // 最小ログイン名長が不明な場合 推測
    lnmax = 256;
  }

  username = malloc(lnmax);
  if (username == NULL) {
    errExit("malloc");
  }

  printf("Username: ");
  fflush(stdout);
  if (fgets(username, lnmax, stdin) == NULL) {
    // EOFならば終了
    exit(EXIT_FAILURE);
  }

  len = strlen(username);
  if (username[len - 1] == '\n') {
    // 行末を削除
    username[len - 1] = '\0';
  }

  pwd = getpwnam(username);
  if (pwd == NULL) {
    fatal("Couldn't get password record");
  }

  spwd = getspnam(username);
  if (spwd == NULL && errno == EACCES) {
    fatal("No permission to read shadow password file");
  }

  if (spwd != NULL) {
    // shadowパスワードが存在すれば shadowパスワードを利用する
    pwd->pw_passwd = spwd->sp_pwdp;
  }
  password = getpass("Password: ");

  // パスワードを暗号化し、平文は即座に破棄する
  encrypted = crypt(password, pwd->pw_passwd);
  for (p = password; *p != '\0';) {
    *p++ = '\0';
  }

  if (encrypted == NULL) {
    errExit("crypt");
  }

  // 入力されたパスワードを暗号化したものと、shadowパスワードが一致するかチェック
  authOk = strcmp(encrypted, pwd->pw_passwd) == 0;

  if (!authOk) {
    printf("Incorrect password\n");
    exit(EXIT_FAILURE);
  }

  printf("Successfully authenticated: UID=%ld\n", (long) pwd->pw_uid);

  // 認証成功 ここで処理を行う
  printf("Now do authenticated work");

  exit(EXIT_SUCCESS);
}

コンパイル時のエラー

この環境で、コンパイルする際に以下のエラーが出て、コンパイルできない状態になっていた。

check_password.c:(.text+0x162): undefined reference to `crypt'

原因

OSやOSバージョン、gccのバージョンにより変わると思いますが、コンパイル時に -lcryptオプションをつかってcryptをlinkさせる必要がありますが、今回コンパイル時に-lcryptオプションを使用していませんでした。
-lcryptをつけると コンパイルができました

$ gcc users_groups/check_password.c error_functions.c -lcrypt

参考

stackoverflow.com

facebook/flow Comment Typesによるpure javascriptでの型チェック

概要

javascriptの静的型付けのチェックを行うツールであるflowというツールを導入し、業務で開発しているプロダクトの品質向上を目指そうと考えています。
既にpure javascriptで書かれた環境があるので、このコードを書き直さずにflowを導入したいと考えており、導入方法について調べた内容をまとめます。
github.com

flowを使った従来の型チェック

一般的に、flowを使ってjavascriptコードの型チェックを行うためには、flow独自のsyntaxでコードを記述し、 実環境へのデプロイは、babelを使ってpure javascriptのコードへトランスパイルするのが一般的です。

// flow独自のsyntaxで記述
// @flow
function square(n: number): number {
  return n * n;
}

square("2"); // Error!
既に本番運用されているプロダクトへのflowの導入の辛さ

しかし、既にpure javascriptで記述して運用されている実環境において、flowを導入するために、イチからflow syntaxで書き直すのは現実的ではありません。
デプロイ処理にbabelを使った pure javascriptへのトランスパイルを挟む必要があるし、テスト周りの処理も見直さなければなりません。
そもそもトランスパイルを挟むなら、型指定のあるtypescriptにまるっとコードを置き換えたほうが良いかと思います。

型チェックのために、コードをflow用に書き直すことなく、pure javascriptでflowの型チェックの利点を活かす事ができれば、既に運用されているnode.jsプロダクトへ楽に導入できるし、プロダクトの品質向上につながります。

私と同じように考えている人は多いらしく、本家のissueや、stackoverflowにも幾つか言及がありました。 github.com

stackoverflow.com

Comment Typesを使った型チェック

前述した要望を見たすために、flowは公式にコメントベースでのsyntaxをサポートしています。
このsyntaxを使うことで、pure javascriptのコードを変えずに、クラスプロパティや、関数、メソッド引数の型チェックが可能になります。
babelによるトランスパイルを挟まなくても型チェックを導入できるので非常に便利です。
flow.org

Comment type include

このComment Typesを使う際には、従来のコメントと区別するために、コメントの先頭にダブルコロン「::」を付ける必要があります。

/*::
type Foo = {
  foo: number,
  bar: boolean,
  baz: string
};
*/

class MyClass {
  /*:: prop: string; */
}

上記のコードは、pure javascriptであり、従来通り実行が可能です。 flowによる型チェックの際に、以下のような形でインクルードされ、型チェックが行われます。

type Foo = {
  foo: number,
  bar: boolean,
  baz: string
};

class MyClass {
  prop: string;
}

ダブルコロンの他に 「flow-include」と記述しても動作します。

/*flow-include
type Foo = {
  foo: number,
  bar: boolean,
  baz: string
};
*/

class MyClass {
  /*flow-include prop: string; */
}
Comment type annotation

前項のように毎回::やflow-includeを使って書く代わりに、コメントの先頭にシングルコロン「:」を使って楽に書くことができます。

例えば下記のように、関数の引数のあとに追加します。

function concat(a /*: string */, b /*: number*/) {
  return a + b;
}

// 引数の型がそれぞれ指定したものと異なっている
concat(1, "a")

flowによる型チェックを実行すると

Error: index.js:24
 24: concat(1, "a")
            ^ number. This type is incompatible with the expected param type of
 13: function concat(a /*: string */, b /*: number*/) {
                           ^^^^^^ string

Error: index.js:24
 24: concat(1, "a")
               ^^^ string. This type is incompatible with the expected param type of
 13: function concat(a /*: string */, b /*: number*/) {
                                            ^^^^^^ number

ちゃんと型チェックが行われ、エラーを出してくれます。

まとめ

flowによる型チェックは、引数のnullチェックなどちゃんとやってくれるようで非常に強力です。
これを実環境のコードやデプロイフローを変えるなく導入できれば、コードのさらなる品質向上が期待できます。

前述したように、flowはComment Typeで、コメントとして記述する方法もちゃんとサポートされているので、 導入するハードルがかなり下がるので、pure javascriptで書いているプロジェクトは導入する余地がかなりあると思います。

Redis Sentinel configファイルのまとめ

概要

Redis Sentinelを用いて、Redisクラスタのモニタリング、master slaveの各プロセスの監視と自動フェイルオーバーを行うことができます。 今回はRedis Sentinelの起動時のconfigファイルについてメモがてらざっくりまとめます。

Redis Sentinel についてざっくりと

Redis Sentinelは、 Redisの運用において高可用性を提供し、Redisに関する障害に関して、人の手を介さずに対応することを可能にします。

さらに、Redisの運用に関する下記の機能を提供します。

  • Monitoring Redisのmaster, slaveが正常に動作しているか常に監視します。
  • Notification 監視対象のRedisノードに問題があった場合に、APIを通じて管理者に通知をしたり、他のプログラムと通信をします。
  • Automatic failover もしRedis masterノードが正常に動かない場合、Sentinelはフェイルオーバープロセスを起動し、slaveをmasterに昇格させます。 複数slaveが存在している場合、masterに昇格しなかったslaveノードに対して、新たなmasterノードのデータをレプリケーションするように再設定します。
  • Configuration provider Sentinelは、Redisクライアントのためのサービスディスカバリを管理します。 例えば、Redisクライアントは現在のRedisサーバのmasterノードを特定するために Sentinelへ接続します。
    もしフェイルオーバーが発生した場合、Sentinelは昇格後の新たなノードへのアドレスをRedisクライアントに伝えます。

Redis Sentinelの設定ファイルについて

Redis Sentinelの起動時に読み込む設定ファイルに付いて解説します。 デプロイする環境によって異なるかと思いますが、 sentinel.confというファイルに設定を記述し、起動時に読み込む形になります。

Redis公式のSentinel解説記事に書いてあるとおり、以下のように記述していきます。 Redis Sentinel Documentation – Redis

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel parallel-syncs resque 5

ここでは、2つのRedisクラスタに対する設定を記述しています。

各ブロックの1行目の項目は、監視対象のmasterノードの設定を記述しています。

sentinel monitor
sentinel monitor <master-group-name> <ip> <port> <quorum>

監視するmasterノード毎に名前をつけて、設定ファイル内でノード毎にフェイルオーバーの設定などを記述します。

ip portは監視対象のノードのip portを指定します。
quorumは、masterノードが死んだことを判定したSentinelの数を設定します。 2を設定していた場合、2つのSentinelが、masterノードがダウンしたと判定した時、フェイルオーバープロセスを始動 させます。

sentilnel down-after-milliseconds
sentinel down-after-milliseconds mymaster 60000

down-after-millisecondsは、各Sentinelがmasterノードに対して、どの間pingによる疎通確認が取れなかったらダウンと判定するかを指定します。

sentinel parallel-syncs
sentinel parallel-syncs <master-group-name> <number-of-syncs>

parallel-syncsは、フェイルオーバーが始動した際に、新たなmasterノードを向くように再設定させるslaveの数を設定します。
この数が小さいほど、フェイルオーバープロセスが完了する時間が長くなります。

まとめ

今回はメモがてら sentinel.confの主な設定項目についてまとめました。 このあたりは設定をいじりながら動かしてみると理解が進むと思うのでまた別記事でまとめたいと思います。