ゆっくりMovieMaker4のFFmpegエンコードで出力した動画が再生できないときの対処法

manjubox.net

YMM4は、エンコード時にMediaFoundationとFFmpegが選べる。 音声の動画全体に占める割合は微々たるものだし、高音質なほうが良いかなと思って、320 kbpsが選べるFFmpegエンコードで出力している。

たまに、エラーも無く出力したのに再生できないことがある。

その度にエンコードし直すと時間が掛かってしまう。 再生できない原因と修復方法を調べたのでメモしておく。

MP4は複数の「box」で構成されている。 この内、mdat boxのサイズがなぜか間違って出力されるのが原因。

修復方法。

バイナリエディタを用意する。 私はHxDを使っているけれど、何でも良い。 選択した範囲のサイズを16進数で確認できる機能があると楽。

mh-nexus.de

「moov」という文字列の4バイト前にカーソルを持ってくる。 関係無いところがたまたまmoovになっているかもしれないので、周囲がスクリーンショットの雰囲気と似ているところ。 最後のほうにある。

先頭に戻り、「mdat」という文字列の4バイト前を、Shiftを押しながらクリック。 バイナリエディタが違って範囲選択の方法が違うなら、それに合わせて。 選択した部分の長さを確認。 HxDならステータスバーに出ている。 0x7F5473C9。

mdatの前の4バイトがmdatのサイズなので、このサイズに書き換え。 バイナリエディタによっては初期設定が上書きではなく挿入かもしれないので、Insertキーとかで切り替える。

保存して再生できたらOK。

あと、エンコード中にエラーで止まることもあった。 これはエンコード前にゆっくりMovieMaker4を再起動するようにしたら、見ることが無くなった。

さくらのVPSでディスクイメージを取得する方法

さくらのVPSで借りているサーバーを1台解約することにした。使わないから解約するのだけど、とはいえ、もしかするとまた必要になることがあるかもしれない。ファイルは全部保存しておくにしても、「動いていたサービスの様子を確認したいな」となったときのことを考えると、手元のVMで動かせるようにしておくのが一番である。

こういうときは、Mondo Rescueが定番で、方法を解説した記事もいっぱいある。でも、ちょいちょいハマリどころがある様子。自動化されているとはいえ、結局はファイルのコピーになるので、大量にファイルがあると重くないかとか、何かのバグで漏れたりしないかが心配。

せっかくの仮想サーバーなのだから、ストレージをそのままコピーしたい。が、その手段は用意されていない。「さくらのVPS ディスクイメージ」とかでググっていたら、下の記事が出てきた。クラウドに移行はできるらしい。

knowledge.sakura.ad.jp

途中に出てくるアーカイブをダウンロードすれば手元のVMでそのまま動かせるのでは? と試してみたら、上手くいったので紹介する。なお、お金が数百円掛かる。

上記の記事の通りにVPSディスクをソースとしてアーカイブを追加する。

f:id:kusano_k:20220314012324p:plain

適当に項目を入れて作成。20分~90分掛かるとのこと。200 GBなので、まあそんなものかと思ったら……。たしかに作成はすぐに終わる。20分も掛かっていなかったかもしれない。しかし、「作成」と「ダウンロード」の2段階あるらしく、この「ダウンロード」がとても遅い。なぜ? 数時間掛かっても終わらなくて、諦めて寝て、起きたら終わっていたくらいの感じ。

f:id:kusano_k:20220314012538p:plain

「コピー完了時点から課金開始」は良いのだけど、こっちは完了したらすぐダウンロードしたいのに、寝ていたら時間が空いてお金が掛かってしまう。

f:id:kusano_k:20220314012648p:plain

ダウンロードはSFTP。IDやパスワードなどが設定された curl のコマンドが出てくるので、それをそのままコンソールに貼り付ければ良い。ここのダウンロードは、少なくとも我が家の100 Mbpsの回線の速度はフルに出ていた。圧縮はされずストレージそのままなので、200 GBのストレージならそのまま200 GB。ダウンロードした後、サーバー側で消すのを忘れないように注意。

お値段はここVPSのストレージが200 GBだけど、100 GBの次が250 GBなので、250 GB分。11時間使って143円のところ、もう日額料金のほうが高いのでそれが適用され、137円。ダウンロードした後で消すのを忘れさえしなければ、たいした金額ではない。

ダウンロードしたファイルは生のディスクイメージ。VirtualBoxなら VBoxManage convertfromrawVM用のディスクに変換できる。

"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" convertfromraw archive.img archive.vdi

あとは適当にVMを作って、このディスクを割り当て、ネットワークの設定を変更したら動いた。ずっと公開鍵を使ってSSHログインしていて、パスワードが分からず、ネットワークが繋がらないからSSHも繋がらず……で手間取った。確認しておきましょう。

ストレージは200 GB中50GBくらい使っていた。ダウンロードしたイメージを圧縮して取っておこうと思ったら、bzip2で200 GBが150 GBくらいにしか縮まなかった。ダウンロード前に dd if=/dev/zero of=zero bs=4k で空き領域を0で埋めておけば良かったかも。

他の方法

悪いこと言わないからMondo Rescueを使ったほうが良い。Mondo Rescueも試して、試した感じとても出来が良い。多少ファイルが多いとかくらいでトラブる作りにはなっていなそう。Mondo Rescueのほうは(空き領域は含まず)残っているファイルだけをアーカイブして、圧縮もしてくれるので、16 GBくらいになった。ちょいちょいハマるという話も、途中のfstabの編集でパーティションのUUIDを/dev/sda?に書き換えるのと、他の途中で書き換えることになるファイルで/dev/vda?を/dev/sda?にしたら動いた。

そういえば、ディスクイメージをそのままコピーしたほうがこの作業無しで動いているのはなぜなのだろう? パーティションのUUIDがそのままコピーされて、UUIDで指定されていればUUIDのほうが優先されるのか?

ダウンロードできるファイルって結局は/dev/vdaそのものなので、100円払わなくても、ddとsshで転送をするという手もある。試してはいない。今どきのファイルシステムなら動いている途中でコピーしたところで壊れはしないだろう。たぶん。怖いならsingle userモードという手もある。途中にgzipなどを挟むこともできる。

