【Python】BitcoinSV用ライブラリ「bitsv」の中身を読み解く①

この記事は約12分で読めます。

 

BitcoinSVのブロックチェーンにPythonからアクセスすることのできるライブラリであるbitsvの関数の引数とか挙動を自分用にメモしている記事。このライブラリを使って何かしたい人には役に立つかもしれないし、ビットコインに自信ニキには今更な情報が多いかもです。

 

 

・初期設定

 

key = '自分の秘密鍵'
my_key = bitsv.Key(key)

 

ここでのkeyの変数に格納している秘密鍵は、
wallet.pyのclass PrivateKey(BaseKey):~  に飛んでいる

 

 

・.getbalance(currency=’satoshi’)

 

.getbalance()自分の秘密鍵のなかにあるBSVの残高をSatoshi換算で返してくれる関数。中でやっていることは、読み込ませた秘密鍵から所有権のある公開鍵を検出してblockchairのAPIで各アドレスの保有量を検索し、合計した数値を返している。

 

引数currencyはデフォルトは’satoshi’だが、ほかにも’jpy’、’usd’など各法定通貨建ての価格でも取得可能。その場合の各法定通貨のレートはhttp://bitcoinsv-rates.com/api/rates/ から取得している。

 

法定通貨換算の場合はまず①usd単位でのbsv価格の取得→②÷10*8でsatoshi単位のbsv価格に換算→③各法定通貨のusd建ての価格をAPIで取得→
④Satoshi単位のusd建てbsv価格×usd建て法定通貨価格の計算結果が戻り値で返ってくる

 

 

・send(self, outputs, fee=None, leftover=None, combine=True,message=None, unspents=None, custom_pushdata=False)

 

send()は標準のbsvトランザクションを発行し、送信する関数。基本的にはoutputをgithubの書式に合わせて設定して実行するだけでOK。

 

引数feeで任意のトランザクション手数料を設定できる。messageはトランザクションにメッセージを付与できるがその分余計なデータをアップロードしているので手数料があがる。

 

custom_pushdataは、ビットコインのプログラミング言語であるscriptに搭載されているOP_CODEのpushdata2、pushdata4でデータを送るとき(デフォルトのトランザクションはpushdata1を使っている)にTrueにするっぽい。

 

scriptにはデフォルトのpushdata1と任意のデータをアップロードするOP_RETURN以外にも様々なOP_CODEがあるが恐らくスマコンの時に使われるのではないかと予想。

 

send()は内部で
・get_unspents()
・create_transaction()
・network_api.broadcast_tx(tx_hex)
を実行し、戻り値はcalc_txid(tx_hex)の結果を返している

 

 

・get_unspents()

 

get_unspents()は、読み込んだ秘密鍵が保有しているウォレットアドレス(公開鍵)のうち未使用のモノを返す関数。

 

ビットコインのアドレスはプライバシー保護のためにUTXOという仕組みを使用しており、これは1回の取引ごとに秘密鍵から新しいウォレットアドレス(公開鍵)を発行し、送金した分以外の残りのコイン(お釣り分)はそこに全部移し替えるという、使い捨て方式になっている。

 

そのため、秘密鍵に紐づけらている公開鍵アドレスは常に一回も使われていない新鮮(未使用/unspent)なアドレスにコインが入っている状態になっている。get_unspents()は自分の秘密鍵がか保有している新鮮な公開鍵アドレスの情報を確認する関数です。

 

戻り値には、秘密鍵に紐づけられている各公開鍵ウォレットアドレスが[確認数、アドレス、インデックス、残高]の順番で格納された2次元配列が返ってきている。

 

 

・create_transaction()

 

内部でcreate_p2pkh_transaction(self, unspents, outputs, custom_pushdata=custom_pushdata)を呼び出しているだけの関数。

 

ちなみにp2pkhとは、Pay-to-Public-Key-Hashの略。

 

P2PKHはBitcoinがSegWitを導入する前に使われていた送金するためのスクリプトです。現在はBitcoinCash、BitcoinSV、Litecoinなどで使われています。

ビットコイン系通貨の送金のスクリプトの基本的なルールは、UTXOのoutputに含まれるLocking Script(scriptPubKey)を解除するためにUnlocking Script(scriptSig)をinputに入れて解除することです。このトランザクションに対して秘密鍵で署名してブロードキャストすることで送金が完了します。

上記を踏まえて、P2PKHはLocking Scriptに送金相手のPublicKey:公開鍵のRIPEMD160(SHA256(PublicKey))したハッシュ値を含むLockgin Scriptで送金することを言います。

参照:https://tomokazu-kozuma.com/explanation-of-bitcoin-script-p2pkh/

 

 

・create_p2pkh_transaction(self, unspents, outputs, custom_pushdata=custom_pushdata)

 

create_p2pkh_transaction()はp2pkhのBSVトランザクションを生成する関数。標準のトランザクションの生成はここがメイン処理っぽい?

 

中でやっていることのうちまず冒頭でやっている処理がこいつら。

# 秘密鍵から公開鍵を生成
public_key = private_key.public_key

