LoginSignup
2
2

More than 3 years have passed since last update.

【Hyperledger Fabric】PrivateDataを使ってみよう

Last updated at Posted at 2019-12-02

1.はじめに

12月になりましたね。寒いです。
AdventCalendarとは無縁の、どーも、のぶこふです。

今回は、Hyperledger Fabric(HLF)のPrivate Data Collections(プライベート データ コレクション:PrivateData)の使用方法について書きます。
「PrivateData?」という方は、公式ドキュメントをご参照ください。
▼公式ドキュメント
https://hyperledger-fabric.readthedocs.io/en/release-1.4/private-data/private-data.html

ざっくり説明すると「許可したPeer間でTxを共有するので、許可されていないPeerはTxの内容を見ることができませんよ」というものです。

  • 「チャネルでも同じようにTxの中身が見れないけど、違いはなんやねん?」
  • 「どこに保存されんねん?」
    • などなど、疑問が湧くかもしれませんが、公式ドキュメントや他の方が説明してくださっているので、私からは割愛させていただきます。 oO(ここで、少し興味を持ってくれたら嬉しい)

1.1.想定読者

  • HLFのPrivateDataを使いたい人

1.2.ゴール

  • HLFでPrivateDataが実装できるようになる

1.3.環境

  • HLF v1.4.4
  • CentOS7 On Oracle VM VirtualBox On Windows10
  • Chaincode:Golang

という環境で、今回の記事をお送りいたします。

2.つくる

marbleだと、すでにサンプルがあるのですが、今回は1から作成するということで、fabcarを修正していきます。

2.1.サンプルダウンロード

おなじみのサンプルを使用します。

# cd /opt
 - /opt に移動します
 - 任意のディレクトリで構いません
# curl -sSL http://bit.ly/2ysbOFE | bash -s 1.4.4
 - ダウンロードが行われます
 - 失敗した場合は、proxyの設定などを見直してみてください
# ls -l
 - 「fabric-samples」があることを確認してください
# cd fabric-samples/chaincode/fabcar/go
 - fabric-samples/chaincode/fabcar/go のフォルダまで移動します

2.2.collections_config.jsonの作成

collections_config.jsonを作成します。
collections_config.jsonは、その名の通り、PrivateDataの定義ファイルです。
PrivateDataの名前やポリシー、ブロックの存続時間などを設定できます。
詳細は「公式ドキュメント」をご参照ください。
▼公式ドキュメント
https://hyperledger-fabric.readthedocs.io/en/release-1.4/private-data-arch.html

# vi collections_config.json
 - collections_config.jsonファイルを開きます(新規作成)

collections_config.jsonの中身は、次のようにします。
おおまかな内容としては、下記の通りです。

  • collectionCars という名前にする
  • Org1MSP.memberのみ許可する
  • ブロックの生存時間は、10ブロックにする
collections_config.json
[
 {
    "name": "collectionCars",
    "policy": "OR('Org1MSP.member')",
    "requiredPeerCount": 0,
    "maxPeerCount": 3,
    "blockToLive":10,
    "memberOnlyRead": true
 }
]

2.3.Chaincode(CC)修正

PrivateDataに保存、取得ができるように、CCを修正します。

2.3.1.fabcar.go

# vi fabcar.go
 - fabcar.goファイルを開きます
