裸族のスカイタワーのファン換装

【販売終了】 裸族のスカイタワー 10Bay SATA6G (CRST1035EU3S6G) - 株式会社センチュリー

これ。 HDDが10台まで入るので、8TBのHDDを挿していけば合計80TBのストレージになる。

やっぱりダメだったのでファンを交換することにした。

f:id:kusano_k:20190620012528j:plain

左が元々付いていたファン。 ARX DC BRUSHLESS FD1214-S1053E。 同じくらいの風量が良いと思うのだけど、ググっても情報が出てこない。

右が交換用に買ったファン。

CFZ-140GLA | Ainex

補足 CFZ-140GLの型番変更品です。違いはありません。なお、中央の丸ラベルは旧型番のままで変更はありません。

どういうこっちゃ……。

裸族のスカイタワーの筐体はタワー型デスクトップと同じ作りで、後ろのネジ2個を外して、側面のパネルを後ろにスライドすれば開く。 固くて他にネジがあるのかと思ったけれど、そんなことはなく、ドライバーでこじれば外れた。

ケーブルの長さが足りないとか、コネクタ形状が合わないとか、そういう問題は無く、素直に交換完了。 風量も手を当てた感じは同じくらい。 HDDの温度が50度を超えるので、どうせならもっと風量の多いファンに交換しても良かったかもしれない。

追記。

55℃になってCrystalDiskInfoの温度が赤くなる。 さすがに怖いので、noctua NF-A14-PWMに交換。 フル回転でもそんなに煩くない。 温度は50℃前後。

PCを買った(2018年版)

PayPayが上限5万円まで20%ポイント還元ということで新しくPCを組んだ。 買ったパーツをメモしておく。 「今使っている電源は何だったっけ?」というときに、いちいちPC本体を確認するのは面倒。

今まで使っていたPCは4年前に買った。 CPUはCore i7-4790、メモリーは16GB、SSDが240GB。 Cドライブが足りないくらいでスペックには特に不満は無いが、USBポートがダメになったり、たまに再起動したりして困っていた。 USBポートがまともに動くなるようになるのは当然として、4年も経っているのだから、普段使うときにも快適になるかなと思ったけれど、体感できるほどに変わることは無かった。 ムーアの法則は終わった。

ハイエンドとまではいかなくてもそこそこ良いものを、多少のスペックよりは安定をという方針。

ケース: Fractal Design Define R6 - Black - Tempered glass

もうそんなにパーツを組み替えることもないので、小さいなケースにしようと思っていたのに、どうしてこうなった。 たしかに組み立てはすごく楽。 置き場所も作業スペースも広ければ、ケースは大きいほうが良いのだが……。 HDDは逆側から入れるので、PCを奥に押し込めている場合は、引っ張り出さないといけない。 電源ボタンやUSB端子が上面に付いていて、光学ドライブを付けなければ前面は全く使わないから、前後を逆にして壁に押しつけられる。 良い。

店に展示してあるPCは側面が透明のものが多いし、マザーボードにもRGB端子が付いているので、どうせ飽きるだろうなとは思いつつも、一度は側面が透明なものを試してみたかった。 案の定、3日で飽きた。 ケースは中身を保護するためにあるのに、もし何かでPCを持ち運ぶことになったら、ガラスに気を使わないといけない。 これしか無かったので内部のパーツが白いものにした。 黒いほうが良いかと思ったけど、CPUファンなどを光らせたときに、白だと色が良く分かる。

電源: CORSAIR RM750x

750W。 PCの調子が悪くなるのはだいたい電源がへたったときだと思っている。 なので、容量大きめで有名どころを。 どうだろうか。

マザーボード: ASUS TUF Z390-PLUS GAMING

ミリタリーグレードのTUFチョークコイルがどうたらこうたら。 高信頼性を謳っている高いものを買ったのだから、今度はUSBポートが壊れたりしないでくれ。 PayPayで当たりが出て、全額キャッシュバック✌

CPU: Intel Core i7-9700K

別にオーバークロックはしないのだが、最新世代のCPUは「K」付き(倍率ロックフリー)しかなかった。 i5-9600Kでも良かったのだけど、IntelのCPUは品薄らしく、店にこれしか無かった。

CPUクーラー: Scythe 虎徹MarkⅡ TUF

オーバークロックはしないので特に拘りはない。 ちなみに、「K」付きのCPUにはクーラーは付いてこないので、何かしら買わないといけない。

無駄に光る。 最近のマザーボードや周辺機器にはRGBの発光を制御できるピンが付いていて、マザーボードから色をコントロールできる。 負荷状況に応じて色を変えるような設定もできるらしい。

ケーブルがメッシュで包まれていて固いので、ズレてファンに当たってしまう。 ガムテープでヒートシンクに貼り付けた。

モリー: crucial W4U2400CM-8G

DDR4 PC4-19200 8GBの2枚組。 定番ブランドということでcurcial。 ヒートシンクの付いたかっこいいメモリーのほうが良かった気もするが、まあいいや。

「16GBは基本的人権」みたいな話を聞くので、前のPCですら16GBだったし32GBか64GBくらいにしようかなと思っていたけれど、値段を見てヒヨった。 一時期は最安値の2倍くらいの価格になっていたらしい。 PCパーツで値上がりとかあるのか。 暗号通貨みたい。 いくで。やるで。DDR4メモリー買い増しや。

グラフィックボード: ASUS TURBO-RTX2070-8G

今回のパーツの中で一番高い。 ゲームはしないが、ディープラーニングで何かやるぞという決意。

SSD: Intel 760p 512GB

小さい。 マザーボードにネジ止めする。 ストレージが1 TB程度で良いなら、SATAケーブルは要らないのか。

HDD: Western Digital WD6003FZBX

