(四)、JavaSDK-Fabric更新链码与更新SDK封装

做一个简单的核验系统

可能很多朋友,用SDK调通了Fabric、不过E2E默认自带的智能合约是转账。
除了转账、还有很多可以玩的东西。
那么在学习更换智能合约链码的过程中,这次我们玩一个可以核验的智能合约。
- MD5加密技术
- Key/Value智能合约链码
- 修改script.sh启动脚本
- 通过cli终端去安装这份链码
- 底层封装
- service封装
- Test中调用


MD5加密技术

MD5加密是一种很常见的加密方式。如果自定义了比较长的加密字符串,解密起来是非常困难的。

public class MD5Util {

    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * 返回大写MD5
     *
     * @param origin
     * @param charsetname
     * @return
     */
    private static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))
                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
            else
                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
        } catch (Exception exception) {
        }
        return resultString.toUpperCase();
    }

    public static String MD5EncodeUtf8(String origin) {
        origin = origin + "这里可以放入你的加密字符串");
        return MD5Encode(origin, "utf-8");
    }


    private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    @Test
    public void fun1() {
        System.out.println(MD5EncodeUtf8("LANTIAN"));
    }

Key/Value智能合约链码

智能合约是用来满足Fabric实际运用需求的一种约束,诸如此类的chaincode有很多。比如小汽车、转账、核验等等。其实也就是一个类,然后有很多的属性,和一些操作的方法。

package main

import (
    "fmt"

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

// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}

// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
    // Get the args from the transaction proposal
    args := stub.GetStringArgs()
    if len(args) != 2 {
            return shim.Error("Incorrect arguments. Expecting a key and a value")
    }

    // Set up any variables or assets here by calling stub.PutState()

    // We store the key and the value on the ledger
    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
            return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
    }
    return shim.Success(nil)
}

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // Extract the function and args from the transaction proposal
    fn, args := stub.GetFunctionAndParameters()

    var result string
    var err error
    if fn == "set" {
            result, err = set(stub, args)
    } else { // assume 'get' even if fn is nil
            result, err = get(stub, args)
    }
    if err != nil {
            return shim.Error(err.Error())
    }

    // Return the result as success payload
    return shim.Success([]byte(result))
}

// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 2 {
            return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
    }

    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
            return "", fmt.Errorf("Failed to set asset: %s", args[0])
    }
    return args[1], nil
}

// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 1 {
            return "", fmt.Errorf("Incorrect arguments. Expecting a key")
    }

    value, err := stub.GetState(args[0])
    if err != nil {
            return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
    }
    if value == nil {
            return "", fmt.Errorf("Asset not found: %s", args[0])
    }
    return string(value), nil
}

// main function starts up the chaincode in the container during instantiate
func main() {
    if err := shim.Start(new(SimpleAsset)); err != nil {
            fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
    }
}

修改script.sh启动脚本

script.sh是一套区块链生成的脚本,从用户核验、到节点加入组织、按照智能合约、实例化智能合约、执行智能合约一套的脚本。

那么。根据需求。我们想修改智能合约的代码,我们通过修改script.sh的方式去解决

那么我们开始修改链码吧。其实很简单,直接把原本的关于链码的部分都去掉就好了。这样执行之后的区块链网络中,就没有自带的链码了。

#!/bin/bash
# Copyright London Stock Exchange Group All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
echo
echo " ____    _____      _      ____    _____           _____   ____    _____ "
echo "/ ___|  |_   _|    / \    |  _ \  |_   _|         | ____| |___ \  | ____|"
echo "\___ \    | |     / _ \   | |_) |   | |    _____  |  _|     __) | |  _|  "
echo " ___) |   | |    / ___ \  |  _ <    | |   |_____| | |___   / __/  | |___ "
echo "|____/    |_|   /_/   \_\ |_| \_\   |_|           |_____| |_____| |_____|"
echo

CHANNEL_NAME="$1"
: ${CHANNEL_NAME:="mychannel"}
: ${TIMEOUT:="60"}
COUNTER=1
MAX_RETRY=5
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

echo "Channel name : "$CHANNEL_NAME

verifyResult () {
    if [ $1 -ne 0 ] ; then
        echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!"
                echo "================== ERROR !!! FAILED to execute End-2-End Scenario =================="
        echo
        exit 1
    fi
}

