「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
}

コミックマーケットC93

コミックマーケットC93で↓の本を頒布した。

ZIP、完全に理解した

f:id:kusano_k:20180121012800j:plain:w320

COMIC ZINで委託販売をしている。

http://shop.comiczin.jp/products/detail.php?product_id=35822

次のために諸々メモ。

刷り部数

前回と同じく100冊刷ったが、12時ちょっと前に完売。 もっと刷るべきだった。 とはいえ、CTFよりさらにニッチだし、告知ツイートのRTやFavもそんなに多くはなかったので、これでも強気のつもりだったのだが……。 ただ、前回は誰かに頼まれて複数買っていく人がいたけど、今回はそういう人がいなかったし、CTFほど売れなかったとも思う。

決済手段

Squareに使える端末がスマホしかなかった。 リーダーを接続するときにちょっと時間が掛かるので、タブレットを持っていって繋ぎっぱなしにしたほうが良かったかもしれない。

ポスター

PO.SU.TAのProとminiを買った。 高いがこのコンパクトさはすごい。 ケースも厚手でサイズがピッタリなので良い感じ。

kinko'sで値段を確認せずにA2サイズのポスターを印刷したら4,000円かかった。 印刷前に「仕上がりはこんな感じになります」と一部分を試し刷りしてくれたり、やけに丁寧だと思ったら……。 印刷所で本と一緒に注文すると余白カット付きでも2,000円らしいので、次からはこっちにしよう。 後ろにポスタースタンドがある時点で邪魔になるので、サイズはA1で良さそう。

ビットコインの送金手数料を安くしすぎてしまった場合の対処法

ビットコインの送金手数料

私が「1 BTC(約100万円)を持っている」というのが実際にはどういうことかというと、「私が秘密鍵を知っている公開鍵Xに対しての1 BTCの送金がビットコインブロックチェーンに存在する」ということである。 私がここから、0.01 BTCをAさんに送金して、マイナーには0.0001 BTCの送金手数料を支払おうとしたとき、私の使っているクライアントは「公開鍵Xから0.01 BTCをAさんの公開鍵に、0.9899 BTCを私の別の公開鍵Yに送金する」という取引指示を公開鍵Xに対応する秘密鍵で署名してビットコインのネットワークに送信する。大量のビットコイン採掘用ASICを抱えるマイナーは、この取引をブロックに詰めて、ブロックのnonceの値を試行錯誤してブロックのハッシュ値をある閾値よりも小さくする。これが成功して新しいブロックをチェーンに繋げることができると、マイナーは採掘報酬の12.5 BTCと、私の取引の入力と出力の差である0.0001 BTC(と、ブロックに詰めたその他の取引の手数料)が手に入る。

マイナーは当然ビットコインが欲しいので、手数料が高い取引を優先してブロックに詰める。 早く承認してほしければそれだけ手数料を積む必要がある。 最近この手数料が高騰していて「1時間以内にブロックに詰めてほしいな」と思うと、1,000円くらい払う必要がある。 ビットコインなら銀行送金に比べて手数料が格安という話が昔はあったなぁ……。

別に急ぐ送金でもないしケチるかと思って手数料を数円にしてしまったら全くブロックに含められなくて困った。 昔ならこれでも待っていれば何とかなったけど、最近は送金が多すぎてブロックは常にいっぱいなのでこんな安い手数料が詰められることはもはやなさそう。

Replace-by-Fee

公開鍵Xからの送金がブロックに含められていないということは、公開鍵Xにはまだビットコインが残っているということで、ここから手数料を上乗せした別の取引を作るという手がまず思い浮かぶ。 が、これは上手く行かない。 ビットコインのネットワークは、すでにネットワーク上に存在する取引と衝突する取引は(その取引がブロックに含められる前でも)拒否する(という動作がリファレンス実装)。

