[IPFS] IPFS分散ファイルシステムを試してみる

#はじめに
EthereumでDAppを開発する時、データのストレージが問題になってくると思います。
Ethereumのステートを変更する時に発生するガスコストはコントラクト実行に必要な計算量によって決まるため、スマートコントラクトのstorage領域にデータを格納するとガスコストが高額になってしまいます。
何か方法はないか調べてみるとIPFSとSwarmという分散ファイルシステムがあることが分かったので、今回はIPFSについて調べたことを残しておきます。

#IPFSとは
IPFS(Inter Planetary File System)とはp2pネットワーク上で動作する分散型のファイルシステムで、HTTPを補完または置き換えるプロトコルです。
HTTPとの最大の違いは、HTTPが「ロケーション指向型」であるのに対し、IPFSは「コンテンツ指向型」である点です。
ロケーション指向型とは、例えば https://hogehoge.com/fuga/aaa.txt というファイルにアクセスする時、ファイルの場所(hogehoge.com/fuga)にアクセスしてファイルを取得・閲覧する仕組みを指します。
これに対しコンテンツ指向型は、ファイルの場所(サーバーやディレクトリ)は問題ではなく、ファイルのコンテンツをp2pネットワークに登録し、必要な時はコンテンツ自体にアクセスする仕組みです。
ロケーション指向型が中央集権的な仕組みであるのに対し、コンテンツ指向型はコンテンツの置かれている場所ではなく、あくまでコンテンツ自体を分散管理するので非中央集権的な仕組みとなっています。

もっと詳しく知りたい方は、参考サイト・参考文献に記載しているサイトや書籍に詳しく記述されていますので、そちらもご覧になってみてください。

#IPFSをインストールする
それでは早速IPFSをインストールしてみようと思います。

###Dockerコンテナを準備する
まずはDockerの環境を構築します。
今回は一からインストールしたいので、ubuntuOSだけのDocker imageを作成します。

それではDockerfileを作成します。

```text:Dockerfile
FROM ubuntu:latest

RUN apt-get update && apt-get install -y\
	wget

WORKDIR /work
```
ipfsのパッケージをインストールしたいのでwgetだけ予めインストールしておきます。

Dockerfileが出来たらdocker buildし、docker runでコンテナを作成します。

ここから下はコンテナ環境での作業です。

###IPFSをインストールする
まずはipfsのダウンロードサイトからインストールパッケージをインストールしてきます。

```bash
root@42db220e8bdf:/work# wget https://dist.ipfs.io/go-ipfs/v0.10.0/go-ipfs_v0.10.0_linux-amd64.tar.gz
--2021-10-03 04:07:28--  https://dist.ipfs.io/go-ipfs/v0.10.0/go-ipfs_v0.10.0_linux-amd64.tar.gz
Resolving dist.ipfs.io (dist.ipfs.io)... 209.94.78.1, 2602:fea2:3::1
Connecting to dist.ipfs.io (dist.ipfs.io)|209.94.78.1|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 26156481 (25M) [application/gzip]
Saving to: 'go-ipfs_v0.10.0_linux-amd64.tar.gz'

go-ipfs_v0.10.0_linux-amd64.tar.gz 100%[================================================================>]  24.94M  1.41MB/s    in 33s     

2021-10-03 04:08:02 (773 KB/s) - 'go-ipfs_v0.10.0_linux-amd64.tar.gz' saved [26156481/26156481]
```
ワーキングディレクトリにtargzファイルがインストールされました。
このtargzファイルを解凍します。

```bash
root@42db220e8bdf:/work# tar xvzf go-ipfs_v0.10.0_linux-amd64.tar.gz 
go-ipfs/LICENSE
go-ipfs/LICENSE-APACHE
go-ipfs/LICENSE-MIT
go-ipfs/README.md
go-ipfs/install.sh
go-ipfs/ipfs
```
解凍するとgo-ipfsフォルダが作成されました。
カレントディレクトリを確認してみます

```bash
root@42db220e8bdf:/work# ls
Dockerfile  go-ipfs  go-ipfs_v0.10.0_linux-amd64.tar.gz
```
もうtargzファイルは要らないと思うので削除します。

go-ipfsフォルダにinstall.shがあるので実行します。

```bash
root@42db220e8bdf:/work# go-ipfs/install.sh 
Moved go-ipfs/ipfs to /usr/local/bin
```
ipfsプロトコルのファイルがusr/local/binへ移動したようです。(pathの設定も自動で行われたはず)
これでipfsコマンドが使えるようになります。

それでは正常にインストール出来たか確認してみます。
ipfsのバージョンを確認し、帰ってきたらインストールは成功しています。

```bash
root@42db220e8bdf:/work# ipfs version
ipfs version 0.10.0
```
問題ないようです。

#IPFSを使ってみる

###ipfsをinitする
まずはipfs initコマンドでイニシャライズします。

```bash
root@42db220e8bdf:/work# ipfs init
generating ED25519 keypair...done
peer identity: 12D3KooWJEKSJGzb9J4BB66XGE5XfeU6LtRErKMXJoirYCMBs9jg
initializing IPFS node at /root/.ipfs
to get started, enter:

	ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme
```
peer identityがこのノードのidとなります。

