説明1

http://hope.2ch.net/test/read.cgi/cryptocoin/1413242198/の説明です。

CREACOINではデータ一式(を表すオブジェクト)を保存したり、ノード間で通信したりするときは
基本的にバイト配列に変換してから保存や送信を行います。
具体的なデータからバイト配列への変換はSHAREDDATA基底クラスのToBinaryメソッドで行うので、データ一式を表すクラスを実装する際にはSHAREDDATA基底クラスを継承するようにし、幾つかのプロパティをオーバーライドします。

一番重要なのはStreamInfoプロパティです。このプロパティでオブジェクトのどのデータを保存するかを指定します。
MainDataInformationの配列を返すFuncを返すようにします。
MainDataInformationでデータの型の指定や実際のデータとバイト配列変換部分との結び付けを行います
(詳細な実装方法は後述)。

その他にIsVersioned、IsVersionSaved、IsCorruptionCheckedなどのプロパティがあります(その他に署名関連のプロパティもありますが、今は使っていないです。使っても良いんですけどね・・・)。
IsVersionedはバージョン機能を有効にするかどうかです。
オブジェクトが保持するデータがバージョンによって変わり得る場合にはtrueにします。
IsVersionSavedはバージョンをバイト配列の中に保持するかです。
バージョン機能を有効にしても必ず出力されるバイト配列の中にバージョンが格納されるとは限りません。
バージョンを格納したい場合には、IsVersionSavedをtrueにします。
IsCorruptionCheckedは破損検査を有効にするかです。
破損検査を有効にすると、バイト配列の中にデータをバイト配列にしたもののSha256ハッシュ値
先頭4ビットが格納されるようになります。使い過ぎると重そうです。

MainDataInformationの基本的な使い方は↓のような感じです。
基本型の場合は第1引数が型で、第2引数がバイト配列変換部分にデータを渡すFuncで第3引数がバイト配列(逆)変換部分からデータをもらうActionです。
(ただ、全ての基本型に対応させていなかったと思います。対応していないと例外を投げます)

new MainDataInfomation(typeof(string), () => this.Name, (o) => this.Name = (string)o),

基本型の配列の場合、配列の長さを渡さなければなりません。可変長の場合はnullを渡します。

new MainDataInfomation(typeof(byte), 32, () => hash, (o) => hash = (byte)o)

SHAREDDATAから派生した型のデータにも対応していますが、その場合は、バージョンを指定しなければなりません。
バージョンがない場合はnullを渡します。

new MainDataInfomation(typeof(TransactionOutput), 0, () => txOutput, (o) => txOutput = (TransactionOutput)o),

SHAREDDATAから派生した型の配列の場合、要素数とバージョンを指定しなければなりません。

new MainDataInfomation(typeof(TransactionOutput), 0, null, () => txOutputs, (o) => txOutputs = (TransactionOutput)o),


MainDataInformationによるデータ指定は結構柔軟だと思います。
MainDataInformationのコンストラクタに渡すFuncやActionは基本的に内部でどのような処理でもできるので、例えば、次のようなこともできます。

new MainDataInfomation(typeof(AnonymousAccountHolder), 0, () => anonymousAccountHolder, (o) =>
{
	anonymousAccountHolder = (AnonymousAccountHolder)o;
	if (anonymousAccountHolder.Version != 0)
		throw new NotSupportedException();
}),

また、StreamInfoプロパティはIEnumerable<MainDataInfomation>を返すFuncなので、次のようなこともできます。
private IEnumerable<MainDataInfomation> StreamInfoInner
{
	get
	{
		bool isInBound = nodeInfo != null;
		yield return new MainDataInfomation(typeof(bool), () => isInBound, (o) => isInBound = (bool)o);
		if (isInBound)
			yield return new MainDataInfomation(typeof(NodeInformation), 0, () => nodeInfo, (o) => nodeInfo = (NodeInformation)o);
		yield return new MainDataInfomation(typeof(int), () => creaVersion, (o) => creaVersion = (int)o);
		yield return new MainDataInfomation(typeof(int), () => protocolVersion, (o) => protocolVersion = (int)o);
		yield return new MainDataInfomation(typeof(string), () => client, (o) => client = (string)o);
		yield return new MainDataInfomation(typeof(bool), () => isTemporary, (o) => isTemporary = (bool)o);
	}
}

何故このような枠組みを作ったのかと思われるかもしれませんが、当初は通信プロトコルの実装も同様な形式でできないかと考えていました。
結局の所、通信プロトコルもどのようなデータを送受信するか、という点が重要なためなるべく宣言的な書き方ができたら分かりやすいだろうと思っていた訳です。
しかし、現時点ではまだ通信プロトコルの宣言的記述は実現できていません。

次世代電子契約及び分散型応用ソフトウェア基盤

 2009年1月、Satoshi NakamotoがBitcoinのブロック鎖(blockchain)を最初に作動させたとき、同時に彼は当時未だ試されたことのなかった急進的な概念を2つ導入していた。1つは「Bitcoin」、即ち、固有の価値や中央発行者のような裏付けを持たずして価値を把持する分散型のP2Pオンライン貨幣である。中央銀行を必要としない貨幣であるという政治的な側面であれ、極めて激しい価格変動であれ、今までのところ、大衆の注目の大部分は貨幣単位としてのBitcoinという部分にあった。しかしながら、Satoshiの壮大な実験には同じく重要なもう1つの部分、即ち、取引(transaction)の順序に関して公衆が同意することを可能にする仕事証明(proof of work)に基づいたブロック鎖の概念があった。応用としてのBitcoinは先願制度と評することができる。ある存在が50BTCを有し、同一の50BTCを同時にA及びBに送付したならば、最初に承認された取引のみが処理されることになる。2つの取引について何れがより早く発生したのかを決定する固有の方法は存在せず、このことが何十年にも亘って分散型の電子貨幣の開発を阻害してきた。Satoshiのブロック鎖は最初の信用できる分散型の解決策であった。そして、現在Bitcoinの技術に対する関心は急激にブロック鎖の部分へと、即ち、ブロック鎖の概念を単なる貨幣以上のもののために使用する方法へと移り始めている。
 良く挙げられる応用としては、ブロック鎖上の電子資産を特別の用途のために作成される貨幣や金融商品を表現するために使用したり(「有色貨幣(colored coin)」)、根底となる物理的なモノの所有権を表現するために使用したり(「電子財産(smart property)」)、領域名(domain name)のような交換不可能な資産を表現するために使用したり(「Namecoin」)することや、分散型交換所、金融派生商品P2P賭博、ブロック鎖上における識別及び評価機能のような、より高度な応用がある。更に、重要な探求領域として「電子契約(smart contract)」がある。電子契約とは、予め制定された規則に従って電子資産を自動的に移動する機能である。例えば、「Aは1日にX貨幣単位まで引き出すことができ、Bは1日にY貨幣単位まで引き出すことができ、A及びBは共同して幾らでも引き出すことができ、AはBの引き出し能力を剥奪することができる」という形式の財務契約を締結することができるかもしれない。電子契約を敷衍したものが分散型自律組織(decentralized autonomous organization:DAO)である。分散型自律組織とは、長期的な電子契約であり、資産を保有し、組織全体の規則をコード化する。Ethereumが提供しようとしているものは、本格的なTuring完全(Turing-complete)の算譜言語(programming language)が組み込まれたブロック鎖であり、「契約」を作成するのに使用することができ、任意の状態遷移関数(state transition function)をコード化するのに使用することができる。使用者は、単に規則を論理的に数行のコードとして書き上げるだけで、如上の如何なる機能でも作成することができ、更には、我々が未だ嘗て想像したことのないような多くの機能を作成することができる。

歴史
 分散型の電子貨幣の概念は、不動産登記簿等の別の応用と同様に、数十年間考えられてきたものである。1980年代及び1990年代の匿名電子貨幣手続きは大部分をChaumの不視化(Chaumian blinding)として知られている基本暗号要素(cryptographic primitive)に依存しており、高度な私事権確保性能を有する電子貨幣を提供したが、集中型の仲介者に依存していたため概して大きな支持を得ることはなかった。1998年、Wei Daiのb-moneyがパズルを解く計算及び分散型の合意を通して貨幣を作ろうという発想を導入した最初の提案となった。しかしながら、この提案には実際に分散型の合意を実現する方法の詳細に関して不備があった。2005年、Hal Finneyは「再使用可能な仕事証明(proof of work)」という概念を導入した。これはb-moneyの発想とAdam Backの計算の困難なHashcashパズルを組み合わせ、暗号貨幣(cryptocurrency)という概念を形作った仕組みであったが、これも又、背部で信用のある計算に依存していたため理想にまでは届かない不十分なものであった。
 貨幣は先願制度を応用したものであり、即ち、多くの場合において取引の順序が極めて重要であるから、分散型の貨幣を実現するには分散型の合意形成を可能にする方法が必要となる。長年に亘ってByzantine耐故障(Byzantine-fault-tolerant)の多数人合意形成方式に関する十分な研究がされてきたにも拘らず、提案されていた全ての手続きは問題の半分しか解決していなかった。Bitcoin以前の全ての貨幣手続きはこの問題に直面し、これを解決することができなかった。これらの手続きは全ての参加者が既知であるということを前提としており、「もしN人が参加しており、悪意のある人間がN/4人より少ないのならば、攻撃があっても耐えることができる」という形での安全性の余地を提供する。しかしながら、問題なのは匿名環境においてはこのような安全性の余地はSybil攻撃(Sybil attack)に対して脆弱であるということであり、即ち、単一の攻撃者であってもサーバ或いはボットネット上に数千の擬似的なノードを作成することができ、ネットワークを一方的に占有するためにこれらのノードを使用することができる。
 Satoshiが齎した革新的な発想は、非常に単純な分散型の合意形成手続きを繋ぎ合わせるというものである。ノードは仕事証明という機構を通して手続きに参加する権利を獲得し、10分毎に取引を組み込んだブロックを生成し、永遠に成長するブロック鎖を作っていく。多くの計算能力を有するノードはそれに比例する影響力を有するが、ネットワーク全体の計算能力を合わせたものよりも多くの計算能力を用意するのは100万個の擬似的なノードを用意するよりも遥かに困難である。Bitcoinのブロック鎖は粗雑であり、単純であるものの、目的の達成のためにはそれで十分であるということが分かってきており、これから5年を掛けて世界中の200を超える通貨や手続きの基盤となることだろう。

