Things of interesting

プログラミングに関する技術ネタの備忘録

仮想通貨をマイニングする その1

やること

仮想通貨をマイニングする

準備

マイニングマシン(マイニングリグ)の構築

色々なサイトを参考に以下のパーツで組んでみました。

組み立て

部品が届きました。
f:id:hnakanoya:20180320121937j:plain

フレームを組み立てて、マザーボード→CPU→メモリ→電源→GPUの順に繋いで完成。 f:id:hnakanoya:20180320122108j:plain

OSはここでダウンロード版を購入したのでUSBメモリを使って起動USBを作成します。 手順は以下を参考にします。

ethOS Mining OS

MacでethOSのイメージをUSBメモリに焼く - Qiita

  • Webで購入するとメールでダウンロードコードとリンクが送られてくるのでOSをDownloadします。回線が細いのか1時間くらいかかりました。
  • ダウンロードしたethos-*.xzファイルを解凍します。
  • diskutil でディスクの番号を確認してddコマンドで焼きます、かなり時間かかりました。(1日置いてみてみたら終わってました。)

ethOS起動

  • ※モニタはGPUではなくてマザボに挿してください
  • USBをマザボに挿して、マウスとキーボードつけて電源を入れます
  • 勝手にUSBからのOS起動になりマイニングが始まるはずです f:id:hnakanoya:20180320124226j:plain ※写真はマイニングアドレス等設定後のものです

GTX1080を1枚の構成で211wくらいのようです。あと2枚追加予定ですがこれなら大丈夫かな・・? f:id:hnakanoya:20180320143358j:plain

設定ファイルの更新

設定ファイルはホームディレクトリ直下のlocal.confにあるのでこれを編集します。(左下のメニューからedit local.confでもいけます。)

バックアップとって編集します。

$ cp -p local.conf local.conf.bk
$ vim local.conf

以下の内容でファイルの先頭にあるデフォルト設定を上書きます。

今回はZcashをマイニングしてみます。ethOSでは Ethereum(ETH)、Monero(XMR)、Zcash(ZEC)がマイニングできるようです。

globalminer ewbf-zcash
maxgputemp 85
stratumproxy enabled
proxywallet 自分のZcashのアドレス
proxypool1 asia1-zcash.flypool.org:3333
proxypool2 cn1-zcash.flypool.org:3333
globalfan 85

globalminer は実際にマイニングを行うソフトでethOSに入っている中から選択します。 以下説明でグラボにGTXシリーズを使う予定なのでewbf-zcashにしてみました。

# globalminer:       set global miner (ethminer sgminer-gm claymore claymore-zcash optiminer-zcash sgminer-gm-xmr cgminer-skein ewbf-zcash dstm-zcash ccminer)  
#           NOTE: ewbf-zcash, dstm-zcash and ccminer (xmr, and others) is for nvidia; for ccminer flags, see http://ethosdistro.com/ccminer.txt )

maxgputempはGPUの温度設定でこれを超えるとマイニングが止まるらしいです。デフォルト値にしておきます。

proxywallet に設定するZcashのアドレスは事前に取得しておきます。jaxxというウォレットを使いました。 アドレスをコピペするにはローカルのMacからethOSにSSHでログインすると簡単です。

$ ethos@{画面に表示されるIPアドレス}
$ live (デフォルトパスワード)

proxypool1 にはマイニングプールのサーバーを設定します。今回はflypoolというマイニングプールを使うことにしたのでそのアドレスをセットします。

※ethOSではリモートコンフィグというネットワークを通じてlocal.confを同期する機能がデフォルトONになっているのでこれをOFFにします。

$ force-local

再起動します。

$ r

再起動後に設定を確認して元の設定にロールバックしていなければOKです。

マイニング収益の確認

マイニングプールから利益が分配されるのに少し時間がかかるのでマイニングの状況を知りたい場合はマイニングプールのWebサイトで見れます。

https://zcash.flypool.org/