Amazon S3のストレージ料金を無料にする裏技

追記: 無料にならなそう。後半を参照。

Amazon Simple Storage Service。 ファイル(オブジェクト)を保存したり、配信したりできるクラウドサービス。

料金は細かく設定されていて、リクエストや転送帯域に関しても課金される。 タイトルで「ストレージ料金」と言っているのは、それらを全部ひっくるめた料金ではなく、狭義の、オブジェクトを保存していることに対して毎月掛かる料金。 最も安いS3 Glacier Deep Archiveでも、0.002USD/GB/月(東京リージョン、2022年1月現在)掛かる。 一見とても安く思えるが、例えば100 TBを10年保存しようと思うと、24,576ドル、約300万円にもなってしまう。 オブジェクトを保存したり取り出したりするときに金が掛かるのは諦めるとして、この保存に掛かる料金を無料にしたい。

はい。

f:id:kusano_k:20220126231820p:plain

ファイルサイズが0バイトなので、お金は掛からないはずである。

f:id:kusano_k:20220126232039p:plain

元のファイルの中身を分割してファイル名にしている。 手元のストレージで同じ事をすると、一見ファイルサイズが0に見えて、inode(ext4)やMFT(NTFS)の容量を消費するから、当然無限に保存できるわけではない。

オブジェクトを1個保存するたびにリクエスト料金が掛かるため、多数のオブジェクトに分割することによって、最初の保存時の料金が高くなる。 何年保存すれば得になるのか、損益分岐点を計算してみる。

素直にファイルをそのままオブジェクトとして保存したときは、最安で0.002USD/GB/月。

キー名(オブジェクト名)は、最大UTF-8で1024バイト。

オブジェクトキー名の作成 - Amazon Simple Storage Service

UTF-8として無効なキー名も使えないかな?」と思ったけど、少なくともAWS CLIでは弾かれた。 バイト列をUTF-8として有効なUnicodeに変換したときにどの程度の詰め込めるかに興味はあるものの……まあ、Base64で良いだろ。 Base64ではサイズが4/3倍に増える。 (コントロールパネルからはディレクトリっぽく見えるけど)S3のキー名にディレクトリの概念は無い。 元のファイル名と連番の部分で32バイト程度は使うだろうか。 1個のオブジェクトのキー名に、(1024-32)/(4/3)で744バイト分のデータを詰め込める。

ファイルの取り出しのLISTは1000件まとめて取得できるので、無視できる。 PUTリクエストは0.0047USD/1,000回。 1回、1バイトあたり6.3172×10-9USD。 ストレージ料金の「GB」が10003なのか10243なのか分からない。 10243とすると、6.783USD/GB。 ということで、3,392月=283年以上保存するならば、S3 Glacier Deep Archiveに保存するよりも、ファイル名にエンコードしたほうがお得💰💰💰


当然「本当に無料になるの?」というのが気になる。 料金の算出の元になるであろうバケットの合計サイズは「バケットメトリクス」で確認でき、これは1日1回更新される。 1日以上経ったので確認してみた。 オブジェクト数717個で、バケットサイズ0バイトになっていれば良い。

f:id:kusano_k:20220129002439p:plain

727.7 KB。 ダメそう😢

なぜファイルサイズが0バイトなのにバケットのサイズが増えているんだという話だが……。

メタデータもストレージ使用量の課金対象

kurochan-note.hatenablog.jp

ここで挙げられているメタデータだけで、1ファイル1 KBにはならないだろうし、ファイル名もメタデータ扱いなのかなぁ。

CloudWatchだとバイト単位の正確な値が確認できて、745,129バイト。 キー名の合計は733,657文字。 キー名の分を除くと、11,472バイト。 オブジェクト1個あたりちょうど16バイト。 切りが良いので、計算は合っていそう。

この16バイトが何なのかは謎。 ETagなのか。 あるいは、(特に設定していない)Content-Typeは text/plain の10文字だったから、それと LastModified か何かで6バイトなのか。

SECCON CTF 2021作問者writeup+作問した感想

CTF Advent Calendar 2021 22日目の記事です。

前日の記事は、Xornetさんによる「SECCON CTF 2021 作問者Writeup + 運営参加記」でした。 CTF Advent Calendarを眺めていたら、Xornetさんの投稿予定を見つけて、「その手があったな」と私も登録した。 作問ではお世話になりました。

明日の記事は、Satokiさんによる「【文学】 Flagを読む」です。 え、Advent Calendarは何か気の利いたことを言いながらパスを回すっぽいけど、タイトルを見ても何も分からない……。 IMCTF、楽しかったです。 ありがとうございました。

SECCON CTFは解く側でだいたいいつも参加している。 誘ってもらって、今年は作問側に回った。 チームで作問するのは初めての経験だったし、問題の解法はそこそこに、裏話とかを書いていこうと思う。 詳細な解法は英語版のほうを見てほしい。 英語、APIのドキュメントなどはあまり困らないけど、ニュース記事や日記は読めない。 書くのも同じで、裏話みたいなのを英語のほうに書くのは諦めた。

なお、Discordでアナウンスされたように、SECCON CTF 2021のスコアサーバーと問題サーバーは少なくとも年内いっぱいは動いている予定。 まだ見ていなくて年末に時間がある人は挑戦してほしい。

score.azure.noc.seccon.jp

s/<script>//gi (misc, 115 teams solved)

英語版: s///gi (misc, 115 teams solved) - HackMD

危険な文字列を除去したいというときにやりがちなミスとして、削除処理を1回しかしないというものがある。 例えば、 <SCR<script>IPT> から <script> を1回削除すると <SCRIPT> になり、危険な文字列が残ってしまう。 「ちゃんと <script> が無くなるまで繰り返してください」という問題。 ただし、入力は64 MiB。

O(n2) では24時間のコンテスト中に終わらない(はず)。 入力をスタックに順番に入れていって、 <script> になったら取り出すということを繰り返す。 1回の削除が O(1) になり、全体が O(n) で、適当に書いても1分も掛からずに終わる。

