Interacting with the Transaction Tracker

Henesis를 이용해 SDK만으로 어떻게 간단하게 트랜잭션의 상태를 추적할 수 있는지 알아보자.

Henesis SDK

Henesis SDK를 통해 트랜잭션의 상태를 추적할 수 있습니다. 현재 SDK는 javascript 언어를 지원하며 추후에 지원 언어를 확장할 계획입니다.

설치하는 법은 링크를 참조하여 주세요.

Authentication

Henesis SDK를 이용하기 위해서는 client-id 가 필요합니다. client-id는 계정 별로 고유하게 부여된 정보이며, 아래와 같이 Henesis CLI를 이용해 조회할 수 있습니다.

$ henesis account:describe
Email: haechi@haechi.io
Name: haechi
Organization: haechi-labs
ClientId: <client-id>

SDK 에서는 아래와 같이 client-id를 이용하여 트랜잭션 상태 추적을 위한 인스턴스를 생성할 수 있습니다.

import { TransactionTracker } from '@haechi-labs/henesis-sdk-js'
const transactionTracker = new TransactionTracker('client-id', {
// platform and network options
platform: 'ethereum',
network: 'ropsten'
});

Select Platform and Network

client-id로 인증을 하고 나서 사용하고자 하는 platformnetwork를 선택합니다.

import { TransactionTracker } from '@haechi-labs/henesis-sdk-js'
const transactionTracker = new TransactionTracker('client-id', {
// platform and network options
platform: 'ethereum',
network: 'ropsten'
});

Henesis에서 지원되는 platformnetwork여기에서 확인가능합니다.

Tracking Transactions

트랜잭션 상태를 추적하기 위해서는 트랜잭션을 발생시킬 때에 생성된 txHash를 사용합니다. 생성한 txHash를 SDK에서 제공되는 trackTransaction 함수 이용해 Henesis에게 해당 트랜잭션을 추적하도록합니다.

web3.js (for Ethereum)
caver.js (for Klaytn)
web3.js (for Ethereum)
const txHash = await web3.eth.sendSignedTransaction(sign(privateKey, txData))
henesis.trackTransaction(txHash, {
"confirmation": 12,
"timeout": 30 * 1000,
});
caver.js (for Klaytn)
const txHash = await caver.klay.sendSignedTransaction(sign(privateKey, txData))
henesis.trackTransaction(txHash, {
"confirmation": 0,
"timeout": 30 * 1000,
});
  • confirmation: 해당 트랜잭션이 담긴 블록 뒤에 몇 개의 블록이 더 생성되어야 confirmation 상태로 인정할 것인지 설정할 수 있습니다. Ethereum: 일반적으로 12 이상으로 하면 chain reorganization이 일어날 확률은 거의 없습니다. Klaytn: 블록이 생성되었을 때 finalize가 되기 때문에 confirmation을 0로 설정합니다.

  • timeout: 트랜잭션이 receipt 상태 바뀌는 것을 얼마동안 기다릴지 정합니다. 해당 기간동안 트랜잭션의 상태가 receipt이 아니라면, pending 상태라고 간주합니다. (단위: ms)

Subscribing to a Transaction Status

새로 생성한 Henesis 인스턴스의 subscribe 함수를 이용하여 트랜잭션 상태를 구독하는 subscription 을 만들 수 있습니다.

// const subscription = await henesis.subscribe(topic: String, options: Object)
const subscription = await henesis.subscribe(
"transaction",
{
subscriptionId: "subscription-id",
ackTimeout: 30 * 1000
}
);
  • topic : 구독할 토픽입니다. 트랜잭션 상태 변화를 듣기 위해서는 transaction을 입력합니다.

  • options : subscription생성에 필요한 정보.

    • subscriptionId: Henesis는 subscription 생성 시점부터 트랜잭션 상태를 유실없이 전달합니다. 네트워크 상황 등으로 인해 연결이 끊기더라도, 재연결이 된다면 마지막으로 전달이 성공된 데이터 이후부터 다시 전송합니다. subscriptionId는 유저가 지정할 수 있습니다. 만약 같은 subscriptionId가 존재하지 않는다면 새로운 subscription이 생성됩니다.

    • ackTimeout: Acknowledge를 기다리는 시간(단위: ms), ackTimeout 내에 Henesis SDK로 부터 ACK이 전달되지 않으면 해당 메세지가 제대로 전달되지 않았다고 판단하여 다시 전송합니다. 기본 값은 10000ms 이며 범위는 10000ms ~ 600000ms(10s ~ 600s) 입니다.

