블로그 아이덴티티블로그
글 개요
2019년 01월 10일 21:14다른 분야 공부/블록체인
본문

이 글은 Hexo 기반의 블로그에서 옮겨온 글이며, 원본 글은 2018년 8월 10일에 작성되었다.

지난 번 이더리움 개인 네트워크 써보기에 이어서, Hyperledger를 써보았다. 이는 여름 방학 블록체인 공부의 일환이다.

이 글의 내용으로는 Hyperledger Fabric 설치, 네트워크 실행 및 구축해보기, Hyperledger Fabric 애플리케이션 만들기를 담고 있다.

아래의 모든 코드블럭에서 사용자 계정에서 실행된 줄은 $, 루트 계정에서 실행된 줄은 >로 표기하였다.

Hyperledger

Hyperledger는 오픈소스 블록체인 기반의 원장(ledger) 및 관련 툴을 다루는 엄브렐라(우산) 구조[각주:1]의 프로젝트이다. Hyperledger 프로젝트에 대한 보다 본격적인 설명은 이 Medium 글에 잘 적혀 있다. Medium 계정이 있는 사람이라면 들어가서 박수쳐주자.

여기서는 Hyperledger의 구현체 중 하나인 Hyperledger Fabric에서 제공하는 기본적인 기능과 예제를 사용하였다. Hyperledger Fabric에 관한 보다 본격적인 설명은 이 블로그 글의 작성자분께서 아주 잘 설명해주셨다.

Hyperledger Fabric 준비하기

요구 사항

Hyperledger Fabric 사전 요구사항에 나와 있는 프로그램들이다. 괄호 안의 버전은 이 글을 작성할 때 사용한 Archlinux에 설치된 버전이다.

  • Docker 엔진 17.06.2 이상 (18.05.0-ce)
    이 글에서 도커를 많이 활용하게 되는데, 도커에 대한 간략한 개념은 이 블로그 글에 잘 설명되어 있다.
    WSL 환경에서 Docker는 제대로 작동하지 않을 수 있으니, WSL 말고 다른 환경에서 진행하길 권장한다.
    다음 단계로 넘어가기 이전에 도커 데몬 실행은 필수이다. sudo systemctl start docker 명령어로 도커 데몬을 시작할 수 있다.

  • Docker Compose 1.14.0 이상 (1.22.0)

  • node 8.9 이상 (v10.8.0 (by arch repo), v9.11.2 (by nvm))
    현재 노드 10 이상은 지원되지 않는 것으로 보인다.

  • npm (6.3.0 (by arch repo), v5.6.0 (by nvm))

  • Go 1.10.x (go1.10.3)

  • cURL 가장 최신 버전 (7.61.0)

  • python 2.7 (2.7.15)
    만일 커맨드라인에 python --version이라고 입력했을때 python 3 이상이 먼저 실행되어도 python 2가 설치되어 있으면 상관이 없었다. 이 글을 작성하는 도중 *.py 파일을 한 번도 본 적이 없지만, 그래도 요구사항이라고 하니 설치해주자.

이외에, Windows 사용자는 이 링크에서 추가 안내가 있다.

Hyperledger Fabric 다운로드

먼저 Hyperledger Fabric 공식 예제를 다운로드하자.

$ git clone https://github.com/hyperledger/fabric-samples
$ cd fabric-samples

그런 다음, Hyperledger Fabric을 설치하자.

$ cd scripts
$ sudo ./bootstrap.sh

위 명령어를 입력하면, 수 분 안에 도커 이미지 몇 개가 받아질 것이다. 저 bootstrap.sh, 친절하게 마지막에 설치된 Hyperledger Fabric 이미지 리스트를 출력해준다.

