DC4

今よく言われてる「草食系」にはなったらあかん! 野獣のような「肉食系」になるため、今は肉ばっかり食べてるよ。

ISUCON8の予選を通過しました。

k02というチームでISUCON8予選に参加し、予選を通過することができました。 528組中16位でした。

isucon.net

準備

kyokomi が事前に kibela を導入してくれていたので毎年よく使うコマンドやNginx/MySQLの設定ファイル等を予め追加していました。

当日の会場とディスプレイ等はwaniji が手配してくれたおかげで横並びで作業することができ、最高の環境でした。

当日

なんとか起床に成功し、恵比寿に集合することに成功。

ひとまず全員でレギュレーションの読込み後、サービスの機能一覧を確認しました。

この時点で h2oMariaDB の存在に気がついたのですが、一旦後回しにし、アプリケーション側のチューニングに集中することに。

今回も諸事情でGo実装を選択していましたが、普段の業務では使っていないためRuby実装とGo実装を交互に読みながらソースを確認していました。

ソースリーディング中はkyokomiが出力してくれたER図がとても役に立ちました。

N+1、インデックス追加、Sheetテーブルのキャシュ等を行いましたが、DBのCPU使用率が高すぎてベンチの結果が上がらなくなったため、早々にDBを別サーバに切り出すことにしました。

このあたりからスコアが徐々に伸び始めて10000点くらいになり、10位代をうろうろしていました。

その後いくつかの変更を加えた後にDBのコネクション数を変更してみたところ、30000点を超えて暫定4位を記録しました

が、/admin/api/reports/events/:id/sales の結果に整合性がない?みたいなエラーが出るようになり、 ベンチーマークの結果がなかなか安定せず、数回に一回だけ成功するガチャ状態に陥ってしまいました。

原因が掴めずここでかなり時間を消費してしまいました...。

この間にシート予約時の場所をランダムではなく、順番に着席させてみたら怒られたりしてゲラゲラ笑ってました。

終盤間際で排他処理が正常にできていないことに気が付き、ベンチマーク結果を安定させることができました。

そのまま終了3分前くらいに、その日の最高得点が出たので終了しました。

感想

2017年ふりかえり。

今年も残り数時間なので恒例のふりかえり。

帰国

約1年半のフィリピン生活を終えて、日本に返ってきました。

食のレベルの高さ、治安の良さを改めて感じました。

帰国直後は毎日セブンイレブンの商品を食べて感動していました。

時々フィリピンの大雑把さや底抜けに明るい雰囲気が恋しくなることも。

フリーランス

昨年から引き続き、リモート中心のフリーランス活動をやっていました。

Rails だけでなくGoやReact関係にも関わることができて勉強になりました。

お仕事を下さった方々/ご紹介して頂いた方々には本当に感謝です。

引越しと断捨離

フィリピン -> 日本(実家) -> 東京(1) -> 東京(2) と今年だけ4回ほど引越しをしました。

引っ越す度に最低限必要なものだけを残してきたため、最終的には全ての持ち物がトランクケース1個になりました。

これを機に定期的に断捨離していこうと思います。

スタートアップにジョイン

入社しました!

来年に向けて

来年は腹筋割りたいです。

それではよいお年を!

未使用アウトプットに基づくトランザクションの生成/署名/送信

Mastering Bitcoinの「未使用アウトプットに基づくトランザクションの生成/署名/送信」を実際にコマンドを実行しながら確認します。

環境は前回作成したDocker環境を使用します。

Bitcoin Coreを試すためのDocker環境を作った。 - DC4

Raw Transaction の受取先アドレスを作成します。

# アドレスの作成
root@56451b900162:/# bitcoin-cli -regtest getnewaddress testuser1
mrmcNKkhippXe8WTm7BbLNSoy5CydCZHhV

Raw Transaction に含めるUTXOを選択

