[BlockChain/Hyperledger Fabric] 패브릭 애플리케이션 실행

yujeongkwon·2023년 8월 14일
1

BlockChain

목록 보기
9/10

ㅋㅋ

시작전 배경 지식


About Asset Transfer

  • Asset Transfer (basic) 샘플은 create, update, and query assets 하는 방법을 보여줌.

  • 구성요소 2가지

    1. 샘플 애플리케이션: 블록체인 네트워크를 호출하여 스마트 계약에 구현된 트랜잭션을 호출
    • 애플리케이션은 아래 디렉토리에 존재
      fabric-samples/asset-transfer-basic/application-gateway-typescript
      1. 스마트 컨트렉트: 원장과 상호 작용하는 트랜잭션을 구현
    • 스마트 컨트렉트은 아래 디렉토리에 존재
      fabric-samples/asset-transfer-basic/chaincode-(typescript, go, java)
  • 이 예에서는 TypeScript 스마트 계약을 사용
    이 자습서는 두 가지 기본 부분으로 구성됩니다.
    1. 블록체인 네트워크를 설정

    • 우리 애플리케이션은 상호 작용할 블록체인 네트워크가 필요하므로 기본 네트워크를 시작하고 애플리케이션에 대한 스마트 계약을 배포
    1. 샘플 애플리케이션을 실행하여 스마트 계약과 상호 작용
    • 우리의 애플리케이션은 assetTransfer 스마트 계약을 사용하여 원장에서 자산을 생성, 쿼리 및 업데이트
    • 일부 초기 자산 생성, 자산 쿼리, 자산 범위 쿼리, 새 자산 생성 및 자산을 새 소유자에게 이전하는 것을 포함하여 응용 프로그램의 코드와 호출하는 트랜잭션을 단계별로 함 보쟈~
  • 이거 끝내면 Fabric 애플리케이션과 스마트 계약이 함께 작동하여 블록체인 네트워크의 분산 원장에서 데이터를 관리하는 방법에 대해 알 수 있을거야~~


시작하기 전에

  • 샘플 애플리케이션을 실행하려면 환경에 Fabric 샘플을 설치해 함
  • 이 튜토리얼의 샘플 애플리케이션은 Node용 Fabric Gateway 클라이언트 API를 사용

블록체인 네트워크 설정


블록체인 네트워크 시작

  • fabric-samples 리포지토리의 로컬 복제본 내 test-network 하위 디렉터리로 이동합니다.
  • 테스트 네트워크가 이미 실행 중인 경우 환경이 깨끗한지 확인하기 위해 네트워크를 종료합니다.
    ./network.sh down
  • network.sh 셸 스크립트를 사용하여 Fabric 테스트 네트워크를 시작합니다.
    ./network.sh up createChannel -c mychannel -ca
    • 마지막 명령은 2개의 피어, 순서 지정 서비스 및 3개의 인증 기관(Orderer, Org1, Org2)이 있는 Fabric 테스트 네트워크를 배포
    • cryptogen 도구를 사용하는 대신 인증 기관(-ca 플래그)을 사용하여 테스트 네트워크를 불러옴
    • 조직 관리자 사용자 등록은 인증 기관이 시작될 때 부트스트랩 됨
  • 내 cmd창
    yujeong@yujeong-VirtualBox:~/go/src$ cd fabric-samples/test-network
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh down
    Using docker and docker-compose
    Stopping network
    Removing network fabric_test
    WARNING: Network fabric_test not found.
    Removing network compose_default
    WARNING: Network compose_default not found.
    Removing volume compose_orderer.example.com
    WARNING: Volume compose_orderer.example.com not found.
    Removing volume compose_peer0.org1.example.com
    WARNING: Volume compose_peer0.org1.example.com not found.
    Removing volume compose_peer0.org2.example.com
    WARNING: Volume compose_peer0.org2.example.com not found.
    Removing volume compose_peer0.org3.example.com
    WARNING: Volume compose_peer0.org3.example.com not found.
    Error response from daemon: get docker_orderer.example.com: no such volume
    Error response from daemon: get docker_peer0.org1.example.com: no such volume
    Error response from daemon: get docker_peer0.org2.example.com: no such volume
    Removing remaining containers
    Removing generated chaincode docker images
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh up createChannel -c mychannel -ca
    Using docker and docker-compose
    Creating channel 'mychannel'.
        ... 
        생략
        ...
    2023-08-09 13:41:29.585 UTC 0001 INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
    2023-08-09 13:41:29.627 UTC 0002 INFO [channelCmd] update -> Successfully submitted channel update
    Anchor peer set for org 'Org2MSP' on channel 'mychannel'
    Channel 'mychannel' joined
    

스마트 계약 배포

이 튜토리얼은 Asset Transfer 스마트 계약 및 애플리케이션의 TypeScript 버전을 보여주지만 TypeScript 애플리케이션 샘플과 함께 모든 스마트 계약 언어 샘플을 사용 가능 나는 go로 바까서 할게~~

  • 체인코드 이름과 언어 옵션과 함께 ./network.sh 스크립트를 호출하여 스마트 계약이 포함된 체인코드 패키지를 배포
    • 아래는 체인코드 수명 주기를 사용하여 설치된 체인코드를 패키징, 설치, 쿼리하고 Org1 및 Org2 모두에 대한 체인코드를 승인하고 최종적으로 체인코드를 커밋
      yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go/ -ccl go
      
    • 체인코드 패키지가 성공적으로 배포된 경우 터미널의 출력 끝
      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
      

