Develop with pleasure!

福岡でCloudとかBlockchainとか。

Gitian Buildで決定性ビルドを実行する

少し前にBitcoin Core v0.21.0がリリースされたけど、このBitcoin CoreのリリースにはGitian Buildという決定性ビルドプロセスが使われている。

https://github.com/bitcoin/bitcoin/blob/master/doc/release-process.md

Gitian Buildとは?

公式サイトによると、複数のビルダーが、同一のバイナリを生成するビルドツールで、それぞれのビルダーが同じバイナリに署名し、バイナリが同じソースから生成されたもので改竄されていないことを保証する。

Gitian Builderとは?

Gitian Builderは、qemuベースのVMでソフトウェアをビルドする際に使用するツール。現在サポートされているVMは、

  • KVM
  • LXC
  • Docker
  • Virtual Box

の4つ。今回はDocker使ってみる。

Bitcoin Coreのビルド

先日リリースされたBitcoin Core v0.21.0をGitian Buildでビルドしてみよう。ビルド方法はこのドキュメントに記載されてる。

セットアップ

Bitcoin Coreには、gitian-build.pyという自動スクリプトが用意されているので、これを使う。

まず、スクリプトをコピー。

$ cp bitcoin/contrib/gitian-build.py .

続いて、セットアップを実行(-dはdockerを使用するオプションで、-kだとkvm、指定されてなかったらlxc)。

$ ./gitian-build.py -d --setup

すると、必要なソフトウェアがインストールされ、以下のリポジトリがcloneされる(最初にスクリプトをコピーしたのは、スクリプトディレクトリにこれらがcloneされるから)。

自分のPGP鍵で署名するには、gitian.sigsリポジトリをフォークして、リモートリポジトリとして登録する↓

$ export NAME = azuchi
$ cd gitian.sigs
$ git remote add $NAME git@github.com:$NAME/gitian.sigs.git
mac用のビルドへの署名用のセットアップ

mac用のビルドに署名する場合は、追加のセットアップが必要になる。まず、Appleのダウンロードページから*1Xcode_11.3.1.xipをダウンロードする*2Xcodeのバージョン7以降は、非macOSSDKを展開するのが楽になるようXcode.appから.xipに変わったらしい。Ubuntuでは以下の方法で、Xcode.appを展開し、Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gzを生成する(Xcode_11.3.1.xipは同じディレクトリに配置しとく)。

$ sudo apt-get install cpio
$ git clone https://github.com/bitcoin-core/apple-sdk-tools.git
$ python3 apple-sdk-tools/extract_xcode.py -f Xcode_11.3.1.xip | cpio -d -i

↑が終わると、Xcode.appというディレクトリが作成されており、Xcodeのデータが展開されてる。ちなみにmacOSの場合は、xip -x Xcode_11.3.1.xipを実行するだけ。

続いて、bitcoingen-sdkスクリプトを使って、Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gzを生成する。

$ ./bitcoin/contrib/macdeploy/gen-sdk 'Xcode.appのパス'

実行ディレクトリに↑のファイルが生成されるので、それをgitian-builder/inputsに配置する。

$ mkdir -p gitian-builder/inputs
$ cp Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz gitian-builder/inputs

ビルド

Bitcoin Core 0.21.0をビルドするためには↓を実行する。

$ export VERSION=0.21.0
$ ./gitian-build.py -j 3 -m 5000 -d --detach-sign --no-commit -b $NAME $VERSION

各オプションは:

  • -jオプションはVMに割り当てるコア数+1。
  • -mVMに割り当てたRAMより少し小さい値(MB単位)で、-jと合わせてビルドの高速化のためのオプション。
  • --detach-signは、デタッチ署名のアサートファイルを作成し、何もコミットしないオプション。
  • --no-commitは、gitに何もコミットしないオプション。
  • -bは、Gitian Buildを行う指示。

ちなみにこの実行にはマシンスペックにもよるけどかなり時間かかる。実行が終わるとbitcoin-binaries/0.21.0LinuxmacOSWindows用の各バイナリが生成されている。