左上のアドレスに自分のアドレスを入れるとマイニング状況が見れます。

ethOSのメニューの一番上から見れる ethOS stats panel の結果とあっていればOKです。flypoolだと0.01zecごとに収益が送金されます。

ハッシュレート

GTX1080 1枚だと500H/sくらいのレートが画面には表示されていますが、3日間の平均では480H/sくらいに落ち着いていました。

しばらく稼働させたらマイニング結果を報告します!

GCP+Mailgun+Railsでメール送信を行う

やること

Railsで作ったアプリからのメール送信をGCPGoogle Cloud platform)+ Mailgunの環境で行えるようにする

GCPへのMailgun導入

公式のドキュメントがあるので以下を参考に導入してきます。

Mailgun でのメールの送信  |  Compute Engine ドキュメント  |  Google Cloud Platform

Mailgunアカウント作成

↑のリンクからアカウントを作成するとSandbox(テスト用)ドメインが初期状態で存在していますが本番で使いたいので新規作成します。
Domains → Add New Domain と進んでドメイン名を入力します。
入力するドメインは保持しているドメインサブドメインを使います。
(hatena.comの場合はmail.hatena.comとかにする)

GCPへの設定追加

Mailgunドメイン作成後にDNS設定内容が画面に表示されるのでこれをGCPDNS設定画面に追加していきます。 f:id:hnakanoya:20180124153617p:plain

以下のDNSレコードを追加します。
TXTレコード2つ(空白が含まれるのでダブルコーテーションで囲って入力)
MXレコード1つ(10 mxa.mailgun.org.と10 mxb.mailgun.org.の2つの値を入力)
CNAMEレコード1つ

設定が完了したらMailgunのDomainsメニューで設定の確認を行います。(DNSの伝播に少し時間がかかるので2,3分空けてください)

Check DNS Records Now を押して全てグリーンになればOKです。 f:id:hnakanoya:20180124154319p:plain

Railsの設定

Gemfile

gem 'mailgun_rails'

production.rb

config.action_mailer.delivery_method = :mailgun
config.action_mailer.mailgun_settings = {
  api_key: 'mailgunのDomains→API Keyの項目をコピー',
  domain: 'mailgunのDomains→一番上のDOMAINをコピー'
}

メール送信をアプリから実行してみて正常に受信できれば完了です!

VSCodeのVueプラグイン(Vetur)でtemplateタグがLintエラーになる(修正済み)

Vueのソースコードを書くのにVeturというプラグインシンタックスハイライト&Lintを付けていますがv0.11.4でtemplateタグのLint仕様が変更になったようです。

Veturのバリデーションを無効にするか、lang=htmlを付けることで治りますLintの対象外にできます。

Setting.json

{
    "vetur.validation.template": false,
}
<template><template lang="html">

GithubのIssueにも上がっていました。

[vue-language-server] '<template>' should have end tag. · Issue #578 · vuejs/vetur · GitHub

追記

本日(2017/12/15)リリースされたv0.11.5で修正されました!早い!

Java(SpringBoot)でbitcoindのJSON-RPCを利用する

やること

bitcoindはJSON形式でブロックやトランザクションの情報を取得できるJSON-RPCという通信プロトコルをサポートしています。
今回はSpringBootで作成したプロジェクト上で、bitcoind JSON-RPCを呼び出すサンプル実装を行います。

事前準備

SPRING INITIALIZR を使ってテンプレートプロジェクトを作成します。

以下の環境で作業を行います。

  • Java9
  • SpringBoot2.0.0M6
  • jsonrpc4j

実装してみる

jsonrpc4j(Java向けJSON-RPCライブラリ)

JavaJSON-RPCを利用するのにJava用ライブラリであるjsonrpc4jを使用します。
build.gradleの依存関係に追加します。
build.gradle

dependencies {
(Spring関連は省略)
    compile group: 'com.github.briandilley.jsonrpc4j', name: 'jsonrpc4j', version: '1.5.1'
}