状態遷移系(state transition system)としてのBitcoin
 Bitcoinの元帳は技術的な観点から見ると状態遷移系と見做すことができる。即ち、既存の全てのBitcoinに対する、所有者であるという地位から構成される「状態」と、現在の状態及び取引を引数として取り、結果たる新しい状態を返す「状態遷移関数(state transition function)」から成る。例えば、標準的な銀行系においては、状態は貸借対照表であり、取引は口座Aから口座BへのXドルの移動要求であり、状態遷移関数は口座AからXドルを控除し、口座BにXドルを付加する。当初から口座Aの残高がXドル未満であった場合には、状態遷移関数は失敗する。従って、状態遷移関数は次のように形式的に定義することができる。

APPLY(S,TX) -> S' or ERROR

 上に示した銀行系の例の場合は次のようになる。

APPLY({ Alice: $50, Bob: $50 },"send $20 from Alice to Bob") = { Alice: $30, Bob: $70 }

 Bitcoinにおける「状態」は(技術的には「未使用の取引出力(unspent transaction outputs:UTXO)」と言われる)鋳造されたけれども未使用の、全てのBitcoinの集まりである。未使用の取引出力は夫々が額面価格と所有者を有する(所有者は20バイトの口座番号(address)によって表される。これは公開鍵を素にしたものである)。取引には1つ以上の取引入力(transaction input)が含まれ、更には、1つ以上の取引出力(transaction output)が含まれる。取引入力の夫々には既存の未使用の取引出力への参照と、所有者を表す口座番号に対応する秘密鍵による署名が含まれる。取引出力の夫々は新しい状態の一部として追加されることになる新しい未使用の取引出力である。
 状態遷移関数APPLY(S,TX) -> S'は大まかには次のように定義することができる。
  1.TXの取引入力の夫々について
    ・参照している取引出力がSに含まれない場合には、ERRORを返す。
    ・署名が参照している取引出力の所有者によるものでない場合には、ERRORを返す。
  2.TXの全ての取引入力の額面価格の合計がTXの全ての取引出力の額面価格の合計より小さい場合には、ERRORを返す。
  3.SからTXの取引入力が参照している全ての取引出力を取り除き、更にTXの全ての取引出力を追加したものを返す。
 1の1つ目の処理は支払い人が使用済みの取引出力を使用することができないようにし、2つ目の処理は他人の所有する取引出力を使用することができないようにする。2の処理は取引の前後で存在する額面価格が変わらないようにする。この手続きは決済の際には次のように使用される。AliceはBobに11.7BTC送金したいものとすると、Aliceは先ず自己の所有する未使用の取引出力の中から合計すると11.7BTC以上となるようなものの集まりを探し出す。現実的には合計すると丁度11.7BTCとなるようなものはないだろうから、例えば、最も11.7BTCに近いものが6BTC+4BTC+2BTC=12BTCであったとすると、Aliceは次にこれら3つを参照する取引入力と2つの取引出力から成る取引を作成する。1つ目の取引出力の額面価格は11.7BTCであり、口座番号はBobのものとなる。2つ目の取引出力は差し引き0.3BTCの「釣り銭」となり、所有者はAlice自身となる。

採掘
 信頼できる集中型のサービスを利用できる場合には、上述の状態遷移系を実装するのは容易である。単に上に記述されている通りに正確にコード化すれば良いだけである。しかしながら、Bitcoinは分散型の貨幣系を構築しようとしているものである。そのため、利害関係人全員が取引の順序について確実に合意することができることを保証するために、状態遷移系に合意形成系を結合しなければならない。Bitcoinの分散型の合意形成処理においては、ネットワーク上のノードは「ブロック」と呼ばれる取引の集合体の生成を絶えず試行し続けなければならない。ネットワークは約10分毎に1ブロックを生成するように設計されており、夫々のブロックは時刻印(timestamp)、仕事証明の解(nonce)、直前のブロックに対する参照(即ち、直前のブロックの要約値(hash))及び直前のブロックが生成されてから発生した全ての取引の一覧を含む。そして、最新のBitcoinの元帳の状態を表現するために絶えず更新され、時間の経過と共に何処までも成長していく永続的な「ブロック鎖」が形成される。
 ブロック鎖の枠組みにおける、ブロックが有効であるか否かを確認する手続きは次のようになる。
  1.直前のブロックとして参照されているブロックが実在し、有効であることを確認する。
  2.ブロックの時刻印が直前のブロックのものよりも後のものであり、2時間先の未来よりは前のものであることを確認する。
  3.ブロックの仕事証明が有効であることを確認する。
  4.直前のブロックが適用された時点での状態をS[0]とする。
  5.ブロックの取引の一覧をTXとする。TXはn個の取引を有するものとする。0...n-1における全てのiについて、S[i+1] = APPLY(S[i],TX[i])とする。状態遷移関数APPLY(S,TX) -> S'がERRORを返した場合には、偽を返す。
  6.S[n]をブロックが適用された時点での状態とし、真を返す。
 本質的に、ブロックの夫々の取引は有効な状態遷移を生起しなければならない。状態は決してブロックの中に直接的には含まれていないことに注意しなければならない。状態は単に検証を実行するノードによって認知される抽象的な観念であり、初期状態から全てのブロックの全ての取引を逓次に適用していくのが状態を(安全に)算出する唯一の方法である。採掘者が取引をブロックに格納する順序が枢要であることに注意しなければならない。ブロックの中に2つの取引A及びBが存在し、BがAによって作成された未使用の取引出力を使用する場合には、AがBより前に格納されている場合にのみブロックは有効となり、そうでない場合には無効となる。
 ブロック検証手続きにおける興味深い部分は「仕事証明」の概念である。あるブロックの仕事証明が有効であるためには、そのブロックのSHA256要約値を256ビットの数値と見做した場合において、その数値が動的に調整される目標値(target)より小さい値でなければならない。本文書の執筆時点においては、目標値は大凡2の192乗である。この要件の目的はブロックの作成を計算的に「困難」にすることであり、これによりSybil攻撃者は自身の利益になるようにブロック鎖全体を再作成することができなくなる。SHA256は完全に予測不可能な擬似無作為関数となるように作られているので、有効なブロックを作成する唯一の方法は単なる試行錯誤でしかない。即ち、仕事証明の解の候補に繰り返し1を加えていきながら、新たな要約値が要件に適合するか否かを逐一調べていくしかないのである。2の192乗という現在の目標値の下では、仕事証明の解を発見するためには、平均して2の64乗回の試行を繰り返さなければならない。通常の場合、目標値はネットワークによって2016ブロック毎に調整される。これにより新たなブロックは平均して10分毎にネットワーク上の何れかのノードによって生成されることになる。この計算的な仕事に対する褒賞として、夫々のブロックの採掘者は何もない所から自身に対して25BTCを給与する取引を自身の採掘したブロックの中に含めることができる。更には、ブロックに格納されている取引の取引入力の総額面価格が取引出力の総額面価格より大きい場合には、その差額も又、取引手数料として採掘者に給与される。尚、採掘がBTC発行の唯一の方法である。初期状態においては貨幣は1枚も存在しない。
 採掘の目的をより深く理解するために、悪意のある攻撃が実行された場合に何が起きるのかを見てみることにしよう。Bitcoinの根底において利用されている暗号は安全性が確立しているので、攻撃者はBitcoinの仕組みの中で、暗号によって直接には保護されていない部分を標的にすることだろう。即ち、それは取引の順序である。攻撃者の戦略は単純である。
  1.商人に何らかの商品(出来れば、迅速に提供されるデジタル商品が良い)と引き換えに100BTCを送付する。
  2.商品が提供されるまで待機する。
  3.同一の100BTCを自己に送付する別の取引を作成する。
  4.ネットワークに対して、自己に送付する取引こそが最初に発生した取引であると納得させる。
 1の後数分すると、取引は、例えば、ブロック番号が27万のブロックに格納される。約1時間後、更に5つのブロックがブロック鎖に追加される。夫々のブロックは間接的に最初の取引を指し示しており、そのため、最初の取引の「確証度を高めている」。この時点で商人は決済が結了したと認め、商品を提供する。今商品はデジタル商品であると想定しているので、商品の提供は即時に完了する。ここで攻撃者は同一の100BTCを自己に送金する別の新しい取引を作成する。このときただ単に新しい取引をネットワークに公開しただけでは新しい取引が処理されることはない。採掘者は状態遷移関数APPLY(S,TX) -> S'を新しい取引に適用しようとするが、新しい取引が既にSには含まれていない取引出力を使用しようとしていることを検知する。そのため、攻撃者はただ単に新しい取引をネットワークに公開するのではなく、ブロック番号が26万9999のブロックを親ブロックとして参照し、自己のブロック番号が27万となる別の、古い取引の代わりに新しい取引を含む新しいブロックを作成する所から開始し、ブロック鎖の「分岐(fork)」を作成する。ブロックの内容が異なるため、新しいブロックを作成するには仕事証明を再度行う必要がある。その上、新しいブロックは古いブロックとは異なる要約値を有しており、元の、ブロック番号が27万1から27万5までのブロックは新しいブロックを指し示してはいない。そのため、元のブロック鎖と攻撃者の新しいブロック鎖は完全に別物である。ブロック鎖分岐が発生した場合には、最長のブロック鎖が真のブロック鎖であると見做される。そのため、正当な採掘者は元のブロック鎖の下で採掘を行い、攻撃者だけが新しいブロック鎖の下で採掘を行うことになる。攻撃者が元のブロック鎖の長さに追い付いて、自己の新しいブロック鎖を最長のものにするには、ネットワークにおける自己以外の要約値計算能力を併せたものよりも大きな要約値計算能力が必要となる(即ち、「51%攻撃」である)。