ストレージが512 GBではさすがに足りないので。 WDのHDDは色がいくつかあって、パフォーマンスの黒。6 TB。 HDDが増えても面倒だから、「8 TB未満のHDDは買わないぞ」という信念があったが、黒は8 TBモデルが無かった。

OS: Windows 10 Pro

前のPCのOSはDSP版のWindows 7をアップグレードしたもの。 OSと一緒に買ったパーツさえ移せば、「前のPCのパーツを交換しただけだ」と強弁できそうな気もするが……まあ、買っておこう。

HomeとProの違いのうち気にしていたのは、リモートデスクトップと、グループポリシーを使って正攻法でWindowsアップデートの再起動を止められること。 どちらも、代替手段はありそうだけど、まあ、Proにしておこう。

値段

種別 パーツ スペック 価格
ケース Fractal Design Define R6 23,630
電源 CORSAIR RM750x 750 W 15,789
マザーボード ASUS TUF Z390-PLUS GAMING 17,992
CPU Intel Core i7-9700K 8 コア, 3.6 GHz 51,494
CPUクーラー Scythe 虎徹MarkⅡ TUF 5,470
モリー crucial W4U2400CM-8G 16 GB 21,578
グラフィックボード ASUS TURBO-RTX2070-8G 8 GB 83,700
SSD Intel 760p 512GB 512 GB 26,978
HDD Western Digital WD6003FZBX 6 TB 28,870
OS Windows 10 Pro 27,410
302,911

ポイントは、普通10%還元のところ、PayPay払いだと8%還元。 ものによってはポイント無し。

改めて計算してみると、ボーナス後と、PayPay100億円の熱気に当てられたのとで、調子に乗った感がある。 しばらくは倹約に努めよう……。

技術書典4

初めて技術書典にサークル出展した。

https://techbookfest.org/event/tbf04/circle/19360001

技術書典は通運会館で開催された第1回に一般参加したことがある。 そのときもとても混み合っていた覚えがあるけれど、そもそも会場が狭かった。 今回のアキバ・スクエアは広いのに、それでも混んでいた。 参加者は6,000人越えらしい。

頒布物

とりあえず、C92とC93で頒布した本を持っていくことにした。

日光企画とねこのしっぽなら直接搬入ができるらしいが、印刷会社を変えるのは勝手が分からず何かミスりそうで怖いので止めておいた。折りたたみ式のキャリーカートなら使っても良いらしく、買ってはみたけれど、キャリーカートでも大変そうなのでやっぱり宅配便で送った。正解だったと思う。印刷会社から直接宅配便で送ってもらえば、送料が1回分節約できたのだろうか?

新刊が無いのは寂しいので、次のコミケで頒布しようとビットコイントランザクションスクリプトを調べていたことを書いてコピー本を作ることにした。

ビットコインブロックチェーンに溶けていく様子。

コンビニの印刷機でもKinko'sでもだいたい白黒1面10円くらいで、冊子印刷だと1面に2ページ入るので、1ページ5円くらいになる。セブンイレブンはファイルを事前にネットで登録しておいて印刷できるものの、これだと値段が倍になる。USBメモリに入れて持って行ったほうが良い。セブンイレブンは印刷の品質が良いが折りもステープラー綴じもしてくれない。Kinko'sはステープラー綴じまでしてくれるものの、冊子印刷の場合はUSBメモリのファイルから直接印刷はできず、一度普通に印刷してスキャンする必要があって品質が落ちる。悩ましい。「Kinko'sでPCをレンタルして出力すれば何とかなるかも」という話を聞いたので次があれば試したい。

売れ行き

「ZIP、完全に理解した」は早々に完売。「ksnctf C92」は、あまり売れないだろうと思って、ZIPの半分刷った。こちらもしばらくして完売。「モナーでもわかるビットコイントランザクションスクリプト(プレビュー版)」はあまり売れず。既刊よりこちらのほうが売れるかなと思い、ネットプリントにアップロードして、完売しても欲しい人は番号で印刷できるようにと考えていたが無用な心配だった……。みんな「完売した」と言っているし、あれだけ参加者がいれば何でも売れそうなものだが、そんなに甘くはなかった。

f:id:kusano_k:20180504234304p:plain

Twitterでプリント番号を共有するというのを見かけた。 いつの間にか、ネットプリントがそれに対応してシェア機能が付いていた。

今後

「ZIP、完全に理解した」は、COMIC ZINに「再販してくれ~」と頼んだらOKをもらったので、印刷を発注した。 来週くらいには在庫が復活すると思う。

COMIC ZIN 通信販売/商品詳細 ZIP、完全に理解した

「ksnctf C92」は、ちまちま印刷するのも面倒なので電子化しようと思う。

Chinachuで録画サーバーを作った

https://chinachu.moe/

今まではメインで使っているデスクトップPCでアニメを録画していた。 特に土日の夜にアニメが集中していてPCを再起動することができずに不便。 録画専用のサーバーを作ることにした。

DockerやAnsibleでのコード化はしていてないので、自分のためにも過程をメモしておく。

ハードウェア

今ではプレミア価格になっているPT3が2枚付き。 ベースはProLiant MicroServer。 音はとても静か。 CPUが非力なので、ブラウザの視聴はカクカクで使い物にならなかった。

ICカードリーダーは、SCR3310-NTTCom。 確定申告の電子申請にも使える。

録画用に4 TBのHDDを増設した。 「Linux HDD増設」とかでググって使えるように。

概要

そもそも、ChinachuとMirakurunの関係が分かっていなかった。 ChinachuはMirakurunに放送の録画を投げる。 Mirakurunは複数のソフトから依頼を受け付けて、録画デバイスを上手いこと使い分ける。 EPG番組表の取得もMirakurunの仕事。 Mirakurunはさらに録画コマンドに処理を投げる。 下のレイヤーから作業を進めると動作確認ができて良い。

