공부 기록

하이퍼레저 패브릭 - 4: Fabric Application 실행

by 너나나

모든 건 공식문서를 따라간다!!!!!!!!

https://guiyum.tistory.com/120

 

하이퍼레저 패브릭 - 3: 채널에 스마트 계약 배포하기 - 2

모든 건 공식문서를 따라간다!!!!!!!! https://guiyum.tistory.com/115 하이퍼레저 패브릭 - 3: 채널에 스마트 계약 배포하기 - 1 모든 건 공식문서를 따라간다!!!!!!!! https://guiyum.tistory.com/114 하이퍼레..

guiyum.tistory.com

이번 튜토리얼은 Fabric application이 배포된 블록체인 네트워크와 상호 작용하는 방법에 대해 소개한다. 여기서는 Fabric Gateway application API를 사용하여 스마트 계약을 출한다. 스마트 계약은 스마트 계약 API로 원장을 쿼리하고 업데이트한다.

 

Asset Transfer

Asset Transfer 샘플은 자산을 생성, 업데이트 및 쿼리하는 방법을 보여준다.

  1. Sample application: 블록체인 네트워크를 호출하여 스마트 계약에 구현된 트랜잭션을 호출한다.
    asset-transfer-basic/application-gateway-typescript​
  2. Smart contract: 원장과 상호 작용하는 트랜잭션을 구현한다.
    asset-transfer-basic/chaincode-(typescript, go, java)​

튜토리얼은 두 파트로 구성된다.

  1. 블록체인 네트워크 설정: 애플리케이션은 상호 작용할 블랙체인 네트워크가 필요하므로 기본 네트워크를 시작하고 애플리케이션에 대한 스마트 계약을 배포
  2. 스마트 계약과 상호작용하는 샘플 애플리케이션 실행: 애플리케이션은 assetTransfer 스마트 계약을 사용하여 원장에서 에셋을 생성, 쿼리 및 업데이트한다. 초기 에셋 생성, 에셋 쿼리, 에셋 범위 쿼리, 새 에셋 생성 및 에셋을 새 소유자에게 이전하는 것을 포함하여 애플리케이션 및 호출하는 트랜잭션 코드를 단계별로 살펴봄

패브릭 애플리케이션과 스마트 계약이 함께 작동하여 블록체인의 분산 원장에 있는 데이터를 관리하는 방법을 알 수 있다!!

 

들어가기 전에

패브릭 테스트 네트워크 실행의 사전요구사항을 충족하고 build-essential을 설치해야 한다.

sudo apt install build-essential

 

블록체인 네트워크 설정

블록체인 네트워크 시작

cd fabric-samples/test-network
./network.sh down
./network.sh up createChannel -c mychannel -ca

이미 실행중인 테스트 네트워크가 있다면 꺼주고 시작!!!!!!!

위 중 맨 아래 명령은 두 개의 피어, 오더링 서비스, 세 개의 인증기관(Orderer, Org1, Org2)이 있는 패브릭 테스트 네트워크를 배포한다. cryptogen tool을 사용하는 대신 인증 기관을 사용하여 테스트 네트워크를 표시하므로 -ca 플래그를 사용하였다. ord admin 유저 등록은 인증 기관이 시작될 때 알아서 실행된다.

스마트 계약 배포

이번에 우리는 typescript를 사용한다.

./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-typescript/ -ccl typescript

이 스크립트는 체인코드 수명주기를 사용하여 설치된 체인코드를 패키징, 설치,쿼리하고 Org1과 Org2 모두에 대해 체인코드를 승인하고 마지막으로 체인코드를 커밋한다.

체인코드 패키지가 성공적으로 배포되면 아래와 비슷한 화면을 볼 수 있다!!

Committed chaincode definition for chaincode 'basic' on channel 'mychannel':
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]
Query chaincode definition successful on peer0.org2 on channel 'mychannel'
Chaincode initialization is not required

 

샘플 애플리케이션 준비

이제 배포된 스마트 계약과 상호 작용하는 데 사용할 샘플 Asset Transfer TypeScript 응용 프로그램을 준비하자!!

cd asset-transfer-basic/application-gateway-typescript

fabric-samples 레포지토리에서 저 경로로 이동하자!!

