P2SHを使いこなす

すみません、ウソです。

p2shで苦労しているので、備忘録を書いていきます。
（適宜追加していきます。）

# 環境
Bitcoin 0.12.1
https://github.com/bitcoin/bitcoin

# P2SHとは
Bitcoinの送金方法には、アドレスの指定の方法が主に２つある。
P2PKH (Pay to PublicKey Hash): 公開鍵のハッシュ値をアドレスとして使う方法
P2SH (Pay to Script Hash): スクリプト（オプション付き送金指示）のハッシュ値をアドレスとして使う方法
スクリプトに公開鍵ハッシュの検証指示を含めることで、意図した相手方にちゃんと届けられる。
またスクリプトはハッシュのみになるため、スクリプトの本体（RedeemScriptという）は別に送る必要がある。
Redeemとは、質入れしたものを買い戻すことを言う。
スクリプトは、bitcoin独特の言語で書かれる。逆ポーランド表記。
例えば、「1+2」という命令の場合、「1 2 OP_ADD」といった形で表現される。
要は、ここに検証の機能をもたせてしまおうということ。
（ということは、ここに検証以外のスクリプトもかけるということ）
スクリプトは、ループ処理ができない「チューリング非完全」と呼ばれる簡単なプログラムしか書けない。
（もし書きたいのであれば、Ethereumや周辺システムがフォローすればよい）


# Script作成
## Stringから
```rb
redeem_script = Bitcoin::Script.from_string("6e OP_NOP2 OP_DROP 03b05c78e5dbde5046d0545fa788bac2303929259aa77f5eedf1d87bddbca1bb3c OP_CHECKSIG")
```

情報はリトルエンディアンで書くことに注意。（6eは、110の意味。03b0...は送り先の公開鍵）

## hexから

```rb
> script_hex = '016eb1752103b05c78e5dbde5046d0545fa788bac2303929259aa77f5eedf1d87bddbca1bb3cac'
> bcs = Bitcoin::Script.new(script_hex.htb)
 => #<Bitcoin::Script:0x007fecaab4d428 @raw_byte_sizes=[39, 0], @previous_output_script=nil, @input_script="\x01n\xB1u!\x03\xB0\\x\xE5\xDB\xDEPF\xD0T_\xA7\x88\xBA\xC209)%\x9A\xA7\x7F^\xED\xF1\xD8{\xDD\xBC\xA1\xBB<\xAC", @raw="\x01n\xB1u!\x03\xB0\\x\xE5\xDB\xDEPF\xD0T_\xA7\x88\xBA\xC209)%\x9A\xA7\x7F^\xED\xF1\xD8{\xDD\xBC\xA1\xBB<\xAC", @chunks=["n", 177, 117, "\x03\xB0\\x\xE5\xDB\xDEPF\xD0T_\xA7\x88\xBA\xC209)%\x9A\xA7\x7F^\xED\xF1\xD8{\xDD\xBC\xA1\xBB<", 172], @exec_stack=[], @stack_alt=[], @stack=[], @last_codeseparator_index=0, @do_exec=true> 
> bcs.to_string
 => "6e OP_NOP2 OP_DROP 03b05c78e5dbde5046d0545fa788bac2303929259aa77f5eedf1d87bddbca1bb3c OP_CHECKSIG" 
```

OP_NOP2はCHECKLOCKTIMEVERIFYと呼ばれる命令。BIP-65で規定されています。

詳しくは、こちら
http://techmedia-think.hatenablog.com/entry/2016/05/31/184257

ちなみに、locktimeだけを取り出したい場合は、こうすればOK。ただし、常に0番目にあるわけではないので、本当はもう少しちゃんと作らないとNG。

```rb
locktime = bcs.to_string.split[0]
```

# P2SHアドレス作成
## Bitcoin::Scriptから
```rb
p2sh_address = Bitcoin::Script.new(Bitcoin::Script.to_p2sh_script(hash160(redeem_script.to_payload.bth))).get_p2sh_address
```
カンタンネ。

## hexから