fabcar.go:新しく構造体を作成します
// PrivateData用の構造体を定義します
// 通常のCarとは異なり「Price」を追加しています
type CarPrivate struct {
        Make   string `json:"make"`
        Model  string `json:"model"`
        Colour string `json:"colour"`
        Owner  string `json:"owner"`
        Pirce  string `json:"price"`
}
Invoke関数に、PrivateDataを保存、取得する関数を呼べるように修正します
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {

        // Retrieve the requested Smart Contract function and arguments
        function, args := APIstub.GetFunctionAndParameters()
        // Route to the appropriate handler function to interact with the ledger appropriately
        if function == "queryCar" {
                return s.queryCar(APIstub, args)
        } else if function == "initLedger" {
                return s.initLedger(APIstub)
        } else if function == "createCar" {
                return s.createCar(APIstub, args)
        } else if function == "queryAllCars" {
                return s.queryAllCars(APIstub)
        } else if function == "changeCarOwner" {
                return s.changeCarOwner(APIstub, args)
+        } else if function == "setPrivateData" {
+                return s.setPrivateData(APIstub, args)
+        } else if function == "getPrivateData" {
+                return s.getPrivateData(APIstub, args)
+        }

        return shim.Error("Invalid Smart Contract function name.")
}
fabcar.go:PrivateDataを保存する関数を作成します
func (s *SmartContract) setPrivateData(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        if len(args) != 6 {
                return shim.Error("Incorrect number of arguments. Expecting 6")
        }

        var car = CarPrivate{Make: args[1], Model: args[2], Colour: args[3], Owner: args[4], Price:args[5]}

        carAsBytes, _ := json.Marshal(car)
        err := APIstub.PutPrivateData("collectionCars", args[0], carAsBytes)
        if err != nil {
                return shim.Error(err.Error())
        }

        return shim.Success(nil)
}
fabcar.go:PrivateDataを取得する関数を作成します
func (s *SmartContract) getPrivateData(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        var jsonResp string
        if len(args) != 1 {
                return shim.Error("Incorrect number of arguments. Expecting 1")
        }

        carAsBytes, err := APIstub.GetPrivateData("collectionCars", args[0])
        if err != nil {
                        jsonResp = "{\"Error\":\"Failed to get state for " + args[0] + "\"}"
                        return shim.Error(jsonResp)
        } else if carAsBytes == nil {
                        jsonResp = "{\"Error\":\"Car does not exist: " + args[0] + "\"}"
                        return shim.Error(jsonResp)
        }

        return shim.Success(carAsBytes)
}

以上でCCの修正は終了です。
全量は下記を御覧ください。

全量(不要なコメントは削除しています)
fabcar.go
package main

import (
        "bytes"
        "encoding/json"
        "fmt"
        "strconv"

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

// Define the Smart Contract structure
type SmartContract struct {
}

// Define the car structure, with 4 properties.  Structure tags are used by encoding/json library
type Car struct {
        Make   string `json:"make"`
        Model  string `json:"model"`
        Colour string `json:"colour"`
        Owner  string `json:"owner"`
}

//                          <<<<<<  ADD  >>>>>
type CarPrivate struct {
        Make   string `json:"make"`
        Model  string `json:"model"`
        Colour string `json:"colour"`
        Owner  string `json:"owner"`
        Price  string `json:"price"`
}

func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
        return shim.Success(nil)
}

//                          <<<<<<  MOD  >>>>>
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {

        // Retrieve the requested Smart Contract function and arguments
        function, args := APIstub.GetFunctionAndParameters()
        // Route to the appropriate handler function to interact with the ledger appropriately
        if function == "queryCar" {
                return s.queryCar(APIstub, args)
        } else if function == "initLedger" {
                return s.initLedger(APIstub)
        } else if function == "createCar" {
                return s.createCar(APIstub, args)
        } else if function == "queryAllCars" {
                return s.queryAllCars(APIstub)
        } else if function == "changeCarOwner" {
                return s.changeCarOwner(APIstub, args)
        } else if function == "setPrivateData" {
                return s.setPrivateData(APIstub, args)
        } else if function == "getPrivateData" {
                return s.getPrivateData(APIstub, args)
        }

        return shim.Error("Invalid Smart Contract function name.")
}

func (s *SmartContract) queryCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        if len(args) != 1 {
                return shim.Error("Incorrect number of arguments. Expecting 1")
        }

        carAsBytes, _ := APIstub.GetState(args[0])
        return shim.Success(carAsBytes)
}