これを認めてしまうと二重支払いが容易になって、ビットコインの使い勝手が悪くなるかららしい。 逆に、これが認められていないので、ビットコインを支払われた側は取引がネットワークに広まった時点である程度は安心できる。

最初の送金時に「後で上書きするかも」というフラグを付けるReplace-by-Fee(RBF)というプロトコルがあって、Electrumに実装されているらしい。

Opt-in Replace-by-Feeによるトランザクションの置換(BIP-125) - Develop with pleasure!

Electrum2.7系で実装されたビットコインの「opt-in RBF」とは何か - ビットコインの情報サイトの運営者ブログ

待つ

ネットワーク上に送信されて、ブロックに入れられていない取引は各ノードのmempoolに入っている。 Mempoolのサイズは有限なのでそのうち消えるはずで、理屈の上では消えた後ならば、改めて手数料を上乗せして送金できるはず。 ただ、ウォレットアプリが一度送信済みとマークした取引をネットワークから消えたのを検知して元に戻すという処理を持っているのか分からない。 どうなんだろう?

ちなみに、たとえ手数料が低くても古くなった取引は優先度を上げるという仕組みが昔はあったけど、無くなったらしい。

Transaction Accelerator

マイニングプールViaBTCが、指定した取引を優先してブロックに詰めるというサービスを(たぶんボランティアで)行っている。 ここに取引IDを入力すれば良い。 ただ、↑の取引で試したら「手数料安すぎ」と蹴られた。

Transaction Accelerator - ViaBTC

お釣りアドレスを使った新たな取引

上手くいった方法。

最初の説明の「0.9899 BTCを私の別の公開鍵Yに送信する」の公開鍵Yから、それなりの手数料を付けて送金する。 マイナーがこの取引のそれなりの手数料を手に入れるには、最初の手数料の安い取引も同時にブロックに含める必要がある。

Bitcoin Coreならば、設定→オプション→ウォレット→コインコントロール機能を有効化する で送金時にどのアドレスから送金するかを選択できるようになる。 「送り先」を自分のアドレスにして、このアドレスに入っている全額を「金額」に入力し、「送金額から手数料を差し引く」にチェックを入れて、適当な手数料を選べば良い。 マイナーはこの手数料を得るために、2個の取引をブロックに詰める必要があるので、普通に選ぶ手数料の2-3倍くらいにしておくと良さそう。

f:id:kusano_k:20171107012528p:plain

コインコントロール機能が無いウォレットならば、自分の持っている全ビットコインを自分に送金すれば、お釣りアドレスも使われるだろうか? 送金が承認されるまで全ビットコインが使えなくなるかもしれないけど。

たまたま承認されただけかもしれないので、同じように困っている人がいたらこれを試して、上手くいったかどうかとか、ブロックに含めたマイニングプールがどこかとかをコメント欄ででも教えてほしい。

源ノ角ゴシックや源ノ明朝をインストールした環境でmoraダウンローダーが起動しない問題

やっと原因が分かった。

源ノ角ゴシックや源ノ明朝のSuper OTC版(言語・ウエイト全部入り)のフォント名が長すぎて、.NET Frameworkがバグっていた。 アンインストールして、Region-specific Subset OTFを入れ直しましょう。

f:id:kusano_k:20170917154922p:plain:w480

moraダウンローダ

f:id:kusano_k:20170917200512p:plain:w480

moraという音楽配信サイトがある。 クールごとにアニソンをまとめたページがあって便利。 DRMフリー。 過去にはダウンロードは10回までという制限があったが、(明記はされていないものの)今はこの制限も無くなったっぽい。

専用のmoraダウンローダーがある。 これを使うと、複数の曲をまとめてダウンロードすることができ、ファイル名を変えてフォルダ分けして保存してくれる。

今期のアニソンを買ってダウンロードしようとしたら、「moraダウンローダーは動作を停止しました」と出てきて起動しなくて困った。

f:id:kusano_k:20170917174947p:plain