이 디렉토리는 Node.js용 Fabric Gateway application API를 사용하여 개발된 샘플 애플리케이션을 포함한다.

다음 명령을 따라해 디펜던시를 설치하고 애플리케이션을 빌드한다.

npm install

애플리케이션의 package.json 안에 정의된 디펜던시를 설치하는 과정이다!! 가장 중요한 패키지는 @hyperledger/fabric-gateway Node.js 패키지로 Fabric Gateway를 연결하고 특정 클라이언트 ID를 사용하여 트랜잭션을 제출 및 평가하고 이벤트를 수신하는 데 사용되는 Fabric Gateway 애플리케이션 API를 제공한다.

 

src 디렉토리는 클라이언트 애플리케이션 소스 코드가 들어있다. 설치 과정 동안 이 소스코드에서 만들어지는 자바스크립트 결과는 dist 디렉토리에 위치한다.

 

샘플 애플리케이션 실행

패브릭 테스트 네트워크를 시작할 때, 인증 기관을 통해 여러 ID(identity)를 생성했다. 여기에는 각 조직의 사용자 ID가 포함된다. 애플리케이션은 이러한 사용자 ID 중 하나의 자격 증명을 사용하여 블록체인 네트워크과 거래한다.

애플리케이션을 실행하고 스마트 계약 함수와 상호작용을 단계별로 살펴보자.  asset-transfer-basic/application-gateway-typescript 디렉토리에서 아래 명령을 실행한다!!

npm start

 

1. Gateway에 gRPC connection 설정

client applicatio은 블록체인 네트워크와 거래하는 데 사용할 Fabric Gateway 서비스에 대한 gRPC 연결을 설정한다. 이렇게 하려면 Fabric Gateway의 엔드 포인트 주소와 TLS를 사용하도록 구성된 경우 적절한 TLS 인증서만 필요하다. 이 샘플에서는 Gateway endpoint 주소는 Fabric Gateway service를 제공하는 피어의 주소이다.

더보기
더보기

gRPC 연결 설정과 관련된 상당한 오버헤드가 있으므로 이 연결은 애플리케이션에서 유지하고 Fabric Gateway와의 모든 상호 작용에 사용해야 한다.

트랜잭션에 사용되는 모든 프라이빗 데이터의 보안을 유지하기 위해서 애플리케이션은 클라이언트 ID와 동일한 조직에 속한 Fabric Gateway에 연결해야 한다.  클라이언트 신원의 조직이 게이트웨이를 호스팅하지 않는 경우 다른 조직의 신뢰할 수 있는 게이트웨이를 사용해야 한다.

 TypeScript 애플리케이션은 게이트웨이의 TLS 인증서의 진위를 확인할 수 있도록 서명 인증 기관의 TLS 인증서를 사용하여 gRPC 연결을 생성한다.

TLS 연결이 성공적으로 설정되기 위해 클라이어언트에서 사용되는 엔드포인트 주소가 반드시 게이트웨이의 TLS 인증서에있는 주소와 일치해야 한다. 클라이언트는 localhost 주소에서 게이트웨이의 Docker container에 접근하기 때문에 이 엔드포인트 주소가 게이트웨이의 구성된 호스트네임으로 해석되도록 gRPC 옵션이 지정된다.

const peerEndpoint = 'localhost:7051';

async function newGrpcConnection(): Promise<grpc.Client> {
    const tlsRootCert = await fs.readFile(tlsCertPath);
    const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);
    return new grpc.Client(peerEndpoint, tlsCredentials, {
        'grpc.ssl_target_name_override': 'peer0.org1.example.com',
    });
}

 

2. Gateway 연결 생성하기

애플리케이션은 Fabric Gateway에 접근할 수 있는 모든 네트워크에 접근하는 데 사용하는 Gateway 연결을 생성하고 이후에 해당 네트워크에 배포된 스마트 계약을 생성한다. 게이트웨이 연결에는 세 가지 요구 사항이 있다.

  1. Fabric Gateway에 대한 gRPC 연결
  2. 네트워크와 거래하는 데 사용되는 클라이언트 신원(ID)
  3. 클라이언트 ID에 대한 디지털 서명을 생성하는 데 사용되는 서명 구현