OS

ネットの記事を見るとUbuntuの例が多い。 Ubuntuを適当にインストールしてSSHで繋げられるようにする。

ドライバ

カードリーダーは標準ドライバで動く。 aptで何をインストールしたか忘れたけど、pcsc_scanJapanese Chijou Digital B-CAS Card (pay TV)と出てくれば良い。

PT3も標準ドライバで動く。 /dev/dvb/adapter?がPT3の枚数×4個見えれば良い。 偶数が衛星波、奇数が地上波。 使うドライバによって、使う録画コマンドが異なる。 標準ドライバのほうが安心だと思ったけど、PT3専用のドライバのほうが使っている人が多くて楽だったかもしれない。

録画コマンド

recdvb(PX-BCUD対応版)のインストール « » Sat's space

recdvbをインストール。 この記事に載っているコマンドで視聴可能な.tsファイルが生成されれば良い。 ただ、このままだとちょっと問題がある。

recdvbrecdvbchksigSNRが取得できない。

USB接続で地デジ4チャンネル録画できるチューナーPX-Q1UDを買ったそして試した - Write and Run

この記事のパッチを当てる。

BSプレミアムなどの番組情報が取得できない(録画もできない?)。PT3にTSIDを渡さないといけないチャンネルがあるらしい。TSIDが何で、どこのレイヤーで効いているのか、分からん。

PT3 + recdvb + MirakurunでNHK BSプレミアムのEGPが取得できない問題に対応 - I am a wannabe.

PT3環境でmirakurun と http://www13.plala.or.jp/sat/recdvb/recdvb-1.3.1.tgz の組み合わせでEPGが取得できない問題に対する修正

このパッチを当てて、さらにコメント欄の修正を加える。 この辺りは4月以降のBSチャンネル再編で修正が必要になりそう。

Mirakurun

https://github.com/Chinachu/Mirakurun

npmに登録されている。 が、一部のチャンネルで録画したファイルがTVTestで視聴できなかった。

PATの書き換えでTSパケットの空きを0xffで埋めるようにした by kusano · Pull Request #34 · Chinachu/Mirakurun

この修正を加えたら、TVTestでも見られるようになった。 手元でビルドしてインストール。 手順は、

https://github.com/Chinachu/Mirakurun/blob/master/.github/CONTRIBUTING.md

に載っている。

mirakurun config [server|tuners|channels]でそれぞれ設定。

serverは初期設定のまま。

tuner。

- name: PT3-S1
  types:
    - BS
    - CS
  command: recdvb --dev 0 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-S2
  types:
    - BS
    - CS
  command: recdvb --dev 2 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-S3
  types:
    - BS
    - CS
  command: recdvb --dev 4 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-S4
  types:
    - BS
    - CS
  command: recdvb --dev 6 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-T1
  types:
    - GR
  command: recdvb --dev 1 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT3-T2
  types:
    - GR
  command: recdvb --dev 3 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-T3
  types:
"/usr/local/etc/mirakurun/tuners.yml" 60L, 1127C
- name: PT3-S1
  types:
    - BS
    - CS
  command: recdvb --dev 0 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-S2
  types:
    - BS
    - CS
  command: recdvb --dev 2 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-S3
  types:
    - BS
    - CS
  command: recdvb --dev 4 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-S4
  types:
    - BS
    - CS
  command: recdvb --dev 6 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-T1
  types:
    - GR
  command: recdvb --dev 1 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT3-T2
  types:
    - GR
  command: recdvb --dev 3 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-T3
  types:
    - GR
  command: recdvb --dev 5 <channel> - -
  decoder: arib-b25-stream-test

- name: PT3-T4
  types:
    - GR
  command: recdvb --dev 7 <channel> - -
  decoder: arib-b25-stream-test

- name: TBS6922
  types:
    - SKY
  command: szap-s2j -c /usr/local/etc/szap-s2j.conf -l <satelite> -S 1 -p -r -M 5 -C 35 <channel>
  dvbDevicePath: /dev/dvb/adapter0/dvr0
  decoder: arib-b1-stream-test
  isDisabled: true

recpt1の代わりにrecdvbを使うように変更。

channels。

うちのマンションはケーブルテレビでテレ玉チバテレビも入るので、チャンネルスキャンをする。

curl -X PUT "http://localhost:40772/api/config/channels/scan"

このコマンドを実行するとchannelsの設定が上書きされて空行などが消えるので注意。

Chinachu

Wikiのインストール手順の通り。

https://github.com/Chinachu/Chinachu/wiki/Gamma-Installation-V2

録画先を増設したHDDにして、ファイル名を<title> [<channel-name>][<date:yyyymmdd-HHMM>].tsにした。

http://サーバーのIPアドレス:20223を開いて、番組表が見られて、録画ができれば良い。 同じLAN内からは繋がる。

nginx

外からも操作できるようにしたい。 DDNSで自宅に繋がるようにはしている。

一応Chinachu単体でも、wuiUserswuiOpenServerwuiOpenPortを設定すれば外からアクセスできるのだが、廃止予定らしい。 nginxでリバースプロキシを立てる。

aptでインストールして、 /etc/nginx/sites-enabled/default を削除。 /etc/nginx/conf.d/に適当なファイル名で、

server {
  listen 80 default_server;
  location / {
    auth_basic "secret";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://サーバーのIPアドレス:20772;
  }
}

を書き込む。 htpasswd -c /etc/nginx/.htpasswd ログインIDでパスワードファイルを作成。

HTTPS

録画サーバーに平文で繋ぐのが憚られるような信用できないネットワークを使う機会があるか……?とは思うが、このご時世、webサーバーを立てたらHTTPSにもしておこう。