hyperledger/fabric-ca          1.2.0               66cc132bd09c        5 weeks ago         252MB
hyperledger/fabric-ca latest 66cc132bd09c 5 weeks ago 252MB
hyperledger/fabric-tools 1.2.0 379602873003 5 weeks ago 1.51GB
hyperledger/fabric-tools latest 379602873003 5 weeks ago 1.51GB
hyperledger/fabric-ccenv 1.2.0 6acf31e2d9a4 5 weeks ago 1.43GB
hyperledger/fabric-ccenv latest 6acf31e2d9a4 5 weeks ago 1.43GB
hyperledger/fabric-orderer 1.2.0 4baf7789a8ec 5 weeks ago 152MB
hyperledger/fabric-orderer latest 4baf7789a8ec 5 weeks ago 152MB
hyperledger/fabric-peer 1.2.0 82c262e65984 5 weeks ago 159MB
hyperledger/fabric-peer latest 82c262e65984 5 weeks ago 159MB
hyperledger/fabric-zookeeper 0.4.10 2b51158f3898 5 weeks ago 1.44GB
hyperledger/fabric-zookeeper latest 2b51158f3898 5 weeks ago 1.44GB
hyperledger/fabric-kafka 0.4.10 936aef6db0e6 5 weeks ago 1.45GB
hyperledger/fabric-kafka latest 936aef6db0e6 5 weeks ago 1.45GB
hyperledger/fabric-couchdb 0.4.10 3092eca241fc 5 weeks ago 1.61GB
hyperledger/fabric-couchdb latest 3092eca241fc 5 weeks ago 1.61GB

방금 받았으니 각 latest 이미지는 각 버전에서 태그만 다른 이미지일 것이다. 대략 8GB정도의 저장 공간이 소모되었다.

Hyperledger Fabric 구동하기

BYFN(Building Your First Network) 시나리오 예제 실행해보기

드디어, 첫 Hyperledger 네트워크를 구축해 볼 때이다. 아무것도 몰라도 좋다. fabric-samples/first-network 디렉토리 안의 byfn.sh를 실행해서 간단한 네트워크를 구성해보자.

$ sudo ./byfn.sh up

약 1~2분간 무언가가 실행되고, 마지막에 이 메시지가 뜨면 정상이다.

========= All GOOD, BYFN execution completed ===========


_____ _ _ ____
| ____| | \ | | | _ \
| _| | \| | | | | |
| |___ | |\ | | |_| |
|_____| |_| \_| |____/

이제, byfn.sh가 도대체 무슨 행동을 한 건지 살펴보자. 먼저, byfn.sh를 뜯어보면, 방금 실행시킨 코드는 아래와 같다.

function networkUp() {

# 도커 이미지 버전 확인
checkPrereqs

# fabric-samples/first-network/crypto-config 폴더가 없으면 아티팩트를 생성한다.
# 여기서 아티팩트란 "설정 기록 파일", 즉 "설정 파일"이라고 보면 된다.
if [ ! -d "crypto-config" ]; then
generateCerts
replacePrivateKey
generateChannelArtifacts
fi

# CouchDB 사용 옵션 여부에 따라 다르게 도커 이미지를 제작한다.
# 우리는 그런 옵션을 주지 않았으므로, 아래쪽 else 명령어로 도커 이미지가 제작되었을 것이다.
if [ "${IF_COUCHDB}" == "couchdb" ]; then
IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1
else
# 별다른 옵션을 주지 않았으므로, 아래 $COMPOSE_FILE 변수는 스크립트 초반에 자동으로
# 설정된 'docker-compose-cli.yaml' 일 것이다.
# 이 파일은 fabric-samples/first-network 디렉토리에 존재한다.
# docker-compose 명령어를 알기 쉽게 분석하면 아래와 같다.
#
# 'docker-compose' : docker-compose 프로그램에게 아래의 동작을 시킨다.
# '-f $COMPOSE_FILE' : docker-compose-cli.yaml 파일을 주고 도커 이미지를
# 만든다.
# 'up -d' : 만들어진 도커 이미지를 컨테이너로 백그라운드에서
# 실행시킨다.
#
# 참고로, 2>&1은 (간단히 말해서) 에러 메시지도 가감없이 다 보여주라는 뜻이다.
IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE up -d 2>&1
fi

# docker-compose가 정상 (0)으로 끝나지 않았으면 오류 메시지를 출력한다.
if [ $? -ne 0 ]; then
echo "ERROR !!!! Unable to start network"
exit 1
fi

# 이제 위의 docker-compose에서 실행시켰던 도커 컨테이너들 중 'cli' 이미지에 접속해서
# fabric-samples/first-network/scripts/script.sh를 실행하게 된다.
docker exec cli scripts/script.sh $CHANNEL_NAME $CLI_DELAY $LANGUAGE $CLI_TIMEOUT $VERBOSE

# 도커가 정상 (0)으로 끝나지 않았으면 오류 메시지를 출력한다.
if [ $? -ne 0 ]; then
echo "ERROR !!!! Test failed"
exit 1
fi
}

