前の記事の最後で「GUI からハーベストできるようにするのも考えたのですが、リモートの鍵をどこに置くかで止まっています」と書きました🥺。
置かないことにしました🎉。
heika v0.9.0 で、GUI のボタンひとつでハーベストできるようになりました。コマンドラインでも、キーストアのアカウントを名前で渡すだけでハーベストします。どちらも、リモートの鍵と VRF の鍵をディスクに書きません。GUI からもコマンドラインからも、実際にブロックができるところまで確かめました。わーいわーい✨。
なにが困っていたか
heika のウォレットには、上級者向けの機能として、BIP 39 のパスフレーズ、いわゆる「25 番目の単語」があります。同じニーモニックでも、25 番目の単語を変えると、まったく別のアカウントが出てきます。なので、脅されてニーモニックを渡すことになっても、25 番目の単語なしの、ちょっとだけ入ったウォレットを見せて「これで全部です」と言えます。本命は、単語を知っている人にしか見えません。いわゆる plausible deniability(もっともらしく否認できること)です。とってもだいじな plausible deniability です。
ただ、これは忘れると資産ごと取り出せなくなりますし、付けると公式ウォレットでは開けない鍵になります。詳しくない人や、かわいいミスを連発しちゃう人がやらかさないように、コマンドラインで --expert を付けたときにだけ聞くようにして隠してあります。GUI にはまだ出していません。「エヘッ」ではすみません。
で、ここにハーベストを足そうとすると困ります。自分のノードでハーベストするには、リモートの鍵と VRF の鍵をノードに渡す必要があります。ふつうのノードは、これをディスクのファイルに置きます。
- 平文のファイルで置くと、その鍵がチェーンでどのアカウントにリンクされているかを引けば、隠していたアカウントが出てきます。「これで全部です」が台無しです
- 暗号化して置くと、今度はその鍵の鍵をどこに置くの、という話になります。マトリョーシカです
というわけで止まっていました。
置かないことにした
結論はこうなりました。
-
鍵はキーストアから導出する。キーストアの秘密ごとに、ハーベストの鍵の元になる
Harvest-Secretというニーモニックを入れておき、そこから導出します - 25 番目の単語をかけて導出する。アカウントと同じ 25 番目の単語をかけるので、単語ごとに別の鍵になります。キーストアを開けられても、隠しウォレットのハーベストの鍵はたどれません
- ノードにはメモリでしか渡さない。デスクトップではノードが別のプロセスなので、標準入力で渡します。Android では同じプロセスなので、そのままメモリで渡します
ノードを止めれば鍵も消えます。次に動かすときは、ウォレットを開いたときにまた導出して渡します。安全第一。
「使わない人にも入れておく」ことが肝要
Harvest-Secret は、ハーベストするかどうかに関わらず、heika がキーストアを書くときに全部の秘密に入れます。誰のキーストアにも同じようにあるので、あることからは、ハーベストしているかどうかがわかりません。TrueCrypt の隠しボリュームで、使っていない領域も乱数で埋めておくのと同じ気持ちです。
それと、一度入れたら書き換えません。最初は「リンクするたびに作り直せばいいのでは」と思ったのですが、キーストアを書き換えた時刻と、チェーンでリンクした時刻が並ぶと、どのアカウントのものかの手がかりになってしまいます。作り直したいときは、導出のパスの番号を一つ上げることにしました。
アカウントの鍵とは道を分けておく
導出のパスは、公式ウォレットのアカウントの鍵と同じ形の、途中の番号だけを変えたものにしました。アカウントの鍵ではそこが 0 なので、リモートは 1、VRF は 2 にしています。何かの拍子にシードを取り違えても、アカウントの鍵そのものが出てくることはありません。heika だけの独自の仕様で、公式ウォレットにはないものです。公式ウォレットには引き継げませんが、ハーベストの鍵はいつでも作り直せるので、互換性より安全性を取りました。
割り切ったところ
完全ではありません。リンクはチェーンに載るので、「このアカウントは誰かのノードでハーベストしている」ことまでは誰にでも見えます。隠せるのは、あくまで「そのアカウントがこのキーストアのもの」という部分です。
他人のノードに委任するときは、この導出は使わず、毎回乱数で鍵を作ります。前の委任先は古い鍵を知っているので、使い回すとそのノードがハーベストを続けられてしまうからです。しかしながら実のところ本当は明らかに使いまわしてるようなトランザクションをちらほら見かける気がするので無力さを痛感するばかりでして……。
使い方
GUI
ウォレットを開いて、ノードのタブで「このノードでブロックを作る」を押すだけです。
- 導出した鍵がもうリンクされていれば、そのまま鍵をノードに渡してハーベストを始めます
- まだなら、リンクするトランザクションを組んで、送る前に中身を見せます。古いリンクがあれば、外すのも同じアグリゲートにまとめます。残高や重要度が足りないときは、ハーベストしても当たらないので、ここで警告します
- 承認すると送って、ブロックに入るのを待ちます
- 入ったのが見えたら、鍵をノードに渡してハーベストを始めます
ウォレットを閉じても(ロックしても)、ノードを止めるまではハーベストを続けます。そこはもともと、鍵の受け渡しで悩んでいただけなので。
コマンドライン
heika harvesting --network mainnet --link-derived main --out link.bin
heika tx sign --network mainnet --wallet-account main --out link.signed.bin link.bin
heika tx announce --network mainnet --connect <ホスト>:<ポート> --client-cert node.full.crt.pem link.signed.bin < node.key.pem
heika experimental peer start --config peer.yaml --harvest main
--link-derived でリンクして、--harvest で同じ鍵を導出してノードに渡します。起動するたびにキーストアのパスフレーズを聞かれます。鍵をディスクに置かないので、そうなります。--expert を付ければ、25 番目の単語も聞かれます。
ハマったところ
手数料が安いと、黙って捨てられる
GUI でリンクのトランザクションを送ったのに、いつまでたってもブロックに入りませんでした。「ばあさんや、なかなかこないねぇ……。」「そうですねぇ、おじいさん……。」と言いながら待っていたのですが、本当に来ません。つらい。
手数料の倍率を見たら、つないだノードが受け付ける下限を下回っていました。下回ると、ノードは何も言わずに捨てます。エラーも返しません。がちでつらい。
リンクのときはノードの下限を聞いて、それを満たすいちばん安い倍率にするようにしました。ついでに送金の画面も、ノードの下限に届かない選択肢は押せなくして、倍率を自分で入れられる「カスタム」を足しています。
古いピアしか見えていないと、枝分かれしたブロックを作る
コマンドラインでハーベストしたら、ブロックができた!……と思ったら、とっくに先に進んだ本線とは別の、古い先端の上に作っていました。
最初につながったピアが、止まったまま何か月も前の高さにいるノードだったんです。ほかのピアを知らないうちは、そのピアから見れば自分が最新なので、「追いついた」と思ってハーベストを始めていました。そういえば最近、ある VPS 屋さんで CVE-2026-99999 対応の一斉再起動がありましたね。自動で起動するようにしていないノードは、また止まったまま増えているかもしれません。しらんけど……っておいなんだその小学生が考えそうなCVE番号はっ!しかも本当にあるし……。へー PwnEscape、こゎぃ……🥺。
ファイナライズされた高さより下にいるピアは、別のチェーンにいるものとして数えないことにしました。そのうえで、同じチェーンのピアを少なくとも1つ聞いていて、自分より先にいるピアがいないときだけ、ブロックを生成します。
押そうとすると逃げるボタン
ノードのタブで、状態を表す1行の長さが変わるたびに、その下のボタンが上下に動いていました。押そうとすると逃げます。近所の横柄な野良猫かっ。明らかに UX 上の深刻な問題にほかならないので行の高さを固定しました。ざまあみやがれってんでぃ💪✨✨。
まだできていないこと
- GUI での 25 番目の単語(コマンドラインだけ)
- チェーン経由の委任を受けること(ほかの人から委任されても、まだハーベストしない)
- ウォレットが手元のノードに問い合わせること(残高と送金は、まだ設定の REST を通す)
- Android で画面を消しているあいだも、ちゃんと動き続けるか(スリープさせないようにはしたけれど、何のせいで止まって見えるのか、よくわからない)
- iOS は、まあ、はい……。
ノードは相変わらず試験的なので、中身も見た目も断りなく変わります。でも、ブロックは作れます。よかったら触ってみてください。