```rb
# Create Bitcoin::Script (hash160) object from script_hex
bcs_hash160 = Bitcoin::Script.new(Bitcoin::Script.to_p2sh_script(hash160(script_hex)))
        
# Obtain a p2sh address from the script
p2sh_addr = bcs_hash160.get_p2sh_address
# => 2NEbifo1SsiCYMQhGxGCg3tcTzR8xHuhqeH

```

# Script変換

以下のようなScriptがあったとする。これをDBに保存したり、先方に送ったりするときには、文字列などに変換したくなる。

```rb
> redeem_script
=> #<Bitcoin::Script:0x007fecae9e3430 @raw_byte_sizes=[39, 0], @previous_output_script=nil, @input_script="\x01n\xB1u!\x02\xAA\xA0\xA7\x96\x9F\e\xED;R\x83vXbY\x89\x13\xAC\nj\xDA\x19\xC0\xD7\xF9u\rf[\xA1\xD1A\x00\xAC", @raw="\x01n\xB1u!\x02\xAA\xA0\xA7\x96\x9F\e\xED;R\x83vXbY\x89\x13\xAC\nj\xDA\x19\xC0\xD7\xF9u\rf[\xA1\xD1A\x00\xAC", @chunks=["n", 177, 117, "\x02\xAA\xA0\xA7\x96\x9F\e\xED;R\x83vXbY\x89\x13\xAC\nj\xDA\x19\xC0\xD7\xF9u\rf[\xA1\xD1A\x00", 172], @exec_stack=[], @stack_alt=[], @stack=[], @last_codeseparator_index=0, @do_exec=true> 
```

## hex変換

```rb
redeem_script.to_payload.bth
#=> "016eb1752102aaa0a7969f1bed3b5283765862598913ac0a6ada19c0d7f9750d665ba1d14100ac" 
```

## 文字列変換

```rb
redeem_script.to_string
# => "6e OP_NOP2 OP_DROP 02aaa0a7969f1bed3b5283765862598913ac0a6ada19c0d7f9750d665ba1d14100 OP_CHECKSIG" 
```

ちなみに、この「02aaa...」は受取人の公開鍵ハッシュ。アドレスを見る場合は以下のようにする。

```rb
include Bitcoin::Util
pubkey_to_address('02aaa0a7969f1bed3b5283765862598913ac0a6ada19c0d7f9750d665ba1d14100')
=> "mkXBzp5CxTHkqPj9mp69VTFHbsgn557p74"

```


# P2SHアドレス検証

Bitcoin::Utilに、methodあり。mainnet/testnetはUtil内部変数で、自動判断の様子。

```rb
module Bitcoin
  module Util
  # get type of given +address+.
  def address_type(address)
    return nil unless valid_address?(address)
    case decode_base58(address)[0...2]
    when address_version; :hash160
    when p2sh_version;    :p2sh
    end
  end
```

# 秘密鍵を抽出

署名に使うpubkeyなどを作るために、秘密鍵があると何かと便利。そんな時は、bitcoin-cliを使うと楽。

```
% bitcoin-cli dumpprivkey mxTDdP1uAhfK32zFt9SdV1UnftMoDqMbq6
```

mxTDdP1uAhfK32zFt9SdV1UnftMoDqMbq6は、bitcoinのアドレス。ローカル端末内に対応する秘密鍵があると、表示してくれる。もし、端末内になければ、エラーメッセージが帰ってくる。

これを使って、Bitcoin::Keyオブジェクトを作ると、いろいろ便利。

```rb
Bitcoin.network = :testnet3    # <= これ重要。ないと、[RuntimeError: Invalid version]が表示される
key = Bitcoin::Key.from_base58('秘密鍵')
```

key.pubで、公開鍵になる。楽チン。


# 考察・注意展

P2SHアドレスを生成するためには、そのUTXOの利用を相手方に制限するために、相手方のpubkeyが欲しいところ。pubkeyを使わない場合は、ほかの手段による検証のロジックをスクリプトに書く必要あり。
（だれでも使えるUTXOを作る場合は、この限りでない）

privkeyを共有するよりもマシなので、何らかの方法で公開鍵を共有する。

----

P2SHの学習にあたり、良質な記事を公開されているハウ・インターナショナルのAzuchi様に感謝いたします。