data = open("flag.txt").read()[:-1]

stack = []
for c in data:
  stack += [c]

  if "".join(stack[-8:]).lower()=="<script>":
    for i in range(8):
      stack.pop()

print("".join(stack))

CTFのジャンルのひとつにPPC(Professional Programming and Coding)というのがあり、その問題。 「PPCを出題するのはどうなのか?」ということで揉めた。 申し訳ない。 「セキュリティ何も関係無いだろ」「競技プログラミングでやれ」的な。 「出すなら、せめてPPC脆弱性を探す問題ではない)と分かるようにするべき」ということで、この問題だけ作問者でもwarmupでもない「ppc」というタグが付いている。

競技プログラミングをやっている人(と一部のアルゴリズム研究者くらい)しか知らないアルゴリズムは色々とあるだろうけど、この問題はそうではないと思っている。 ただ、まあ、競技プログラミングをやっていない人でも頑張れば解けるくらいの難易度になっている一方で、やっている人には簡単すぎるよな……。

競技プログラミングPPCよりもCryptoのほうが近いのではないかという気がしている。 中国剰余定理とかナップサック問題とかどちらでも見る。 この競技プログラミングの問題は、最初の文章の解読だけできれば、CTFのCryptoを解いている人はあっさり解けそう。

atcoder.jp

極論、Cryptoも競技プログラミングも、愚直には指数時間掛かる処理を多項式時間に落とせば解ける。 Cryptoを解いている人が競技プログラミングに手を出したり、逆のことをしてみると、楽しいのではなかろうか。

qchecker (misc, 14 teams solved)

英語版: qchecker (misc, 14 teams solved) - HackMD

f:id:kusano_k:20211222054558p:plain