Configクラスの作成

JSON-RPC Clientが呼び出すURLやユーザー・パスワードの設定を行います。
Server側となるbitcoindのconf設定は以下の記事と同じ前提です。

hnak.hatenablog.jp

以下のクラスを作成して適当なディレクトリに配置します。
BitcoindConfig.java

@Configuration
public class BitcoindConfig {
    private static final String endpoint = "http://localhost:18332";
    private static final String rpcuser ="username";
    private static final String rpcpassword ="password";
   
    public BitcoindConfig() {
        Authenticator.setDefault(new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication (rpcuser, rpcpassword.toCharArray());
            }
        });
    }

    @Bean
    public JsonRpcHttpClient jsonRpcHttpClient() {
        URL url = null;
        //You can add authentication headers etc to this map
        Map<String, String> map = new HashMap<>();
        try {
            url = new URL(endpoint);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new JsonRpcHttpClient(url, map);
    }

    @Bean
    public BitcoindClientAPI bitcoindClientAPI(JsonRpcHttpClient jsonRpcHttpClient) {
        return ProxyUtil.createClientProxy(getClass().getClassLoader(), BitcoindClientAPI.class, jsonRpcHttpClient);
    }
}

testnet,regtestのデフォルトポートは18332、本番環境は8332です。

JSON-RPC用のインターフェースクラスの作成

次にJSON-RPC用のインターフェースクラスを作成します。
@JsonRpcMethodアノテーションマッピングするAPIを指定することでJSON-RPCを呼び出すことができます。
呼び出したいAPIは以下を参考に引数、返り値を合わせます。

Chain Query: Bitcoin API:

今回は getblock APIを呼び出してみるのでblockhashとverbosityを引数に設定して、返り値はverbosity=1の想定でAPI Resultに合わせたPOJOを定義しておきます。

BitcoindClientAPI.java

public interface BitcoindClientAPI {

    @JsonRpcMethod("getblock")
    GetBlockResult getBlock(@JsonRpcParam(value = "blockhash") String blockhash, @JsonRpcParam(value = "verbosity") int verbosity);

}

GetBlockResult.java

@Getter
@Setter
public class GetBlockResult {
    private String hash;
    private int confirmations;
    private int size;
    private int strippedsize;
    private int weight;
    private int height;
    private int version;
    private String versionHex;
    private String merkleroot;
    private List<String> tx;
    private int time;
    private int mediantime;
    private int nonce;
    private String bits;
    private int difficulty;
    private String chainwork;
    private String previousblockhash;
    private String nextblockhash;
}

呼び出し元コントローラーの作成

@RestController
@RequestMapping("bitcoind")
public class BitcoindRpcController {

    @Autowired
    private BitcoindClientAPI bitcoindClientAPI;

    @RequestMapping(value = "block/{blockhash}", method = RequestMethod.GET)
    public GetBlockResult getBlock(@PathVariable("blockhash") String blockhash) {
        return bitcoindClientAPI.getBlock(blockhash, 1); // verbosity = 1
    }
}

動作確認

./gradlew bootRunでプロセスを起動し、以下のURLを叩いてブロックの情報が取得できればOKです。 http://localhost:8080/bitcoind/block/{blockhash}

※{blockhash} の部分にはbitcoin-cliのgenerateコマンドで実際に産出されたブロックのHash値を指定してください。

Spring Webfluxで例外ハンドリングを行う

やること

SpringWebflux導入にあたって例外ハンドリングの方法を調べて実装します。
SpringMVCだとControllerAdvice+ResponseEntityExceptionHandlerを使って共通の例外ハンドリングとかを実装していると思いますが、これらが使えなくなるので替わりの方法を探します。

WebfluxのAPI実装について

このブログの内容を参考にさせていただきました。
はじめてのSpring WebFlux (その1 - Spring WebFluxを試す) - BLOG.IK.AM

