[BlockChain/Hyperledger Fabric] 스마트 계약을 채널에 배포하기 (1)

yujeongkwon·2023년 8월 9일
0

BlockChain

목록 보기
6/10
  • 최종 사용자는 스마트 계약을 호출하여 블록체인 원장과 상호 작용
  • Hyperledger Fabric에서 스마트 계약은 체인코드라고 하는 패키지에 배포
  • 트랜잭션의 유효성을 검사하거나 원장을 쿼리하려는 조직은 피어에 체인코드를 설치해야 함.
  • 채널에 가입된 피어에 체인코드 설치 -> 채널 구성원은 체인코드를 채널에 배포 -> 체인코드의 스마트 계약을 사용하여 채널 원장에서 자산을 생성하거나 업데이트
  • 체인코드는 패브릭 체인코드 수명 주기라는 프로세스를 사용하여 채널에 배포
  • Fabric 체인코드 라이프사이클을 사용하면 여러 조직에서 체인코드를 사용하여 트랜잭션을 생성하기 전에 체인코드가 작동하는 방식에 동의할 수 있다.
    • ex) 보증 정책 : 트랜잭션을 검증하기 위해 체인코드를 실행해야 하는 조직을 지정
      <-> 채널 구성원은 Fabric 체인코드 라이프사이클을 사용하여 체인코드 보증 정책에 동의해야 함.

네트워크 시작

  • 테스트 네트워크 디렉터리로 이동

    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ^C
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ 
    
  • 초기 상태에서 작동하려고 합니다. 다음 명령은 활성 또는 오래된 도커 컨테이너를 종료하고 이전에 생성된 아티팩트를 제거

    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh down
    
  • 테스트 네워크 시작(./network.sh up)

    • createChannel 명령이 하는 일
      • 이름이 지정된 채널 생성
      • mychannel 두 개의 채널 구성원인 Org1과 Org2
    • 출력
      ~~머라막 뜨고
      Anchor peer set for org 'Org2MSP' on channel 'mychannel'
      Channel 'mychannel' joined
      
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh up createChannel
      

Logspout 설정(선택 사항)

  • 선택이지만 log를 볼 수 있으면 조으니까 하자 ㅇㅇ
  • Logspout을 설치하고 구성하는 스크립트, monitordocker.sh는 /fabric-samples/test-network에 이미 포함되어 있음
  • Logspout 도구는 터미널에 지속적으로 로그를 스트리밍하므로 새 터미널 창 켜야함.
  • 새 터미널을 열고 test-network로 이동
    yujeong@yujeong-VirtualBox:~$ cd  go/src
    yujeong@yujeong-VirtualBox:~/go/src$ cd fabric-samples/test-network
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ 
    
  • 다음 명령을 실행하여 Logspout을 시작
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./monitordocker.sh fabric_test
    
  • 출력
    Starting monitoring on all containers on the network fabric_test
    Unable to find image 'gliderlabs/logspout:latest' locally
    latest: Pulling from gliderlabs/logspout
    8572bc8fb8a3: Pull complete 
    bd801371a862: Pull complete 
    58100c398b34: Pull complete 
    Digest: sha256:2d81c026e11ac67f7887029dbfd7d36ee986d946066b45c1dabd966278eb5681
    Status: Downloaded newer image for gliderlabs/logspout:latest
    a6139444d7dc3604282693a4fbd4d64f320e9bb27d02bcc5a10e66a3cc2b05d3
    
  • 처음에는 로그가 표시되지 않음 but 체인코드를 배포하면 변경됨.
  • 터미널창을 크고 글을 작게하는거 추천

스마트 컨트렉트 패키징

  • 피어에 설치하기 전에 체인코드를 패키ㅇ 해야함
  • go로 작성된 스마트 컨트렉트로 가자 ㅇㅇ

