クラウドレスで顧客の秘密鍵紛失リスクゼロなポイント運用をSymbolブロックチェーンで実現する

# はじめに

先日のSymbolハードフォークでモザイク生成時にRevocable属性を追加することができるようになりました。これによって、発行者側によるユーザに対して透明性のあるモザイク減算が可能になりました。

この変更における、私の考える最も大きなブレイクスルーは「**ユーザ側で秘密鍵を管理する必要のないユースケースが発生した**」ことです。

例えばブロックチェーンでのポイントシステムを検討した場合、今までであれば貯めたポイントを使用するためにはユーザが秘密鍵を所有しておく必要がありました。これはユーザにポイント操作の権限を帰属させることができるというメリットがある一方で、ユーザに秘密鍵の管理する負担を負わせるということでもありました。

これは従来システムであればサーバがポイント操作を一括管理しておけば済む話で、将来的にブロックチェーンの価値を活かしきれるかどうかも分からない未知の狭い経済圏で、秘密鍵の管理をユーザ側の負担にさせてまでする話か？という見方が大多数で多くのアイデアが打ち消される一因にもなっていたと思います。

しかしRevocable属性の追加により、ユーザは秘密鍵を所有せず、アドレスを店舗特有の物理的なもの（紙・カード）に紐づけて所有を証明することで、店舗側の秘密鍵でポイント運用が出来るようになります。この仕組みはクラウド環境さえ必要とせず、すべて店舗側のウォレットで完結します。

これはスマートコントラクトなどの開発コストが膨大にかかるブロックチェーンではあまり意味の無い話かもしれませんが、Symbolのように誰でも使えるブロックチェーンであれば従来のスタンプカードよりも低コストで運用できる可能性も見えてきます。


まさに昔、小さな商店街が紙やはさみでカードを作り小さな経済圏を生み出したように、手を伸ばせばだれもが手の届く場所にあるSymbolブロックチェーンを活用して、さらに可能性のある経済圏を生み出すことができるかもしれません。


# 解説の流れ

少し内容が多いので、先に流れを説明しておきます。

- （準備）ポイントの作成
    - ショップアカウントの作成
    - モザイク生成
    - モザイクIDの確認
- (新規登録)ポイントカードの作成
    - 顧客アドレスの作成
    - QRコード作成
    - ポイントカードに貼り付け
- (運用)ポイント運用
    - QRコードスキャン
    - ポイントをためる
    - ポイントを使う

なお、体験には以下の記事から検証環境を事前に構築しておいてください。

https://qiita.com/nem_takanobu/items/e88d5ea76cd609e76bbf

# ポイントの作成

#### ショップアカウントの作成

ウォレットでショップ管理用のアカウントを作成してください。
秘密鍵を控えておきます。


#### トークン生成

リボーカブル設定のモザイクは~~ウォレットではまだ作成できないので~~、スクリプトで生成します。
後記：Arcanaウォレットで簡単に作れるようになったようです！ 

https://nemlog.nem.social/blog/75382


```js
shop = sym.Account.createFromPrivateKey(
  "SHOPアカウントの秘密鍵",
  networkType
);

isSupplyMutable = true;
isTransferable = false;
isRestrictable = true;
isRevokable = true;

nonce = sym.MosaicNonce.createRandom();
mosaicDefTx = sym.MosaicDefinitionTransaction.create(
    sym.Deadline.create(epochAdjustment),
    nonce,
    sym.MosaicId.createFromNonce(nonce, shop.address),
    sym.MosaicFlags.create(isSupplyMutable, isTransferable, isRestrictable, isRevokable),
    0,
    sym.UInt64.fromUint(0),
    networkType
);

//モザイク変更
mosaicChangeTx = sym.MosaicSupplyChangeTransaction.create(
    sym.Deadline.create(epochAdjustment),
    mosaicDefTx.mosaicId,
    sym.MosaicSupplyChangeAction.Increase,
    sym.UInt64.fromUint(1000000),
    networkType
);

aggregateArray = [
    mosaicDefTx.toAggregate(shop.publicAccount),
    mosaicChangeTx.toAggregate(shop.publicAccount),
]

aggregateTx = sym.AggregateTransaction.createComplete(
    sym.Deadline.create(epochAdjustment),
    aggregateArray,
    networkType,[],
).setMaxFeeForAggregate(100, 0);

signedTx = shop.sign(aggregateTx,generationHash);
txRepo.announce(signedTx).subscribe(x=>console.log(x));

```