源ノ角ゴシックと源ノ明朝

「げんのかくごしっく」「げんのみんちょう」と読む。Adobeが配布しているOSSフォント。

フリーなのにウェイトが多くて楽しい。 1個のフォントをソフトウェア処理で太字にするのは素人で、プロは最初から太さを変えて作られたフォントを使うらしい。

f:id:kusano_k:20170917195410p:plain

ダウンロードページ(ゴシック明朝)に行くと、種類が色々あってどれをダウンロードして良いのか迷う。 最近のOSにしか対応していないが、Super OTCというのをダウンロードすれば、1ファイルで言語もウェイトも全て揃う。

これが間違いの元だった……。 フォントをインストールすると、レジストリHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fontsに次の2個の値が追加される。長さはどちらも259文字。

  • 源ノ角ゴシック ExtraLight & Source Han Sans K ExtraLight & Source Han Sans SC ExtraLight & Source Han Sans TC ExtraLight & 源ノ角ゴシック Light & Source Han Sans K Light & Source Han Sans SC Light & Source Han Sans TC Light & 源ノ角ゴシック Normal & Source Han Sans K Normal & Sou
  • 源ノ明朝 ExtraLight & Source Han Serif K ExtraLight & Source Han Serif SC ExtraLight & Source Han Serif TC ExtraLight & 源ノ明朝 Light & Source Han Serif K Light & Source Han Serif SC Light & Source Han Serif TC Light & 源ノ明朝 & Source Han Serif K & Source Han Serif SC

中身を見ると、倍くらいの長さのフォント名を書きたがっているように見えるので、どこかで切り捨てられているらしい。

Adobeがこのフォント名にしているならば文句を言いたいけれど、セットになったフォントの名前を&で連結してレジストリの値名にしているのはWindowsだろうか?

調べ方

moraダウンローダーは黙って落ちるので、何も情報は得られない。 .NETだから普通にデバッグはできないし、どうするのだろうと思ったけど、便利なソフトがあった。

dnSpy

アセンブル、逆コンパイル等のソースコード解析作業ではなく、moraダウンローダーがスルーしている例外を調べる目的で使う。 分かりにくいけど「build passing」のバッジからビルド済みのバイナリがダウンロードできる。

このソフト上で実行すると例外が発生したときに、例外の中身が見られる。 $exception変数のInnerExceptionを順番に見ていくと大元の原因が分かる。

f:id:kusano_k:20170917190200p:plain:w480

レジストリが何とかで落ちているので、あとはSysinternals SuiteProcess Monitorを使うと、レジストリにどのような操作をしているかが分かる。 長さ512(たぶんバイトなので256文字)のバッファで読もうとしてエラーが返っていた。 惜しい。 あと4文字長ければ……。

RegEnumValueERROR_MORE_DATAを返しているだけなので、バッファーオーバーフローなどではなく、細工されたフォントをインストールしてmoraダウンローダーを起動するとシステムを乗っ取られるとかはない。

詳細

https://github.com/kusano/mora_not_run

.NET Frameworkの3.5以下の版には、RegistryKey.GetValueが256文字以上の値の名前を列挙できないというバグがあるらしい。 そんな制約は書いてないぞ。

試しに.NET Framework 2.0でウィンドウを作ってみたけれど、フォントの一覧は読みにいかなかったので、何がトリガーなのか分からない。

moraダウンローダーは.NET Frameworkを使っているだけなので、対応するなら.NET Frameworkのバージョンを上げるしかなさそう。

Registry Element Size Limits (Windows)

MSDNを見てみると、値の名前の最大長は16,383文字。 過去には(ANSIだけど)260文字という制約があったこともあるらしい。 フォント名が259文字に切り詰められていたのは、260文字までしか対応していない環境やソフトを意識してのことだろうか。 比べてみると最大長が256文字のRegistryKey.GetValueは雑。