https://certbot.eff.org/

で、nginxとOSを選択。 出てきたコマンドを入力すると、証明書の取得、nginxの設定の変更、証明書の更新をcronに登録までやってくれる。 すごい。

Samba

メインのWindows PCから録画したファイルを見られるようにする。 sudo apt install sambaでsambaをインストールして、/etc/samba/smb.confに次の設定を追加。

[video]
  path = 録画ディレクトリ
  browsable = yes
  guest ok = no
  read only = no
  writable = yes

sudo smbpasswd -a ユーザー名

録画予約

この時期にいちいちアニメのタイトルを登録して、表記揺れに悩まされたりするのが面倒。 ジャンルアニメを全部録画したらどうなるのだろうと試してみたところ、1週間で1 TBくらいだった。 4 TBのHDDではちょっと足りない。 また、地上波と衛星波がそれぞれ4 chずつだと、数番組は予約が被って録画ができなかった。 ちまちまと登録するしかない。

テキストエディタに回転表示機能があったら便利なのではないか

C言語でこういうコードがあったとする。

int f(Z *z) {
    int result;
    W w;
    X x;
    Y y;
    Z z;
    result = hoge(&w);
    if (result != E_OK)
        return result;
    result = fuga(&w, &x);
    if (result != E_OK)
        return result;
    result = piyo(&w, &x, &y);
    if (result != E_OK) {
        /* piyoのエラーはログを出す */
        log("error in piyo !!!! code: %d, arg: (%s, %s)", result, W2str(&w), X2str(&x));
        return result;
    }
    result = hogera(&y, &z);
    if (result != E_OK)
        return result;
    return E_OK;
}

Erlangで同じコードを書くと、Erlangは途中でreturnすることができないので、こうなる。

-spec f() -> {ok, z()} | {error, term()}.
f() ->
    case hoge() of
        {ok, W} ->
            case fuga(W) of
                {ok, X} ->
                    case piyo(W, X) of
                        {ok, Y} ->
                            case hogera(Y) of
                                {ok, Z} ->
                                    {ok, Z};
                                {error, Reason} ->
                                    {error, Reason}
                            end;
                        {error, Reason} ->
                            %% piyoのエラーはログを出す
                            log("error in piyo !!! code: ~p, arg: (~p, ~p)", [Reason, W, X]),
                            {error, Reason}
                    end;
                {error, Reason} ->
                    {error, Reason}
            end;
        {error, Reason} ->
            {error, Reason}
    end.

ネストが深いし、関数の呼び出しとエラー処理が離れていて読みづらい。 returnのある言語でも、single-entry single-exitルールで書いていたり、JavaScriptのいわゆるコールバック地獄になったりすると、同じようになる。

エラー処理が離れている点については、エラー処理を先に書くという手がある。

-spec f() -> {ok, z()} | {error, term()}.
f() ->
    case hoge() of
        {error, Reason} ->
            {error, Reason};
        {ok, W} ->
            case fuga(W) of
                {error, Reason} ->
                    {error, Reason};
                {ok, X} ->
                    case piyo(W, X) of
                        {error, Reason} ->
                            %% piyoのエラーはログを出す
                            log("error in piyo !!! code: ~p, arg: (~p, ~p)", [Reason, W, X]),
                            {error, Reason};
                        {ok, Y} ->
                            case hogera(Y) of
                                {error, Reason} ->
                                    {error, Reason};
                                {ok, Z} ->
                                    {ok, Z}
                            end
                    end
            end
    end.

見た目も元のC言語のコードに近い。 ただし、ネストの深さは変わらない。 C言語では上から下に読めるが、Erlangでは左上から右下に読むことになる。

エディタがこのように斜めに表示してくれれば、ネストの深さの問題が解決する。

f:id:kusano_k:20180310171629p:plain

「Google にソフトウェアエンジニアとして入社して10年と10日がたちました」のクイズを解いた

Google にソフトウェアエンジニアとして入社して10年と10日がたちました — hayato.io

面白かったので、今からでも挑戦してみてほしい。 プログラミングが大変なのは最初のステップだけで、あとはひらめき。 下に解法を書く。 出題者のきらきら☆はやとたんさんの許可は得ています。

































































問題の画像はジュリア集合。 また、PNG内にJuliaというkey名で、left=0.0,top=-0.75,width=1.5,height=1.5,c=-0.?????-0.?????iというコメントがある。

一部は伏せ字でパラメタが与えられているので、伏せ字の部分を計算すれば同じ画像が生成できそう。 ここが一番大変だった。 全探索は無理なので、上位桁だけ探索して、差分が小さい値の下位桁を探索したり、画像の一部だけで差分を計算したりした。 伏せ字の部分の答えは、c=-0.75037-0.11177i。 次のプログラムで問題の画像と(ほぼ)同じ画像が得られる。

from PIL import Image
img = Image.new("RGB", (797, 797))
for y in range(797):
  for x in range(797):
    p = (x/797.*1.5)+(y/797.*1.5-0.75)*1j
    c = 0
    while c<255:
      p = p*p + (-0.75037-0.11177j)
      if abs(p)>2.:
        break
      c += 1
    img.putpixel((x, y), (c, c, c/2+128))
img.save("ans.png")
print "".join(map(chr, C))

f:id:kusano_k:20180228183855p:plain

この画像と問題の画像には差がある。 問題の画像を拡大して良く見ると、ポツポツと点が見える。 ジュリア集合にはこのような点は無いはず。

f:id:kusano_k:20180228184425p:plain

問題の画像と本来のジュリア集合で差があるところの差分を調べると次のようになっている。

32, 68, 68, 32, 32, 68, 50, 32, 32, 32, 32, 50, 32, 32, 32, 32, 32, 32, 32, 50, 32, 32, 32, 32, 32, 32, 32, 32, 48, 50, 50, 32, …

