マイニング練習シリーズ第2弾です。前回はMoneroのテストネットで、ノード構築からマイニング、報酬受け取りまでを一巡しました。今回はDero(デロ)というプライバシー系の暗号資産(仮想通貨)で練習します。

今回もテストネットを使って練習を・・・と思ったのですが、事情により参加者1人のローカル・プライベートテストネットという形になりました。

なぜDeroで練習するのか

このサイトで何度か言及しているCodablecashのPoW(マイニングの計算)は、CPU向けの複数のアルゴリズムを組み合わせたつくりになっています。その部品のひとつにAstroBWTという計算があり、この計算の本家が今回のDeroです(現行実装はDeroHEと呼ばれ、アルゴリズムはAstroBWTv3に進化しているようです)。

前回のMoneroは「CPU向けPoWの代表格」という大枠の類似でしたが、今回は部品そのものの本家で体験してみよう、というわけです。マシンはいつもの中古ミニPC(Core i3-10105T、4コア8スレッド、GPUなし)です。

公開テストネットが死んでいた

さっそく調査を開始したところ、Deroのテストネットに入るための入口(シードノードといって、最初に接続しに行く案内所のようなノード)は設定ファイル上1つだけで、そこに到達できませんでした。どうやら公開テストネットは現在稼働していないようです。

使われなくなったテストネットはこんな感じになってしまうのか・・・と思いつつ調べると、derod(Deroのノード本体)はテストネットモードでピア(通信相手)がゼロでも、ジェネシスブロック(チェーンの最初のブロック)から自分ひとりのチェーンを育てられることが分かりました。

  • 参加者は自分1人。掘るのも自分、ブロックを検証するのも自分
  • チェーンはジェネシスから始まるまっさらな新品
  • 外部との通信が一切不要になるので、コンテナは--network=none(ネットワーク完全遮断)で起動できる

前回は「外向き接続だけの非公開ノード」でしたが、今回はさらに進んで外部通信完全ゼロでの練習です。隔離練習としてはむしろ理想的な形になりました。

環境づくり: 今回もPodmanで

環境の作り方は前回と同じで、Podman(ルートレスコンテナ)を使用します。Deroは公式がビルド済みバイナリを配布しているので、今回もソースからのビルドは不要です。

Containerfileというコンテナ用のレシピファイルはこんな感じです。