샘플 애플리케이션은 Org1 사용자의 X.509 인증서를 클라이언트 ID로 사용하고 해당 사용자의 개인 키를 기반으로 하는 서명 구현을 사용한다.

const client = await newGrpcConnection();

const gateway = connect({
    client,
    identity: await newIdentity(),
    signer: await newSigner(),
});

async function newIdentity(): Promise<Identity> {
    const credentials = await fs.readFile(certPath);
    return { mspId: 'Org1MSP', credentials };
}

async function newSigner(): Promise<Signer> {
    const privateKeyPem = await fs.readFile(keyPath);
    const privateKey = crypto.createPrivateKey(privateKeyPem);
    return signers.newPrivateKeySigner(privateKey);
}

 

3. 호출할 스마트 계약에 접근

샘플 애플리케이션은 게이트웨이 연결을 사용하여 네트워크에 대한 참조(레퍼런스)를 얻은 다음 해당 네트워크에 배포된 체인코드 내의 기본 계약을 얻는다.

const channelName = 'mychannel';
const chaincodeName = 'basic';

const network = gateway.getNetwork(channelName);
const contract = network.getContract(chaincodeName);

체인코드 패키지에 여러 스마트 계약이 포함된 경우 getContract() 호출에 대한 인수로 체인코드의 이름과 특정 스마트 계약의 이름을 모두 제공할 수 있다.

const contract = network.getContract('chaincodeName', 'smartContractName');

 

4. 샘플 에셋으로 원장 채우기

체인코드 패키지의 초기 배포 직후 원장은 비어있다. 애플리케이션은 몇개의 샘플 에셋으로 원장을 채우는 InitLedger transaction function을 호출하기 위해 submitTransaction()을 사용한다.  

submitTransaction()은 Fabric 게이트웨이를 사용하여 다음을 수행한다.

  1. 거래 제안을 승인
  2. 승인된 거래를 오더링 서비스에 제출
  3. 트랜잭션이 커밋될 때까지 기다리며 원장 상태를 업데이트

5. 원장 read/write 트랜잭션 함수 호출

이제 애플리케이션은 스마트 계약에서 트랜잭션 함수를 호출하여 원장의 자산을 쿼리하고, 추가 자산을 생성/수정하는 비즈니스 로직을 실행할 준비가 됐다!!!!!!

 

- 모든 에셋 쿼리

애플리케이션은 읽기 전용 트랜잭션 호출을 수행하여 원장을 쿼리하기 위해 evaluateTransaction()을 사용한다. evaluateTransaction()은 패브릭 게이트웨이를 사용하여 트랜잭션 함수를 호출하고 결과를 리턴한다. 트랜잭션은 오더링 서비스로 전송되지 않으며 원장 업데이트가 발생하지 않는다.

아래에서 샘플 애플리케이션은 원장을 채울 때 이전 단계에서 생성된 모든 자산을 가져온다.

GetAllAssets를 호출해보자!!

const resultBytes = await contract.evaluateTransaction('GetAllAssets');

const resultJson = utf8Decoder.decode(resultBytes);
const result = JSON.parse(resultJson);
console.log('*** Result:', result);
더보기
더보기

트랜잭션 함수는 모든 유형의 데이터를 반환할 수 있으므로 트랜잭션 함수 결과는 항상 바이트로 리턴된다.애플리케이션은 결과 바이트를 올바르게 해석해야 한다.

 

- 새로운 에셋 생성

샘플 애플리케이션은 새 에셋을 생성하기 위해 트랜잭션을 제출한다.

CreateAsset을 호출!!!

const assetId = `asset${Date.now()}`;

await contract.submitTransaction(
    'CreateAsset',
    assetId,
    'yellow',
    '5',
    'Tom',
    '1300',
);
더보기
더보기

CreateAsset 트랜잭션은 항상 체인코드가 예상하는 것과 같은 타입, 같은 개수의 인수를 제출해야 한다. CreateAsset 트랜잭션은 ID, Color, Size, Owner, AppraisedValue 순으로 인자를 받으므로 assetId, "yellow", "5", "Tom", "1300"을 인자로 전달하였다.

 

- 에셋 수정

