techmedia-think.hatenablog.com
先月リリースされたBitcoin Core 0.13.0にSegwitのコードがマージされtestnetで利用可能になったので、関連するRPCの変更点を見てみる。
RPCの変更点
getblock と getblockheader の変更点
出力内容に新しく以下の項目が追加されている。
- strippedsize
witnessデータを除外したブロックのサイズ - weight
BIP-141でブロックサイズの上限が1MBから新しい制限に変更された。新しい制限は、witness関連のデータを除外したトランザクションをシリアライズしたバイト単位のブロックサイズ=ベースサイズとBIP-144に定義されているシリアライズフォーマットでトランザクションをシリアライズしたバイト単位のブロックサイズ=トータルサイズから、ベースサイズ✕3+トータルサイズ=block weightを算出しそのサイズが4,000,000以下というもの。weightにはそのblock weightが入る。 - versionHex
Segwitとは関係ないけど、BIP-9のソフトフォークの仕様でブロックのnVersionにversion bitが記載されてるようになったのに伴い、versionデータの16進表記が追加された。
getrawtransaction と decoderawtransaction の変更点
RPCの1つであるgetrawtransactionに引数1を付与してコールするとトランザクションのデータがJSON形式で表示され、decoderawtransactionもトランザクションデータをデコードしてJSON形式で表示するRPCである。
今回のSegwitの対応でその出力内容に新しく以下の項目が追加されている。
- hash
トランザクションのハッシュ(witness transactionsのtxidとは異なる) - vsize
仮想トランザクションサイズ=トランザクションのweight/4の値 - txinwitness
vinのパラメータとして追加されたwitnessデータの16進表記。
getmininginfo
getblock等に加わったblock weightを表示する以下の項目が追加された。
- currentblockweight
現在のブロックのweight
createwitnessaddress(新規)
↓のように16進表記のscriptPubKeyを引数にとり、そのスクリプトのwitness addressを作成するRPC。
createwitnessaddress <scriptPubkey>
戻り値は以下の2つ。
- address
(witness scriptのP2SHの)アドレスの値 - witnessScript
16進エンコードされたwitness scriptの文字列
引数のスクリプトからwitness scriptを実際に生成している処理が standard.cpp の↓
CScript GetScriptForWitness(const CScript& redeemscript) { CScript ret; txnouttype typ; std::vector<std::vector<unsigned char> > vSolutions; if (Solver(redeemscript, typ, vSolutions)) { if (typ == TX_PUBKEY) { unsigned char h160[20]; CHash160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160); ret << OP_0 << std::vector<unsigned char>(&h160[0], &h160[20]); return ret; } else if (typ == TX_PUBKEYHASH) { ret << OP_0 << vSolutions[0]; return ret; } } uint256 hash; CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin()); ret << OP_0 << ToByteVector(hash); return ret; }
スクリプトがP2PKHなら、(P2PKHじゃなくてpay-to-pubkeyの場合はpubkeyのhash160してる)
OP_0 <公開鍵のハッシュ値>
というversion byteが0でwitness programがP2WPKHのwitness scriptになる。
スクリプトがP2SHなら
OP_0 <スクリプトのSHA-256ハッシュ>
というversion byteが0でwitness programがP2WSHのwitness scriptになる。
そうして生成したwitness scriptのhash160ハッシュして、P2SHのスクリプトハッシュとしてアドレスを生成している。
※ここで生成されるアドレスは、P2SHでネストされたP2WPKHかP2WSHになり純粋なP2WPKHやP2WSHではない。詳細はBIP-141で定義されているけどSegwitでは以下の4つのscriptPubKeyの形式がある中で、P2SHでネストされたものがこのRPCで生成される。
- P2WPKH
- P2WPKH nested in BIP16 P2SH
- P2WSH
- P2WSH nested in BIP16 P2SH
$ bitcoin-cli -regtest createwitnessaddress 21027ee57c6a7f4e1f4983fefc4a547b5a8576781ef87b7b06b6a3558b8e2461e0f4ac { "address": "2NBXLLu2TBrF2N3V2AdiCofFJBqWFrbFUPZ", "witnessScript": "0014741459afe36cda3c132497ccbbe77be080515ac9" }
なお、P2SHでネストされていない、P2WPKHなscriptPubkeyにBitcoinを送った際のトランザクションをパースして出力を確認すると↓のように、P2PKHやP2SHなどで表示されていたaddress情報は含まれない。
"vout": [ { "value": 0.00900000, "n": 0, "scriptPubKey": { "asm": "0 015380566edf72c60106edd28871a1093ad42817", "hex": "0014015380566edf72c60106edd28871a1093ad42817", "type": "witness_v0_keyhash" } } ]
bitcoin-ruby使って自前で↑と同じP2WPKHのwitness scriptと P2WPKH nested in BIP16 P2SHなアドレスを生成するコードを書くと↓のようになる。
addwitnessaddress(新規)
createwitnessaddressがscriptPubKeyからアドレスとwitness scriptを生成するのに対し、addwitnessaddressは既存のP2PKHやP2SHのアドレスからwitness scriptを作成し、そこからwitnessアドレスを生成してウォレットに登録する。(ここで作られるアドレスもcreatewitnessaddressと同様P2SHでネストしたscriptPubkey)
addwitnessaddress "address"
↑のcreatewitnessaddressの引数に渡したscriptPubkeyの公開鍵から生成したアドレスは”mr6j4GmqxFq4eAmsDSUgGzSmdFcEyr6m4Z”なので、それを引数にaddwitnessaddressを実行すると
$ bitcoin-cli -regtest addwitnessaddress mr6j4GmqxFq4eAmsDSUgGzSmdFcEyr6m4Z 2NBXLLu2TBrF2N3V2AdiCofFJBqWFrbFUPZ
同じアドレスが生成されているのが分かる。
rpcwallet.cppの以下のコードで、アドレスタイプ(P2PKHなのかP2SHなのか)別にアドレスからwitness scriptを作成してウォレットに追加している。
class Witnessifier : public boost::static_visitor<bool> { public: CScriptID result; bool operator()(const CNoDestination &dest) const { return false; } bool operator()(const CKeyID &keyID) { CPubKey pubkey; if (pwalletMain && pwalletMain->GetPubKey(keyID, pubkey)) { CScript basescript; basescript << ToByteVector(pubkey) << OP_CHECKSIG; CScript witscript = GetScriptForWitness(basescript); pwalletMain->AddCScript(witscript); result = CScriptID(witscript); return true; } return false; } bool operator()(const CScriptID &scriptID) { CScript subscript; if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { int witnessversion; std::vector<unsigned char> witprog; if (subscript.IsWitnessProgram(witnessversion, witprog)) { result = scriptID; return true; } CScript witscript = GetScriptForWitness(subscript); pwalletMain->AddCScript(witscript); result = CScriptID(witscript); return true; } return false; } };
アドレスに登録されることで、ウォレット内のアドレスとしてUTXOの抽出などの対象になる。
signrawtransaction
トランザクションに署名をするsignrawtransactionも、署名データをwitnessデータにセットしたトランザクションを作成できるようになった。
P2WPKH宛てにBitcoinを送付したトランザクション↓
{ "hex": "010000000153f944b5233653d7037126e0cfaa5236f40500b060331bd9d87985955a70a151000000006b483045022100f1798f45a0b7cffbce6f9939f4c176d50cef27c58b366b5ad66744fafa92b8d002202f16cb139d9ceabcf971d08dfc9dcbe711a37ad35beea23e92bc923934d08036012102bf15b363ff14a24f2c66221c3e84caeaacc846a17bae9657a33055af93b74857ffffffff01a0bb0d0000000000160014015380566edf72c60106edd28871a1093ad4281700000000", "txid": "ce8ab573226d7c3eb3a4a6d2d5867fb792993e5c284120d49005a9e1812caee2", "hash": "ce8ab573226d7c3eb3a4a6d2d5867fb792993e5c284120d49005a9e1812caee2", "size": 189, "vsize": 189, "version": 1, "locktime": 0, "vin": [ { "txid": "51a1705a958579d8d91b3360b00005f43652aacfe0267103d7533623b544f953", "vout": 0, "scriptSig": { "asm": "3045022100f1798f45a0b7cffbce6f9939f4c176d50cef27c58b366b5ad66744fafa92b8d002202f16cb139d9ceabcf971d08dfc9dcbe711a37ad35beea23e92bc923934d08036[ALL] 02bf15b363ff14a24f2c66221c3e84caeaacc846a17bae9657a33055af93b74857", "hex": "483045022100f1798f45a0b7cffbce6f9939f4c176d50cef27c58b366b5ad66744fafa92b8d002202f16cb139d9ceabcf971d08dfc9dcbe711a37ad35beea23e92bc923934d08036012102bf15b363ff14a24f2c66221c3e84caeaacc846a17bae9657a33055af93b74857" }, "sequence": 4294967295 } ], "vout": [ { "value": 0.00900000, "n": 0, "scriptPubKey": { "asm": "0 015380566edf72c60106edd28871a1093ad42817", "hex": "0014015380566edf72c60106edd28871a1093ad42817", "type": "witness_v0_keyhash" } } ], "blockhash": "05a1597c9e819dbe74d23dff6c8ca8524e7c99d0d91478c540b0606c9b08fc23", "confirmations": 1, "time": 1473600993, "blocktime": 1473600993 }
を入力にセットしたトランザクションを作成し、signrawtransactionに渡すと↓のようにwitnessデータに署名データがセットされる。
$ bitcoin-cli -regtest decoderawtransaction 01000000000101e2ae2c81e1a90590d42041285c3e9992b77f86d5d2a6a4b33e7c6d2273b58ace0000000000ffffffff0100350c00000000001976a914548742b7ee154defc3837876854490e8f6770c5e88ac02473044022069d189fd38ccd885074d8d5d4ff5797449451144e1c3bdeaf27e3a26db604bba02200dddabc2b20aaa68ef4175257a314a97eefb50edfa341b89f5537afd90f2c34f0121033d79f048c78bce024c5c163bdde233a11c24bafc6e100183bf9fe82c1d3b412200000000 { "txid": "119a735e396cd9691d0b3d3c9407a9fd3e2248f7bce41502058d89c7f69a5741", "hash": "8a6042c5c5333ea62dfda559f474204b59f359b1e69b86bc54611d6ca8c1c61d", "size": 194, "vsize": 113, "version": 1, "locktime": 0, "vin": [ { "txid": "ce8ab573226d7c3eb3a4a6d2d5867fb792993e5c284120d49005a9e1812caee2", "vout": 0, "scriptSig": { "asm": "", "hex": "" }, "txinwitness": [ "3044022069d189fd38ccd885074d8d5d4ff5797449451144e1c3bdeaf27e3a26db604bba02200dddabc2b20aaa68ef4175257a314a97eefb50edfa341b89f5537afd90f2c34f01", "033d79f048c78bce024c5c163bdde233a11c24bafc6e100183bf9fe82c1d3b4122" ], "sequence": 4294967295 } ], "vout": [ { "value": 0.00800000, "n": 0, "scriptPubKey": { "asm": "OP_DUP OP_HASH160 548742b7ee154defc3837876854490e8f6770c5e OP_EQUALVERIFY OP_CHECKSIG", "hex": "76a914548742b7ee154defc3837876854490e8f6770c5e88ac", "reqSigs": 1, "type": "pubkeyhash", "addresses": [ "moDu6EtnGGpcTEkNZRJbytr9rJA4H49VRe" ] } } ] }
signrawtransactionを使う分には特に意識しないけど、witnessなトランザクションではトランザクションへの署名時に生成するトランザクションのSIG_HASHの生成ロジックが変わっているので注意する必要がある。具体的な変更点はBIP-143に定義されている↓
techmedia-think.hatenablog.com