살펴보니 위의 코드는 사전 준비 작업일 뿐, 실제 몸통은 fabric-samples/first-network/scripts/script.sh에 있어보인다. 위에서 docker-compose 명령어로 만들었던 도커 컨테이너들은 sudo docker ps -a 명령으로 확인할 수 있었고, 9개의 컨테이너가 실행중이었다. 이번엔 script.sh를 살펴보자. 이를 참고해서 이제 직접 Hyperledger 네트워크를 만들어 볼 것이다.

# (전략)
# 아래에서 사용되는 함수들은 script.sh 혹은 같은 폴더에서 확인할 수 있는 utils.sh에 있다.

## 채널 만들기.
## 채널이라 함은 'Hyperledger Fabric 네트워크'라고 해도 되고, 둘 이상의 네트워크 구성원이 있는
## 논리적인 개념을 "채널"이라 한다. 채널에는 구성원('조직'), 그리고 그 '조직' 당 하나 이상의 중계
## 피어(anchor peer), 채널 정책, 분산 공유 원장, 원장을 관리하는 체인코드 애플리케이션, 주문
## 서비스 노드 등을 포함한다. 많은 개념들이 나왔지만 여기서 모든걸 다 알 필요는 없다. 일단은 그냥
## 그렇구나 하고 넘어가도 좋다.
echo "Creating channel..."
createChannel

## 아까 docker-compose로 만든 피어가 이 채널에 들어오도록 한다.
echo "Having all peers join the channel..."
joinChannel

## 등록한 피어를 각 조직의 중계 피어로 설정한다.
echo "Updating anchor peers for org1..."
updateAnchorPeers 0 1
echo "Updating anchor peers for org2..."
updateAnchorPeers 0 2

## peer0.org1, peer0.org2 피어에 체인코드 애플리케이션을 설치한다.
echo "Installing chaincode on peer0.org1..."
installChaincode 0 1
echo "Install chaincode on peer0.org2..."
installChaincode 0 2

## peer0.org2의 체인코드를 채널에 인스턴스화한다.
## 인스턴스화를 도커에 비유하자면, 도커 이미지로 도커 컨테이너를 만드는 것으로 보면 된다. 이 함수
## 소스 코드를 보면 "a"에 "100", "b"에 200을 넣어 초기화하는 구문이 포함되어있다.
echo "Instantiating chaincode on peer0.org2..."
instantiateChaincode 0 2

## peer0.org1의 체인코드를 채널에서 조회한다.
## chaincodeQuery 함수에서는 "a" 값을 찾아 주어진 예상값과 비교하는 절차가 함수 막바지에
## 포함되어 있다. 즉, 여기서 "a"와 100을 비교하는 것이다. 위의 체인코드가 인스턴스화에 성공했다면
## "a"에는 100이 저장되어 있을 것이고, 이 절차는 잘 넘어갈것이다.
echo "Querying chaincode on peer0.org1..."
chaincodeQuery 0 1 100

## peer0.org1과 peer0.org2의 체인코드를 호출한다.
## chaincodeInvoke 함수 소스 코드를 살펴보면 'mycc' 라는 체인코드를 호출하고 있는데, 이
## mycc라는 체인코드는 인자 3개를 받아 첫번째 인자에서 세번째 인자 만큼을 빼고, 두번째 인자에
## 세번째 인자 만큼을 더하는 것이다. chaincodeInvoke 함수에서는 이 mycc에게
## '{"Args":["invoke","a","b","10"]}'라는 인자를 넘겨주어, "a"에서 10을 빼어 "b"에게
## 더해주게된다. 진짜로 이렇게 되었는지, 아래쪽 chaincodeQuery 함수에서 확인해보게 될 것이다.
## 여기서 언급한 'mycc'의 체인코드의 소스 코드는 다음 링크에 있다: https://git.io/fN7hO
echo "Sending invoke transaction on peer0.org1 peer0.org2..."
chaincodeInvoke 0 1 0 2

## peer1.org2에 체인코드 애플리케이션을 설치한다.
echo "Installing chaincode on peer1.org2..."
installChaincode 1 2