ちなみに、Windowsレジストリエディタは260文字以上の長い値名を表示することはできたけれど、長い値名を設定しようとすると259文字に切り詰められた。

おまけ

moraダウンローダー以外のアプリが28e89a9f-e67d-3028-aa1b-e5ebcde6f3c8というMutexを作成していると同様に落ちる。 ↑のフォントのレジストリを列挙するプログラムでは、ついでにこのMutexを作成しているので、確認ができる。 たぶん、同時起動抑制とかに使っているのだと思う。 この文字列がmoraダウンローダーのGUIDならば何の問題も無いのだけど、これは.NET Framework 2.0のSystem.Reflection.AssemblyのGUID。 何か勘違いをしている?

あと、moraダウンローダーを起動して、最初の1曲目のダウンロードが「エラーコード:21003」で失敗するという問題がある。

サポートに訊いたら、「プロキシ設定やネットワーク通信状況に起因した問題である可能性」と言われたけど、最初の1曲目は常に失敗するし、Twitterを検索しても似たようなことを言っている人がいるし、ネットワークの問題などではないのでは。ついでに直してほしい。

コミケで同人誌を頒布した

東京に引っ越してきて以来、コミケにはだいたい毎回行っている。 「自分でも本を作りたいなぁ」その度に言っていて、やっと実現した。 ちゃんと記録していたわけではないが、費用は10万円くらい、時間は200時間くらいだろうか。 1,000円で100部頒布できたので、だいたい費用の元は取れているはず。

この本を頒布した。

f:id:kusano_k:20170810040428p:plain:w160

http://sanya.sweetduet.info/ksnctfc92/

申込み

申込みの締め切りが意外と早い。 8月の夏コミの締め切りは2月、12月の冬コミの締め切りは8月。 さらに、オンライン申込みであっても、申込書を物理で買う必要がある。 コミケの会場で買うのが一番楽で安い。 次回の申込書の通販の注文締め切りは明日の12時なので、今すぐ注文しないと間に合わない。

https://shop.circle.ms/CShopping/Detail/1364

必要なのは「申込書番号」と「オンライン申込用番号」なので、誰かに余ったものを譲ってもらうならば、とりあえずこの番号だけ送ってもらうという手がある。

申込み多数の場合は「抽選」と書かれているけれど、(たぶん)実際には選考なので、真面目に書く必要がある。サークルカットがつらい。

当落発表

今回の当落発表は6月9日だった。 ここから準備をすると余裕が無いとは思っていたけど、結局何もせず……。

コンテストサイトの製作

コンテストを開催して、その解説を頒布するという趣旨なので、コンテストサイトが必要。

ksnctfは技術的負債の塊で触りたくなかったので、新規にExpressで作った。 ソースコードGitHubに置いている。 Expressはシンプルで良かった。 多人数で大規模なサイトを作ろうとすると、自由度が高い分大変かもしれない。

CSSフレームワークBulma。 綺麗だし、必要なものは一通り揃っているしで、こっちも良い感じ。

本の製作

これが一番重要。 色々迷った末、markdownで書いてpandocでPDFに変換した。 コマンドはこれ。

pandoc 00_preface\preface.md -o 00_preface\preface.latex
pandoc \
  -B 00_preface\preface.latex \
  01_preliminary\preliminary.md \
 : \
  12_colophon\colophon.md \
  -o ksnctfc92.pdf \
  --latex-engine=lualatex \
  -V documentclass=ltjsbook \
  -V papersize=b5 \
  -V classoption=openany \
  -H header.latex \
  --top-level-division=chapter \
  --highlight-style monochrome \
  --toc \
  --toc-depth=2

pandoc -D latexで元になる.latexの中身が出てくる。 ちょこちょこLaTeXのコマンドを本文にも入れる必要が出てきてしまったので、次は全部LaTeXでも良いかもしれない。 あるいは「CSSではじめる同人製作」という本を買ってきたので、この本を参考にHTMLか。 なかなかこれといった方法が無くてつらい。

