ステラ(Stellar)を使った暗号通貨システム -その4-

前回の記事では、商店街で共通利用できるポイントの仕組みに、裏側でブロックチェーンを使って実現するためのフローをご紹介しました。

  • 事前準備1. 店舗アカウントの作成
  • 事前準備2. ユーザアカウントの作成
  • 実際の買い物シーン
  • 決済額に応じたポイント支払い

ここでは、その各フローにおいてブロックチェーンがどのように動いているかを具体的にみていくことで、ブロックチェーンを体系的にイメージしていく一助にできればと思います。

事前準備1. 店舗アカウントの作成
> 2. 暗号通貨システムが、サイトのアカウントとペアとなる暗号通貨システムのアカウント(=公開鍵)を一緒に作成する

ECサイトの店舗アカウントが”shop01@example.com”だったとします。
ブロックチェーンのシステムにおけるアカウントは公開鍵と呼ばれる可読性のない乱数型となっており、メールアドレスのような形式を利用することはできないため、これと一意に紐付けができるよう、フロントサービス(ECサイト)でのアカウント作成のタイミングでこの発行もおこなっておきます。

公開鍵は鍵ペアとしてセットで払い出される秘密鍵によって非可逆で復号できますが、その逆はできません。
(内部的にはEd25519というデジタル署名アルゴリズムを使っており、内部実装はこのようになっています)

そのため、秘密鍵さえ安全にアカウント作成者に渡せれば、本システムのようなアーキテクチャであれば公開鍵はシステムが保存して、メールアドレスのようなサービス上のアカウントと関連付けて管理することも可能です。

ただし、決してシステム側で秘密鍵を保存してはなりませんし、その秘密鍵の保存はユーザ責において厳重管理を求める仕様でなければなりません。
通常のアカウントにおけるIDとパスワードのような複合要素とは異なり、鍵ペアは前述の通り秘密鍵単体から公開鍵を複合することができるため、秘密鍵の漏洩はIDとパスワードが一緒に漏洩すると同義となります。
銀行口座でいえば、口座番号と暗証番号をいっぺんに盗まれるようなものです。

そのため、システムアーキテクチャとしては、必ず一度はシステム側からネットワークを通じてクライアント側に送出しなければならない都合上、通信経路はHTTPSによる暗号化が必須になります。
(より上位のソリューションとしてはペーパーウォレットやコールドウォレットなどがあります)

例えばStellarでは、以下のようなコマンドで鍵ペアの生成や公開鍵の複合がおこなえます。

stellar-core --genseed
    Secret seed: SSYR2T6S4HRMHXJW3HYJ4SWO6IU7QVP7QOANLDLTZBLESN5UM6UGQWYJ
    Public: GSYKAAQJBS4T66JM5AJBLAAGZTJOWH7FPGSO7JT5DFLDAFRI4YGLW5FC

stellar-core --convertid SZYR2T6S4HRMHXJW3HYJ4SWO6IU7QVP7QOANLDLTZBLESN5UM6UGQWYJ
    Seed:
        strKey: SZYR2T6S4HRMHXJW3HYJ4SWO6IU7QVP7QOANLDLTZBLESN5UM6UGQWYJ
    PublicKey:
        strKey: GSYKAAQJBS4T66JM5AJBLAAGZTJOWH7FPGSO7JT5DFLDAFRI4YGLW5FC

これをシステム側で
・店舗ID: 1
・店舗アカウント: shop01@example.com
・店舗パスワード: ****** (非可逆暗号化で保存)
・公開鍵: G****** (平文で保存)
・秘密鍵: – (決して保存しない)
として保存することで、サービス上のアカウントとブロックチェーンのアカウントを紐付けて管理できるようになります。

事前準備2. ユーザアカウントの作成
> 4. 暗号通貨システムが、サイトのアカウントとぺアとなる暗号通貨システムのアカウント(=公開鍵)を一緒に作成する

前述の店舗アカウント同様、買い物客(エンドユーザ)のアカウントにも一意に紐付けた鍵ペアを発行します。

ブロックチェーンにおけるアカウントは、そのアカウントが店舗側(toB)アカウントなのか顧客側(toC)アカウントなのかの区別はありません。アカウントとして持つ属性情報は鍵ペア以外にありません。これが匿名性が高いといわれている所以です。
以前のCCでNEMがハッキング被害にあった件も、その犯人となる公開鍵やロンダリングされた公開鍵もすべて把握できているのにも関わらず、個人を特定するための属性情報が何もないことから、その犯人が誰なのかは結局分からずじまいでした。

アカウントの属性どころか、内部実装としては、分散サーバで構築されたコア(=ノード)1台1台に割り当てられるネットワークアカウントのようなものも鍵ペアが利用されますし、そのシステム全体で一意となるルートアカウントや、イシュアーアカウントと呼ばれるアセット(仮想通貨)の発行(現実世界でいうところの造幣局の位置付け)のアカウントもこの鍵ペアで動いています。

語弊を恐れず書いてしまうと、ブロックチェーンのシステムは秘密鍵さえあればなんでもできますが、逆に秘密鍵がなければ何もできない(傍観しかできない)仕組みなのです。

実際の買い物シーン
> 買い物客に金額(とポイント)を提示