func (s *SmartContract) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response {
        cars := []Car{
                Car{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
                Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
                Car{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
                Car{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
                Car{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
                Car{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"},
                Car{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
                Car{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
                Car{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
                Car{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
        }

        i := 0
        for i < len(cars) {
                fmt.Println("i is ", i)
                carAsBytes, _ := json.Marshal(cars[i])
                APIstub.PutState("CAR"+strconv.Itoa(i), carAsBytes)
                fmt.Println("Added", cars[i])
                i = i + 1
        }

        return shim.Success(nil)
}

func (s *SmartContract) createCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        if len(args) != 5 {
                return shim.Error("Incorrect number of arguments. Expecting 5")
        }

        var car = Car{Make: args[1], Model: args[2], Colour: args[3], Owner: args[4]}

        carAsBytes, _ := json.Marshal(car)
        APIstub.PutState(args[0], carAsBytes)

        return shim.Success(nil)
}

func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {

        startKey := "CAR0"
        endKey := "CAR999"

        resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
        if err != nil {
                return shim.Error(err.Error())
        }
        defer resultsIterator.Close()

        // buffer is a JSON array containing QueryResults
        var buffer bytes.Buffer
        buffer.WriteString("[")

        bArrayMemberAlreadyWritten := false
        for resultsIterator.HasNext() {
                queryResponse, err := resultsIterator.Next()
                if err != nil {
                        return shim.Error(err.Error())
                }
                // Add a comma before array members, suppress it for the first array member
                if bArrayMemberAlreadyWritten == true {
                        buffer.WriteString(",")
                }
                buffer.WriteString("{\"Key\":")
                buffer.WriteString("\"")
                buffer.WriteString(queryResponse.Key)
                buffer.WriteString("\"")

                buffer.WriteString(", \"Record\":")
                // Record is a JSON object, so we write as-is
                buffer.WriteString(string(queryResponse.Value))
                buffer.WriteString("}")
                bArrayMemberAlreadyWritten = true
        }
        buffer.WriteString("]")

        fmt.Printf("- queryAllCars:\n%s\n", buffer.String())

        return shim.Success(buffer.Bytes())
}

func (s *SmartContract) changeCarOwner(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        if len(args) != 2 {
                return shim.Error("Incorrect number of arguments. Expecting 2")
        }

        carAsBytes, _ := APIstub.GetState(args[0])
        car := Car{}

        json.Unmarshal(carAsBytes, &car)
        car.Owner = args[1]

        carAsBytes, _ = json.Marshal(car)
        APIstub.PutState(args[0], carAsBytes)

        return shim.Success(nil)
}

//                          <<<<<<  ADD  >>>>>
func (s *SmartContract) setPrivateData(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        if len(args) != 6 {
                return shim.Error("Incorrect number of arguments. Expecting 6")
        }

        var car = CarPrivate{Make: args[1], Model: args[2], Colour: args[3], Owner: args[4], Price:args[5]}

        carAsBytes, _ := json.Marshal(car)
        err := APIstub.PutPrivateData("collectionCars", args[0], carAsBytes)
        if err != nil {
                return shim.Error(err.Error())
        }

        return shim.Success(nil)
}

//                          <<<<<<  ADD  >>>>>
func (s *SmartContract) getPrivateData(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        var jsonResp string
        if len(args) != 1 {
                return shim.Error("Incorrect number of arguments. Expecting 1")
        }

        carAsBytes, err := APIstub.GetPrivateData("collectionCars", args[0])
        if err != nil {
                        jsonResp = "{\"Error\":\"Failed to get state for " + args[0] + "\"}"
                        return shim.Error(jsonResp)
        } else if carAsBytes == nil {
                        jsonResp = "{\"Error\":\"Car does not exist: " + args[0] + "\"}"
                        return shim.Error(jsonResp)
        }

        return shim.Success(carAsBytes)
}

// The main function is only relevant in unit test mode. Only included here for completeness.
func main() {

        // Create a new Smart Contract
        err := shim.Start(new(SmartContract))
        if err != nil {
                fmt.Printf("Error creating new Smart Contract: %s", err)
        }
}

以上でCCの修正は完了です。
それでは実行・・・いえいえ、まだ大事な作業があります。

2.4.インスタンス化時にcollections_config.jsonを読み込むようにする

PrivateDataを使用するには、先程作成したcollections_config.jsonを読み込むようにする必要があります。
読み込みのタイミングは、インスタンス化を行うタイミングです。

# cd ../../../fabcar/
 - /opt/fabric-samples/fabcar/ まで移動します
# vi startFabric.sh
 - startFabric.sh を開きます

インスタンス化を行っている箇所で、collections_config.jsonを読み込むように加筆します。
98行目の末尾に「\」を追記する事も忘れずに!

     82 echo "Instantiating smart contract on mychannel"
     83 docker exec \
     84   -e CORE_PEER_LOCALMSPID=Org1MSP \
     85   -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
     86   cli \
     87   peer chaincode instantiate \
     88     -o orderer.example.com:7050 \
     89     -C mychannel \
     90     -n fabcar \
     91     -l "$CC_RUNTIME_LANGUAGE" \
     92     -v 1.0 \
     93     -c '{"Args":[]}' \
     94     -P "AND('Org1MSP.member','Org2MSP.member')" \
     95     --tls \
     96     --cafile ${ORDERER_TLS_ROOTCERT_FILE} \
     97     --peerAddresses peer0.org1.example.com:7051 \
+     98     --tlsRootCertFiles ${ORG1_TLS_ROOTCERT_FILE} \
+     99     --collections-config  /opt/gopath/src/github.com/chaincode/fabcar/go/collections_config.json

2.5.CC呼び出し元アプリ作成

最後にCCを呼び出すアプリを作成します。
と言っても、すでにあるのを複製して作成します。
まずはPrivateDataを保存するCCを呼び出すアプリです。

# cd javascript/
 - javascriptフォルダに移動します
# cp invoke.js setPrivateData.js
 - invoke.js をコピーします
# vi setPrivateData.js
 - コピーしたファイルを開きます
'use strict';

const { FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');

async function main() {
    try {

        // Create a new file system based wallet for managing identities.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // Check to see if we've already enrolled the user.
        const userExists = await wallet.exists('user1');
        if (!userExists) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

        // Create a new gateway for connecting to our peer node.
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });

        // Get the network (channel) our contract is deployed to.
        const network = await gateway.getNetwork('mychannel');

        // Get the contract from the network.
        const contract = network.getContract('fabcar');

        // Submit the specified transaction.
        // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom')
        // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR10', 'Dave')
-        await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');
+        await contract.submitTransaction('setPrivateData', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom', '1000' );
        console.log('Transaction has been submitted');

        // Disconnect from the gateway.
        await gateway.disconnect();

    } catch (error) {
        console.error(`Failed to submit transaction: ${error}`);
        process.exit(1);
    }
}

main();

続いて、PrivateDataを取得するアプリです。

# cp query.js getPrivateData.js
 - query.jsをコピーします
# vi getPrivateData.js
 - コピーしたファイルを開きます
'use strict';

const { FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');

async function main() {
    try {

        // Create a new file system based wallet for managing identities.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // Check to see if we've already enrolled the user.
        const userExists = await wallet.exists('user1');
        if (!userExists) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

        // Create a new gateway for connecting to our peer node.
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });

        // Get the network (channel) our contract is deployed to.
        const network = await gateway.getNetwork('mychannel');

        // Get the contract from the network.
        const contract = network.getContract('fabcar');

        // Evaluate the specified transaction.
        // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4')
        // queryAllCars transaction - requires no arguments, ex: ('queryAllCars')
-        const result = await contract.evaluateTransaction('queryAllCars');
+        const result = await contract.evaluateTransaction('getPrivateData', 'CAR12');
        console.log(`Transaction has been evaluated, result is: ${result.toString()}`);

    } catch (error) {
        console.error(`Failed to evaluate transaction: ${error}`);
        process.exit(1);
    }
}

main();

これで、ようやく準備が整いました。
それでは実行してみましょう。

3.うごかす

3.1.startFabric.sh実行

# cd ../
 - ディレクトリを移動します
# ls -l
 - startFabric.sh があることを確認します
# ./startFabric.sh
 - Errorが発生せずに終了すること

3.2.admin&user1作成

# cd javascript/
 - javascriptフォルダに移動します
# npm install
 - 不足しているライブラリをインストールします
 - エラーが出た場合は、メッセージに則って対応します
 - 私の場合は「nycをインストールしてください」と出たので、下記コマンドでインストールしています
 # npm audit
 # npm install --save-dev nyc@14.1.1
# node enrollAdmin.js
 - エラーが発生しないこと
# node registerUser.js
 - エラーが発生しないこと
# ls -l wallet/
 - adminとuser1のフォルダが作成されていること

3.3.PrivateData保存、取得

# node setPrivateData.js
 - Transaction has been submitted
# node getPrivateData.js
 - Transaction has been evaluated, result is: {"colour":"Black","make":"Honda","model":"Accord","owner":"Tom","price":"1000"}

無事に保存、取得が行えたようですね。

3.4.StateDBの取得

続いて、普通のStateDBへ取得を行ってみましょう。

# node query.js
 - Transaction has been evaluated, result is: [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]

PrivateDataに保存した「"Key":"CAR12"」がありませんね。

3.5.別ユーザで取得

デフォルトで作成すると、user1はOrg1の所属です。
Org2でも試してみます。

# docker exec -it cli bash
 - cliコンテナに入ります
 - 以下、cliコンテナ内

CHANNEL_NAME=mychannel
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:9051
 - 環境変数を設定
peer chaincode query -C $CHANNEL_NAME -n fabcar -c '{"Args":["getPrivateData","CAR12"]}'
 - Error: endorsement failure during query. response: status:500 message:"{\"Error\":\"Failed to get state for CAR12\"}"

エラーが返ってきました。
念の為、Org1でも試してみましょう。

CHANNEL_NAME=mychannel
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
 - 環境変数を設定
peer chaincode query -C $CHANNEL_NAME -n fabcar -c '{"Args":["getPrivateData","CAR12"]}'
 - {"colour":"Black","make":"Honda","model":"Accord","owner":"Tom","price":"1000"}

想定通りの結果が返ってきました。

3.6.ブロックの生存時間を確認する

ブロックの生存時間は10と設定していました。
Txを大量に発行して、ブロックを進めてみます。

peer channel fetch newest -o $ORDERER_CA -c mychannel  last.block
 - ブロック番号を確認
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar -c '{"Args":["changeCarOwner","CAR1", "Nobkov"]}'
 - 大量にTx発行(ブロックを進める)
peer channel fetch newest -o $ORDERER_CA -c mychannel  last.block
 - ブロック番号を確認
peer chaincode query -C $CHANNEL_NAME -n fabcar -c '{"Args":["getPrivateData","CAR12"]}'
 - Error: endorsement failure during query. response: status:500 message:"{\"Error\":\"Car does not exist: CAR12\"}"

削除されているので、参照ができなくなりました。

4.おわりに

さて、いかがでしたでしょうか。
CCの実装も特に大きく変えることなく、PrivateDataへ保存・取得する事ができました。
また、生存時間の確認も行えましたね。

PrivateDataはチャネルと同様に重要な概念となるので、しっかりと抑えておきたいところです。
(肝に銘じる)

今回はここまでです。
ありがとうございました。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2