値の範囲がASCIIコードっぽいので、文字に直す。

 DD  D2    2       2        022    A 22 2  0 1   20 00  0      2  2 0     D D         2 0        60 2    0   4    7  0   0  6  002  2 02  63      222 72   2   22      2  0 0             2   6 2     00 2                0   0  0  0          D  2   0  2 7  F  2     2   4    C        0            7       0   02  D  D     2         0 0     0  2 0    6  02002 2   2   2 00    0C 1 3      0     2D   2D2 2  0  0   C   F     022 0   1  02      0  6             32     2        2  B2       22      6                    020   0      2  02  0    3     2       D2        2  00   0E         0   87   0 20     EF     2    2 34  1    2   D2  D      DDD  00    2  6    0   2     6 2   2         2   0    6 3 93 2 0  0    2     D 2  20 2  2   6 2   2   0      2  200   2   200       0    30 0  0   2 DD        2       0  2  0    20        0  02    0 6             23               2  2            20       0 0    0          2   2   22             6 20   2                 20      7         22 6    0     2 2     0   22    3    A  22       D  DD   D        A00 E 0 2      2  9 22 2          02   0      3  6      0       D  2 D   2  2  7 2             F7 50              0 0     4  1  370 0      2 2     D 2 0        0   2 0   00 0 4    200 0      0  2202 0  2  38  2 2    DD 2 2    22     2  0C    2   0        2                2       3 83936  2    C2  D  2  D  D   00    2  7  2  2  20 220  0    20 0        2  2     3    00       2  D22   2    2      7 0   2 2  7 0    2        0B  200 0  02 73     02   2        D    D 2 200 2 0      2   2   7      222          2 2        0 3   20 00    D 22 2DD2 0      2 22    0 0    2     0         2          0  3 6 5C 00   A 2D 2D  D 2 D 2 0      0      020      7 220 000   2072 2       7   37 2  2 0     D     D 22 0     0 2 63200 2   0 04 4    0 22     2   0  0          022 22  D 2    D    D     2    2 2  2 0 2   260 20   2   A      0 0    2 9 2    0        D       D  22   0  0     20     2  8 6 2 2 0     07       2     6  3     0    D    D  2       22     11       0     8    2      2 2 0          4     2        22   2    2      2 20 72 0      0        0   0   0   2 2      2  3   2000 0 2  DD        DD      2 2     2 2   2            2   2 0     0  2  2    6       B       D  2D    2           02    0 0   00  0      0               7             22           0 0  0   4   7    2  2 7 7   2         00     0 0    34 0   2 2 2    2   DD    2 20  000          2 0 4             9 22 0      6 3 7     2 2     DD     2  20 002  22       00 0 C   6  0 020027 2 7   2  02 2    33  2   0    D   22 2D   202  0   27        20 2   72 22  20    9     2   0  0      2  2 2 222 D      2   0   200      02       6 6       2        02 2     3   5   0     D D   D    D  22   0           0    6   0  0         0  2      5  3   2  20   D      22D2 2  0  02      02C           9   2 2        0 00         493 0     D2     DD 2   D   2     0     2   0   5   2      0C 0 6    2    2   536   0        2     D 2D2 20     225  00 0        5        2 2  F   2   0   0      2   002   D  2 D DD2  0  2     602   2    0        00 002  6  2  0 0        1    0        22D      D    0       0    2 2    8 702     2      070      6 1 8 00  22    2 2  2  2 2   2   02       A0  2 2   6  20        2      20220 1     3   0 2 D D  D  2      0       00 2   200    0      0    00    2   0 0      3 31   2 0  D  D  D  D DD     2 0      27           4 22 2   200 7 2C2  2       6 32     2  2 2      22  2     22 2    C0  0   2            0   50       20 63   2 0     2    2 2      2  2        0         A02    0            0 0    0 725 4         D D D D  2       0 0  0  C2   22   07  9  0  2   2 2  72    2     3    3  2 0  2       D  D     20     0   02  2 0  2 6           0        0        66   0    D     2           22  22  0     02   0   622  00   2  2   2    2 4 6        2   2 D  D    2   0    2  0  2  0 02     0   000   20  2     0   00 684       0   2     2            0 0        0    2         0   2   F 020       5   63    2          D D2 D         02   22  2 0   2  28   2        2      0 22           0 0    2   2        002    26    2  2   0  0 2220  0     02    2          5      2  2 DDD   D 2       0  0    020 00 0 2   2    2     0        2     3    0   22  DD   2     D  202 022    00  202   C  E 0 002  2  0277    2    0        2 0 00D2 2   2D         2 0     2   02 2    7  70 2 02   20 4     0 2 2  8  5  0     2D   2  2        00   2            22            0    6 0A   0000  3    2   0   2            D0   22  2 1  202  0 0  26  2 0     202    720   2  3   3   2   2    2 DD   D2 22  2       02   0 22     6   2         9 2  222       6   202  0     D     2  D  2  00  0  0  02 0  0   390  0   2    6 0 0        3   6 0  22   2 D     2  2 0     2 0      2   0      62   2 00   5D        0   335        2 D      D2 DDD2      0 2 7  0 00  2          20         2C 0  22  6     0 2 00  2D 2       2   2    20   22  22   2  67             2     2 00  63 3   2    2 D  D   2      B        02  0 2    0 20  6F 20     0 0 6       2   09  21   0 0         2D   20     20 2 4  2 0        2F 0 2 2  2 2  50   00   2    3       2   2  2 D  D   A   0 0 20    0  0 220   E6   0     2   2 0  2    0     3 0  22       D        7  0      0   2 2 0       4       2 0    2     2 0 4  6  2    0   D  D22 D2    C20 2        20       0   6 2  0  0     E 2 2   02   6 53 2    0 2  2    2  2          0        22 0 2   5 0 22               0 0        0     0     D 2 D  D 2  2   2 2  0    0   2    2720  0     5   0   2   72       2 0   D        22D      0  0     0 0 2         5 2    2 2  0  2   0 2   666    0                 2D 0         6         00    2 0      0 4      02 0    3    22   22         DD D  22 2  2 0        0  2 2   E0 00          0 0   22            2       2  D 22      0  0       200 0  209         0020   0      00       0          D    22    0          9 2  2 2      202 22     0       2 02 2      6  0  000      D  DDD      2            2       0         7  6      2202     6    2 22         D        0   2  24  0   00 2   646 70  20 C  8   220 0   C   63 7 0      2 22    22 2         2 3 22     20     22C   0 2 2  6  0     0   3   3       2  D    2  D2    2 0        0       00  D 2 202     26  2   0 22 0     6   000     D   B   D   0     2  6 0       22 27  00  0       6    222   A  12  C   2 20  22    

