分散ネットワークChristieによるBlockchainの実装
|
Takahiro Ikki, Shinji Kono
琉球大学
|
研究目的
- コンピュータのデータの不整合は, 誤作動や複数人によるデータの同時書き込みによって発生し, 特に分散環境下で問題となる.
- ブロックチェーンはデータの分散ができ, 不整合の検知が可能な仕組みとなっている.
- 当研究室で開発中のGearsOSの分散システムの技術として, ブロックチェーンが使用できるか調査中である.
- 将来的にGearsOSに組み込む予定のある分散フレームワークChristieに分散フレームワークを実装することにした. (?次にも同様の記述がある)
Christie
- Christieは当研究室で開発している分散フレームワークである.
- 現在はjava上で開発されているが, GearsOSに組み込む予定があるため, 言語Continuation based Cへ書き換え可能な構成となっている. (?GeasOSの解説がより欲しい?)
- 言語CbCと近い概念として以下の概念が存在する。
- CodeGear(以下CG)
- CodeGearManager(以下CGM)
- DataGear(以下DG)
- DataGearManager(以下DGM)
Christieの言語概念
- CGはスレッド, クラスに相当し, javaの継承を用いて記述する.
- DGは変数データに相当し, CG内でアノテーションを用いて変数データを取り出せる.
- CGMはノードであり, DG, CG, DGMを管理する.
- DGMはDGを管理するものであり, putという操作により, 変数データ(DG)を格納できる.
- DGMにはLocalDGMとRemoteDGMが存在する。LocalDGMは各ノード固有のデータベースである。RemoteDSMは他ノードのLocalDGMに対応するproxyであり、接続しているノードの数だけ存在する。
- DGMのput操作を行う際にはLocalとRemoteのどちらかを選ぶ.Localであれば、LocalのCGMが管理するDGMに対しDGを格納し, Remoteの場合は接続したRemoteさきのCGMのDGMにDGを格納する.
DGM
- RocalDGMを立ち上げるにはDataSegmentクラスが提供する、connectメソッドを用い、接続したいポートのipアドレスとport番号、そして任意のManager名を指定することで立ち上げる。
- 立ち上げ後はManager名を指定してDataSegmentAPI用いてDSのやり取りを行うため、プログラマはManager名を意識することでLocalへの操作もRemoteへの操作も同様に扱える。
DGのアノテーション
- DGを取り出す際にはCG内で宣言した変数データにアノテーションをつける。DGアノテーションには
Take、Peek、TakeFrom、PeekFrom、の4つがある。
- Take
- Peek
- 先頭のDGを読み込むが、DGが消去されない。そのため特に操作をしない場合、同じデータを参照し続ける。
- TakeFrom
- Takeと似ているが、Remote DGM nameをしているすることで、その接続先のDGM からTake操作をおこえる。
- PeekFrom
- Peekと似ているが、 Remote DGM nameをしているすることで、その接続先のDGM からPeek操作をおこえる。
Christieのコード例
package christie.example.HelloWorld;
import christie.codegear.CodeGearManager;
import christie.codegear.StartCodeGear;
public class StartHelloWorld extends StartCodeGear {
public StartHelloWorld(CodeGearManager cgm) {
super(cgm);
}
public static void main(String[] args){
CodeGearManager cgm = createCGM(10000);
cgm.setup(new HelloWorldCodeGear());
cgm.getLocalDGM().put("helloWorld","hello");
cgm.getLocalDGM().put("helloWorld","world");
}
}
TopologyManager
- TopologyManagerとはTopologyを形成するために、参加を表明したノード、TopologyNodeに名前を与え、必要があればノード同士の配線を行うノードである。
- TopologyManagerのTopology形成方法として、静的Topologyと動的Topologyがある。
- 動的Topologyは参加を表明したノードに対し、動的にノード同士の関係を作る。例えばTreeを構成する場合、参加したノードから順にrootに近い役割を与え、またCodeGearはノードが参加し、parentに接続された後に実行される。
ブロックチェーンのトランザクション
- ブロックチェーンはP2Pにてネットワーク間が動作している。つまり、ブロックチェーンにはサーバー、クライアントの区別がなく全てのノードが平等である。
- ブロックチェーンにおけるブロックは複数のトランザクションをまとめたものである。ブロックの構造は使用するコンセンサスにより変わるが基本的には、previous block hash, merkle root hash, timeが含まれるBlockHeaderとTransactionListにより構成される。
- BlockHeaderには、前のブロックをハッシュ化したもの、トランザクションをまとめたmarkle treeのrootのhash、そのブロックを生成したtimeとなっている。
- previous block hashは、前のブロックのパラメータを並べてhash化したものである。
- 上記のものがそれぞれ連なっていることによって下の図のようなブロック繋がっている。そのため一つが更新されたらそのあとにつながるブロック全てを更新しなければならなくなる。
Blockの動作
- ブロックが生成された場合、知っているノードにそのブロックをブロードキャストする。通信量を抑えるためにブロックを送ったあと、ブロックをシリアライズして送信する場合もある。
- 誤りがあればさらにそのノードがブロックをブロードキャストする。そしてTransaction PoolというTransactionをためておく場所から、そのブロックに含まれるTransactionを削除し、新しいブロックを生成する。
Transaction
- トランザクションとはデータのやり取りを行なった記録の最小単位である。トランザクションの構造は次のとおりである。
- TransactionHash
- data
- sendAddress
- receiveAddress
- signature
- トランザクションの一部と秘密鍵をSHA256でハッシュ化したもの。ECDSAで署名している。
- トランザクションはノード間で伝搬され、ノードごとに検証される。そして検証を終え、不正なトランザクションであればそれを破棄し、検証に通った場合はTransaction Poolに取り込まれ、また検証したノードからトランザクションがブロードキャストする。
コンセンサスアルゴリズム
- fork
- ブロック生成後にブロードキャストを行うと、ブロック高の同じもしくは高いブロックチェーンにたどり着く状態があり、異なるブロックを持った二つのブロックチェーンのうちどちらかを破棄する必要がある。
- fork状態を解消するために用いられるのがコンセンサスアルゴリズムである。
- ブロックチェーンはパブリックブロックチェーンとコンソーシアムブロックチェーンの場合によってコンセンサスアルゴリズムが変わる。
- パブリックブロックチェーン
- 不特定たすのノードが参加するブロックチェーンを指す。
- 不特定多数のノード間、全体のノードの参加数が変わる状況でコンセンサスの変わるアルゴリズムでなければならない。
- コンソーシアムブロックチェーン
- 許可したノードのみが参加できるブロックチェーンである。
Paxos
- Paxosはノードの多数決によってコンセンサスをとるアルゴリズムである。
- Paxosは以下のような問題があっても値を一意に決めることができる。
- 1,プロセス毎に処理の速度が違う。つまりメッセージの返信が遅い可能性がある。
- 2,通信にどれだけの時間がかかるかわからず、その途中でメッセージが失われる可能性がある。
- 3,プロセスは停止する可能性もある。
Paxosの役割ノード
- Paxosは3つの役割ノードがある。
- proposer
- accepter
- lerner
- accepterから値を集計し、過半数以上のaccepterが持っている値を決める。
Paxosの役割定義
- 提案
- 異なる提案ごとにユニークな提案番号と値からなる。提案番号とは、異なる提案を見分けるための識別子であり、単調増加である。
- 値(提案)がacceptされる
- 値(提案)が選択(chosen)される
- 過半数以上のacceptorによって、値がacceptされた場合、それを値(提案)が選択されたと言う。
paxosのアルゴリズム
- paxosのアルゴリズムは2フューズあり、一つ目のフェーズprepare-promiseと二つ目のフェーズaccept-acceptedの二つに区分される。
paxosのアルゴリズム prepare-promise
paxosのアルゴリズム accept-accepted
- (1)proposerは過半数のacceptorから返事が来たのなら、次の提案をaccepterに送る。これをacceptリクエストという。
- (a)もし、約束のみ帰って来ているのならば、任意の値vをprepareリクエストで送った提案に設定する。
- (b)もし、acceptされた提案が帰って来たら、その中で最大の提案番号v’をprepareリクエストで送った提案の値として設定する。
- (2)acceptorはacceptリクエストが来た場合、Promiseした提案よりもacceptリクエストで提案された番号が低ければ、その提案を拒否する。それ以外の場合、acceptする。
Paxos
- Proof of Workと比較しメッセージ通信量と耐障害性のトレードオフになっている。
- Paxosでコンセンサスを取る際、Proof of Workと比較して次のようなメリットがある。
- CPUにリソースを消費しない。
- Transactionの確定に時間がかからない。
- Paxos自体がリーダー選出に向いているアルゴリズムである。そのため、リーダーを決定し、そのノードのブロックチェーンの一貫性のみをかんがえることができる。
Context
- Context とは使用される Code Gear と Data Gear を全て格納した Meta Data Gear である。
- Gears OSは必要なCode Gear、Data Gearに参照したい場合、このContext を通す必要がある。
context の定義
/* context define */
struct Context {
int codeNum; //実行可能な Code Gear の数
__code (**code) (struct Context*); //実行可能な code Gear のリスト
void* heapStart; //Data Gear の Allocate用のヒープ
void* heap;
long heapLimit;
int dataNum; //Data Gear の数
union Data **data; //Data Gear のリスト
};
#Context
- Code/Data Gear の名前は enum で定義される。
- Code/Data Gear の名前とポインタの対応は enum を使って行われる。
enum Code {
C_cg1,
C_cg2,
};
enum Data {
D_dg1,
D_dg2,
};
Data Gear の定義
- Data Gear は union と struxt を用いて定義される
- これをもとに必要な Data Gear の allocate を行う
union Data {
struct Time {
enum Code next;
double time;
} time;
struct LoopCounter {
int i;
} loopCounter;
...
};
Interface
- Code Gear と Data Gear は Interface と呼ばれるまとまりとして記述される。
- Interface は使用される Data Gear の定義と、それに対する Code Gear の集合である。
- Interface の操作に対応する Code Gear の引数は Interface に定義されている Data Gear を通して行われる。
Interface のコード
typedef struct Stack<Type, Impl>{
union Data* stack;
union Data* data;
union Data* data1;
__code whenEmpty(...);
__code clear(Impl* stack,__code next(...));
__code push(Impl* stack,Type* data, __code next(...));
__code pop(Impl* stack, __code next(Type* data, ...));
__code pop2(Impl* stack, __code next(Type* data, Type* data1, ...));
__code isEmpty(Impl* stack, __code next(...), __code whenEmpty(...));
__code get(Impl* stack, __code next(Type* data, ...));
__code get2(Impl* stack, __code next(Type* data, Type* data1, ...));
__code next(...);
} Stack;
Interface の実装例
Stack* createSingleLinkedStack(struct Context* context) {
struct Stack* stack = new Stack();
struct SingleLinkedStack* singleLinkedStack = new SingleLinkedStack();
stack->stack = (union Data*)singleLinkedStack;
singleLinkedStack->top = NULL;
stack->push = C_pushSingleLinkedStack;
stack->pop = C_popSingleLinkedStack;
stack->pop2 = C_pop2SingleLinkedStack;
stack->get = C_getSingleLinkedStack;
stack->get2 = C_get2SingleLinkedStack;
stack->isEmpty = C_isEmptySingleLinkedStack;
stack->clear = C_clearSingleLinkedStack;
return stack;
}
Interface の実装例
__code pushSingleLinkedStack(struct SingleLinkedStack* stack,
union Data* data, __code next(...)) {
Element* element = new Element();
element->next = stack->top;
element->data = data;
stack->top = element;
goto next(...);
}
interface の使用例
- goto interface->code() と記述する。
__code stackTest1(struct Stack* stack) {
Node* node = new Node();
node->color = Red;
goto stack->push(node, stackTest2);
}
stub Code Gear
- Code Gear が必要とする Data Gear を取り出す際に Context を通す必要がある。
- しかし、Meta Data Gear である Context をノーマルレベルの Code Gear から直接アクセスするのはよろしくない。
- そこで Context から必要なデータを取り出して Code Gear に接続する、メタレベルの stub Code Gear を定義し、これを介して間接的に必要な Data Gear にアクセスする。
stub Code Gear の例
__code clearSingleLinkedStack(struct Context *context,
struct SingleLinkedStack* stack,enum Code next) {
stack->top = NULL;
goto meta(context, next);
}
__code clearSingleLinkedStack_stub(struct Context* context) {
SingleLinkedStack* stack =
(SingleLinkedStack*)GearImpl(context, Stack, stack);
enum Code next = Gearef(context, Stack)->next;
goto clearSingleLinkedStack(context, stack, next);
}
Context、stub Code Gear の自動生成
- Gears OS ではノーマルレベルの計算の他に Context や stub などのメタ計算を記述する必要がある。
- 現在の CbC で Gears OS を記述すると、このメタ計算の記述も行わなくてはならず、これには多くの労力を要する。
- この記述を助けるために Context を生成する generate_context と stub Code Gear を生成する generate_stub を perl スクリプトで作成した。
stub Code Gear の生成
- stub Code Gear は Code Gear 間の継続に挟まれ、Code Gear が必要な Data Gear を Context から取り出す処理を行うものである。
- stub Code Gear は Code Gear 毎に記述する必要があり、そのCode Gear の引数を見て取り出す Data Gear を選択する。
- generate_stub は指定された cbc ファイルの __code で記述された Code Gear を取得。
- Code Gear の引数と interface を照らし合わせ、Gearef または GearImpl を決定する。
- cbc ファイルの Code Gear から、生成した stub Code Gear を加えたファイルを生成する。
生成された stub Code Gear
__code clearSingleLinkedStack(struct Context *context,
struct SingleLinkedStack* stack,enum Code next) {
stack->top = NULL;
goto meta(context, next);
}
__code clearSingleLinkedStack_stub(struct Context* context) {
SingleLinkedStack* stack =
(SingleLinkedStack*)GearImpl(context, Stack, stack);
enum Code next = Gearef(context, Stack)->next;
goto clearSingleLinkedStack(context, stack, next);
}
Context の生成
- generate_context は context.h から Data Gear、generate_stub から生成されたファイルから Code Gear を取得し、以下を生成する。
- Code/Data Gear を enum で定義した enumCode.h、enumData.h
- 取得した Code/Data Gear から Context の生成を行う target-context
- Context を生成する際の Data Gear の Allocation を行う dataGearInit.c
今後の課題
- 本研究では CbC を用いた Code Gear と Data Gear を持つ Gears OS の記述を行なった。
- また、Gears OS の記述に必要な Meta の生成を行う perl スクリプトの作成を行なった。
- これにより Gears OS のコードの煩雑さは改善され、ユーザーは Context への接続を意識する必要がなくなった。
- 今後の課題は、今回 perl スクリプトによって Context や stub を含むファイルの生成を行なったが、LLVM/clang 上で実装しコンパイラから直接 CbC を実行できるようにすることを目的とする。
- また、xv6 を Gears OS での書き換えや、継続ではスタックは積まないため、スタックトレースを使わない手法でのデバッグの考案などもある。