Hyperledger Fabricのチェーンコードで`getStateByRange` の仕様が微妙っぽかったので調べてみたら微妙だった

Hyperledger Fabricのチェーンコードを実装していて気になったのでメモ。

Hyperledger Fabricってなんぞ？という方はこちらをご参考ください。

Amazon Managed BlockchainがリリースされたのでHyperledger Fabricも合わせて情報をまとめてみた - Qiita
https://qiita.com/kai_kou/items/5a968f42c5f96296f6fe

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7e


## `getStateByRange` メソッドの範囲指定で`endKey` は取得範囲に入らない

ひとまず再現コード抜粋。TypeScriptで実装しています。

TypeScriptを利用した実装方法については下記をご参考ください。

Hyperledger Fabricのチェーンコード(Node.js)をTypeScriptで実装できるようにしてみた - Qiita
https://qiita.com/kai_kou/items/0726c9a462bdcaf3856c


```ts:チェーンコード実装(抜粋)
(略)

  async Invoke(stub: ChaincodeStub): Promise<ChaincodeResponse> {
    let ret = stub.getFunctionAndParameters();
    const {fcn, params} =  stub.getFunctionAndParameters();
    console.info(ret);

    const method = (this as any)[fcn];
    if (!method) {
      throw new Error('Received unknown function ' + ret.fcn + ' invocation');
    }

    try {
      let payload = await method(stub, params);
      return Shim.success(payload);
    } catch (err) {
      return Shim.error(err);
    }
  }

  async queryByRange(stub: ChaincodeStub, args: string[]): Promise<Buffer> {
    if (args.length != 2) {
      throw new Error('Incorrect number of arguments.');
    }

    let startKey = args[0];
    let endKey = args[1];
    let results = [];

    let iterator = await stub.getStateByRange(startKey, endKey);
    while(true) {
      const res = await iterator.next();
      if (res.value && res.value.value.toString()) {
        let item = JSON.parse(res.value.value.toString('utf8'));
        results.push(item);
      }

      if (res.done) {
        await iterator.close();
        return Buffer.from(JSON.stringify(results));
      }
    }
  }
(略)
```

- ~~`value.value` とか気持ち悪いなぁ~~
- `.toString('utf8')` って`'utf8'` を指定しないと'JSON.parse' できなくてハマります。

ステートDBには以下のようなデータが保存されている前提です。

```json:ステートDBの中身
[
  {"id":"hoge01","hoge":"hoge"},
  {"id":"hoge02","hoge":"hoge"},
  {"id":"hoge03","hoge":"hoge"}
]
```

で、チェーンコードの`queryByRange` を実行して情報を取得してみます。
※Fablic CLIで実行、パラメータはいろいろ省略しています。

```console
> docker exec cli peer chaincode query \
  -o $ORDERER -C mychannel -n $cc_name \
  -c '{"function":"queryByRange","Args":["hoge01", "hoge01"]}'

[]
```

あれ取れない。

```console
> docker exec cli peer chaincode query \
  -o $ORDERER -C mychannel -n $cc_name \
  -c '{"function":"queryByRange","Args":["hoge01", "hoge02"]}'

[{"id":"hoge01","hoge":"hoge"}]
```

お、まさか。。。

```console
> docker exec cli peer chaincode query \
  -o $ORDERER -C mychannel -n $cc_name \
  -c '{"function":"queryByRange","Args":["hoge01", "hoge03"]}'

[{"id":"hoge01","hoge":"hoge"},{"id":"hoge02","hoge":"hoge"}]
```

なるほど。`endKey` は取得対象外なのね。。。



気になって仕様を確認してみたら確かにそういう仕様でした。


Hyperledger Fabric Node.js Contract and Shim Class: ChaincodeStub
https://fabric-shim.github.io/master/fabric-shim.ChaincodeStub.html


>Returns a range iterator over a set of keys in the ledger. The iterator can be used to iterate over all keys between the startKey (inclusive) and endKey (exclusive).

Google翻訳
>元帳の一連のキーに対する範囲反復子を返します。 反復子はstartKey（これを含む）とendKey（これを含まない）の間のすべてのキーを反復処理するために使用できます。

**endKey（これを含まない）**

oh...
そうですか。

キーに数値を含めてインクリメントする場合ならまあ対応は楽ですが、キーにULIDを利用しようとしてたのでこれはちっと面倒な感じです。むむむぅ。


## 参考

Hyperledger Fabric Node.js Contract and Shim Class: ChaincodeStub
https://fabric-shim.github.io/master/fabric-shim.ChaincodeStub.html

Amazon Managed BlockchainがリリースされたのでHyperledger Fabricも合わせて情報をまとめてみた - Qiita
https://qiita.com/kai_kou/items/5a968f42c5f96296f6fe

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7e

Hyperledger Fabricのチェーンコード(Node.js)をTypeScriptで実装できるようにしてみた - Qiita
https://qiita.com/kai_kou/items/0726c9a462bdcaf3856c