ここではブロックチェーンは絡みませんが、一応補足しておくと、たとえば10%のポイントがつくサービスであれば、1,000円の買い物に対して100ポイントが還元されます。
これをECサイト側の機能として関連付けをおこない、POSレジがあれば店舗が入力した購入金額にフロントサービスで一定の比率でポイントが算出され、それをデータベースに登録しつつ、買い物客に提示されるような仕組みをイメージしていただければと思います。

そこで提示された金額がQRコード決済になっていれば、ユーザは以下の処理でポイントの充当を受けることができます。

決済額に応じたポイント支払い
> 7. 1の店舗はそのスマホの画面からQRコード(例)を読み込み、レジで会計をおこなう(現物通貨は旧来通りの手渡し)
> 8. 7の会計要求が暗号通貨システムに送られ、金額に見合ったポイントが3の買い物客に支払われる

8がブロックチェーンの支払システムの要となるフローです。
まず7を説明すると、上記で提出された金額(QRコード決済)を買い物客が支払います。
支払う現物通貨はここではキャッシュでも構いません。
重要なのは、買い物客が自分のスマホ等であらかじめECサイトにログインしておいたアカウントで、このQRコードを読み込むことにあります。

前述の通り、このシステムではサイトのアカウントとペアとなる暗号通貨システムのアカウントを紐付けているため、そのユーザはQRコードを読み込んだ時点で、QRコードに記載された決済IDから上記フローで登録しておいたデータベースを照会しにいって、そこに記録された店舗アカウントに対してpush通知をおこないます。

これを受けて、POSレジ(店舗側)はポイントの支払を承認(あらかじめPOSレジにUSBで秘密鍵が記録された媒体(コールドウォレット)を自動で読み込んで、店舗側は”OK”ボタンを押すだけ、のようなイメージ)をおこないます。

これが8の会計要求にあたり、push通知時に送られた決済IDを元に店舗側からデータベースに記録された買い物客の公開鍵に対して、10%に相当するポイントを、承認時に受け取った揮発的な秘密鍵を元にブロックチェーンに決済要求を送り、これをレジャに記録して(かつ、分散サーバにコンセンサスの承認要求を送って一定の閾値以上の合意が得られれば、つまり不正なシステム改ざんがなければ)、決済完了の応答を買い物客に返却します。
買い物客が今手元でQRコードを読み込んだスマホ上に「100ポイントがチャージされました」のようなメッセージが表示されるイメージです。

ブロックチェーンの台帳上には、いつ、誰が(店舗アカウント)、誰に(ユーザアカウント)、何を(ポイント=ブロックチェーンでいうところのアセット)、いくら(額面)支払ったかが、改ざん不可能な分散台帳として記録されます。

これを利用することで、会計時に買い物客がすでにポイントを保有していれば、これを上記の公開鍵を逆の向きで実施することでポイントを利用した決済が可能となります。

このときの実際の、内部的なプログラムも紹介しておきます。

server.loadAccount(srcAccountId).then(function(account) {
  var transaction = new StellarSdk.TransactionBuilder(account)
    .addOperation(StellarSdk.Operation.payment({
      destination: dstAccountId,
      asset: asset,
      amount: amount,
    }))
    .build();
  transaction.sign(srcKeypair);
  console.log(transaction.toEnvelope().toXDR('base64'));
  server.submitTransaction(transaction)
    .then(function(transactionResult) {
      console.log(JSON.stringify(transactionResult, null, 2));
    })
    .catch(function(err) {
      console.error(err);
    });
})
.catch(function(e) {
  console.error(e);
});

---

Paying asset: Asset {
  code: 'PNT',
  issuer: 'G**********'
}
loading account... G**********
"hash": "1*********9",
"ledger": 2*****3,
"envelope_xdr": "A********Q=",
"result_xdr": "A********A=",
"result_meta_xdr": "A********A"

(leadger(台帳)の”2*****3″ページ目に”1*********9″というハッシュでブロックが記録されたことが通知されています。金額や時間はXDRという出力形式を復号することで確認できます)

ちなみにこのプログラム内の transaction.sign(srcKeypair) に複数の署名を指定することでマルチシグと呼ばれるセキュアな決済方式を導入することができます。

これは万が一手元の秘密鍵が漏洩しても、複数署名形式にしておくことで、単一の秘密鍵では帳簿の更新系操作ができないような仕組みの導入になります。

transactionには、ここではStellarSdk.Operation.paymentひとつだけですが、双方向取引や1:n取引のような複数の更新系操作をまとめてひとつの分離不可能であるアトミックな要求として、submitTransactionに指定することで実施できます。
これはつまりオールオアナッシングの処理で、双方向取引で片方だけが成立して片方だけが不成立というような制約条件の反故を防止する仕組みになります(データベースのトランザクションと同じACID処理です)。

いかがでしたでしょうか?

後半は少し専門的な説明になりましたが、こうして非フィンテックな業務系にもブロックチェーンの改ざん不可能なデータを利用したサービスへの活用ができることがイメージしていただければ幸いです。

なんといっても、分散サーバ型で構築すれば、間違えて事故や漏洩があった場合はもちろん、導入検証やサービスのライフサイクルが完了したらサーバを落としてしまえば、無害化できるため、中小規模の事業やサービスの用途であれば、リスクをヘッジした現実的な運用が可能かと思います。

以降はまだ分かりませんが、SCPや3f+1のような、もう少し中核的な技術の部分の紹介に入っていければと思っています。