Webfluxでも@Controller使った実装が使えますが、他にRouter Functionsというモデルを使って実装できるようになっています。
今回、例外ハンドリングを実装するにあたってRouter FunctionsのFilter機能を使っているので、主にRouter Functionsを使った実装になっています。
@Controller使った共通的な例外ハンドリングの仕組みはみつからなかったので今回は言及しません。

事前準備

SPRING INITIALIZR を使ってテンプレートプロジェクトを作成します。
SpringBootは2.0系を選択して、ReactiveWebをDependenciesに入れておきます。

以下の環境で作業を行います。

  • Java9
  • SpringBoot2.0.0M6

Router Functionsクラスの実装

ハンドラクラスの作成

/parent/child1のGETメソッドと/parent/child2へのPOSTメソッドの2つのAPIを定義したハンドラクラスを作成します。
nest()を利用することでパスのネストを定義することができます。
コメントアウトしていますが、GETなどでクエリからパラメータを取得する場合はqueryParam()で取得できます。またPOSTなどでBodyからパラメータを取得する場合はbodyToMonoで任意のMonoクラスに変換できます。
GetSampleResult、PostSampleRequest は適当なPOJO、WebfluxServiceも適当なダミーのServiceクラスです。

WebfluxHandler.java

@Component
public class WebfluxHandler {

    @Autowired
    private WebfluxService webfluxService;

    public RouterFunction<ServerResponse> routes() {
        return nest(path("/parent"),
                route(GET("/child1"), this::getSmple)
                        .andRoute(POST("/child2"), this::postSample)
        );
    }

    private Mono<ServerResponse> getSample(ServerRequest req) {
        // int a = Integer.valueOf(req.queryParam("aaa").orElse("1"));
        // String b = req.queryParam("bbb").orElse("defaultValue");
        // return ok().body(Flux.just(webfluxService.getSample(a, b)), GetSampleResult.class);
        return ok().build();
    }

    private Mono<ServerResponse> postSample(ServerRequest req) {
        // PostSampleRequest param = req.bodyToMono(PostSampleRequest.class).block();
        // webfluxService.postSample(param.getName, param.getValue());
        return ok().build();
    }
}

WebfluxのConfigクラスでfilterを定義

先ほど作成したハンドラを呼び出すことでルーティング設定が有効になります。
併せて.filter()を呼び出すことでException発生時の挙動を定義しています。
routes().andRoute()を呼び出すことで複数ハンドラに対してfilterを設定することもできます。 WebfluxRoutesConfig.java

@Configuration
public class WebfluxRoutesConfig {
    @Bean
    public RouterFunction<ServerResponse> routes(WebfluxHandler webfluxHandler ) {
        return webfluxHandler.routes().filter((request, next) -> {
            try {
                return next.handle(request);
            } catch (Exception e) {
                log.error("An error occured", e);
                return ServerResponse.badRequest().build();
            }
        });
    }
}

動作確認

/gradlew bootRunでプロセスを起動して、上記のパスへのHTTPリクエストに対して期待した応答が返ればOKです。
内容確認したい場合はコメントアウトを外して任意のメッセージを返したりExceptionを発生させたりしてみてください。

Spring WebfluxのWebsocketを使ってサーバー間通信

やること

Webfluxを導入してみたので、既存のspring-bootに入っているwebsocketからwebfluxのサポートするwebsocket実装に切り替える。
サーバー間通信で使用するので、通信としてのServerもClientもJavaで実装する(ブラウザは使用しない)

事前準備

SPRING INITIALIZR を使ってテンプレートプロジェクトを作成します。
SpringBootは2.0系を選択して、ReactiveWebをDependenciesに入れておきます。

以下の環境で作業を行います。

  • Java9
  • SpringBoot2.0.0M6

Websocketクラスの実装

Serverサイドのマッピングの設定

FluxWebSocketConfig.java
Mappingを設定することで指定したパスでwebsocket通信が受付できるようになります。

@Configuration
public class FluxWebSocketConfig {
    
