独自暗号貨幣を作ろう 第7回
<暗号学的要約関数>
暗号学的要約関数を実装します。
必要になるものを考えます。
1.ノード識別子(ノード識別子はノード情報の暗号学的要約値)
2.口座番号(口座番号は楕円曲線DSAの公開鍵又はその暗号学的要約値)
3.取引識別子(取引識別子は取引の暗号学的要約値)
4.ブロック識別子(ブロック識別子はブロックの暗号学的要約値)
ノード識別子はP2Pネットワーク上でノードを識別するための値です。これはSHA-256暗号学的要約値で良いでしょう。
口座番号は口座を識別するための値です。Bitcoinと同様にSHA-256暗号学的要約値のRIPEMD160暗号学的要約値とします。
暗号貨幣の実装のために使用できる仕事証明の方式としては、(PrimecoinやRiecoinで採用されている)素数を用いたものもありますが、CREACOINはBitcoinと同様の、暗号学的要約関数を用いるものを採用することにします。
ブロック識別子は暗号貨幣の安全性に係わってくるものであり、どの暗号学的要約関数を採用するかは非常に重要です。
又、ブロック識別子は仕事証明に係わってくるものでもあります。採掘者が有効な新しいブロックを作成するためには、ブロックの暗号学的要約値が目標値(target)以下となるような仕事証明の解(nonce)を発見しなければなりません。暗号学的要約関数は与えられた入力値に対して擬似無作為な出力値を返すので、実際に仕事証明の解を発見するには候補に対して虱潰しにブロックの暗号学的要約値が目標値以下となるか調べていかなければなりません。
即ち、採掘者は仕事証明の解(の候補)を変えながらブロックの暗号学的要約値を(通常は)途方もない回数計算する必要があり、どの暗号学的要約関数を採用するかは採掘の態様に密接に係わってくる要素の1つです。
そんな訳で、ブロック識別子の計算のためにどの暗号学的要約関数を採用するかに関しては様々な議論がある訳ですが、(議論の詳細な説明は省いて)CREACOINでは複数の異なる暗号学的要約関数を繋げて作った連鎖型の暗号学的要約関数を採用することにします。
この種の暗号学的要約関数の中で、暗号貨幣において利用されているものとしては、(Quarkで採用されている)Quark、(Darkcoinで採用されている)X11等が有名ですが、CREACOINではSHA-3公募の第2選考を通過した14種の暗号学的要約関数を繋げて作った連鎖型の暗号学的要約関数を採用したいと思います。これをX11に倣ってX14と呼ぶことにしましょう。
SHA-3公募の第2選考を通過した14種の暗号学的要約関数とは、次の14種です。
1.BLAKE
2.BlueMidnightWish
3.Grøstl
4.Skein
5.JH
6.Keccak
7.Luffa
8.CubeHash
9.SHAvite
10.SIMD
11.ECHO
12.Fugue
13.Hamsi
14.Shabal
これらの512ビット版を上記の順番で繋げ、最後に結果として生じる512ビットの暗号学的要約値の左256ビットのみを取り出す操作を加えたものをX14とします。
当初はX14を採用する予定でしたが、更にSHA-1を加えると2ちゃんねるのトリップ検索を行うことが可能です。暗号学的要約値を用いる仕事証明では、仕事証明の解を求める計算は取引の発生を確実にするという唯一の目的のために行われているものであり、ある意味無駄の多い浪費的な計算です。その計算を他の用途のためにも用いることができるというのは非常に貴重であって、価値のあることです。そこで、CREACOINはSHA-3公募の第2選考を通過した14種の暗号学的要約関数とSHA-1暗号学的要約関数を繋げて作った連鎖型の暗号学的要約関数を採用したいと思います。
取引識別子はSHA-256暗号学的要約値のSHA-256暗号学的要約値にしましょう。
それではコードを書きます。先ずは、夫々の暗号学的要約(値を計算する)関数を拡張メソッドとして実装します。
SHA-1とSHA-256とRIPEMD-160は.NET Frameworkの実装を利用し、SHA-3公募の第2選考を通過した14種の暗号学的要約関数はHashLibの実装を利用することにします。
private static HashAlgorithm haSha1 = null; private static HashAlgorithm haSha256 = null; private static HashAlgorithm haRipemd160 = null; private static IHash haBlake512 = null; private static IHash haBmw512 = null; private static IHash haGroestl512 = null; private static IHash haSkein512 = null; private static IHash haJh512 = null; private static IHash haKeccak512 = null; private static IHash haLuffa512 = null; private static IHash haCubehash512 = null; private static IHash haShavite512 = null; private static IHash haSimd512 = null; private static IHash haEcho512 = null; private static IHash haFugue512 = null; private static IHash haHamsi512 = null; private static IHash haShabal512 = null; public static byte[] ComputeSha1(this byte[] bytes) { if (haSha1 == null) haSha1 = new SHA1CryptoServiceProvider(); return haSha1.ComputeHash(bytes); } public static byte[] ComputeSha256(this byte[] bytes) { if (haSha256 == null) haSha256 = HashAlgorithm.Create("SHA-256"); return haSha256.ComputeHash(bytes); } public static byte[] ComputeRipemd160(this byte[] bytes) { if (haRipemd160 == null) haRipemd160 = HashAlgorithm.Create("RIPEMD-160"); return haRipemd160.ComputeHash(bytes); } public static byte[] ComputeBlake512(this byte[] bytes) { if (haBlake512 == null) haBlake512 = HashFactory.Crypto.SHA3.CreateBlake512(); return haBlake512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeBmw512(this byte[] bytes) { if (haBmw512 == null) haBmw512 = HashFactory.Crypto.SHA3.CreateBlueMidnightWish512(); return haBmw512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeGroestl512(this byte[] bytes) { if (haGroestl512 == null) haGroestl512 = HashFactory.Crypto.SHA3.CreateGroestl512(); return haGroestl512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeSkein512(this byte[] bytes) { if (haSkein512 == null) haSkein512 = HashFactory.Crypto.SHA3.CreateSkein512(); return haSkein512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeJh512(this byte[] bytes) { if (haJh512 == null) haJh512 = HashFactory.Crypto.SHA3.CreateJH512(); return haJh512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeKeccak512(this byte[] bytes) { if (haKeccak512 == null) haKeccak512 = HashFactory.Crypto.SHA3.CreateKeccak512(); return haKeccak512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeLuffa512(this byte[] bytes) { if (haLuffa512 == null) haLuffa512 = HashFactory.Crypto.SHA3.CreateLuffa512(); return haLuffa512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeCubehash512(this byte[] bytes) { if (haCubehash512 == null) haCubehash512 = HashFactory.Crypto.SHA3.CreateCubeHash512(); return haCubehash512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeShavite512(this byte[] bytes) { if (haShavite512 == null) haShavite512 = HashFactory.Crypto.SHA3.CreateSHAvite3_512(); return haShavite512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeSimd512(this byte[] bytes) { if (haSimd512 == null) haSimd512 = HashFactory.Crypto.SHA3.CreateSIMD512(); return haSimd512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeEcho512(this byte[] bytes) { if (haEcho512 == null) haEcho512 = HashFactory.Crypto.SHA3.CreateEcho512(); return haEcho512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeFugue512(this byte[] bytes) { if (haFugue512 == null) haFugue512 = HashFactory.Crypto.SHA3.CreateFugue512(); return haFugue512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeHamsi512(this byte[] bytes) { if (haHamsi512 == null) haHamsi512 = HashFactory.Crypto.SHA3.CreateHamsi512(); return haHamsi512.ComputeBytes(bytes).GetBytes(); } public static byte[] ComputeShabal512(this byte[] bytes) { if (haShabal512 == null) haShabal512 = HashFactory.Crypto.SHA3.CreateShabal512(); return haShabal512.ComputeBytes(bytes).GetBytes(); }
次に、暗号学的要約値を保持するSha256Hashクラス、Sha256Sha256Hashクラス、Sha256Ripemd160Hashクラス及びX14Hashクラスを作ります(X14Hashクラスは今のところ使用予定はありません)。
public class Sha256Hash : HASHBASE { public Sha256Hash() : base() { } public Sha256Hash(string stringHash) : base(stringHash) { } public Sha256Hash(byte[] data) : base(data) { } public override int SizeBit { get { return 256; } } protected override byte[] ComputeHash(byte[] data) { return data.ComputeSha256(); } } public class Sha256Sha256Hash : HASHBASE { public Sha256Sha256Hash() : base() { } public Sha256Sha256Hash(string stringHash) : base(stringHash) { } public Sha256Sha256Hash(byte[] data) : base(data) { } public override int SizeBit { get { return 256; } } protected override byte[] ComputeHash(byte[] data) { return data.ComputeSha256().ComputeSha256(); } } public class Sha256Ripemd160Hash : HASHBASE { public Sha256Ripemd160Hash() : base() { } public Sha256Ripemd160Hash(string stringHash) : base(stringHash) { } public Sha256Ripemd160Hash(byte[] data) : base(data) { } public override int SizeBit { get { return 160; } } protected override byte[] ComputeHash(byte[] data) { return data.ComputeSha256().ComputeRipemd160(); } } public class X14Hash : HASHBASE { public X14Hash() : base() { } public X14Hash(string stringHash) : base(stringHash) { } public X14Hash(byte[] data) : base(data) { } public override int SizeBit { get { return 256; } } protected override byte[] ComputeHash(byte[] data) { return data.ComputeBlake512().ComputeBmw512().ComputeGroestl512().ComputeSkein512().ComputeJh512().ComputeKeccak512().ComputeLuffa512().ComputeCubehash512().ComputeShavite512().ComputeSimd512().ComputeEcho512().ComputeFugue512().ComputeHamsi512().ComputeShabal512().Decompose(0, SizeByte); } }
最後に、X15クラスを作ります。
SHA-1を連鎖の途中に入れると情報量が160ビットになってしまうので、計算方法を少し工夫しました。
tripKeyにトリップキーが格納され、tripにトリップが格納されます。
public class X15Hash : HASHBASE { public X15Hash() : base() { } public X15Hash(string stringHash) : base(stringHash) { } public X15Hash(byte[] data) : base(data) { } public string tripKey { get; private set; } public string trip { get; private set; } public override int SizeBit { get { return 256; } } protected override byte[] ComputeHash(byte[] data) { tripKey = Convert.ToBase64String(data.ComputeSha1()); byte[] sha1base64shiftjissha1 = Encoding.GetEncoding("Shift-JIS").GetBytes(tripKey).ComputeSha1(); tripKey += "#"; trip = "◆" + Convert.ToBase64String(sha1base64shiftjissha1).Substring(0, 12).Replace('+', '.'); byte[] blake512 = data.ComputeBlake512(); byte[] bmw512 = data.ComputeBmw512(); byte[] groestl512 = data.ComputeGroestl512(); byte[] skein512 = data.ComputeSkein512(); byte[] jh512 = data.ComputeJh512(); byte[] keccak512 = data.ComputeKeccak512(); byte[] luffa512 = data.ComputeLuffa512(); byte[] cubehash512 = sha1base64shiftjissha1.Combine(blake512).ComputeCubehash512(); byte[] shavite512 = bmw512.Combine(groestl512).ComputeShavite512(); byte[] simd512 = skein512.Combine(jh512).ComputeSimd512(); byte[] echo512 = keccak512.Combine(luffa512).ComputeEcho512(); byte[] fugue512 = cubehash512.Combine(shavite512).ComputeFugue512(); byte[] hamsi512 = simd512.Combine(echo512).ComputeHamsi512(); byte[] shabal512 = fugue512.Combine(hamsi512).ComputeShabal512(); return shavite512.Decompose(0, SizeByte); } }