#未使用のアウトプット(UTXO)を確認
bitcoin-cli -regtest listunspent 
...
  {
    "txid": "5f76f24fe419f09e7da5010d8e3d110db4189f3b51f797026d619a7ca95416f8",
    "vout": 0,
    "address": "mzLCAPscRZtYZysh2RjUz8XEgXZuAU7e9v",
    "scriptPubKey": "2103562423e4bd01816ee0e06df4b327970b6f5f04ac817ad9e66ab039d20bd34db7ac",
    "amount": 50.00000000,
    "confirmations": 147,
    "spendable": true,
    "solvable": true,
    "safe": true
  }
...

Raw Transaction の作成

# > bitcoin-cli createrawtransaction "[{\"txid\":\"myid\",\"vout\":0}]" "{\"address\":0.01}"
# `txid` 、`vout` に 上記のUTXO、受取先アドレス、送金額(10BTC) / お釣り(39.9995BTC) (手数料: 0.0005) を指定する。
root@56451b900162:/# bitcoin-cli -regtest createrawtransaction "[{\"txid\":\"5f76f24fe419f09e7da5010d8e3d110db4189f3b51f797026d619a7ca95416f8\",\"vout\":0}]" "{\"mrmcNKkhippXe8WTm7BbLNSoy5CydCZHhV\":10,  \"mzLCAPscRZtYZysh2RjUz8XEgXZuAU7e9v\":39.9995}"
020000000100cc5d9cf910e58d423e4e686d67c9719e6e300eb93be1ecdc92ab53e274b2310000000000ffffffff0200ca9a3b000000001976a9145e4973ba22b718b8336c53cc70504ed16e07caf688acb0646aee000000001976a914ce6164048e09beba6beb0ea63fc9a7edc501393e88ac00000000

vout・・・1つのトランザクションを分割する際の番号

出力された16進数をデコードして内容を確認します。

bitcoin-cli -regtest decoderawtransaction 020000000100cc5d9cf910e58d423e4e686d67c9719e6e300eb93be1ecdc92ab53e274b2310000000000ffffffff0200ca9a3b000000001976a9145e4973ba22b718b8336c53cc70504ed16e07caf688acb0646aee000000001976a914ce6164048e09beba6beb0ea63fc9a7edc501393e88ac00000000
{
  "txid": "086065375607d9afd95a105eda40c362dec067bff7d2ba1cedc7002a5b27ce1e",
  "hash": "086065375607d9afd95a105eda40c362dec067bff7d2ba1cedc7002a5b27ce1e",
  "size": 119,
  "vsize": 119,
  "version": 2,
  "locktime": 0,
  "vin": [
    {
      "txid": "31b274e253ab92dcece13bb90e306e9e71c9676d684e3e428de510f99c5dcc00",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 10.00000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 5e4973ba22b718b8336c53cc70504ed16e07caf6 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9145e4973ba22b718b8336c53cc70504ed16e07caf688ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mp7VodBS75f57jFgGWHb6NHNCrDH54CxNr"
        ]
      }
    },
    {
      "value": 39.99950000,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 ce6164048e09beba6beb0ea63fc9a7edc501393e OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914ce6164048e09beba6beb0ea63fc9a7edc501393e88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mzLCAPscRZtYZysh2RjUz8XEgXZuAU7e9v"
        ]
      }
    }
  ]
}

scriptSigがnullなので、署名が行われていないことが確認できます。

署名の実施

root@56451b900162:/# bitcoin-cli -regtest signrawtransaction 020000000100cc5d9cf910e58d423e4e686d67c9719e6e300eb93be1ecdc92ab53e274b2310000000000ffffffff0200ca9a3b000000001976a9145e4973ba22b718b8336c53cc70504ed16e07caf688acb0646aee000000001976a914ce6164048e09beba6beb0ea63fc9a7edc501393e88ac00000000
{
  "hex": "020000000100cc5d9cf910e58d423e4e686d67c9719e6e300eb93be1ecdc92ab53e274b23100000000484730440220171a3a74c11ce5e7ffcc2a3ee6cf86957edfa955dd0ca3052ea3d1631eb616f5022054a24b3201cbf3d3b979c4d3074ef490be5cf89b8857056021933201d1f2b7e501ffffffff0200ca9a3b000000001976a9145e4973ba22b718b8336c53cc70504ed16e07caf688acb0646aee000000001976a914ce6164048e09beba6beb0ea63fc9a7edc501393e88ac00000000",
  "complete": true
}