ここでだいぶ悩んだ。 ジュリア集合との差分で値を埋め込む方式では、元の画像が明るいところには値を埋め込めないので、値が埋め込まれている位置には意味が無い……?とか、空白と16進数の文字で出現頻度に偏りがあるが……?とか考えた。

この文字列の長さは6241文字である。 ところで、問題の画像のサイズは797x797ピクセルだった。 なぜ800x800ではなく、こんな中途半端な値なのだろう? 797は素数だった。 また、6241は792素因数分解できる。

ということで、この文字列を79文字ごとに改行して正方形にしてみる。

 DD  D2    2       2        022    A 22 2  0 1   20 00  0      2  2 0     D D  
       2 0        60 2    0   4    7  0   0  6  002  2 02  63      222 72   2  
 22      2  0 0             2   6 2     00 2                0   0  0  0        
  D  2   0  2 7  F  2     2   4    C        0            7       0   02  D  D  
   2         0 0     0  2 0    6  02002 2   2   2 00    0C 1 3      0     2D   
2D2 2  0  0   C   F     022 0   1  02      0  6             32     2        2  
B2       22      6                    020   0      2  02  0    3     2       D2
        2  00   0E         0   87   0 20     EF     2    2 34  1    2   D2  D  
    DDD  00    2  6    0   2     6 2   2         2   0    6 3 93 2 0  0    2   
  D 2  20 2  2   6 2   2   0      2  200   2   200       0    30 0  0   2 DD   
     2       0  2  0    20        0  02    0 6             23               2  
2            20       0 0    0          2   2   22             6 20   2        
         20      7         22 6    0     2 2     0   22    3    A  22       D  
DD   D        A00 E 0 2      2  9 22 2          02   0      3  6      0       D
  2 D   2  2  7 2             F7 50              0 0     4  1  370 0      2 2  
   D 2 0        0   2 0   00 0 4    200 0      0  2202 0  2  38  2 2    DD 2 2 
   22     2  0C    2   0        2                2       3 83936  2    C2  D  2
  D  D   00    2  7  2  2  20 220  0    20 0        2  2     3    00       2  D
22   2    2      7 0   2 2  7 0    2        0B  200 0  02 73     02   2        
D    D 2 200 2 0      2   2   7      222          2 2        0 3   20 00    D 2
2 2DD2 0      2 22    0 0    2     0         2          0  3 6 5C 00   A 2D 2D 
 D 2 D 2 0      0      020      7 220 000   2072 2       7   37 2  2 0     D   
  D 22 0     0 2 63200 2   0 04 4    0 22     2   0  0          022 22  D 2    
D    D     2    2 2  2 0 2   260 20   2   A      0 0    2 9 2    0        D    
   D  22   0  0     20     2  8 6 2 2 0     07       2     6  3     0    D    D
  2       22     11       0     8    2      2 2 0          4     2        22   
2    2      2 20 72 0      0        0   0   0   2 2      2  3   2000 0 2  DD   
     DD      2 2     2 2   2            2   2 0     0  2  2    6       B       
D  2D    2           02    0 0   00  0      0               7             22   
        0 0  0   4   7    2  2 7 7   2         00     0 0    34 0   2 2 2    2 
  DD    2 20  000          2 0 4             9 22 0      6 3 7     2 2     DD  
   2  20 002  22       00 0 C   6  0 020027 2 7   2  02 2    33  2   0    D   2
2 2D   202  0   27        20 2   72 22  20    9     2   0  0      2  2 2 222 D 
     2   0   200      02       6 6       2        02 2     3   5   0     D D   
D    D  22   0           0    6   0  0         0  2      5  3   2  20   D      
22D2 2  0  02      02C           9   2 2        0 00         493 0     D2     D
D 2   D   2     0     2   0   5   2      0C 0 6    2    2   536   0        2   
  D 2D2 20     225  00 0        5        2 2  F   2   0   0      2   002   D  2
 D DD2  0  2     602   2    0        00 002  6  2  0 0        1    0        22D
      D    0       0    2 2    8 702     2      070      6 1 8 00  22    2 2  2
  2 2   2   02       A0  2 2   6  20        2      20220 1     3   0 2 D D  D  
2      0       00 2   200    0      0    00    2   0 0      3 31   2 0  D  D  D
  D DD     2 0      27           4 22 2   200 7 2C2  2       6 32     2  2 2   
   22  2     22 2    C0  0   2            0   50       20 63   2 0     2    2 2
      2  2        0         A02    0            0 0    0 725 4         D D D D 
 2       0 0  0  C2   22   07  9  0  2   2 2  72    2     3    3  2 0  2       
