2017年10月11日水曜日

[Bitcoin]Pay-to-Public-Key-Hash (P2PKH)を検証してみる

前のトランザクションのOutputをInputに使うわけですが、秘密鍵をもっている人だけがそのOutputを使うことができます。


↑こちらの本を読んでいるとコーヒーショップの支払いの例として
Outputにはlocking scriptに
OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUAL OP_CHECKING
と書いてあって

Inputにはunlocking scriptに
<Cafe Signature> <Cafe Public Key>
と書いてあってこれらを組み合わせて

<Cafe Signature> <Cafe Public Key> OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUAL OP_CHECKING
この検証scriptがTRUEになったら有効な秘密鍵をもっていることになるらしいです。

なんのことやらさっぱり? なので、具体的なデータをみてみます。

トランザクションID=07e8989e7bc46f87485b975f153a3887e76c3e63f0a0d88af70186bbf1ce8306

のInputの0番目が解錠条件を満たしているのかbitcoin-rubyは使わずにRubyの標準添付ライブラリ は使ってよいというしばりで検証してみます。
このトランザクションIDは本物のbitcoinで使われているトランザクションです。

BLOCKCHAIN
chainFlyer

このトランザクションIDは適当に選びました。

前提

bitcoindインストールして動かしておく必要があります。

require 'json'
require 'digest'
require 'bitcoin'
TRANSACTION_ID = '07e8989e7bc46f87485b975f153a3887e76c3e63f0a0d88af70186bbf1ce8306'
Vin = Struct.new(:txid, :index, :unlocking_script)
Vout = Struct.new(:locking_script)
def decode_transaction(tx_id)
JSON.parse(`bitcoin-cli getrawtransaction #{tx_id} 1`)
end
def decodescript(hex)
JSON.parse(`bitcoin-cli decodescript #{hex}`)['asm']
end
def get_vin_n(transaction, n = 0)
json = transaction['vin'][n]
unlocking_script = decodescript(json['scriptSig']['hex'])
Vin.new(json['txid'], json['vout'], unlocking_script)
end
def get_vout_n(transaction, index)
json = transaction['vout'][index]
locking_script = decodescript(json['scriptPubKey']['hex'])
Vout.new(locking_script)
end
def hash160(input)
digest = Digest::SHA256.digest(["#{input}"].pack('H*'))
Digest::RMD160.hexdigest(digest)
end
def check_sig(public_key, signature)
# https://en.bitcoin.it/wiki/OP_CHECKSIG
# 理解およばず・・・・・・
# https://github.com/lian/bitcoin-ruby 様のお力をお借りいたしました🙇
Bitcoin::Script.from_string(signature, "#{public_key} OP_CHECKSIG").run
end
transaction = decode_transaction(TRANSACTION_ID)
vin = get_vin_n(transaction)
vout = get_vout_n(decode_transaction(vin.txid), vin.index)
script = "#{vin.unlocking_script} #{vout.locking_script}"
puts script
# =>
# 304502210095787cb1ba8ebc48911c891924d62b8ff421accd40c2fba6070395fd772398f5022020f3df39b498b16eb61023f3947cb4386a75d68ed3628786c700f73e8743e54501 03d0dedbb2ca7e2c1aecd9d88dc36d1a101db20d70594ecfc1e34e0811541c44cb OP_DUP OP_HASH160 e667088e73ebfc2c6c3a5bc90ed29c3473f167a2 OP_EQUALVERIFY OP_CHECKSIG
script_ary = script.split(' ')
# 逆ポーランド記法を解く
stack = []
script_ary.each do |s|
case s
when /^OP_DUP$/
stack.push(stack.last)
when /^OP_HASH160$/
stack.push(hash160(stack.pop))
when /^OP_EQUALVERIFY$/
if stack[-1] == stack[-2]
stack.pop
stack.pop
end
when /^OP_CHECKSIG$/
stack.push(check_sig(stack.pop, stack.pop))
else
stack.push(s)
end
p stack
end
puts stack.first
# => true
view raw p2pkh.rb hosted with ❤ by GitHub

OP_CHECKSIGをまだ理解できず、bitcoin-ruby 様の力をお借りしました。
理解して書き直したいとおもっています。



0 件のコメント:

コメントを投稿