Merkle木(Merkle tree)
 Bitcoinの拡張性に関する重要な特徴として、ブロックのデータ構造が複数の階層を成しているという点が挙げられる。実際の所、ブロックの要約値は唯単にブロックの頭部(block header)の要約値である。ブロックの頭部とは、即ち、時刻印、仕事証明の解、直前のブロックの要約値及びMerkle木と呼ばれるデータ構造の根の要約値を含む約200バイトのデータである。Merkle木は二分木の一種であり、葉として大量のデータを含み、中間的な節として2つの子から計算した要約値を含み、最後に、中間的な節と同様に2つの子から計算した要約値であり、Merkle木の「頂点」を表す根を含む。このMerkle木にブロックの全ての取引が格納される。Merkle木の目的はブロックのデータの一部のみを得られるようにすることである。ノードはブロックの頭部のみをある情報源からダウンロードすることができ、ノードにとって重要な、Merkle木の一部のみを別の異なる情報源からダウンロードすることができる。この場合であっても、全てのデータが正真であることが保証される。この仕組みが上手く働くのは、要約値が葉から根に向かって上に伝播するからである。仮に悪意のある使用者がMerkle木の底辺にある取引を贋物に交換しようとしても、それは真上にある節の変化を惹起し、更に真上にある節の変化を惹起し、最終的には根の変化を惹起し、そのため、ブロックの要約値の変化を惹起し、手続きは完全に異なる別のブロックとしてこれを登録しようとする(殆どの場合は、仕事証明が無効となる)。
 Merkle木はBitcoinの長期的な維持のために、ほぼ確実に不可欠なものである。ネットワークにおいて全てのブロックを完全に保持し、処理する「完全ノード(full node)」は2014年4月現在約15GBの補助記憶装置の領域を消費しており、1ヶ月で1GBを超える領域が新たに必要となる。現在でも幾つかのデスクトップ計算機では完全ノードを動かすことが可能であるが、携帯電話では動かすことができない。将来的には完全ノードとして参加できるのは企業と愛好家だけになるだろう。「簡易決済検証(simplified payment verification:SPV)」と言われる手続きは「軽量ノード(light node)」と呼ばれる別の種類のノードがネットワークに参加することを可能にする。軽量ノードはブロックの頭部をダウンロードし、ブロックの頭部に対する仕事証明を検証し、Merkle木の中で自己に関係のある取引に関連付けられている「枝」のみをダウンロードする。これによって軽量ノードは高い安全性の保証の下で任意の取引がどのような状態にあるかや自己の口座の現在残高が幾らであるかを、ブロック鎖全体の非常に小さな部分のみをダウンロードして確認することができる。

ブロック鎖の別の応用
 ブロック鎖という根本的な考えを暗号貨幣とは異なる別のもののために用いるという更に進んだ考えにも長い歴史がある。2005年、Nick Szaboは「所有者権限のある安全な財産権」という概念を発表した。この概念は、自給自足や所有権の時効取得やジョージア州の地租等のような複雑な枠組みの構成において、「近時の複製データベース技術の進歩」によって、如何にしてブロック鎖に基づいた土地の所有権登記を記録する仕組みが実現されるかを説明するものである。しかしながら、不運にも当時利用可能な効果的な複製データベースは存在せず、この枠組みが実際に実装されることはなかった。ところが、2009年、Bitcoinの分散型合意形成系が開発されると、多くの別の新たな応用が急速に出現し始めた。
 ・Namecoin
  2010年に作られた、分散型名称登録データベースと言うべきものである。Tor、Bitcoin及びBitMessage等の分散型の枠組みにおいては、口座(account)を識別する何らかの手段が必要となる。それによって、他者との間で口座を使ったやり取りを行うことができる。しかしながら、既存の全ての口座識別の手段においては、利用できる識別子の種類は1LW79wp5ZBqaHW1jL5TCiBCrhQYtHagUWyのような擬似無作為な要約値のみである。理想的には「george」のような名称の口座を持つことができるのが好ましいのだが、ある人間が「george」という名称の口座を作ることができる場合、他の人間も同様に同一の過程を経て、自身のために「george」という名称の口座を登録することができ、成り済ましを行うことができるということが問題となる。唯一の解決策は最初に登録しようとした者が登録に成功し、2番目に登録しようとした者は登録に失敗するという、先に申請した者を優先する仕組みであり、この用途のためにはBitcoinの合意形成手続きが完璧に適合する。Namecoinはそのような合意形成手続きを用いて実装された、最古の、そして、最も成功している名称登録系である。
 ・有色貨幣
  有色貨幣はBitcoinのブロック鎖上で誰でも独自の電子貨幣、或いは、単位を有する貨幣の重要で自明な場合である電子代用貨幣を作成できる枠組みとして存立することを目的としている。有色貨幣の枠組みにおいては、ある者がBitcoinの特定の未使用の取引出力に公然と色を割り当てることによって新しい貨幣を「発行」し、他の未使用の取引出力の色は取引入力の色と同一の色となるよう再帰的に定義される(取引入力の色が混合色である場合には、幾つかの特別な規則が適用される)。これによって、使用者は特定の色を有する未使用の取引出力のみを含む財布を保持することができ、通常のBitcoinと殆ど同じように有色貨幣を送付することができる。受け取った全ての未使用の取引出力の色はブロック鎖を後ろに向かって走査することによって決定される。
 ・高次貨幣(metacoin)
  高次貨幣の背景にあるものは、Bitcoinの取引を高次貨幣の取引を格納するために使用し、Bitcoinのものとは異なる別の状態遷移関数APPLY'を作って、Bitcoinの上に存立する別の異なる枠組みを作るという発想である。高次貨幣の枠組みは無効な高次貨幣の取引がBitcoinのブロック鎖の中に現れるのを阻止することができないので、APPLY'(S,TX)がERRORを返す場合にはAPPLY'(S,TX) = Sと、何も適用されなかった場合と同一の結果となるものとする。高次貨幣の発想は任意の暗号貨幣の手続きを作成する単純な機構を提供する。潜在的には、Bitcoinの内部では実装することのできない高度な機能を持たせることができる。一方で、採掘やネットワークの形成のような複雑な事柄は既にBitcoin手続きによって処理されているため、開発費用を非常に安くすることができる。
 そのため、一般的には、合意形成手続きを構築するには2つの方法がある。1つは独立したネットワークを構築する方法であり、もう1つはBitcoinの上に手続きを構築する方法である。1つ目の方法は、Namecoinのような応用においてはかなり上手く行っているけれども、実装は困難である。夫々の実装は独立したブロック鎖を作動させる必要があり、又、全ての必要な状態遷移とネットワーク形成のコードを作成し、試験する必要がある。更に、我々は分散型合意形成技術の全ての応用はべき乗則に従うと予測している。即ち、大部分の応用は独立したブロック鎖を必要としない程余りにも小規模のものであると思われる。又、分散型の応用、特に分散型自律組織において大きな部分を占めるものとして、互いに情報を交換する必要のあるものがあることに注意しなければならない。
 他方で、Bitcoinに基づいた枠組みはBitcoinの簡易決済検証の機能を継承することができないという難点を有する。簡易決済検証がBitcoinにおいて有効なのは、取引の有効性を見定める際の代用物としてブロック鎖の深さを用いることができるからである。即ち、ある時点において、取引の先祖がブロック鎖の十分に深い部分に埋没されてしまえば、取引は状態の正当な一部であると言っても安全である。それに対して、ブロック鎖に基づいた高次の手続きにおいては、ブロック鎖に自己の手続きの文脈において有効ではない取引が含まれないように強制することはできない。従って、完全に安全な高次手続きのための簡易決済検証の実装はある取引が有効であるか決定するためにBitcoinのブロック鎖を先頭に向かって走査していく必要がある。現在、全てのBitcoinに基づいた高次手続きの「軽い」実装はデータを提供するために用いられる信頼されたサーバに依存している。これは、特に暗号貨幣の主たる目的の1つが信用の必要性を除去することであるような場合においては、ほぼ間違いなく最善ではなく次善の方法でしかない。

スクリプトの作成
 実際のところBitcoin手続きは、一切の拡張を行わずとも、弱い種類の「電子契約」概念の実現を容易にする。Bitcoinにおける未使用の取引出力は単に公開鍵によって所有されるだけでなく、スタックに基づいた単純な算譜言語によって表現されたより複雑なスクリプトに所有させることもできる。この枠組みにおいてはそのような未使用の取引出力を使用しようとする取引はスクリプトの条件を充足するデータを提供する必要がある。実際のところ、基本的な公開鍵による所有の機構もスクリプトを使って実装されている。スクリプトは入力として楕円曲線署名を取り、取引と未使用の取引出力を所有する口座番号について検証を実行する。検証が成功した場合には1を返し、失敗した場合には0を返す。多くの更なる用法のために別のより複雑なスクリプトが存在する。例えば、検証が成功するためには所与の3つの秘密鍵の内何れか2つの秘密鍵による署名を必要とするスクリプトを構成することができる(複数署名(multisig))。このような構成は法人口座や安全な普通預金口座や幾つかの第三者預託取引の状況において有効である。更にスクリプトは計算的な問題の解に対する褒賞金を支払うのに用いることもできる。更には、「このBitcoinの未使用の取引出力は、もしあなたがこの額面価格のDogecoinを私に送金する取引の簡易決済検証による証明を提供することができたならば、あなたのものである」というような内容のスクリプトでさえ構成することができる。これは本質的に分散型の暗号貨幣取引所を実現可能にする。
 しかしながら、Bitcoinにおいて実装されているようなスクリプト言語には幾つかの重大な制限がある。
  ・Turing完全性(Turing-completeness)の欠如
   即ち、あらゆる計算の中でBitcoinスクリプト言語が対応しているものの範囲は大きくはあるものの、実際のところそれは殆ど全ての計算に対応していないのである。欠けているものの内で主要なのは反復である。これは取引の検証において無限反復が生じるのを避けるためである。これは理論的にはスクリプトの作成者にとって乗り越えられる障害である。如何なる反復も、単に根底を為すコードを条件分岐を使って何回も繰り返すことによって置き換えることができるのである。しかしながら、これでは必要な記憶領域の大きさという点でスクリプトが非常に非効率的なものになってしまう。例えば、別の楕円曲線署名方式を実装するには256回の乗算工程が必要であり、コード内に全ての工程を1つずつ書いていかなければならないということになる。
  ・値の不可知
   未使用の取引出力のスクリプトには引き出すことのできる額面価格に対する細かい制御を提供する方法が備わっていない。例えば、神託付きの契約の1つの強力な使用例はヘッジ契約である。ヘッジ契約においては、A及びBは1000ドルの価値があるBitcoinを預託し、スクリプトは30日後にその時点で1000ドルの価値のあるBitcoinをAに送金し、残りのBitcoinをBに送金する。1Bitcoinのドルに対する価値を決定するためには神託が必要である。そうは言っても、現在得られるのは完全に集中型の解決策に対する信用及び基盤要件についての大規模な向上である。しかしながら、未使用の取引出力は全か無かであるから、ヘッジ契約を実現するには多くの様々な額面価格(例えば、30までの全てのkに対して、2のk乗の額面価格)の未使用の取引出力を持たせ、神託によって何れの未使用の取引出力をA又はBに送金するかを選択する非常に非効率的な苦渋の方策を採るしかない。
  ・状態の欠如
   未使用の取引出力は、使用されるか、或いは、使用されないままであるかの何れかである。それ以上の何らかの他の内部状態を保持する複数の段階を備えた契約やスクリプトを作成することはできない。そのため、複数の段階を備える選択売買権契約や分散型取引所の指値や(安全な計算的な報奨金のために必要な)2つの段階を備える暗号学的な誓約手続きを作成するのは困難である。そして、未使用の取引出力は単純な1回限りの契約を作成するためだけに使用することができ、分散型組織のようなより複雑な「状態を有する」契約を作成するためには使用することができないということでもある。更には、高次手続きの実装を困難にする。状態が2つしかないということは値の不可知と相俟って、もう1つの重要な応用である引き出し上限を実現するのが不可能であるということである。
  ・ブロック鎖の不可知
   未使用の取引出力は仕事証明の解や直前のブロックの要約値等のブロック鎖のデータを知ることができない。そのため、スクリプト言語に潜在的に価値のある無作為性の源泉が与えられることがなく、賭博や他の幾つかの範疇における応用に著しい制限が課されることになる。
 結局のところ、暗号貨幣を下敷きにして高度な応用物を作成するには3つの方法がある。1つは新たなブロック鎖の作成である。もう1つはBitcoin上でのスクリプトの使用である。最後はBitcoinを基盤とした高次手続きの作成である。新規にブロック鎖を作成すれば、何の制限もされずに自由に一揃いの機能を作成することができるが、開発に時間を費やさなければならないし、新しいブロック鎖の枠組みを始動させるのには多大な労力を要する。スクリプトの使用は実装及び標準化が容易であるが、能力に関して非常に制限がある。高次手続きの作成は容易であるものの、拡張性に関して欠陥がある。そして、我々はEthereumによって3つの枠組み全ての利点を同時に提供する一般化された枠組みを作ろうとしているのである。


