しゅーと (@shutingrz)
しゅーと (@shutingrz)
Security researcher
Apr 28, 2019 6 min read

自作仮想通貨入門(6) - ジェネシスブロックの作成と設定

※この章では src/chainparams.cpp を変更します。

仮想通貨はブロックチェーンの原理を基にして運用されています。
つまり、ブロックチェーンを利用するためにはブロックチェーンの一番初めに位置するブロック、
すなわちジェネシス(起源)ブロックを作成する必要があります。

ジェネシスブロック(genesis)の構造

ジェネシスブロックは以下の構造になっています。
(括弧内はchainparams.cppでの変数名をあらわしています)

ブロック生成時刻(nTime)

ブロックを生成した時刻を記載
ジェネシスブロックの場合は起点としたい時刻を指定する
(後述する、スクリプトに記載する時刻と乖離しないように)

採掘難易度/Difficulty(nBits)

※実装では整数ではなく、係数部・指数部の形で表現します。
※詳しくはbitcoinのページ参照。(https://bitcoin.org/en/developer-reference#target-nbits)
ジェネシスブロックはその仮想通貨で定めた最小採掘難易度でマイニングします。
その最小難易度は同じくchainparams.cppにある consensus.powLimit で定められています。
Litecoin の powLimitは「“00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff”」を指定しており、
これを係数部・指数部のnBitsで表現すると「0x1d00ffff」となります。
powLimitを変更しないなら、「0x1d00ffff」を指定してください。

ブロックバージョン(nVersion)

ブロックのバージョン。ブロック構造が変わらない限り数値は変化しません。
ジェネシスブロックは通常1を指定します。

前のブロックのハッシュ(hashPrevBlock)

ジェネシスブロックは起源となるのでこれは存在しません。(nullがセットされる)

Merkle Root (hashMerkleRoot)

トランザクションのハッシュ木(Merkle Tree)の頂点のハッシュ値。
ジェネシスブロックは一般的にトランザクションは1つだけなので、
結果的には自分で生成するトランザクションのハッシュ値になります。
→「SHA256(SHA256(genesis_tx))」

nonce (nNonce)

PoWを示すための値。
この値を変えながらブロックヘッダのハッシュをnBitsよりも小さい値にします。

トランザクション (txNew)

ジェネシスブロックはトランザクションは1つのみ。

  • トランザクションバージョン (nVersion)
    トランザクションのバージョン。トランザクション構造が変わらない限り数値は変化しません。
    ジェネシスブロックは通常1を指定。

  • インプット (scriptSig)
    これは最初のトランザクションであり、送金などのインプットはありません。 こういったトランザクションのことをCoinBaseと呼びます。
    参照: http://learnmeabitcoin.com/glossary/coinbase-transaction

  • トランザクション生成時刻を示すフィールド (pszTimestamp)
    大体の仮想通貨は、その日のニュース記事をそのまま転載して、その日以前に仮想通貨が存在しなかったことを証明することが多いです。
    (これによって開発者が採掘をしていないことを示したい?)

  • ブロック作成報酬 (nValue)
    src/validation.cpp の GetBlockSubsidy() で定める値にします。
    この値はデフォルトで 50 なので、50 を指定してください。

  • アウトプット (scriptPubKey)

    • 送信先アドレス (genesisOutputScript)
      ジェネシスブロックでは適当に作成した秘密鍵から公開鍵を生成します。
      この送信先アドレスは初期開発者が作成するものですが、一般的に開発者個人はこのアドレスを利用していないようです。
      (blockchain explorerでジェネシスブロックのscriptPubkeyを参照してみてください。)

以上の情報をもとに、ジェネシスブロックの作成に必要な変数を代入していきます。


ジェネシスブロック作成に必要なパラメータを決める

PubKeyの生成

opensslコマンドを使って楕円曲線暗号(secp256k1)から秘密鍵とその証明書を生成します。

$ openssl ecparam -genkey -name secp256k1 -rand /dev/urandom -out private_key
$ openssl ec -in private_key -pubout -outform DER|tail -c 65|xxd -p -c 65 > pubkey.hex
$ cat pubkey.hex

=> 今回は「043aca3e996c24f8c2453d02532471107e75d219cd09c18a6d574e69338d79e3bbe288330f7b299032f44735162aec272713aacecceacf25855945fe5003309e94」が生成されました。

pszTimestamp の作成

sciptSig の仕様上、20 - 100 byteの範囲で作成してください。

kobe-np 11/Jul/2018 Zenkoku hatsu, keisatsu ga kasou tsuka 5000 yen soutou wo sashi osae

2018/07/11 の神戸新聞のニュースから引っ張ってきました。
「全国初、警察が仮想通貨5千円相当を差し押さえ」

nTime の指定

上のpszTimestampの記事の時間と大きくずれない適当なunixtimeを指定してください。
今回は 2018/7/11 21:00:00 の「1531310400」にします。

nNonce

ここでいうnonceは、PoWを満たすnonceのことではなく、PoWを満たすnonceを探すにあたって、どのnonceの値から始めるかをしめすものです。
ジェネシスブロックの採掘時、このnonceを基準として1ずつインクリメントされます。
今回は適当に「634556435」にします。

nBits

採掘難易度をビット表記したものです。
「0x1e0ffff0」にします。

ジェネシスブロックの作成

今回は様々なアルゴリズムのブロックを生成できる「GenesisH0」を用いてジェネシスブロックを作成します。

https://github.com/lhartikk/GenesisH0

Mainnet/Testnetのジェネシスブロック

-z に pszTimestamp、 -p に PubKey、 -t に nTime、 -n に nNonce、 -b に nBits を指定します。

python2.7 genesis.py -a scrypt -z "kobe-np 11/Jul/2018 Zenkoku hatsu, keisatsu ga kasou tsuka 5000 yen soutou wo sashi osae" -p "043aca3e996c24f8c2453d02532471107e75d219cd09c18a6d574e69338d79e3bbe288330f7b299032f44735162aec272713aacecceacf25855945fe5003309e94" -t 1531310400 -n 634556435 -b 0x1e0ffff0

しばらく(スペックによりますが10分以内には)待つと、ジェネシスブロックとなるnonceが見つかります。

genesis hash found!
nonce: 634696322
genesis hash: 2f7c64e8e13a55a4f4fe11fd8cc0e5a59b8d59ade336501f17f8ef0c30b93f8c

Mainnet/TestnetのnNonceには 634696322 を指定すればいいとわかりますね。

Regtestのジェネシスブロック

Regtestはちゃんとマイニングできるかテストするモードなので、採掘難易度を最小にしてジェネシスブロックを作成します。
採掘難易度を示すnBitsの値は「0x207fffff」にします。
また、Litecoinは探すnNonceを0からにしているので、こちらも0から始めるようにしましょう。

python2.7 genesis.py -a scrypt -z "kobe-np 11/Jul/2018 Zenkoku hatsu, keisatsu ga kasou tsuka 5000 yen soutou wo sashi osae" -p "043aca3e996c24f8c2453d02532471107e75d219cd09c18a6d574e69338d79e3bbe288330f7b299032f44735162aec272713aacecceacf25855945fe5003309e94" -t 1531310400 -n 0 -b 0x207fffff

採掘難易度が最小なので、一回目の試行で見つかります。

genesis hash found!
nonce: 1
genesis hash: 967aaa62c5210edfca1dad7dcf37259f57472326515dbd4654c1705b5395c713

RegtestのnNonceは1でいいことがわかります。

ソースコードの変更作業

ジェネシスブロックを作成しソースコードに記載する情報が集まったため、
今までの情報を使って src/chainparams.cpp を変更します。

CreateGenesisBlock()

この関数はオーバーロードされており同名で2つの関数がありますが、pszTimestampが定数で定義されている方を書き換えます。

const char* pszTimestamp = "[上で作ったpszTimestampの値]";
const CScript genesisOutputScript = CScript() << ParseHex("[Pubkeyの値]") << OP_CHECKSIG;

CMainParams() / CTestNetParams() / CRegTestParams()

上で決めた nTime、nNonce、nBits を入れていきます。

genesis = CreateGenesisBlock([nTime], [ジェネシスブロック作成時に見つけたnonce], [ジェネシスブロック作成時に指定したnBits], 1, 50 * COIN);

ジェネシスブロックに関する記載は以上で終了です。
chainparams.cpp は他にも色々変える必要があるので、次の章も引き続きやっていきます。