샘플 애플리케이션은 새로 생성된 자산의 소유권을 이전하기 위해 트랜잭션을 제출한다. 이번에는 트랜잭션이 원장에 커밋될 때까지 기다리는 대신 승인된 트랜잭션을 주문 서비스에 성공적으로 제출 한 후 반환되는 submitAsync()를 사용하여 트랜잭션이 호출된다. 이를 통해 애플리케이션은 커밋되기를 기다리는 동안 트랜잭션 결과를 사용하여 작업을 수행할 수 있다.

TransferAsset을 호출!!!

const commit = await contract.submitAsync('TransferAsset', {
    arguments: [assetId, 'Saptha'],
});
const oldOwner = utf8Decoder.decode(commit.getResult());

console.log(`*** Successfully submitted transaction to transfer ownership from ${oldOwner} to Saptha`);
console.log('*** Waiting for transaction commit');

const status = await commit.getStatus();
if (!status.successful) {
    throw new Error(`Transaction ${status.transactionId} failed to commit with status code ${status.code}`);
}

console.log('*** Transaction committed successfully');

 

- 수정된 에셋 쿼리

그 다음 샘플 애플리케이션은 전송된 에셋에 대한 쿼리를 평가하여 에셋이 설명된 속성으로 생성되었으며 이후 새 소유자에게 전송되었음을 보여준다.

ReadAsset 호출!!

const resultBytes = await contract.evaluateTransaction('ReadAsset', assetId);

const resultJson = utf8Decoder.decode(resultBytes);
const result = JSON.parse(resultJson);
console.log('*** Result:', result);

 

- 트랜잭션 에러 처리

시퀀스의 마지막 부분은 트랜잭션 제출 오류를 보여준다. 샘플 어플리케이션은 존재하지 않는 에셋 ID를 넣어 UpdateAsset 트랜잭션을 제출하려 한다. 트랜잭션 함수에서 에러 응답을 반환하고 submitTransaction() 함수 호출에 실패한다.

submitTransaction() 함수의 실패는 제출 흐름 상에서 오류가 발생한 곳을 표시하고 애플리케이션이 적절하게 응답할 수 있도록 하는 추가 정보를 포함한 여러 유형의 오류를 생성할 수 있다. 발생할 수 있는 에러 타입은 API 문서를 참고해야 한다.

try {
    await contract.submitTransaction(
        'UpdateAsset',
        'asset70',
        'blue',
        '5',
        'Tomoko',
        '300',
    );
    console.log('******** FAILED to return an error');
} catch (error) {
    console.log('*** Successfully caught the error: \n', error);
}

EndorseError 타입은 보증 과정에서 실패가 발생하였음을 의미한다. gRPC 상태 코드 ABORTED는 어플리케이션이 패브릭 게이트웨이를 성공적으로 호출했지만 보증 과정에서 실패가 발생하였음을 나타낸다. gRPC 상태 코드 UNAVAILABLE, DEADLINE_EXCEEDED는 패브릭 게이트웨이에 연결할 수 없거나 시간 내에 응답을 받지 못하였으므로 작업을 재시도하는 것이 적절할 수 있음을 나타낸다.

정리하기

cd ../../test-network
./network.sh down

asset-transfer 샘플 사용이 끝났으므로 테스트 네트워크를 중단한다.

 

정리

이번 예제에서는 테스트 네트워크를 시작하고 스마트 컨트랙트를 배포하여 블록체인 네트워크를 설정하는 법을 살펴보았다. 클라이언트 어플리케이션을 실행한 뒤 어플리케이션의 코드를 살펴보았다. 패브릭 게이트웨이 API를 사용하여 패브릭 게이트웨이에 연결하고 배포된 스마트 컨트랙트에서 트랜잭션 기능을 호출하여 원장을 쿼리, 추가, 수정하는 방법을 알 수 있었다.


참고 자료

https://hyperledger-fabric.readthedocs.io/en/latest/write_first_app.html

 

Running a Fabric Application — hyperledger-fabricdocs main documentation

When we started the Fabric test network earlier in this tutorial, several identities were created using the Certificate Authorities. These include a user identity for each of the organizations. The application will use the credentials of one of these user

hyperledger-fabric.readthedocs.io

 

블로그의 정보

공부 기록

너나나

활동하기