OSSの分散型台帳 Hyperledger/fabric をdocker swarmで分散させてみよう（2/3）〜 chaincode解説編

この記事は、OSSの分散型台帳技術である[Hyperledger/fabric](http://hyperledger-fabric.readthedocs.io/en/latest/)を用いて簡易な入出金処理を作り、[docker swarm mode](https://docs.docker.com/engine/swarm/)で分散処理させてみる、という連載の２回目です。

今回は、Hyperledger/fabricのchaincodeを開発するための環境構築手順と、前回動作させた入金・送金・出金サンプルアプリのchaincodeの内容を解説します。Hyperledger/fabricのAPI利用方法がわかれば、chaincodeの記述は難しく無いと思います。

1. [OSSの分散型台帳 Hyperledger/fabric をdocker swarmで分散させてみよう（1/3）〜 動作確認編](https://qiita.com/nmatsui/items/cbcb7807984c65694e46)
2. [OSSの分散型台帳 Hyperledger/fabric をdocker swarmで分散させてみよう（2/3）〜 chaincode解説編](https://qiita.com/nmatsui/items/9689982e799ccb1255ed)
3. [OSSの分散型台帳 Hyperledger/fabric をdocker swarmで分散させてみよう（3/3）〜 分散環境解説編](https://qiita.com/nmatsui/items/7eb4e214b628f96814a0)

# Hyperledger/fabricのchaincode開発環境の構築
Hyperledger/fabric 1.1のchaincodeは、[Go](https://golang.org/)あるいは[node.js](https://nodejs.org/)で記述します。最終的にはJava等も視野に入っているようですが、リポジトリ（[hyperledger/fabric-chaincode-java](https://github.com/hyperledger/fabric-chaincode-java)）を見る限りまだまだというところです。

今回はGoでchaincodeを記述することにします。

## goとdockerのインストール
1. goのインストール
    * 1.9以降のgolangをインストールし、適切にGOPATHを設定してください。

1. dockerとdoker-composeのインストール
    * chaincodeの動作確認を行えるように、17.06.2-ce 以降のdocker、及び対応するバージョンのdocker-composeをインストールしてください。

## Hyperledger/fabricの取得
1. Hyperledger/fabricの開発用ライブラリを取得する

    ```bash
    ubuntu@node0:~$ go get -d github.com/hyperledger/fabric/protos/peer
    ubuntu@node0:~$ go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim
    ```
    * **注意**
        * githubで公開されている[Hyperledger/fabric](https://github.com/hyperledger/fabric)をそのままgit clone (go get) しただけでは、暗号処理関連のライブラリが足りないためビルドができません。
        * そのため暗号処理を用いない開発用tag `nopkcs11` を指定して
`github.com/hyperledger/fabric/core/chaincode/shim`を上書きすることで、手元でビルドの成否が確認できるようにします。

## chaincodeのスケルトンを作成しビルド
開発用ライブラリを正しく`go get`できているか確認するために、ログを出すだけで何もしない空のchaincodeを作ってビルドしてみます。

1. chaincode用のディレクトリを${GOPATH}/src以下に作成する

    ```bash
    ubuntu@node0:~$ mkdir -p ${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/
    ubuntu@node0:~$ cd ${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/
    ```

1. chaincodeのスケルトンを作成する

    ```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/fabric-payment.go
    package main

    import (
    	"fmt"

    	"github.com/hyperledger/fabric/core/chaincode/shim"
    	sc "github.com/hyperledger/fabric/protos/peer"
    )

    // shim.NewLoggerでロガーが取得できる
    var logger = shim.NewLogger("main")

    type EntryPoint struct {
    }

    // chaincodeのinstantiate時に起動される初期化メソッド
    // （今回のサンプルでは、何もしない）
    func (s *EntryPoint) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
    	logger.Info("instantiated chaincode")
    	return shim.Success(nil)
    }

    // cliやSDKからchaincodeを呼び出した際に起動するエントリーポイントとなるメソッド
    // 最終的には、指定されたfunctionに従って、具体的な処理を他のメソッドに委譲する
    func (s *EntryPoint) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
    	function, args := APIstub.GetFunctionAndParameters()

    	msg := fmt.Sprintf("No such function. function = %s, args = %s", function, args)
    	logger.Error(msg)
    	return shim.Error(msg)
    }

    func main() {
    	if err := shim.Start(new(EntryPoint)); err != nil {
    		logger.Errorf("Error creating new Chaincode. Error = %s\n", err)
    	}
    }
    ```
    * `Init`と`Invoke`を実装している型であれば、Hyperledger/fabricネットワーク上で実行可能なchaincodeとなります。

1. chaincodeのスケルトンをビルドする
    * ビルド時にも、 `--tags nopkcs11` を指定してください。

    ```bash
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample$ go build --tags nopkcs11 fabric-payment.go
    ```
    * 何もエラーメッセージが表示されず、実行ファイル`fabric-payment`が生成されていればビルド成功です。
    * 実行ファイルを動作させると、次のようなログが出力されると思います。

    ```bash
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample$ ./fabric-payment
    2018-03-04 09:49:19.168 JST [shim] SetupChaincodeLogging -> INFO 001 Chaincode log level not provided; defaulting to: INFO
    2018-03-04 09:49:19.168 JST [shim] SetupChaincodeLogging -> INFO 002 Chaincode (build level: ) starting up ...
    2018-03-04 09:49:19.168 JST [main] Error -> ERRO 003 Error creating new Chaincode. Error = Error chaincode id not provided
    ```

# モデルの作成
Hyperledger/fabricは、JSON（のバイト配列）としてデータを台帳に記帳します。そのため、JSON化できるのであればどのような非定型データであれ扱うことはできますが、ある程度固定されたデータ構造を定義しておいた方がchaincodeの見通しがよくなります。また記帳する最新データ（state）の保存先としてcouchdbを用いる場合は、データ構造が固定されていればIndexを有効に活用できます。
そこでまず、記帳したいデータ構造をstructとして定義します。

> 以下で紹介するコードでは、importやエラー処理を省略しています。実際のコードは[githubリポジトリ](https://github.com/nmatsui/fabric-payment-sample-chaincode/)を参照してください。

## モデルを定義する際の注意点
Hyperleder/fabricは、台帳にデータを記帳する際に以下のようなステップで処理を行います。

1. SDKはpeerにトランザクションのproposalを送信
1. peerは自身の最新の台帳状態を用いてchaincodeをシミュレート実行する
    * 無限ループに陥るなどしないことを確認
    * シミュレートに成功した場合、「シミュレート時点の台帳の状態」と「更新後の台帳の状態」をSDKに返す
        * この時点ではまだ台帳に記帳されない
1. シミュレート実行が全て成功した場合、SDKは(peerをproxyにして）ordererにトランザクション処理を依頼する
1. ordererは依頼されたトランザクションを整列させ、各peerに順序立ててコミットを依頼する
1. 各peerは、「シミュレート時点の台帳の状態」が他のトランザクションによって更新されていないことを確認し、更新後の状態を記帳してトランザクションをコミットする

![Figure 1. Illustration of one possible transaction flow (common-case path).](http://hyperledger-fabric.readthedocs.io/en/release-1.1/_images/flow-4.png)
出典：http://hyperledger-fabric.readthedocs.io/en/release-1.1/arch-deep-dive.html

そのため、複数のトランザクションが並列して「同じデータ」を更新しようとすると、どれか一つのトランザクション以外は全て却下されてしまいます。つまり、毎回同じデータを更新するようなchaincodeは、更新スループットが劣悪になってしまうということです（実際に性能テストをしたわけではありませんが・・・）。
モデルの定義時には、このトランザクション処理のアーキテクチャに十分注意しましょう。

## モデル種別を定義する
今回のサンプルアプリでは、「口座」と「イベント（入金・送金・出金）」という２つのモデルを扱います。これらの種別を判別するための型を定義します。

- 口座（AccountModel）
- イベント（EventModel）
    - 入金（DepositEvent）
    - 送金（RemitEvent）
    - 出金（WithdrawEvent）

ただしGoには、Enum型がありません。そこで以下のような、JSON serializableなEnumもどきの型を定義します。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/types/model_type.go
const (
	unknownModelStr = "unknown"
	accountModelStr = "account"
	eventModelStr   = "event"
)

// ModelType : model type
type ModelType int

// concrete ModelType
const (
	UnKnownModel ModelType = iota
	AccountModel
	EventModel
)

// String : Stringer interface
func (t ModelType) String() string {
	switch t {
	case AccountModel:
		return accountModelStr
	case EventModel:
		return eventModelStr
	default:
		return unknownModelStr
	}
}

// MarshalJSON : Marshaler interface
func (t ModelType) MarshalJSON() ([]byte, error) {
	return json.Marshal(t.String())
}

// UnmarshalJSON : Marshaler interface
func (t *ModelType) UnmarshalJSON(data []byte) error {
	var s string
	switch s {
	case accountModelStr:
		*t = AccountModel
	case eventModelStr:
		*t = EventModel
	default:
		*t = UnKnownModel
	}
	return nil
}
```

## モデルを定義する
AccountモデルとEventモデルは、次のようになります。非常にシンプルです。
台帳からモデル種別で絞り込んでstateを検索できるように、ModelTypeやEventTypeを型に組み込んでいます。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/models/account.go
type Account struct {
	ModelType types.ModelType `json:"model_type"`
	No        string          `json:"no"`
	Name      string          `json:"name"`
	Balance   int             `json:"balance"`
}
```
```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/models/event.go
type AccountState struct {
	No              string `json:"no"`
	Name            string `json:"name"`
	PreviousBalance int    `json:"previous_balance"`
	CurrentBalance  int    `json:"current_balance"`
}

type Event struct {
	ModelType        types.ModelType `json:"model_type"`
	EventType        types.EventType `json:"event_type"`
	No               string          `json:"no"`
	Amount           int             `json:"amount"`
	FromAccountState *AccountState   `json:"from_account"`
	ToAccountState   *AccountState   `json:"to_account"`
}
```

# 口座のCRUD
次に口座（Account）の検索、作成、変更、削除を行うchaincodeを記述します。単なる関数として実装しても良いのですが、処理をグループ化するために`AccountContract`型のメソッドとして定義します。

REST APIで指定したパラメータは、SDK経由で引数の`args []string`に渡されてきます。

## 口座一覧の取得（ListAccount）
couchdbのクエリ文字列を引数として`shim.ChaincodeStubInterface.GetQueryResult`を呼び出すことで、stateから最新の口座を一覧で取得します。

> LevelDBを用いる場合、クエリを利用することができません。この場合、CompositeKeyという機能を使うことになります。詳細は、公式の[サンプルコード](https://github.com/hyperledger/fabric-samples/blob/release/chaincode/marbles02/marbles_chaincode.go)を参照してください。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/account.go
// AccountContract : a struct which has the methods related to manage Account
type AccountContract struct {
}

// ListAccount : return a list of all accounts
func (ac *AccountContract) ListAccount(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	accountLogger.Infof("invoke ListAccount, args=%s\n", args)
	if len(args) != 0 { //パラメータ数をチェック（口座一覧取得にはパラメータが無いはず）
	}

	// stateから最新のAccountを全て取得するクエリを生成（couchdbのQuery文法に従う）
	query := map[string]interface{}{
		"selector": map[string]interface{}{
			"model_type": types.AccountModel,
		},
	}
	queryBytes, err := json.Marshal(query)
	accountLogger.Infof("Query string = '%s'", string(queryBytes))

	// クエリを用いてstateから最新のAccount一覧を取得するIteratorを生成
	resultsIterator, err := APIstub.GetQueryResult(string(queryBytes))
	// 取得したIteratorは、メソッド離脱時にCloseする
	defer resultsIterator.Close() 

	// stateから取得したJSONを*Account型にキャストしてsliceに追加
	results := make([]*models.Account, 0)
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		account := new(models.Account)
		results = append(results, account)
	}

	// レスポンスとなるJSONのバイト配列を生成
	jsonBytes, err := json.Marshal(results)
	return shim.Success(jsonBytes)
}
```

## 口座の新規作成（CreateAccount）
AccontオブジェクトのJSONのバイト配列を、口座番号をキーとして`shim.ChaincodeStubInterface.PutState`に渡せば、口座情報を新規登録できます。
なお同一キーがある場合、PutStateはそのオブジェクトを上書きしてしまうため、口座番号は一意でなければなりません。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/account.go
// CreateAccount : create a new account
func (ac *AccountContract) CreateAccount(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	accountLogger.Infof("invoke CreateAccount, args=%s\n", args)
	if len(args) != 1 {
	}
	name := args[0] // 一つ目のパラメータが "name"

	// 一意な口座番号を生成する（詳細はgithubのソースコードを参照）
	no, err := utils.GetAccountNo(APIstub) 

	// Accountオブジェクトを生成してJSONバイト配列化
	account := models.Account{
		ModelType: types.AccountModel,
		No:        no,
		Name:      name,
		Balance:   0,
	}
	jsonBytes, err := json.Marshal(account)

	// Accountオブジェクトを保存
	if err := APIstub.PutState(no, jsonBytes); err != nil {
	}
	// 作成したAccountオブジェクトのJSONバイト配列をレスポンスとして返す
	return shim.Success(jsonBytes)
}
```

## 口座の取得（RetrieveAccount）
Accountオブジェクトは口座番号をキーとして台帳に記帳されていますので、`shim.ChaincodeStubInterface.GetState`に口座番号を渡せばAccountオブジェクト（のバイト配列）が得られます。Accountオブジェクトは口座名の更新や口座削除、入送出金処理でも必要になりますので、関数化しています。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/utils/validator.go
func GetAccount(APIstub shim.ChaincodeStubInterface, no string) (*models.Account, error) {
	var account = new(models.Account) // 空のAccountオブジェクトを生成
	accountBytes, err := APIstub.GetState(no)
	if err != nil { // GetStateでエラーが発生した場合は、空のAccountオブジェクトと発生したerrを返す
		return account, err
	} else if accountBytes == nil { // Accountオブジェクトが無い場合は、Error interfaceを実装したWarningResultオブジェクトを返す（WarningResultの詳細はgithubのソースコードを参照）
		msg := fmt.Sprintf("Account does not exist, no = %s", no)
		warning := &WarningResult{StatusCode: 404, Message: msg}
		return account, warning
	}

	if err := json.Unmarshal(accountBytes, account); err != nil {
		return account, err // AccountオブジェクトをJSONバイト配列化できなかった場合はエラーを返す
	}
	return account, nil // 正常に取得できた場合、エラーをnilとしてAccountオブジェクトを返す
}
```
```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/account.go
func (ac *AccountContract) RetrieveAccount(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	accountLogger.Infof("invoke RetrieveAccount, args=%s\n", args)
	if len(args) != 1 {
	}
	no := args[0]

	// 引数として渡された口座番号をキーにしてAccountオブジェクトをstateから取得
	account, err := utils.GetAccount(APIstub, no)
	jsonBytes, err := json.Marshal(account)
	// 取得したAccountオブジェクトのJSONバイト配列をレスポンスとして返す
	return shim.Success(jsonBytes)
}
```

## 口座名の更新（UpdateAccountName）
`shim.ChaincodeStubInterface.GetState`に口座番号を指定してAccountオブジェクトを取得し、名前を変更した後に`shim.ChaincodeStubInterface.PutState`すれば、口座名を変更できます。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/account.go
// UpdateAccountName : update the name of an account
func (ac *AccountContract) UpdateAccountName(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	accountLogger.Infof("invoke UpdateAccountName, args=%s\n", args)
	if len(args) != 2 {
	}
	no := args[0]
	name := args[1]

	// 引数として渡された口座番号をキーにしてAccountオブジェクトをstateから取得
	account, err := utils.GetAccount(APIstub, no)

	// 取得したAccountオブジェクトのnameを更新
	account.Name = name

	jsonBytes, err := json.Marshal(account)
	// nameを変更したAccountオブジェクトを保存
	if err := APIstub.PutState(no, jsonBytes); err != nil {
	}
	// 更新後のAccountオブジェクトのJSONバイト配列をレスポンスとして返す
	return shim.Success(jsonBytes)
}
```

## 口座の削除（DeleteAccount）
口座番号を指定して`shim.ChaincodeStubInterface.DelState`すれば、その口座を削除することができます。存在しない口座は削除できないので、DelStateする前にAccountオブジェクトの存在確認をしています。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/account.go
// DeleteAccount : delete an account
func (ac *AccountContract) DeleteAccount(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	accountLogger.Infof("invoke DeleteAccount, args=%s\n", args)
	if len(args) != 1 {
	}
	no := args[0]

	// 引数として渡された口座番号をキーにしてAccountオブジェクトをstateから取得（取得したAccountオブジェクトは使わないため、_で受ける
	_, err := utils.GetAccount(APIstub, no)

	// 口座番号を指定して、保存されているオブジェクトをstate dbから削除
	if err := APIstub.DelState(no); err != nil {
	}
	return shim.Success(nil)
}
```

# 入金・送金・出金
入金・出金・送金処理も、口座の操作と同様、GetQueryResultやGetState、PutStateを用いて記述することができます。

## 入金（Deposit)
入金先のAccountオブジェクトの残高更新と、入金イベントの記録を同時に行います。これら二つのオブジェクトの更新は、一つのトランザクションとして処理されます。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/event.go
// EventContract : a struct which has the methods related to event
type EventContract struct {
}

// Deposit : deposit amount to an account
func (ec *EventContract) Deposit(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	eventLogger.Infof("invoke Deposit, args=%s\n", args)
	if len(args) != 2 {
	}
	toAccountNo := args[0] // 一つ目の引数は、入金先の口座番号
	amountStr := args[1] // 二つ目の引数は、入金額（の文字列）

	// 入金額をvalidate（0より大きい整数であること）
	amount, err := utils.GetAmount(amountStr)

	// 入金先のAccountオブジェクトを取得
	toAccount, err := utils.GetAccount(APIstub, toAccountNo)

	// 一意なイベント番号を生成
	eventNo, err := utils.GetEventNo(APIstub)

	// 入金前の残高と入金後の残高を計算
	toAccountPreviousBalance := toAccount.Balance
	toAccount.Balance += amount

	// 残高の変化を記録したAccountStateオブジェクトを作成
	toAccountState := &models.AccountState{
		No:              toAccount.No,
		Name:            toAccount.Name,
		PreviousBalance: toAccountPreviousBalance,
		CurrentBalance:  toAccount.Balance,
	}

	// 入金イベントを記録したEventオブジェクトを作成
	event := &models.Event{
		ModelType:        types.EventModel,
		EventType:        types.DepositEvent,
		No:               eventNo,
		Amount:           amount,
		FromAccountState: nil,
		ToAccountState:   toAccountState,
	}

	// 残高更新後のAccountオブジェクトを保存
	toAccountBytes, err := json.Marshal(toAccount)
	if err := APIstub.PutState(toAccount.No, toAccountBytes); err != nil {
	}

	// 入金Eventオブジェクトを保存
	eventBytes, err := json.Marshal(event)
	if err := APIstub.PutState(event.No, eventBytes); err != nil {
	}
	return shim.Success(eventBytes)
}
```

## 送金（Remit）
送金元と送金先のAccountオブジェクトの残高更新と、送金イベントの記録を同時に行います。送金元の残高が0より小さくなる場合はエラーを発生させます。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/event.go
// Remit : remit amount from an account to another account
func (ec *EventContract) Remit(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	eventLogger.Infof("invoke Remit, args=%s\n", args)
	if len(args) != 3 {
	}
	fromAccountNo := args[0]
	toAccountNo := args[1]
	amountStr := args[2]

	// 送金額をvalidate（0より大きい整数であること）
	amount, err := utils.GetAmount(amountStr)

	// 送金元のAccountオブジェクトを取得
	fromAccount, err := utils.GetAccount(APIstub, fromAccountNo)
	// 送金先のAccountオブジェクトを取得
	toAccount, err := utils.GetAccount(APIstub, toAccountNo)

	if fromAccount.Balance < amount {
		// 残高不足の場合はエラー発生
	}

	// 一意なイベント番号を生成
	eventNo, err := utils.GetEventNo(APIstub)

	// 送金元の残高から送金額を減額
	fromAccountPreviousBalance := fromAccount.Balance
	fromAccount.Balance -= amount

	// 送金先の残高へ送金額を増額
	toAccountPreviousBalance := toAccount.Balance
	toAccount.Balance += amount

	// 送金元の残高の変化を記録したAccountStateオブジェクトを作成
	fromAccountState := &models.AccountState{
		No:              fromAccount.No,
		Name:            fromAccount.Name,
		PreviousBalance: fromAccountPreviousBalance,
		CurrentBalance:  fromAccount.Balance,
	}

	// 送金先の残高の変化を記録したAccountStateオブジェクトを作成
	toAccountState := &models.AccountState{
		No:              toAccount.No,
		Name:            toAccount.Name,
		PreviousBalance: toAccountPreviousBalance,
		CurrentBalance:  toAccount.Balance,
	}

	// 送金Eventオブジェクトを作成
	event := &models.Event{
		ModelType:        types.EventModel,
		EventType:        types.RemitEvent,
		No:               eventNo,
		Amount:           amount,
		FromAccountState: fromAccountState,
		ToAccountState:   toAccountState,
	}

	// 残高更新後のAccountオブジェクトを保存
	fromAccountBytes, err := json.Marshal(fromAccount)
	if err := APIstub.PutState(fromAccount.No, fromAccountBytes); err != nil {
	}

	toAccountBytes, err := json.Marshal(toAccount)
	if err := APIstub.PutState(toAccount.No, toAccountBytes); err != nil {
	}

	// 送金Eventオブジェクトを保存
	eventBytes, err := json.Marshal(event)
	if err := APIstub.PutState(event.No, eventBytes); err != nil {
	}
	return shim.Success(eventBytes)
}
```

## 出金（Withdraw）
出金元のAccountオブジェクトの残高更新と、出金イベントを記録します。送金と同様、出金元の残高が0より小さくなる場合はエラーを発生させます。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/event.go
// Withdraw : withdraw amount from an account
func (ec *EventContract) Withdraw(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	eventLogger.Infof("invoke Withdraw, args=%s\n", args)
	if len(args) != 2 {
	}
	fromAccountNo := args[0]
	amountStr := args[1]

	// 出金額をvalidate（0より大きい整数であること）
	amount, err := utils.GetAmount(amountStr)

	// 出金元のAccountオブジェクトを取得
	fromAccount, err := utils.GetAccount(APIstub, fromAccountNo)

	if fromAccount.Balance < amount {
		// 残高不足の場合はエラー発生
	}

	// 一意なイベント番号を生成
	eventNo, err := utils.GetEventNo(APIstub)

	// 出金後の残高を計算
	fromAccountPreviousBalance := fromAccount.Balance
	fromAccount.Balance -= amount

	// 出金元の残高の変化を記録したAccountStateオブジェクトを作成
	fromAccountState := &models.AccountState{
		No:              fromAccount.No,
		Name:            fromAccount.Name,
		PreviousBalance: fromAccountPreviousBalance,
		CurrentBalance:  fromAccount.Balance,
	}

	// 出金Eventオブジェクトを作成
	event := &models.Event{
		ModelType:        types.EventModel,
		EventType:        types.WithdrawEvent,
		No:               eventNo,
		Amount:           amount,
		FromAccountState: fromAccountState,
		ToAccountState:   nil,
	}

	// 残高更新後のAccountオブジェクトを保存
	fromAccountBytes, err := json.Marshal(fromAccount)
	if err := APIstub.PutState(fromAccount.No, fromAccountBytes); err != nil {
	}

	// 出金Eventオブジェクトを保存
	eventBytes, err := json.Marshal(event)
	if err := APIstub.PutState(event.No, eventBytes); err != nil {
	}
	return shim.Success(eventBytes)
}
```

## 入金・送金・出金の一覧取得
口座一覧の取得と同様、`shim.ChaincodeStubInterface.GetQueryResult`を呼び出すことでstateから最新のイベント（入金・送金・出金）を一覧で取得します。また引数として"deposit|remit|withdraw"のいずれかが引き渡されている場合は、それらのイベントに絞り込んで検索することもできるようにしています。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/event.go
// ListEvent : return a list of all events
func (ec *EventContract) ListEvent(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	eventLogger.Infof("invoke ListEvent, args=%s\n", args)
	if len(args) > 1 {
	}

	// "model_type"として"event"を指定したクエリを生成
	query := map[string]interface{}{
		"selector": map[string]interface{}{
			"model_type": types.EventModel,
		},
	}

	// 一つ目の引数が渡されている場合、"event_type"として"deposit" or "remit" or "withdraw"をクエリに追加指定
	if len(args) == 1 {
		switch args[0] {
		case types.DepositEvent.String():
			query["selector"].(map[string]interface{})["event_type"] = types.DepositEvent
		case types.RemitEvent.String():
			query["selector"].(map[string]interface{})["event_type"] = types.RemitEvent
		case types.WithdrawEvent.String():
			query["selector"].(map[string]interface{})["event_type"] = types.WithdrawEvent
		default:
			// "deposit", "remit", "withdraw" 以外の引数はエラー
		}
	}

	// クエリJSONのバイト配列を生成
	queryBytes, err := json.Marshal(query)
	eventLogger.Infof("Query string = '%s'", string(queryBytes))
	// クエリを用いてstateから最新のEvent一覧を取得するIteratorを生成
	resultsIterator, err := APIstub.GetQueryResult(string(queryBytes))
	// 取得したIteratorは、メソッド離脱時にCloseする
	defer resultsIterator.Close()

	// stateから取得したJSONを*Event型にキャストしてsliceに追加
	results := make([]*models.Event, 0)
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		event := new(models.Event)
		if err := json.Unmarshal(queryResponse.Value, event); err != nil {
		}
		results = append(results, event)
	}
	jsonBytes, err := json.Marshal(results)
	return shim.Success(jsonBytes)
}
```

# 更新履歴の取得
Hyperledter/fabricは台帳に記帳されたデータの更新情報を全て記録していますので、更新履歴を取得することも容易です。キーを指定して更新履歴を取得する`shim.ChaincodeStubInterface.GetHistoryForKey`というAPIが提供されてます。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/history.go
// HistoryContract : a struct which has the methods related to query Histories
type HistoryContract struct {
}

// ListHistory : return all histories of a contract
func (hc *HistoryContract) ListHistory(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	historyLogger.Infof("invoke ListHistory, args=%s\n", args)
	if len(args) != 1 {
	}
	no := args[0] // 引数としてキー（口座番号かイベント番号）を渡す

	// 指定したキーの更新履歴のIteratorを取得する
	resultsIterator, err := APIstub.GetHistoryForKey(no)
	// 取得したIteratorは、メソッド離脱時にCloseする
	defer resultsIterator.Close()

	// 指定した更新履歴をhistoryType型にキャストしてsliceに追加
	histories := make([]*historyType, 0)
	for resultsIterator.HasNext() {
		response, err := resultsIterator.Next()

		// 指定したキーが口座であれイベントであれ同じように扱えるように、Account型やEvent型ではなく、どのようなJSONでも受け付けることのできる map[string]interface{} 型を宣言する
		var state map[string]interface{}

		// 削除済みのオブジェクトでは無いならば、response.valueとして記帳時点のオブジェクトの状態がJSONバイト配列として得られる
		if !response.IsDelete {
			if err := json.Unmarshal(response.Value, &state); err != nil {
				historyLogger.Error(err.Error())
				return shim.Error(err.Error())
			}
		}
		// 変更履歴オブジェクトを生成
		history := &historyType{
			TxID:      response.TxId, // Hyperledger/fabricが生成したトランザクションID
			No:        no, // 口座番号かイベント番号
			State:     state, // 記帳された時点でのオブジェクトの状態
			Timestamp: time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String(), // トランザクションがコミットされた日時
			IsDelete:  response.IsDelete, // 削除済みフラグ
		}
		// 変更履歴オブジェクトをsliceに追加
		histories = append(histories, history)
	}
	jsonBytes, err := json.Marshal(histories)
	return shim.Success(jsonBytes)
}
```

# エントリポイントの更新
口座の操作や入送出金処理が実装できたら、cliやSDKから呼び出せるようにInvokeメソッドを更新します。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/fabric-payment.go
var accountContract = new(contracts.AccountContract)
var eventContract = new(contracts.EventContract)
var historyContract = new(contracts.HistoryContract)

func (s *EntryPoint) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
	function, args := APIstub.GetFunctionAndParameters()

	switch function {
	case "listAccount":
		return accountContract.ListAccount(APIstub, args)
	case "createAccount":
		return accountContract.CreateAccount(APIstub, args)
	case "retrieveAccount":
		return accountContract.RetrieveAccount(APIstub, args)
	case "updateAccountName":
		return accountContract.UpdateAccountName(APIstub, args)
	case "deleteAccount":
		return accountContract.DeleteAccount(APIstub, args)
	case "listEvent":
		return eventContract.ListEvent(APIstub, args)
	case "deposit":
		return eventContract.Deposit(APIstub, args)
	case "remit":
		return eventContract.Remit(APIstub, args)
	case "withdraw":
		return eventContract.Withdraw(APIstub, args)
	case "listHistory":
		return historyContract.ListHistory(APIstub, args)
	}
	msg := fmt.Sprintf("No such function. function = %s, args = %s", function, args)
	logger.Error(msg)
	return shim.Error(msg)
}
```

# couchdbのインデックス作成
クエリを用いてオブジェクトを取得する`shim.ChaincodeStubInterface.GetQueryResult`は柔軟に利用できて便利ですが、デフォルトの状態だと全オブジェクトを探索してしまいます。Hyperledger 1.1からは、couchdbにindexを作成させることができるようになりました。またindexを活用すると、検索結果のsortも指定することができます。（[CouchDB as the State Database](http://hyperledger-fabric.readthedocs.io/en/release-1.1/couchdb_as_state_database.html)）
クエリを用いる場合は、indexを上手く活用しましょう。

## 実行されるクエリ文字列を確認する
chaincodeコンテナのログを確認すると、どのようなクエリ文字列が利用されたのかがわかります。
例えば口座一覧を取得した場合は`{"selector":{"model_type":"account"}}`、入金イベント一覧を取得した場合は`{"selector":{"event_type":"deposit","model_type":"event"}}`のようなクエリ文字列が利用されていることがわかります。

```bash
ubuntu@node0:~$ docker logs -f $(docker ps -q -f name=dev-peer)
...
2018-03-04 03:16:50.976 UTC [contracts/account] Infof -> INFO 004 Query string = '{"selector":{"model_type":"account"}}'
...
2018-03-04 03:19:44.746 UTC [contracts/event] Infof -> INFO 012 Query string = '{"selector":{"event_type":"deposit","model_type":"event"}}'
```
発行されるクエリ文字列にマッチするように、indexを定義します。

## indexを定義する
chaincodeに `META-INF/statedb/couchdb/indexes/*.json` というJSONファイルが含まれていると、couchdbのindexを自動生成します。
例えば`model_type`を指定したクエリに対するindexとしては、次のようなJSONになります。

```json:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/META-INF/statedb/couchdb/indexes/model_index.json
{
  "index": {
    "fields": ["model_type"]
  },
  "ddoc": "modelIndexDoc",
  "name":"modelIndex",
  "type":"json"
}
```
index定義JSONのフォーマットは、[couchdbのマニュアル](http://docs.couchdb.org/en/2.1.1/api/database/find.html#db-index)を参照してください。
クエリ文字列のパターンを見ると、これ以外にも`model_type`と`event_type`を双方指定したクエリに対するindexも必要なことがわかります。

## couchdbのDBを確認する
チャネルにchaincodeをinstallしてinstantiateすると、couchdbに `<<CHANNEL_NAME>>_<<CHAINCODE_NAME>>` というstateを保持するためのDBが作成されます。今回のサンプルでは、 `fabric-sample_fabric-payment` がstate DBになります。

```bash
ubuntu@node0:~$ docker exec cli curl http://localhost:5984/_all_dbs/ | jq .
[
  "_global_changes",
  "_replicator",
  "_users",
  "fabric-sample_",
  "fabric-sample_fabric-payment",
  "fabric-sample_lscc"
]
```

## 設定したindexを確認する
上記で確認したstate DBに対して `/_index/`をGETすると、state DBにどのようなindexが設定されているか確認できます。`model_type`を指定したクエリに対するindexと、`model_type`及び`event_type`を指定したクエリに対するindexが定義されていることがわかります。

```bash
ubuntu@node0:~$ docker exec cli curl http://couchdb0:5984/fabric-sample_fabric-payment/_index/ | jq .
{
  "total_rows": 3,
  "indexes": [
    {
      "ddoc": null,
      "name": "_all_docs",
      "type": "special",
      "def": {
        "fields": [
          {
            "_id": "asc"
          }
        ]
      }
    },
    {
      "ddoc": "_design/modelEventIndexDoc",
      "name": "modelEventIndex",
      "type": "json",
      "def": {
        "fields": [
          {
            "model_type": "asc"
          },
          {
            "event_type": "asc"
          }
        ],
        "partial_filter_selector": {}
      }
    },
    {
      "ddoc": "_design/modelIndexDoc",
      "name": "modelIndex",
      "type": "json",
      "def": {
        "fields": [
          {
            "model_type": "asc"
          }
        ],
        "partial_filter_selector": {}
      }
    }
  ]
}
```

## indexが利用されることを確認する
couchdbのDBに対して `/_explain/` にクエリ文字列を与えてPOSTすると、そのクエリが発行された場合にどのようなindexが利用されるか、実行計画を確認することができます。設定したindexが利用されていることを確認しましょう。

```bash
ubuntu@node0:~$ docker exec cli curl -H "Content-Type: application/json" http://couchdb0:5984/fabric-sample_fabric-payment/_explain/ -X POST -d '{"selector":{"model_type":"account"}}' | jq .
{
  "dbname": "fabric-sample_fabric-payment",
  "index": {
    "ddoc": "_design/modelIndexDoc",
    "name": "modelIndex",
    "type": "json",
    "def": {
      "fields": [
        {
          "model_type": "asc"
        }
      ],
      "partial_filter_selector": {}
    }
  },
  "selector": {
    "model_type": {
      "$eq": "account"
    }
  },
...
```
```bash
ubuntu@node0:~$ docker exec cli curl -H "Content-Type: application/json" http://couchdb0:5984/fabric-sample_fabric-payment/_explain/ -X POST -d '{"selector":{"event_type":"deposit","model_type":"event"}}' | jq .
{
  "dbname": "fabric-sample_fabric-payment",
  "index": {
    "ddoc": "_design/modelEventIndexDoc",
    "name": "modelEventIndex",
    "type": "json",
    "def": {
      "fields": [
        {
          "model_type": "asc"
        },
        {
          "event_type": "asc"
        }
      ],
      "partial_filter_selector": {}
    }
  },
  "selector": {
    "$and": [
      {
        "event_type": {
          "$eq": "deposit"
        }
      },
      {
        "model_type": {
          "$eq": "event"
        }
      }
    ]
  },
...
```

# chaincodeの実行
お疲れ様でした。これで一通りchaincodeが完成です。作成したchaincodeを実行してみましょう。第一回で構築した、単体のdocker環境を用います。

![solo.png](https://qiita-image-store.s3.amazonaws.com/0/37648/69293fa7-f5c2-d0e4-2cbe-94e1d3774d10.png)


ただし、REST APIからSDK経由で呼び出すのではなく、cliコンテナから直接chaincodeを起動します。

## 開発用dockerコンテナを起動
1. fabric-payment-sample-docker/dev-solo/ に移動

    ```bash
    ubuntu@node0:~$ cd fabric-payment-sample-docker/dev-solo/
    ```

1. 各コンポーネントの暗号鍵やアーティファクトを生成する

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ source .env
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ export CA_ADMIN_PASSWORD=<<任意の文字列>>
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ ./generate_artifact.sh ${CA_ADMIN_PASSWORD}
```

1. 環境変数を設定する

    ```bash
    ORDERER_ADDRESS="orderer:7050"
    PEER_ADDRESS="peer:7051"
    LOCALMSPID="Org1MSP"
    PEER_MSPCONFIGPATH="/etc/hyperledger/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp"
    CHAINCODE_SRC="github.com/nmatsui/fabric-payment-sample-chaincode"
    CHAINCODE_VERSION="0.1"
    ```
    * `fabric-payment-sample-chaincode`以外の名前にした場合、CHAINCODE_SRCを適切に設定してください。

1. docker内部にbridge networkを作成する

    ```bash
    docker network create --driver=bridge --attachable=true fabric-sample-nw
    ```
    * docker-compose.yamlでは、`fabric-sample-nw`というネットワークにコンテナを接続します。ネットワーク名を変更した場合はdocker-compose.yamlも修正してください。

1. docker-composeを用いてコンテナ群を起動する

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker-compose -f docker-compose.yaml up -d
    ```
    * peer, couchdb, orderer, cliの各コンテナが起動していることを確認

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                              NAMES
41b3370d272c        fabric-payment/api           "npm start"              37 seconds ago      Up 35 seconds       0.0.0.0:3000->3000/tcp                             api
070fe3e78f64        hyperledger/fabric-tools     "/bin/bash"              37 seconds ago      Up 36 seconds                                                          cli
23efcf53f8c1        hyperledger/fabric-peer      "peer node start"        38 seconds ago      Up 36 seconds       0.0.0.0:32772->7051/tcp, 0.0.0.0:32771->7053/tcp   peer
b13b50dcffc8        hyperledger/fabric-couchdb   "tini -- /docker-ent…"   38 seconds ago      Up 37 seconds       4369/tcp, 9100/tcp, 0.0.0.0:32769->5984/tcp        couchdb
bf9770e74b1e        hyperledger/fabric-orderer   "orderer"                38 seconds ago      Up 37 seconds       0.0.0.0:32770->7050/tcp                            orderer
85d6c89d3596        hyperledger/fabric-ca        "sh -c 'fabric-ca-se…"   38 seconds ago      Up 36 seconds       0.0.0.0:32768->7054/tcp                            ca
   ```

1. cliコンテナからチャネルを作成する

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" cli peer channel create -o ${ORDERER_ADDRESS} -c ${CHANNEL_NAME} -f /etc/hyperledger/artifacts/channel.tx
    ```

1. peerをチャネルにjoinさせる

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer channel join -b /etc/hyperledger/artifacts/${CHANNEL_NAME}.block
    ```

1. anchorを更新する

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer channel update -o ${ORDERER_ADDRESS} -c ${CHANNEL_NAME} -f /etc/hyperledger/artifacts/Org1MSPanchors.tx
    ```

## chaincodeの初回インストール
1. 作成したchaincodeをインストールする

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode install -n ${CHAINCODE_NAME} -p ${CHAINCODE_SRC} -v ${CHAINCODE_VERSION}
    ```

1. chaincodeをinstantiateする
    * `-c '{"Args":[""]}'`にて渡す文字列は、chaincodeの`Init`メソッドの引数となります。今回のchaincodeのInitメソッドは何の引数も取らないため、何も渡しません。

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode instantiate -o ${ORDERER_ADDRESS} -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -v ${CHAINCODE_VERSION} -c '{"Args":[""]}' -P "OR ('Org1MSP.member')"
    ```
    * instantiateに成功すると、`dev-<<peerのID>>-<<CHAINCODE_NAME>>-バージョン番号-...`というchaincodeコンテナが自動起動しているはずです。

    ```bash
    ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker ps
CONTAINER ID        IMAGE                                                                                                            COMMAND                  CREATED             STATUS              PORTS                                              NAMES
6e4dd2c7baff        dev-peer0.org1.example.com-fabric-payment-0.1-5937be8eee46b8bca564d23f7316cb6fd253fb536b7aece7268323db087b8073   "chaincode -peer.add…"   2 minutes ago       Up 2 minutes                                                           dev-peer0.org1.example.com-fabric-payment-0.1
41b3370d272c        fabric-payment/api                                                                                               "npm start"              12 minutes ago      Up 12 minutes       0.0.0.0:3000->3000/tcp                             api
070fe3e78f64        hyperledger/fabric-tools                                                                                         "/bin/bash"              12 minutes ago      Up 12 minutes                                                          cli
23efcf53f8c1        hyperledger/fabric-peer                                                                                          "peer node start"        12 minutes ago      Up 12 minutes       0.0.0.0:32772->7051/tcp, 0.0.0.0:32771->7053/tcp   peer
b13b50dcffc8        hyperledger/fabric-couchdb                                                                                       "tini -- /docker-ent…"   12 minutes ago      Up 12 minutes       4369/tcp, 9100/tcp, 0.0.0.0:32769->5984/tcp        couchdb
bf9770e74b1e        hyperledger/fabric-orderer                                                                                       "orderer"                12 minutes ago      Up 12 minutes       0.0.0.0:32770->7050/tcp                            orderer
85d6c89d3596        hyperledger/fabric-ca                                                                                            "sh -c 'fabric-ca-se…"   12 minutes ago      Up 12 minutes       0.0.0.0:32768->7054/tcp                            ca
    ```

## cliからchaincodeを実行（検索）
ListAccountやRetrieveAccountなど、台帳の状態を更新しないchaincodeを実行する場合、cliコンテナで`peer chaincode query`というコマンドを実行します。
`-c '{"Args":["listAccount"]}'`の一つ目の引数は実際の処理を委譲する関数のラベルで、二つ目以降は関数に引数として渡されます。

```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/fabric-payment.go
func (s *EntryPoint) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
    function, args := APIstub.GetFunctionAndParameters() // 一つ目の引数がfunction、それ以降がargsに代入される

    switch function {
    case "listAccount":
        return accountContract.ListAccount(APIstub, args) // 一つ目の引数が "listAccount" だった場合、ListAccountメソッドに処理を委譲する
    ...
}
```

```bash
ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode query -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -c '{"Args":["listAccount"]}'
2018-03-04 07:51:50.577 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2018-03-04 07:51:50.578 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2018-03-04 07:51:50.578 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2018-03-04 07:51:50.578 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2018-03-04 07:51:50.578 UTC [chaincodeCmd] getChaincodeSpec -> DEBU 005 java chaincode disabled
2018-03-04 07:51:50.578 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB9070A7508031A0C0896B4FED40510...741A0D0A0B6C6973744163636F756E74
2018-03-04 07:51:50.578 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: 5AB6FA2458A4B4B88C0D50A9FABBBEE4B085D23F93F6662F94F021378493BDA3
2018-03-04 07:51:50.678 UTC [main] main -> INFO 008 Exiting.....
Query Result: []
```

まだ台帳に口座を登録していないため、結果は空配列です。

## cliからchaincodeを実行（更新）
CreateAccountやDepositなど、台帳の状態を更新する処理は `peer chaincode invoke`コマンドを用います。invokeの場合、トランザクションのコミット処理をordererに依頼しなければならないため、依頼先のordererのアドレスを引数として渡す必要があります。

```bash
ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode invoke -o ${ORDERER_ADDRESS} -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -c '{"Args":["createAccount", " ほげほげ"]}'
2018-03-04 08:03:04.194 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2018-03-04 08:03:04.194 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2018-03-04 08:03:04.196 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2018-03-04 08:03:04.197 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2018-03-04 08:03:04.197 UTC [chaincodeCmd] getChaincodeSpec -> DEBU 005 java chaincode disabled
2018-03-04 08:03:04.198 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB8070A7408031A0B08B8B9FED40510...6E740A0CE381BBE38192E381BBE38192
2018-03-04 08:03:04.198 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: E3951FEBD9BCAA12081D106639FB9F1EA3B9D00FD00B06808F3CF195C08B5DF8
2018-03-04 08:03:04.214 UTC [msp/identity] Sign -> DEBU 008 Sign: plaintext: 0AB8070A7408031A0B08B8B9FED40510...42E66439C107E3EAD2B99A6ED0E8BE91
2018-03-04 08:03:04.215 UTC [msp/identity] Sign -> DEBU 009 Sign: digest: 30E4C37CA4F49674731E8F2C119D7136F9D3AC98F2292337D4F06F9682E1AEEA
2018-03-04 08:03:04.220 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> DEBU 00a ESCC invoke result: version:1 response:<status:200 message:"OK" payload:"{\"model_type\":\"account\",\"no\":\"3391863708800601\",\"name\":\"\343\201\273\343\201\222\343\201\273\343\201\222\",\"balance\":0}" > payload:"\n BZG\005\236N2\020\000~N\336\215H\255.\230f$\356Dm\251|\230\220\t \355\232\376X\022\244\002\n\261\001\022\216\001\n\016fabric-payment\022|\n\022\n\0203391863708800601\032f\n\0203391863708800601\032R{\"model_type\":\"account\",\"no\":\"3391863708800601\",\"name\":\"\343\201\273\343\201\222\343\201\273\343\201\222\",\"balance\":0}\022\036\n\004lscc\022\026\n\024\n\016fabric-payment\022\002\010\002\032W\010\310\001\032R{\"model_type\":\"account\",\"no\":\"3391863708800601\",\"name\":\"\343\201\273\343\201\222\343\201\273\343\201\222\",\"balance\":0}\"\025\022\016fabric-payment\032\0030.1" endorsement:<endorser:"\n\007Org1MSP\022\226\006-----BEGIN CERTIFICATE-----\nMIICGTCCAcCgAwIBAgIRAMytpI0Cnu3FKTpKDIAe3xIwCgYIKoZIzj0EAwIwczEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMTgwMzA3MDcyMzM2WhcNMjgwMzA0MDcyMzM2\nWjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN\nU2FuIEZyYW5jaXNjbzEfMB0GA1UEAxMWcGVlcjAub3JnMS5leGFtcGxlLmNvbTBZ\nMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDzdWDN8BOKdV8fC21wqvR+JmA6jwnai\n7quHCeynh2XZmFDFXE9aAeXDagKe94Amhs3epx6J8Qp8NGKC8gMbm+ejTTBLMA4G\nA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIN/NM15GEaD1\n/7sk/O/LETlo5/vsn0/9v1o80vvtqsZCMAoGCCqGSM49BAMCA0cAMEQCIGLVBC25\nBGy5ik8w6pCy7egFiOk5q7/5B0DNvB5ZMjrgAiA/cAA4fe4sqzYBmfR1M0sVbSG5\nZbIaHX1pHlNydQ7HRg==\n-----END CERTIFICATE-----\n" signature:"0E\002!\000\361\007\251\034\327\220g\340~m`gb1\252d\000\014\314\303\036\220\t\275Am8\207lF\255^\002 \013\353\221\026,e\365>\335_\376\254\\\034\272\266B\346d9\301\007\343\352\322\271\232n\320\350\276\221" >
2018-03-04 08:03:04.221 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 00b Chaincode invoke successful. result: status:200 payload:"{\"model_type\":\"account\",\"no\":\"3391863708800601\",\"name\":\"\343\201\273\343\201\222\343\201\273\343\201\222\",\"balance\":0}"
2018-03-07 08:03:04.221 UTC [main] main -> INFO 00c Exiting.....
```

`peer chaincode invoke`コマンドはすぐに結果が戻ってきますが、この時点ではまだpeerにproposalを投げただけです。実際にトランザクションがコミットされるまでには時間がかかりますので、 `peer chaincode invoke`した直後に`peer chaincode query`を実行した場合、更新前のデータが表示される場合があります。RDB的には[MVCC](https://ja.wikipedia.org/wiki/MultiVersion_Concurrency_Control)のような挙動です。

コミットされたトランザクションは、queryすると取得することができます。

```bash
ubuntu@node0:~/fabric-payment-sample-docker/dev-solo$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode query -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -c '{"Args":["listAccount"]}'
2018-03-04 08:04:18.109 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2018-03-04 08:04:18.109 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2018-03-04 08:04:18.109 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2018-03-04 08:04:18.109 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2018-03-04 08:04:18.109 UTC [chaincodeCmd] getChaincodeSpec -> DEBU 005 java chaincode disabled
2018-03-04 08:04:18.110 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB8070A7408031A0B0882BAFED40510...741A0D0A0B6C6973744163636F756E74
2018-03-04 08:04:18.110 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: C0B00291DDB568DCB73CE29665073AFC43667F38E56B692EB285D7D5E074091F
Query Result: [{"model_type":"account","no":"3391863708800601","name":"ほげほげ","balance":0}]
```

# chaincodeの修正
同じチャネルに同じ名前のchaincodeを再度instantiateすることはできません。Hyperledger/fabric 1.1の時点では、chaincodeをuninstallするコマンドも提供されていないようです（[peer chaincdeコマンドのマニュアル](http://hyperledger-fabric.readthedocs.io/en/release-1.1/commands/peerchaincode.html)）

登録済みのchaincodeのupgradeをすることは可能ですので、chaincodeを修正してupgradeしてみましょう。

1. chaincodeの修正
    * とりあえずListAccountを起動すると、問答無用でエラーが発生するように変更する

    ```go:${GOPATH}/src/github.com/nmatsui/fabric-payment-sample-chaincode/contracts/account.go
    // ListAccount : return a list of all accounts
    func (ac *AccountContract) ListAccount(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
    	accountLogger.Error("ERROR!!!!")
    	return shim.Error("ERROR!!!!")
        ...
    }
    ```

1. ローカルでビルドが成功することを確認

    ```bash
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample-chaincode$ go build --tags nopkcs11 fabric-payment.go
    ```

1. バージョンを上げてchaincodeをインストール

    ```bash
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample-chaincode$ CHAINCODE_VERSION="0.1.1"
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample-chaincode$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode install -n ${CHAINCODE_NAME} -p ${CHAINCODE_SRC} -v ${CHAINCODE_VERSION}
    ```
    * `-v`は内部的には単なる文字列として扱われているようなので、インストール済みのchaincodeのバージョンと重複しなければどのような文字列でもかまわないと思います。日時とかでも良いかもしれません。

1. インストールしたバージョン番号を指定してchaincodeをupgrade
    * `peer chaincode upgrade`コマンドを用いてchaincodeをupgradeします。

    ```bash
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample-chaincode$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode upgrade -o ${ORDERER_ADDRESS} -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -v ${CHAINCODE_VERSION} -c '{"Args":[""]}' -P "OR ('Org1MSP.member')"
    ```
    * upgradeに成功すると、新たなバージョン番号でchaincodeコンテナが起動しているはずです。

    ```bash
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample-chaincode$ docker ps
CONTAINER ID        IMAGE                                                                                                              COMMAND                  CREATED              STATUS              PORTS                                              NAMES
d4aeb1cade49        dev-peer0.org1.example.com-fabric-payment-0.1.1-874aea1ca4ce200d18b066ef104cb747da2d4780a3a96664ef8c724d21c25ec5   "chaincode -peer.add…"   About a minute ago   Up About a minute                                                      dev-peer0.org1.example.com-fabric-payment-0.1.1
6e4dd2c7baff        dev-peer0.org1.example.com-fabric-payment-0.1-5937be8eee46b8bca564d23f7316cb6fd253fb536b7aece7268323db087b8073     "chaincode -peer.add…"   44 minutes ago       Up 44 minutes                                                          dev-peer0.org1.example.com-fabric-payment-0.1
41b3370d272c        fabric-payment/api                                                                                                 "npm start"              About an hour ago    Up About an hour    0.0.0.0:3000->3000/tcp                             api
070fe3e78f64        hyperledger/fabric-tools                                                                                           "/bin/bash"              About an hour ago    Up About an hour                                                       cli
23efcf53f8c1        hyperledger/fabric-peer                                                                                            "peer node start"        About an hour ago    Up About an hour    0.0.0.0:32772->7051/tcp, 0.0.0.0:32771->7053/tcp   peer
b13b50dcffc8        hyperledger/fabric-couchdb                                                                                         "tini -- /docker-ent…"   About an hour ago    Up About an hour    4369/tcp, 9100/tcp, 0.0.0.0:32769->5984/tcp        couchdb
bf9770e74b1e        hyperledger/fabric-orderer                                                                                         "orderer"                About an hour ago    Up About an hour    0.0.0.0:32770->7050/tcp                            orderer
85d6c89d3596        hyperledger/fabric-ca                                                                                              "sh -c 'fabric-ca-se…"   About an hour ago    Up About an hour    0.0.0.0:32768->7054/tcp                            ca
    ```

1. 更新したchaincodeを実行する

    ```bash
    ubuntu@node0:~/go/src/github.com/nmatsui/fabric-payment-sample-chaincode$ docker exec -e "CORE_PEER_LOCALMSPID=${LOCALMSPID}" -e "CORE_PEER_MSPCONFIGPATH=${PEER_MSPCONFIGPATH}" -e "CORE_PEER_ADDRESS=${PEER_ADDRESS}" cli peer chaincode query -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -c '{"Args":["listAccount"]}'
2018-03-07 08:32:21.665 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2018-03-07 08:32:21.666 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2018-03-07 08:32:21.666 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2018-03-07 08:32:21.666 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2018-03-07 08:32:21.666 UTC [chaincodeCmd] getChaincodeSpec -> DEBU 005 java chaincode disabled
2018-03-07 08:32:21.667 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB9070A7508031A0C0895C7FED40510...741A0D0A0B6C6973744163636F756E74
2018-03-07 08:32:21.667 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: F7B0A9B235CE8CDAD556F79B9626C805BB59B95ACBB8C34E46BA99D6734CDAE8
Error: Error endorsing query: rpc error: code = Unknown desc = chaincode error (status: 500, message: ERROR!!!!) - <nil>
    ...
    ```
    * 修正したように、エラーが発生していることが確認できます。

# 次回は
[次回](https://qiita.com/nmatsui/items/7eb4e214b628f96814a0)は、docker swarmクラスタ上でHyperledger/fabricを分散動作させてみます。