サブマリンスワップのコントラクト2
以前の記事でサブマリンスワップのコントラクトについて解説しました。その中でスワップインとスワップアウトのコントラクトの差異について触れましたが、その理由が分かったので記載したいと思います。
まずスワップインとスワップアウトの流れは以下になります。
スワップイン
- ユーザーはインボイスを生成する
- ユーザーはオンチェーンへ資金を送金する
- サービスプロバイダーはインボイスへ支払いをする
- ユーザーはインボイスへの支払いを決済する(この際プリイメジが公開される)
- サービスプロバイダーはプリイメジを使ってオンチェーンの資金を引き出す
(スワップインのコードはこちら)
スワップアウト
- ユーザーはプリイメジとそのハッシュを生成し、ハッシュのみサービスプロバイダーへ渡す。サービスプロバイダーはホドルインボイスを生成する
- ユーザーはホドルインボイスへ支払いをする
- サービスプロバイダーはオンチェーンへ資金を送金する
- ユーザーはプリイメジを使ってオンチェーンの資金を引き出す
- サービスプロバイダーはプリイメジを使ってホドルインボイスへの支払いを決済する
(スワップアウトのコードはこちら)
スクリプトの差異について
上記の2つのスワップで使われているスクリプトには小さいけど大きな違いがあります。それはプリイメジの32バイト長チェックです。このチェックはスワップアウトのみに必要です。このチェックがない場合、悪意あるユーザーは不正ができてしまいます。ユーザーは32バイト長でないプリイメジ(例えば10バイト長)を生成し、そのハッシュを渡してサービスプロバイダーにホドルインボイスを作ってもらいます。ユーザーはそのインボイスへ支払いをすると、サービスプロバイダーはオンチェーンへ資金を送金します。ユーザーは10バイト長のプリイメジを使ってオンチェーンの資金を引き出します。この際、サービスプロバイダーはプリイメジを知ることができますが、ホドルインボイスへの支払いを決済させることができません。なぜならライトニングネットワークで決済させるためのプリイメジは32バイト長と決まっているからです。
スワップアウトの手順1では、プリイメジの作成をユーザーへ託し、そのハッシュのみを受け取っているのでサービスプロバイダーはプリイメジの長さを確かめるすべがありません。そのため、オンチェーンの資金を引き出すためのプリイメジの長さを32バイト長であると制約を課するためにスクリプトには32バイト長のチェックがあるのです。
スワップインでのスクリプトにはこのバイト長チェックはありません。なぜならライトニングネットワークで決済をするのはユーザーであり、かつユーザーが最初にオンチェーンへ資金を送金しているからです。そしてサービスプロバイダーがインボイスへの支払いをして決済される、ということは32バイト長のプリイメジが公開されるということなので、そのプリイメジを使ってオンチェーンの資金を引き出します。オンチェーンの資金を引き出すためのプリイメジは32バイト長である必要もありません。
直感的にはスワップインもスワップアウトもその取引をする当事者が入れ替われば同じスクリプトでも問題ないと思われます。スワップインでユーザーとサービスプロバイダーを入れ替えて読み替えてみてください。ユーザーはスワップアウトをしていることになります。確かにその通りなのですが、その場合悪意あるユーザーはサービスプロバイダーにオンチェーンの送金のみさせて取引をキャンセルする、オンチェーンの手数料のみ支払わさせるようなDos攻撃をされてしまいます。そのような悪さを低減させるために、どちらの取引も最初に送金をするのはユーザーになっています。スワップアウトではユーザーが最初に送金をしていますが、ライトニングネットワークでの送金はキャンセルができるので、手順4でユーザーがオンチェーンから引き出しを故意にしなかった場合、サービスプロバイダーは無駄にオンチェーン手数料を払うことにはなります(ただしBoltzではこのような攻撃を低減させるためにユーザーにオンチェーン手数料分のみ最初に支払わせるようなオプション機能があります)。
以上、サブマリンスワップのスクリプトのチェック機能について解説しました。このチェックは一見大したことないように感じますが、このチェックがない場合取引で資金を中抜きされてしまいます。何気ないチェックですがとても重要なことが分かりました。