Ethereum
 Ethereumはスクリプト作成、代替貨幣(altcoin)、ブロック鎖上の高次手続きという3つの概念を融合し、改良し、開発者がこれらの概念によって提供される拡張性、標準化、機能の完全性、開発の容易さ、相互運用性を全て同時に有する如何なる合意に基づく応用物でも作成できるようにすることを目的としている。Ethereumは組み込みのTuring完全な算譜言語を備えたブロック鎖という本質的に究極的で抽象的で根本的な層を作成することによってこの目的を達成する。この算譜言語によって誰でも電子契約や分散型の応用物を記述することができる。即ち、どのような独自の所有のための規則や取引形式や状態遷移関数でも作成することができる。Namecoinの機能を必要最小限にしたものは2行のコードで記述することができる。貨幣や評価機構等の他の手続きは20行未満のコードで作成することができる。Turing完全性、額面価格の認識、ブロック鎖の認識、状態の保持によって追加される、Bitcoinスクリプト作成によって得られるものより遥かに多くの能力を使えば、電子契約という、額面価格を含み、ある条件が充足されたときにのみその額面価格が使用できるようになる暗号学的な「箱」も作成することができる。

Ethereumの口座
 Ethereumにおける状態は「口座」と呼ばれる物件から構成されている。夫々の口座は20バイトの口座番号を有する。状態遷移は口座間での額面価格と情報の直接的な移動である。Ethereumの口座は4つの要素を含む。
  ・夫々の取引が1度のみ処理されることを保証するために使用される計数器。
  ・口座の現在のetherの残高。
  ・存在する場合には、口座の契約のコード。
  ・口座の記憶領域(初期値は空)
 「ether」はEthereumにおける主たる内部的な暗号燃料(crypto-fuel)であり、取引手数料を支払うのに使用される。口座には概して2つの種類がある。1つは外部的に所有される口座であり、これは秘密鍵によって管理される。もう1つは契約口座であり、これは契約のコードによって管理される。外部的に所有される口座には如何なるコードも含まれない。又、取引を作成して署名を行うことで外部的に所有される口座から通信文を送信することができる。契約口座が通信文を受け取った場合には必ずコードが実行され、コードは内部的な記憶領域に対して読み書きを行い、更に別の通信文を送信するか、或いは、契約を作成する。

通信文及び取引
 Ethereumにおける「通信文」は幾許かBitcoinにおける取引に類似しているが、3つの重要な違いがある。1つ目は、Ethereumの通信文は外部的な存在か、或いは、契約によって作成される可能性がある一方で、Bitcoinの取引は外部的にしか作成される可能性がないということである。2つ目は、Ethereumの通信文はデータを格納することのできる明確な選択肢が用意されているということである。3つ目は、Ethereumの通信文の受信者は、それが契約口座である場合には、返信を行う選択肢があるということである。即ち、Ethereumの通信文は関数の概念を包摂してもいるということである。
 「取引」という用語は、Ethereumで用いられる場合、外部的に所有される口座から送信されることになる通信文を格納する署名されたデータの容器を表す。取引は通信文の受信者、送信者を識別する署名、etherの額面価格及び送信するデータを含む。更に、 夫々STARTGAS、GASPRICEと呼ばれる2つの値も含む。指数関数的な爆発と無限反復を阻止するため、夫々の取引はコード実行における計算的な段階をどれくらいまで生成できるか、最初の通信文だけでなく実行の間に生成される追加の全ての通信文も含めて、上限を設定しなければならない。STARTGASはこの上限であり、GASPRICEは1つの計算的な段階毎に採掘者に支払われる手数料である。取引の実行の最中に燃料が枯渇した場合には、全ての状態は元に戻される。但し、手数料が返ってくることはない。又、取引の実行が停止し、尚幾らかの燃料が残留している場合には、それは送信者に返戻される。契約を作成するために、別々の種類の取引に対応する別々の種類の通信文が用意されてもいる。契約の口座番号は口座の計数器と取引に含まれるデータの要約値に基づいて計算される。
 通信文機構の枢要な帰結はEthereumにおける「第一級物件(first class citizen)」たる性質である。分かり易く言えば、契約は、通信文を送信する能力と他の契約を作成する能力を含めて、外部的な口座と同等の能力を有するということである。これによって、契約は同時に多くの役割を果たすことができる。例えば、分散型組織(1つ目の契約)は特別に作られた量子的に安全なLamport署名(2つ目の契約)を用いる偏執的な個人と安全性のために5つの鍵を有する口座(3つ目の契約)を用いて連署を行う主体の間での第三者預託口座(4つ目の契約)を成員とすることができる。分散型組織と第三者預託口座が契約の夫々の当事者が如何なる種類の口座であるかを考慮する必要がないということはEthereum基盤の利点である。

Ethereumの状態遷移関数
 Ethereumの状態遷移関数APPLY(S,TX) -> S'は次のように定義される。
  1.取引が正常に構成されているか(即ち、正当な額面価格を有するか)確認し、署名が有効であるか確認し、計数器が送付者の口座の計数器に一致するか確認する。そうでない場合には、errorを返す。
  2.取引手数料をSTARTGAS * GASPRICEによって計算し、署名から送付を行っている口座を決定する。送付者の口座残高から手数料を控除し、送付者の計数器の数字を1進める。手数料を支払うのに十分な残高がない場合には、errorを返す。
  3.GAS = STARTGASと初期化し、取引の長さに応じて手数料を支払うため取引のバイト毎に幾らかの量の燃料を控除する。
  4.送付者の口座から受け取り口座に取引の額面価格を移動する。受け取り口座が未だ存在していない場合には、受け取り口座を作成する。受け取り口座が契約である場合には、契約のコードを最後までか、或いは、燃料が枯渇するまで実行する。
  5.送付者が十分な額面価格を有していないか、或いは、コードの実行によって燃料が枯渇したために額面価格の移動が失敗した場合には、手数料の支払い以外の全ての状態の変化を取り消し、採掘者の口座に手数料を付加する。
  6.そうでない場合には、全ての残余燃料に対する手数料を送付者に返戻し、消費された燃料に対する手数料を採掘者に送付する。
 例えば、契約のコードが次のものであると仮定しよう。

if !contract.storage[msg.data[0]]:
contract.storage[msg.data[0]] = msg.data[1]

 契約のコードは実際には低水準のEVMコードとして記述されることに注意しなければならない。上のコードは分かり易くするためEthereumの高水準言語であるSerpentによって記述されている。このコードはEVMコードに変換することができる。契約の記憶領域は空の状態から始まり、送信される取引は10etherの額面価格を有し、2000の燃料を有し、燃料価格は0.001であり、2つのデータ要素 [ 2, 'CHARLIE' ]を有すると仮定する。この場合における状態遷移関数の実行過程は次のようになる。
  1.取引が正常に構成されており、有効であることを確認する。
  2.取引の送付者が少なくとも2000 * 0.001 = 2ether所有しているか確認する。所有している場合には、送付者の口座から2ether控除する。
  3.取引の長さが170バイトであり、バイト当たりの手数料が5であると仮定すると、燃料から850控除する。その結果、残余燃料は1150となる。
  4.送付者の口座から更に10ether控除し、契約の口座に10ether付加する。
  5.コードを実行する。この場合においては単純である。契約の記憶領域の2番地が使用されているか確認し、使用されていないならば、値としてCHARLIEを設定する。これに187燃料を要したと仮定すると、残余燃料の量は1150 - 187 = 963である。
  6.963 * 0.001 = 0.963etherを送付者の口座に返戻し、結果として生じた状態を返す。
 取引の送付先に契約が存在しない場合には、総取引手数料は単に所与のGASPRICEに取引のバイト単位の長さを掛けたものに等しくなり、取引と共に送付されたデータは無意味である。又、契約によって始動された通信文は自身が発生させた副次的な計算に燃料制限を割り当てることができ、副次的な計算が燃料を消尽した場合には、通信文の始動の時点までしか復元はされない。故に、取引と同様に、契約は自身が発生させた副次的な計算に対して厳格な制限を設定することによって制限された形で計算資源を確保することができる。

