ノード情報の取得
ノードへリクエスト
アカウント情報を取得するパケットタイプは、0x443
です。
Catapult.ts
/** パケットタイプ */
private PacketType = {
PULL_BLOCK: 0x0_04,
CHAIN_STATISTICS: 0x0_05,
PULL_BLOCKS: 0x0_08,
PUSH_TRANSACTIONS: 0x0_09,
PUSH_PARTIAL_TRANSACTIONS: 0x1_00,
NODE_DISCOVERY_PULL_PING: 0x1_11,
TIME_SYNC_NETWORK_TIME: 0x1_20,
FINALIZATION_STATISTICS: 0x1_32,
UNLOCKED_ACCOUNTS: 0x3_04,
ACCOUNT_INFOS: 0x4_43,
METADATA_INFOS: 0x4_44,
}
async getAccountInfos(addresses: Uint8Array[]) {
const accountInfos: AccountInfo[] = []
try {
const payload = Buffer.concat(addresses)
const socketData = await this.request(this.PacketType.ACCOUNT_INFOS, payload)
if (socketData) {
let offset = 0
do {
const accountInfo = AccountInfo.deserialize(socketData.slice(offset))
accountInfos.push(accountInfo)
offset += accountInfo.accountInfoSize
} while (offset < socketData.length)
}
} catch (e) {
if (e instanceof Error) console.error(e.message)
else console.error(e)
} finally {
this.close()
}
return accountInfos
}
レスポンスデータの解析
シリアライズされたデータが返ってくるので、デシリアライズします。
src/models/AccountInfo.ts
import { Address, models } from 'symbol-sdk/symbol'
import { PacketBuffer } from '../PacketBuffer.js'
// AccountType Unlinked: 0x00, Main: 0x01, Remote: 0x02, Remote_Unlinked: 0x03
// AccountStateFormat Regular(通常のアカウント): 0, HighValue(収穫可能なアカウント): 1
// AccountKeyTypeFlags Unset: 0x00, Linked: 0x01, Node: 0x02, VRF: 0x04
export class AccountInfo {
constructor(
public accountInfoSize: number,
public accountDataSize: number,
public accountInfoKey: string,
public version: number,
public address: Address,
public addressHeight: models.Height,
public publicKey: models.PublicKey,
public publicKeyHeight: models.Height,
public accountType: number,
public accountStateFormat: number,
public accountKeyTypeFlags: number,
public votingPublickeysCount: number,
public linkedPublicKey: models.PublicKey | undefined,
public nodePublicKey: models.PublicKey | undefined,
public vrfPublicKey: models.PublicKey | undefined,
public votingKeys: VotingKey[],
public importanceScore: bigint | undefined,
public importanceHeight: models.Height | undefined,
public activityBuckets: ActivityBucket[],
public balancesCount: number,
public balances: models.Mosaic[]
) {}
static deserialize(payload: Uint8Array) {
const packetBuf = new PacketBuffer(Buffer.from(payload))
const accountInfoSize = packetBuf.readUInt32LE()
const accountDataSize = packetBuf.readUInt32LE()
const accountInfoKey = packetBuf.readHexString(24).toUpperCase()
const version = packetBuf.readUInt16LE()
const address = Address.fromDecodedAddressHexString(packetBuf.readHexString(24))
const addressHeight = new models.Height(packetBuf.readBigUInt64LE())
const publicKey = new models.PublicKey(packetBuf.readHexString(32))
const publicKeyHeight = new models.Height(packetBuf.readBigUInt64LE())
const accountType = packetBuf.readUInt8()
const accountStateFormat = packetBuf.readUInt8()
const accountKeyTypeFlags = packetBuf.readUInt8()
const votingKeysCount = packetBuf.readUInt8()
let linkedPublicKey
if (accountKeyTypeFlags & 0x01) {
linkedPublicKey = new models.PublicKey(packetBuf.readHexString(32))
}
let nodePublicKey
if (accountKeyTypeFlags & 0x02) {
nodePublicKey = new models.PublicKey(packetBuf.readHexString(32))
}
let vrfPublicKey
if (accountKeyTypeFlags & 0x04) {
vrfPublicKey = new models.PublicKey(packetBuf.readHexString(32))
}
const votingKeys: VotingKey[] = []
for (let i = 0; i < votingKeysCount; i++) {
const votingPublicKey = new models.PublicKey(packetBuf.readHexString(32))
const startEpoch = packetBuf.readUInt32LE()
const endEpoch = packetBuf.readUInt32LE()
const votingKey = new VotingKey(votingPublicKey, startEpoch, endEpoch)
votingKeys.push(votingKey)
}
let importanceScore
let importanceHeight
const heightActivityBuckets: ActivityBucket[] = []
if (accountStateFormat === 1) {
importanceScore = packetBuf.readBigUInt64LE()
importanceHeight = new models.Height(packetBuf.readBigUInt64LE())
for (let i = 0; i < 5; i++) {
const startHeight = new models.Height(packetBuf.readBigUInt64LE())
const totalFeesPaid = new models.Amount(packetBuf.readBigUInt64LE())
const beneficiaryCount = packetBuf.readUInt32LE()
const rawScore = packetBuf.readBigUInt64LE()
const activityBucket = new ActivityBucket(
startHeight,
totalFeesPaid,
beneficiaryCount,
rawScore
)
heightActivityBuckets.push(activityBucket)
}
}
const balancesCount = packetBuf.readUInt16LE()
const balances: models.Mosaic[] = []
for (let i = 0; i < balancesCount; i++) {
const mosaic = new models.Mosaic()
mosaic.mosaicId = new models.MosaicId(packetBuf.readBigUInt64LE())
mosaic.amount = new models.Amount(packetBuf.readBigUInt64LE())
balances.push(mosaic)
}
return new AccountInfo(
accountInfoSize,
accountDataSize,
accountInfoKey,
version,
address,
addressHeight,
publicKey,
publicKeyHeight,
accountType,
accountStateFormat,
accountKeyTypeFlags,
votingKeysCount,
linkedPublicKey,
nodePublicKey,
vrfPublicKey,
votingKeys,
importanceScore,
importanceHeight,
heightActivityBuckets,
balancesCount,
balances
)
}
toJson() {
const linkedPublicKey = this.linkedPublicKey
? { publicKey: this.linkedPublicKey?.toString() }
: undefined
const nodePublicKey = this.nodePublicKey
? { publicKey: this.nodePublicKey?.toString() }
: undefined
const vrfPublicKey = this.vrfPublicKey
? { publicKey: this.vrfPublicKey?.toString() }
: undefined
const votingKeys =
this.votingKeys.length !== 0 ? this.votingKeys.map((v) => v.toJson()) : undefined
return {
version: this.version,
address: this.address.toString(),
addressHeight: this.addressHeight.value.toString(),
publicKey: this.publicKey.toString(),
publicKeyHeight: this.publicKeyHeight.value.toString(),
accountType: this.accountType,
supplementalPublicKeys: {
linked: linkedPublicKey,
node: nodePublicKey,
vrf: vrfPublicKey,
voting: votingKeys,
},
activityBuckets: this.activityBuckets.map((a) => a.toJson()),
mosaics: this.balances.map((b) => b.toJson()),
importance: this.importanceScore?.toString() ?? '0',
importanceHeight: this.importanceHeight?.value.toString() ?? '0',
}
}
}
export class SupplementalPublicKeys {
constructor(
public linked: SupplementalPublicKey | undefined,
public node: SupplementalPublicKey | undefined,
public vrf: SupplementalPublicKey | undefined,
public voting: VotingKey[]
) {}
toJson() {
return {
linked: this.linked?.toString(),
node: this.node?.toString(),
vrf: this.vrf?.toString(),
voting: this.voting.map((v) => v.toJson()),
}
}
}
export class SupplementalPublicKey {
constructor(public publicKey: models.PublicKey) {}
toJson() {
return {
publicKey: this.publicKey.toString(),
}
}
}
export class VotingKey {
constructor(
public publickey: models.PublicKey,
public startEpoch: number,
public endEpoch: number
) {}
toJson() {
return {
publickey: this.publickey.toString(),
startEpoch: this.startEpoch.toString(),
endEpoch: this.endEpoch.toString(),
}
}
}
export class ActivityBucket {
constructor(
public startHeight: models.Height,
public totalFeesPaid: models.Amount,
public beneficiaryCount: number,
public rawScore: bigint
) {}
toJson() {
return {
startHeight: this.startHeight.value.toString(),
totalFeesPaid: this.totalFeesPaid.value.toString(),
beneficiaryCount: this.beneficiaryCount,
rawScore: this.rawScore.toString(),
}
}
}
メインの作成
実行するコードを作成します。
src/mainAccountInfo.ts
import { Address } from 'symbol-sdk/symbol'
import { Catapult } from './Catapult.js'
import { utils } from 'symbol-sdk'
const addresses = [
new Address('TADGQ7GDVIIOPD7TICWISNLARICML2TSFGEJWIA').bytes,
new Address('TBZN46UIU5BFLJI46VB4JTHHCE5EN2RFLR7NX3A').bytes,
new Address('TBJHAFYICU7LOTDROE7QVR6FN5O6KZYOAYLVSVA').bytes,
new Address('TAX3CCCOQTC2JC5FA23DTUVIQKOMUO2NWFXAUXA').bytes,
new Address('TATVSNP4N3IUP7VS57R7KSWZ7CKKX5U3YDPSOMQ').bytes,
]
const catapult = new Catapult(
'cert/ca.crt.pem',
'cert/node.crt.pem',
'cert/node.key.pem',
'127.0.0.1'
)
const accountInfos = await catapult.getAccountInfos(addresses)
for (const accountInfo of accountInfos) {
console.log(JSON.stringify(accountInfo.toJson(), null, 2))
}
実行
yarn tsx .\src\mainAccountInfo.ts
実行すると以下の様に結果が出力されます。
socket connected.
{
"version": 1,
"address": "TADGQ7GDVIIOPD7TICWISNLARICML2TSFGEJWIA",
"addressHeight": "144956",
"publicKey": "03A9EF5A45C8D4D059D3E9C95DC5956091E3E855DC812F363DB41D987BF927A6",
"publicKeyHeight": "144961",
"accountType": 1,
"supplementalPublicKeys": {
"linked": {
"publicKey": "B697066EC0419DF7354727822BB83581BC17E99D82914623700272BCA9C9B355"
},
"vrf": {
"publicKey": "97D8B26A629FB6AF1CAA06441AEBDA8D8504F3E867292C01F9581A5C80FE5702"
},
"voting": [
{
"publickey": "EC167F9C009F64F183DEE8FECFA737DDFFB3FD6BC01C82E681ED8886D6D0D656",
"startEpoch": "1568",
"endEpoch": "2287"
}
]
},
"activityBuckets": [
{
"startHeight": "1965240",
"totalFeesPaid": "0",
"beneficiaryCount": 0,
"rawScore": "14178634374253"
},
{
"startHeight": "1965060",
"totalFeesPaid": "0",
"beneficiaryCount": 0,
"rawScore": "14177684407803"
},
{
"startHeight": "1964880",
"totalFeesPaid": "0",
"beneficiaryCount": 0,
"rawScore": "14177007248007"
},
{
"startHeight": "1964700",
"totalFeesPaid": "0",
"beneficiaryCount": 0,
"rawScore": "14176147652219"
},
{
"startHeight": "1964520",
"totalFeesPaid": "0",
"beneficiaryCount": 0,
"rawScore": "14174655376668"
}
],
"mosaics": [
{
"mosaicId": "8268645399043017678",
"amount": "15515353361466"
}
],
"importance": "14177684407803",
"importanceHeight": "1965240"
}
{
"version": 1,
"address": "TBZN46UIU5BFLJI46VB4JTHHCE5EN2RFLR7NX3A",
...
socket close: 1091