샘플 애플리케이션 준비하기

아 여기서는 typescript머시기 따라했는데 go면 go, java면 java로 할 수 있음 java로 실행하는거 뒤에서 하겠음.


  • 배포된 스마트 계약과 상호 작용하는 데 사용할 샘플 Asset Transfer typescript 애플리케이션을 준비
    새 터미널을 열고 application-gateway-typescript 디렉터리로 이동
    • 이 디렉토리에는 노드용 Fabric Gateway 클라이언트 API를 사용하여 개발된 샘플 애플리케이션이 포함됨.
  • npm install 명령을 실행하여 종속성을 설치하고 애플리케이션을 빌드
    • 완료하는 데 시간이 걸릴 수 있음.
    • 이 프로세스는 애플리케이션의 package.json에 정의된 애플리케이션 종속성을 설치
    • 그 중 가장 중요한 것은 @hyperledger/fabric-gateway Node.js 패키지
      • 이는 아래 행동을 위한 Fabric Gateway 클라이언트 API를 제공
      • Fabric Gateway를 연결
      • 특정 클라이언트 ID를 사용하여 트랜잭션을 제출 및 평가
      • 이벤트를 수신
    • npm 설치가 완료되면 애플리케이션을 실행하기 위한 모든 준비가 완료된 것
  • 에러 졸라 뜸. 하란대로 했더니 됐음 (사실 어디서부터 해결됐는지 모르겠음 아래터미널 이전 기록은 이것저것 보고 node 지웠다 깔고 막이래~)
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ npm install
    > asset-transfer-basic@1.0.0 prepare
    > npm run build
    > asset-transfer-basic@1.0.0 build
    > tsc
    added 169 packages, and audited 170 packages in 1m
    37 packages are looking for funding
      run `npm fund` for details
    found 0 vulnerabilities
    npm notice 
    npm notice New minor version of npm available! 9.6.7 -> 9.8.1
    npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.8.1
    npm notice Run npm install -g npm@9.8.1 to update!
    npm notice 
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ npm install -g npm@9.8.1
    npm ERR! code EACCES
    npm ERR! syscall rename
    npm ERR! path /usr/local/lib/node_modules/npm
    npm ERR! dest /usr/local/lib/node_modules/.npm-i9nnxROI
    npm ERR! errno -13
    npm ERR! Error: EACCES: permission denied, rename '/usr/local/lib/node_modules/npm' -> '/usr/local/lib/node_modules/.npm-i9nnxROI'
    npm ERR!  [Error: EACCES: permission denied, rename '/usr/local/lib/node_modules/npm' -> '/usr/local/lib/node_modules/.npm-i9nnxROI'] {
    npm ERR!   errno: -13,
    npm ERR!   code: 'EACCES',
    npm ERR!   syscall: 'rename',
    npm ERR!   path: '/usr/local/lib/node_modules/npm',
    npm ERR!   dest: '/usr/local/lib/node_modules/.npm-i9nnxROI'
    npm ERR! }
    npm ERR! 
    npm ERR! The operation was rejected by your operating system.
    npm ERR! It is likely you do not have the permissions to access this file as the current user
    npm ERR! 
    npm ERR! If you believe this might be a permissions issue, please double-check the
    npm ERR! permissions of the file and its containing directories, or try running
    npm ERR! the command again as root/Administrator.
    npm ERR! A complete log of this run can be found in: /home/yujeong/.npm/_logs/2023-08-10T07_19_54_421Z-debug-0.log
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ sudo npm install -g npm@9.8.1
    [sudo] yujeong 암호: 
    removed 16 packages, and changed 43 packages in 7s
    28 packages are looking for funding
      run `npm fund` for details
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ npm install
    
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ ls
    dist  node_modules  package-lock.json  package.json  src  tsconfig.json
    

샘플 애플리케이션 실행하기

  • 이 자습서의 앞부분에서 Fabric 테스트 네트워크를 시작했을 때 인증 기관을 사용하여 여러 ID가 생성
    • 여기에는 각 조직의 사용자 ID가 포함
    • 애플리케이션은 이러한 사용자 ID 중 하나의 자격 증명을 사용하여 블록체인 네트워크와 거래
  • 애플리케이션을 실행한 다음 스마트 계약 기능과의 각 상호 작용을 단계별로 살펴보쟈.
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ npm start
    

