IOTA:【実践】APIでウォレットを実装。受送金と残高計算。

　フルノードを最新版v1.4.1.6にしてから調子が上がってきたのでAPIを本格的にいじってみた。今までの記事とは違って、実践的な記事にしていきたい。

# 環境構築
　大きく分けて２つ環境構築のステップがある。
>1. フルノード構築
>2. ライブラリ用意

　このうち一番厄介なのは1のフルノード構築だ。2に関しては、今回はJavascriptのNode.jsを使う。

## フルノード構築
　フルノードを運用するために必要な手順は以下の記事で簡単にまとめたので参照してほしい。
> [IOTA:【随時更新】最速でフルノードを立ち上げる。](https://qiita.com/ABmushi/items/6b8413c1fb510e716d58)

　さて、フルノードを立ち上げてもAPIリクエストするためには、フルノードを同期させるところまで完了する必要がある。フルノードの同期とは、
>1. `latestMilestoneIndex`と`latestSolidSubtangleMilestoneIndex`のフィールドの数字が同じである。
>2. この数字がSlackの#botboxチャンネルで更新されるマイルストーン番号と一致している。

　この手順が厄介であるがゆえに、多くの人がIOTAに手を付けずにいると筆者は考えている。

*注）同期させなくても実行できるAPIリクエストはあるが、送金受金などお金をやり取りするAPIは同期なしには実行できない。本記事はウォレットを模倣することを目的にしているため、あくまでフルノード同期を前提に話を進めていく。*

## ライブラリの用意
　フルノードを走らせているマシンにライブラリをインストールしよう。今回はUbuntuのマシンを前提に話を進めていく。
### Node.jsのインストール
```shell:Node.jsのインストール
sudo apt-get update
sudo apt-get install nodejs
```
### npmのインストール
```shell:npmのインストール
sudo apt-get install npm
```

### IOTAのライブラリ
https://github.com/iotaledger/iota.lib.js

```shell:iota.lib.jsのインストール
npm install iota.lib.js
```

　ここまでペタペタペーストでささっと準備完了だ。フルノード立ち上げまでが環境構築の難所だった。

# 基本API
　まずAPIをマシンが認識して正しく動くかの確認のために`getNodeInfo`リクエストで、自分のフルノードのステータスを表示させてみよう。
　コードの保存ディレクトリだが、`~/node_modules/iota.lib.js/examples/`を使っていく。

```js:basic.js
var IOTA = require('../lib/iota');

//  ポート番号はiri.iniで設定したAPIポートを使う。
var iota = new IOTA({'host':'http://localhost','port':14265});

//  おなじみのgetNodeInfo API
iota.api.getNodeInfo(function(error, success) {
    if (error) {
        console.error(error);
    } else {
        console.log(success);
    }
});
```
　これを`node basic.js`コマンドで実行すると下記のような結果が返ってくる。

```js:結果
{ appName: 'IRI',
  appVersion: '1.4.1.6',
  jreAvailableProcessors: 4,
  jreFreeMemory: 781752912,
  jreVersion: '1.8.0_151',
  jreMaxMemory: 1993867264,
  jreTotalMemory: 1993867264,
  latestMilestone: 'SOETRTUZATLQHS9KJGPOJQSWBJUUDPNLSZCAGAGASJSMZPJUMAJWMSACWLISMKHTXUZUAQQARBOY99999',
  latestMilestoneIndex: 322044,
  latestSolidSubtangleMilestone: 'SOETRTUZATLQHS9KJGPOJQSWBJUUDPNLSZCAGAGASJSMZPJUMAJWMSACWLISMKHTXUZUAQQARBOY99999',
  latestSolidSubtangleMilestoneIndex: 322044,
  neighbors: 4,
  packetsQueueSize: 0,
  time: 1515351225845,
  tips: 4549,
  transactionsToRequest: 2,
  duration: 1 }
```

　'latestMilestoneIndex = latestSolidSubtangleMilestoneIndex'が**#botbox**のマイルストーン番号322044と一致している。準備完了だ！。

# 受金
## Private Keyとは
　81トライトのSeedから生成するのがPrivate Key。ウォレットを作る際はAPIが内部で自動にやってくれるのでこのステップは必要ない。

```js:privateKey.js
var IOTA = require('../lib/iota');
var Signing = require('../lib/crypto/signing/signing');
var Converter = require('../lib/crypto/converter/converter');

var iota = new IOTA({'host':'http://localhost','port':14265});

var seed = 'ABMUSHI9999';

var index = 0;
var security = 2;

// トリッツに変換
var key_trits = Signing.key(Converter.trits(seed),index,security);

// トライトに変換
var key = Converter.trytes(key_trits);

//　Private Keyの長さを表示
console.log('key length: ',key.length);

//　Private Keyの表示
console.log('Private key: ',key);
```

```text:実行結果
key length:  4374
Private key:  UVJBFCMASCXNLDCUS9IGZSNUHZSZU9RLMLENNSKLNDLIIWNXSFKBPEECY9CYBSHJXACOVOMGW9SXZIHTZTEYCYJXWT9ZJLFPYFMCONZJH9ATMGTKUJBNLOKUYLUTMZJWTVMKOLLY9IIWQUYBFYYMXTCTAIE9VNJZNC9TAQKXXNGEVRPGBWTXXDCTJZSOTGAINIPMSSJTBHMKMURJETYTF9BEMOKKEJWCLTUVILTBQ9HYHSIKYLBBVS9VKFSJWVNY9QQEAZTGRUQU99DAXBYSJJ9RNOLKYWSHPRGFIFSSKDRWV9SWAPURLXMKWTSA9SVTZGNZVTEDBVNKVRROFURKETEAS9KGMOSZZYDFQVBNWOCHGNPBJ9SNM9GJXNMLYTBSXCARFYXCKDLAXTRESCOMCFMZLFIP9BXKOTIGWMOJFIIUOJIFRIJPSQUDTHHOFYBSPTXMHRLLSXVGZLLGXOQXEVYN9LAWBLCEX9SDECBQKRBEOXUHZGBFZZQYLQVOPEMOEADFLAKSFOPJKVCWJH9ATREUFC9NFSYPGMBGTVKPUVX9NVK9OZEKPVBZWMSNICHHNCGXDPJKCTLKJEYUOQDLYXSUFK9HAZKJFYYJBALRNCXQQPVGFYYUBJWQMNPVQCKFHZERQWXDHKBUOVKCHWUYITKMYYRMZQFURMNRVTBPQOPWTDIRDWZL9IQNEDDHVMEIBYYQHBDUTVNFTAGNBINBASTMYPFTJP9ANCUAMMAHVFFBQGQVWPBSQZPZYVYLUBLH9JISTTCYUOYKWTSKDWVJLJGR9TY9ZDAP9UZZRWFSCYDHZHPEVFAOBFWLFPFYKUDCVEEINJT9XLZQZQTRZCPKGBBIDPEFGQGTN9KEWAYVOGNWEZMBUQHLJKDJWTDAMCICBUHSKXIRGJMPZETYOOAER9JAMTPTGGFG9RFZRBZVWHTQYJVHH9OHBXW9PRWYGKWCEGIERLWZEZRAIODDMCCKFKEYWSKTRPLPYXVPIUVJG99Q9UMAISMVD9EHMWMNWNKSUFSCGMBQYPEGFCOZMDFVHOCHMAFOBKHEZPJWUSMLDKDTSQIYUUVJQKPBCJCFVQTKHPABVS9QXQZCYXFLSCKELAAQOBBKZJJSUISEQGEDSODEKCKGBHYNUBPGODVEGVXOODG9HSLJFSNTTWYJY9HKZYNNQSAZXNFINCBFCBS99WTXVZNDPPSBMVRKDNFOJZCMGLWTUHKGXBZUHHH9SHOVACQWVGCLEACVXKZSUJOFNQYFPGTZRTNHPUKNHQFACBOOROHITJGMFTZYZXE9MHSCUHRVAURXIIWPFIUWVQPVYZWCFBHPQOIQRBNQWAPNTLINAPFUQXOKML9WZNTIUSXBIVVPLTWWXNDQWYPKPANVZFSWPGHRDLKTBKM9LVMNW9VXQGHCRYVYKLDABIBUUFGQXDWJPMUJRCCQDUZRZLHDTNQEHDKEHAMMOMX9RBTXNZANIOCZSZPIFIYQGLXCMMYPCVCOY9IENHFXBRKDWYSVSVVVUDBBHUZDDTYWFWPABELGRCYVCBNPLCTSVUGMOEDFOXYASGCNCHJWM9QTEEATNATRBSHPSSR9PNROLOKSZOHJ99VVUFFKZOTIQRKEJEEYRJVFVOJBLDPSAHOERXJYKVWWQTAMJNQ9GQTIMUGSHNJGGLWRNFKGOIMMEBIYCIIVCPVRWHQFRVOOQWKICECNHWYKCECZZNUEPQNMGNPMUECYVMIEVNUFYJZLZB9USRIPDYZKZONICFRCWVLUWDPVFNLUMIZOBYPGAZANDKZEIGWRNSKBPLQSYGVMNKARJW9WGAYSESIVIAFGEBAPVYFBRUSDTRNKOKGHUDLAIJPBVXEIRBPKW9ZMVYTTYZZTAFNRVYQMMJKBTHTQGOSRWTBHG9Z9DS9YILJSKHCDEZJSNYGHIDTO9WYPRGBBPVKMBKGEZCCDPJVLKUFCBTGQNYW9HKZBYWKMBFICNNVDJTDJNJLVWRRDHEFOJDETKDUQE9MXFUDPXYAAFQYIKRFPPAHMBOPBRACFMIRTJVNU9EWXOLRJCETWCDOVRXDGGBZOQHWBXXULMKOJHWOJJSDLQJ9VTWGCABHZCFKSJC9GQW9RVNXZDFUDYJOSUNB9SLMJHPCXRT9EZGNZJUOWMEXWQL9HP9SJOBIHOPJHBXIIHIL99MXIINCBJGASDVZCVZXFOMQALRATKXIOZTLIHYFKNMX9CXZIMFFPIENKMRLYFCHP9ZYCHVZTJKLAP9QRQGZYVFXRH9BKIBR9EZATZTHEWI9LCROAZIJKDQHNUYSMPQVJUQFF9MDKQYZJYYBXSPNTMEPGEWQZPLBAKBXYWSVSNVAFIBVHWIFIRKVNLFGULQTIOOOPVUKGBHXFCMDYMIKUMGQHKSEEQ9WGLYTF99VBFLCWN9CDKUJYYRKINXDKVECSQZZAW9CFVLPSXOZPHNREYWRZZDMTOBXJINVEWKYIQXJOZHEKUFNKLDVDURSPHUULPMAAWVDBXBXXQIMSVKDQVVYHD9AXDHSYSOG9EVOBYVBPOWSPLPEEES9HDJZOPOBGSJJGEDMSWAESIWCWBABEKLFRXLXIJHKXQOVWXR9UBIGPW9TSVVNOKEBLBKTESLIWBTTSJQFNBGJVJHXNRFHQKLRHJOBOQGBNTTVNQVYPSFYVXJKTYLFXDRVMRIZRTKRRQTWALGDRNUAXBTRBYYOX9TGMJSQV9BFZTPYMYXKIMMTHXBMDKYSBGTQAUDTIRPQLDIXEOCEUGRXQREZUIYIEEDWBFPRRKWEYT9BLFUBIBWGPIBHNTVLCQBWMW99IHSXWBDYBWUKFLMHVWSKHUPHRBV9JNHGZTWPMMCEMEBAFPQATVU9BUOPDVSPOTHNWKPZIKPGVY9FTKVSJTQYOKKAJIZEVLSCASTGRILOIKMKWEPVRZGRBSNYHCZZMZAUTNGP9OWPXNMDZNLEVBWZFASFSWOSERMNADAXBRHGIIZKLUVBPUEFVEGMCCVDDWRYLWLBOASTXTUYXXKENRXRHGFTJRBGFIXVBUPNVLRDZJMCKQFMPOEQTKSGFTMDTVIXP9LHME9XBRIDXXZNYVSMPPHBVFDPWVYZMOPSWIBTIOIDSDJSOOKJZBOMZGSJWPIJAIUYXVTGICIU9UBDPBRDSDZDTVQVXTPYTXHLSKTAUXSSQWRMHHQULPAQQNPRAGWURBKMUBYSKDPTIOSPVFFUFQC9SEWKFRKDVANAJQPGOZLKHSMBYQSWLUCODZYYBWQBUAPX9GLTPYSAEKOGBUROOHOGBWHEEGPROUPSEFVUYQSLCMOKLEXM9CQCEOECGRJMKCPYISWDAGMZUEBFYFMBZXUWYOPQSWTFMVD9ABFVCCQYTOKTGEJIFKTXAEKQXI9CELLMOFKIOEZGCQGFKAWVEBRPCEG99KP9LCBMIZFOQCVI9NPMXO9XZXUJPDNACESVMCNPNWUGAOMHDKYCWBITYJOSIPCIMFRZDKEDLDJEVRGEBDGMUTJAE9JKUYSIPUKVIGFEOTDBZTORFASTGNRFLRABOPTLH9UZVBPHSSHJOSEEKDGSBCF99ABNWK9QJSBZWVKJSFXSYNOCWEYUURVBFOBDFCNQLHQQKDOBAG9VFVCJICU9YRXZH9KPPEUBTQGUZMFDWCQWNAYBQHWVLJRTCAMZKFXNMZGDIKLLOHCTTFTBR9YKVINGSCPZNLPKTAFYGKQPMCCY9UJFIHMYHJ9ZZRWXU9AUEDCAKDJGLQ9QTZKRZGQQWYWXLXDUTVITAH9AMUNZCVPMMGQVJAFCRXFVRHB9JZRLILNBYKTFBNNWSMJCHOVMWAVLZHJMOD9ZJPR9HAWOYJJKYWSGDYFBHGMHVZEMOJNQHQKFFWVHF9UMBZ9SGXGAZXDPHUUIBXTTAHRT9LVDUGDAFLMTMI9HPWUOIRKNMLZLERKODBJTNUXFVSTWFAOXUHG9NAIMFRMZAAMNLJMVUOWCCBQBUZJEJZVAYZJEFKFLODHAPQDREMEVNZQIIFEWJVLIWLIXKSHOTAWPLTJXUMXIZRTTNLJS9MYHWGILCRXRGJIOYXOAOV9USEJ9TEKKPANKHOHDILEAGMFYI9NODDYBTPJOXJDMPHETFKDATSUHTCG9HLREDDUYSJKADJABAXQDXWW9SHITXVVANNIPVVJBGJRXQW9CIEXLKPAMUHWLJUMWZUHKOP9SFWCZABNTVRTF9NDIADOFAFXQRHTFTSWICWMXSMPJXIXYPBUNPJEPIV99ESXGBBKAERG9NWSABEGONCYFLUTLFGYFQINDSERGHODZACEXJLVGQTTIUAWQDIJJUEADGR9RVZZNYRN9MIHTLKDZGKKGQSVSJGNNEXKSLLVLFSDTGUVDMECAP9CUO9X
```

　なにやら長い文字列が表示されたと思う。長さは**4374**トライト。これがアドレス生成と署名に使われるPrivate Keyの正体である。次にこのPrivate Keyのパターンを変える`index`、長さを変える`security`という引数について見てみよう。
　なお今後は長すぎる文字列は...と省略して表示する。

## indexとsecurityとは
　`index`によって変化するPrivate Keyの様子を見てみよう。

```js:indexテスト
for(var i=0;i<10;i++){

    // indexをインクリメントしていく。
    var key_trits = Signing.key(Converter.trits(seed),i,security);
    var key = Converter.trytes(key_trits);

    console.log('- - - - - - - - - - - - - - - - - - - ');
    console.log('index: ',i);
    console.log('key length: ',key.length);
    console.log('Private key: ',key);
}
```

```text:結果
- - - - - - - - - - - - - - - - - - - 
index:  0
key length:  4374
Private key:  UVJBFCMASCXNLDCUS9IGZSNUHZSZU9RLMLENNS...
- - - - - - - - - - - - - - - - - - - 
index:  1
key length:  4374
Private key:  UL9H9ZESBSPTHTSVA9KNKPTMJLCCWZVCBAUBMG...
- - - - - - - - - - - - - - - - - - - 
index:  2
key length:  4374
Private key:  GISSLIRLYKEFOSYQJGKYO9JPRRHJLWOEJEXBAQ...
- - - - - - - - - - - - - - - - - - - 
index:  3
key length:  4374
Private key:  GYMPGGFTVLZGKWVVVWJCQCQFKTP9FXIPGWAQIB...
- - - - - - - - - - - - - - - - - - - 
index:  4
key length:  4374
Private key:  CPOHZZUZLQAHTBDOQYNRILSULOXUGHQWACOZBU...
- - - - - - - - - - - - - - - - - - - 
index:  5
key length:  4374
Private key:  VJOQLHZK9F9ISXSTABBEBMGYGO9OYDURLNYIUA...
- - - - - - - - - - - - - - - - - - - 
index:  6
key length:  4374
Private key:  SXYIFTNTYDTRFBVVJXGRPHSLYFR9HYADYCWIQX...
- - - - - - - - - - - - - - - - - - - 
index:  7
key length:  4374
Private key:  H9NAZMOBNADQA9FUL9NRWQPLBLAGBGBNCSNFOV...
- - - - - - - - - - - - - - - - - - - 
index:  8
key length:  4374
Private key:  SZXMZTUVYGJKQB9POS9ZNLMYYHEOGKCBZHRPLL...
- - - - - - - - - - - - - - - - - - - 
index:  9
key length:  4374
Private key:  IWXLHGALLBLXWVOTSMCADWTAHJDPZLTZYURFJI...
```
　このように、IOTAのPrivate Keyはindexを変化させることによって毎回違うPrivate Keyを使って署名を行う。
　次に`security`によって変化するPrivate Keyの様子を見てみよう。デフォルトは**`security = 2`**。

```js:securityテスト
for(var i=0;i<3;i++){
    for(var s=1;s<4;s++){

        var key_trits = Signing.key(Converter.trits(seed),i,s);
        var key = Converter.trytes(key_trits);

        console.log('- - - - - - - - - - - - - - - - - - - ');
        console.log('index: ',i);
        console.log('security: ',s);
        console.log('key length: ',key.length);
        console.log('Private key: ',key);
    }
}   
```

```text:結果
- - - - - - - - - - - - - - - - - - - 
index:  0
security:  1
key length:  2187
Private key:  UVJBFCMASCXNLDCUS9IGZSNUHZSZU9RLMLENNSKLNDLIIWNXSFKBPEECY9CYBSHJXACOVOMGW9...
- - - - - - - - - - - - - - - - - - - 
index:  0
security:  2
key length:  4374
Private key:  UVJBFCMASCXNLDCUS9IGZSNUHZSZU9RLMLENNSKLNDLIIWNXSFKBPEECY9CYBSHJXACOVOMGW9...
- - - - - - - - - - - - - - - - - - - 
index:  0
security:  3
key length:  6561
Private key:  UVJBFCMASCXNLDCUS9IGZSNUHZSZU9RLMLENNSKLNDLIIWNXSFKBPEECY9CYBSHJXACOVOMGW9...
- - - - - - - - - - - - - - - - - - - 
index:  1
security:  1
key length:  2187
Private key:  UL9H9ZESBSPTHTSVA9KNKPTMJLCCWZVCBAUBMGUDBEVVJMFHOWIK9D9MZDZITCIXUEWNSMKLTD...
- - - - - - - - - - - - - - - - - - - 
index:  1
security:  2
key length:  4374
Private key:  UL9H9ZESBSPTHTSVA9KNKPTMJLCCWZVCBAUBMGUDBEVVJMFHOWIK9D9MZDZITCIXUEWNSMKLTD...
- - - - - - - - - - - - - - - - - - - 
index:  1
security:  3
key length:  6561
Private key:  UL9H9ZESBSPTHTSVA9KNKPTMJLCCWZVCBAUBMGUDBEVVJMFHOWIK9D9MZDZITCIXUEWNSMKLTD...
- - - - - - - - - - - - - - - - - - - 
index:  2
security:  1
key length:  2187
Private key:  GISSLIRLYKEFOSYQJGKYO9JPRRHJLWOEJEXBAQSEOPQTIUTCWGCYB9MQEJDHODLNIWRNTWCVTF...
- - - - - - - - - - - - - - - - - - - 
index:  2
security:  2
key length:  4374
Private key:  GISSLIRLYKEFOSYQJGKYO9JPRRHJLWOEJEXBAQSEOPQTIUTCWGCYB9MQEJDHODLNIWRNTWCVTF...
- - - - - - - - - - - - - - - - - - - 
index:  2
security:  3
key length:  6561
Private key:  GISSLIRLYKEFOSYQJGKYO9JPRRHJLWOEJEXBAQSEOPQTIUTCWGCYB9MQEJDHODLNIWRNTWCVTF...
```

このようになる。面白いのはsecurityによって全く違うPrivate Keyが生成されるのではなく、序盤は同じ文字列が生成されることだ。

## アドレス生成
　それではアドレスを生成してお金を受け取る用意を整えよう。

```js:アドレス生成
var IOTA = require('../lib/iota');
var iota = new IOTA({'host':'http://localhost','port':14265});

var seed = 'TESTABMUSHI9...';

var index = 0;
var security = 2;

// checksumを設定しないと公式ウォレットから送金できないため、checksumをtrueにしておく。
iota.api.getNewAddress(seed,{'index':index,'security':security,'checksum':true},function(error,success){
    if(error){
        console.log(error);
    }else{
        console.log('address: ',success);
    }
});

```

```text:結果
address:  9JXPVTYVEYUGDACQIOZQUHRKZOVX9ONFYRHKKGRXQOHAIFDITGQXRPMBK9QE9JQSQKJWGAD9QKV9PZDNAMGERSVVSB
```

とりあえず今は公式ウォレットから送金します。
![send.png](https://qiita-image-store.s3.amazonaws.com/0/187795/3b9bf0ff-abb6-fc3b-e0a6-ec200f09663f.png)

承認までにアドレスの残高を取得するコードを書いちゃいましょう。

## アドレスの残高

```js:残高
var IOTA = require('../lib/iota');
var iota = new IOTA({'host':'http://localhost','port':14265});

// アドレスは配列で渡すため、複数を指定できる。
var addressArray = ['9JXPVTYVEYUGDACQIOZQUHRKZOVX9ONFYRHKKGRXQOHAIFDITGQXRPMBK9QE9JQSQKJWGAD9QKV9PZDNAMGERSVVSB'];

// ２番目の引数は100を指定。
iota.api.getBalances(addressArray,100,function(error,success){
    if(error){
        console.error(error);
    }else{
        console.log(success);
    }
});

```

```text:結果
{ balances: [ '1000' ],
  references: [ 'XCGTP9EXPPQDOZKUJWEEPHWUNRIKYBCPKABEMFISFJTTP9NFNM99CXANYHHAAEPRXRADHFJNRXGOZ9999' ],
  milestoneIndex: 322346,
  duration: 692 }

```



# 送金
　さあ、受け取ったお金を送金してみよう。今回は先ほど1000iota受け取ったSeedと、これから相手役をするSeedの合計二つのSeedを使ってやり取りをする。

## Transferオブジェクト
送金情報をTransferオブジェクトにまとめる。

```js:Transferオブジェクト
{
    address: String // 81トライトの受け取りアドレス
    value: Int      // 送金額（単位はiotaなので注意）
    message: String // トライトにエンコードされたメッセージ
    tag: String     // 27トライトのタグ
}
```
今回は別のSeedから生成したアドレス`DEFBQVKHMDUDBDZBPBPM9ZUPPDSPEXMCQJWQTABHQUHMVXXMDBPTNBIZNLIQEEYOBMUKENQRUOSZZJZOZ9QJLIPGMC`に`13`iota送金する。

```js:例
var transfer = {
    address: 'DEFBQVKHMDUDBDZBPBPM9ZUPPDSPEXMCQJWQTABHQUHMVXXMDBPTNBIZNLIQEEYOBMUKENQRUOSZZJZOZ9QJLIPGMC',
    value: 13,
    message: '',
    tag: 'ABMUSHI9TEST'
}
```

## sendTransfer
　送金のリクエスト行う関数は`sendTransfer`。上記Transferオブジェクトを配列で指定するので複数の送金も一斉に行える。

```js:send.js
var IOTA = require('../lib/iota');
var iota = new IOTA({'host':'http://localhost','port':14265});

//  送金者のSeed
var seed = 'TESTABMUSHI9...';

//  受金者のアドレス
var address = 'DEFBQVKHMDUDBDZBPBPM9ZUPPDSPEXMCQJWQTABHQUHMVXXMDBPTNBIZNLIQEEYOBMUKENQRUOSZZJZOZ9QJLIPGMC';

//    送金額
var value = 13;

//  Transferオブジェクトは配列で渡す。
var transfers = [{
    address: address,
    value: value,
    message: '',
    tag: 'ABMUSHI9TEST'
}]

var depth = 5;    //  3~12がおすすめ。
var minWeightMagnitude = 14;    //   大抵14。

//  Send Transfer リクエスト
iota.api.sendTransfer(seed,depth,minWeightMagnitude,transfers,function(error,success){
    if(error){
        console.error(error);
    }else{
        console.log(success);
    }
});

```
　やはりウォレットの公開ノードより自分のノードの方が空いてるためか、送金自体は30秒もかからなかった。（ネットワークに承認されるのは時間がかかるが。）
　結果返されるのは送金Bundle。Bundleの構造については[こちら](https://qiita.com/ABmushi/items/0c9f73e08fdb6597ab9c)で詳しく解説したが今回の例で簡単に説明する。
BundleはTransactionオブジェクトを複数まとめたものだ。今回は`message`が空っぽで`security=2`なので、
>1. 出力部は`13`iotaの送信先アドレスを設定するのみ。... １つ
2. 入力部は2187*security分の署名を保管する。 ... ２つ
3. お釣りの`987`iotaを送金する。 ... １つ

となる。つまり合計４つのTransactionが生成された。その４つがBundleにまとめられて返される。略図で表すと以下のように見える。
![bundle.png](https://qiita-image-store.s3.amazonaws.com/0/187795/6d0c8753-edce-91d0-3bc6-5a3893f47097.png)
　ちなみに今回の取引をIOTA searchで見てみると[このよう](https://iotasear.ch/bundle/LZVFJOUHNJKTUXAYFTILJHEAFKEGCFPEGXYEXRGIDUZT9YPHEDWF9GIOB9DCJCFZLJS9NYWUW9E9NATHC)になっている。そして、実際返されるBundleオブジェクトは以下のようになる。

```js:Bundleが丸ごと返される。
[ { hash: 'CYIAIJQJDXGLSCDPFHYWSJQUFGLAESRCJDSHYWXPEVIMJGMQFMQDOBLWN9FMKHUQVSNHTAWXHCMYA9999',
    signatureMessageFragment: '999999999999999999999999999999999999999999999999999999999999999999999999999999999999999...',
    address: 'DEFBQVKHMDUDBDZBPBPM9ZUPPDSPEXMCQJWQTABHQUHMVXXMDBPTNBIZNLIQEEYOBMUKENQRUOSZZJZOZ',
    value: 13,
    obsoleteTag: 'PCMUSHI9TEST999999999999999',
    timestamp: 1515388352,
    currentIndex: 0,
    lastIndex: 3,
    bundle: 'LZVFJOUHNJKTUXAYFTILJHEAFKEGCFPEGXYEXRGIDUZT9YPHEDWF9GIOB9DCJCFZLJS9NYWUW9E9NATHC',
    trunkTransaction: 'KCSCGLLFBQXXOKHDEALHTMEMLYXLBX9XRVCRQLWZIJWBBGIWYYVQTGXXUVGXYHGDBLB9LSHTDTIN99999',
    branchTransaction: 'ILNMHZBRDCTNLVMBIUFMOYMNDUDCUNSLT9EVHTKWSDQBVVJIXI9B9MMCZFKR9KHVIZCBPF9INYHXZ9999',
    tag: 'ABMUSHI9TEST999999999999999',
    attachmentTimestamp: 1515388392080,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 12,
    nonce: 'SICXTHAPZYLRFOMNIHUZONYXLCE' },
  { hash: 'KCSCGLLFBQXXOKHDEALHTMEMLYXLBX9XRVCRQLWZIJWBBGIWYYVQTGXXUVGXYHGDBLB9LSHTDTIN99999',
    signatureMessageFragment: 'LPKWPRCHVOLRU9HGAMVUDROYDPVTYLAZXRRPUYG9VNEUOT9HDNWLNRQ99HIJERCXLSLXBHNPTERPYXSUXOPSWFN...',
    address: '9JXPVTYVEYUGDACQIOZQUHRKZOVX9ONFYRHKKGRXQOHAIFDITGQXRPMBK9QE9JQSQKJWGAD9QKV9PZDNA',
    value: -1000,
    obsoleteTag: 'ABMUSHI9TEST999999999999999',
    timestamp: 1515388357,
    currentIndex: 1,
    lastIndex: 3,
    bundle: 'LZVFJOUHNJKTUXAYFTILJHEAFKEGCFPEGXYEXRGIDUZT9YPHEDWF9GIOB9DCJCFZLJS9NYWUW9E9NATHC',
    trunkTransaction: 'FLMLQQBCNE9LTFBVDSLUIERHFYCGCMQDBOPUJGERVAFSRHBPLSTHKMRLHWZOTZTBJFYEGHYJBTKUZ9999',
    branchTransaction: 'ILNMHZBRDCTNLVMBIUFMOYMNDUDCUNSLT9EVHTKWSDQBVVJIXI9B9MMCZFKR9KHVIZCBPF9INYHXZ9999',
    tag: 'ABMUSHI9TEST999999999999999',
    attachmentTimestamp: 1515388391756,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 12,
    nonce: 'SSWEGXB9NLIMCBQJAIP9OQMXVGE' },
  { hash: 'FLMLQQBCNE9LTFBVDSLUIERHFYCGCMQDBOPUJGERVAFSRHBPLSTHKMRLHWZOTZTBJFYEGHYJBTKUZ9999',
    signatureMessageFragment: '999ORQLZJEUZWPOBHUJR9AXLKFZSDFTQJBNJG9NL9SZSMIRZMNYLMEVQDOGQMEUWRNAXMENENFRNTSVYWIHFUPM...',
    address: '9JXPVTYVEYUGDACQIOZQUHRKZOVX9ONFYRHKKGRXQOHAIFDITGQXRPMBK9QE9JQSQKJWGAD9QKV9PZDNA',
    value: 0,
    obsoleteTag: 'ABMUSHI9TEST999999999999999',
    timestamp: 1515388357,
    currentIndex: 2,
    lastIndex: 3,
    bundle: 'LZVFJOUHNJKTUXAYFTILJHEAFKEGCFPEGXYEXRGIDUZT9YPHEDWF9GIOB9DCJCFZLJS9NYWUW9E9NATHC',
    trunkTransaction: 'OMJNSTWEEKLSQNVTLFGKM9MYIFEALDJPQEKEW9NSIKORLNHGTCIWQFODM9BZNHIKMQDGXIMGWKRPA9999',
    branchTransaction: 'ILNMHZBRDCTNLVMBIUFMOYMNDUDCUNSLT9EVHTKWSDQBVVJIXI9B9MMCZFKR9KHVIZCBPF9INYHXZ9999',
    tag: 'ABMUSHI9TEST999999999999999',
    attachmentTimestamp: 1515388385850,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 12,
    nonce: 'BVGHTNVKWSHZGLAMXZTPBMIIEFG' },
  { hash: 'OMJNSTWEEKLSQNVTLFGKM9MYIFEALDJPQEKEW9NSIKORLNHGTCIWQFODM9BZNHIKMQDGXIMGWKRPA9999',
    signatureMessageFragment: '999999999999999999999999999999999999999999999999999999999999999999999999999999999999999...',
    address: 'NCIYBEMWZFBQHVEISSRJUCQDNGJFKZTWP9SLCFSBRWEYY9ESAXNTP9WDVXWJFGIIAVQKRGO9TRZFWVTRA',
    value: 987,
    obsoleteTag: 'ABMUSHI9TEST999999999999999',
    timestamp: 1515388358,
    currentIndex: 3,
    lastIndex: 3,
    bundle: 'LZVFJOUHNJKTUXAYFTILJHEAFKEGCFPEGXYEXRGIDUZT9YPHEDWF9GIOB9DCJCFZLJS9NYWUW9E9NATHC',
    trunkTransaction: 'ILNMHZBRDCTNLVMBIUFMOYMNDUDCUNSLT9EVHTKWSDQBVVJIXI9B9MMCZFKR9KHVIZCBPF9INYHXZ9999',
    branchTransaction: '9ZFUVLAXCKNVUHLUZJADZWMAIZEKMKVWYTFUUEQJWKSHAFHGKLSYRPMAQXJJIQQIK9TGWBJDAZIKA9999',
    tag: 'ABMUSHI9TEST999999999999999',
    attachmentTimestamp: 1515388380939,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 12,
    nonce: 'VGDCYEFCKKSNMRLJFQKDSTFKIKG' } ]
```

## 受け取り主の残高は増えたか？
　さて、上の13iotaの送金が承認されるまでだいたい30分かかったところで、まず着金を確認してみよう。アドレスを`DEFBQVKHMDUDBDZBPBPM9ZUPPDSPEXMCQJWQTABHQUHMVXXMDBPTNBIZNLIQEEYOBMUKENQRUOSZZJZOZ9QJLIPGMC`に設定して`getBalance`してみよう。

```js:getBalanceの結果
{ balances: [ '13' ],
  references: [ 'KVZNXXBQSUVLEOVSKSQLJEVOJWVBIIFTOPUBHFJ9YOTFYZWLEHMPLWRYGKMSTMVCZ9ZXCZWXLNAIZ9999' ],
  milestoneIndex: 322415,
  duration: 578 }
```
ちゃんと`13`iota分、着金している。
## 送り主の残高は減ったか？
　次に送り主のアドレスの残高を確認してみよう。

```js:アドレス残高
{ balances: [ '0' ],
  references: [ 'CDKOPNULQTJZYVCNAZEKVPJNMP9YKMKPNQXRELCDMPVIRNAPNHHNBJUQD9MAVPASABNGBJQQXYWOA9999' ],
  milestoneIndex: 322384,
  duration: 452 }
```
　おや？`13`だけしか送っていないのに残高がゼロ。`1000-13 = 987`のはずなのにおかしい！と思うかもしれないが実はこれが正しい。というのも、IOTAではお釣り`=987`は新しいアドレスに送金されるからだ。新しいアドレスとは、`index`を**＋１**した結果生成されるアドレスのことだ。コードで見てみよう。

```js:nextAddress.js
var IOTA = require('../lib/iota');
var iota = new IOTA({'host':'http://localhost','port':14265});

var seed = 'TESTABMUSHI9...';

// indexを＋１。（さっきは0だった）
var index = 1;

//  securityは変えない。
var security = 2;

iota.api.getNewAddress(seed,{'index':index,'security':security,'checksum':true},function(error,success){
    if(error){
        console.log(error);
    }else{
        console.log('address: ',success);
    }
});
```

```js:新しいアドレス
address:  VLRVBXGUTJCXPTJARJBOE9SRIPHDULMUGITHVDOXYBYCJRRHUKDXSDTLKRJMLISJPBXWCZJYAP9VGOCPAJEZOPAFYY
```
　ここで問題が発生した。このアドレスの残高を調べるとゼロなのだ。[ソースコード](https://github.com/iotaledger/iota.lib.js/blob/master/lib/api/api.js)を調べてみると、どうやら`getNewAddress`の`index`に指定する整数は、**今までアドレスが使われたことがあるかを検索し始めるindexの検索開始地点**であることが分かった。また、調べると上のアドレス`VLRVBXGUTJCXPTJAR...`は`index=2`のアドレスだと判明した。なるほど、確かに`index=2`はまだ使われていない。
　ということでもう使われた`index=1`のアドレスを取得するためには、自作で`getAddress`という関数を作らなければならなくなった。アドレスの生成方法は[この記事](https://qiita.com/ABmushi/items/422d1bf94be0c919583a#%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E7%94%9F%E6%88%90)でも詳しく解説した。以下は略図である。
![address_gen.png](https://qiita-image-store.s3.amazonaws.com/0/187795/20d53f02-4e46-bc2b-ad35-2003a73d6664.png)
　コードにすると以下のようになる。

```js:getAddress.js
// アドレス生成はオフラインでできる。（APIはいらない）
// var IOTA = require('../lib/iota');

var Signing = require('../lib/crypto/signing/signing');
var Converter = require('../lib/crypto/converter/converter');

// アドレス生成はオフラインでできる。（APIはいらない）
// var iota = new IOTA({'host':'http://localhost','port':14265});

//  アドレス生成関数。
var getAddress = function(seed,index,security){
    //  Private Keyを得る。
    var key_trits = Signing.key(Converter.trits(seed),index,security);

    //  digestを得る。
    var digest_trits = Signing.digests(key_trits);

    //  addressを得る。
    var address_trits = Signing.address(digest_trits);

    var address = Converter.trytes(address_trits);

    return address;
}

var seed = 'TESTABMUSHI9...';

var index = 1;
var security = 2;

//　実行
var address = getAddress(seed,index,security);

//  表示
console.log('address: ',address);
```

```js:結果
address:  NCIYBEMWZFBQHVEISSRJUCQDNGJFKZTWP9SLCFSBRWEYY9ESAXNTP9WDVXWJFGIIAVQKRGO9TRZFWVTRA
```

この新しいアドレスに対して[`getBalances`]()する。

```js:
{ balances: [ '987' ],
  references: [ 'PXE9OCISTJHDCYPHVMWUBAWCUDQDTHBXSJOCP99IUCONDGS9OQTGOBJIFUXHOAGTKEJZJNBOHOIRA9999' ],
  milestoneIndex: 322389,
  duration: 478 }

```
　ついに、求めていた`987`が入ったアドレスにたどり着いた。実はこのアドレス`NCIYBEMWZ...`だが、先ほど`sendTranfer`が返したBundle内の最後のTransaction（currentIndex=3）の`address`を見れば見つかる。というのも、この`987`iotaはお釣りなのでBundleの差額出力部でお釣りの送金先を指定しているからだ。（\*[*差額出力部とは?*](https://qiita.com/ABmushi/items/0c9f73e08fdb6597ab9c#%E5%B7%AE%E9%A1%8D%E5%87%BA%E5%8A%9B%E9%83%A8---remainder)）
　何はともあれ送金が上手くいったことが確認できた。

# アカウント残高計算
　さて、IOTAではアカウント自体の残高はどうやって求めているのだろう？前章で苦戦したように、今まで使ったアドレス全てを調べて合計を求めないとアカウントの残高（＝ウォレットに表示される残高）は求められない。
## アドレス連続生成し合計計算
　ということで、とりあえず5個（`index`の0~4）アドレスを生成してそれぞれの残高を合計しよう。

```js:balance.js
var IOTA = require('../lib/iota');
var Signing = require('../lib/crypto/signing/signing');
var Converter = require('../lib/crypto/converter/converter');

var iota = new IOTA({'host':'http://localhost','port':14265});

var getAddress = function(seed,index,security){
    var key_trits = Signing.key(Converter.trits(seed),index,security);
    var digest_trits = Signing.digests(key_trits);
    var address_trits = Signing.address(digest_trits);
    var address = Converter.trytes(address_trits);
    return address;
}

// アドレスを配列で受け取って残高を表示する。
var getBalance = function(addressArray){
    iota.api.getBalances(addressArray,100,function(error,success){
        if(error){
            console.error(error);
        }else{
            var list = success.balances;
            var total = 0;
            for(i=0;i<list.length;i++){
                var balance = parseInt(list[i]);
                console.log('- - - - - - - - - - - - - - - - - - - -');
                console.log('index: ',i);
                console.log('address: ',addressArray[i]);
                console.log('balance: ',balance);
                total += balance;
            }
            console.log('- - - - - - - - - - - - - - - - - - - -');
            console.log('account balance: ',total);
        }
    });
}

var seed = 'TESTABMUSHI9...';
var security = 2;
var addressArray = [];
var maxIndex = 5;    // 今回は５つ生成する（0~4）。

//  アドレス連続生成
for(var i = 0;i<maxIndex;i++){
    address = getAddress(seed,i,security);　 //  index = i　のアドレス
    addressArray.push(address);
}
// 残高
getBalance(addressArray);
```

```text:結果
- - - - - - - - - - - - - - - - - - - -
index:  0
address:  9JXPVTYVEYUGDACQIOZQUHRKZOVX9ONFYRHKKGRXQOHAIFDITGQXRPMBK9QE9JQSQKJWGAD9QKV9PZDNA
balance:  0
- - - - - - - - - - - - - - - - - - - -
index:  1
address:  NCIYBEMWZFBQHVEISSRJUCQDNGJFKZTWP9SLCFSBRWEYY9ESAXNTP9WDVXWJFGIIAVQKRGO9TRZFWVTRA
balance:  987
- - - - - - - - - - - - - - - - - - - -
index:  2
address:  VLRVBXGUTJCXPTJARJBOE9SRIPHDULMUGITHVDOXYBYCJRRHUKDXSDTLKRJMLISJPBXWCZJYAP9VGOCPA
balance:  0
- - - - - - - - - - - - - - - - - - - -
index:  3
address:  APCMI9ESIIGEUVKLJHHHGWBXEAFHJXFCLLZD9GZBZC9LIXD9RMWUDRYLUAJATQJDJVHOULTBDHVD9DIVD
balance:  0
- - - - - - - - - - - - - - - - - - - -
index:  4
address:  99OEIIKAHOHWSXMBAEGYJFFLDAGMDKHKXTYFJDWJKXIGIEFJZKMFSEZTVMGY9TXGXFWWDXZOWAYKPKBYW
balance:  0
- - - - - - - - - - - - - - - - - - - -
account balance:  987
```

## 簡単な方法
　さて、ここでアドレスを何個生成すれば良いかという問題が起きる。その数が足りないと、表示されるアカウントの残高も足りなくなる。先ほどの例だと５つ検索にかけたが、もしかすると`index=6`などにも残高が残っているかもしれないのだ。そこで、公式APIに`getAccountData`というものがあるのでこれを使ってみよう。

```js:account.js
var IOTA = require('../lib/iota');
var iota = new IOTA({'host':'http://localhost','port':14265});

var seed = 'TESTABMUSHI9...';

//  seedを与えるだけ。
iota.api.getAccountData(seed,function(error,success){
    if(error){
        console.error(error);
    }else{
        console.log(success);
    }
});
```


```js:結果
{ latestAddress: 'VLRVBXGUTJCXPTJARJBOE9SRIPHDULMUGITHVDOXYBYCJRRHUKDXSDTLKRJMLISJPBXWCZJYAP9VGOCPA',
  addresses: 
   [ '9JXPVTYVEYUGDACQIOZQUHRKZOVX9ONFYRHKKGRXQOHAIFDITGQXRPMBK9QE9JQSQKJWGAD9QKV9PZDNA',
     'NCIYBEMWZFBQHVEISSRJUCQDNGJFKZTWP9SLCFSBRWEYY9ESAXNTP9WDVXWJFGIIAVQKRGO9TRZFWVTRA' ],
  transfers: 
   [ [ [Object], [Object], [Object], [Object] ],
     [ [Object], [Object], [Object], [Object] ],
     [ [Object], [Object], [Object], [Object] ],
     [ [Object], [Object], [Object], [Object] ] ],
  inputs: 
   [ { address: 'NCIYBEMWZFBQHVEISSRJUCQDNGJFKZTWP9SLCFSBRWEYY9ESAXNTP9WDVXWJFGIIAVQKRGO9TRZFWVTRA',
       keyIndex: 1,
       security: 2,
       balance: 987 } ],
  balance: 987 }
```
　このように最終的な`balance`を自動的に持ってきてくれる便利なAPIだ。

# 参考文献
公式の[README](https://github.com/iotaledger/iota.lib.js/blob/master/README.md)
公式APIソース：[iota.lib.js/lib/api/api.js](https://github.com/iotaledger/iota.lib.js/blob/master/lib/api/api.js)

# 最後に
　今回の記事はソースコードだらけになってしまったが、実際のコードをテストしてみるうちに、IOTAのいいところは送金手数料がゼロだと今更気づいた。
　特に今回は1000iota（＝0.5円）くらいを移動させた。メインネットでテストしたいけど、怖いから少額取引でテストをしたいと考えたときに手数料がかからないというのは大変都合が良い。もし、これがビットコインやEthereumなら手数料が高すぎてこんな記事のために実際のお金を動かせない。
　こんなことを思ったのも、**[暗号通貨バブルはイノベーションを絞め殺している](http://jp.techcrunch.com/2018/01/08/2018-01-07-the-cryptocurrency-bubble-is-strangling-innovation/)**という記事を読んでからだ。引用すると、

>*Ethereumがトークン化のコストを劇的に下げる方法を考え出せば別だ。もちろん実験的サービスは多数作られてはいる。しかしほどんど誰も利用しない。こうしたサービスには好奇心の強いユーザーが近づいてみるものの、一回限りの実験にしても高すぎる手数料に驚かされている。まして日常利用するようなことにはならない。結果として、暗号通貨テクノロジーを利用する実験もイノベーションも投機バブルが破裂するまでは一時停止状態だ。*

とあるように、今バブル状態にある仮想通貨は、そんな異常から離れた日常と相容れない。しかし、仮想通貨が目指すのは、日常のなかの支払いである。この矛盾をどう解消していくか。
　仮想通貨という壮大になりつつある社会実験の行く末はいかに...

　ではまた。