ㅇ
ㅋㅋ
Asset Transfer (basic) 샘플은 create, update, and query assets 하는 방법을 보여줌.
구성요소 2가지
fabric-samples/asset-transfer-basic/application-gateway-typescript
fabric-samples/asset-transfer-basic/chaincode-(typescript, go, java)
이 예에서는 TypeScript 스마트 계약을 사용
이 자습서는 두 가지 기본 부분으로 구성됩니다.
1. 블록체인 네트워크를 설정
이거 끝내면 Fabric 애플리케이션과 스마트 계약이 함께 작동하여 블록체인 네트워크의 분산 원장에서 데이터를 관리하는 방법에 대해 알 수 있을거야~~
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
스크립트를 호출하여 스마트 계약이 포함된 체인코드 패키지를 배포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로 실행하는거 뒤에서 하겠음.
npm install
명령을 실행하여 종속성을 설치하고 애플리케이션을 빌드package.json
에 정의된 애플리케이션 종속성을 설치@hyperledger/fabric-gateway
Node.js 패키지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
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-typescript$ npm start
gRPC 연결 설정과 관련된 상당한 오버헤드가 있으므로 애플리케이션에서 이 연결을 유지하고 Fabric Gateway와의 모든 상호 작용에 사용
경고
트랜잭션에 사용되는 개인 데이터의 보안을 유지하기 위해 애플리케이션은 클라이언트 ID와 동일한 조직에 속한 Fabric Gateway에 연결해야 함. 클라이언트 ID의 조직이 게이트웨이를 호스팅하지 않는 경우 다른 조직의 신뢰할 수 있는 게이트웨이를 사용해야 함.
TypeScript 응용 프로그램은 서명 인증 기관의 TLS 인증서를 사용해 gRPC 연결을 생성하여 게이트웨이의 TLS 인증서의 진위를 확인
TLS 연결이 성공적으로 설정되려면 클라이언트가 사용하는 endpoint 주소가 게이트웨이의 TLS 인증서에 있는 주소와 일치해야 함.
app.ts에서 발췌한 부분
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',
});
}
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);
}
const network = gateway.getNetwork(channelName);
const contract = network.getContract(chaincodeName);
체인코드 패키지에 여러 스마트 계약이 포함된 경우 체인코드의 이름과 특정 스마트 계약의 이름을 모두 getContract() 호출에 대한 인수로 제공가능
const contract = network.getContract(chaincodeName, smartContractName);
체인코드 패키지의 초기 배포 직후 원장은 비어 있dma
애플리케이션은 submitTransaction()을 사용하여 일부 샘플 자산으로 원장을 채우는 InitLedger 트랜잭션 함수를 호출
submitTransaction()은 Fabric Gateway를 사용하여 다음을 수행
샘플 애플리케이션 InitLedger 호출:
await contract.submitTransaction('InitLedger');
이제 애플리케이션은 스마트 계약에서 트랜잭션 기능을 호출하여 원장에서 쿼리하고, 추가 자산을 생성하고, 자산을 수정하는 비즈니스 로직을 실행할 준비가 됨.
애플리케이션은 읽기 전용 트랜잭션 호출을 수행하여 원장을 조회하기 위해 evaluateTransaction()을 사용
evaluateTransaction() : Fabric Gateway를 사용하여 트랜잭션 기능을 호출하고 그 결과를 리턴
아래에서 샘플 애플리케이션은 원장을 채울 때 이전 단계에서 생성된 모든 자산을 가져오고 있음.
샘플 애플리케이션 GetAllAssets 호출:
const resultBytes = await contract.evaluateTransaction('GetAllAssets');
const resultJson = utf8Decoder.decode(resultBytes);
const result = JSON.parse(resultJson);
console.log('*** Result:', result);
트랜잭션 함수는 모든 유형의 데이터를 반환할 수 있으므로 트랜잭션 함수 결과는 항상 바이트로 반환
- 종종 트랜잭션 함수는 문자열을 반환
- 위의 경우와 같이 JSON 데이터의 UTF-8 문자열 반환
- 응용 프로그램은 결과 바이트를 올바르게 해석할 책임을 가짐
*** 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 } ]
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
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 }
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">'failed to endorse transaction, see attached details for more info'</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 '@grpc/grpc-js';
import { connect, Contract, Identity, Signer, signers } from '@hyperledger/fabric-gateway';
import * as crypto from 'crypto';
import { promises as fs } from 'fs';
import * as path from 'path';
import { TextDecoder } from 'util';
const channelName = envOrDefault('CHANNEL_NAME', 'mychannel');
const chaincodeName = envOrDefault('CHAINCODE_NAME', 'basic');
const mspId = envOrDefault('MSP_ID', 'Org1MSP');
// Path to crypto materials.
const cryptoPath = envOrDefault('CRYPTO_PATH', path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com'));
// Path to user private key directory.
const keyDirectoryPath = envOrDefault('KEY_DIRECTORY_PATH', path.resolve(cryptoPath, 'users', 'User1@org1.example.com', 'msp', 'keystore'));
// Path to user certificate.
const certPath = envOrDefault('CERT_PATH', path.resolve(cryptoPath, 'users', 'User1@org1.example.com', 'msp', 'signcerts', 'cert.pem'));
// Path to peer tls certificate.
const tlsCertPath = envOrDefault('TLS_CERT_PATH', path.resolve(cryptoPath, 'peers', 'peer0.org1.example.com', 'tls', 'ca.crt'));
// Gateway peer endpoint.
const peerEndpoint = envOrDefault('PEER_ENDPOINT', 'localhost:7051');
// Gateway peer SSL host name override.
const peerHostAlias = envOrDefault('PEER_HOST_ALIAS', 'peer0.org1.example.com');
const utf8Decoder = new TextDecoder();
const assetId = `asset${Date.now()}`;
async function main(): Promise<void> {
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: () => {
return { deadline: Date.now() + 5000 }; // 5 seconds
},
endorseOptions: () => {
return { deadline: Date.now() + 15000 }; // 15 seconds
},
submitOptions: () => {
return { deadline: Date.now() + 5000 }; // 5 seconds
},
commitStatusOptions: () => {
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 'InitLedger' 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 => {
console.error('******** FAILED to run the application:', error);
process.exitCode = 1;
});
/**
* 클라이언트가 게이트웨의 Docker 컨테이너에 액세스 하므로 peerEndpoint를 localhost 주소로 하고,
* gRPC 옵션을 endpoint 주소를 게이트웨이의 구성된 호스트 이름으로 해석하도록 설정
**/
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': peerHostAlias,
});
}
async function newIdentity(): Promise<Identity> {
const credentials = await fs.readFile(certPath);
return { mspId, credentials };
}
async function newSigner(): Promise<Signer> {
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 "init" function.
*/
async function initLedger(contract: Contract): Promise<void> {
console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger');
await contract.submitTransaction('InitLedger');
console.log('*** Transaction committed successfully');
}
/**
* Evaluate a transaction to query ledger state.
*/
async function getAllAssets(contract: Contract): Promise<void> {
console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger');
const resultBytes = await contract.evaluateTransaction('GetAllAssets');
const resultJson = utf8Decoder.decode(resultBytes);
const result = JSON.parse(resultJson);
console.log('*** Result:', result);
}
/**
* Submit a transaction synchronously, blocking until it has been committed to the ledger.
*/
async function createAsset(contract: Contract): Promise<void> {
console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments');
await contract.submitTransaction(
'CreateAsset',
assetId,
'yellow',
'5',
'Tom',
'1300',
);
console.log('*** 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.
*/
async function transferAssetAsync(contract: Contract): Promise<void> {
console.log('\n--> Async Submit Transaction: TransferAsset, updates existing asset owner');
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 function readAssetByID(contract: Contract): Promise<void> {
console.log('\n--> Evaluate Transaction: ReadAsset, function returns asset attributes');
const resultBytes = await contract.evaluateTransaction('ReadAsset', assetId);
const resultJson = utf8Decoder.decode(resultBytes);
const result = JSON.parse(resultJson);
console.log('*** Result:', result);
}
/**
* submitTransaction() will throw an error containing details of any error responses from the smart contract.
*/
async function updateNonExistentAsset(contract: Contract): Promise<void>{
console.log('\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error');
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);
}
}
/**
* 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<void> {
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}`);
}
필요없는 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
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$
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
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)
yujeong@yujeong-VirtualBox:~/go/src/fabric-samples/asset-transfer-basic/application-gateway-java$ sudo apt install 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
Main-Class: ApplicationGatewayJava
를 붙여넣기/*
* 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());
}
}
}
우리가 지금까지 한거