1. 게이트웨이에 대한 gRPC 연결 설정

  • 클라이언트 애플리케이션은 블록체인 네트워크와 거래하는 데 사용할 Fabric Gateway 서비스에 대한 gRPC 연결을 설정
    • 이렇게 하려면 Fabric Gateway의 엔드포인트 주소만 필요하며, TLS를 사용하도록 구성된 경우, 적절한 TLS 인증서가 필요
    • 이 샘플에서 게이트웨이 엔드포인트 주소는 Fabric Gateway 서비스를 제공하는 피어의 주소

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

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

  • TypeScript 응용 프로그램은 서명 인증 기관의 TLS 인증서를 사용해 gRPC 연결을 생성하여 게이트웨이의 TLS 인증서의 진위를 확인

  • TLS 연결이 성공적으로 설정되려면 클라이언트가 사용하는 endpoint 주소가 게이트웨이의 TLS 인증서에 있는 주소와 일치해야 함.

  • app.ts에서 발췌한 부분

    • 클라이언트는 localhost 주소에서 게이트웨이의 Docker 컨테이너에 액세스하므로 이 엔드포인트 주소가 게이트웨이의 구성된 호스트 이름으로 해석되도록 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. 게이트웨이 연결 생성

  • 애플리케이션은 Fabric 게이트웨이에 액세스할 수 있는 모든 네트워크(채널과 유사)에 액세스하는 데 사용하는 게이트웨이 연결 생성 -> 이후 해당 네트워크에 배포되는 스마트 계약을 생성
  • 게이트웨이 연결의 세 가지 요구 사항
    1. 패브릭 게이트웨이에 대한 gRPC 연결.
    1. 네트워크와 거래하는 데 사용되는 클라이언트 ID
    2. 클라이언트 ID에 대한 디지털 서명을 생성하는 데 사용되는 서명 구현
  • app.ts에서 발췌한 부분
    • 샘플 애플리케이션은 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 network = gateway.getNetwork(channelName);
const contract = network.getContract(chaincodeName);
  • 체인코드 패키지에 여러 스마트 계약이 포함된 경우 체인코드의 이름과 특정 스마트 계약의 이름을 모두 getContract() 호출에 대한 인수로 제공가능

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

4.샘플 자산으로 원장 채우기

  • 체인코드 패키지의 초기 배포 직후 원장은 비어 있dma

  • 애플리케이션은 submitTransaction()을 사용하여 일부 샘플 자산으로 원장을 채우는 InitLedger 트랜잭션 함수를 호출

  • submitTransaction()은 Fabric Gateway를 사용하여 다음을 수행

    1. 거래 제안 승인
    2. 승인된 거래를 주문 서비스에 제출
    3. 트랜잭션이 커밋되고 원장 상태가 업데이트될 때까지 대기
  • 샘플 애플리케이션 InitLedger 호출:

     await contract.submitTransaction('InitLedger');

5. 트랜잭션 함수를 호출하여 자산을 읽고 쓰기

  • 이제 애플리케이션은 스마트 계약에서 트랜잭션 기능을 호출하여 원장에서 쿼리하고, 추가 자산을 생성하고, 자산을 수정하는 비즈니스 로직을 실행할 준비가 됨.


    모든 자산 쿼리

  • 애플리케이션은 읽기 전용 트랜잭션 호출을 수행하여 원장을 조회하기 위해 evaluateTransaction()을 사용

  • evaluateTransaction() : Fabric Gateway를 사용하여 트랜잭션 기능을 호출하고 그 결과를 리턴

    • 트랜잭션은 ordering service 서비스로 전송되지 않으며 원장 업데이트가 발생하지 x
  • 아래에서 샘플 애플리케이션은 원장을 채울 때 이전 단계에서 생성된 모든 자산을 가져오고 있음.

  • 샘플 애플리케이션 GetAllAssets 호출:

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

트랜잭션 함수는 모든 유형의 데이터를 반환할 수 있으므로 트랜잭션 함수 결과는 항상 바이트로 반환
- 종종 트랜잭션 함수는 문자열을 반환
- 위의 경우와 같이 JSON 데이터의 UTF-8 문자열 반환
- 응용 프로그램은 결과 바이트를 올바르게 해석할 책임을 가짐

  • 위에서 npm start 했을 때, 터미널 출력은 아래와 같
    *** Result: [
      {
        AppraisedValue: 300,
        Color: 'blue',
        ID: 'asset1',
        Owner: 'Tomoko',
        Size: 5
      },
      {
        AppraisedValue: 400,
        Color: 'red',
        ID: 'asset2',
        Owner: 'Brad',
        Size: 5
      },
      {
        AppraisedValue: 500,
        Color: 'green',
        ID: 'asset3',
        Owner: 'Jin Soo',
        Size: 10
      },
      {
        AppraisedValue: 600,
        Color: 'yellow',
        ID: 'asset4',
        Owner: 'Max',
        Size: 10
      },
      {
        AppraisedValue: 700,
        Color: 'black',
        ID: 'asset5',
        Owner: 'Adriana',
        Size: 15
      },
      {
        AppraisedValue: 800,
        Color: 'white',
        ID: 'asset6',
        Owner: 'Michel',
        Size: 15
      }
    ]
    

새 자산 만들기

  • 샘플 애플리케이션은 트랜잭션을 제출하여 새 자산을 생성
  • 샘플 애플리케이션 CreateAsset 호출:
const assetId = `asset${Date.now()}`;

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

위의 애플리케이션 스니펫에서 CreateAsset 트랜잭션은 체인코드가 예상하는 것과 동일한 유형 및 수의 인수와 올바른 순서로 제출된다는 점에 유의.
이 경우 올바르게 순서가 지정된 인수는 아래와 같음
assetId, "yellow", "5", "Tom", "1300"
해당 스마트 계약의 CreateAsset 트랜잭션 함수는 자산 개체를 정의하는 다음 인수 시퀀스를 예상
ID, Color, Size, Owner, AppraisedValue