もう一度デコードして内容を確認します。

{
  "txid": "7c7f41ccd1d0df0f3ab1009ab7993ec32ff161b9edaa3c196e6644dda54c8efb",
  "hash": "7c7f41ccd1d0df0f3ab1009ab7993ec32ff161b9edaa3c196e6644dda54c8efb",
  "size": 191,
  "vsize": 191,
  "version": 2,
  "locktime": 0,
  "vin": [
    {
      "txid": "31b274e253ab92dcece13bb90e306e9e71c9676d684e3e428de510f99c5dcc00",
      "vout": 0,
      "scriptSig": {
        "asm": "30440220171a3a74c11ce5e7ffcc2a3ee6cf86957edfa955dd0ca3052ea3d1631eb616f5022054a24b3201cbf3d3b979c4d3074ef490be5cf89b8857056021933201d1f2b7e5[ALL]",
        "hex": "4730440220171a3a74c11ce5e7ffcc2a3ee6cf86957edfa955dd0ca3052ea3d1631eb616f5022054a24b3201cbf3d3b979c4d3074ef490be5cf89b8857056021933201d1f2b7e501"
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 10.00000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 5e4973ba22b718b8336c53cc70504ed16e07caf6 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9145e4973ba22b718b8336c53cc70504ed16e07caf688ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mp7VodBS75f57jFgGWHb6NHNCrDH54CxNr"
        ]
      }
    },
    {
      "value": 39.99950000,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 ce6164048e09beba6beb0ea63fc9a7edc501393e OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914ce6164048e09beba6beb0ea63fc9a7edc501393e88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mzLCAPscRZtYZysh2RjUz8XEgXZuAU7e9v"
        ]
      }
    }
  ]
}

今回は scriptSigが設定されている事が確認できます。

この状態で 署名されたhexsendrawtransaction 等を使用してブロードキャストすることができます。

Gethでetherを送金する

前回作成した環境を使い、アカウント作成/マイニング/送金まで行っていきます。

gethを試すためのDocker環境を作った。 - DC4

以下コマンドでGethのインストール後、テストネットに接続します。

$ docker-compose build
$ docker-compose up

起動後暫くはDAGのダウンロードが行われます。
DAGは ethereumのPoWに必要なもので、詳しくは以下を参照して下さい。
Ethash DAG · ethereum/wiki Wiki · GitHub

コンテナに接続後、Gethにアタッチします。

docker exec -i -t gethdockersample_geth_1 /bin/bash
geth attach ipc://eth_data/geth.ipc

アカウントの作成

以下のコマンドでアドレスを発行します。
引数に入れたパスワードを忘れるとEtherが引き出せなくなるので注意して下さい。 0xから始まる値がアドレスとなります。

> personal.newAccount("hoge1")
> 0x3f10fa5b63d60f1bc89783a11c415b99fe5aac3a

送金受信用としてもう一つアカウントを作成します。

> personal.newAccount("hoge2")
> 0xf7637d91ab359f567e97e901bf4ef75142fb6e96

ここで作成されたアドレスの情報はkeystore配下に格納されています。
以下ファイルを移動する事で別のノードへアドレスを移動することができます。

root@943656414d81:/# ll eth_private_net/keystore/
total 16
drwx------ 2 root root 4096 Aug 12 15:49 ./
drwxr-xr-x 5 root root 4096 Aug 12 15:49 ../
-rw------- 1 root root  491 Aug 12 15:49 UTC--2017-08-12T06-49-18.310228365Z--3f10fa5b63d60f1bc89783a11c415b99fe5aac3a
-rw------- 1 root root  491 Aug 12 15:49 UTC--2017-08-12T06-49-22.989639328Z--f7637d91ab359f567e97e901bf4ef75142fb6e96