D  D     20     0   02  2 0  2 6           0        0        66   0    D     2 
          22  22  0     02   0   622  00   2  2   2    2 4 6        2   2 D  D 
   2   0    2  0  2  0 02     0   000   20  2     0   00 684       0   2     2 
           0 0        0    2         0   2   F 020       5   63    2          D
 D2 D         02   22  2 0   2  28   2        2      0 22           0 0    2   
2        002    26    2  2   0  0 2220  0     02    2          5      2  2 DDD 
  D 2       0  0    020 00 0 2   2    2     0        2     3    0   22  DD   2 
    D  202 022    00  202   C  E 0 002  2  0277    2    0        2 0 00D2 2   2
D         2 0     2   02 2    7  70 2 02   20 4     0 2 2  8  5  0     2D   2  
2        00   2            22            0    6 0A   0000  3    2   0   2      
      D0   22  2 1  202  0 0  26  2 0     202    720   2  3   3   2   2    2 DD
   D2 22  2       02   0 22     6   2         9 2  222       6   202  0     D  
   2  D  2  00  0  0  02 0  0   390  0   2    6 0 0        3   6 0  22   2 D   
  2  2 0     2 0      2   0      62   2 00   5D        0   335        2 D      
D2 DDD2      0 2 7  0 00  2          20         2C 0  22  6     0 2 00  2D 2   
    2   2    20   22  22   2  67             2     2 00  63 3   2    2 D  D   2
      B        02  0 2    0 20  6F 20     0 0 6       2   09  21   0 0         
2D   20     20 2 4  2 0        2F 0 2 2  2 2  50   00   2    3       2   2  2 D
  D   A   0 0 20    0  0 220   E6   0     2   2 0  2    0     3 0  22       D  
      7  0      0   2 2 0       4       2 0    2     2 0 4  6  2    0   D  D22 
D2    C20 2        20       0   6 2  0  0     E 2 2   02   6 53 2    0 2  2    
2  2          0        22 0 2   5 0 22               0 0        0     0     D 2
 D  D 2  2   2 2  0    0   2    2720  0     5   0   2   72       2 0   D       
 22D      0  0     0 0 2         5 2    2 2  0  2   0 2   666    0             
    2D 0         6         00    2 0      0 4      02 0    3    22   22        
 DD D  22 2  2 0        0  2 2   E0 00          0 0   22            2       2  
D 22      0  0       200 0  209         0020   0      00       0          D    
22    0          9 2  2 2      202 22     0       2 02 2      6  0  000      D 
 DDD      2            2       0         7  6      2202     6    2 22         D
        0   2  24  0   00 2   646 70  20 C  8   220 0   C   63 7 0      2 22   
 22 2         2 3 22     20     22C   0 2 2  6  0     0   3   3       2  D    2
  D2    2 0        0       00  D 2 202     26  2   0 22 0     6   000     D   B
   D   0     2  6 0       22 27  00  0       6    222   A  12  C   2 20  22    

これを縦に読むと2がだいたい交互に出てくることに気が付く。 縦に読んで、空白を削除すると、

2B2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2B0A7C20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207C0A7C202020202020202020202020202020202020202020202020202020202020436F6E67726174756C6174696F6E7321202020202020202020202020202020202020202020202020202020202020207C0A7C20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207C0A7C20202020202020202020202020202022446F20746865207269676874207468696E672E204D61792074686520636F6465206265207769746820796F752E222020202020202020202020202020207C0A7C20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207C0A7C20202020202020202020202020202020202020202020202020205468616E6B20796F7520666F7220796F75722074696D652E2020202020202020202020202020202020202020202020202020207C0A7C20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207C0A7C20437265617465642062792062383630636132383364303135643833396631303433313237353336666232393063373438646665353938373439613635323363623130663635653031323561207C0A7C20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207C0A2B2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2B

となる。16進数として文字列に直すと、

+-----------------------------------------------------------------------------+
|                                                                             |
|                              Congratulations!                               |
|                                                                             |
|               "Do the right thing. May the code be with you."               |
|                                                                             |
|                          Thank you for your time.                           |
|                                                                             |
| Created by b860ca283d015d839f1043127536fb290c748dfe598749a6523cb10f65e0125a |
|                                                                             |
+-----------------------------------------------------------------------------+

「画像に隠されたメッセージ」はDo the right thing. May the code be with you.なので、私(@kusano_k)の場合は、

$ echo -n 'kusano_k' 'Do the right thing. May the code be with you.' | sha256sum
ffc5b750d7a37441f98ca4f9e4912bf7ad956934a4e9b4fe1740a935bd056235  -

10周年おめでとうございます!

NEETCOINに名前を刻んだ

以前ビットコインで、Ghash.I.O.というマイナーがハッシュレートの50%以上を占めて「これはヤバイ」と話題になったことがある。

ビットコインが「終了」の危機に瀕している ひとりの山師のハッシュレートが51%を超えたため - Market Hack

ただ、この記事は間違いで、検証作業は参加者全員が行っている。Ghash.I.Oが悪意を持ったときにできるのは、コインを捏造することではなく、過去の支払いを無かったことにすること。

ハッシュレートの現在の分布は↓のサイトなどで確認ができる。

https://blockchain.info/ja/pools

f:id:kusano_k:20180123054816p:plain:w480

このハッシュレートは、実際には過去の一定期間にブロックを採掘したマイニングプールの割合から計算しているのだと思う。 あるブロックがどこのマイニングプールから採掘されたのかが、なぜ分かるかというと、ブロックにマイニングプールの名前を刻んでいるから(なので、マイニングプールが強力なハッシュレートを使って悪さをしようとするならば、そもそもブロックに自分の名前を書かず、ハッシュレートをごまかすと思う)。

マイニングプールの名前は各ブロックに格納される一番最初の「取引」に書く。 この取引はマイニングの報酬が与えられる取引。