## peer1.org2의 체인코드를 채널에서 조회한다.
## 위에서 'mycc' 체인코드를 호출했기에, "a"는 90, "b"는 100이 되어있을 것이다.
## 따라서, 여기선 "a"와 90을 비교하도록 chaincodeQuery 함수가 호출되었다.
echo "Querying chaincode on peer1.org2..."
chaincodeQuery 1 2 90

echo
echo "========= All GOOD, BYFN execution completed =========== "

# (후략)

script.sh는 간단히 채널과 피어를 만들고, 체인코드 조회와 호출이 정상 작동하는지 테스트해보는 것이었다. 이제 직접 네트워크를 만들어보기 위해, BYFN 네트워크를 종료하자. 아래 명령어를 입력하면 byfn.sh에서 생성한 도커 컨테이너까지 말끔히 없애준다.

$ sudo ./byfn.sh down

직접 Hyperledger 네트워크 빌드하기

이제 위 first-networkbyfn.sh에서 자동으로 수행한 작업을 수동으로 수행해보면서 나의 Hyperledger 네트워크를 직접 만들어보자. 먼저 적당한 곳에 작업 디렉토리를 따로 하나 생성해주자.

$ mkdir my-network
$ cd my-network

그리고 cryptogen 명령어를 이용해서 Hyperledger Fabric 인증서를 만들어주기 위해, cryptogen의 설정파일인 crypto-config.yaml 파일을 아래와 같이 작성하자. YAML 형식을 모른다면, 이 글에 간단하게 잘 적혀있다.

# 이 파일은 fabric-samples/first-network/crypto-config.yaml의 한글 번역본이며, 일부
# 자체 조사하여 설명을 첨했다.
# -------------------------------------------------------------------------
# "OrdererOrgs" - 다른 원고 노드(orderer node)를 관리하는 조직의 정의
# -------------------------------------------------------------------------
OrdererOrgs:
# -------------------------------------------------------------------------
# Orderer
# -------------------------------------------------------------------------
- Name: Orderer
Domain: example.com
# -------------------------------------------------------------------------
# "Specs"
# -------------------------------------------------------------------------
# Specs는 조직의 특성을 정의하는 행렬이다. 각 특성 항목은 두개의 필드로 구성되어있다.
# - Hostname: (필수) 도메인을 제외한 호스트 이름
# - CommonName: (선택) 기본 설정 "{{Hostname}}.{{Domain}}"을 덮어쓰는 FQDN
# -------------------------------------------------------------------------
Specs:
- Hostname: orderer
# -------------------------------------------------------------------------
# "PeerOrgs" - 피어 노드를 관리하는 조직의 정의
# -------------------------------------------------------------------------
PeerOrgs:
# -------------------------------------------------------------------------
# Org1
# -------------------------------------------------------------------------
- Name: Org1
Domain: org1.example.com
# -------------------------------------------------------------------------
# "EnableNodeOUs"
# -------------------------------------------------------------------------
# Hyperledger Fabric MSP에서 해당 노드에 대해 신원 분류를 할지 말지를 결정하는 항목이다.
# 아래 문서가 이 변수의 정확한 사용에 대해 알 수 있는 유일한 문서인데, 보아도 정확히 어떤
# 작용을 하는지 아직 이해할 수 없었다.
# https://hyperledger-fabric.readthedocs.io/en/release-1.1/msp.html
# -------------------------------------------------------------------------
EnableNodeOUs: true
# -------------------------------------------------------------------------
# "Specs" - 위에서 설명한 Specs 항목을 이곳에도 지정할 수 있다.
# -------------------------------------------------------------------------
# "Template"
# -------------------------------------------------------------------------
# 템플릿으로부터 1개 이상의 호스트를 자동으로 만들 수 있게 하는 옵션이다. 호스트 이름은
# 기본적으로 peer%d 형식으로, 0 부터 지정개수 - 1 까지 지정된다. 아래와 같은 옵션을 지정할
# 수 있다.
# - Count: 생성할 호스트의 수
# - Start: 호스트 번호 세기가 시작하는 숫자
# - Hostname: 지정하는 호스트 이름이다. (기본값: {{.Prefix}}{{.Index}})
# 참고: 위의 "Specs" 와 이 "Template"을 모두 활용한다면 같은 이름의 호스트가 생성될 수
# 있으므로 이름 충돌에 주의해야한다.
# -------------------------------------------------------------------------
Template:
Count: 2
# -------------------------------------------------------------------------
# "Users"
# -------------------------------------------------------------------------
# 옵션은 한 가지다.
# - Count: 관리자(Admin) 이외의 유저 수
# -------------------------------------------------------------------------
Users:
Count: 1
# -------------------------------------------------------------------------
# Org2: 위의 Org1과 같은 방법으로 옵션을 지정하였다.
# -------------------------------------------------------------------------
- Name: Org2
Domain: org2.example.com
EnableNodeOUs: true
Template:
Count: 2
Users:
Count: 1