isTransferable = false;　でポイントを他人に譲渡できなくなります。
isRevokable = true;　でショップアカウントからモザイクの没収が可能になります。

#### モザイクIDの確認

ポイントの生成に成功したら、ウォレットやエクスプローラで確認します。
12桁のモザイクIDを控えておきます。

# ポイントカードの作成

ユーザごとにポイントカードを作成します。
新規顧客となるユーザが希望するたびにこの作業は発生します。


#### アドレス作成
新規にアカウントを生成し、アドレスを取得します。秘密鍵は破棄します。

```js
user = sym.Account.generateNewAccount(networkType);
user.address.plain();
```
将来ユーザーに秘密鍵を管理させるような仕組みを検討したい場合は、ブリッジするなりできるのでその時がきたら考えましょう。


#### QRコード作成

アドレス情報を含むQRコードを作成します。

```js

var addQR = new qr.AddressQR("user-id",user.address.plain());

addQR.toBase64().subscribe(x => {
  (tag= document.createElement('img')).src = x;
  document.getElementsByTagName('body')[0].appendChild(tag);
});
```

上記スクリプトを実行するとブラウザに出力されます。
user-id のところは店舗側のCRMシステムなどで割り振られたIDを記載してもよいかもしれません。



#### カードに張り付け

ブラウザに表示されたQRコードをよき大きさで印刷して切り取り、店舗オリジナルの紙カード等に張り付けてください。他人と共用されると困るポイントの場合はできるだけ複製が困難な台紙に張り付けるようにしてください。（後記：この作業は非常に重要です。利用者が他人のQRコードを上から貼り付けられないようにパウチなどするか、カードとQRコードを一緒に印刷するなど**店側が準備したQRコードであることをかならず分かるように**しておいてください）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29187/eeb5f39f-12a0-1136-4046-5257c37f0d49.png)


# ポイント運用

ここからはポイントの運用についての説明です。

#### QRコードをスキャンする

現在のウォレットではポイントの減算はできませんので、以下に例示するスクリプトへアドレス情報を渡すために、こちらの記事を参考にQRコードをスキャンしてください。

https://qiita.com/nem_takanobu/items/1de5ecb6713c2a5cb6c3

#### ポイントをためる

ユーザのQRコードをスキャンして取得できたアドレスを使って
ショップ用ウォレットでポイントをユーザに付与してください。

#### ポイントを使う

ショップ側からユーザのアドレスに紐づけられていたポイントを引き抜きます。
ウォレットではMosaicSupplyRevocationTransactionがまだサポートされていないので、スクリプトを実行します。

```js

json = JSON.parse(code.data);
userAddress = sym.Address.createFromRawAddress(json.data.address.address);
shop = sym.Account.createFromPrivateKey(
  "SHOPの秘密鍵",
  networkType
);

revTx = sym.MosaicSupplyRevocationTransaction.create(
    sym.Deadline.create(epochAdjustment),
    userAddress,
    new sym.Mosaic(new sym.MosaicId("16桁のモザイクID"), sym.UInt64.fromUint(3)),
    networkType
).setMaxFee(100);

signedTx = shop.sign(revTx,generationHash);
txRepo.announce(signedTx).subscribe(x=>console.log(x));


//https://testnet.symbol.fyi/transactions/A39BCCB30DD8A6FF062A95E865CBB2753472846DC9C5DDFF03C40BF24CB95FCC
```

ブロックチェーンですので、こっそり奪い取ることはできません。
透明性のある検証可能な形でポイントが移転します。

# さいごに

ユーザにアドレスだけ渡してポイント運用する方法を紹介しました。
トークンは生成者によって没収可能な形に設定されていて、非中央集権とは言えません。
ブロックチェーンのゼロダウンタイム性だけをうまく活用することで、
運用のハードルは下がったのではないでしょうか。

是非お試しください。