「取引」は入力と出力から構成される。 入力には、過去の取引のIDとScriptSig(基本的には過去にその取引でコインを受け取ったのが自分であることを示す電子署名)が並んでいる。 出力には、支払先のScriptPubkey(基本的には公開鍵)と金額が並んでいる。 マイニング報酬の取引の入力には「過去の取引」が存在しないので、IDには000…0を書き、ScriptSigの部分には好きなデータが書ける(はずだったけど、途中で最初にブロックの高さを書かないといけなくなった)。 ここにマイニングプールの名前が書かれている。例えば、505582番目のブロック最初の取引を見ると、/BTC.COM/ という文字列が含まれている。

暗号通貨のマイニングというとマイニングプールに繋いで採掘するのが普通だが、これでは最初の取引に自分の名前が刻めない。

NEETCOINという暗号通貨があるのを知った。 今からビットコインのブロックを採掘するのは無理だけど、新しい暗号通貨ならば採掘している人が少ないから何とかなりそう。

NEETCOINのハッシュアルゴリズムは、Litecoinや昔のMonacoinと同じScrypt。 ScryptはASICが出回っているので、CPUやGPUで太刀打ちするのが難しい。 以前に同僚からScryptに対応したASICを買い取っていた。

他で見ない形をしているけれど、Gridseed miniが2個。 元から付いているヒートシンクを外して、基盤を表と裏に貼り付け、別途ファンを付けたらしい。 1個で毎秒約250kハッシュ(khash/s)、2個で500 khash/s。 CPUだと4年前に3万5千円で買ったCore i7 4790 3.60GHzで80 khash/sくらい。 ただ、これでも最新の高いASICよりはだいぶ能力が劣るらしく、数日動かしても1個もブロックが掘れなかった。 26万円くらいのAntminer L3+は504 Mhash/sらしい。

こういう機械を持っている人の計算量(ハッシュレート)を買うことにした。 NiceHashが有名。 入金までしてみたものの、なかなか使い勝手が悪い。 NiceHashではハッシュレートを取りまとめて送ってくるので強力すぎる。 上限は設定できるけれど、最低でも0.01 Thash/s。 これは採掘をしていたときのネットワーク全体のハッシュレートよりも強い。 採掘できるコインが一定になるように難易度が調節されるので、難易度が上がって効率が悪くなる。 あと、他の購入者の設定金額との兼ね合いで、ハッシュレートが回ってきたり来なかったりする。 まあ、すでにネットワーク全体のハッシュレート高い暗号通貨を、後述のような試行錯誤をしたりすることなく採掘する分にはこれで困らないのだろう。

Mining Rig Rentalsならば、1人の相手からハッシュレートを買えるので、強力すぎないしハッシュレートも一定。

この手のハッシュレート購入サイトは、Stratumというプロトコルの接続先を指定してハッシュレートを買う。 手元で動かしている暗号通貨のウォレットとのプロトコル(Getwork)とは異なるので、変換する必要がある。

プロキシしてくれるプログラムがあるだろうとググってみると、stratum-mining-proxyというのが出てきたが、これはGetworkのマイニングツールをStratumのサーバーに接続するためのもので逆方向。 マイニングプールはまさにStratumをGetworkに変換しているので、大仰する気もするが、自分でマイニングプールを動かさないといけないらしい。 これがなかなか面倒だった。

https://github.com/Crypto-Expert/stratum-mining

このソフトを使う。 フォークが色々とあるけれど、結局本家のこれが一番新しいのではないかと思う。 ただし、ちょこちょこ修正が必要。

Wikiにセットアップ方法が書かれている。

まず、依存ソフトのstratumが、distribute_setupのエラーでビルドに失敗するので、別途pipでインストールしてsetup.pyを書き換える。 参考

stratum-mingも修正する

別にマイニングプールで人を集めるわけでも無いのでSQLiteで充分だけど、実装されていないところがあるので、MySQLをセットアップして使う。 DBの初期設定にはこのSQLを流し込む。 結局試していないが、もしかしたらDB無しでも動くかも。

設定ファイルに追加したり、変更したりした項目は下記の通り。

CONFIG_VERSION = None
CENTRAL_WALLET = '自分のNEETCOINのアドレス'
COINDAEMON_TRUSTED_HOST = '自分のウォレットのIPアドレス'
COINDAEMON_TRUSTED_PORT = 21010
COINDAEMON_TRUSTED_USER = 'user name'
COINDAEMON_TRUSTED_PASSWORD = 'password'
ALGO_NAME = 'ltc_scrypt'
CUSTOM_HEADER =  "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
CUSTOM_DIFF1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
COINDAEMON_REWARD = 'POS'
DB_MYSQL_PASS = 'pooldb'
USERS_AUTOADD = True
COINBASE_EXTRAS = 'この値がブロックに書かれる'
POOL_TARGET = 76294
VDIFF_MAX_TARGET = 76294
BOT_ENABLED = False

POOL_TARGETVDIFF_MAX_TARGETの値を大きくしてプールの難易度を上げておかないとMining Rig Rentalsでエラーになる。

0.5 LTC(1万円分くらい)で、約500 NEETCOINを採掘できた。 採掘している途中でNEETCOINを取り扱う取引所が出てきたからか、採掘する人が増えたので今はもっと効率が悪い。

残念ながら、NEETCOINのブロックエクスプローラにはマイニングプールの名前やScriptSigは出てこない。 例えば87190番目のブロックは私が採掘したので、ウォレットのデバッグウィンドウでgetblockbynumber 87190 trueと打ち込むと私のIDがcoinbaseに書かれているのが分かる。

"vin" : [
{
"coinbase" : "0396540104dea8635a08f8000001010000000b2f406b7573616e6f5f6b2f",
"sequence" : 0
}