자산 업데이트

  • 샘플 애플리케이션은 새로 생성된 자산의 소유권을 이전하기 위해 트랜잭션을 제출
  • 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');
  • 터미널 출력:

    --> Async Submit Transaction: TransferAsset, updates existing asset owner
    *** Successfully submitted transaction to transfer ownership from Tom to Saptha
    *** Waiting for transaction commit
    *** 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);
  • 터미널 출력:
    --> Evaluate Transaction: ReadAsset, function returns asset attributes
    *** Result: {
      AppraisedValue: 1300,
      Color: 'yellow',
      ID: 'asset1691653455835',
      Owner: 'Saptha',
      Size: 5
    }

트랜잭션 오류 처리

  • 시퀀스의 마지막 부분은 트랜잭션 제출 오류를 보여줌.
  • 이 예에서 애플리케이션은 UpdateAsset 트랜잭션을 제출하려고 시도하지만 존재하지 않는 자산 ID를 지정
  • 트랜잭션 함수가 오류 응답을 반환하고 submitTransaction() 호출이 실패
  • submitTransaction() 실패는 제출 흐름에서 오류가 발생한 지점을 나타내고 애플리케이션이 적절하게 응답할 수 있도록 하는 추가 정보를 포함하는 여러 가지 유형의 오류 생성
  • UpdateAsset 호출에 실패한 샘플 애플리케이션:
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);
}
  • 터미널 출력(스택 추적 제외):

    --> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error
    *** Successfully caught the error: 
     EndorseError: 10 ABORTED: failed to endorse transaction, see attached details for more info
        at
    ... 
    {
      code: 10,
      details: [
        {
          address: 'peer0.org1.example.com:7051',
          message: 'chaincode response 500, the asset asset70 does not exist',
          mspId: 'Org1MSP'
        }
      ],
      cause: Error: 10 ABORTED: failed to endorse transaction, see attached details for more info
          at
      ...
      <pre>{
    code: <font color="#A2734C">10</font>,
    details: <font color="#26A269">&apos;failed to endorse transaction, see attached details for more info&apos;</font>,
    metadata: Metadata { internalRepr: <font color="#2AA1B3">[Map]</font>, options: {} }

    },
    transactionId: '8b1ca98e27d69720211ec0f6f2bc00363a207908297c9968531655d856d755fc'
    }

  • EndorseError type : 승인 중에 실패가 발생했음을 나타냄

  • ABORTED의 gRPC 상태 코드 : 애플리케이션이 Fabric Gateway를 성공적으로 호출했지만 승인 프로세스 중에 실패가 발생했음을 나타냄.

  • UNAVAILABLE 또는 DEADLINE_EXCEEDED의 gRPC 상태 코드 : Fabric Gateway에 연결할 수 없거나 적시에 응답을 받지 못했으므로 작업을 재시도하는 것이 적절할 수 있음을 나타냄

  • 아래는 TypeScript 애플리케이션(app.ts)의 전체 코드

    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ ls
    dist  node_modules  package-lock.json  package.json  src  tsconfig.json
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ cd src
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript/src$ ls
    app.ts
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript/src$ cat app.ts
    