이제 이 파일을 cryptogen 프로그램으로 넣어주자. 나는 fabric-samples/my-network 디렉토리에서 진행했다. 자신이 만든 작업 디렉토리와 fabric-samples/bin 디렉토리의 상대 위치를 잘 살펴가며 진행하자.

$ ../bin/cryptogen generate --config=./crypto-config.yaml
org1.example.com
org2.example.com

이제 crypto-config라는 디렉토리가 작업 디렉토리 안에 생성되었을 것이다. 이제는 Hyperledger Fabric 네트워크(채널)의 첫 블록(제네시스 태초마을 블록)을 만들자. configtxgen 프로그램에 넣어줄 설정 파일을 먼저 만들어야 한다. configtx.yaml 파일 안에 아래의 내용을 입력하자.

# 이 파일은 fabric-samples/first-network/configtx.yaml의 복사본이며, 자세한 주석은
# 삭제하였다.
Organizations:
- &OrdererOrg
Name: OrdererOrg
ID: OrdererMSP
MSPDir: crypto-config/ordererOrganizations/example.com/msp
- &Org1
Name: Org1MSP
ID: Org1MSP
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
AnchorPeers:
- Host: peer0.org1.example.com
Port: 7051
- &Org2
Name: Org2MSP
ID: Org2MSP
MSPDir: crypto-config/peerOrganizations/org2.example.com/msp
AnchorPeers:
- Host: peer0.org2.example.com
Port: 7051
Capabilities:
Global: &ChannelCapabilities
V1_1: true
Orderer: &OrdererCapabilities
V1_1: true
Application: &ApplicationCapabilities
V1_2: true
Application: &ApplicationDefaults
Organizations:
Orderer: &OrdererDefaults
OrdererType: solo
Addresses:
- orderer.example.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 10
AbsoluteMaxBytes: 99 MB
PreferredMaxBytes: 512 KB
Kafka:
Brokers:
- 127.0.0.1:9092
Organizations:
Profiles:
TwoOrgsOrdererGenesis:
Capabilities:
<<: *ChannelCapabilities
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2
TwoOrgsChannel:
Consortium: SampleConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
Capabilities:
<<: *ApplicationCapabilities

이제 configtxgen을 실행해서 태초마을 블록을 생성하자.

$ mkdir channel-artifacts
$ ../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block -channelID mychannel

이후 차례로 채널 구성 트랜잭션을 보내 채널 구성을 완료하자.

$ ../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP

이제 기본 도커 설정 파일을 작성하고 Hyperledger Fabric 네트워크를 실행하자. 도커 설정도 first-network의 것을 그대로 가져왔으며, docker-compose의 설정 파일을 다루는 것은 이 글의 범주에서 벗어나므로 크게 다루지는 않는다. 다만, 아래 명령을 수행함으로써 하나의 orderer 컨테이너(노드)와 네 개의 peer 컨테이너(노드)가 생성된다.

$ cp ../first-network/base . -r
$ cp ../first-network/docker-compose-cli.yaml .
$ sudo env IMAGE_TAG=latest CHANNEL_NAME=mychannel docker-compose -f docker-compose-cli.yaml up -d
WARNING: The COMPOSE_PROJECT_NAME variable is not set. Defaulting to a blank string.
Creating network "my-network_byfn" with the default driver
Creating volume "my-network_orderer.example.com" with default driver
Creating volume "my-network_peer0.org1.example.com" with default driver
Creating volume "my-network_peer1.org1.example.com" with default driver
Creating volume "my-network_peer0.org2.example.com" with default driver
Creating volume "my-network_peer1.org2.example.com" with default driver
Creating peer1.org2.example.com ... done
Creating peer0.org1.example.com ... done
Creating peer1.org1.example.com ... done
Creating peer0.org2.example.com ... done
Creating orderer.example.com ... done
Creating cli ... done