    @Autowired
    private FluxWebSocketHandler handler;

    @Bean
    public HandlerMapping webSocketMapping() {
        Map<String, WebSocketHandler> map = new HashMap<>();
        map.put("/path", handler);

        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setUrlMap(map);
        mapping.setOrder(-1); // before annotated controllers
        return mapping;
    }

    @Bean
    public WebSocketHandlerAdapter handlerAdapter() {
        return new WebSocketHandlerAdapter();
    }
}

FluxWebSocketHandler.java
Handlerでは通信後の処理を記述しています。最初の通信でhandle()がコールされるので、ここでEmitterProcessorを返却するようにしています。

セッション確立後にsendMessageを使ってメッセージを配信するようにしていますが、ここでEmitterProcessorに対してMessageを渡すことでClient側に伝播されます。

@Slf4j
@Component
public class FluxWebSocketHandler implements WebSocketHandler {
    private final EmitterProcessor<String> in = EmitterProcessor.create();

    public void sendMessage(String message) {
        log.info("Send Message:" + message);
        in.onNext(message);
    }

    @Override
    public Mono<Void> handle(WebSocketSession session) {
        session.receive()
        .map(WebSocketMessage::getPayloadAsText)
        .log()
        .subscribe();
    return session.send(Mono.just(session.textMessage("connected!"))).then(session.send(in.map(session::textMessage)));
    }
}

session.receive()の処理はClient側からのメッセージを受けないのであれば不要です。

Clientサイドのリクエストの設定

FluxWebSocketClientSample.java
リクエスト処理は別スレッドで起動しています。(本当はWebSocketClientの機能で別スレッド起動ができるはずです。。。)

@Slf4j
@Component
public class FluxWebSocketClientSample {

    @PostConstruct
    private void init() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Runnable runner = this::connect;
        executor.execute(runner);
    }

    private void connect() {
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        WebSocketClient client = new ReactorNettyWebSocketClient();
        
        client.execute(URI.create("ws://localhost:8080/path"),
                session -> session.receive()
                        .map(WebSocketMessage::getPayloadAsText)
                        .log()
                        .then()
                )
                .subscribe();

    }
}

↑のサンプルではServer→Clientの送信だけですが、双方向でメッセージを送りたい場合はこちらでもEmitterProcessorを設定します。

動作確認

./gradlew bootRunでプロセスを起動して.logメソッドによるメッセージが表示されたら成功です。
適当なクラスを用意してFluxWebSocketHandler.sendMessage()を呼び出すことで任意のメッセージを伝播することができるはずです。

最後に

全般的にReactorNettyWebSocketClientやEmitterProcessorの仕様・使い方がわかりません。。。まとまっている資料やサイトがあれば教えてください。。。

Docker上のWordpressをサブディレクトリで動かす

やること

Dockerを使ってWebサイトを公開しているが、同じドメインのサブディレクトリでWordpressで作ったブログを表示させたい。
以下のような構成になるイメージです。

http://hoge.co.jp <- メインのWebサイト
http://hoge.co.jp/blog <- 追加するブログ用のサブディレクトリ

WordpressのDockerfileの作成

公式のWordpressイメージのルートを変更するやり方がわからなかったのですが、以下のブログの記事を参考にさせてもらいました!

Dockerの公式WordPressイメージを使い、下層ディレクトリにある既存のWordPressを動かす - Docker入門 | ねこしすてむ

wordpress コンテナのアクセス用IPアドレスの取得

IPアドレスは固定で設定していないので直接コンテナから取得します。(コンテナ名がwordpressの場合)

$ sudo docker-compose exec wordpress bash

$ ip route show | awk '/default/ {print $3}'
172.19.0.1

nginx.conf の修正

server定義に以下を追加します。
proxy_passにはwordpressコンテナのIPを設定します。

server {
(省略)
  location ^~ /blog {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_pass http://172.19.0.1:8000;
  }
}

/blogにアクセスしてWordpressのトップページが表示されたら完了です。