티스토리 뷰
패브릭 애플리케이션 실행~
Fabric 블록체인 네트워크의 기본 아키텍쳐에 아직 익숙하지 않은 경우, 주요개념 색션을 방문하는게 좋습니다.
====> 봐도 뭔지 모르겠음. 일단 ㄱㄱ
이 튜토리얼은 Fabric 애플리케이션이 배포된 블록체인 네트워크와 상호작용하는 방법에 대한 소개.
Fabric Gateway 애플리케이션 API를 사용하여 구축된 샘플 프로그램을 사용하여 스마트 계약을 호출하고 스마트 계약 API로 원장을 쿼리하고 업데이트한다.
블록체인 네트워크 시작
이동해서, 일단 환경을 깨끗하게 만들어주고, Fabric test network를 시작한다.
cd fabric-samples/test-network
./network.sh down
./network.sh up createChannel -c mychannel -ca
스마트 계약 배포
자바스크립트로된 체인코드를 배포한다.
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-javascript/ -ccl javascript
위 스크립트는 체인코드 라이프사이클을 사용하여 설치된 체인코드를 패키징, 설치,쿼리하고, Org1 과 Org2 모두에 대해 체인코드를 승인하고 마지막으로 체인코드를 커밋한다.
체인코드 패키지가 성공적으로 배포되면 터미널의 출력 끝은 아래와 비슷하게 나온다.
샘플 애플리케이션 준비~
새 터미널을 열고 디렉토리 이동, npm install
cd asset-transfer-basic/application-gateway-typescript
npm install
인데,...
한시간 넘게 헤매다가 발견한 구세주:
샘플 애플리케이션 실행
그리고 npm start하면 실행된다.
빠르게 진행되고 마지막에 오류와 함께 종료된다.
application-gateway-typescript git:(main) npm start
> asset-transfer-basic@1.0.0 start
> node dist/app.js
--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger
*** Transaction committed successfully
--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger
*** Result: [
{
AppraisedValue: 300,
Color: 'blue',
ID: 'asset1',
Owner: 'Tomoko',
Size: 5,
docType: 'asset'
},
{
AppraisedValue: 1300,
Color: 'yellow',
ID: 'asset1643075061863',
Owner: 'Saptha',
Size: 5
},
{
AppraisedValue: 400,
Color: 'red',
ID: 'asset2',
Owner: 'Brad',
Size: 5,
docType: 'asset'
},
{
AppraisedValue: 500,
Color: 'green',
ID: 'asset3',
Owner: 'Jin Soo',
Size: 10,
docType: 'asset'
},
{
AppraisedValue: 600,
Color: 'yellow',
ID: 'asset4',
Owner: 'Max',
Size: 10,
docType: 'asset'
},
{
AppraisedValue: 700,
Color: 'black',
ID: 'asset5',
Owner: 'Adriana',
Size: 15,
docType: 'asset'
},
{
AppraisedValue: 800,
Color: 'white',
ID: 'asset6',
Owner: 'Michel',
Size: 15,
docType: 'asset'
}
]
--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments
*** 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
--> Evaluate Transaction: ReadAsset, function returns asset attributes
*** Result: {
AppraisedValue: 1300,
Color: 'yellow',
ID: 'asset1643075684079',
Owner: 'Saptha',
Size: 5
}
--> 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 /home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@hyperledger/fabric-gateway/dist/client.js:45:347
at Object.callback (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@hyperledger/fabric-gateway/dist/client.js:81:27)
at Object.onReceiveStatus (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/client.js:180:36)
at Object.onReceiveStatus (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:365:141)
at Object.onReceiveStatus (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:328:181)
at /home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/call-stream.js:182:78
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
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 Object.callErrorFromStatus (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/call.js:31:26)
at Object.onReceiveStatus (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/client.js:180:52)
at Object.onReceiveStatus (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:365:141)
at Object.onReceiveStatus (/home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:328:181)
at /home/ehdcjf/go/src/github.com/ehdcjf/fabric-samples/asset-transfer-basic/application-gateway-typescript/node_modules/@grpc/grpc-js/build/src/call-stream.js:182:78
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
code: 10,
details: 'failed to endorse transaction, see attached details for more info',
metadata: Metadata { internalRepr: [Map], options: {} }
},
transactionId: '2bbd87204433414a703a807cd7f47a9ed00ce251913b932c3cbb499f6920459a'
}
왜 오류가 나오냐고! 원인을 찾으려고, 직접 app.ts를 봤는데. 그냥 오류가 나오도록 설정 되어 있었다.
문서는 샘플 애플리케이션이 어떻게 작동하는 지 설명한다. 에러처리까지 ^^
1. 게이트웨이에 대한 gRPC 연결 설정
클라이언트 애플리케이션은 블록체인 네트워크와 거래하는 데 사용할 Fabric Gateway 서비스에 대한 gRPC 연결을 설정한다. 이렇게 하려면 Fabric Gateway의 끝점 주소와 TLS를 사용하도록 구성된 경우 적절한 TLS 인증서만 필요하다. 이 샘플에서 게이트웨이 앤드포인트 주소는 Fabric Gateway 서비스를 제공하는 피어의 주소다.
Typescript 애플리케이션은 서명 인증 기관의 LTS 인증서를 사용하여 gRPC 연결을 게이트웨이의 TLS 인증서의 진위를 확인할 수 있다.
TLS 연결이 성공적으로 설정되려면 클라이언트에서 사용하는 끝점 주소가 게이트웨이의 TLS 인증서에 있는 주소와 일치해야 한다. 클라이언트는게이트웨이의 Docker 컨테이너에 엑세스하므로 localhost가 게이트웨이에 구성된 endpoiont의 주소로 해석되도록 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 Gateway에 엑세스 하기 위해 모든 Gateway에 엑세스하는 데 사용하는 연결을 생성한다.
그리고 순차적으로 스마트 계약이 이 네트워크에 배포된다. Gateway connection은 세 가지 요구사항이 필요하다.
1. Fabric Gateway에 gRPC 연결 (위에서 한 거)
2. network와 트랜잭션하는데 쓰이는 Client identity
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. 호출할 스마트 계약에 액세스
샘플 애플리케이션은 Gateway 연결을 사용하여 해당 네트워크와 네트워크에 배포된 체인코드 안의 스마트 컨트랙트에 대한 참조를 얻는다
const channelName = 'mychannel';
const chaincodeName = 'basic';
const network = gateway.getNetwork(channelName);
const contract = network.getContract(chaincodeName);
체인코드가 여러개의 스마트 컨트랙트로 구성되어 있다면. 체인코드이름과. 특정 스마트컨트랙트의 이름을 이용해서 참조를 얻을 수 있다.
const contract = network.getContract('chaincodeName', 'smartContractName');
4. 원장을 샘플 자산으로 채우기
체인코드 패키지의 초기 배포 직후 원장은 비어있다. 애플리케이션은 submitTransaction() 을 사용해서 원장을 몇 개의 샘플 자산으로 채우는 InitLedger 트랜잭션 함수를 호출한다. submitTransaction()는 Fabric Gateway를 사용하여 다음을 수행한다.
1. 거래 제안을 승인한다.
2. 승인된 거래를 order service에 제출한다.
3. 트랜잭션이 커밋될 때까지 기다리며 원장 상태를 업데이트 한다.
샘플 애플리케이션 InitLedger 호출:
await contract.submitTransaction('InitLedger');
5. 자산을 읽고, 쓰는 트랜잭션 호출
애플리케이션은 스마트 계약에서 트랜잭션 기능을 호출하여 원장의 자산을 쿼리하고, 추가 자산을 생성하고, 수정하는 비지니스 로직을 실행
모든 자산 쿼리
evaluateTransaction()을 이용하여 읽기 전용 트랜잭션을 호출을 수행하여 원장을 쿼리.
evaluateTransaction()은 Fabric network를 사용하여 트랜잭션 기능을 호출하고 결과를 반환한다.
트랜잭션은 order service로 전송되지 않으며 원장 업데이트가 발생하지 않는다.
아래에서 샘플 애플리케이션은 원장을 채울 때, 이전 단계에서 생성된 모든 자산을 가져오고 있다.
const resultBytes = await contract.evaluateTransaction('GetAllAssets');
const resultJson = utf8Decoder.decode(resultBytes);
const result = JSON.parse(resultJson);
console.log('*** Result:', result);
출력 결과는 다음과 같다.
*** Result: [
{
AppraisedValue: 300,
Color: 'blue',
ID: 'asset1',
Owner: 'Tomoko',
Size: 5,
docType: 'asset'
},
{
AppraisedValue: 400,
Color: 'red',
ID: 'asset2',
Owner: 'Brad',
Size: 5,
docType: 'asset'
},
{
AppraisedValue: 500,
Color: 'green',
ID: 'asset3',
Owner: 'Jin Soo',
Size: 10,
docType: 'asset'
},
{
AppraisedValue: 600,
Color: 'yellow',
ID: 'asset4',
Owner: 'Max',
Size: 10,
docType: 'asset'
},
{
AppraisedValue: 700,
Color: 'black',
ID: 'asset5',
Owner: 'Adriana',
Size: 15,
docType: 'asset'
},
{
AppraisedValue: 800,
Color: 'white',
ID: 'asset6',
Owner: 'Michel',
Size: 15,
docType: 'asset'
}
]
새 자산 만들기
CreateAsset() 을 통해 새로운 자산을 생성한다.
const assetId = `asset${Date.now()}`;
await contract.submitTransaction(
'CreateAsset',
assetId,
'yellow',
'5',
'Tom',
'1300',
);
애플리케이션에 작성된 함수의 인자는 체인코드가 예상하는, 체인코드에 작성된 것과 동일한 타입, 수, 순서로 작성되어야 한다.
위에서 올바르게 배열된 인수는 다음과 같다.
assetId, "yellow", "5", "Tom", "1300"
체인코드에서는 인자 순서를 다음과 같이 예상한다.
ID, Color, Size, Owner, AppraisedValue
자산 업데이트
샘플 애플리케이션은 새로 생성된 자산의 소유권을 이전하기 위해 트랜잭션을 제출한다.
으로 호출되는 트랜잭션은 이번에는 트랜잭션이 원장에 커밋될 때까지 기다리는 게 아니라, 보증된 트랜잭션이 order service에 성공적으로 제출되면 반환하게 된다. 이렇게 하면 트랜잭션이 커밋되기를 기다리는 동안 트랜잭션 결과를 사용하여 작업을 진행할 수 있다.
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');
터미널 출력은 다음과 같다.
*** 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);
터미널 출력은 다음과 같다.
*** Result: {
AppraisedValue: 1300,
Color: 'yellow',
ID: 'asset1639084597466',
Owner: 'Saptha',
Size: 5
}
트랜잭션 오류 처리
시퀀스의 마지막 부분은 트랜잭션 제출 오류를 보여준다. 이 예에서 애플리케이셔은 UpdateAsset을 시도하지만, 존재하지 않는 자산ID(asset70)를 지정한다. 트랜잭션 함수는 오류 응답을 반환하고, submitTransaction() 호출은 실패한다.
submitTransaction()실패는 제출 흐름에서 오류가 발생한 지점을 표시하고, 애플리케이션이 적절하게 응답할 수 이또록하는 추가 정보를 포함하는 여러 유형의 오류를 생성할 수 있다.
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);
}
출력결과
*** 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: 'error in simulation: transaction returned with failure: Error: The asset asset70 does not exist',
mspId: 'Org1MSP'
}
],
cause: Error: 10 ABORTED: failed to endorse transaction, see attached details for more info
at ... {
code: 10,
details: 'failed to endorse transaction, see attached details for more info',
metadata: Metadata { internalRepr: [Map], options: {} }
},
transactionId: 'a92980d41eef1d6492d63acd5fbb6ef1db0f53252330ad28e548fedfdb9167fe'
}
EndorseError 유형은 보증 중 실패가 발생했음을 나타내고, gRPC 상태코드 ABORTED는 애플리케이션이 Fabric Gateway를 성공적으로 호출했지만 보증 프로세스에서 실패가 발생했음을 나타낸다.
gRPC 상태코드 UNAVAILABLE 또는 DEADLINE_EXCEEDED는 Fabric Gateway에 연결할 수 없거나 적시에 응답을 받지 못했음을 암시하므로 작업을 재시도하는 것이 적절할 것이다.
마무리
이제 마치자.
./network.sh down
'블록체인' 카테고리의 다른 글
블록체인의 특징 및 중요성 (0) | 2022.04.18 |
---|---|
IPFS 실습 (0) | 2022.04.02 |
Hyperledger Tutorial2: Deploying a smart contract to a channel (0) | 2022.01.24 |
HyperLedger Fabric Peers(node) 개념 (0) | 2022.01.14 |
Geth) Geth 콘솔 (0) | 2021.12.27 |
- Total
- Today
- Yesterday
- 개발자면접
- node.js
- MySQL
- 로드나인
- 그래프
- create db
- 은둔청년체험
- 투포인터
- 면접질문
- MOD
- 롱베케이션
- 서버개발
- 최소공통조상
- DB 생성
- 다이나밍프로그래밍
- 면접비
- 다이나믹프로그래밍
- 동적프로그래밍
- create databases;
- 투포인터 연습
- BFS
- 서버점검
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |