NEMを使った抽選プログラム

先週nemketというNEM上で使われる暗号通貨XEM決済オンリーのイベントが行われてましたが、そこでNEMのモザイクを利用した抽選企画が行ったので、その裏側を紹介したいと思います。

## NEMのモザイクで抽選をする方法

NEMのモザイクを使って抽選するには以下の手順で行うことができます。

1. 抽選用のモザイクを作成して、参加者に配る
2. 応募期間内に参加者に指定したアドレスにモザイクを送り返してもらう
3. 応募期間後に抽選プログラムを実行して、当選者を決める
4. 当選通知プログラムを実行して、当選者に当選通知を送付する

以下、一部プログラムを交えて紹介しますが、NEMのAPIへのアクセスに当たっては、@44uk_i3 が作成した[nis-ruby](https://github.com/44uk/nis-ruby)というライブラリを使っています。

## 抽選用のモザイクを作成して、参加者に配る

その気になれば、モザイクの作成もNEMのAPI経由で行うこともできますが、わざわざそんなことをしなくてもNanoWalletで作ることができるので、今回はNanoWallet上で作成しました。
なお、モザイクを作成する時はそのモザイクの用途に応じた設計が必要となりますが、今回は抽選プログラム用なので以下のように設定しました。

* 供給量: 参加予定数より多めに作っておく
    * 後から変更も可能だが、めんどうなので
* 可分性(小数点の有無): 0 (なし)
    * 抽選券を1.5枚, 3.14枚と扱うことはないため
* 譲渡許可: 可能
    * 指定したアドレスに送ってもらう必要があるため
* 徴収: なし
    * ありにするとモザイク送信時に特定の別なモザイク(XEMを含)を徴収できるが、なんの説明もなしに徴収するのは好ましくないので注意

nemket本番では主催者が手動でモザイクを配布していましたが、事前に行ったキャンペーンでは、主催者からアドレスのリストのみを受け取ってアドレスのリストを読み込んで抽選用のモザイクを配布するようにしていました。

```ruby

mosaic = nil
# モザイク定義取得のAPIを実行してnamespaceがdaokaで作られたモザイクを抽出する
mosaic_difinitions = nis.namespace_mosaic_definition_page(namespace: 'daoka')

# namespace "daoka" で作成されたモザイクの中から抽選用につかう "daokasweep" 
# というモザイクの定義を取得する
mosaic_difinitions.each do |md|
  if md.mosaic.id.name == 'daokasweep'
    mosaic = md.mosaic
  end
end

# address.txtに1行ずつアドレスが格納されているを1行ずつ読み込んで、モザイクを送っている
File.open("address.txt", "r") do |f|
  f.each_line do |addr|
    address = addr.chomp
    # 指定したアドレスに'mosaic send test'というmessageを付与してモザイクを送付する
    tx = Nis::Transaction::Transfer.new(address, 1, 'mosaic send test')
    tx.mosaics << Nis::Struct::MosaicAttachment.new(mosaic, 1)
    req = Nis::Request::Announce.new(tx, keyPair)
    res = nis.transaction_announce(req)
    tx_log_file.puts("#{address} : #{res.transaction_hash}")

    sleep(3)
  end
```

## 応募期間内に参加者に指定したアドレスにモザイクを送り返してもらう

こちらはA賞は`NAXXXXXXXXXXXX...` , B賞は`NBXXXXXXXXXXX...`といった感じに賞別にアドレスを作成して、参加者は応募したい賞のアドレスにモザイクを送信してもらうようにしました。

## 応募期間後に抽選プログラムを実行して、当選者を決める

抽選プログラムは以下のことを行い、当選者の決定を行いました。

* アドレス毎にトランザクション受信のAPI`/account/transfers/incoming`を叩いて、応募用モザイクが添付されているトランザクションを抽出し、そのアドレスを抽選用配列に追加する。(以下、主要なメソッドのソースコードを記載)

```ruby
# 指定したアドレスの受信トランザクションを取得する
def self.transaction_meta_datas(last_id = nil)
    nis = Nis.new(host: NIS_HOST)
    nis.account_transfers_incoming(address: ADDRESS, id: last_id)
end

# トランザクションには送金トランザクションだけでなく、モザイク定義トランザクションなど
# 色々なトランザクションがあるため、送金トランザクションのみを抽出する
def self.is_target_transaction(metadata: metadata)
  transefer_transaction = nil
  if metadata.transaction.is_a?(Nis::Struct::TransferTransaction)
    transefer_transaction = metadata.transaction
  end

  if metadata.transaction.is_a?(Nis::Struct::MultisigTransaction)
    if metadata.transaction.otherTrans.is_a?(Nis::Struct::TransferTransaction)
      transefer_transaction = metadata.transaction.otherTrans
    end
  end

  return false if transefer_transaction.nil?

  is_target_mosaic_transfer?(transefer: transefer_transaction)
end

# 抽選用のモザイクが添付されているトランザクションを抽出する
def self.is_target_mosaic_transfer?(transefer: transefer_transaction)
  return false unless is_timestamp_less_than_endtime(transefer.timeStamp)
  return false if transefer.mosaics.nil?
  transefer.mosaics.each do |mosaic|
    if is_target_mosaic_id?(mosaic[:mosaicId])
      if mosaic[:quantity] >= MINMU_MOSAIC_AMOUNT
        return true
      end
    end
  end
  return false
end

```

* 抽選用の配列から当選者分のアドレスをランダムに抽出する

## 当選通知プログラムを実行して、当選者に当選通知を送付する

当選者に対して、当選通知を行うのは抽選用のモザイクを配るときとほぼ同じプログラムでいけるのでソースコードは割愛しますが、1つNEMの特色として、トランザクションに1024byteのmessage領域を持っており、その範囲内で任意のメッセージを送ることが可能なため、「xxxxが当選しました！」というメッセージを付けることができます。