eval$uate=%w(a=%(eval$uate=%w(#{$uate})*"");Bftjarzs=b=->a{a.split(?+).map{|b|b.to_i(36)}};c=b["awyiv4fjfkuu2pkv+awyiv4f
v                  ut                  71                  6g                  3j                  +a                  x
c  e5e4pxrogszr3+5i0o  mfd5dm9xf9q7+axce5  e4khrz21ypr+5htqqi  9iasvmjri7+axcc76i  03zrn7gu7+cbt4  m8  xybr3cb27+1ge6  s
n  jex10w3si9+1k8vdb4  fzcys2yo0"];d,e,f,  g,h,i=b["0+0+zeexa  xq012eg+k2htkr1ola  j6+3cbp5mnkzll  t3  +2qpvamo605t7j  "
]  ;(j=eval(?A<<82<<7  1<<86)[0])&&d==0&&  (e+=1;k=2**64;l=->  (a,b){(a-j.ord)*25  6.pow(b-2,b)%b  };  f=l[f,k+13];g=  l
[                  g,                  k+  37];h=l[h,k+51];i=  l[i,k+81];j==?}&&(  d=e==32&&f+g+h  +i  ==0?2:1);a.sub  !
(/"0.*?"/,'"0'+[d  ,e  ,f,g,h,i].map{|x|x  .to_s(36)}*?+<<34)  );srand(f);k=b["7a  cw+jsjm+46d84"  ];  l=d==2?7:6;m=[  ?
#*(l*20)<<10]*11*  ""  ;l.times{|a|b=d==0  &&e!=0?rand(4):0;9  .times{|e|9.times{  |f|(c[k[d]/10*  *a  %10]>>(e*9+f)&  1
)!=0&&(g=f;h=e;b.  ti  mes{g,h=h,8-g};t=(  h*l+l+a)*20+h+g*2+  2;m[t]=m[t+1]=""<<  32)}}};a.sub!(  /B  .*?=/,"B=");n=  m
.                  co                  un                  t(                  ?#                  )-  a.length;a.sub  !
("B=","B#{(1..n).map{(rand(26)+97).chr}*""}=");o=0;m.length.times{|b|m[b]==?#&&o<a.length&&(m[b]=a[o];o+=1)};puts(m))*""

Quineで実装したフラグチェッカー。 フラグを1文字与えるごとに状態が変化していくので、CORRECTと表示されるようなフラグを見つければクリア。

上の色はwriteup用に画像処理ソフトで塗っている。 本当はカラーで出力するようにしたかったけれど、横幅120文字に詰め込むことができなくて諦めた。

Reversingでは、何かのVM用のバイトコードとか、謎アーキテクチャのバイナリを解析する問題を良く見るけれど、ソースコードを解析する問題があっても良いのでは? という主張。

状態を保存する変数を先頭ではなく中央あたりに持ってきたり、単純には差分が取れないように SECCON の文字を回転させたりはしているけれど、まあ、そんなに難しくはない。 結局、フラグを整数として見たときの剰余がある値になっているかどうかを調べているだけなので、ソースコードを解読したら、あとは中国剰余定理。

で、なぜこれがreversingではなくmiscなのかというと、レビューで「reversing要素が少ないからすぐに解けた」と指摘されたから。 たしかに、半分reversingで半分cryto+一発ネタ感はある。 同時に「簡単だけど、見た目のとっつきづらさと暗号要素もあるから、正解者数は伸びないかも」とも言っていて、実際にその通りですごい。

Vulnerabilities (web, 94 teams solved)

英語版: Vulnerabilities (web, 94 teams solved) - HackMD

f:id:kusano_k:20211222235528p:plain

Go製のウェブアプリ。

{
  "Name": "x",
  "name": "",
  "ID": 14
}

で解ける。

当初はWebの問題が少なく。Warmup枠が無かった(いや、正確にはCookie Spinnerが当初はwarmup枠だった。warmup……? 🤔)ので出題してみた。 最初は Name のチェックが無かったけど、「warmupとはいえ、こんなやるだけの問題はダメ」と言われた。 ボツは悲しいので、「何かもう一ひねりできないかな~」とGinやGORMのソースコードを漁ったが、Goはしっかりしていてつらかった。 JavaScriptなら、適当に違う型の値でも突っ込めば何とでもなりそうなのに。 静的型付け言語強い。 Gin(が中で使っているencoding/json)がなぜか大文字小文字を無視していたので、これで。 別に区別する必要性は無さそうなものだけど……JSONはキー名に小文字を使うことが多く、Goの構造体のフィールド名は先頭を大文字にしないといけないからだろうか。

フロント側が適当でも問題としては成立するけれど、やはり良い見た目にしたい。 最初はときどき撮っていた花の写真でも使おうかなと思ったけど、写真だけ撮っていて名前が分からないのでダメだった。 ふと脆弱性のロゴを見てみたらたいていCC0だったので、テーマを脆弱性にして、ありがたく使わせてもらった。 ちなみにデザインはPure CSS

https://purecss.io/layouts/blog/

sed programming (reversing, 14 teams solved)

英語版: sed programming (reversing, 14 teams solved) - HackMD

$ echo "SECCON{dummy}" | sed -f checker.sed
WRONG
#!/bin/sed -f

# Check flag format
# Some characters are used internally
/^SECCON{[02-9A-HJ-Z_a-km-z]*}$/!{
  cINVALID FORMAT
  b
}

:t
s/1Illl11IlIl1/1IlIl11Illl1/;tt
s/1Illl11III1/1III11Illl1/;tt
s/1Ill11IlIl1/1IlIl11Ill1/;tt
s/1Illl11l1/1l11Illl1/;tt
 :
s/o/1IlIl11IIll11IIll11IlIl11IIll11IIll11IIll11IIll1/;tt
s/O/1IlIl11IIll11IlIl11IlIl11IIll11IIll11IIll11IIll1/;tt
s/j/1IlIl11IIll11IIll11IlIl11IIll11IlIl11IIll11IlIl1/;tt
s/^/1IIll11IIll11IlIl11IIll11IIlI11l1/;tt

sedスクリプト

sedは単に正規表現で置換をするだけだと思っている人も多い……というか私もそう思っていて、「s/<script>//giにnaive解を付けたほうが良いかな? ワンライナーで書けないかな?」と調べていて、ちょっとしたプログラミング言語くらいの機能があることを知った。 info sed と打ってみると分量がすごい。

もっとも、 sed の機能はほとんど使っておらず、(空文字列を置換対象にできなかったので使った ^ 以外は)正規表現すら無しで、ひたすら置換を繰り返しているだけ。 マルコフアルゴリズム

マルコフアルゴリズムチューリング完全性をアピールしたいと思い、1次元セルオートマトンを実装している。 あるシステムAを使ってチューリング完全なシステムBをシミュレートできるならば、システムAはチューリング完全である。 入力を13世代進めて、特定の文字列になっていれば、正解。

作るのも面倒だったけど、解くのはもっと面倒だろうに、解いているチームはすごい。

constellations (reversing, 17 teams solved)

英語版: constellations (reversing, 17 teams solved) - HackMD

Goアプリの解析。 実行するとフラグが出てくるけど遅い。 高速化すればOK。

Goはネイティブコードに変換される。 綺麗に戻してくれる逆コンパイラは無いはず(あったら教えてほしい)。 Goのランタイムも入ってサイズは大きいけれど、シンボルは残しているので、 main.main から読んでいけば読めるはず。

……ということを改めて確認していたら、デバッグ情報としてソースコードが残っていることに気が付いた😩 GDB+PEDAで見ていると普通に表示されていた。 作問時に確認したときはコンソールを小さくでもしていたのだろうか?

追記: ソースコードは、バイナリの中にあるのではなく、単に私の手元にあるソースコードが表示されているだけだった。 良かった。

この問題だけフラグが妙に長いのは、フラグの各文字に対応する文字列をデバッガから1個ずつコピペするのではなく、そこも自動化してくれということである。 たとえフラグが普通の長さでも、結果的にはそのほうが早いと思う。

Average Calculator (pwnable, 56 teams solved)

英語版: Average Calculator (pwnable, 56 teams solved) - HackMD

良くあるスタックバッファオーバーフローpwnable。 ただし、スタックの各値が64bitのところ、32bitちょっとしか書き込めないので、そこを何とかしてくださいという問題。

ROPで scanf("%lld") を呼び出して、GOT overwriteをすれば良い。

平均を取っているのは、「オーバーフローを防ぐために32bit程度に制限しているんですよ」と言いたいだけであって、何の意味も無い。 何の意味も無かったのだけれど…… @moratorium08さんが、「これ、FullRELROでも解けない? i を書き換えて __libc_main のアドレスのところを飛ばすようにすれば、 sum__libc_main ±任意の値にできるから、それで system にして、あとはスタックを上手く調節すれば~」と言っていて、「平均にちゃんと意味が出てきた!」と色めき立った。まあ、冷静に考えると、__libc_main を書き換えないとROPができないので無理だったのだが。この手法では無いけれど、@moratorium08さんは結局FullRELROでも解いていたので、腕に自信のある人は挑戦してみてほしい。

感想

振り返ってみると、簡単な問題か変な問題しか作っていないな……。 作問をしてみて分かったけど、そのジャンルを極めるくらいじゃないと、良い問題は作れませんね。 「良い問題とは?」という話がそもそもあるけれど……後から「○○CTFの××の手法で解ける」みたいに語られるのは良い問題だろうか。 いつかはそういう問題を作りたい。

作問を引き受けた理由で一番大きいのは、どんな風に作問をするのか知りたかったから。

テンプレートに「 docker-compose up で問題サーバーが立ち上がるようになっていること」みたいなことが書かれていて、「は~、今どきはプルリクがマージされたら自動で問題サーバーが立ち上がるのかな。すげ~」と思っていたけれど、そこは作問者にサーバーが割り当てられて、手動だった😇 私の手元のTera Termでは、今も問題サーバーのログが流れている。 まあ、問題によっては自動化できない部分もあるだろうし、どの問題でも緊急対応をしないといけない可能性もあるし。

もっとも、作問者の手作業が発生するのはサーバーの管理くらいで、あとのソルバーによるチェックとか配布ファイルを固めるのとかは自動化(もしくはインフラチームの頑張り)されていた。 問題のレビューと合わせてそこら辺もチェックされるだろうから、CTFでときどき見かける「配布ファイル間違えてました。ごめんね」というミスも発生しづらそう。

SECCONのカンファレンスにCTFのインフラチームの発表もあって、ちょっと雰囲気が分かるかもしれない(事前に申し込んだ人だけの限定公開だったけど、なぜか公式Twitterがツイートしているので見られる)。 ↓の15分くらいから。

相互にレビューをするので、未公表の問題を何日も掛けてじっくりと解けるのも、人によっては嬉しい特権だろうか。 今回のCryptoはなぜかやたらとLLLが出題されていたけれど、そのおかげでLLLがだいぶ理解できた。 最初は非想定解法もけっこう残っているので、非想定解法を探すのが好きな人も楽しいかもしれない。

暗号通貨Qtumのoffline stakingについて

qtum.org

Qtumという暗号通貨を買った。この暗号通貨はProof of Stakeを採用している。マイニング報酬を得るために普通はクライアントをずっと立ち上げておかなければいけないのだけれど、Qtumはstakingを他人にdelegate(委任)することができる。

で、良く分からずにその辺の操作をしていたら、4,000円分くらい溶けた。悲劇を繰り返さないようにメモを書いておく。

Delegationを開始するには約1 QTUM、delegationを中止するには約0.02 QTUM掛かるので注意。

あと、delegationを受け付けるようにした。マイニング報酬のうち5%を私が受け取る。デフォルト設定だと最小100 QTUMからのところ、最小10 QTUMから受け付けるようにしている。よければ使ってください。ただ、delegateする前にこの記事を一通り読むことをおすすめします。

https://gist.github.com/kusano/c9be0020210b5dee82e2a6063293af26

Qtumとは

coin.z.com

日本語だとこれが一番詳しいだろうか。

BitcoinとEthereumのいいとこ取りというか、プログラムの作りとしては、Bitcoinがベースになっている。Bitcoinの送金の認証の部分は簡単なスクリプト言語になっている。ここに、コントラクトを作成する命令や、コントラクトを実行する命令を追加している。

一通り触ってみた感じ、とても出来が良い。ブロックチェーンエクスプローラーがしっかりしているのも良い。

qtum.info

Proof of Stake(PoS)とは

以前に書いた。

qiita.com

Bitcoinの電力消費は以前から話題になっている。それを解決するProof of ○○として記憶容量があるのだけど、それもちょうど今日話題になっていた(その用途なら新品よりも中古のHDDを買い漁りそうな気もするが……)。

pc.watch.impress.co.jp

取引の正当性を証明するのに何らかのリソースを使うのではなく、そのコインを持っていることのみを使うのは究極の解決策のような気がしてくる。

問題があるとしたら、電力なり記憶領域なりを利用して採掘されたわけではないコインに価値を感じるか?というところだろうか。100万円分の電力を消費して採掘されたコインなら100万円分の価値があるように思えるが、コインから生まれたコインに価値を感じるか?という。本当は順序が逆で、100万円分の価値があるから100万円分の電力が注ぎ込まれるのだろうけど。

あとは、仮想通貨全体に占める採用例はそれほど多くはないので、何か致命的な問題が残っているというリスクもあるかもしれない。

QtumのPoSのパラメタはシンプルで、1ブロックあたり4 QTUM(半減期で減っていく)。所持しているQTUMの量に応じた確率で採掘できる。ただし、取引が採掘に使えるのは取引が入っているブロックから500ブロック経過した後。採掘間隔は平均2分に調節されるので、16時間。この16時間があることによって、チェーンが分岐したときに双方がチェーンを伸ばしたがるということを防いでいるのかもしれない。

公式のチュートリアルでの推奨やクライアントの仕組みとして大きな取引を100 QTUMくらいの取引に分けるようになっているのは、この16時間があるから。採掘したら新規取引扱いになるので500ブロックは採掘できなくなる。金額が大きいと16時間がバカにならない。

今、状況を見てみたところ、採掘に参加しているコインの総量は2,000万QTUMくらいで、期待年利は4.6% 参加するコインの総量が増えれば年利は下がるし、減れば上がる。Whitepaperによると、発行数は1億QTUMで、最初に5,1000万QTUMが販売されたらしいから、こんなものか。

f:id:kusano_k:20210421012621p:plain

Offline staking

Super stakingとも呼ばれている。

PoSのもう一つの問題として、採掘するためにはクライアントを動かし続けないといけないことがある。まあ、誰かしらクライアントと動かしていないとネットワークが破綻するのでこれは良いのだけど。ただ、ネットワークに繋ぐということはホットウォレットということであり、そこに例えば1億円分の仮想通貨を入れておくのは怖い。逆に、1,000円分の仮想通貨のためにクライアントを動かし続けるのも面倒。金額が安ければ採掘しなかったことによる損も小さいのだが、損した気分にはなる。

そこで、Qtumでは誰かに採掘をdelegate(委任)することができる。これは面白い。あくまで自分の採掘をdelegateしているだけであって、マイニングプールとは異なる。AさんとBさんがCさんに採掘をdelegateして、CさんがAさんの取引を使って採掘に成功したとき、採掘報酬は(QTUMの所有者である)Aさんと(実際に採掘してくれた報酬として)Cさんで分け合うことになる。Bさんはもらえない。

最初は、「え? そんなことできるの?」と思ったけど、話は簡単。DelegateするAさんは事前に「報酬はx%でCさんにdelegateします」という情報を署名付きで流しておく、Cさんは「今この瞬間、Aさんの取引は採掘できる条件を満たしてますよね?」とブロックを作る。他の参加者は、Cさんが本当にAさんからdelegateされているかや、Cさんがx%以外の報酬を取っていたりしないかを見張る。詳しくは、

github.com

これを全てコントラクトで実装していたら、「なるほど、BitcoinとEthereumを合わせるとそんなこともできるのか」となってとても綺麗な話だったのだけど、そんなことはなかった。Delegateするところはコントラクトだが、採掘するところはBitcoinプロトコルがガッツリ書き換えられている。それならそれで、delegateするところも無理にコントラクトを使わなくて良いと思うんだよな……。

Delegateするコントラクトはこれ。6,064バイト。

github.com

これだけでかければ、delegateするのに1 QTUM分くらいのガス(コントラクトを実行する手数料)が掛かるか……と思ったけど、そうではなかった。

コントラクトのソースコードはこれ。わざわざ、ガスを2,000,000消費するようになっている。

https://github.com/qtumproject/offline-staking-contract/blob/bd7b160f574f3a34c4f12ca009477d90ddc01c54/offline-staking.sol#L66-L72

      // we need to make this function call expensive to avoid spam, so here we consume ~2M gas that will go to miners
        if(gasleft()<0x1E9480) revert("Not enough gas left to consume, the recommended gas limit to call this function is 2,250,000");
        uint gas=gasleft();
        while(true) {
            dummy=0x0;
            if(gas-gasleft()>=0x1E8480)break;
        }

で、それに気が付かずに、数回無駄にdelegateしたり解除したりしてお金を溶かしました。確認画面にはちゃんとガスの使用量上限とガスの料金は書かれていたから、計算すれば良かったのだけど。送金が0.001 QTUMくらいでできるのに、採掘のdelegateにそんなにコストが掛かるとは思わなかった……。

f:id:kusano_k:20210421015753p:plain

罠があって、初期設定だと、あるアドレスAをdelegateする → そのコントラクトを実行するのにアドレスAからQTUMが支払われる → 残金が別のアドレスBに送信される → アドレスAの残金が0になる ということが起こる。「Use change address」のチェックを外しましょう。そもそもなぜこんな設定があるかというと、店Aに支払ったときと店Bに支払ったときの送金元アドレスが同じだと、同じ人が支払ったと紐付けられてしまうというプライバシー的な問題があるから。それができてしまうようになるというリスクはある。

f:id:kusano_k:20210421021505p:plain

あるいは、「コインコントロール機能を有効化する」にチェックを入れると、送金時に下のようなダイアログを表示できて、自分に送金された取引の一覧から使用する取引を選んだり、残金の送金先を手動で設定したりできる。ちゃんと100 QTUMごとに分かれているかを確認するのにも使える。

f:id:kusano_k:20210421021948p:plain

Delegateしたアドレスの残高が0になってしまっても慌てる必要は無い。自分の持っている他のアドレスからdelegateしたアドレスに送金すれば良いだけ。送金手数料はdelegateする手数料よりとても安い。

Offline stakingに対する攻撃

stake-a-thon.qtum.org

Offline stakingを受け付けていて(掲載を申請した)アドレスの一覧が公開されている。これに載っている人に悪意があったり、あるいは載せることで悪意のある人に目を付けられたときに何が起こるか。公式ブログにも色々と書かれている。

blog.qtum.org

Offline stakingを受け付ける側に悪意がある場合。

  • DelegateされたQTUMを勝手使ってしまう
    • これはできない
    • これができないから、大金(大QTUM)をコールドウォレットに入れて、自分の管理する別のクライアントにdelegateするということができる
      • ハッキングでネットワークに繋いでいるクライアントに何をされても、損するのはそのクライアントに入れているお金だけ
  • 「Delegationを受け付けるよ」と言いながら、採掘しない
    • これはできる
    • 採掘できればdelegation報酬分を得するからそんなことはしないだろうと期待するしかない
    • エクスプローラーで採掘できたかどうかは見られるので、期待値より少なければ怪しい
    • 頑張ってスクリプトを書けば、「このブロックが採掘されたタイミングで先に私の取引で採掘できたはずでは?」と怪しむことはできるかもしれない
      • でも、PoSを採掘してみると分かるけど、手元で採掘に成功しても他の誰かに先を越されることは普通にある
    • 「あ、こいつ採掘していないな」と分かって他の人に乗り換えるのにも、1 QTUM掛かってしまう
    • 悪意というか、飽きて止めるというのが多そう
    • このリスクが大きいので、自分で採掘することにした
  • 掲載されているdelegation報酬が嘘
    • クライアントに設定した報酬より安いと採掘されない、高い場合はその報酬がそのまま支払われる
    • これもできる
    • そもそも、delegationを受け付ける側は特にコントラクトを実行したりはしないようで、外部からクライアントで最小報酬を何%にしているのか確認するすべが無い
    • 嘘を付いてもメリットは無いだろうと思うしかない

Offline stakingをdelegateする側に悪意がある場合。

  • 勝手にdelegateする
    • 上記の一覧に載っていなくても、エクスプローラーからOffline stakingをしているアドレスを探すことはできる
    • まあ、delegateされて損することは無いだろう
    • 自分用に最小報酬を0%にしている場合は、ちょっとイラっとする
      • ので、上記のFAQだとそういうときはwhitelistで運用しろと言っている
  • 最小報酬未満でdelegateする
    • そういう取引はクライアントが採掘しないはず
  • 大量にdelegationを送りつける
    • 負荷が高まりそう
      • 「この取引で採掘できるかな?」というのを毎秒試しているので
    • これを防ぐため、delegationのコントラクトはガスを大量に消費するし、Minimum UTXO Valueが設定できるようになっているのだろうか

Super stakingします

ということで自分で採掘することにしたので、ついでにdelegationも受け付けるようにした。

gist.github.com

私は未だにNEETCOIN(今でも参加しているのが700万NEET程度しかいないので、1,000 NEETくらいの手持ちで毎日何回も採掘できている)や、XPCoinも採掘しているから、しばらくは続けると思います。止めるときは、なるべく↑のGistに書くようにします。保証はしませんが。

デフォルトでは100 QTUM未満は受け付けないようになっているのだけど、ここを10 QTUMにしているので少額でもどうぞ。

ただ、ソースコードのこの部分がちょっと気になる。

            // Check the super staker min utxo value
            if(coinTxPrev.out.nValue < DEFAULT_STAKING_MIN_UTXO_VALUE)
                return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "stake-delegation-not-min-utxo", strprintf("CheckProofOfStake() : Stake for block at height %i do not have the minimum amount required for super staker", nHeight));

https://github.com/qtumproject/qtum/blob/cb6fefeab03bb88544660c37b39ddf7c94e7a278/src/pos.cpp#L240

DEFAULT_STAKING_MIN_UTXO_VALUE は100 QTUM。コメントやソースコードを追うに、これはdelegationを受け付ける私が100 QTUMを持っていれば良いと思うのだけど……。

開発者直々のコメントが付いていた。Delegateする側はいくら少なくても良いらしい。

JB395 commented 18 hours ago Qtum offline staking accepts delegated addresses with any quantity of QTUM.

あと、度々書いているようにdelegateするには約1 QTUM必要なので、小額でdelegateして得かどうかは良く考えてほしい。現在の年利は4.6% 2021年末に半減期で報酬が2 QTUMになる予定。

DISCO presentsディスカバリーチャンネル コードコンテスト2021参加記

www.discoverychannel.jp

略称DDCC2021。 これに参加して、こんな感じでパチンコをしてきた。

このコンテストは毎年こんな感じで実機を動かす問題。 画面の中ではなく物を動かすのは楽しそうだよなぁと思っていたので、参加できて満足。 まあ、順位はショボかったけど。

予選

例年はAtCoder上で開かれていたのに、今年は自社のサイト。 別にAtCoderが嫌いなわけではないけど、日本のプログラミングコンテストAtCoderに独占されてしまうのはそれはそれで不安なので、良い流れ。

そのせいか問題は(ABCを全完できるくらいの人にとっては)簡単。 私は開始45分くらいで全部解いた。

procon.disco.co.jp

「いくら問題が簡単とはいえ、強い人と弱い人の差は出るだろ? なぜお前が本戦に進めているの?」という話がある。 いつもは本戦に200人くらい集めていて、この本戦に行ったことはある。 ただし、実機を動かせるのは、本戦の中でのコードコンテストで上位の人だけ。 私は指をくわえて見ていた。 今年は感染症対策のために人数を絞り、コードコンテスト無しで全員が実機を動かせる。 就活年度の学生が20人、その他の人が10人。 私の実力ではまず無理な人数。

ひとつはAtCoderでの開催ではないので、気が付いていない人がそれなりにいたこと。 また、事前に申込みが締め切られるので、コンテスト開始直前に気が付いても遅い。 Twitterで「気が付かなかった」と嘆いている人がそれなりにいた。

もうひとつ、

Q: stdc++やboostなどのライブラリは使用可能ですか?

A: C/C++の標準ライブラリではないため、使用できません。

https://procon.disco.co.jp/backnumber/ddcc2021_qual/faq.html

というルールに引っ掛かった人が多かったのではないかという噂がある。 stdc++とは、gccに存在するbits/stdc++.hというヘッダファイルで、これを#includeすると全ての標準ヘッダが#includeされる。 普段から使っている人がそれなりにいるらしい。 懇親会でもあれば中の人に真相を訊いてみたかったけど、無かったので闇の中。

問題の解答とソースコードを合わせて提出する昔のGoogle Code Jamのような形式で、コンテスト中の正否は解答だけで判定されるので、コンテスト中には気が付けない。 昔のGoogle Code Jamは、たしか「なんなら手で解いても良いよ」で提出されたソースコードはチェックされていなかったと思うけど、DDCCは後からソースコードを動かしてたしかにその出力が出てくるのか確認しているのかもしれない。 まあ、リアルタイムにそれをやろうとすると大変だろうな。

本戦(問題以外)

当日に緊急事態が宣言されていたら、オンラインで後日にという話だった。 緊急事態宣言は終了したから無事開催。

正直、「よくやるなぁ。まあやるなら行くけど」と思っていた。 感染症対策はしっかりしていた。 まず、受付で付けてきたマスクを主催者が用意したものに交換。 「マスク着用必須です」は今なら良く見るけど、これだけでは参加者がどんなマスクを付けているのか分からないのか。 アルコール消毒。 エレベーターはスタッフ1人と参加者3人以下を厳守。 200人集めていたときと同じ広い会場に、参加者別にビニールテントが組み立てられていた。 机の上には、アルコールスプレーとフェイスシールドとゴミ袋(会場のゴミ箱は使うなということか)。 会場の自販機は使用禁止。

最後に実機で確認するときは参加者が周りに集まって観戦する。 ここでも実機の周りにビニールテントを置いていた。 あ、会場の隅にあったビニールテントは単なる予備ではなかったのか。 3グループに分かれての入れ替え制で、入れ替え時には消毒。

解散前には「オフ会しないでまっすぐ帰れ」と釘を刺していた。

本戦(問題)

f:id:kusano_k:20210328153729j:plain

問題は、冒頭の動画の通り。 射出機の角度に加えて、台の角度を動かせる。 カイジのような20トンの水は必要無い。 玉を打ち出しながら台の角度を変えることはできない。 プログラムは、(台の支柱3本それぞれの高さ、射出機の角度、玉を何発打つか、玉を打った後の待機時間)の列を出力する。 装置はこれを読んで、台と射出機の角度を変えてから、玉を打ち出す。 玉は全100個。 1個の穴あたり有効なのは10個までで、穴に入った有効な玉数×1個でも玉が入った穴の個数が得点。 最初に全動作をまとめて出力するので、穴に入った個数を見てから動きを変えるようなことはできない。

まずはシミュレーターで競う。 どのような動作をしたのかは分からず、点数だけが返ってくる。 これで正の点数を取った人だけが実機で動かせるとのこと。

穴の方向に射出機を向けるだけでしょ? → フォーマットエラー。 なぜかと思ったら、射出機の可動範囲を超えていた。 直接は狙えない穴もあるのか。 まあ、正の点数を取れば良く満点を取る必要は無いから、範囲を超えたやつはクリップしておくか → 正の点数ゲット。 ちなみに、この前半の点数はどうでも良いと思っていたが、最後の実機で同点だったらシミュレーターの点数で順位を付けるというルールがあり、これで最終的に順位がちょっと落ちた。

角度的に直接狙えない穴がある。 また、穴の位置をExcelでプロットすると、他の穴の後ろにある穴もある。

f:id:kusano_k:20210330012810p:plain

ここで台を傾けるのか。 なるほど。 手元でも玉の動きを物理的にシミュレートして、台と射出機の角度をランダムに決めて、狙った穴に入るかどうかを判定すれば良いだろう。 という実装をしている間に時間切れ。 ちなみに、シミュレートは0.001秒ごとに玉の位置と速度を更新するようにしたけど、台を左右にしか傾けないならば数値計算でわりと簡単に出せるっぽい。 他の参加者が言っていた。 たしかに。

シミュレーションの順位発表。 真ん中ちょい下くらい。 この時点で台を傾けるところまで実装している人も多いのか。 さすが。

会社紹介を聞いて、実機コンテストの問題発表。 だいたいそのままだけど、制限時間が短くなるらしい。 台や射出機の角度を10回変えると考えると、時間が足りない。 10個全部の穴を狙うのか、穴の数を絞って球数を増やすのかの駆け引きだろうか。 コーディング時間が短くて焦っていたからあまり自信は無いけど、狙う穴の数を絞る選択肢は無いよなぁ。 角度をランダムに決めて入るかどうかをシミュレートするやつ実装完了。 実機コンテストも提出の度に(運営の)シミュレーターのスコアが返ってくる。 想定しているスコアよりも低いのはなぜだ……。

実機での中間確認。 ああ、玉を打ち終わった後で次の角度の変更が行われるとはいえ、打った玉はまだ移動中で、そこで台を動かすと移動中の玉の狙いがズレてしまうのか。 玉を打った後の待機時間はそのためね。 でも、時間はギリギリだから、ただ待つくらいならとりあえず打っておいたほうが良いよなぁ。 運良くどこかの穴に入るかもしれないし。

コーディング2回目。 台を傾けなくても狙える穴のときに台を傾けるのは悪手なので、傾けないようにする。 角度ガチャだけだと一瞬で実行が終わる。 後は何をしよう。 実機には当然誤差があるはずである。 ギリギリだと入るかどうか怪しいよね。 ということで、狙った穴のサイズを小さいと思い、狙っていない穴のサイズは大きいと思って、なるべく中心を狙うようにしてみる。 本当は出た結果を微調整すると速いのだろうけど、それはやっていない。

終結果。 冒頭の動画のように、台を傾けないで狙える穴にはだいたい入ったけど、傾けて狙うものはほとんど入らずに終了。 悲しい。

他の参加者の話を聞くに、台の傾きをなるべく小さくするというのがポイントだったらしい。 1回目の確認のときに、「やっぱり傾けるとどうしてもズレるな。傾きを変えると移動中の玉が外れる以外にもこのデメリットがあるので、傾けなくてすむ穴は傾けないべきだな」とは考えていたけれど、0-1ではなくて、傾きが大きければ大きいほど誤差が大きくなるとまで考えるべきだった。

手前で曲がってしまっている。 解説の人が「転がり抵抗」がどうこうと言っていた。 台の素材は堅いゴムみたいな感じだったから、抵抗が大きくて速度が落ちるんだろうなぁ。

uwiさんが、傾きを固定するということをしていて頭が良いなと思った。 たしかに、ちょっと軌跡を曲げられれば何でも良いので、1種類の傾きで複数の穴が狙える。 射出機の角度を変えるだけでも一緒に台の角度を変えても使う時間は一緒だけれど、台を傾けなければ事前に打っている玉の軌跡が変わらないというメリットがある。

台の角度は3本の支柱の高さで変えるので、好きな向きにできる。 「左右は使うけど、前後方向の傾きを変える意味は無いよね」と思っていた。 奥の方を下げることで、玉を加速させて早く穴に入れ、次の角度変更の影響を小さくしている人がいた。 なるほど。

実機ならではだと思ったのが、隅に当てて穴に入れている人がいた。 この台の周囲には隙間があって台から外れた玉は落ちていくのだけど、台の隅だけは支えるため隙間が無い。 ここを狙って枠に反射させ、そのままでは狙えない穴を狙っていた。

「これ時間的に満点は無理ですよね?」「いや、射出機と台の角度を10回変えると無理なのであって、ギリギリを狙って2個の穴に入れられれば可能性はある」という話もあった。

「考えることが少なくてつまらなくない? 時間も限られているからしょうがないか」とか思っていたけど、本当は考えることはいくらでもあったわ。私の考えが足りないだけだった。リアルに物を動かしているのだから、そりゃそうか。面白い。

SoftEther VPNのL2TPにWindows 10の標準機能のL2TPで繋ぐときのエラー

ja.softether.org

SoftEther VPNクライアントを使えば良いのだけど、OSの標準機能だけですむならそのほうが良いよね。

f:id:kusano_k:20210328000101p:plain

リモートサーバーが応答しないため、使用するコンピューターとVPNサーバー間のネットワーク接続を確立できませんでした。これは使用するコンピューターとリモートサーバー間のネットワークデバイスファイアウォール、NAT、ルーターなど)の1つがVPN接続を許可するように構成されていないことが原因だと考えられます。管理者またはサービスプロバイダーに問い合わせて、どのデバイスが問題を引き起こしているのかを判定してください。

IPv6アドレスもIPv4アドレスも引けるドメインを指定していたのだが、これをIPv4アドレスでの指定にしたら繋がった。例示されている原因はどれも関係無かった。

他にハマりそうなところ。1個1個オフにして確認したわけではないので、関係無かったり、デフォルトの設定で問題無かったりするものもあるかも。

  • L2TPSoftEtherの本体とは別に有効無効が切り替えられるので、有効にする
  • L2TPSoftEtherの管理画面のポート一覧には出ていない、UDP 500とUDP 4500を使うので、開ける
  • 接続先のネットワーク上にDHCPサーバーが無いなら、「仮想NATおよび仮想DHCP機能(SecureNAT)」を有効にする
  • レジストリのHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PolicyAgentにAssumeUDPEncapsulationContextOnSendRuleという名前の32ビットDWORD値を作って値を2にする
  • 接続元のルーターの設定で「IPsecパススルー」を有効にする
  • コントロールパネルのネットワーク接続でVPNに対応する接続のプロパティ→セキュリティで認証を「次のプロトコルを許可する」にする

f:id:kusano_k:20210328001332p:plain