FROM docker.io/library/ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates \
        curl \
    && rm -rf /var/lib/apt/lists/*

# 更新時は https://github.com/deroproject/derohe/releases の
# checksum.txt.signed からハッシュを取り直す(中身はbase64)
ARG DERO_RELEASE=Release142
ARG DERO_SHA256=4a6c0096dc27308fc2efe6f16e9bb2cb6d5ec6a397e62ad3b16d3a2ccf013380

RUN curl -fsSL -o /tmp/dero.tar.gz \
        "https://github.com/deroproject/derohe/releases/download/${DERO_RELEASE}/dero_linux_amd64.tar.gz" \
    && echo "${DERO_SHA256}  /tmp/dero.tar.gz" | sha256sum -c - \
    && mkdir -p /opt/dero \
    && tar xzf /tmp/dero.tar.gz -C /opt/dero --strip-components=2 \
    && rm /tmp/dero.tar.gz \
    && ln -s /opt/dero/derod-linux-amd64 /opt/dero/derod \
    && ln -s /opt/dero/dero-wallet-cli-linux-amd64 /opt/dero/dero-wallet-cli \
    && ln -s /opt/dero/dero-miner-linux-amd64 /opt/dero/dero-miner \
    && ln -s /opt/dero/explorer-linux-amd64 /opt/dero/explorer

RUN userdel -r ubuntu 2>/dev/null || true; \
    groupadd -g 1000 miner \
    && useradd -m -u 1000 -g 1000 -s /bin/bash miner \
    # Deroの各プログラムは「自分の隣」にログを書こうとし、書けないと
    # ウォレットは即終了する。練習用なので/opt/deroを書込可にして許す
    && chown -R miner:miner /opt/dero

USER miner
WORKDIR /data
ENV PATH="/opt/dero:${PATH}"

CMD ["/bin/bash"]

前回同様、展開前にSHA256ハッシュを照合しています。Deroのリリースにはchecksum.txt.signedというファイルが添付されていて、ヘッダを除いた本体をbase64 -dで展開するとハッシュ一覧が出てきます。

ビルドと起動はこうです。起動オプションの意味は前回の記事と同じなので、今回の新顔--network=noneだけ説明します。

$ podman build -t dero-practice .
$ podman run -d --rm --name dero-testnet \
    --userns=keep-id --network=none --cap-drop=all \
    --security-opt=no-new-privileges \
    -v "$PWD/data:/data" dero-practice \
    derod --testnet --data-dir=/data --log-dir=/data

--network=noneは文字通り「このコンテナにネットワークを与えない」指定です。ウォレットもマイナーもpodman execで同じコンテナの中に入って動かすので、外部どころかホストとの通信も不要、という徹底ぶりにできます。

起動するとすぐ、ジェネシスだけの高さ0のチェーンが誕生します。前回はノード同期に一晩かかりましたが、今回は同期ゼロ秒です。当たり前ですが、新品のチェーンには同期すべき過去がありません。

ウォレット作成と、「アカウント登録」

ウォレット作成は前回と似た対話式です。

$ podman exec -it dero-testnet dero-wallet-cli --testnet \
    --generate-new-wallet --wallet-file=/data/wallet.db \
    --daemon-address=127.0.0.1:40402

パスワードを決めて、シードの言語を選ぶと、25語のシード(復元用の合言葉)が表示されます。

ウォレットは番号を選ぶメニュー画面になっていて、1で自分のアドレス(deto1...で始まる文字列)が表示されます。

ここからがMoneroと違うところです。Deroでは、作ったばかりのアドレスはそのままでは使えません。受け取りも採掘もできない「未登録」状態で、登録トランザクションをチェーンのブロックに取り込ませて初めて有効になります。実際、未登録のままマイナーを起動してみたらunregistered minerという表示になっていました。

登録は「CPU時間で払う」

メニュー4で登録を開始すると、CPUが全力になり、ファンがまわり始めます。これは登録用の事前PoWで、仕組みを調べるとこうなっていました。

  • ウォレットが登録トランザクションを、乱数を変えながら延々と作り直す
  • トランザクションのハッシュの先頭3バイトが00 00 00になったら当たり
  • 期待値は約1677万回。1回ごとの計算が楕円曲線という重い演算なので、時間がかかる

Deroは送金手数料が無料の設計なのですが、無料となると大量のゴミアカウント作成などの不正行為が発生しやすくなります。その対策として、手数料の代わりにCPU時間で入場料を払わせる仕組みになっているようです。

うちのミニPCの8スレッド全力で、当たりまで約8分。後でチェーン閲覧画面を見たら、自分の登録トランザクションのIDは00000044...で始まっていました。ちゃんと先頭3バイトがゼロになっていますね!

鶏と卵の問題: 最初のブロックは誰が掘るのか

登録トランザクションを送信(registration tx dispatched successfully)しても、それだけでは未登録のままです。誰かがブロックを掘って取り込んでくれる必要があります。

ここで疑問が湧きます。掘るには登録済みアドレスが要る。登録するには掘ってくれる人が要る。参加者1人の新品チェーンではどうしたらいいのか?

答えはジェネシスブロックにありました。Deroは開発者のアドレスをジェネシスに登録済みで埋め込んであり、誰でも最初からこのアドレス宛てには掘れるのです。まずは開発者アドレス宛てに掘って、チェーンを回し始めます。

$ podman exec -it dero-testnet dero-miner --testnet \
    --wallet-address deto1qy0ehnqjpr0wxqnknyc66du2fsxyktppkr8m8e6jvplp954klfjz2qqdzcd8p \
    --daemon-rpc-address 127.0.0.1:10100 --mining-threads 4

参加者1人+難易度最低の新品チェーンなので、ブロックは即座に積まれ始めます。自分の登録トランザクションは高さ1のブロック(チェーン最初の実ブロック)に取り込まれていました。

まもなくウォレットのAccount Unregistered表示が消え、残高に8.00000が出現。掘った覚えのない8DEROは、テストネット限定のウェルカムボーナスでした(ソースを見ると、登録成功時に全アカウントへ8DERO配る処理がテストネット分岐に入っています。蛇口いらずで親切)。

登録が済んだら、マイナーを止めて受取先を自分のアドレスに変えて掘り直します。残高がじわじわ増えていき、最終的に高さ7まで掘って残高9.16850DEROになりました。

一人ぼっちチェーンのログ

以下は、回している間に気がついたことのメモです。

  • we want to broadcast block, but donot have peers — 掘れたブロックを配りたいのに配る相手がいない、という意味。参加者1人なので正常です
  • ミニブロックのrejectが3割ほど出ますが、チェーンは普通に伸びていきます
  • コンテナ内のログ時刻はUTC表示で、手元の時計と9時間ずれます
  • ミニブロックというのはDeroの構造で、1ブロックはミニブロック10個でできています。マイナーが見つけるのはミニブロック単位で、報酬はブロック内で分配される仕組みです
  • ブロック間隔の目標は18秒と、Monero(約2分)よりだいぶ速いです。

事件: ウォレットファイルが消えた

残高確認のためウォレットを開き直したら、wallet.db存在しないというエラーが出ました。さっきまで使っていたのに、です。

調べると、このウォレット(アルファ版)はメニューの8(Close)か0(Exit)でクリーンに終了したときにだけファイルを書く実装でした。作成した時点ではメモリ上にしかなく、初回セッションを(ターミナルを閉じるなどして)クリーン終了しなかったため、ファイルが一度も保存されていなかったのです。

ここで冒頭の25語シードのメモの出番です。メニュー3(シードからの復元)に25語を入れると、ウォレットは完全に復元されました。しかも復元経路では即座にwallet.dbが保存され、バックアップの.bakまで自動生成されました。残高9.16850もそのまま。

教訓を並べておきます。

  1. シードのメモは練習でも習慣化しておく
  2. ウォレットの終了は必ずメニューから(クリーン終了時にしか保存されないソフトが実在する)
  3. 復元=鍵の復元であって、チェーン上の状態(登録・残高)とは関係がない

ハッシュレートの実測

うちのミニPCでAstroBWTv3がどのくらい出るのか、スレッド数を変えて測りました。マイナーにはdero-miner --benchというベンチマーク機能が同梱されていて、採掘なしで計測だけできます。

スレッド数 ハッシュレート
1 305.6H/s
4 1035.7H/s
6 1235.2H/s
8 1395.6H/s

採掘中のライブ表示とも一致します(計測中はクロック3.6GHz張り付き・67°C・スロットリングなしも確認)。

なお、マイナーのステータス行は

NW 26.2 KH/s ... MINING @ 954 H/s

のような2本立てで、NWネットワーク全体の推定ハッシュレートMINING @自分の実測です。参加者1人のチェーンではNW推定はブロック生成ペースからの逆算で実態とかけ離れた数字になりがちなので、見るべきはMINING @欄のほうです。

面白いのは前回のMoneroとの比較です。Moneroはうちの6MBのL3キャッシュが3スレッドで満杯になるため3スレッドが最適解でしたが、Deroは8スレッドが最速(4スレッド比+35%)で、ハイパースレッディングが素直に効きます。AstroBWTv3は1スレッドあたりの作業メモリが小さく、L3の取り合いが起きないのではないか、と推測しています。

同じ「CPUマイニング」でも、アルゴリズムが変わると最適なスレッド数も1秒あたりの回数も全然違う。前回も学んだ「H/sという数字は通貨をまたいで比較できない」を、今回も実感しました。

残高が誰にも見えない

Deroはプライバシーコインの中でも、準同型暗号という方式で残高そのものを暗号化したままチェーンに記録するのが特徴だそうです。Deroには公式のチェーン閲覧画面(explorer)が同梱されていて、Web画面を立ち上げて手元のブラウザでその中身を見てみました。

ここでひとつ手戻りがありました。本編のコンテナは--network=noneで起動しているため、後からポートを公開できません。ブラウザから見るには、いったんコンテナを止めて、ポート公開あり(--network=noneなし)の構成で起動し直す必要があります。といっても外向きの接続先は例の死んだシードノード1件だけなので、実質外部通信なしのままです。チェーンデータは./dataに残っているので、起動し直してもチェーンは無事です。

$ podman stop dero-testnet
$ podman run -d --rm --name dero-testnet \
    --userns=keep-id -p 8080:8080 --cap-drop=all \
    --security-opt=no-new-privileges \
    -v "$PWD/data:/data" dero-practice \
    derod --testnet --data-dir=/data --log-dir=/data
$ podman exec -d dero-testnet explorer \
    --daemon-address=127.0.0.1:40402 --http-address=0.0.0.0:8080

あとは手元のブラウザでhttp://<ミニPCのアドレス>:8080を開くだけです。

全ブロック・全トランザクションを見られる立場になっても、各アドレスの残高はどこにも表示されません。チェーンが自分のアカウントについて記録しているのは、こういう暗号文の塊だけです。

(チェーン側から見た自分のアカウントの「残高」)
data: 000f7a5b1c6d07765e4c573c16287d2b26c5ddc03f87814fb33fc9639099a482
      120002eacfbf92b94015c9b0b3d901dae37ec68f74dea7e4484c76d505aade4ad7c001

対して、パスワードでウォレットを開くと、プロンプトにちゃんと金額が表示されます。

deto1qyz 16/16 9.16850   TESTNET>>>

アドレスの先頭・同期状況・残高、という並びです。同じ残高が、チェーン側では読めない暗号文、ウォレット側では平文の金額。これを知っているのは、パスワードでウォレットを開けた自分だけです。

一方で、採掘報酬の受取先アドレスはブロックにそのまま露出します。プライバシーコインであっても、マイナーとして参加した瞬間にそのアドレスは半公開になるわけです。つまり、採掘用アドレスは人に見られる前提、保管用アドレスは非公開の金庫、と役割を分ける方針がますます固まりました。

ちなみに、explorerが公式同梱なのにも理由があって、「他人が運営するexplorerサイトで自分のトランザクションを検索すると、検索行動自体からプライバシーが漏れる。だから自分専用のexplorerを持て」という思想だそうです。徹底しています。

ジェネシスブロックの中身

せっかくチェーンの全部を見渡せるので、チェーンの始まりも見てみました。実物の値はこうです。

高さ0(ジェネシスブロック)
  hash:       03cb55c786330a91263cf6fbac06994931994878d975c5248670e3624e657640
  timestamp:  0
  difficulty: 1 / nonce: 0 / tx数: 0
チェーン総供給: 12,281,263 DERO
  • タイムスタンプがUnix時間の起点ぴったりの0(=1970年1月1日)で、explorerの経過時間表示が約56年になっていて笑いました
  • 総供給の約1228万DEROは、開発者アカウントへのプレマイン(事前発行)です。ジェネシスの時点でテストネットの全コインが発行済みで、以後のマイニング報酬はそこから払われる形でした

続く高さ1(チェーン最初の実ブロック)も見どころでした。こちらが中身です。

高さ1(最初の実ブロック)
  hash:       e2808a604ef4952bce7f7db71ca6e0a746a2554aaa70f28c6d9cf56200150f9a
  timestamp:  2026-08-02 20:09:46 (JST換算)
  difficulty: 10000
  tips:       03cb55c7...(親=ジェネシス)
  miners:     deto1qy0ehnqj...z2qqdzcd8p
              deto1qy0ehnqj...z2qqdzcd8p
              (同じ開発者アドレスが計10行)
  txs:        00000044a155d663a6aa2e774798e61d66e3cf424f2bd8bcf77aec975c9c5efa

マイナー欄には開発者アドレスがミニブロックの数だけ、つまり10行並んでいます。「1ブロック=ミニブロック10個」の実物ですね。今回は1人で10個とも掘ったので同じアドレスの繰り返しですが、本来ここは複数のマイナーの顔ぶれが並ぶ場所です。

そしてトランザクションは、例の00000044...で始まる自分の登録トランザクション1件だけ。サイズはわずか101バイトでした。あの8分間のCPU全力は、この101バイトを作るためだったんですね。

ブロックチェーンの「最初の1個」を隅々まで眺める機会はなかなかないので、ローカルテストネットならではの楽しみでした。

後片付け

前回のMoneroはチェーンデータが6GBありましたが、今回は参加者1人・高さ7のチェーンなので数MBです。片付けは同じ流れです。

$ podman stop dero-testnet    # ノード停止(--rm付き起動なのでコンテナも消える)
$ rm -rf ./data               # チェーン+ウォレット
$ podman rmi dero-practice    # コンテナイメージ

ウォレットは25語シードさえあれば再現できますし、Containerfileと起動スクリプトを残しておけば、環境ごと数分で作り直せます。今回は同期待ちすらないので、再現は前回より気軽です。

まとめ

  • Deroの公開テストネットは死んでいた
  • Deroのアカウント登録制は「手数料無料の代わりにCPU時間で入場料を払う」スパム対策
  • ハッシュレートは4スレッドで約1.0kH/s(8スレッド約1.4kH/sが最速)