setGlobals () {

    if [ $1 -eq 0 -o $1 -eq 1 ] ; then
        CORE_PEER_LOCALMSPID="Org1MSP"
        CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
        CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
        if [ $1 -eq 0 ]; then
            CORE_PEER_ADDRESS=peer0.org1.example.com:7051
        else
            CORE_PEER_ADDRESS=peer1.org1.example.com:7051
        fi
    else
        CORE_PEER_LOCALMSPID="Org2MSP"
        CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
        CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
        if [ $1 -eq 2 ]; then
            CORE_PEER_ADDRESS=peer0.org2.example.com:7051
        else
            CORE_PEER_ADDRESS=peer1.org2.example.com:7051
        fi
    fi

    env |grep CORE
}

createChannel() {
    setGlobals 0

        if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
        peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt
    else
        peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
    fi
    res=$?
    cat log.txt
    verifyResult $res "Channel creation failed"
    echo "===================== Channel \"$CHANNEL_NAME\" is created successfully ===================== "
    echo
}

updateAnchorPeers() {
        PEER=$1
        setGlobals $PEER

        if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
        peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt
    else
        peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
    fi
    res=$?
    cat log.txt
    verifyResult $res "Anchor peer update failed"
    echo "===================== Anchor peers for org \"$CORE_PEER_LOCALMSPID\" on \"$CHANNEL_NAME\" is updated successfully ===================== "
    sleep 5
    echo
}

## Sometimes Join takes time hence RETRY atleast for 5 times
joinWithRetry () {
    peer channel join -b $CHANNEL_NAME.block  >&log.txt
    res=$?
    cat log.txt
    if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then
        COUNTER=` expr $COUNTER + 1`
        echo "PEER$1 failed to join the channel, Retry after 2 seconds"
        sleep 2
        joinWithRetry $1
    else
        COUNTER=1
    fi
        verifyResult $res "After $MAX_RETRY attempts, PEER$ch has failed to Join the Channel"
}

joinChannel () {
    for ch in 0 1 2 3; do
        setGlobals $ch
        joinWithRetry $ch
        echo "===================== PEER$ch joined on the channel \"$CHANNEL_NAME\" ===================== "
        sleep 2
        echo
    done
}


## Create channel
echo "Creating channel..."
createChannel

## Join all the peers to the channel
echo "Having all peers join the channel..."
joinChannel

## Set the anchor peers for each org in the channel
echo "Updating anchor peers for org1..."
updateAnchorPeers 0
echo "Updating anchor peers for org2..."
updateAnchorPeers 2



echo
echo "===================== All GOOD, End-2-End execution completed ===================== "
echo

echo
echo " _____   _   _   ____            _____   ____    _____ "
echo "| ____| | \ | | |  _ \          | ____| |___ \  | ____|"
echo "|  _|   |  \| | | | | |  _____  |  _|     __) | |  _|  "
echo "| |___  | |\  | | |_| | |_____| | |___   / __/  | |___ "
echo "|_____| |_| \_| |____/          |_____| |_____| |_____|"
echo

exit 0

通过cli终端去安装这份链码

首先,我们还是把区块链网络中开启起来。

cd $GOPATH/src/github.com/hyperledger/fabric/examples/e2e_cli/
./network_setup.sh down(已经启动的话、或者不知道有没有启动)
./network_setup.sh up

会发现,到安装链码之前的步骤就停下来了。

===================== PEER3 joined on the channel "mychannel" ===================== 


===================== Anchor peers for org "Org1MSP" on "mychannel" is updated successfully ===================== 


===================== Anchor peers for org "Org2MSP" on "mychannel" is updated successfully ===================== 


===================== All GOOD, End-2-End execution completed ===================== 


 _____   _   _   ____            _____   ____    _____ 
| ____| | \ | | |  _ \          | ____| |___ \  | ____|
|  _|   |  \| | | | | |  _____  |  _|     __) | |  _|  
| |___  | |\  | | |_| | |_____| | |___   / __/  | |___ 
|_____| |_| \_| |____/          |_____| |_____| |_____|

接下里、我们开始进行安装新链码的操作。需要把链码放到指定的位置里面去。

服务器端
使用cli安装链码
进入cli