###readmeとusageをチェック
イニシャライズが終わるとreadmeのコマンドが表示されるので、これを実行してみます。
ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme

```bash
root@42db220e8bdf:/work# ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme
Hello and Welcome to IPFS!

██╗██████╗ ███████╗███████╗
██║██╔══██╗██╔════╝██╔════╝
██║██████╔╝█████╗  ███████╗
██║██╔═══╝ ██╔══╝  ╚════██║
██║██║     ██║     ███████║
╚═╝╚═╝     ╚═╝     ╚══════╝

If you're seeing this, you have successfully installed
IPFS and are now interfacing with the ipfs merkledag!

 -------------------------------------------------------
| Warning:                                              |
|   This is alpha software. Use at your own discretion! |
|   Much is missing or lacking polish. There are bugs.  |
|   Not yet secure. Read the security notes for more.   |
 -------------------------------------------------------

Check out some of the other files in this directory:

  ./about
  ./help
  ./quick-start     <-- usage examples
  ./readme          <-- this file
  ./security-notes
```

同様にusageも確認してみます。

```bash
root@42db220e8bdf:/work# ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/quick-start
# 0.1 - Quick Start

This is a set of short examples with minimal explanation. It is meant as
a "quick start".


Add a file to ipfs:

  echo "hello world" >hello
  (must be online)
  ipfs swarm peers
  ipfs id
  ipfs cat <hash-of-remote-object>


Mount:

  (warning: fuse is finicky!)
  ipfs mount
  cd /ipfs/<the-hash-here>
  ls


Tool:

  ipfs version
  ipfs update
  ipfs commands
  ipfs config --help
  open http://localhost:5001/webui


Browse:

  WebUI:

    http://localhost:5001/webui

  video:

    http://localhost:8080/ipfs/QmVc6zuAneKJzicnJpfrqCH9gSy6bz54JhcypfJYhGUFQu/play#/ipfs/QmTKZgRNwDNZwHtJSjCp6r5FYefzpULfy37JvMt9DwvXse

  images:

    http://localhost:8080/ipfs/QmZpc3HvfjEXvLWGQPWbHk3AjD5j8NEN4gmFN8Jmrd5g83/cs

  markdown renderer app:

    http://localhost:8080/ipfs/QmX7M9CiYXjVeFnkfVGf3y5ixTZ2ACeSGyL1vBJY1HvQPp/mdown
```
なんか色々出来るようです。
シンプルだけど分かりやすそうですね。

###コンテンツをリポジトリに登録してみる
それではusageに従って色々試してみます。
まずは"Hello World"という文字列を格納したhelloファイルを作成します。

```bash
root@42db220e8bdf:/work# echo "Hello World" > hello
root@42db220e8bdf:/work# ls
Dockerfile  go-ipfs  hello 
``` 

ipfs addコマンドでこのhelloファイルのコンテンツをリポジトリにaddしてみます。

```bash
root@42db220e8bdf:/work# ipfs add hello 
added QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u hello
 12 B / 12 B [=====================================================================================================================] 100.00%
```
helloファイル内のコンテンツがリポジトリにipfsオブジェクトとして登録されました。
"QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u"はオブジェクトのアドレスです。 

ipfs catコマンドでオブジェクトのアドレスを指定するとコンテンツを表示することが出来ます。

```bash
root@42db220e8bdf:/work# ipfs cat QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u
Hello World
```
helloファイルのコンテンツ(Hello World)が格納されています。

このオブジェクトの構成を見てみます。
ipfs object getコマンドを使うとオブジェクトの構成が見れるようです。

```bash
root@42db220e8bdf:/work# ipfs object get QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u
{"Links":[],"Data":"\u0008\u0002\u0012\u000cHello World\n\u0018\u000c"}
```
ユニコード文字が入っていますが、Dataの中にHello Worldが入っています。

それでは新たにhello2ファイルを作成して、そこのhelloファイルと同じ"Hello World"文字列を格納してみます。

```bash
root@42db220e8bdf:/work# echo "Hello World" > hello2
root@42db220e8bdf:/work# ls
Dockerfile  go-ipfs  hello  hello2
```

このhello2をipfs addしてみます。

```bash
root@42db220e8bdf:/work# ipfs add hello2
added QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u hello2
 12 B / 12 B [=====================================================================================================================] 100.00%
```

hello2のオブジェクトアドレスとhelloのオブジェクトアドレスが一緒ですね。
これは"Hello World"というコンテンツが同一なので同じオブジェクトになっているからです。

このようにIPFSはファイルの場所ではなくコンテンツをオブジェクト化し登録・参照出来る仕組みとなっています。

#おわりに
少し長くなってきたので今回はIPFSの環境構築と動作確認までの確認にしようと思います。
次回はディレクトリの作成やIFPSネットワークへの接続の方を試してみようと思います。
ご覧いただきありがとうございました。

#参考サイト、文献

* [IPFS入門](https://ipfs-book.decentralized-web.jp/)
IPFSについて詳しく紹介されています。
すごく参考になったサイトです。

* 「マスタリング イーサリアム」オライリー 
   Andreas M.Antonopoulos, Gavin Wood 著
　　EthereumやDAppについて詳しい書籍です。