# 生成した公開鍵の長さをバイト列で表現
# Pythonのデフォルト関数のto_bytes()で文字数をバイト列に変換する public_key_len = len(public_key).to_bytes(1, byteorder='little') # ビットコインのプログラミング言語「script」でのトランザクション # 発行コードをバイト列で表現したもの scriptCode = private_key.scriptcode #scriptのコードの長さを計算して整数で返している
scriptCode_len = int_to_varint(len(scriptCode)) 

 

最後のScriptCodeがよく分からなかったのだが、P2PKHの場合、scriptCodeはscriptPubKeyということになるらしい。要は↓のこと。

 

OP_DUP OP_HASH160 <pubkey hash> OP_EQUALVERIFY OP_CHECKSIG

 

ただこれをそのままブロックチェーンにブロードキャストすることができないらしく、ブロックチェーンにscriptのスクリプトをブロードキャストする際は各OP_CODEのコード番号(106とか)を16進数に変換したバイト列で表現してあげる必要があるらしい。

 

ビットコインのOP_CODEの各WordとかCodeとか16進数(hex)での表現方法は下記参照、ビットコイン(BTC)とビットコインSV(BSV)は、OP_RETURNがないとか若干違うので、正確な参考にはならないがイメージ的にはこんな感じ

https://qiita.com/j0hnta/items/4c55f3d906331389059d

 

 

ともかく、BitcoinSVのOP_CODEについてはbitsv内で↓のように定義されているので、これがBitcoinSVでの各OP_CODEのWordとCodeらしい。

 

tranzaction.py

OP_0 = b'\x00'
OP_CHECKLOCKTIMEVERIFY = b'\xb1'
OP_CHECKSIG = b'\xac'
OP_DUP = b'v'
OP_EQUALVERIFY = b'\x88'
OP_HASH160 = b'\xa9'
OP_PUSH_20 = b'\x14'
OP_RETURN = b'\x6a'
OP_PUSHDATA1 = b'\x4c'
OP_PUSHDATA2 = b'\x4d'
OP_PUSHDATA4 = b'\x4e'

 

 

wallet.py

 def scriptcode(self): 
    self._scriptcode = (OP_DUP +
                        OP_HASH160 + 
                        OP_PUSH_20 +
                        address_to_public_key_hash(self.address) +
                        OP_EQUALVERIFY +
                        OP_CHECKSIG) 
    return self._scriptcode

 

なので、._scriptcodeの中身はb’v\xa9\x14\x17(\xd7!\xaa\xfc\xf5\xd0j[\xf6\xaf\xb7\xd8\xc3\xcc\xa9\x8c)\x96\x88\xac みたいな感じの人間が見ると、なにを命令しているのか分かよく分からないものになっている

そして、create_p2pkh_transaction()の中身の戻ると
version = VERSION_1
lock_time = LOCK_TIME
# sequence = SEQUENCE
hash_type = HASH_TYPE
input_count = int_to_varint(len(unspents))
output_count = int_to_varint(len(outputs))
ここはさっきのscriptcodeとやっていることは同じ、あらかじめ決められているOP_CODEのコード番号を16進数のバイト列で表現しているだけ。
tranzaction.py
VERSION_1 = 0x01.to_bytes(4, byteorder='little')
SEQUENCE = 0xffffffff.to_bytes(4, byteorder='little')
LOCK_TIME = 0x00.to_bytes(4, byteorder='little') 
~# Python 3 doesn't allow bitwise operators on byte objects... 
HASH_TYPE = 0x01.to_bytes(4, byteorder='little')
# BitcoinSV fork ID.
SIGHASH_FORKID = 0x40.to_bytes(4, byteorder='little')
# So we just do this for now.
FIXME HASH_TYPE = 0x41.to_bytes(4, byteorder='little') 
##

 

 

そして、その次の

output_block = construct_output_block(outputs, custom_pushdata=custom_pushdata)
の部分はoutblockに使用するアドレスの残高とか諸々の情報をバイト列にして、それを格納して返す関数みたい。これの関数だけでOUTPUTはできているっぽく、create_p2pkh_transaction()の大半はINPUYの作成に費やされている。
OUTPUTはネットワークに送信するトランザクションをロックするだけでINPUT部分はそのロックされた電子署名を解読する処理とか行っているので、その辺がINPUT処理が長い理由かなと思われる。

戻り値は
bytes_to_hex(
version +
input_count +
construct_input_block(inputs) +
output_count +
output_block +
lock_time
)

 

そして、作成したトランザクションをビットコインネットワークにブロードキャスト(公開)する必要がある。

 

これにはBitindexというサービスのAPIを使用して行っているみたい。ここのAPIにPOSTするとビットコイントランザクションがネットワークに公開されて、Mempoolに保管され、マイナーによる認証受けたのちブロックに格納される。

 

終わり

 

今まで高レイヤーの部分ばっかりやっていたので、低レイヤー部分でよくある16進数とか2進数とかビットとかバイトの処理にぶち当たった経験がなかったので、今回のbitsvのライブラリの解析は非常にためになった。他人の作ったコードの解析をする時はVS_Codeは超便利。

 

まあこういうことをいちいち理解しなくても直感的に動かせるツールとかが出てきたらDappsとかブロックチェーン回りの開発ももっと加速するかなと思う。これから楽しみ。

 

 

コメント

タイトルとURLをコピーしました