印刷は写真を元にPhotoshopでゴリゴリと。 フリーソフトとか安いソフトでも何とかなるかもしれないが、デファクトスタンダードなので、PhotoshopIllustratorがあると安心。 しかし高い……。

本の印刷はポプルスに頼んだ。 私が最初に買った同人誌が「空の境界」でポプルスが印刷したものだった。 懐かしい。

2週間前くらいに原稿ができていれば良いイメージ。

決済手段

決済手段を色々と用意した。

http://sanya.sweetduet.info/ksnctfc92/

クレジットカード

Squareのリーダーを買った。 5,000円。 1,000円くらいだった気がしたけど、それは磁気だけでICカード非対応のリーダー。 今は使えないらしい。 VISA、Mastercard、American Expressは登録したらすぐに使える。 その他のJCBなどは審査が必要。 審査まではしていないので、3ブランドだけ使用可。

pixiv PAY

https://pay.pixiv.net/ pixivがコミケの直前に出してきたアプリ。 今回のコミケでも対応しているサークルがたまにあった。 ギリギリだから雑な作りかと思ったら、良くできていて直感的に使える。 1回支払う側で使った。 QRコードを読み取ってボタンを押すだけなので、支払う側も楽。 どうしても現金のほうが速いから、列ができるようなサークルだと厳しいが、そうでもなければけっこう便利そう。

ビットコイン

breadwalletならスマホで受け取れる。 iOS版はApple Watchにも対応していて、Apple Watchの操作だけでQRコードを出して「ここに払ってくれ」とできる。 6承認どころか1承認を待つのも大変なので、ネットワークにトランザクションが流れたら本を渡してしまって良いと思う。 たしか、ビットコインのノードは二重支払いになるようなトランザクションが流れてくると、2個目のほうは拒否するようになっているのがデフォルトなので、実際に二重支払いをするのはなかなか大変そう。 トランザクションが溢れるようになったら、手数料0で支払って、ネットワークから消えるのを待つという手はあるかも。

モナーコイン

モナコインちゃんbotを使うのが一番手軽だと思う。 支払う人が事前にモナコインちゃんbotモナーコインを送っておく必要があるけれど、モナーコインを日常的に使っている人なら、たいてい預けているでしょう。

外貨

外貨が好きで、海外に行ったときにはなるべくコンプしようとしているので、外貨でもOKと書いておいた。

結果

クレジットカードを使った人が1人。 その他は0。 もっと言うと、1万円札、5千円札、500円玉もそれぞれ1人ずつ。 他は全員が千円札だった。 「お釣りを準備しておけ」という話を聞くので、千円札を50枚持っていったけれど、手を付ける必要が無かった。 クレジットカードを使った人がいたから、Squareリーダーが無駄にならなくて良かった。

同じように色々な支払い手段を用意している人が来て話したが、やはりクレジットカードを使う人がたまにいるくらいで、他は全く使われないらしいw (「買いに来てくれ」と言われたけど、結局行かなくてすみません)

当日

外に並んでいる人たちを横目に、すっと入れる。 開場前はビックサイトの人口密度が低い。 サークルチケットすごい。

PO.SU.TAというポスタースタンドの評判が良いので買おうと思ったけど、買おうとしたときにはもう遅かった。 次に出展するときには買おう。 コミケ側で1,000円で貸してくれるというので借りた。 これ↓の後ろのやつ。

ブックスタンドと値札は100円ショップで買った。 値札は良いのだけど、ブックスタンドがすぐに外れるので、見本誌を取ろうとした人が慌てて申し訳なかった。 民法第486条に金を払った人は領収書を請求できると書かれているので、いちおう領収書も買ったが、一切要求されなかった。

動画を流そうと思って、iPad miniを持っていった。 すぐに画面が消えるしループ再生はできないしで諦めた。 動画を普通に開いてもループの設定項目は無いが、アルバムを作ってスライドショーならループ再生できたらしい。 そんなの分かるか。