Go

  • 체인코드를 패키징하기 전에 체인코드 종속성을 설치해야 함.

  • 자산 전송(기본) 체인코드의 Go 버전이 포함된 폴더로 이동

    yujeong@yujeong-VirtualBox:~/go/src$ cd fabric-samples/asset-transfer-basic/chaincode-go
    
  • Go 모듈을 사용하여 체인코드 종속성을 설치

    • 종속성은 파일 asset-transfer-basic/chaincode-godp go.mod에 있음.

    • go.mod

      • Go 모듈을 정의하고 해당 모듈이 의존하는 다른 모듈들의 버전을 기록하는데 사용
      • Go 프로젝트의 의존성을 정확히 관리
      • 다른 환경에서도 동일한 의존성을 사용하여 프로젝트를 빌드할 수 있도록 도와주는 중요한 파일
      • Fabric 계약 API를 스마트 컨트렉트 패키지를 가져와서 중요함
    • 함보셈 ㅋㅋ

      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/chaincode-go$ cat go.mod
        module github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go
      
        go 1.17
      
        require (
            github.com/hyperledger/fabric-chaincode-go v0.0.0-20230228194215-b84622ba6a7a
            github.com/hyperledger/fabric-contract-api-go v1.2.1
            github.com/hyperledger/fabric-protos-go v0.3.0
            github.com/stretchr/testify v1.8.2
            google.golang.org/protobuf v1.28.1
        )
      
        ~머시기
        
    • asset-transfer-basic/chaincode-go/chaincode/smartcontract.go을 열어서 스마트 계약 시작 시 계약 API를 사용하여 SmartContract 유형을 정의하는 방법을 확인 가능

    • API 설명서

    • 아래 체인코드 작성법에 대 더 궁금하다면 : Writing Your First Chaincode

    • 지금은 테스트 듀토리얼 따라지만 나중에 ㄹㅇ 우리 네트크 생성할 때는 아래 처럼 생성하고 nano로 코드 쓰면 될듯, nano 이후 줄은 체인코드 패키징 전 모듈화 하는건데 난 빈파일에 해서 그런가 안대네 나중에 코드 진짜 짜고 해봄. ㅇㅇ

      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ mkdir atcc && cd atcc
      mkdir: `atcc' 디렉터리를 만들 수 없습니다: 파일이 있습니다
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ls
      CHAINCODE_AS_A_SERVICE_TUTORIAL.md  compose           prometheus-grafana
      README.md                           configtx          scripts
      addOrg3                             log.txt           setOrgEnv.sh
      atcc                                monitordocker.sh  system-genesis-block
      basic.tar.gz                        network.sh
      channel-artifacts                   organizations
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ cd atcc
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network/atcc$ go mod init atcc
      go: creating new go.mod: module atcc
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network/atcc$ touch atcc.go
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network/atcc$ ls
      atcc.go  go.mod
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network/atcc$ nano atcc.go
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network/atcc$ go mod tidy
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network/atcc$ go mod vendor
      go: no dependencies to vendor
      
    • test-network에 있는 체인코드 확인하기

      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic$ cat chaincode-go/chaincode/smartcontract.go
      
package chaincode
import (
  "encoding/json"
  "fmt"
  "log"

  "github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract는 자산관리에 대한 함수를 제공. 즉 스마트 컨트렉트를 정의하고, 컨트렉트에 필요한 메서드들을 구현
//= world statte와 주고 받는 데이터를 관리하기 위한 컨트렉트
// 블록체인 원장에서 데터를 읽고 쓰는 스마트 컨트렉트 내 정의된 기능
type SmartContract struct {
	contractapi.Contract
}


// Asset은 간단한 assset을 구성하는 것에 대한 기본 세부 정보 = 자산(블록) 객체 정의
// 알파벳 순서로 구조 필드를 삽입하여 여러 언어에 걸쳐 결정
// golang은 marshal to json 명령을 유지하지만 자동으로 정렬하진 않는다.
type Asset struct {
  ID             string `json:"ID"`
  Color          string `json:"color"`
  Size           int    `json:"size"`
  Owner          string `json:"owner"`
  AppraisedValue int    `json:"appraisedValue"`
}

// InitLedger는 기본 자산 집합을 원장에 추가
// contractapi.TransactionContextInterface : 모의 트랜잭션 컨텍트 for 단위 테스트 작성 단순화(ex 호출 추적, test 설정)
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
	// 원장에 추가할 asset 배열
    assets := []Asset{
      {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
      {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
      {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
      {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
      {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
      {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
    }
	
    //= for asset in assets:
	for _, asset := range assets {
    	//asset(블록체인 컨트렉트 객체(블록))을 JSON으로 직렬화
		assetJSON, err := json.Marshal(asset)
		if err != nil {
			return err
		}
		// JSON으로 직렬화된 블록을 world state에 넣기. 넣는 과정에 에러가 생겼다면 err
		err = ctx.GetStub().PutState(asset.ID, assetJSON)
		if err != nil {
			return fmt.Errorf("failed to put to world state. %v", err)
		}
	}
	// 원장에 초기 자산을 모두 정상적으로 put 했다면 null return
	return nil
}

// CreateAsset은 지정된 세부 정보와 함께 새 자산을 world state로 발행
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
	//id로 검색해서 원래 있으면 error 리턴 다른 error 떠도 error 리턴
	exists, err := s.AssetExists(ctx, id)
	if err != nil {
		return err
	}
	if exists {
		return fmt.Errorf("the asset %s already exists", id)
	}
	
    // 매개변수로 받은 값을 바탕으로 블록체인 컨트렉트 객체(블록)생성
	asset := Asset{
		ID:             id,
		Color:          color,
		Size:           size,
		Owner:          owner,
		AppraisedValue: appraisedValue,
	}
    // 블록체인 컨트렉트 객체(블록) asset을 JSON 형식으로 직렬화 문제있음 error 리턴
	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return err
	}
	
    // world stste에 저장
	return ctx.GetStub().PutState(id, assetJSON)
}

// ReadAsset은 주어진 ID로 world state에 저장된 자산을 반환
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
	// world state에서 id에 해당하는 데이터를 가져옴 문제 있음 error
	assetJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return nil, fmt.Errorf("failed to read from world state: %v", err)
	}
    // 가져왔는데 아무것도 없어도 error
	if assetJSON == nil {
		return nil, fmt.Errorf("the asset %s does not exist", id)
	}
	
    // JSON 데이터를 Aseet 구조체로 변환 문제있음 error
	var asset Asset
	err = json.Unmarshal(assetJSON, &asset)
	if err != nil {
		return nil, err
	}
	
    // Asset 구조체 반환
	return &asset, nil
}

// UpdateAsset은 제공된 매개 변수를 사용하여 world state의 기존 자산을 업데이트
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
	// id로 world state에서 자산 찾기 없거나 erorr 나면 erorr 리턴
	exists, err := s.AssetExists(ctx, id)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("the asset %s does not exist", id)
	}

	// 원래 asset을 새 asset으로 덮어쓰기
	asset := Asset{
		ID:             id,
		Color:          color,
		Size:           size,
		Owner:          owner,
		AppraisedValue: appraisedValue,
	}
    // 업데이트한 asset을 JSON 형식으로 직렬화 error 나면 error
	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return err
	}
	
    //world state에 넣기
	return ctx.GetStub().PutState(id, assetJSON)
}

// DeleteAsset는 world stste에서 주어진 asset을 제거
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
	// world state에서 id로 찾기 없거나 error 나면 error 리턴
	exists, err := s.AssetExists(ctx, id)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("the asset %s does not exist", id)
	}
	
    //world state에서 id로 지우기
	return ctx.GetStub().DelState(id)
}

// AssetExists world state에 주어진 id로 찾아 asset이 존재하면 리턴
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
	//world state에서 id로 찾기  없으면 false와 erorr 리턴
	assetJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return false, fmt.Errorf("failed to read from world state: %v", err)
	}
	// 있으면 asset과 null로 리턴
	return assetJSON != nil, nil
}

// TransferAsset은 world state에서 지정된 ID를 가진 자산의 소유자 필드를 업데이트하고 이전 소유자를 반환
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
	//world state에서 id로 찾아서 가져오기
	asset, err := s.ReadAsset(ctx, id)
	if err != nil {
		return "", err
	}
	
    // 원래 주인저장하고 새로운 주인으로 변경
	oldOwner := asset.Owner
	asset.Owner = newOwner
	
    //변경된 asset을 JSON 형식으로 직렬화
	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return "", err
	}
	
    //world state에 넣기
	err = ctx.GetStub().PutState(id, assetJSON)
	if err != nil {
		return "", err
	}
	
    // 원래 주인 반환
	return oldOwner, nil
}

// GetAllAssets는 world state의 모든 asset들을 반환
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
	// startKey 및 endKey에 대해 빈 문자열이 포함된 범위 쿼리를 실행 = 모두 가져오기
	// 체인 코드 네임스페이스의 모든 자산에 대한 개방형 쿼리
	resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
	if err != nil {
		return nil, err
	}
	defer resultsIterator.Close()
	// 결과를 저장할 빈 Asset 객체 배열인 assets 생성
	var assets []*Asset
    // resultsIterator을 돌면서 assets에 추가
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}
		
        // JSON -> 블록 객체로 assets에 넣기
		var asset Asset
		err = json.Unmarshal(queryResponse.Value, &asset)
		if err != nil {
			return nil, err
		}
		assets = append(assets, &asset)
	}
	
    // 모든 asset 블록이 담긴 assets 반환
	return assets, nil
}
  • asset-transfer-basic/chaincode-go 디렉토리에서 스마트 컨트렉트 종속성 설치
    • 명령어가 성공적이라면 go 패키지는 vendor 폴더에 설치됨.
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic$ cd chaincode-go
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/chaincode-go$ GO111MODULE=on go mod vendor
      github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode/mocks imports
      	github.com/hyperledger/fabric-protos-go/peer imports
      	google.golang.org/grpc imports
      	google.golang.org/grpc/internal/transport imports
      	golang.org/x/net/http2 imports
      	io/fs: package io/fs is not in GOROOT (/usr/local/go/src/io/fs)
      
  • 종속성이 설치됐으므로 체인코드 패키지 생성가능
  • test-network 디렉토리로 돌아가서 다른 네트워크 아티팩트와 체인코드 패키징 가능
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/chaincode-go$ cd ..
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic$ cd ..
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples$ cd test-network
    
  • peer : 필요한 형식으로 체인코드 패키지를 생성하는 CLI
    • peer 바이너리는 fabric-samples/bin 디렉토리에 있다.
    • 아래 명령을 사용하여 해당 바이너리를 CLI 경로에 추가한다.
    • 추가.. 혹시나 궁금하다고 cat peer하지 말자.. 주술 같은 문구가 좌라락 펼쳐지니까..
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ export PATH=${PWD}/../bin:$PATH
      
  • fabric-samples/core.yaml에 있는 파일을 가르키기 위해 FABRIC_CFG_PATH 설정
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ export FABRIC_CFG_PATH=$PWD/../config/
    
  • peer CLI를 사용할 수 있는지 확인하기 위해 바이너리 버전을 확인
    • 이 듀토리얼을 따라하기 위한 바이너리 버전은 2.0.0 이어야 함.
    • ... 높아도 되져? ㅎㅎ..
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ peer version
      ^[[Apeer:
       Version: v2.5.4
       Commit SHA: e1e8e2e
       Go version: go1.20.6
       OS/Arch: linux/amd64
       Chaincode:
        Base Docker Label: org.hyperledger.fabric
        Docker Namespace: hyperledger
      
  • 이제 peer lifecycle chaincode 명령을 사용하여 체인코드 패키지를 생성할 수 있음.
  • 아래 명령어는 현재 디렉토리에서 basic.tar.gz. 라고 이름이 지정된 패키지를 생성
    • ls를 통해 생성된 것 확인 가능
    • --lang 플래그 : 체인 코드 언어를 지정하는 데 사용
    • --path 플래그 : 스마트 계약 코드의 위치를 제공
      • 경로는 정규화된 경로이거나 현재 작업 디렉터리에 상대적인 경로여야 함.
    • --label 플래그 : 설치된 후 체인코드를 식별할 체인코드 레이블을 지정하는 데 사용
      • 레이블에 체인코드 이름과 버전을 포함하는 것이 좋음.
  • 테스트 네트워크의 피어에 체인코드 패키지를 만들었으므로 체인코드를 설치할 수 있음.
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ peer lifecycle chaincode package basic.tar.gz --path ../asset-transfer-basic/chaincode-go/ --lang golang --label basic_1.0
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ls
    CHAINCODE_AS_A_SERVICE_TUTORIAL.md  compose           organizations
    README.md                           configtx          prometheus-grafana
    addOrg3                             log.txt           scripts
    basic.tar.gz                        monitordocker.sh  setOrgEnv.sh
    channel-artifacts                   network.sh        system-genesis-block
    

profile
인생 살자.

0개의 댓글