node-twitterでstreamAPIに再接続するときの実装

node-twitterを使用するとtweetはもちろんfilter/streamの取得も簡単に行えてテスト実行レベルでは何の問題もない。
ただ実際にBOTを稼働させてみるとたまにstreamが切断されて即時再接続を行おうとしてエラーコード420(速度制限)が返ってくる挙動がみられる。例えばアプリがクラッシュしたときにservice化などで自動再起動設定にしていて即時再接続を行うと、エラーコード420を延々繰り返してゾンビ化してしまう(しまった)。

解決策

twitterのドキュメントを調べてみると420が返ってきた場合(と他のエラーの場合)の再接続のベストプラクティスが示されているのでそれに従う。またstreamも一度にひとつだけ開くようにする。
  • Back off exponentially for HTTP 420 errors. Start with a 1 minute wait and double each attempt. Note that every HTTP 420 received increases the time you must wait until rate limiting will no longer will be in effect for your account.
HTTP 420 errorの場合インターバルを置き再々接続までの時間を最大値まで指数関数的に増やすようにと指示されている。(例:60秒,120秒,240秒,…3600秒)
node-twitterでは実装の例などは無いので自分で実装する必要があるが、issue/159にコードの例があり参考にできる。 https://github.com/desmondmorris/node-twitter/issues/159
ただしここにおけるstream destroy()の問題は解決しているようだしsetTimeout()もPromiseでラップして書きたいので書き直してみた。

実装

‘use strict’;
const Twitter = require(‘twitter’);
  const client = new Twitter({
    consumer_key:process.env.TWITTER_CONSUMER_KEY,
    consumer_secret:process.env.TWITTER_CONSUMER_SECRET,
    access_token_key:process.env.TWITTER_ACCESS_TOKEN_KEY,
    access_token_secret:process.env.TWITTER_ACCESS_TOKEN_SECRET
});
var timeintervalsec = 1;
var exponent = 0;
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
// インターバルを60* 2^0SEC,60*2^1SEC,60*2^2SEC…としてmain taskを実行する実装
function main() {
    timeintervalsec=60*Math.pow(2, exponent);
    wait(timeintervalsec*1000)
        .then(() => {
            letdate=newDate();
            console.log(date+’ Reconnecting… Interval Time = ‘+timeintervalsec+” sec”)
            varstream=client.stream(‘statuses/filter’, { track:’something’ });
            stream.on(‘data’, function (event) {
                exponent=0; //初期化
                console.log(‘Maintask’);
            });
            stream.on(‘error’, function (error) {
                if (exponent>6) { // exponentが6以上つまり 2^12=3840 > 3600sec=1hourの場合処理を終了する
                    console.log(‘再接続間隔が1時間以上となったため終了’);
                    throwerror;
                } else if (error.message == ‘Status Code: 420’) { // 420の場合はインターバルを増やして再接続する
                    stream.destroy(); //多重起動を防止するため
                    exponent++;
                    main();
                } else {
                    console.log(‘420以外のエラーによる終了’);
                    throwerror;
                }
            });
        })
        .catch();
};
main();
(recconect.js)

議論

  • コードは420の場合のみなので他のエラーの場合も実装してもいいかもしれないが、素直にクラッシュさせたほうがいいと思ってこのまま使用していたりする。
  • twitterから420が返ってこないとテストできない。そこで邪道かもしれないがソースコードの420のところを401にしてKEYを一部変更して起動するとtwitterから401 errorが返ってくるので指数関数的に間隔をあけて、けなげに再接続を試みているのを観察できる。わざと420を起こすのも迷惑だしどうするのが一番いいかな?

参考

作成したBOT。twitterで指定のキーワードをつぶやくと仮想通貨を投げてくれる。
ソースコード
似たような問題に対処してる方

Ubuntu16.04を18.04にアップデートした際のトラブルと対処したこと

ubuntu16.04LTSを使用していたのですが、やっと安定版の18.04LTSが正式リリースされたのでアップデートしました。ターミナルから18.04に無事アップデートできたので、指示どおり再起動したところ、トラブルが発生。