コードの実行
 Ethereumの契約におけるコードは「Ethereum仮想機械コード(Ethereum virtual machine code)」或いは「EVMコード」と呼ばれる低水準のスタックに基づいたバイトコードの言語によって記述される。この言語のコードはバイト列から成り、夫々のバイトは操作を表す。一般的に、コードの実行とは、(0から始まる)算譜計数器(program counter)が現在指示する操作を実行し、算譜計数器を1だけ進めるのを繰り返す無限反復である。但し、コードの末尾に到達するか、操作に失敗するか、或いは、STOP又はRETURN命令を検出した場合には終了する。操作の実行に際しては、データを格納するための、3つの種類の記憶領域が利用される。
 ・スタック。先入れ先出しによって、複数の値を格納することができる。即ち、新しい要素は先頭に追加され、取得の際にも先頭の要素が取り出される。
 ・一時的な記憶領域。無限に伸展することができるバイト配列である。
 ・契約の永続的な記憶領域。鍵によって参照される値を格納することができる。計算の終了後に初期化されるスタックや一時的な記憶領域とは別様に、格納された値は永続的に保持される。
 コードは更に額面価格や送付者や受信した通信文やブロックの頭部の内容を取得することができ、出力としてバイト配列を返すこともできる。
 EVMコードの形式的な実行様式は驚異的に単純である。Ethereum仮想機械が実行されている間の完全な計算状態は組(block_state, transaction, message, code, memory, stack, pc, gas)によって表される。ここで、block_stateは全ての口座に関する大域的な状態であり、口座残高と永続的な記憶領域を含む。実行の段階毎に、codeのpc番目のバイトを参照することによって現在の命令を識別する。夫々の命令は計算状態に対して如何なる影響を及ぼすかという点において独自の定義を有する。例えば、ADDはスタックから2つの要素を取得し、それらの和をスタックに追加し、gasから1だけ控除し、pcを1だけ進める。SSTOREはスタックから2つの要素を取得し、2番目の要素を契約の永続的な記憶領域の最初の要素によって指示される番地に格納する。実行時翻訳を通してEVMコードを最適化する多くの実装があるものの、EVMの基本的な実装は高々数百行のコードで実現することができる。

ブロック鎖及び採掘
 Ethereumのブロック鎖は多くの点でBitcoinのブロック鎖と類似しているが、幾つか異なる点もある。ブロック鎖の構造に関するEthereumとBitcoinの相違は、Bitcoinの場合とは異なり、Ethereumのブロックは取引の一覧と最近の状態の両方の複製を含んでいるということである。更に、ブロック番号と難易度もブロックに格納される。Ethereumにおけるブロック検証の流れは次のようになる。
  1.直前のブロックとして参照しているブロックが存在し、有効であるか確認する。
  2.ブロックの時刻印(timestamp)が直前のブロックとして参照しているブロックのものよりも大きいか確認し、15分以上未来の時刻ではないか確認する。
  3.ブロック番号、難易度、取引根、叔父ブロック根、燃料制限(様々な低水準のEthereum特有の概念)が有効であるか確認する。
  4.ブロックに対する仕事証明が有効であるか確認する。
  5.S[0]を直前のブロックのSTATE_ROOTとする。
  6.TXをブロックの取引の一覧とする。取引の一覧がn個の取引を有すると仮定する。0...n-1の全てのiに対して、S[i+1] = APPLY(S[i],TX[i])と設定する。何れかのAPPLY関数の適用がerrorを返した場合、或いは、ここに到達するまでに消費した燃料の総量がGASLIMITを超過する場合には、errorを返す。
  7.S_FINALをS[n]とする。但し、採掘者に対して支払われるブロック報酬を付加する。
  8.S_FINALとSTATE_ROOTが一致するか確認する。一致する場合、ブロックは有効であり、そうでない場合、無効である。
 一見すると、全体的な状態を夫々のブロックに格納する必要があるため、このような方法は非常に非効率的であるように思われるかもしれない。しかしながら、効率性はBitcoinと同程度である。何故ならば、状態は木構造化して格納され、夫々のブロックを適用する際には木のほんの一部しか変更する必要がないからである。そのため、一般的に、2つの隣接するブロックの間では、木の大部分は同一であり、そのため、データは1度格納され、ポインタを使って2度参照されるだけで良い。「Patricia木(Patricia tree)」として知られている特別な種類の木を使用し、単に節への変更を効率的にするだけでなく、節の挿入や削除も効率的にするMerkle木の概念への変更を加えることによって、これを達成することができる。加えて全ての状態情報は最新のブロックの一部であるので、過去から現在までのブロック鎖全体を保持する必要がない。仮にこれがBitcoinに適用できるとするならば、必要な記憶領域が10~20倍削減されるという計算になる。


応用
 一般的には、Ethereumを基盤として構築することのできる応用物には3つの種類がある。1つ目は、金融的な応用物である。即ち、使用者に対して、金銭を使った契約を締結したり、管理したりする、より強力な手段を提供するものである。これには、副次的な貨幣、金融派生商品、ヘッジ契約、普通預金財布、遺言、幾つかの種類の本格的な雇用契約が含まれる。2つ目は、半金融的な応用物である。即ち、金銭が係わってくる一方で、非金銭的な側面も大きいものである。例えば、計算的な問題の解に対する独立執行力のある褒賞金がある。3つ目は、完全に金融的ではない応用物である。例えば、オンライン投票や分散型統治がある。

代用貨幣系
 ブロック鎖を基盤とした代用貨幣系は多くの応用を有する。米ドルや金などの資産を表す副次的な貨幣や株式や電子資産を表す各種の代用貨幣や安全で偽造不可能な割引購入券を実現するために利用することができる。更には、従来型の価値に一切結び付けられていない代用貨幣系さえ作成することができ、動機付けのための得点制度として使用することができる。代用貨幣系をEthereumで実装するのは驚異的に簡単である。理解すべき主要な点は貨幣、或いは、代用貨幣系は全て根本的に1つの操作を有するデータベースであるということである。その操作とは、AからX単位を控除し、BにX単位を付加する取引である。但し、次の2点を条件とする。(1)取引前にAが少なくともX単位有する。(2)取引がAに承認されている。代用貨幣系を実装するにはこの機構を契約の中に実装するだけで良い。
 代用貨幣系を実装するための基本的なSerpentコードは次のようになる。

from = msg.sender
to = msg.data[0]
value = msg.data[1]

if contract.storage[from] >= value:
contract.storage[from] = contract.storage[from] - value
contract.storage[to] = contract.storage[to] + value

 これは本質的に、本文書の上部で詳細に説明した「銀行制度」の状態遷移関数の文字通りの実装である。初期段階及び幾つかの特殊な場合において貨幣単位を配布できるようにするためには何行かの追加のコードが必要となる。又、他の契約が口座残高を問い合わせることができるようにする機能を追加するのが理想的である。しかし、唯それだけである。理論的には、Ethereumを基盤とした副次的な貨幣としての役割を果たす代用貨幣系は、潜在的にBitcoinのブロック鎖を基盤とした高次貨幣に欠けているもう1つの重要な特徴を有する。それは、副次的な貨幣で直接取引手数料を支払うことができるということである。これを実装する方法は、契約が手数料として徴収する内部的な貨幣単位を収集し、絶えず開催される競売で転売することによって手数料を支払うために使用されたものと同量のetherを補充し、それを送付者に返戻するというものである。従って、使用者はetherを使って口座を「始動させる」必要があるが、契約は毎回それを返戻することになるため、一旦供給したetherは再使用可能となる。

金融派生商品及び安定的な価値を有する貨幣
 金融派生商品は「電子契約」の最も卑近な応用であり、コードとして実装するのが簡単なものの1つである。金融的な契約を実装する場合における主要な困難は大部分が外部的な価格情報を参照する必要があるということである。例えば、非常に魅力的な応用としてether(や別の暗号貨幣)の米ドルに対する価値の変動の危険を回避する電子契約が挙げられるが、それを実現するためには契約がETH/USDの値を知る必要がある。これを行う最も単純な方法は、特定の陣営(例えば、NASDAQ)によって管理される「データ入力」契約を用いるものである。この契約は当該陣営が必要に応じて契約を更新する能力を有するように作られており、他の契約が通信文を送信し、価格情報が含まれている返信文を受信できるような界面(inferface)を提供する。
 このような重大な構成要素を所与のものとすると、ヘッジ契約は次のようなものとなる。
  1.Aが1000ether入力するのを待機する。
  2.Bが1000ether入力するのを待機する。
  3.1000etherの対米ドル価格をデータ入力契約に問い合わせることによって計算し、永続的な記憶領域に記録する。例えば、これがx米ドルであったと仮定する。
  4.30日後、A及びBが契約を「再起動する」ことを許可する。再起動が行われると、x米ドルに等しい価値を有するetherの量が再びデータ入力契約に問い合わせることによって計算され、その分のetherがAに送付され、残りがBに送付される。
 このような契約は暗号貨幣による商業において顕著な可能性を有する。暗号貨幣に関して言及される主要な問題の1つとして、価格が変動しやすいという事実がある。多くの使用者や商人は暗号学的な資産を取り扱うに際して安全性と利便性を欲しているかもしれないが、1日にして資金の価値の23%を喪失するという可能性には直面したくないかもしれない。今まで、最も良く提案されていた解決策は発行者によって裏付けられた資産であった。即ち、発行者が貨幣単位の発行と回収を実行する権利を有する副次的な貨幣を作成し、特定の裏付けとなる資産(例えば、金や米貨)1単位を引き換えにその貨幣1単位を提供し、その貨幣1単位を返送した者に対しては裏付けとなっている資産1単位を提供することを約束するという方法である。この機構によって、発行者が信頼できると仮定するならば、如何なる暗号学的ではない資産であっても暗号学的資産に「持ち上げる」ことが可能である。
 しかしながら、実際の所、発行者は常に信頼できる訳ではない。又、幾らかの場合においては銀行業務の基盤はそのようなサービスを提供するのに余りにも弱過ぎるか、或いは、全く適していない。代わりに金融商品がその手段を提供する。即ち、単一の発行者のみによって資産を裏付ける資金を提供するのではなく、投機家による分散型の市場が対象となっている暗号学的な資産(例えば、ETH)の価格が上昇するという賭けを実行することによってその役割を果たす。発行者とは異なり、ヘッジ契約は当事者の資金を第三者預託取引として保持するため、投機家には取引の自身の側の債務を履行しないという選択肢はない。とは言え、これを実際に行うには価格情報を提供する信頼できる情報源が必要となるため、この方法は完全には分散化されていないということに注意しなければならない。とは言え、これが基盤要件を緩和し(発行者になるのとは異なり、価格情報を提供するのには何の免許も必要ではないし、言論の自由に分類される可能性が高い)、潜在的な不正行為の発生を減少させるという点において顕著な改善であるということはほぼ間違いない。

