これはBlockchain Advent Calendar 2019 5日目の記事です。
こんにちは、そして初めまして。Yちゃんと申します。Twitterなんかでブロックチェーン...というよりかは暗号通貨関連の開発をしている高1です。今回は私が現在開発しているatomicswap-qtの基盤技術であるatomicswapに関して、技術的な詳しい日本語解説がなかったのである程度噛み砕いた上で説明していきたいと思います(なお、ある程度解説は入れたつもりですが、基本的にはそれなりにBitcoinの知識がある方向けですのでご了承ください)。私もまだまだ初心者ですので、説明に誤りがあると感じましたらコメントしていただければ改めます。
#atomicswapとは
atomicswapとはとある二者間においてトラストレスな暗号通貨の交換が行える仕組みで、別名cross-chain atomicswapとも呼ばれます。2014年頃には既に提唱されており、何度か行われていたようですが、Decredが初めて標準的な実装を公開し、Litecoinと行ったようです。MonacoinとDecredならびにBitcoinで日本初のatomicswapが行われたことを知っている人もいるのではないでしょうか。また、初心者から上級者まで、様々な人に適したマルチウォレットもにゃにおいてもatomicswapが実装されており、気軽に使えるようになっています。
#atomicswapの利点
誰も信用する必要がありません。取引所を介せず、取引相手さえ信用する必要が無いのです。取引失敗時は自分の資産を取り戻せます、ブロックチェーンの利用にあたって手数料がかかってしまいますが...。まあ全ての資産を失うよりかは断然良いでしょう。
#atomicswapの(技術的)仕様
さて、ここからが本題の詳しい(技術的)仕様です(後で読み返してこれ本当に技術的仕様と言えるのか怪しくなってきた)。Decredの実装に基づいて解説を書いていくことをあらかじめ言っておきます。
また、blockchain(α)と(β)が出てきますが、チェーンを区別するためのものです。Bitcoin blockchainとLitecoin blockchainだとでも思っておけばいいと思います。
atomicswapはinitiate, participate, redeem(initiator), redeem(participator)の4段階で成り立っています。取引失敗時の返金システムとしてrefundという段階が存在しますが、今回は説明上省きます。また、今後initiateする人をinitiator、participateする人をparticipatorと表記します。さらに、例として挙げるtransactionに関しては全て私が初めて行ったatomicswapの履歴を参照するものとします。
##initiate(取引の開始)
initiateは始まりという意味があるように、始めに必要な
- secret(participatorに直接渡してはいけない値)
- secret hash(participatorに直接渡してはならない、sha256でハッシュ化されたsecret)
- atomicswap contract(α)
- contract transaction(α、bitcoin transaction)
の4つの、以下のような部品を生成します。(その前にparticipatorに送金する額の指定と相手のblockchain(α)上のアドレスを教えて貰う必要があります)
Secret: 02021dcb827c9940b523ec185729e51a1bbf9b3a7df7a752f26310ab1924b8ae
Secret hash: 2698fce577ad2ade0d3b9e2fd84c843fcbbb50fd4d2aac39a0ef316cc2211715
atomicswap contract(α): 6382012088a8202698fce577ad2ade0d3b9e2fd84c843fcbbb50fd4d2aac39a0ef316cc22117158876a91472d9da25b748603a8fbdd12245bf69038fbad48f670430aa835cb17576a9148be09b1af9fae89de619caa11fb4898810bbb0166888ac
contract transaction(α): 0200000001f74292c26470dee6eb1241edcd650a27a5dc2a4f6ab2b58295d090fdf8a0215a000000006b483045022100be42bd7389a9fa11bdc3ac2f0ca6247feba358d2f522455636580c49e4d35fdb02207fbab2ebe7c44ebfc4d77f1fcc1092ce718ad9641bece200328d2e1fb58ef83b012102333d43d39d740072331b130aa772e478874c9e90ae8a408b28e31d976eb866cefeffffff02809698000000000017a914e8cc44db87e0c67c6662484d2c2ac2d4b07034d687a2495d050000000017a9144d1e1435aabe3958d56eec84a05d0be8324728238700000000
secretは暗号学的に安全な乱数で構成されたbyte列で、secret hashは前の説明の通りsecretをsha256でハッシュ化したものです。
atomicswap contractの正体はただのbitcoin scriptです。なお、本来のbitcoin scriptとは少し違う部分があります。
atomicswap contractは以下の要素で成り立っています。(説明は簡易なので一部誤りがあります)
OP_IF // もし条件を満たす、redeemを実行するときであるならば
OP_SIZE 0x01 0x20 OP_EQUALVERIFY // secretの長さが合致しているかを確認し
OP_SHA256 0x20 <secret hash> OP_EQUALVERIFY // secret hashが合致しているか確認できれば
OP_DUP OP_HASH160 0x14 <address hash> // participator宛のbitcoin transactionを発行する
OP_ELSE // 条件を満たせない、refundを実行するときであるならば
0x04 <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP // locktimeを確認し、locktimeを過ぎていれば
OP_DUP OP_HASH160 0x14 <address hash> // initiator宛のbitcoin transactionを発行する
OP_ENDIF // IF/ELSEの終了
OP_EQUALVERIFY OP_CHECKSIG // 発行されたbitcoin transactionをチェックする
これらはblockchain上で行われるわけではないのでOP_IFに必要な条件はcontract内には含まれていません。contractはあくまでプログラム内でデータを比較/取得するために用いられます。形だけみたいなものです。
2020/02/17追記
contractは実際にP2SHのトランザクションのscript signatureになるため、contractの一つ前にOP_TRUE(0x51)かOP_FALSE(0x00)を入れて判定されブロックチェーンにブロードキャストされる際に実行されます。形だけではありませんでした、お詫びして訂正いたします。
contract transaction(α)はatomicswap contractをバイト列に直したものを公開鍵として、それをsha256とripemd160(2つ合わせてhash160と呼ぶ)でハッシュ化した公開鍵ハッシュをBase58(Base64から読み間違えが多いものを取り除いたもの)でエンコードしたアドレス(例:PVp6dLP5o2DZoLck5rEh7xuNazEdN8MKKZ)に送金するtransactionです。先ほど例に挙げたcontract transactionを分解してみましょう。以下のようになります。(rawtransactionの分解(deserialize)に関してはBlockchain Advent Calendar 2019の1日目の記事を読むことを推奨します。)
分解したtransaction | byte数 | 意味 |
---|---|---|
02000000 | 4 | transactionのバージョン(今回は2) |
01 | 1 | tx inの数(今回は1) |
f74292c26470dee6eb1241edcd650a27a5dc2a4f6ab2b58295d090fdf8a0215a | 32 | outpointのtxid(これをlittle endianでエンコードすると存在するtxidになる) |
00000000 | 4 | outpointのindex、今回は0 |
6b | 1 | script sigの長さ(byte数)、今回は107 |
483045022100be42bd7389a9fa11bdc3ac2f0ca6247feba358d2f522455636580c49e4d35fdb02207fbab2ebe7c44ebfc4d77f1fcc1092ce718ad9641bece200328d2e1fb58ef83b012102333d43d39d740072331b130aa772e478874c9e90ae8a408b28e31d976eb866ce | 107 | script sig |
feffffff | 4 | sequence、little endianでエンコードすると4294967294 |
02 | 1 | tx outの数(今回は2) |
8096980000000000 | 8 | 1つめのtx outのvalue、送金金額(1e8で割って0.1) |
17 | 1 | 1つめのtx outのscript pubkeyの長さ(byte数)、今回は23 |
a914e8cc44db87e0c67c6662484d2c2ac2d4b07034d687 | 23 | 1つめのtx outのscript pubkey |
a2495d0500000000 | 8 | 2つめのtx outのvalue、送金金額(1e8で割って0.89999778) |
17 | 1 | 2つめのtx outのscript pubkeyの長さ(byte数)、今回は23 |
a9144d1e1435aabe3958d56eec84a05d0be83247282387 | 23 | 2つめのtx outのscript pubkey |
00000000 | 4 | locktime、今回は0 |
0.1の送金がatomicswapのための送金で、0.89999778の送金がいわゆるお釣りアドレスへの送金です。
initiateでは4つの部品のうちcontract transaction(α)をblockchain(α)へbroadcastします。そして、participatorにparticipate(参加)してもらうためにatomicswap contract(α)とcontract transaction(α)の2つと自身のblockchain(β)上のアドレスを相手に渡す必要があります。
##participate(取引への参加)
participatorはinitiatorからatomicswap contract(α)、contract transaction(α)を受け取り、それらをプログラムに入力することによってatomicswap contract(α)が正当なものであるかを検証し、secret hashを算出します(initiateのatomicswap contractの要素欄で説明した通り、atomicswap contractにはsecret hashが含まれているので、atomicswap contractがちゃんと書式に従って構成されているかを検証した後、secret hashを算出します)。
initiatorのsecret hashを元に、blockchain β上の通貨を下記のatomicswap contract(β)をハッシュ化したアドレスに送金するtransactionを生成します。この時は
- atomicswap contract(β)
- contract transaction(β、bitcoin transaction)
の2つのものが生成されます。
上記2つのものは先程initiatorによって発行されたものとは別のものです。
OP_IF // もし条件を満たす、redeemを実行するときであるならば
OP_SIZE 0x01 0x20 OP_EQUALVERIFY // secretの長さが合致しているかを確認し
OP_SHA256 0x20 <secret hash> OP_EQUALVERIFY // secret hashが合致しているか確認できれば
OP_DUP OP_HASH160 0x14 <address hash> // *initiator*宛のbitcoin transactionを発行する
OP_ELSE // 条件を満たせない、refundを実行するときであるならば
0x04 <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP // locktimeを確認し、locktimeを過ぎていれば
OP_DUP OP_HASH160 0x14 <address hash> // *participator*宛のbitcoin transactionを発行する
OP_ENDIF // IF/ELSEの終了
OP_EQUALVERIFY OP_CHECKSIG // 発行されたbitcoin transactionをチェックする
contractの形自体は変わっていませんが、アスタリスクで強調した部分がparticipatorに合わせて変わっています。contract transaction(β)はαとあまり変わりないので分解は省略します。
これら2つをinitiatorに渡す必要があります。
redeem(initiator、取引の引き換え)
redeemはinitiator、participatorがそれぞれ行わなければならず、最初にinitiatorが行います。これはredeemにおいて現状initiatorのみがもつsecretが必要だからです。initiatorは先程participatorから受け取ったatomicswap contract(β)とcontract transaction(β)、そして自分が持つsecretをプログラムに入力することで相手が送金したblockchain(β)上の通貨を受け取ることができます。この際、
- redeem transaction(α)
が生成され、broadcastされます。これには先程までinitiatorのみが持っていたsecretがscript sig内に含まれていて、これをparticipatorに渡すことでparticipatorはsecretを知ることが出来ます。では、contract transactionのように分解してみましょう。
分解したtransaction | byte数 | 意味 |
---|---|---|
02000000 | 4 | transactionのバージョン(今回は2) |
01 | 1 | tx inの数(今回は1) |
b6efa4478137f7e48f064a788470c9bfc12a66cd86c52817e9c06afc1c27c8a4 | 32 | outpointのtxid(これをlittle endianでエンコードすると存在するtxidになる) |
01000000 | 4 | outpointのindex、今回は1 |
f0 | 1 | script sigの長さ(byte数)、今回は240 |
4830450221009fd92c2e4860f871fa25e3f51825339ad6bdfe76f9f890201a231472ca047da202200754076b945773a7746b56fe16313b832fbdd8bc5660cc4c35ec74d5f9a18f170121032a61b95e2599c45936a6ca6a392461201f7fb69d14cdb001de8df52c5455c8192002021dcb827c9940b523ec185729e51a1bbf9b3a7df7a752f26310ab1924b8ae514c616382012088a8202698fce577ad2ade0d3b9e2fd84c843fcbbb50fd4d2aac39a0ef316cc22117158876a9147bf1955d8855c52490bc3d5457f457afe6e822f06704045b825cb17576a91425a5fef531dd352f709101f2f58ae159895ccf096888ac | 240 | script sig |
ffffffff | 4 | sequence、little endianでエンコードすると4294967295 |
01 | 1 | tx outの数(今回は1) |
60e4744817000000 | 8 | value、送金金額(1e8で割って999.99868) |
19 | 1 | script pubkeyの長さ(byte数)、今回は25 |
76a914e8652bca8b955a6268c754d66f1e358bd206852688ac | 25 | script pubkey |
045b825c | 4 | locktime、little endianでエンコードすると1552046852 |
redeem(participator)
今度はparticipatorのターン番です。まず、先程initiatorから受け取ったredeem transaction(α)からsecretを算出します。
次にinitiatorから前に受け取っていたatomicswap contract(α)、contract transaction(α)と算出したsecretを用いてblockchain(α)上の通貨を受け取ることが出来、この際
- redeem contract(β)
が生成され、broadcastされます。これにてcontractが成立し、取引が終了します。
まとめ
なんだかんだ言ってもっと詳細を知りたい方はDecredによるGoの実装を読むのが一番手っ取り早いと思います。ただ、これはいろんなライブラリを参照していて正直なところ読みにくいので私が今後公開する予定のatomicswap-qtのコードを参照してくれてもいいんですよ...?(Pythonでの再実装、個人的にはアホほどめんどくさかった)
まだ公開できるレベルのコードにはなってないので、Twitterなんかでメンション飛ばしてくれればプライベートリポジトリに招待します...(なお、あらかじめ言っておきますが多分クソコードです。本格的にコード書くの初めてですしおすし。)
2020/01/12追記
atomicswap-qtを公開しました