アカウントの選択とパスワードの入力までは可能なのですが、パスワードを入力してログインしようとした瞬間にフリーズするという不具合でした。

まあよくあるよねと思いつつ、なんとか自力で立ち上げられるところまで来たのでメモ。

条件

・ubuntu16.04LTSからubuntu17.10にアップデートしてすぐにubuntu18.04LTSにアップデートしたときに発生。

・win10とubuntuのディアルブート環境

マシンの構成がprocessor Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
GPU [GeForce GTX 1050 Mobile] だったので、GPUのドライバ辺りだろうなあと思い結果的にNIVIDIAのドライバからubuntuの標準のドライバに戻したりいろいろしたら入れるようになった。

試したこと

以前のカーネルのバージョンで起動してみる。

Advanced options for ubuntuからインストールされている古いカーネルのバージョンで起動できる。以前これで行けたので試してみたが起動せず。

recovery modeで起動

現時点で最新のバージョンのリカバリーモード4.15.0-20-generic(recovery mode)で起動した。

リカバリーモードには入れたのでdpkgとgrubでパッケージとブートローダーをアップデート。

resumeで低解像度モードで起動。入れたのでとりあえずターミナルから、

$sudo apt-get update

$sudo apt-get upgrade

その後「ソフトウェアとアップデート」からGPUのドライバをubuntuの標準のやつに戻しました。

その後また低解像度モードでログインしてドライバを確認した画像↓

この後再起動すると通常のモードでログインできました。

いろいろやったので何が原因なのかはっきりわからなかったんですが、多分NIVIDIAのGPUドライバだと思います(偏見)。

ダウングレードするハメにならなくてよかったです。

終わり。

みなりん*氏によるcoincheckクラッカーへのマーキングはなにをやっているのか

 みなりん*氏によるcoincheckクラッカーへのマーキング行為がJK17というバズワードとともに界隈で話題になっている。即応性と根気強さにおいてみなりん*氏を賞賛したい。すごい。

 しかし、実は行っていること自体は全てNEMの標準機能を使用しており、しっかり準備すれば誰にでも行えることであるのも確かである。その手軽さもNEMの特徴と言える。みなりん*氏が犯人のアドレスを追跡しマーキングを行った方法を、NEMの機能の説明を交えながら解説する。

 特にビットコインなどに詳しい面々もNEMの実際の機能などについては意外と知らなかったりすることがわかったので整理していおきたい。

 

・coincheckから盗まれたXEMがどこに行ったのかを特定する

 匿名性の無い暗号通貨は取引の様子を誰でも確認することができる。coincheckのNEMアドレスがわかればクラッカーのアドレスを特定するのは容易だ。例えば以下のNEMBEX(NEM Block Chain Explorer)で表示されたアカウントがcoincheckのアカウントである。

http://explorer.ournem.com/#/s_account?account=NC3BI3DNMR2PGEOOMP2NKXQGSAKMS7GYRKVA5CSZ

何を見ればいいのかわからない人はスクロールして2018-01-26 00:02:13以降のトランザクションを確認してみよう。

・NEMのモザイクとは

 今回マーキングに使用されたNEMのモザイクとは、NEMネットワーク上で独自のトークンを作成できる機能である。トークンというのは「ポイントカードのポイント」や「地域通貨」、もっと身近な例では「肩叩き券」のようなものであり、通貨の域には達していないが、ある限定された範囲で使用できる通貨の「ようなもの」である。NEMのネットワークに手数料を払えば誰でも自由にモザイクと呼ばれるトークンを発行することができる。

 NEMのモザイクの作成を行う際には、「名前」、「説明」、「発行数」、「発行数のロックの有無」の他に「徴収」を設定できる。この徴収の設定が今回のマーキングに使用されたモザイクの肝である。

 