犯罪収益移転防止法の本人確認

明日のコミケの東た26-bでこの本を頒布します。

f:id:kusano_k:20170810040428p:plain:w320

http://sanya.sweetduet.info/ksnctfc92/

支払い方法として、モナーコインも受け付けることにした。 他人から支払ってもらう前に自分で試したかったけど、モナーコインは1 MONAも持っていないので、bitbankで買おうとした。 土曜日にアカウントを作って本人確認書類として免許証の写真を送った。月曜日にbitbankから住所確認用の書類が発送されて、火曜日に簡易書留受け取り、特にメールなどは来なかったけど水曜日には入出金ができるようになっていた。 休日を除けば最短。速い。

余所でこのような本人確認をしたときには、届いた書類に書かれていたコードをサイト上で入力するとか、本人限定受取郵便で配達員に身分証明書を確認されるとかだった。 bitbankから届いた書類を見ると、「口座開設申込みありがとう。1営業日程度で口座を有効化するからちょっと待ってね」と書かれているだけ。 おや?と思ったけど、なるほど、簡易書留なら差出人は受け取られたことが分かるから、受け取るだけで充分なのか。

と納得しかけたものの、これだと私以外の誰かが私の免許証のコピーで口座開設を申し込んで、私が書類を受け取って放置していたら私名義の口座ができてしまう。 本人確認のために免許証のコピーを渡すのは良くあることなので、「信用できない相手に免許証のコピーを渡すな」と言われても無理がある。 「ハンコを押して受け取った書類を放置するな」と言われれば、まあ、それはそうなのだが……。 架空請求では、公的機関から届いた書類以外は全て無視して連絡はするな、と言われている。 どのような書類なら無視して、どのような書類なら対応するべきなのかというのが、なかなか難しい。

そもそも、本人確認をするメリットがbitbankには無い。 金を貸すわけではなく、預かる側なので、誰がアカウントを作っていようが金さえ振り込んでくれるなら構わないはず。 なぜ、本人確認をしているのかというと法律で決まっているから。

改正犯罪による収益の移転防止に関する法律が4月1日に施行され(たはずだけど反映されていない……)、特定事業者に仮想通貨交換業者が含められた。 第四条で、特定事業者は顧客の住所などを確認することが義務づけされている。 確認の方法が犯罪による収益の移転防止に関する法律施行規則の第六条に定められていて、

ホ 当該顧客等又はその代表者等から当該顧客等の本人確認書類のうち次条第一号若しくは第四号に定めるもの又はその写しの送付を受けて(略)当該本人確認書類又はその写しに記載されている当該顧客等の住居に宛てて、取引関係文書を書留郵便等により、転送不要郵便物等として送付する方法

とのこと。 「取引関係文書」は上のほうに「預金通帳その他の当該顧客等との取引に係る文書(以下「取引関係文書」という。)」と書かれている。 預金通帳と並べられていることからして、それが無ければ取引ができないというレベルのものである必要がありそう。

ググっていたら、日本証券業協会Q&Aが出てきた。

55 「取引関係文書」の範囲

Q 施行規則第5条第1項第1号ロ等において規定されている「取引関係文書」には、例えば、口座開設のお礼状も含まれるのか。

A 口座開設のお礼状も「取引関係文書」に含まれる。

実はわりと何でも良いらしい。 協会が勝手にそんな判断もできないだろうから、お上に確認したんじゃなかろうか。

ということで、bitbankの方法でも問題は無いと。 bitbankのためでも顧客のためでもない法律なのだから、必要以上にbitbankが手間を掛ける義理は無い。 まあ、放置して勝手に口座を作られても、警察が家に来て「お前の口座で黒い金が動いていたぞ」と言われたときに「知らんがな」と言えば良いだけなので、特に不利益をこうむることも無いんじゃなかろうか。