Bitcoinのトランザクション構造については調査していた。
ブロックの構造についても調べたことがあった。
しかし、マイニング自体はやったことがなかった。
計算はブロックヘッダを作って nonce を入れ替えながら条件を満たすようにする作業だろうが、報酬を受け取る coinbaseトランザクションについて調べていなかったので、それを中心に調査した。
最終的に regtest ではマイニングすることができたのだが、 testnet では nonce を見つけられなかったので動作確認のコードを最後まで動かせていない。なので今回は動作確認のコードは載せていない。
手元に書籍の「ビットコインとブロックチェーン」(2016年7月21日 初版第1刷)があるので、それの第8章を読んでいた。しかし時代は流れ、いくつか変更があった。
目立ったのはこんなところか。
- 「トランザクション年齢、トランザクション手数料、トランザクション優先度」項が削除されていた。
- coinbaseトランザクションの作り方が変更?になっていた。
generation トランザクションという呼び名が削除されて coinbaseトランザクションだけになっていたが、そこはささいな変更だろう。
トランザクション優先度は削除されたとのこと。
coinbaseトランザクションは segwit 対応になったことで構成が変わった。こちらの記事を読むまでwitness root hash 計算での coinbaseトランザクションの値を間違っていた。感謝。
ブロックの元になるデータは Bitcoin Core のgetblocktemplate
RPCから取ってくるのが楽だろう。トランザクションも取得できるし、witness root hash 計算に使うWTXID もある。
coinbaseトランザクションの scriptSig には BIP-34 を参考にしてブロック高を入れる。BIP-34 は regtest だと 500ブロックでアクティベートされたそうだが、300ブロックくらいで試したときも同じようにできた。
coinbaseトランザクションの送金先は、昔は P2PK アドレスが多かったので制限があるのかと思っていたが、特にそういうのは無かった。vout#0 は報酬の送金先、vout#1 は特定の OP_RETURN
になっているようだったが、順番が固定なのかは調べ切れていない。OUTPUT が3つ以上でも特に問題はない。
特定の OP_RETURN
はサイズが 36バイト分で、最初の 4バイトが aa21a9ed
、次の32バイトが Doubel-SHA256(witness root hash | witness reserved value)
になっている(BIP-141より)。
coinbaseトランザクションの WITNESS 部は1つで、オールゼロ 32バイト分。LOCKTIME部は特に決まりは無さそうだった。
こうやって、運良く nonce が見つかると、 submitheader
RPC と submitblock
RPC で展開する。
ブロックが見つかったら、他のマイナーが今の作業を止めて新しいブロックに対する作業に取りかかれるように先に通知を出すという話を聞いたことがあるが、それが submitheader
だろうか?
まあ、これはあくまで Bitcoin Core の RPC であって、Bitcoinプロトコルがそのまま展開されるわけではない。プロトコルの方まで読まないと細かいところまではわからないのだが、今回は省略だ。
regtest だと difficulty target が 0x7fffff0000000000000000000000000000000000000000000000000000000000とかなり大きい値だったので、nonce は見つかりやすい。
うまくいかないと bad-cb-height
、 time-too-old
のようなエラーが出た。
同じノリで testnet も意外とあっさりできるんじゃないのかと期待してやってみたのだが・・・4時間経っても見つけられていない。まあ、その間にブロックが進んでやり直しになったりはするのだが、それにしても見つからないものだな。甘かったです。
試作したコードは JavaScriptで書いて、nonce を乱数で選んで for文でぐるぐる回しているだけだ。本職のマイナーの人からすると「甘えてるのか!」と思われそうだ。
OSが載った環境だと、それだけで演算が別の作業に使われて計算する量が減ってしまうので、GPUなりOS無しの環境だったりを用意して演算に専念させたくなるという気持ちは理解できた。