해당 subscription 인스턴스를 이용하여 다음과 같이 트랜잭션 상태를 추적할 수 있습니다.

subscription.on("message", async (message) => {
switch(message.result.type) {
case 'pending' : {
// trackTransaction 함수 호출시 설정한 timeout이 지났을 때
// TODO: Retry 로직을 작성하면 됩니다.
break;
}
case 'receipt' : {
// transaction이 채굴될 때
break;
}
case 'confirmation' : {
// transaction이 채굴된 후 'confirmation'개의 블록이 추가 생성될 때
break;
}
}
message.ack();// 반드시 호출해야 합니다.
}
subscription.on("error", async (error) => {
// handling errors
});

Henesis에서 전달하는 데이터를 구독하기 위해서 on 함수를 사용합니다. subscription 에는 2가지 토픽이 존재하며, 각 토픽의 데이터는 아래와 같은 상황에서 발생합니다. 각 토픽에서 전달되는 데이터를 이용해 원하는 로직을 구현할 수 있습니다.

Topic

발생 상황

message

추적하는 트랜잭션의 상태가 변경됬을 때

error

Henesis에 장애 상황이 발생했을 때 (네트워크 에러, 노드 에러)

"message" 토픽으로 구독하는 데이터

message 토픽을 통해 3가지 type 의 데이터를 구독할 수 있습니다.

  • pending henesis.trackTransaction을 호출하고 timeout 이 지난 후에도 채굴이 되지 않았을 때 발생합니다.

Ethereum
Klaytn
Ethereum
{
"ackId":"400fcbf0-f8c2-488f-a582-311d90a89733",
"id":"4770a7fa-f121-4a73-986f-e96e20101c22",
"data":{
"type":"pending",
"result":{
"transactionHash":"0xda21fb02bd65f8c67719186c7623680bb4fa4b80fd35da4cd86f20b8d0e86d3d",
"confirmation":12,
"timeout":5000
}
}
...
}
Klaytn
{
"ackId":"400fcbf0-f8c2-488f-a582-311d90a89733",
"id":"4770a7fa-f121-4a73-986f-e96e20101c22",
"data":{
"type":"pending",
"result":{
"transactionHash":"0xda21fb02bd65f8c67719186c7623680bb4fa4b80fd35da4cd86f20b8d0e86d3d",
"confirmation":0,
"timeout":5000
}
}
...
}
  • receipt 추적하는 txHash 가 채굴 되었을 때 발생합니다.

Ethereum
Klaytn
Ethereum
{
"ackId": "7ce5136f-5aff-442f-8048-1ae3529be44d",
"id": "7238f816-3bb7-4d47-b879-687795e11950",
"data": {
"type": "receipt",
"result": {
"blockHash": "0x323638b745cafd2eaf15ef6cb3fb17e2e5eb506b274043a4791fd714c845ab58",
"blockNumber": 6958719,
"contractAddress": null,
"cumulativeGasUsed": 1884514,
"from": "0xd6eebcfe0aca733961e2aa9b449cc06bcf98f202",
"gasUsed": 27646,
"logs": [],
"logsBloom": "0x0000...",
"status": true,
"to": "0xfea46633729847ebb49f82c64be3417398bb00a1",
"transactionHash": "0xff2526c36b171ae02d0244bf00750a40f44b4a922d5684751ca88a329d837094",
"transactionIndex": 18
}
},
...
}
Klaytn
{
"ackId": "5a6c63e1-cde0-4cf7-b393-fc338d25cf9a",
"id": "f060f377-9f4b-4a0b-af9b-9ef000e9c448",
"data": {
"type": "confirmation",
"result": {
"blockHash": "0xf0798362fd70e669d370f089f805a2d765db3e8f22d9dedbf387b159cc8ac509",
"blockNumber": 14622924,
"contractAddress": null,
"from": "0x861bdf827013277f148750e22637579efdf9f5ff",
"gas": "0x171000",
"gasPrice": "0x5d21dba00",
"gasUsed": 21000,
"input": "0x",
"logs": [],
"logsBloom": "0x0000...",
"nonce": "0x4f4",
"senderTxHash": "0x03d0fff8dc2d22ea2634e39fb9e3f4b9f8b81201137377aaf42389755bba25f4",
"signatures": [
{
"V": "0x7f6",
"R": "0xefc5190ce923a7dc62baf9fc00de895247d1945436b7b33cee522de4acc56732",
"S": "0x13e07d4dcd446e030034f4e47eb4150bf78a219dd5b6407a325a22bba35be046"
}
],
"status": true,
"to": "0x861bdf827013277f148750e22637579efdf9f5ff",
"transactionHash": "0x03d0fff8dc2d22ea2634e39fb9e3f4b9f8b81201137377aaf42389755bba25f4",
"transactionIndex": 0,
"type": "TxTypeLegacyTransaction",
"typeInt": 0,
"value": "0x1"
}
},
...
}
  • confirmation 추적하는 txHash 가 채굴되고confirmation 수 만큼 추가 블록이 생성되었을 때 발생합니다.

Ethereum
Klaytn
Ethereum
{
"ackId": "7ce5136f-5aff-442f-8048-1ae3529be44d",
"id": "7238f816-3bb7-4d47-b879-687795e11950",
"data": {
"type": "receipt",
"result": {
"blockHash": "0x323638b745cafd2eaf15ef6cb3fb17e2e5eb506b274043a4791fd714c845ab58",
"blockNumber": 6958719,
"contractAddress": null,
"cumulativeGasUsed": 1884514,
"from": "0xd6eebcfe0aca733961e2aa9b449cc06bcf98f202",
"gasUsed": 27646,
"logs": [],
"logsBloom": "0x0000...",
"status": true,
"to": "0xfea46633729847ebb49f82c64be3417398bb00a1",
"transactionHash": "0xff2526c36b171ae02d0244bf00750a40f44b4a922d5684751ca88a329d837094",
"transactionIndex": 18
}
},
...
}
Klaytn
{
"ackId": "5a6c63e1-cde0-4cf7-b393-fc338d25cf9a",
"id": "f060f377-9f4b-4a0b-af9b-9ef000e9c448",
"data": {
"type": "confirmation",
"result": {
"blockHash": "0xf0798362fd70e669d370f089f805a2d765db3e8f22d9dedbf387b159cc8ac509",
"blockNumber": 14622924,
"contractAddress": null,
"from": "0x861bdf827013277f148750e22637579efdf9f5ff",
"gas": "0x171000",
"gasPrice": "0x5d21dba00",
"gasUsed": 21000,
"input": "0x",
"logs": [],
"logsBloom": "0x0000...",
"nonce": "0x4f4",
"senderTxHash": "0x03d0fff8dc2d22ea2634e39fb9e3f4b9f8b81201137377aaf42389755bba25f4",
"signatures": [
{
"V": "0x7f6",
"R": "0xefc5190ce923a7dc62baf9fc00de895247d1945436b7b33cee522de4acc56732",
"S": "0x13e07d4dcd446e030034f4e47eb4150bf78a219dd5b6407a325a22bba35be046"
}
],
"status": true,
"to": "0x861bdf827013277f148750e22637579efdf9f5ff",
"transactionHash": "0x03d0fff8dc2d22ea2634e39fb9e3f4b9f8b81201137377aaf42389755bba25f4",
"transactionIndex": 0,
"type": "TxTypeLegacyTransaction",
"typeInt": 0,
"value": "0x1"
}
},
...
}