マイニング

以下コマンドでマイニング開始/終了が行われます。

> miner.start()
true
> miner.stop()
true

暫く待つと最初に作成されたアドレスにマイニング報酬が付与されます。

> eth.coinbase
"0x3f10fa5b63d60f1bc89783a11c415b99fe5aac3a"

> eth.getBalance(eth.coinbase)
9.575e+21

f:id:Yasun:20170812172900p:plain

送金

マイング報酬を持つアドレス 0x3f10fa5b63d60f1bc89783a11c415b99fe5aac3a から 0xf7637d91ab359f567e97e901bf4ef75142fb6e96 へEtherを送金してみます。

送信元アドレスのロックを解除します。

> personal.unlockAccount(eth.coinbase)
Unlock account 3f10fa5b63d60f1bc89783a11c415b99fe5aac3a
Passphrase:
true

10ETHを送金します。実行後にトランザクションのアドレスが発行されます。

> eth.sendTransaction({from: eth.coinbase, to: "0xf7637d91ab359f567e97e901bf4ef75142fb6e96", value: web3.toWei(10, "ether")})
"0x161da21dfe25da7920bd084c33f66863dedac0248dd92ccb84bdd11e09c33e58"

直後に受取側の残高を確認します。

> eth.getBalance('0xf7637d91ab359f567e97e901bf4ef75142fb6e96')
0

この時点ではまだ送金トランザクションがブロックに取り込まれていないため、残高が反映されていません。 マイニングを開始し、残高を確認します。

> miner.start()
> true
> eth.getBalance('0xf7637d91ab359f567e97e901bf4ef75142fb6e96')
10000000000000000000

10000000000000000000wei がアドレスに反映されています。
weiとはetherの最小単位であり、1weiは1etherの10^-18です。

10etherを受け取ったアドレスから5etherを送り返します。

> eth.sendTransaction({from: "0xf7637d91ab359f567e97e901bf4ef75142fb6e96", to: eth.coinbase , value: web3.toWei(5, "ether")})
"0x5e92caf1391a234aefa768dc8dcd32692da4412523c8c97912d3bb4227c9f4aa"

> eth.getBalance('0xf7637d91ab359f567e97e901bf4ef75142fb6e96')
4999580000000000000

5etherに加えて送金手数料として0.00042etherが余分に支払われていることがわかります。

手数料の内訳を確認するため、送金トランザクションの詳細を確認します。

> eth.getTransaction('0x5e92caf1391a234aefa768dc8dcd32692da4412523c8c97912d3bb4227c9f4aa')
{
  blockHash: "0x1e7127bdba210c3a8f75d26e5508dc9ab00a51f1534543670b7b6aedcedd7435",
  blockNumber: 1924,
  from: "0xf7637d91ab359f567e97e901bf4ef75142fb6e96",
  gas: 90000,
  gasPrice: 20000000000,
  hash: "0x5e92caf1391a234aefa768dc8dcd32692da4412523c8c97912d3bb4227c9f4aa",
  input: "0x",
  nonce: 0,
  to: "0x3f10fa5b63d60f1bc89783a11c415b99fe5aac3a",
  transactionIndex: 0,
  value: 5000000000000000000
}

上記で手数料に関係するのは gasgasPrice の値です。

少しややこしいのですが gas とはトランザクション処理時の最大の 手数料値 です。
実際に発生する料金ではなく、最大で発生する可能性のある料金です。
この値は実行されるトランザクションの内容によって変動し、estimageGasメソッドを用いて見積もる事が可能です。

gasPriceとは 1 gasが何weiかを表す値です。
この値は常に変動しており、gasPriceメソッド等を用いて確認することができます。
Ethereum Average GasPrice Chart

上記の場合は、90000(gas) * 20000000000wei(gasPrice) となり、 最大で0.0018etherの手数料が発生する可能性がある、ということになります。

今回はここまで。