본문 바로가기

프로그래밍/블록체인

Fabric application 만들기(1)

체인코드 작성하기

패브릭 네트워크를 구성하는 작업은 기존의 Samples를 사용하고 체인코드 작성에 대해 설명하겠습니다. Hyperledger Fabric Samples를 설치하고 실행해본 분을 대상으로 작성되었습니다. 만약 어려움이 있다면 Samples 실행하기(1)Samples 실행하기(2)를 읽어보시기 바랍니다.

기존 패브릭 네트워크

패브릭 네트워크를 구성하기 위해서는 configtx.yaml, docker-compose.yaml, crypto-config.yaml 혹은 fabric-ca 구성 작업이 필요하기 때문에 이번 시간에는 패브릭 네트워크 예제를 이용하여 네트워크 구성 부분은 넘어가도록 하겠습니다.

아래의 명령어를 통해 fabcar에서 사용한 test-network와 config 디렉토리를 원하는 작업 디렉토리에 복사합니다. 저는 작업 디렉토리를 example로 만들고 복사하겠습니다.

mkdir example

cp -r fabric-samples/test-network example/

# core.yaml가 존재하는 config 디렉토리
cp -r fabric-samples/config example/

패브릭 네트워크 실행

네트워크를 실행시키기 위해 network.sh up과 createChannel 옵션을 주고 파일을 실행시킵니다.

cd example/test-network

./network.sh up createChannel

node.js용 체인코드

체인코드를 만들기 위해서는 example 디렉토리에 chaincode 디렉토리를 생성하고 체인코드될 디렉토리를 만들어줍니다. 그리고 npm init으로 package.json을 만들고 fabric-contract-apifabric-shim을 설치합니다.

chaincode 디렉토리 이름은 변경되면 안됩니다. scripts/deployCC.sh에서 디렉토리 이름이 chaincode로 정해져있습니다. deployCC.sh 파일에서 수정하신다면 변경하셔도 상관없습니다.

저는 간단한 message를 저장하는 블록체인 애플리케이션을 작성해보겠습니다.

# 체인코드 디렉토리
cd chaincode

# message 디렉토리
mkdir message

# 패브릭 node.js용 api 설치
npm init -y
npm install fabric-contract-api
npm install fabric-shim

package.json에 script 부분에 test 아래에 "start": "fabric-chaincode-node start"을 작성해주셔야 체인코드가 실행될 수 있습니다.

index.js에 message contract를 만들고 module exports 해줘야합니다. 기존의 fabcar를 바탕으로 작성한 contract입니다.

'use strict';

const { Contract } = require('fabric-contract-api');
class Message extends Contract {
    async initLedger(ctx) {
        const msg = [
            {
                sender: 'inomp',
                receiver: 'fabric',
                message: 'Hello fabric!',
                datetime: '2020-05-15 23:48:33'
            },
            {
                sender: 'fabric',
                receiver: 'inomp',
                message: 'Hello inomp!',
                datetime: '2020-05-15 23:49:00'
            }
        ];
        for (let i = 0; i < msg.length; i++) {
            await ctx.stub.putState('MSG' + i, Buffer.from(JSON.stringify(msg[i])));
        }
    }

    async createMsg(ctx, key, sender, receiver, message, datetime){
        const msg = {
            sender,
            receiver,
            message,
            datetime,
        };
        await ctx.stub.putState(key, Buffer.from(JSON.stringify(msg)));
    }

    async queryAllMsgs(ctx) {
        const startKey = 'MSG0';
        const endKey = 'MSG999';
        const allResults = [];
        for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) {
            const strValue = Buffer.from(value).toString('utf8');
            let record;
            try {
                record = JSON.parse(strValue);
            } catch (err) {
                console.log(err);
                record = strValue;
            }
            allResults.push({ Key: key, Record: record });
        }
        console.info(allResults);
        return JSON.stringify(allResults);
    }

}

module.exports.Message = Message;
module.exports.contracts = [ Message ];

이후에 scripts/deployCC.sh에 부분에서 함수부분과 chaincode 부분을 수정해주셔야합니다.

# 3번째 줄
CC_RUNTIME_LANGUAGE="javascript"

# 30번째 줄
    CC_SRC_PATH="../chaincode/fabcar/go/"

# 272번째 줄 
    peer chaincode query -C $CHANNEL_NAME -n fabcar -c '{"Args":["queryAllMsgs"]}' >&log.txt

이제 실행하면 fabcar가 아닌 message의 애플리케이션이 되는 것을 보실 수 있습니다. 자세한 contract api 부분은 여기를 참고하시면 여러 함수들을 사용하실 수 있습니다.

앞서 Samples 실행하기(2)에서 invoke하는 부분도 가능하니 참고하여 invoke, queryAll을 하면 추가된 것을 확인하실 수 있습니다.

./network.sh deployCC

# 실행 결과 마지막
[{"Key":"MSG0","Record":{"sender":"inomp","receiver":"fabric","message":"Hello fabric!","datetime":"2020-05-15 23:48:33"}},{"Key":"MSG1","Record":{"sender":"fabric","receiver":"inomp","message":"Hello inomp!","datetime":"2020-05-15 23:49:00"}}]
# user setting
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
# invoke
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls true --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n fabcar --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"createMsg","Args":["MSG2","inomp","you","Hi!!", "2020-05-16 12:24:50"]}'

# 확인
peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllMsgs"]}'

/p>

test-network를 복사하지 않고 deploCC.sh와 체인코드만 수정하여 하시면 조금 더 편하게 진행하실 수 있습니다.

(참고)디렉토리 구조