이제 docker ps -a 명령어로 확인하면 6개의 도커 이미지가 돌아가고 있음을 확인할 수 있을 것이다.

지금부터는 도커에 접속하여 위의 first-network에서 했던 테스트를 직접 해보자. 먼저 도커에 접속해본다. 참고로, cli 도커는 우분투 환경을 기반으로 구성되어있다. (다른 컨테이너는 확인해보지 않았다.)

$ sudo docker exec -it cli bash
> (이 부분은 곧 업데이트할 예정이다.)

이렇게, 씹고 뜯고 맛보고 즐기고 볼 장 다 봤으면 아래의 명령어로 나의 네트워크를 종료하자.

$ sudo docker-compose -f docker-compose-cli.yaml down --volumes --remove-orphans

Hyperledger Fabric 애플리케이션 실행해보기

마지막으로, Hyperledger Fabric 예제 애플리케이션 fabcar를 구동해보자. 먼저 아래 코드를 fabric-samples/fabcar 디렉토리에서 실행하자.

$ sudo ./startFabric.sh

1분 가량의 작업이 끝나면 다음과 같은 메시지가 나타난다.

Start by installing required packages run 'npm install'
Then run 'node enrollAdmin.js', then 'node registerUser'

The 'node invoke.js' will fail until it has been updated with valid arguments
The 'node query.js' may be run at anytime once the user has been registered

저들의 말에 순순히 따라주자. 먼저 npm i 명령어를 입력하여 package.json에 적힌 요구 npm 패키지를 받아오자.

$ npm i

위 명령 실행 도중에 나타나는 cc의 warning과 note는 신경쓰지 않아도 좋다. (ERR가 발생하면 더이상 진행이 불가능하다. 아래 문제 해결에 해결법이 적혀 있다.) 의존 패키지 설치가 완료되었으면 Adminuser1을 영입할 차례이다. 아래 명령어로 관리자와 user1을 만들어주자.

$ node enrollAdmin.js
$ node registerUser.js

그런 다음, 간단하게 쿼리(조회)를 해보자.

$ node query.js
(...)
Response 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"}}
]

이제 원장(ledger)을 업데이트해보자. invoke.js 파일을 열어 chaincodeIdfcn 등을 담고 있는 var request를 아래와 같이 수정한다.

// createCar chaincode function - requires 5 args, ex: args: ['CAR12', 'Honda', 'Accord', 'Black', 'Tom'],
// changeCarOwner chaincode function - requires 2 args , ex: args: ['CAR10', 'Dave'],
// must send the proposal to endorsing peers
var request = {
chaincodeId: 'fabcar',
fcn: 'createCar',
args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'],
chainId: 'mychannel',
txId: tx_id
};

이제 invoke.js를 실행한 뒤(node invoke.js), query.js 파일을 열어 const request를 아래와 같이 수정한다.

const request = {
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR10']
};

이제 query.js를 실행시키면 아래와 같은 결과가 나온다.

$ node query.js
(...)
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"}

아까 invoke.js를 통해 추가한 CAR10이 제대로 추가되었음을 확인할 수 있다. 이제 아래 명령어로 생성된 도커를 지우고 모든 것을 끝내자.

# fabric-samples/basic-network 디렉토리로 이동
$ cd ../basic-network
# fabcar에서 이용한 basic-network를 종료하고 삭제
$ sudo ./teardown.sh

문제 해결

bootstrap.sh 실행 중 문제 발생

Cannot connect to the Docker daemon

Warning: failed to get default registry endpoint from daemon (Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?). Using system default: https://index.docker.io/v1/
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

만일 위와 같은 메시지가 뜬다면 꼭 sudo systemctl start docker 혹은 sudo service docker start를 해주자. 도커 데몬이 실행중이지 않아서 발생하는 오류이다.

Got permission denied

Warning: failed to get default registry endpoint from daemon (Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.37/info: dial unix /var/run/docker.sock: connect: permission denied). Using system default: https://index.docker.io/v1/
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.37/images/create?fromImage=hyperledger%2Ffabric-peer&tag=1.2.0: dial unix /var/run/docker.sock: connect: permission denied
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.37/images/hyperledger/fabric-peer:1.2.0/tag?repo=hyperledger%2Ffabric-peer&tag=latest: dial unix /var/run/docker.sock: connect: permission denied