・徴収とは

 徴収とはモザイクを送金する時に送信者が支払う手数料のことだ。送信手数料と言ったほうが通りがいいが、モザイクを作成する人が送信手数料を「徴収」することができるという機能なので、こういう言い方になる。

 NEMのモザイクを作成すると、徴収の量と種類、送り先を設定することができる。もちろん設定しなくても良い。この機能により、頻繁に独自トークンが取引されるようなサービスの収益化を行うなどの利用が想定されている。

 ところでモザイクの徴収の対象は「XEM」というNEMネットワークの基軸通貨だけでなく、自由に「モザイク」を設定できる。

NEMの公式クライアントであるNANOWalletでのモザイク作成画面
NEMの公式クライアントであるNANOWalletでのモザイク作成画面。徴収するモザイクをXEM以外にも自由に設定できる。

 

・みなりん*氏のマーキング=アドレスから剥がせないモザイクとは?

 別に直接確認したわけではないのでみなりん*氏の手法と細かい部分に違いがある可能性があるが、特定のアドレスに送りつけて剥がせなくするモザイクの作り方を説明する。

 まず2種類のモザイクを発行する。

 1つは発行者のみが所有し、他の誰にも送金していないモザイクである。これをAモザイクとする。次に発行するモザイクは徴収にAモザイクを設定したモザイクである。これが今回犯人のアドレスに送られマーキングに使用されたモザイクだ。これをBモザイクとする。

 犯人にBモザイクを送信すると手数料としてAモザイクが徴収される。徴収先を自分にしておけば、自分で支払い自分で受け取るのでAモザイクの残高は結果的に変化しない。一方、犯人はAモザイクを持っていないので送られたBモザイクを誰にも送信することができない。手数料不足で不正なトランザクションとして弾かれてしまう。そもそもAモザイクは発行者が自分のアドレスに全て保有しているため、どこかでAモザイクを手に入れることも不可能である。

 犯人が盗みだしたXEMを別のアカウントに移すたびに、常時監視しているみなりん*氏がここで言う「Bモザイク」を犯人の新しいアドレスに送りつけている。そのため他の人間が、あるアドレスについてクラッカーのものかどうかを確認したかったらBモザイクの有無を確認するだけでいい。

 もちろん自分でcoincheckのアドレスから盗まれたXEMの流れを調べてアドレスを確認することも可能だが、犯人が大量のアドレスを経由してXEMを送金し撹乱した場合のことを考えると、みなりん*氏がやっていることは非常に効果的であると言える。

 これにより取引所やXEMを扱うお店などで犯人が盗みだしたXEMを使用するのは非常に困難になった。

・間違えてマーキングしてしまったら?

 間違った人にBモザイクを送ってしまったり、犯人が反省してcoincheckにXEMを返却した場合、モザイクを剥がす必要が出てくるかもしれない。その場合手数料分のAモザイクをそのアドレスに送ってあげれば、みなりん*氏にBモザイクを送り返すことができる(ちなみに譲渡許可という機能があり、みなりん*氏以外に送り返せないようにモザイクを設定することも可能。おそらく実施している。)

・相手のNEMアドレスが犯人のものかを確認する方法

 現在犯人は一般人のアドレスに無作為(?)にXEMを送っているようだ。NEMを使用している人は身に覚えのないXEMが知らない誰かから送られて来た時に、それがcoincheckハッキング犯のものなのか、ただの善意の匿名寄付なのか確認する必要がある。

 例えばその送金元が、「NDDZVF32WB3LWRNG3IVGHCOCAZWENCNRGEZJVCJI」というアドレスだった場合。それを確認する一番早い方法はNEMBEXで検索することだ。

 

http://chain.nem.ninja/#/blocks/0

http://chain.nem.ninja/#/search/NDDZVF32WB3LWRNG3IVGHCOCAZWENCNRGEZJVCJI

画面中程に所有するモザイク一覧が表示される。そこにみなりん*氏からのメッセージが読み取れるだろう。

 

すなわち、これがクラッカーのアドレスであることがわかる。

 もし、このようなことがあった場合は、クラッカーから送られた金額分のXEMは保持したままにし、NEM財団やcoincheck側の指示が発表されるのを待つようにとアナウンスされている。監視は行われているものの、普通のアカウントにXEMが送られた場合にみなりん*氏のマーキングが行われるということは無いようなので安心して欲しい。