識別及び評価系
 全ての代替暗号貨幣の中で最初のものであったNamecoinはBitcoinに類似したブロック鎖を使用者が自己の名称を他のデータと共に公開データベースに登録できる名称登録系を提供するために利用しようとした。Namecoinの良く挙げられる使用例は「bitcoin.org」(或いは、Namecoinの場合、「bitcoin.bit」)のような領域名をIPアドレスに対応付けるDNS系である。他の使用例としては、電子メールの認証が挙げられる。又、より高度な評価系の構築のために利用できる可能性もある。EthereumにおいてNamecoinに類似した名称登録系を提供する基本的な契約は次のようになる。

if !contract.storage[tx.data[0]]:
contract.storage[tx.data[0]] = tx.data[1]

 この契約は非常に単純である。単にEthereumネットワーク内の、追加することはできるが、変更したり、削除したりすることはできないデータベースでしかない。誰でも名称を何らかの値と共に登録することができ、そのような登録は未来永劫残り続ける。より洗練された名称登録契約は他の契約が問い合わせを実行することができる「機能条項」を有することにもなるだろうし、名称の「所有者」(即ち、最初の登録者)がデータを変更したり、所有権を移転したりする機構を有することにもなるだろう。更には、評価機能や信用の輪(web of trust)の機能を追加することさえ可能である。

分散型ファイル保管
 過去数年に亘って、一般向けのオンラインファイル保管を業とする多数の新興企業が出現した。その中で最も有名なのはDropboxである。このようなサービスにおいては、使用者は自身のHDDのバックアップをアップロードし、サービスにバックアップを保管させ、月々の手数料と引き換えにバックアップを利用することができる。しかしながら、現時点ではファイル保管市場は比較的非効率的な場合がある。既存のサービスを大雑把に見てみると、特に無料の割り当ても企業向けの割り引きもない20~200GBの「不気味の谷」の水準において、主流のファイル保管のための月々の価格は、1ヶ月でHDD1台分の価格を上回るものとなっている。Ethereumの契約は分散型のファイル保管体制の開発を可能にすることができ、個々の使用者は自己のHDDを貸し出すことによって小額を稼ぐことができ、HDDの未使用の領域を使用することによってファイル保管の費用を更に押し下げることができる。
 そのような手段を下支えしている重要な要素は「分散型のDropbox契約」と呼ばれているものである。この契約は次のように機能する。最初に、目的のデータをブロックに分割し、私的情報を保護するため夫々のブロックを暗号化し、ブロックから成るMerkle木を構築する。次に、Merkle木のブロックをNブロック毎に(無作為さを持たせるために、契約のコードから利用可能な直前のブロックの要約値を使って)無作為に選定し、その特定のブロックの簡易決済検証に類似した保持証明が含まれている取引を最初に呈示した者にX etherを与えるという内容の契約を作成する。ファイルを再ダウンロードしたい場合には、ファイルを復元するために小額決済のための手続きを使用することができる(例えば、32キロバイト毎に1szabo支払う)。最も手数料的に効率的な方法は、支払い人が最後まで取引を公開しないものである。代わりに、32キロバイト毎に取引を同一の使い捨て乱数を有する若干利益の大きいものに置き換える。
 この手続きは重要な特徴を有している。多くの無作為なノードがファイルを忘却する決断を行うことはないと信頼する必要があるように思えるかもしれないが、そのような危険性は秘密共有を通してファイルを多くの部分に分割し、夫々の部分が依然として何処かのノードに保有されているということを知るために契約を監視することによって殆ど0にすることができる。契約が金銭を支払っている限り、誰かが依然としてファイルを保管しているという暗号学的な証明が提供される。

分散型自律組織
 「分散型自律組織」とは、一般的には、ある種の成員或いは株主を擁し、67%以上の多数を占めることにより資金を使用したり、コードを変更したりする権利を行使できるような仮想的な存在である。組織がどのように資金を割り当てるべきかということに関しては、成員が集合的に決定することになる。分散型自律組織の資金を割り当てる方法としては様々考えられるが、助成金や俸給によるものがある。更には、より異質な、仕事に対して報酬を与えることを目的とする部内貨幣のようなものも考えられるかもしれない。分散型自律組織は、本質的には、従来の法律上の企業や非営利組織といったものを再現するものであるが、運営のために暗号学的なブロック鎖の技術のみを使用するものである。今までの所、分散型自律組織を取り巻く議論の多くは大体が配当を受け取る株主と取引可能な株式を伴う「分散型自律式株式会社」の「資本家」様式についてのものであったが、若しかしたら分散型自律共同体と表現されているかもしれない、意思決定のために全ての成員に対して等しい持分を与え、成員の加入と脱退を承認するのに既存の成員の67%以上の同意が必要となる別の様式もある。1人の人間が唯1つのみ成員としての地位を有することができるという要件は集団によって集合的に実施される必要があることになる。
 一般的に、分散型自律組織をコード化する方法は、概略的には、次のようになる。最も単純な様式は単に変更に関して成員の67%以上の同意があった場合にのみ変更を実施する自己変更のコードによるものである。コードは理論的には変更不能であるものの、コードを分解して別々の契約に格納し、どの契約を呼び出すかを左右する口座番号を変更可能な永続的な記憶領域に格納することによって、容易に事実上変更可能とすることができる。このような単純な分散型自律組織の契約の実装においては、3つの種類の取引があり、提供するデータによって区別される。
  ・[0,i,K,V]はi番目の提案を登録するためのデータである。永続的な記憶領域のK番地に格納されている口座番号をVに変更する。
  ・[0,i]はi番目の提案に同意する投票を登録するためのデータである。
  ・[2,i]は十分な投票が行われた場合においてi番目の提案の最終決定を行うためのデータである。
 契約は夫々の提案に対して条項を有し、投票を行った者の一覧と共に最終決定が行われていない全ての永続的な記憶領域への変更を保持し、更には、全ての成員の一覧も保持することになる。如何なる永続的な記憶領域への変更であっても、成員の67%が同意すれば、最終決定を行う取引は変更を実施することができる。より洗練された枠組みにおいては、取引の送信や成員の加入又は脱退のための投票機能も組み込まれることになり、流動的な民主主義におけるような形式の投票の委任(即ち、成員は誰でも自己の投票を誰かに委任することができ、委任は推移的である。若しAがBに委任し、BがCに委任したならば、CがAの投票を決定する)さえ可能となるかもしれない。このような枠組みは分散型自律組織が分散型共同体として有機的に成長することを可能にすることになり、最終的には、誰が成員であるか抜き出す作業を専門家に委任することを可能にする。「現在の枠組み」におけるものとは別様に、時間の経過と共に共同体の夫々の成員が夫々の歩調を変えるのに従い、専門家はあっさりと現れたり、消えたりする可能性がある。
 如何なる口座であっても0以上の株式を有することができ、意思決定のためには67%以上の株主の同意が必要となる分散型株式会社に対しては別の枠組みがある。完全な枠組みには資産管理の機能や株式売買の注文を出す能力や注文を受理する能力(契約の内部に買い注文と売り注文を対応させる機構を内蔵するのが望ましい)が含まれることになる。委任にも流動的な民主主義におけるような形式で対応し、「取締役会」の概念を一般化する。

更なる応用
 1.普通預金財布
  Aliceは自身の資金を安全な状態で保有したいが、自己の秘密鍵を紛失したり、誰かが窃取したりすることを懸念しているとするならば、次のようにしてetherを銀行であるBobとの契約の管理下に置くことができる。
   ・Aliceは単独では最大でも1日に資金の1%までしか引き出すことができない。
   ・Bobは単独では最大でも1日に資金の1%までしか引き出すことができない。但し、Aliceは自身の鍵を使ってBobの引き出し能力を停止する取引を作成することができる。
   ・Alice及びBobは共同すれば幾らでも引き出すことができる。
  通常Aliceにとって1日1%は十分である。たとえより多く引き出したいとしても、Aliceは協力を求めるためにBobに連絡を取ることができる。たとえAliceの鍵が窃取されたとしても、即刻Bobに対して資金を新しい契約に移動するよう要請することができる。たとえAliceが自身の鍵を紛失したとしても、Bobが最終的に全ての資金を単独で引き出すことになるだけである。たとえBobが悪意を持っているということが判明したとしても、AliceはBobの引き出し能力を停止することができる。
 2.作物保険
  価格指数の代わりに天候データの供給を受ける金融商品契約であっても作成は容易である。Iowa州の農家がIowa州の降水量に基づいて支払いを行う金融商品を購入した場合には、旱魃が発生したとしても、農家は自動的に金銭を受け取ることになるし、十分な降雨があったとしても、農家は作物が良く育って満足することになる。
 3.分散型のデータ供給
  差額を目的とする金融的な契約のために、「SchellingCoin」と呼ばれる枠組みを通してデータ供給を分散化することが実際に可能であるかもしれない。基本的にSchellingCoinは次のように機能する。N人の参加者は全員が与件(例えば、ETH/USDの価格)の実際の値を提供し、それらの値は保持され、第1四分位数から第3四分位数までの値を提供した者には報酬として1つの代用貨幣が与えられる。全ての者は自己以外の者が提供することになる値と同一の値を提供しようという動機を有し、多数の参加者が現実的に合意できる値は唯1つの明白な値、即ち、真の値である。これは、理論的には、ETH/USDの価格やBerlinの気温や特定の難しい計算の結果さえも含むどのような値でも提供することのできる分散型の枠組みを作る。
 4.高規格の複数署名を用いた第三者預託取引
  Bitcoinでは、例えば、所与の5つの鍵の内3つの以上の鍵がなければ資金を使用することができない複数署名の取引契約を締結することができる。Ethereumでは、更なる粒度が提供される。例えば、所与の5つの鍵の内4つ以上の鍵があれば全ての資金を使用することができ、3つ以上の鍵があれば1日に資金の10%まで使用することができ、2つ以上の鍵があれば1日に資金の0.5%まで使用することができる複数署名の取引契約を締結することができる。その上、Ethereumの複数署名は非同期的である。即ち、2人の参加者は異なる時間にブロック鎖に自己の署名を登録することができ、最後の署名が自動的に取引を送信する。