docker exec -it cli bash

安装链码

peer chaincode install -n mycc -v 0 -p  github.com/hyperledger/fabric/examples/chaincode/go/sacc

实例化链码、这边通常会久一些、耐心等待一下。

peer chaincode instantiate -n mycc -v 0 -c  '{"Args":["a","10"]}' -C mychannel

执行智能合约-修改a的值

peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C mychannel

查询智能合约-查询a的值

peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C mychannel

到这里、我们就完成了链码更新的所有操作了~!!!

底层封装

好了、我们终于可以开始修改SDK了

为了方便理解、先给出接口。

public interface ChaincodeService {


    /**
     * 执行智能合约
     *
     * @param key   键
     * @param Value 值
     */
    public void invoke(String key, String Value);


    /**
     * 查询智能合约
     *
     * @param key 键
     * @return 智能合约结果
     */
    public Map query(String key);
}

实现

@Service
public class ChaincodeServiceImpl implements ChaincodeService {

    @Autowired
    private CheckService checkService;

    @Autowired
    private OCRService ocrService;



    /**
     * 执行智能合约
     *
     * @param key   键
     * @param Value 值
     */
    @Override
    public void invoke(String key, String Value) {
        try {
            ChaincodeManager manager = FabricManager.obtain().getManager();
            String[] str = {key, Value};
            Map result = manager.invoke("set", str);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     * 查询智能合约
     *
     * @param key 键
     * @return 智能合约结果
     */
    @Override
    public Map query(String key) {
        try {
            ChaincodeManager manager = FabricManager.obtain().getManager();
            String[] str = {key};
            Map query = manager.query("query", str);
            return query;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

service封装

底层的封装只是简单的Key、Value交互、为了满足核验逻辑需求、我们进一步封装。
接口

public interface CheckService {
    /**
     * 增加税票信息
     *
     * @param information 图片参数
     */
    public void create(String information);


    /**
     * 检验税票信息
     *
     * @param information 税票信息
     * @return true代表检验通过  false代表验证不通过
     */
    public boolean check(String information);
}

实现,把传入的信息,进行MD5加密后存入区块链,也从fabric网络中取出MD5信息进行匹配。

@Service
public class CheckServiceImpl implements CheckService {
    @Autowired
    private OCRService ocrService;

    @Autowired
    private ChaincodeService chaincodeService;


    /**
     * 增加税票信息
     *
     * @param information 税票信息
     */
    @Override
    public void create(String information) {
        //MD5算法加密
        String MD5 = MD5Util.MD5EncodeUtf8(information);
        chaincodeService.invoke(MD5, MD5);

    }

    /**
     * 检验税票信息
     *
     * @param information 税票信息
     * @return true代表检验通过  false代表验证不通过
     */
    @Override
    public boolean check(String information) {
        Boolean flag = false;
        try {
            //MD5算法加密
            String MD5 = MD5Util.MD5EncodeUtf8(information);

            Map mapResult = chaincodeService.query(MD5);
            System.out.println(mapResult);

            flag = mapResult.get("code").contains("success");
            System.out.println(flag);


            return flag;
        } catch (Exception e) {

            return flag;
        }
    }
}

Test调用

好了、期待已久了吧。别着急,我们这就开始(๑′ᴗ‵๑)I玩

如果没有做封装的话

    @Test
    public void ChaincodeManagerTest() {
        try {
            ChaincodeManager manager = FabricManager.obtain().getManager();
            //String[] str = {"a"};
            //Map query = manager.query("query", str);
            //System.out.println(query);
            //Boolean flag = query.get("code").contains("success");
            //System.out.println(flag);

            //String[] str = {"a", "111"};
            //Map result = manager.invoke("set", str);
            //System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

如果、你封装之后的话。这里为什么要一个睡眠呢。因为执行的时候是多线程的。会导致MD5还未存入的时候、就进行读取。就会发生读不到的异常。

    @Test
    public void TestCreateAndCheck() throws InterruptedException {
        checkService.create("发票识别码开票日期发票代码");
        Thread.sleep(5000);
        checkService.check("发票识别码开票日期发票代码");
    }

这里的话、就完成了一个简易的发票核验区块链系统、是不是很简单~加油��

你可能感兴趣的:(fabric)