도커 데몬 소켓을 가져올 권한이 없어서 발생하는 오류이다. bootstrap.sh를 관리자 권한(sudo)으로 실행해주자.

byfn.sh 실행 중 문제 발생

Couldn't connect to Docker daemon

ERROR: Couldn't connect to Docker daemon at http+docker://localhost - is it running?

If it's at a non-standard location, specify the URL with the DOCKER_HOST environment variable.
ERROR !!!! Unable to start network

byfn.sh가 도커 데몬 소켓을 가져오지 못해 발생하는 오류이다. byfn.sh를 관리자 권한(sudo)으로 실행해주자.

configtxgen 실행 중 문제 발생

Could not find configtx.yaml.

2018-08-11 05:26:53.159 KST [common/tools/configtxgen/localconfig] Load -> CRIT 003 Error reading configuration:  Unsupported Config Type ""
2018-08-11 05:26:53.159 KST [common/tools/configtxgen] func1 -> ERRO 004 Could not find configtx.yaml. Please make sure that FABRIC_CFG_PATH or --configPath is set to a path which contains configtx.yaml

configtxgen에서 configtx.yaml 파일을 찾지 못해 벌어진 일이다. --configPath 옵션은 오류 메시지와 달리 현재 버전에서 -configPath로 하이픈이 하나 줄어들었고, 또한 그 역할은 설정 파일의 위치를 지정하는 것이 아닌 configtx.yaml이 들어있는 디렉터리를 지정하는 옵션이므로, 설정 파일 이름은 꼭 configtx.yaml로 해주도록 하고, 되도록이면 해당 configtx.yaml 파일이 들어있는 디렉토리에서 configtxgen 명령을 실행하자.

Error writing genesis block

2018-08-11 05:42:53.723 KST [common/tools/configtxgen] main -> CRIT 00c Error on outputBlock: Error writing genesis block: open ./channel-artifacts/genesis.block: no such file or directory

파일을 만드려는 디렉토리가 존재하지 않아 발생하는 오류이다. mkdir 명령어로 channel-artifacts 디렉토리를 먼저 생성해주자.

npm i 실행 중 문제 발생

node-pre-gyp ERR!

node-pre-gyp ERR! Tried to download(403): https://storage.googleapis.com/grpc-precompiled-binaries/node/grpc/v1.10.1/node-v64-linux-x64-glibc.tar.gz
node-pre-gyp ERR! Pre-built binaries not found for grpc@1.10.1 and node@10.8.0 (node-v64 ABI, glibc) (falling back to source compile with node-gyp)
node-pre-gyp ERR! Pre-built binaries not installable for grpc@1.10.1 and node@10.8.0 (node-v64 ABI, glibc) (falling back to source compile with node-gyp)
node-pre-gyp ERR! Hit error Connection closed while downloading tarball file

grpc가 최신 node 버전에 대응하지 않아서 발생한 문제이다. node 9, npm 5에서 정상적으로 작동하니, node 10을 이용하고 있다면 아래와 같이 nvm을 이용하여 낮은 버전의 node를 사용하자.

# 아래 끝 부분에는 자신이 이용하는, bash 계열의 쉘을 적자. 딱히 없다면 그냥 bash로 두면 된다.
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
$ export NVM_DIR="$HOME/.nvm"
$ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
$ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
$ nvm install 9
$ nvm use 9
# 이후 이 쉘에서 fabric-samples/fabcar 디렉토리로 가 node와 npm 작업을 수행한다.

참고로, nvm의 사용은 fish 에서 지원되지 않는다. 혹시 fish를 사용중이라면 zshbash에서 nvm을 설치 및 실행하자.

node invoke.js 실행 중 문제 발생

Promise is rejected

error: [client-utils.js]: sendPeersProposal - Promise is rejected: Error: Invalid Smart Contract function name.

참고한 문서


  1. '표준안' 만을 정의하는 하나의 메인 프로젝트를 중심으로, 그 표준안을 구현한 여러 서브 프로젝트가 있는 프로젝트의 형태를 말한다. [본문으로]
가장 위로