/*
 * Copyright IBM Corp. All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

import * as grpc from &apos;@grpc/grpc-js&apos;;
import { connect, Contract, Identity, Signer, signers } from &apos;@hyperledger/fabric-gateway&apos;;
import * as crypto from &apos;crypto&apos;;
import { promises as fs } from &apos;fs&apos;;
import * as path from &apos;path&apos;;
import { TextDecoder } from &apos;util&apos;;

const channelName = envOrDefault(&apos;CHANNEL_NAME&apos;, &apos;mychannel&apos;);
const chaincodeName = envOrDefault(&apos;CHAINCODE_NAME&apos;, &apos;basic&apos;);
const mspId = envOrDefault(&apos;MSP_ID&apos;, &apos;Org1MSP&apos;);

// Path to crypto materials.
const cryptoPath = envOrDefault(&apos;CRYPTO_PATH&apos;, path.resolve(__dirname, &apos;..&apos;, &apos;..&apos;, &apos;..&apos;, &apos;test-network&apos;, &apos;organizations&apos;, &apos;peerOrganizations&apos;, &apos;org1.example.com&apos;));

// Path to user private key directory.
const keyDirectoryPath = envOrDefault(&apos;KEY_DIRECTORY_PATH&apos;, path.resolve(cryptoPath, &apos;users&apos;, &apos;User1@org1.example.com&apos;, &apos;msp&apos;, &apos;keystore&apos;));

// Path to user certificate.
const certPath = envOrDefault(&apos;CERT_PATH&apos;, path.resolve(cryptoPath, &apos;users&apos;, &apos;User1@org1.example.com&apos;, &apos;msp&apos;, &apos;signcerts&apos;, &apos;cert.pem&apos;));

// Path to peer tls certificate.
const tlsCertPath = envOrDefault(&apos;TLS_CERT_PATH&apos;, path.resolve(cryptoPath, &apos;peers&apos;, &apos;peer0.org1.example.com&apos;, &apos;tls&apos;, &apos;ca.crt&apos;));

// Gateway peer endpoint.
const peerEndpoint = envOrDefault(&apos;PEER_ENDPOINT&apos;, &apos;localhost:7051&apos;);

// Gateway peer SSL host name override.
const peerHostAlias = envOrDefault(&apos;PEER_HOST_ALIAS&apos;, &apos;peer0.org1.example.com&apos;);

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

async function main(): Promise&lt;void&gt; {

    await displayInputParameters();

    // The gRPC client connection should be shared by all Gateway connections to this endpoint.
    const client = await newGrpcConnection();

    const gateway = connect({
        client,
        identity: await newIdentity(),
        signer: await newSigner(),
        // Default timeouts for different gRPC calls
        evaluateOptions: () =&gt; {
            return { deadline: Date.now() + 5000 }; // 5 seconds
        },
        endorseOptions: () =&gt; {
            return { deadline: Date.now() + 15000 }; // 15 seconds
        },
        submitOptions: () =&gt; {
            return { deadline: Date.now() + 5000 }; // 5 seconds
        },
        commitStatusOptions: () =&gt; {
            return { deadline: Date.now() + 60000 }; // 1 minute
        },
    });

    try {
        // Get a network instance representing the channel where the smart contract is deployed.
        const network = gateway.getNetwork(channelName);

        // Get the smart contract from the network.
        const contract = network.getContract(chaincodeName);

        // Initialize a set of asset data on the ledger using the chaincode &apos;InitLedger&apos; function.
        await initLedger(contract);

        // Return all the current assets on the ledger.
        await getAllAssets(contract);

        // Create a new asset on the ledger.
        await createAsset(contract);

        // Update an existing asset asynchronously.
        await transferAssetAsync(contract);

        // Get the asset details by assetID.
        await readAssetByID(contract);

        // Update an asset which does not exist.
        await updateNonExistentAsset(contract)
    } finally {
        gateway.close();
        client.close();
    }
}

main().catch(error =&gt; {
    console.error(&apos;******** FAILED to run the application:&apos;, error);
    process.exitCode = 1;
});

/**
* 클라이언트가 게이트웨의 Docker 컨테이너에 액세스 하므로 peerEndpoint를 localhost 주소로 하고,
* gRPC 옵션을 endpoint 주소를 게이트웨이의 구성된 호스트 이름으로 해석하도록 설정
**/
async function newGrpcConnection(): Promise&lt;grpc.Client&gt; {
    const tlsRootCert = await fs.readFile(tlsCertPath);
    const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);
    return new grpc.Client(peerEndpoint, tlsCredentials, {
        &apos;grpc.ssl_target_name_override&apos;: peerHostAlias,
    });
}

async function newIdentity(): Promise&lt;Identity&gt; {
    const credentials = await fs.readFile(certPath);
    return { mspId, credentials };
}

async function newSigner(): Promise&lt;Signer&gt; {
    const files = await fs.readdir(keyDirectoryPath);
    const keyPath = path.resolve(keyDirectoryPath, files[0]);
    const privateKeyPem = await fs.readFile(keyPath);
    const privateKey = crypto.createPrivateKey(privateKeyPem);
    return signers.newPrivateKeySigner(privateKey);
}

/**
 * This type of transaction would typically only be run once by an application the first time it was started after its
 * initial deployment. A new version of the chaincode deployed later would likely not need to run an &quot;init&quot; function.
 */
async function initLedger(contract: Contract): Promise&lt;void&gt; {
    console.log(&apos;\n--&gt; Submit Transaction: InitLedger, function creates the initial set of assets on the ledger&apos;);

    await contract.submitTransaction(&apos;InitLedger&apos;);

    console.log(&apos;*** Transaction committed successfully&apos;);
}

/**
 * Evaluate a transaction to query ledger state.
 */