次世代電子契約及び分散型応用ソフトウェア基盤#03

01 02

採掘
 信頼できる集中型のサービスを利用できる場合には、上述の状態遷移系を実装するのは容易である。単に上に記述されている通りに正確にコード化すれば良いだけである。しかしながら、Bitcoinは分散型の貨幣系を構築しようとしているものである。そのため、利害関係人全員が取引の順序について確実に合意することができることを保証するために、状態遷移系に合意形成系を結合しなければならない。Bitcoinの分散型の合意形成処理においては、ネットワーク上のノードは「ブロック」と呼ばれる取引の集合体の生成を絶えず試行し続けなければならない。ネットワークは約10分毎に1ブロックを生成するように設計されており、夫々のブロックは時刻印(timestamp)、仕事証明の解(nonce)、直前のブロックに対する参照(即ち、直前のブロックの要約値(hash))及び直前のブロックが生成されてから発生した全ての取引の一覧を含む。そして、最新のBitcoinの元帳の状態を表現するために絶えず更新され、時間の経過と共に何処までも成長していく永続的な「ブロック鎖」が形成される。
 ブロック鎖の枠組みにおける、ブロックが有効であるか否かを確認する手続きは次のようになる。
  1.直前のブロックとして参照されているブロックが実在し、有効であることを確認する。
  2.ブロックの時刻印が直前のブロックのものよりも後のものであり、2時間先の未来よりは前のものであることを確認する。
  3.ブロックの仕事証明が有効であることを確認する。
  4.直前のブロックが適用された時点での状態をS[0]とする。
  5.ブロックの取引の一覧をTXとする。TXはn個の取引を有するものとする。0...n-1における全てのiについて、S[i+1] = APPLY(S[i],TX[i])とする。状態遷移関数APPLY(S,TX) -> S'がERRORを返した場合には、偽を返す。
  6.S[n]をブロックが適用された時点での状態とし、真を返す。
 本質的に、ブロックの夫々の取引は有効な状態遷移を生起しなければならない。状態は決してブロックの中に直接的には含まれていないことに注意しなければならない。状態は単に検証を実行するノードによって認知される抽象的な観念であり、初期状態から全てのブロックの全ての取引を逓次に適用していくのが状態を(安全に)算出する唯一の方法である。採掘者が取引をブロックに格納する順序が枢要であることに注意しなければならない。ブロックの中に2つの取引A及びBが存在し、BがAによって作成された未使用の取引出力を使用する場合には、AがBより前に格納されている場合にのみブロックは有効となり、そうでない場合には無効となる。
 ブロック検証手続きにおける興味深い部分は「仕事証明」の概念である。あるブロックの仕事証明が有効であるためには、そのブロックのSHA256要約値を256ビットの数値と見做した場合において、その数値が動的に調整される目標値(target)より小さい値でなければならない。本文書の執筆時点においては、目標値は大凡2の192乗である。この要件の目的はブロックの作成を計算的に「困難」にすることであり、これによりSybil攻撃者は自身の利益になるようにブロック鎖全体を再作成することができなくなる。SHA256は完全に予測不可能な擬似無作為関数となるように作られているので、有効なブロックを作成する唯一の方法は単なる試行錯誤でしかない。即ち、仕事証明の解の候補に繰り返し1を加えていきながら、新たな要約値が要件に適合するか否かを逐一調べていくしかないのである。2の192乗という現在の目標値の下では、仕事証明の解を発見するためには、平均して2の64乗回の試行を繰り返さなければならない。通常の場合、目標値はネットワークによって2016ブロック毎に調整される。これにより新たなブロックは平均して10分毎にネットワーク上の何れかのノードによって生成されることになる。この計算的な仕事に対する褒賞として、夫々のブロックの採掘者は何もない所から自身に対して25BTCを給与する取引を自身の採掘したブロックの中に含めることができる。更には、ブロックに格納されている取引の取引入力の総額面価格が取引出力の総額面価格より大きい場合には、その差額も又、取引手数料として採掘者に給与される。尚、採掘がBTC発行の唯一の方法である。初期状態においては貨幣は1枚も存在しない。
 採掘の目的をより深く理解するために、悪意のある攻撃が実行された場合に何が起きるのかを見てみることにしよう。Bitcoinの根底において利用されている暗号は安全性が確立しているので、攻撃者はBitcoinの仕組みの中で、暗号によって直接には保護されていない部分を標的にすることだろう。即ち、それは取引の順序である。攻撃者の戦略は単純である。
  1.商人に何らかの商品(出来れば、迅速に提供されるデジタル商品が良い)と引き換えに100BTCを送付する。
  2.商品が提供されるまで待機する。
  3.同一の100BTCを自己に送付する別の取引を作成する。
  4.ネットワークに対して、自己に送付する取引こそが最初に発生した取引であると納得させる。
 1の後数分すると、取引は、例えば、ブロック番号が27万のブロックに格納される。約1時間後、更に5つのブロックがブロック鎖に追加される。夫々のブロックは間接的に最初の取引を指し示しており、そのため、最初の取引の「確証度を高めている」。この時点で商人は決済が結了したと認め、商品を提供する。今商品はデジタル商品であると想定しているので、商品の提供は即時に完了する。ここで攻撃者は同一の100BTCを自己に送金する別の新しい取引を作成する。このときただ単に新しい取引をネットワークに公開しただけでは新しい取引が処理されることはない。採掘者は状態遷移関数APPLY(S,TX) -> S'を新しい取引に適用しようとするが、新しい取引が既にSには含まれていない取引出力を使用しようとしていることを検知する。そのため、攻撃者はただ単に新しい取引をネットワークに公開するのではなく、ブロック番号が26万9999のブロックを親ブロックとして参照し、自己のブロック番号が27万となる別の、古い取引の代わりに新しい取引を含む新しいブロックを作成する所から開始し、ブロック鎖の「分岐(fork)」を作成する。ブロックの内容が異なるため、新しいブロックを作成するには仕事証明を再度行う必要がある。その上、新しいブロックは古いブロックとは異なる要約値を有しており、元の、ブロック番号が27万1から27万5までのブロックは新しいブロックを指し示してはいない。そのため、元のブロック鎖と攻撃者の新しいブロック鎖は完全に別物である。ブロック鎖分岐が発生した場合には、最長のブロック鎖が真のブロック鎖であると見做される。そのため、正当な採掘者は元のブロック鎖の下で採掘を行い、攻撃者だけが新しいブロック鎖の下で採掘を行うことになる。攻撃者が元のブロック鎖の長さに追い付いて、自己の新しいブロック鎖を最長のものにするには、ネットワークにおける自己以外の要約値計算能力を併せたものよりも大きな要約値計算能力が必要となる(即ち、「51%攻撃」である)。

独自暗号貨幣を作ろう 第12回

第1回 第2回 第3回 第4回 第5回 第6回 第7回 第8回 第9回 第10回
第11回


<ブロック>
最も基本的な取引についてはある程度実装できましたので、次はブロックの実装をしていきたいと思います。

ブロックとは、取引の集まりに仕事証明に必要な幾つかの情報を添付したものです。仕事証明に必要な幾つかの情報はブロックの頭部(block header)として1つに纏められます。

但し、起源ブロック(genesis block)という特殊なブロックが1つだけあります。通常のブロックはそのブロックの丁度1つ前に当たるブロックを参照しています。そのため、ブロックの集まりは木構造を成すことになりますが、それらが1つの木に纏められるためには少なくとも根に当たるブロックが必要となります。この、(時間的には)最初に作られるブロックが起源ブロックです。起源ブロックは取引やブロックの頭部を有しません(有する必要がありません)。
ネットワーク全体で(大多数のノードが信認する)唯1つのブロック木(block tree)を構成するための便法でしかないので、結局はネットワークに参加する大多数のノードが納得するならどんなデータでも良いのです。
しかし、通常は起源ブロックに開発者が事前採掘(premining)をしていないことを示す文字列を格納します。即ち、その暗号貨幣が始動する直前に起こった出来事等を文字列として格納し、少なくともその出来事が起こるより前には開発者によって事前採掘がされていないということを保証します。

起源ブロック以外の普通のブロックは取引の集まりとブロックの頭部を有します。取引の集まりには、ブロック報酬(block reward)と取引手数料を採掘者が所有する口座に送金する1つ以上の新しい貨幣を生成する取引が含まれます。

CREACOINの場合は、採掘者へ報酬を与えるだけでなく、ブロック報酬の一部はCREA基金が獲得するものとします(CREA基金とは、コンテンツ制作者に報酬を与えるために設置される基金です。詳細についてはCREACOINのスレを参照してください)。そのため、ブロックに格納される取引の集まりには採掘者の口座に送金するための新しい貨幣を生成する取引だけでなく、基金の口座に送金するための新しい貨幣を生成する取引も含まれることになります。

しかしながら、全ての普通のブロックに採掘者の口座と基金の口座の両方に新しい貨幣を送金する取引を格納するのは必要な記憶領域という観点から非効率的でしょう。ブロックの採掘者は夫々のブロック毎に毎回異なる可能性がありますが、基金は唯一無二の存在であり、ブロック毎に基金の口座を変える必要はありません。そのため、基金の取り分についてはブロック毎に発生させるのではなく、1万ブロック程度毎に発生させるのが妥当でしょう。つまり、CREACOINでは、普通のブロックは更に2種類に分かれることになります。

以上の仕様上の要求から、CREACOINでは、ブロックを以下の3種に分けて実装していくことにします。
 1.起源ブロック
 2.採掘者の口座のみに新しい貨幣が送金されるブロック(通常ブロック)
 3.採掘者の口座だけでなく、基金の口座にも新しい貨幣が送金されるブロック(基金獲得分付きブロック)