$ ls bitcoin-binaries/0.21.0 
bitcoin-0.21.0-aarch64-linux-gnu-debug.tar.gz    bitcoin-0.21.0-riscv64-linux-gnu.tar.gz
bitcoin-0.21.0-aarch64-linux-gnu.tar.gz          bitcoin-0.21.0-win64-debug.zip
bitcoin-0.21.0-arm-linux-gnueabihf-debug.tar.gz  bitcoin-0.21.0-win64-setup-unsigned.exe
bitcoin-0.21.0-arm-linux-gnueabihf.tar.gz        bitcoin-0.21.0-win64.zip
bitcoin-0.21.0-osx-unsigned.dmg                  bitcoin-0.21.0-x86_64-linux-gnu-debug.tar.gz
bitcoin-0.21.0-osx64.tar.gz                      bitcoin-0.21.0-x86_64-linux-gnu.tar.gz
bitcoin-0.21.0-riscv64-linux-gnu-debug.tar.gz    bitcoin-0.21.0.tar.gz

実行中のログにも表示されるが、これらのバイナリのハッシュ値は、公開されているBitcoin Coreのダウンロードサイトに記載されているハッシュ値と等しい。

そして、gitian.sigs/に以下の3つのassertファイルが生成されてる。

  • 0.21.0-linux/azuchi/bitcoin-core-linux-0.21-build.assert
  • 0.21.0-osx-unsigned/azuchi/bitcoin-core-osx-0.21-build.assert
  • 0.21.0-win-unsigned/azuchi/bitcoin-core-win-0.21-build.assert

以下のコマンドでこれらに署名する。

$ gpg --output ${VERSION}-linux/${NAME}/bitcoin-core-linux-${VERSION%\.*}-build.assert.sig --detach-sign ${VERSION}-linux/$NAME/bitcoin-core-linux-${VERSION%\.*}-build.assert
$ gpg --output ${VERSION}-osx-unsigned/$NAME/bitcoin-core-osx-${VERSION%\.*}-build.assert.sig --detach-sign ${VERSION}-osx-unsigned/$NAME/bitcoin-core-osx-${VERSION%\.*}-build.assert
$ gpg --output ${VERSION}-win-unsigned/$NAME/bitcoin-core-win-${VERSION%\.*}-build.assert.sig --detach-sign ${VERSION}-win-unsigned/$NAME/bitcoin-core-win-${VERSION%\.*}-build.assert

すろと、以下の3つの署名ファイルが生成される。

  • 0.21.0-linux/azuchi/bitcoin-core-linux-0.21-build.assert.sig
  • 0.21.0-osx-unsigned/azuchi/bitcoin-core-osx-0.21-build.assert.sig
  • 0.21.0-win-unsigned/azuchi/bitcoin-core-win-0.21-build.assert.sig

各OS毎の.assertファイルと.assert.sigファイルをコミットし、bitcoin-core/gitian.sigsリポジトリにPRを送ると。

$ git checkout -b ${VERSION}-not-codesigned
$ git commit -S -a -m "Add $NAME $VERSION non-code signed signatures"
$ git push --set-upstream $NAME $VERSION-not-codesigned

そしてコード署名者のみ、Windows/macOSのdetached signaturesを作成する。コード署名をするのは1人のみで、Windows/macOSのビルドがそれぞれ3つの一致する署名がある場合のみ、それぞれのリリース鍵で署名できる。

コード署名者以外は、Windows/macOSのdetached signaturesが作られるのを待つ。detached signaturesは、bitcoin-detached-sigsリポジトリにコミットされるらしいが、リポジトリ見る限り0.11.0以降はコミットされてないっぽい(プロセスが変わってる?)。

その後、署名されたWindows/macOS用のバイナリを作成する。さらに各ビルダーは署名されたバイナリへの署名を作成する。この署名がgitian.sigsリポジトリ${VERSION}-osx-signed/"${SIGNER}"${VERSION}-win-signed/"${SIGNER}"にコミットされる。

そして最後に、ビルドされた成果物に対して、SHA256SUMS.ascが生成され、それにGPG署名される。

*1:Apple IDでログインが必要

*2:7.3GBほどの容量がある