async function getAllAssets(contract: Contract): Promise&lt;void&gt; {
    console.log(&apos;\n--&gt; Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger&apos;);

    const resultBytes = await contract.evaluateTransaction(&apos;GetAllAssets&apos;);

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

/**
 * Submit a transaction synchronously, blocking until it has been committed to the ledger.
 */
async function createAsset(contract: Contract): Promise&lt;void&gt; {
    console.log(&apos;\n--&gt; Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments&apos;);

    await contract.submitTransaction(
        &apos;CreateAsset&apos;,
        assetId,
        &apos;yellow&apos;,
        &apos;5&apos;,
        &apos;Tom&apos;,
        &apos;1300&apos;,
    );

    console.log(&apos;*** Transaction committed successfully&apos;);
}

/**
 * Submit transaction asynchronously, allowing the application to process the smart contract response (e.g. update a UI)
 * while waiting for the commit notification.
 */
async function transferAssetAsync(contract: Contract): Promise&lt;void&gt; {
    console.log(&apos;\n--&gt; Async Submit Transaction: TransferAsset, updates existing asset owner&apos;);

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

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

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

    console.log(&apos;*** Transaction committed successfully&apos;);
}

async function readAssetByID(contract: Contract): Promise&lt;void&gt; {
    console.log(&apos;\n--&gt; Evaluate Transaction: ReadAsset, function returns asset attributes&apos;);

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

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

/**
 * submitTransaction() will throw an error containing details of any error responses from the smart contract.
 */
async function updateNonExistentAsset(contract: Contract): Promise&lt;void&gt;{
    console.log(&apos;\n--&gt; Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error&apos;);

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

/**
 * envOrDefault() will return the value of an environment variable, or a default value if the variable is undefined.
 */
function envOrDefault(key: string, defaultValue: string): string {
    return process.env[key] || defaultValue;
}

/**
 * displayInputParameters() will print the global scope parameters used by the main driver routine.
 */
async function displayInputParameters(): Promise&lt;void&gt; {
    console.log(`channelName:       ${channelName}`);
    console.log(`chaincodeName:     ${chaincodeName}`);
    console.log(`mspId:             ${mspId}`);
    console.log(`cryptoPath:        ${cryptoPath}`);
    console.log(`keyDirectoryPath:  ${keyDirectoryPath}`);
    console.log(`certPath:          ${certPath}`);
    console.log(`tlsCertPath:       ${tlsCertPath}`);
    console.log(`peerEndpoint:      ${peerEndpoint}`);
    console.log(`peerHostAlias:     ${peerHostAlias}`);
 }

java gatway 실행시켜보자 ㅇㅇ

필요없는 nodejs, npm 제거 하자 난 앞으로 안쓸거라 ㅇ-ㅇ

블록체인 네트워크 설정

거진 위랑 똑같으니까 설명 제외 터미널만 보여줌

블록체인 네트워크 시작

  • 원래 거 지위기
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh down
    
  • 넽 실행
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh up createChannel -c mychannel -ca
    

스마트 계약 배포

  • go 언어로 하자~
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/test-network$ ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go/ -ccl go
    

샘플 어플리케이션 준비

  • 새 터미널 열어서 실행
    yujeong@yujeong-VirtualBox:~/go/src$ cd fabric-samples
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples$ cd asset-transfer-basic/application-gateway-java
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ 
  • java 설치하고 환경설정하자 ㅇㅇ
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ sudo add-apt-repository ppa:openjdk-r/ppa
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ sudo apt update
    
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ sudo apt install openjdk-11-jdk openjdk-11-jre
    
  • java 버전 확인
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ javac -version
    javac 11.0.20
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ java -version
    openjdk version "11.0.20" 2023-07-18
    OpenJDK Runtime Environment (build 11.0.20+8-post-Ubuntu-1ubuntu122.04)
    OpenJDK 64-Bit Server VM (build 11.0.20+8-post-Ubuntu-1ubuntu122.04, mixed mode, sharing)
    
  • gradle도 설치하자..
    yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ sudo apt install gradle
    
  • gradle 버전 확인
------------------------------------------------------------
Gradle 4.4.1
------------------------------------------------------------

Build time:   2012-12-21 00:00:00 UTC
Revision:     none

Groovy:       2.4.21
Ant:          Apache Ant(TM) version 1.10.12 compiled on January 17 1970
JVM:          11.0.20 (Ubuntu 11.0.20+8-post-Ubuntu-1ubuntu122.04)
OS:           Linux 6.2.0-26-generic amd64
- `echo $JAVA_HOME` 명려어 쳐서 암것도 안나오면 설정하기 난 재시작해서 로그엄서짐 ㅋㅋ - [리눅스 java 설치 및 Gradle로 빌드하기](https://may9noy.tistory.com/546) 이거 따라하자 -
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ ./gradlew build
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ ls
build  build.gradle  gradle  gradlew  gradlew.bat  settings.gradle  src
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ cd gradle
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/gradle$ ls
wrapper
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/gradle$ cd ..
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ cd build
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/build$ ls
classes  distributions  generated  libs  scripts  tmp
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/build$ cd libs
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/build/libs$ ls
asset-transfer-basic.jar
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/build/libs$ java -jar asset-transfer-basic.jar
asset-transfer-basic.jar에 기본 Manifest 속성이 없습니다.
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/build/libs$ vim MANIFEST.MF
명령어 'vim' 을(를) 찾을 수 없습니다. 그러나 다음을 통해 설치할 수 있습니다:
sudo apt install vim         # version 2:8.2.3995-1ubuntu2.10, or
sudo apt install vim-tiny    # version 2:8.2.3995-1ubuntu2.10
sudo apt install neovim      # version 0.6.1-3
sudo apt install vim-athena  # version 2:8.2.3995-1ubuntu2.10
sudo apt install vim-gtk3    # version 2:8.2.3995-1ubuntu2.10
sudo apt install vim-nox     # version 2:8.2.3995-1ubuntu2.10
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java/build/libs$ nano MANIFEST.MF
  • nano로 MANIFEST.MF 열어서 Main-Class: ApplicationGatewayJava 를 붙여넣기
  • 아슈발 . 안해 왜자꾸 mainfest 이지ㅏㄹㄹ여기해따 기해따 경로바꿧다 ㅁㄹ앗다저지랄 다해줬는데도 안되냐? 진짜 인생 짜증난다.
  • 그냥 코드만 살펴보자 슈발.
/*
 * Copyright IBM Corp. All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser;
import io.grpc.Grpc;
import io.grpc.ManagedChannel;
import io.grpc.TlsChannelCredentials;
import org.hyperledger.fabric.client.CommitException;
import org.hyperledger.fabric.client.CommitStatusException;
import org.hyperledger.fabric.client.Contract;
import org.hyperledger.fabric.client.EndorseException;
import org.hyperledger.fabric.client.Gateway;
import org.hyperledger.fabric.client.GatewayException;
import org.hyperledger.fabric.client.SubmitException;
import org.hyperledger.fabric.client.identity.Identities;
import org.hyperledger.fabric.client.identity.Identity;
import org.hyperledger.fabric.client.identity.Signer;
import org.hyperledger.fabric.client.identity.Signers;
import org.hyperledger.fabric.client.identity.X509Identity;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.cert.CertificateException;
import java.time.Instant;
import java.util.concurrent.TimeUnit;

public final class App {
	private static final String MSP_ID = System.getenv().getOrDefault("MSP_ID", "Org1MSP");
	private static final String CHANNEL_NAME = System.getenv().getOrDefault("CHANNEL_NAME", "mychannel");
	private static final String CHAINCODE_NAME = System.getenv().getOrDefault("CHAINCODE_NAME", "basic");

	// Path to crypto materials.
	private static final Path CRYPTO_PATH = Paths.get("../../test-network/organizations/peerOrganizations/org1.example.com");
	// Path to user certificate.
	private static final Path CERT_PATH = CRYPTO_PATH.resolve(Paths.get("users/User1@org1.example.com/msp/signcerts/cert.pem"));
	// Path to user private key directory.
	private static final Path KEY_DIR_PATH = CRYPTO_PATH.resolve(Paths.get("users/User1@org1.example.com/msp/keystore"));
	// Path to peer tls certificate.
	private static final Path TLS_CERT_PATH = CRYPTO_PATH.resolve(Paths.get("peers/peer0.org1.example.com/tls/ca.crt"));

	// 클라이언트가 사용하는 url 주소 = 게이트 웨이의 TLS 인증서에 있는 주소
    // = Docker 컨테이너에 액세스 = 게이트웨이의 호스트 이름
	private static final String PEER_ENDPOINT = "localhost:7051";
	private static final String OVERRIDE_AUTH = "peer0.org1.example.com";

	private final Contract contract;
	private final String assetId = "asset" + Instant.now().toEpochMilli();
	private final Gson gson = new GsonBuilder().setPrettyPrinting().create();

	public static void main(final String[] args) throws Exception {
    	/** 게이트웨이 연결 생성
		* gRPC 클라이언트 연결은 이 엔드포인트에 대한 모든 게이트웨이 연결에서 공유
        * 게이트웨이에 접근할 수 있는 모든 채널에 접근할 때 사용하는 게이트웨이 연결 생성
        * 이후 스마트 계약 생성
        */
		var channel = newGrpcConnection();

		var builder = Gateway.newInstance().identity(newIdentity()).signer(newSigner()).connection(channel)
				// Default timeouts for different gRPC calls
				.evaluateOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
				.endorseOptions(options -> options.withDeadlineAfter(15, TimeUnit.SECONDS))
				.submitOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
				.commitStatusOptions(options -> options.withDeadlineAfter(1, TimeUnit.MINUTES));
		
        /** 게이트 웨이 연결 됐다면 스마트 컨트렉트에 접근시작 */
		try (var gateway = builder.connect()) {
			new App(gateway).run();
		} finally {
			channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
		}
	}
	
    /** 게이트웨이에 대한 gRPC 연결 설정
    * 클라이언트가 localhost 주소 게트웨이의 Docker 컨터이너에 접속 하므로
    * 엔드포인트 주소가 게이트웨이의 구성된 호스트 이름으로 해석되도록 gRPC 옵션 지정
    */
	private static ManagedChannel newGrpcConnection() throws IOException {
		var credentials = TlsChannelCredentials.newBuilder()
				.trustManager(TLS_CERT_PATH.toFile())
				.build();
		return Grpc.newChannelBuilder(PEER_ENDPOINT, credentials)
				.overrideAuthority(OVERRIDE_AUTH)
				.build();
	}
	
    /** 네트워크와 거래하는 데 사용되는 클라이언트 ID */
	private static Identity newIdentity() throws IOException, CertificateException {
		var certReader = Files.newBufferedReader(CERT_PATH);
		var certificate = Identities.readX509Certificate(certReader);

		return new X509Identity(MSP_ID, certificate);
	}
	
    /** 클라이언트 ID에 대한 디지털 서명을 생성하는 데 사용되는 서명 구현*/
	private static Signer newSigner() throws IOException, InvalidKeyException {
		var keyReader = Files.newBufferedReader(getPrivateKeyPath());
		var privateKey = Identities.readPrivateKey(keyReader);

		return Signers.newPrivateKeySigner(privateKey);
	}

	private static Path getPrivateKeyPath() throws IOException {
		try (var keyFiles = Files.list(KEY_DIR_PATH)) {
			return keyFiles.findFirst().orElseThrow();
		}
	}
	
    /** 게이트 웨이에 연결 됐다면 스마트 계약에 접근*/
	public App(final Gateway gateway) {
		// 스마트 계약이 배포된 채널을 나타내는 네트워크 인스턴스를 가져옴
		var network = gateway.getNetwork(CHANNEL_NAME);

		// 네트워크에서 스마트 계약을 가져옴.
		contract = network.getContract(CHAINCODE_NAME);
	}
	
    /** 모두 아래에서  submitTransaction()을 사용하여 각각의  트랜잭션 함수 호출*/
	public void run() throws GatewayException, CommitException {
		// 체인 코드 'InitLedger' 기능을 사용하여 원장의 자산 데이터 집합 초기화
		initLedger();

		// Return all the current assets on the ledger.
		getAllAssets();

		// Create a new asset on the ledger.
		createAsset();

		// Update an existing asset asynchronously.
		transferAssetAsync();

		// Get the asset details by assetID.
		readAssetById();

		// Update an asset which does not exist.
		updateNonExistentAsset();
	}
	
	/**
	 * 일반적으로 초기 배포 후 처음 시작할 때 응용 프로그램에서 한 번만 실행
     * 나중에 배포된 체인 코드의 새 버전은 "init" 함수를 실행 안해도 ㄱㅊ
	 */
	private void initLedger() throws EndorseException, SubmitException, CommitStatusException, CommitException {
		System.out.println("\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger");
		
        //submitTransaction()을 사용하여 InitLedger 트랜잭션 함수를 호출
		contract.submitTransaction("InitLedger");

		System.out.println("*** Transaction committed successfully");
	}

	/**
	 * Evaluate a transaction to query ledger state.
	 */
	private void getAllAssets() throws GatewayException {
		System.out.println("\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger");

		var result = contract.evaluateTransaction("GetAllAssets");
		
		System.out.println("*** Result: " + prettyJson(result));
	}

	private String prettyJson(final byte[] json) {
		return prettyJson(new String(json, StandardCharsets.UTF_8));
	}

	private String prettyJson(final String json) {
		var parsedJson = JsonParser.parseString(json);
		return gson.toJson(parsedJson);
	}

	/**
	 * Submit a transaction synchronously, blocking until it has been committed to
	 * the ledger.
	 */
	private void createAsset() throws EndorseException, SubmitException, CommitStatusException, CommitException {
		System.out.println("\n--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments");

		contract.submitTransaction("CreateAsset", assetId, "yellow", "5", "Tom", "1300");

		System.out.println("*** Transaction committed successfully");
	}

	/**
	 * Submit transaction asynchronously, allowing the application to process the
	 * smart contract response (e.g. update a UI) while waiting for the commit
	 * notification.
	 */
	private void transferAssetAsync() throws EndorseException, SubmitException, CommitStatusException {
		System.out.println("\n--> Async Submit Transaction: TransferAsset, updates existing asset owner");

		var commit = contract.newProposal("TransferAsset")
				.addArguments(assetId, "Saptha")
				.build()
				.endorse()
				.submitAsync();

		var result = commit.getResult();
		var oldOwner = new String(result, StandardCharsets.UTF_8);

		System.out.println("*** Successfully submitted transaction to transfer ownership from " + oldOwner + " to Saptha");
		System.out.println("*** Waiting for transaction commit");

		var status = commit.getStatus();
		if (!status.isSuccessful()) {
			throw new RuntimeException("Transaction " + status.getTransactionId() +
					" failed to commit with status code " + status.getCode());
		}
		
		System.out.println("*** Transaction committed successfully");
	}

	private void readAssetById() throws GatewayException {
		System.out.println("\n--> Evaluate Transaction: ReadAsset, function returns asset attributes");

		var evaluateResult = contract.evaluateTransaction("ReadAsset", assetId);
		
		System.out.println("*** Result:" + prettyJson(evaluateResult));
	}

	/**
	 * submitTransaction() will throw an error containing details of any error
	 * responses from the smart contract.
	 */
	private void updateNonExistentAsset() {
		try {
			System.out.println("\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error");
			
			contract.submitTransaction("UpdateAsset", "asset70", "blue", "5", "Tomoko", "300");
			
			System.out.println("******** FAILED to return an error");
		} catch (EndorseException | SubmitException | CommitStatusException e) {
			System.out.println("*** Successfully caught the error: ");
			e.printStackTrace(System.out);
			System.out.println("Transaction ID: " + e.getTransactionId());

			var details = e.getDetails();
			if (!details.isEmpty()) {
				System.out.println("Error Details:");
				for (var detail : details) {
					System.out.println("- address: " + detail.getAddress() + ", mspId: " + detail.getMspId()
							+ ", message: " + detail.getMessage());
				}
			}
		} catch (CommitException e) {
			System.out.println("*** Successfully caught the error: " + e);
			e.printStackTrace(System.out);
			System.out.println("Transaction ID: " + e.getTransactionId());
			System.out.println("Status code: " + e.getCode());
		}
	}
}

정리

  • 자산 전송 샘플 사용을 마치면 network.sh 스크립트를 사용하여 테스트 네트워크를 종료
    ./network.sh 다운
  • 이 명령은 우리가 생성한 블록체인 네트워크의 인증 기관, 피어 및 주문 노드를 종료
  • 원장의 모든 데이터가 손실

요약¶

우리가 지금까지 한거

  • 테스트 네트워크를 시작
  • 스마트 계약을 배포 -> 블록체인 네트워크를 설정
  • 클라이언트 애플리케이션 실행 -> 애플리케이션 코드를 검사 -> Fabric Gateway 클라이언트 API를 사용하여 Fabric Gateway에 연결 -> 배치된 스마트 계약에서 트랜잭션 기능을 호출 -> 원장을 쿼리하고 업데이트하는 방법
profile
인생 살자.

0개의 댓글