取り敢えず、全ての種類のブロックが継承するBlock抽象クラスを作っておきましょう。以前作ったTXBLOCKBASE抽象クラスを継承します。特に追加の機能はないです。

public abstract class Block<BlockidHashType> : TXBLOCKBASE<BlockidHashType>
    where BlockidHashType : HASHBASE
{
    public Block(int? _version) : base(_version) { }
}


<起源ブロック>
起源ブロックを表すGenesisBlockクラスを作ります。
genesisWordフィールドに適当な文字列を入れることになります。始動直前のBitcoinの価格でも入れれば良いのではないでしょうか。

public class GenesisBlock<BlockidHashType> : Block<BlockidHashType>
    where BlockidHashType : HASHBASE
{
    public GenesisBlock() : base(null) { }
 
    public readonly string genesisWord = "Bitstamp 2014/05/25 BTC/USD High 586.34 BTC to the moooooooon!!";
 
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo
    {
        get
        {
            return (msrw) => new MainDataInfomation[]{
                new MainDataInfomation(typeof(string), () => genesisWord, (o) => { throw new NotSupportedException("genesis_block_cant_read"); }),
            };
        }
    }
}

独自暗号貨幣を作ろう 第11回

第1回 第2回 第3回 第4回 第5回 第6回 第7回 第8回 第9回 第10回


<修正1>
TXBLOCKBASE抽象クラスのId仮想プロパティの実装が間違っていたので修正します。
IdCacheを計算した後はIsModifiedフィールドを偽に変更しなければなりませんでした。

public abstract class TXBLOCKBASE<TxidBlockidHashType> : SHAREDDATA
    where TxidBlockidHashType : HASHBASE
{
    public TXBLOCKBASE(int? _version) : base(_version) { }
 
    protected bool isModified;
 
    protected TxidBlockidHashType idCache;
    public virtual TxidBlockidHashType Id
    {
        get
        {
            if (isModified || idCache == null)
            {
                idCache = Activator.CreateInstance(typeof(TxidBlockidHashType), ToBinary()) as TxidBlockidHashType;
                isModified = false;
            }
            return idCache;
        }
    }
 
    public virtual bool IsValid { get { return true; } }
}


<修正2>
貨幣を移動する取引に含まれる取引入力に署名を行った際にisModifiedフィールドを真に変更するのを忘れていました。修正します。

public class TransferTransaction<TxidHashType, PubKeyHashType, PubKeyType> : Transaction<TxidHashType, PubKeyHashType>
    where TxidHashType : HASHBASE
    where PubKeyHashType : HASHBASE
    where PubKeyType : DSAPUBKEYBASE
{
(中略)
    public void Sign(TransactionOutput<PubKeyHashType>[] prevTxOutputs, DSAPRIVKEYBASE[] privKeys)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
        if (privKeys.Length != inputs.Length)
            throw new ArgumentException("inputs_and_priv_keys");
 
        byte[] bytesToSign = GetBytesToSign(prevTxOutputs);
 
        for (int i = 0; i < inputs.Length; i++)
            inputs[i].SetSenderSig(privKeys[i].Sign(bytesToSign));
 
        //取引入力の内容が変更された
        isModified = true;
    }
(中略)
}


<改良1>
今まで楕円曲線DSAの公開鍵及び秘密鍵を可変長としていましたが、直列化する場合に必要なデータ長の削減のために固定長に変更します。

DSAPUBKEYBASE抽象クラス及びDSAPRIVKEYBASE抽象クラスにSizeByte抽象プロパティを追加し、StreamInfo抽象プロパティの実装を変更します。DSAKEYPAIRBASEクラスの実装も変更します。

public abstract class DSAPUBKEYBASE : SHAREDDATA
{
    public DSAPUBKEYBASE() : base(null) { }
 
    public DSAPUBKEYBASE(byte[] _pubKey)
        : base(null)
    {
        if (_pubKey.Length != SizeByte)
            throw new InvalidOperationException("invalid_length_pub_key");
 
        pubKey = _pubKey;
    }
 
    public byte[] pubKey { get; private set; }
 
    public abstract int SizeByte { get; }
 
    public abstract bool Verify(byte[] data, byte[] signature);
 
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo
    {
        get
        {
            return (msrw) => new MainDataInfomation[]{
                new MainDataInfomation(typeof(byte[]), SizeByte, () => pubKey, (o) => pubKey = (byte[])o),
            };
        }
    }
}
 
public abstract class DSAPRIVKEYBASE : SHAREDDATA
{
    public DSAPRIVKEYBASE() : base(null) { }
 
    public DSAPRIVKEYBASE(byte[] _privKey)
        : base(null)
    {
        if (_privKey.Length != SizeByte)
            throw new InvalidOperationException("invalid_length_priv_key");
 
        privKey = _privKey;
    }
 
    public byte[] privKey { get; private set; }
 
    public abstract int SizeByte { get; }
 
    public abstract byte[] Sign(byte[] data);
 
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo
    {
        get
        {
            return (msrw) => new MainDataInfomation[]{
                new MainDataInfomation(typeof(byte[]), SizeByte, () => privKey, (o) => privKey = (byte[])o),
            };
        }
    }
}
 
public abstract class DSAKEYPAIRBASE<DsaPubKeyType, DsaPrivKeyType> : SHAREDDATA
    where DsaPubKeyType : DSAPUBKEYBASE
    where DsaPrivKeyType : DSAPRIVKEYBASE
{
    public DSAKEYPAIRBASE() : base(null) { }
 
    public DsaPubKeyType pubKey { get; protected set; }
    public DsaPrivKeyType privKey { get; protected set; }
 
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo
    {
        get
        {
            return (msrw) => new MainDataInfomation[]{
                new MainDataInfomation(typeof(DsaPubKeyType), null, () => pubKey, (o) => pubKey = (DsaPubKeyType)o),
                new MainDataInfomation(typeof(DsaPrivKeyType), null, () => privKey, (o) => privKey = (DsaPrivKeyType)o),
            };
        }
    }
}


具象クラスにSizeByte抽象プロパティを実装します。それ程大きな変更ではないので、コードは示しません。


<改良2>
更に取引入力の署名も可変長から固定長に変更します。senderSigLength静的フィールドを追加します。

public class TransactionInput<TxidHashType, PubKeyType> : SHAREDDATA
    where TxidHashType : HASHBASE
    where PubKeyType : DSAPUBKEYBASE
{
(中略)
    public static readonly int senderSigLength = 64;
(中略)
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo
    {
        get
        {
            return (msrw) => new MainDataInfomation[]{
                new MainDataInfomation(typeof(TxidHashType), null, () => prevTxHash, (o) => prevTxHash = (TxidHashType)o),
                new MainDataInfomation(typeof(int), () => prevTxOutputIndex, (o) => prevTxOutputIndex = (int)o),
                new MainDataInfomation(typeof(byte[]), senderSigLength, () => senderSig, (o) => senderSig = (byte[])o),
                new MainDataInfomation(typeof(PubKeyType), null, () => senderPubKey, (o) => senderPubKey = (PubKeyType)o),
            };
        }
    }
(中略)
    public void SetSenderSig(byte[] sig)
    {
        if (sig.Length != senderSigLength)
            throw new ArgumentException("tx_in_sender_sig_length");
 
        senderSig = sig;
    }
}

独自暗号貨幣を作ろう 第10回

第1回 第2回 第3回 第4回 第5回 第6回 第7回 第8回 第9回


<取引の有効性の検証>
取引入力への署名を実装することができたので、次は取引の有効性の検証を実装しましょう。現時点で、取引には新しい貨幣を生成する取引と貨幣を移動する取引の2種類がありますが、新しい貨幣を生成する取引に関しては特に検証することはありません(ブロックに含まれる新しい貨幣を生成する取引がブロックの報酬とブロックに含まれる取引手数料に合致するかといった検証はブロック側の職分なのでここでは実装しません)。
貨幣を移動する取引に関しては、完全な有効性の検証のためには次のような検証を行わなければなりません。
 1.署名の検証(署名が公開鍵に対応する秘密鍵によって為されたものであるか)
 2.公開鍵の検証(公開鍵の暗号学的要約値が取引入力が参照している取引出力の口座番号に合致するか)
 3.額面価格の検証(全ての取引入力の総額面価格が全ての取引出力の総額面金額以上であるか)

夫々の検証を行うメソッドを追加し、全ての検証を行うメソッドも追加します。又、取引手数料を取得するためのメソッドも追加します。夫々のメソッドに引数として渡された夫々の取引出力が正しいものであることを前提としているのは前回と同様です。

public class TransferTransaction<TxidHashType, PubKeyHashType, PubKeyType> : Transaction<TxidHashType, PubKeyHashType>
    where TxidHashType : HASHBASE
    where PubKeyHashType : HASHBASE
    where PubKeyType : DSAPUBKEYBASE
{
(中略)
    public bool VerifySignature(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        byte[] bytesToSign = GetBytesToSign(prevTxOutputs);
 
        for (int i = 0; i < inputs.Length; i++)
            if (!inputs[i].senderPubKey.Verify(bytesToSign, inputs[i].senderSig))
                return false;
        return true;
    }
 
    public bool VerifyPubKey(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        for (int i = 0; i < inputs.Length; i++)
            if (!(Activator.CreateInstance(typeof(PubKeyHashType), inputs[i].senderPubKey.pubKey) as PubKeyHashType).Equals(prevTxOutputs[i].receiverPubKeyHash))
                return false;
        return true;
    }
 
    public bool VerifyAmount(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        return GetFee(prevTxOutputs).rawAmount >= 0;
    }
 
    public bool VerifyAll(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        if (Version == 0)
            return VerifySignature(prevTxOutputs) && VerifyPubKey(prevTxOutputs) && VerifyAmount(prevTxOutputs);
        else
            throw new NotSupportedException("transfer_tx_verify_all");
    }
 
    public CurrencyUnit GetFee(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        long totalPrevOutputs = 0;
        for (int i = 0; i < prevTxOutputs.Length; i++)
            totalPrevOutputs += prevTxOutputs[i].amount.rawAmount;
        long totalOutpus = 0;
        for (int i = 0; i < outputs.Length; i++)
            totalOutpus += outputs[i].amount.rawAmount;
 
        return new CurrencyUnit(totalPrevOutputs - totalOutpus);
    }
}