[Hyperledger-fabric] Hyperledger Fabric을 이용한 블록체인 네트워크 구축 3
홈 CCTV 접근 제어 시스템
요구사항
1. 기본 동작:
• 홈 CCTV에 접근할 수 있는 사람은 등록된 사용자만 가능.
• 등록되지 않은 사용자가 접근하려고 하면, 알림이 발생.
2. 구현 방식:
• Hyperledger Fabric 블록체인 네트워크를 활용해 사용자 데이터 관리.
• 등록된 사용자 정보는 블록체인의 원장(State)에 저장.
• 접근 요청 시 사용자가 등록된 사용자 목록에 있는지 확인.
• 등록되지 않은 사용자라면 이벤트를 발생시키거나 알림을 보냄.
전체 폴더 구조
project/
├── network/
│ ├── crypto-config/
│ ├── configtx.yaml
│ ├── docker-compose.yml
│ ├── channel-artifacts/
│ │ ├── genesis.block
│ │ ├── mychannel.tx
│ └── scripts/
│ ├── createChannel.sh
│ └── setPeerEnv.sh
├── chaincode/
│ └── cctvAccess/
│ ├── main.go
│ └── go.mod
├── backend/
│ ├── app.js
│ ├── connection-org1.json
│ ├── wallet/
│ └── package.json
├── frontend/
│ ├── public/
│ ├── src/
│ │ ├── App.js
│ │ └── index.js
│ └── package.json
└── README.md
2. Hyperledger Fabric 네트워크 설정
2.1 네트워크 환경 파일
• 경로: network/docker-compose.yml
• 내용:
Hyperledger Fabric 네트워크 환경을 설정합니다. Orderer, Peer, CLI 컨테이너를 정의하고, 각 노드가 인증서와 채널 정보를 참조하도록 설정합니다.
version: '2'
networks:
basic:
services:
orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer:latest
environment:
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_GENESISMETHOD=file
- ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
ports:
- 7050:7050
volumes:
- ./channel-artifacts:/etc/hyperledger/configtx
- ./crypto-config/ordererOrganizations:/etc/hyperledger/ordererOrganizations
peer0.org1.example.com:
container_name: peer0.org1.example.com
image: hyperledger/fabric-peer:latest
environment:
- CORE_PEER_ID=peer0.org1.example.com
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
ports:
- 7051:7051
volumes:
- ./crypto-config:/etc/hyperledger/crypto-config
2.2 채널 생성 스크립트
• 경로: network/scripts/createChannel.sh
• 내용: 채널을 생성하고 피어 노드가 채널에 참여하도록 설정하는 스크립트입니다.
#!/bin/bash
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/mychannel.tx
peer channel join -b mychannel.block
2.3 네트워크 실행
• 명령어:
docker-compose -f docker-compose.yml up -d
• 네트워크 상태 확인:
docker ps
3. 체인코드 작성
3.1 체인코드 파일
• 경로: chaincode/cctvAccess/main.go
• 내용: 등록된 사용자 관리 체인코드. 이전에 제공한 Go 언어 예제를 사용.
package main
import (
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SmartContract struct {
contractapi.Contract
}
// 사용자 등록
func (s *SmartContract) RegisterUser(ctx contractapi.TransactionContextInterface, key string, userName string) error {
exists, err := s.UserExists(ctx, key)
if err != nil {
return err
}
if exists {
return fmt.Errorf("user already registered with key: %s", key)
}
return ctx.GetStub().PutState(key, []byte(userName))
}
// 사용자 확인
func (s *SmartContract) VerifyUser(ctx contractapi.TransactionContextInterface, key string) (bool, string, error) {
data, err := ctx.GetStub().GetState(key)
if err != nil {
return false, "", fmt.Errorf("failed to read state for key %s: %v", key, err)
}
if data == nil {
return false, "", nil // 사용자 미등록
}
return true, string(data), nil
}
// 사용자 삭제
func (s *SmartContract) DeleteUser(ctx contractapi.TransactionContextInterface, key string) error {
exists, err := s.UserExists(ctx, key)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("user with key %s does not exist", key)
}
return ctx.GetStub().DelState(key)
}
// 사용자 존재 여부 확인 (내부 함수)
func (s *SmartContract) UserExists(ctx contractapi.TransactionContextInterface, key string) (bool, error) {
data, err := ctx.GetStub().GetState(key)
if err != nil {
return false, err
}
return data != nil, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(new(SmartContract))
if err != nil {
fmt.Printf("Error creating CCTV Access chaincode: %s", err.Error())
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting CCTV Access chaincode: %s", err.Error())
}
}
3.2 체인코드 패키징
• 명령어:
peer lifecycle chaincode package cctvAccess.tar.gz \
--path ./chaincode/cctvAccess --lang golang --label cctvAccess_1
3.3 체인코드 설치
• 각 Peer 노드에 체인코드 설치:
peer lifecycle chaincode install cctvAccess.tar.gz
3.4 체인코드 승인 및 커밋
• 승인:
peer lifecycle chaincode approveformyorg \
--channelID mychannel --name cctvAccess --version 1.0 \
--sequence 1 --signature-policy "OR('Org1MSP.peer')"
• 커밋:
peer lifecycle chaincode commit \
--channelID mychannel --name cctvAccess --version 1.0 \
--sequence 1
4. 백엔드 코드
const express = require('express');
const { Gateway, Wallets } = require('fabric-network');
const path = require('path');
const fs = require('fs');
const app = express();
app.use(express.json());
// 네트워크 연결 설정
const connectToNetwork = async () => {
const ccpPath = path.resolve(__dirname, 'connection-org1.json');
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
const wallet = await Wallets.newFileSystemWallet('./wallet');
const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
const network = await gateway.getNetwork('mychannel');
const contract = network.getContract('cctvAccess');
return contract;
};
// 사용자 등록 API
app.post('/api/register', async (req, res) => {
try {
const { key, userName } = req.body;
const contract = await connectToNetwork();
await contract.submitTransaction('RegisterUser', key, userName);
res.status(200).send(`User ${userName} registered with key ${key}`);
} catch (error) {
res.status(500).send(`Failed to register user: ${error}`);
}
});
// 사용자 확인 API
app.get('/api/verify/:key', async (req, res) => {
try {
const { key } = req.params;
const contract = await connectToNetwork();
const result = await contract.evaluateTransaction('VerifyUser', key);
const [exists, userName] = JSON.parse(result.toString());
if (exists) {
res.status(200).send(`Access granted for user: ${userName}`);
} else {
res.status(403).send('Access denied: User not registered');
}
} catch (error) {
res.status(500).send(`Failed to verify user: ${error}`);
}
});
// 사용자 삭제 API
app.delete('/api/delete/:key', async (req, res) => {
try {
const { key } = req.params;
const contract = await connectToNetwork();
await contract.submitTransaction('DeleteUser', key);
res.status(200).send(`User with key ${key} deleted`);
} catch (error) {
res.status(500).send(`Failed to delete user: ${error}`);
}
});
app.listen(3000, () => console.log('API server running on port 3000'));
4.1 주요 파일
• 경로: backend/app.js
• 내용: Hyperledger Fabric SDK를 사용하여 체인코드와 연동하는 Express 서버입니다.
5. 프론트엔드 코드
import React, { useState } from 'react';
const CCTVApp = () => {
const [key, setKey] = useState('');
const [userName, setUserName] = useState('');
const [response, setResponse] = useState('');
const registerUser = async () => {
const res = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key, userName }),
});
setResponse(await res.text());
};
const verifyUser = async () => {
const res = await fetch(`/api/verify/${key}`);
setResponse(await res.text());
};
return (
<div>
<input value={key} onChange={(e) => setKey(e.target.value)} placeholder="User Key" />
<input value={userName} onChange={(e) => setUserName(e.target.value)} placeholder="User Name" />
<button onClick={registerUser}>Register</button>
<button onClick={verifyUser}>Verify</button>
<p>{response}</p>
</div>
);
};
export default CCTVApp;
5.1 주요 파일
• 경로: frontend/src/App.js
• 내용: 사용자 등록 및 확인 인터페이스를 제공하는 React 컴포넌트.
6. 시스템 작동 흐름
1. 네트워크 상태:
• Orderer와 Peer가 정상적으로 작동하며, mychannel에 체인코드가 배포된 상태.
2. 사용자 등록:
• 키와 이름을 입력하여 사용자 등록.
• 데이터는 블록체인의 원장에 저장됨.
3.. 사용자 접근 확인:
• 입력된 키를 통해 사용자가 등록된 사용자 목록에 있는지 확인.
• 등록된 경우 접근 허용 메시지, 그렇지 않으면 접근 차단 메시지 표시.
이번 프로젝트를 통해 Hyperledger Fabric 기반의 홈 CCTV 접근 제어 시스템을 구현하며 블록체인 기술의 실제 적용 과정을 경험했습니다. 처음에는 Fabric 네트워크를 구성하는 작업이 쉽지 않았습니다. Orderer, Peer, 그리고 채널 설정 과정에서 많은 파일과 스크립트를 작성해야 했고, 특히 configtx.yaml과 crypto-config.yaml 같은 설정 파일 작성은 익숙하지 않아 실수가 잦았습니다. 하지만 이를 반복적으로 수정하고 디버깅하면서 Hyperledger Fabric의 네트워크 구조와 각 구성 요소의 역할을 명확히 이해하게 되었습니다.
체인코드를 작성하면서는 블록체인의 상태 데이터(State)에 데이터를 저장하고 조회하는 방식의 기본을 배울 수 있었습니다. 체인코드 내에서 사용자 등록, 확인, 삭제 기능을 구현하며 스마트 계약의 기본 동작을 이해했고, 이를 네트워크에 배포하는 과정을 통해 Fabric 체인코드 수명 주기를 익힐 수 있었습니다. 특히 체인코드의 패키징, 설치, 승인, 커밋이라는 일련의 과정은 Fabric의 체계적인 동작 방식을 체감할 수 있는 좋은 기회였습니다.