diff --git a/app/dimensions/clientcommandhandler.ts b/app/dimensions/clientcommandhandler.ts index 9f442ea..bec484d 100644 --- a/app/dimensions/clientcommandhandler.ts +++ b/app/dimensions/clientcommandhandler.ts @@ -1,5 +1,5 @@ -import Client from './client.js'; -import ClientState from './clientstate.js'; +import Client from "./client.js"; +import ClientState from "./clientstate.js"; export interface Command { name: string; @@ -13,12 +13,12 @@ export class ClientCommandHandler { /** * Turns a message into a command object, splitting the command name * from its arguments. - * + * * @param message The message to convert into a command object * @return The command object created */ public parseCommand(message: string): Command { - let args: string[] = message.split(' '); + let args: string[] = message.split(" "); let name: string = message.substr(1, args[0].length - 1); // Remove first arg as it is the command name @@ -28,47 +28,64 @@ export class ClientCommandHandler { /** * Handles any matching command from the client - * + * * @param command The command object with the name and args * @param client The client that is trying to use the command * @return whether or not the command was handled */ public handle(command: Command, client: Client): boolean { - let handled: boolean = false; + //let handled: boolean = false; if (client.servers[command.name]) { - if (client.server.name.toLowerCase() == command.name && client.connected) { - client.sendChatMessage(client.options.language.phrases.youAreAlreadyInthatDimension); + if ( + client.server.name.toLowerCase() == command.name && + client.connected + ) { + client.sendChatMessage( + client.options.language.phrases.youAreAlreadyInthatDimension, + ); } else { - if (client.state === ClientState.FullyConnected || client.state === ClientState.Disconnected) { - client.sendChatMessage(client.options.language.phrases.shiftingToDimension.replace("${name}", client.servers[command.name].name), "FF0000"); + if ( + client.state === ClientState.FullyConnected || + client.state === ClientState.Disconnected + ) { + client.sendChatMessage( + client.options.language.phrases.shiftingToDimension.replace( + "${name}", + client.servers[command.name].name, + ), + "FF0000", + ); client.changeServer(client.servers[command.name]); } else { - client.sendChatMessage(client.options.language.phrases.youNeedToWaitUntilConnected); + client.sendChatMessage( + client.options.language.phrases.youNeedToWaitUntilConnected, + ); } } - handled = true; + //handled = true; } else { switch (command.name) { case "who": - handled = this.handleWho(client); - break; - case "dimensions": - case client.options.language.phrases.dimensionsCommandName: - handled = this.handleDimensions(client); + this.handleWho(client); break; + //case "dimensions": + //case client.options.language.phrases.dimensionsCommandName: + //this.handleDimensions(client); + //break; + //CSFT case "void": - handled = this.handleVoid(client); + this.handleVoid(client); break; } } - - return handled; + //服务器处理 + return false; } /** * Adds a message denoting how many users exist in total on this Dimensions instance - * + * * @param args The command args * @param client The client executing who * @return Whether or not the who command was handled @@ -81,19 +98,20 @@ export class ClientCommandHandler { } // Try to make it come after the normal response - setTimeout(function() { - client.sendChatMessage(client.options.language.phrases.playerCount.replace("${total}", total.toString())); + //CSFT - 修改 + setTimeout(function () { + client.sendChatMessage( + client.options.language.phrases.playerCount.replace( + "${total}", + total.toString(), + ), + "00BFFF", + ); }, 100); return false; } - /** - * Gives the client a list of dimensions available prefixed with '/' - * - * @param args The command args - * @param client The client who is executing the command - * @return Whether the dimensions command was handled - */ + /* private handleDimensions(client: Client): boolean { let dimensionsList: string = ""; let dimensionNames: string[] = Object.keys(client.servers); @@ -101,7 +119,8 @@ export class ClientCommandHandler { let name: string = dimensionNames[i]; let hidden: boolean = client.servers[name].hidden; if (!hidden) { - dimensionsList += (i > 0 ? "[c/00B530:,] " : " ") + "/" + client.servers[name].name; + dimensionsList += + (i > 0 ? "[c/00B530:,] " : " ") + "/" + client.servers[name].name; } } @@ -111,9 +130,10 @@ export class ClientCommandHandler { return true; } + */ /** * Allows a user to disconnect from their current dimension leaving them without a server - * + * * @param args The command args * @param client The client executing the void command * @return Whether the void command was handled @@ -123,6 +143,6 @@ export class ClientCommandHandler { client.sendChatMessage(client.options.language.phrases.youEnteredTheVoid); return true; } -}; +} export default ClientCommandHandler; diff --git a/app/dimensions/clientpackethandler.ts b/app/dimensions/clientpackethandler.ts index e7cac08..f950db9 100644 --- a/app/dimensions/clientpackethandler.ts +++ b/app/dimensions/clientpackethandler.ts @@ -1,11 +1,26 @@ -import Item from './item.js'; -import Client from './client.js'; -import RawPacket from './packets/rawpacket.js'; -import { Command } from './clientcommandhandler.js'; -import ClientState from './clientstate.js'; -import ErrorHelper from './errorhelper.js'; - -import { ConnectRequestPacket, PlayerInfoPacket, PlayerBuffsSetPacket, PlayerBuffAddPacket, PlayerInventorySlotPacket, PlayerManaPacket, PlayerHealthPacket, PlayerUpdatePacket, ClientUuidPacket, NetModuleLoadPacket, ItemDropUpdatePacket, ItemOwnerPacket, PlayerSpawnPacket, Parser, } from "terraria-packet"; +import Item from "./item.js"; +import Client from "./client.js"; +import RawPacket from "./packets/rawpacket.js"; +import { Command } from "./clientcommandhandler.js"; +import ClientState from "./clientstate.js"; +import ErrorHelper from "./errorhelper.js"; + +import { + ConnectRequestPacket, + PlayerInfoPacket, + PlayerBuffsSetPacket, + PlayerBuffAddPacket, + PlayerInventorySlotPacket, + PlayerManaPacket, + PlayerHealthPacket, + PlayerUpdatePacket, + ClientUuidPacket, + NetModuleLoadPacket, + ItemDropUpdatePacket, + ItemOwnerPacket, + PlayerSpawnPacket, + Parser, +} from "terraria-packet"; class ClientPacketHandler { private currentClient!: Client; @@ -16,9 +31,15 @@ class ClientPacketHandler { let handled = false; for (let key in handlers) { let handler = handlers[key]; - if (typeof handler.priorPacketHandlers !== 'undefined' && typeof handler.priorPacketHandlers.clientHandler !== 'undefined') { + if ( + typeof handler.priorPacketHandlers !== "undefined" && + typeof handler.priorPacketHandlers.clientHandler !== "undefined" + ) { try { - handled = handler.priorPacketHandlers.clientHandler.handlePacket(client, packet); + handled = handler.priorPacketHandlers.clientHandler.handlePacket( + client, + packet, + ); if (handled) { break; } @@ -41,9 +62,15 @@ class ClientPacketHandler { let handled = false; for (let key in handlers) { let handler = handlers[key]; - if (typeof handler.postPacketHandlers !== 'undefined' && typeof handler.postPacketHandlers.clientHandler !== 'undefined') { + if ( + typeof handler.postPacketHandlers !== "undefined" && + typeof handler.postPacketHandlers.clientHandler !== "undefined" + ) { try { - handled = handler.postPacketHandlers.clientHandler.handlePacket(client, packet); + handled = handler.postPacketHandlers.clientHandler.handlePacket( + client, + packet, + ); if (handled) { break; } @@ -76,24 +103,32 @@ class ClientPacketHandler { switch (parsed.TAG) { case "ReaderError": if (parsed._0.error instanceof Error) { - client.logging.error(`Error parsing packet: ${parsed._0.context} ${parsed._0.error.message}\n${rawPacket.data.toString("hex")}`); + client.logging.error( + `Error parsing packet: ${parsed._0.context} ${parsed._0.error.message}\n${rawPacket.data.toString("hex")}`, + ); } else { - client.logging.error(`Error parsing packet: ${parsed._0.context}\n${rawPacket.data.toString("hex")}`); + client.logging.error( + `Error parsing packet: ${parsed._0.context}\n${rawPacket.data.toString("hex")}`, + ); } break; default: - client.logging.error(`Error parsing packet: ${parsed.TAG}\n${rawPacket.data.toString("hex")}`); + client.logging.error( + `Error parsing packet: ${parsed.TAG}\n${rawPacket.data.toString("hex")}`, + ); break; } - return null + return null; } else { switch (parsed) { case "IgnoredPacket": client.logging.debug(`Ignoring packet: ${rawPacket.packetType}`); break; default: - client.logging.error(`Error parsing packet: ${rawPacket.packetType} ${parsed}\n${rawPacket.data.toString("hex")}`); - return null + client.logging.error( + `Error parsing packet: ${rawPacket.packetType} ${parsed}\n${rawPacket.data.toString("hex")}`, + ); + return null; } } } else { @@ -170,7 +205,9 @@ class ClientPacketHandler { return rawPacket.data; } - private handleConnectRequest(connectRequest: ConnectRequestPacket.t): boolean { + private handleConnectRequest( + connectRequest: ConnectRequestPacket.t, + ): boolean { if (this.currentClient.version === "unknown") { this.currentClient.version = connectRequest?.version ?? "unknown"; } @@ -180,13 +217,25 @@ class ClientPacketHandler { /* Updates tracked visuals for player to restore them when they switch from * an SSC to a non-SSC server */ - private handlePlayerInfo(playerInfo: PlayerInfoPacket.t, rawPacket: RawPacket): boolean { + private handlePlayerInfo( + playerInfo: PlayerInfoPacket.t, + rawPacket: RawPacket, + ): boolean { const player = this.currentClient.player; if (player.name !== playerInfo.name) { if (player.allowedNameChange) { - this.currentClient.setName(playerInfo.name); + // CSFT - 修改 + if (player.name !== "") { + this.currentClient.disconnect("禁止在传送期间修改名称!"); + } else { + this.currentClient.setName(playerInfo.name); + } } else if (this.currentClient.options.nameChanges?.mode === "rewrite") { - const data = PlayerInfoPacket.toBuffer({ ...playerInfo, playerId: this.currentClient.player.id, name: player.name }); + const data = PlayerInfoPacket.toBuffer({ + ...playerInfo, + playerId: this.currentClient.player.id, + name: player.name, + }); if (data.TAG === "Ok") { rawPacket.data = data._0; } @@ -219,7 +268,10 @@ class ClientPacketHandler { /* Used to prevent invisibility buff from being sent to the server * for used when the config is set to blockInvis = true */ - private handleUpdatePlayerBuff(playerBuffsSet: PlayerBuffsSetPacket.t, rawPacket: RawPacket): boolean { + private handleUpdatePlayerBuff( + playerBuffsSet: PlayerBuffsSetPacket.t, + rawPacket: RawPacket, + ): boolean { let shouldBlockInvis = false; const blockInvis = this.currentClient.options.blockInvis; switch (blockInvis) { @@ -229,7 +281,13 @@ class ClientPacketHandler { case false: break; default: - shouldBlockInvis = blockInvis.enabled && blockInvis.servers.some(server => server.toLowerCase() === this.currentClient.server.name.toLowerCase()) + shouldBlockInvis = + blockInvis.enabled && + blockInvis.servers.some( + (server) => + server.toLowerCase() === + this.currentClient.server.name.toLowerCase(), + ); break; } @@ -241,9 +299,14 @@ class ClientPacketHandler { return buff; }); - const buf = PlayerBuffsSetPacket.toBuffer({ playerId: this.currentClient.player.id, buffs }) + const buf = PlayerBuffsSetPacket.toBuffer({ + playerId: this.currentClient.player.id, + buffs, + }); if (buf.TAG === "Error") { - this.currentClient.logging.error(`Error creating player buffs set: ${buf._0}`); + this.currentClient.logging.error( + `Error creating player buffs set: ${buf._0}`, + ); return true; } rawPacket.data = buf._0; @@ -270,7 +333,13 @@ class ClientPacketHandler { case false: break; default: - shouldBlockInvis = blockInvis.enabled && blockInvis.servers.some(server => server.toLowerCase() === this.currentClient.server.name.toLowerCase()) + shouldBlockInvis = + blockInvis.enabled && + blockInvis.servers.some( + (server) => + server.toLowerCase() === + this.currentClient.server.name.toLowerCase(), + ); break; } @@ -283,10 +352,21 @@ class ClientPacketHandler { /* Tracks the players inventory slots to restore them when they switch * from an SSC server to a Non-SSC server */ - private handlePlayerInventorySlot(playerInventorySlot: PlayerInventorySlotPacket.t): boolean { - if ((this.currentClient.state === ClientState.FreshConnection || this.currentClient.state === ClientState.ConnectionSwitchEstablished) && !this.currentClient.waitingCharacterRestore) { + private handlePlayerInventorySlot( + playerInventorySlot: PlayerInventorySlotPacket.t, + ): boolean { + if ( + (this.currentClient.state === ClientState.FreshConnection || + this.currentClient.state === ClientState.ConnectionSwitchEstablished) && + !this.currentClient.waitingCharacterRestore + ) { const { slot, stack, prefix, itemType } = playerInventorySlot; - this.currentClient.player.inventory[slot] = new Item(slot, stack, prefix, itemType); + this.currentClient.player.inventory[slot] = new Item( + slot, + stack, + prefix, + itemType, + ); } return false; @@ -295,8 +375,7 @@ class ClientPacketHandler { /* Tracks the player mana to restore it when they switch from an * SSC server to a Non-SSC server */ private handlePlayerMana(playerMana: PlayerManaPacket.t): boolean { - if (!this.currentClient.player.allowedManaChange) - return false; + if (!this.currentClient.player.allowedManaChange) return false; const { maxMana } = playerMana; this.currentClient.player.mana = maxMana; @@ -307,7 +386,10 @@ class ClientPacketHandler { /* Tracks the player HP to restore it when they switch from an * SSC server to a Non-SSC server */ - private handlePlayerHP(playerHealth: PlayerHealthPacket.t, rawPacket: RawPacket): boolean { + private handlePlayerHP( + playerHealth: PlayerHealthPacket.t, + rawPacket: RawPacket, + ): boolean { if (!this.currentClient.player.allowedLifeChange) { return false; } @@ -325,7 +407,10 @@ class ClientPacketHandler { return false; } - private handleUpdatePlayer(_playerUpdate: PlayerUpdatePacket.t, rawPacket: RawPacket): boolean { + private handleUpdatePlayer( + _playerUpdate: PlayerUpdatePacket.t, + rawPacket: RawPacket, + ): boolean { // Prevent this being sent too early (causing kicked for invalid operation) if (this.currentClient.state !== ClientState.FullyConnected) { this.currentClient.packetQueue.push({ rawPacket }); @@ -339,7 +424,10 @@ class ClientPacketHandler { * which causes them to be kicked. It also adds it to the packet queue * so that it may be sent when the client has fully connected (and wont * get kicked for sending it) */ - private handleUpdateItemDrop(_itemDropUpdate: ItemDropUpdatePacket.t, rawPacket: RawPacket): boolean { + private handleUpdateItemDrop( + _itemDropUpdate: ItemDropUpdatePacket.t, + rawPacket: RawPacket, + ): boolean { // Prevent this being sent too early (causing kicked for invalid operation) if (this.currentClient.state !== ClientState.FullyConnected) { this.currentClient.packetQueue.push({ rawPacket }); @@ -356,7 +444,10 @@ class ClientPacketHandler { * * Note: This packet is important for tShock SSC to work. If this was * prevented outright, SSC would be broken (inventory would be unchangable) */ - private handleUpdateItemOwner(_itemOwner: ItemOwnerPacket.t, rawPacket: RawPacket): boolean { + private handleUpdateItemOwner( + _itemOwner: ItemOwnerPacket.t, + rawPacket: RawPacket, + ): boolean { // Prevent this being sent too early (causing kicked for invalid operation) if (this.currentClient.state !== ClientState.FullyConnected) { this.currentClient.packetQueue.push({ rawPacket }); @@ -393,8 +484,12 @@ class ClientPacketHandler { // If chat message is a command if (chatMessage.length > 1 && chatMessage.substr(0, 1) === "/") { - let command: Command = this.currentClient.globalHandlers.command.parseCommand(chatMessage); - handled = this.currentClient.globalHandlers.command.handle(command, this.currentClient); + let command: Command = + this.currentClient.globalHandlers.command.parseCommand(chatMessage); + handled = this.currentClient.globalHandlers.command.handle( + command, + this.currentClient, + ); } return handled; @@ -414,15 +509,14 @@ class ClientPacketHandler { this.currentClient.packetQueue.push({ rawPacket: { data: packet.data, - packetType: packet.packetType - } + packetType: packet.packetType, + }, }); return true; } return false; } - } export default ClientPacketHandler; diff --git a/app/dimensions/configloader.ts b/app/dimensions/configloader.ts index 5a898fc..9e8cc89 100644 --- a/app/dimensions/configloader.ts +++ b/app/dimensions/configloader.ts @@ -1,12 +1,12 @@ -import RoutingServer from './routingserver.js'; -import * as Language from './language.js'; -import * as assert from 'assert'; -import * as path from 'path'; -import * as fs from 'fs'; -import * as yaml from 'yaml'; -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; -import { createRequire } from 'module'; +import RoutingServer from "./routingserver.js"; +import * as Language from "./language.js"; +import * as assert from "assert"; +import * as path from "path"; +import * as fs from "fs"; +import * as yaml from "yaml"; +import { fileURLToPath } from "url"; +import { dirname } from "path"; +import { createRequire } from "module"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -65,11 +65,13 @@ export type EnabledBlackList = { port: number; apiKey: string; errorPolicy: "AllowJoining" | "DenyJoining"; -} +}; -export type BlackList = EnabledBlackList | { - enabled: false; -} +export type BlackList = + | EnabledBlackList + | { + enabled: false; + }; export interface RestApiResponse { name?: string; @@ -108,30 +110,37 @@ export interface NameChanges { exclusions: string[]; } -export type UnvalidatedDebuffOnSwitch = { - enabled: false; -} | { - enabled: true; - buffTypes?: number[]; - debuffTimeInSeconds?: number; -} - -export type DebuffOnSwitch = { - enabled: false; -} | { - enabled: true; - buffTypes: number[]; - debuffTimeInSeconds: number; -} - -export type DisconnectOnKick = { - type: "always"; -} | { - type: "never"; -} | { - type: "onKickReasonPrefix"; - kickReasonPrefixes: string[]; -} +export type UnvalidatedDebuffOnSwitch = + | { + enabled: false; + } + | { + enabled: true; + buffTypes?: number[]; + debuffTimeInSeconds?: number; + }; + +export type DebuffOnSwitch = + | { + enabled: false; + } + | { + enabled: true; + buffTypes: number[]; + debuffTimeInSeconds: number; + }; + +export type DisconnectOnKick = + | { + type: "always"; + } + | { + type: "never"; + } + | { + type: "onKickReasonPrefix"; + kickReasonPrefixes: string[]; + }; export interface UnvalidatedConfigOptions { socketTimeout: number; @@ -157,14 +166,14 @@ export interface ConfigOptions { socketNoDelay: boolean; fakeVersion: FakeVersion; restApi: RestApi; - blockInvis: boolean | { enabled: boolean, servers: string[] }; + blockInvis: boolean | { enabled: boolean; servers: string[] }; blacklist: BlackList; log: LogOptions; connectionLimit: ConnectionLimit; connectionRateLimit: ConnectionRateLimit; redis: RedisConfig; nameChanges?: NameChanges; - language: Language.LanguageDefinition, + language: Language.LanguageDefinition; debuffOnSwitch: DebuffOnSwitch; disconnectOnKick: DisconnectOnKick; hotReload: boolean; @@ -187,17 +196,21 @@ export interface Config { // that uses a yaml file. This means we can use fs to read that file avoiding the module cache and // soft link issue, as well as properly support hot reloading when dimensions is deployed in k8s. // Legacy config.js remains supported for compatibility. -export const oldConfigFilePath = path.resolve(__dirname, '../../config.js'); -export const configurationDirectory = path.resolve(__dirname, '../../configuration'); +export const oldConfigFilePath = path.resolve(__dirname, "../../config.cjs"); +export const configurationDirectory = path.resolve( + __dirname, + "../../configuration", +); -export let usingOldConfig = false +export let usingOldConfig = false; function loadLegacyConfigSync(fresh: boolean): UnvalidatedConfig { const resolvedPath = legacyRequire.resolve(oldConfigFilePath); if (fresh && legacyRequire.cache[resolvedPath]) { delete legacyRequire.cache[resolvedPath]; } const legacyModule = legacyRequire(resolvedPath); - const config = legacyModule?.ConfigSettings ?? legacyModule?.default ?? legacyModule; + const config = + legacyModule?.ConfigSettings ?? legacyModule?.default ?? legacyModule; if (!config || typeof config !== "object") { throw new Error("Legacy config.js did not export ConfigSettings"); } @@ -206,19 +219,30 @@ function loadLegacyConfigSync(fresh: boolean): UnvalidatedConfig { function loadConfigSync(): UnvalidatedConfig { // For initial load, we prefer YAML config if it exists (ESM-compatible) - if (fs.existsSync(path.resolve(configurationDirectory, 'config.yaml'))) { + if (fs.existsSync(path.resolve(configurationDirectory, "config.yaml"))) { usingOldConfig = false; - return yaml.parse(fs.readFileSync(path.resolve(configurationDirectory, 'config.yaml'), 'utf8')); + return yaml.parse( + fs.readFileSync( + path.resolve(configurationDirectory, "config.yaml"), + "utf8", + ), + ); } else if (fs.existsSync(oldConfigFilePath)) { usingOldConfig = true; return loadLegacyConfigSync(false); } else { - throw new Error("No config file found. Please create configuration/config.yaml or if using a legacy config make sure config.js is present"); + throw new Error( + "No config file found. Please create configuration/config.yaml or if using a legacy config make sure config.js is present", + ); } } function validateConfig(unvalidatedConfig: UnvalidatedConfig): Config { - const debuffOnSwitch = { enabled: true, buffTypes: [ /* Webbed */ 149, /* Stoned */ 156], debuffTimeInSeconds: 5 } + const debuffOnSwitch = { + enabled: true, + buffTypes: [/* Webbed */ 149, /* Stoned */ 156], + debuffTimeInSeconds: 5, + }; const disconnectOnKick: DisconnectOnKick = { type: "never" }; const blacklist: BlackList = { enabled: false }; let validatedConfig: Config = { @@ -235,31 +259,55 @@ function validateConfig(unvalidatedConfig: UnvalidatedConfig): Config { try { if (typeof unvalidatedConfig.options.nameChanges !== "undefined") { - assert.ok(unvalidatedConfig.options.nameChanges.mode === "legacy" || unvalidatedConfig.options.nameChanges.mode === "rewrite", "nameChanges.mode must be either 'legacy' or 'rewrite'"); - assert.ok(Array.isArray(unvalidatedConfig.options.nameChanges.exclusions), "nameChanges.exclusions must be an array"); + assert.ok( + unvalidatedConfig.options.nameChanges.mode === "legacy" || + unvalidatedConfig.options.nameChanges.mode === "rewrite", + "nameChanges.mode must be either 'legacy' or 'rewrite'", + ); + assert.ok( + Array.isArray(unvalidatedConfig.options.nameChanges.exclusions), + "nameChanges.exclusions must be an array", + ); } if (typeof unvalidatedConfig.options.debuffOnSwitch !== "undefined") { if (!unvalidatedConfig.options.debuffOnSwitch.enabled) { debuffOnSwitch.enabled = false; } else { - if (typeof unvalidatedConfig.options.debuffOnSwitch.buffTypes !== "undefined") { - debuffOnSwitch.buffTypes = unvalidatedConfig.options.debuffOnSwitch.buffTypes; + if ( + typeof unvalidatedConfig.options.debuffOnSwitch.buffTypes !== + "undefined" + ) { + debuffOnSwitch.buffTypes = + unvalidatedConfig.options.debuffOnSwitch.buffTypes; } - if (typeof unvalidatedConfig.options.debuffOnSwitch.debuffTimeInSeconds !== "undefined") - debuffOnSwitch.debuffTimeInSeconds = unvalidatedConfig.options.debuffOnSwitch.debuffTimeInSeconds; + if ( + typeof unvalidatedConfig.options.debuffOnSwitch + .debuffTimeInSeconds !== "undefined" + ) + debuffOnSwitch.debuffTimeInSeconds = + unvalidatedConfig.options.debuffOnSwitch.debuffTimeInSeconds; } } if (typeof unvalidatedConfig.options.disconnectOnKick !== "undefined") { - if (unvalidatedConfig.options.disconnectOnKick.type === "onKickReasonPrefix") { - assert.ok(Array.isArray(unvalidatedConfig.options.disconnectOnKick.kickReasonPrefixes), "disconnectOnKick.kickReasonPrefixes must be an array"); + if ( + unvalidatedConfig.options.disconnectOnKick.type === "onKickReasonPrefix" + ) { + assert.ok( + Array.isArray( + unvalidatedConfig.options.disconnectOnKick.kickReasonPrefixes, + ), + "disconnectOnKick.kickReasonPrefixes must be an array", + ); validatedConfig.options.disconnectOnKick = { type: "onKickReasonPrefix", - kickReasonPrefixes: unvalidatedConfig.options.disconnectOnKick.kickReasonPrefixes, - } + kickReasonPrefixes: + unvalidatedConfig.options.disconnectOnKick.kickReasonPrefixes, + }; } else { - unvalidatedConfig.options.disconnectOnKick.type = unvalidatedConfig.options.disconnectOnKick.type; + unvalidatedConfig.options.disconnectOnKick.type = + unvalidatedConfig.options.disconnectOnKick.type; } } @@ -275,7 +323,10 @@ function validateConfig(unvalidatedConfig: UnvalidatedConfig): Config { validatedConfig.options.language = Language.chinese; break; default: - console.log("Unrecognised language:", unvalidatedConfig.options.language); + console.log( + "Unrecognised language:", + unvalidatedConfig.options.language, + ); process.exit(1); } } @@ -287,37 +338,68 @@ function validateConfig(unvalidatedConfig: UnvalidatedConfig): Config { }; } - validatedConfig.options.blacklist.enabled = unvalidatedConfig.options.blacklist.enabled ?? false; + validatedConfig.options.blacklist.enabled = + unvalidatedConfig.options.blacklist.enabled ?? false; if (validatedConfig.options.blacklist.enabled) { - assert.ok(typeof unvalidatedConfig.options.blacklist.hostname !== "undefined", "Blacklist enabled but no hostname provided"); - assert.ok(typeof unvalidatedConfig.options.blacklist.path !== "undefined", "Blacklist enabled but no path provided"); - assert.ok(typeof unvalidatedConfig.options.blacklist.port !== "undefined", "Blacklist enabled but no port provided"); - assert.ok(typeof unvalidatedConfig.options.blacklist.apiKey !== "undefined", "Blacklist enabled but no api key provided"); - assert.ok(unvalidatedConfig.options.blacklist.errorPolicy === "AllowJoining" || unvalidatedConfig.options.blacklist.errorPolicy === "DenyJoining", "Blacklist errorPolicy must be either 'AllowJoining' or 'DenyJoining'"); - - validatedConfig.options.blacklist.hostname = unvalidatedConfig.options.blacklist.hostname!; - validatedConfig.options.blacklist.path = unvalidatedConfig.options.blacklist.path!; - validatedConfig.options.blacklist.port = unvalidatedConfig.options.blacklist.port!; - validatedConfig.options.blacklist.apiKey = unvalidatedConfig.options.blacklist.apiKey!; - validatedConfig.options.blacklist.errorPolicy = unvalidatedConfig.options.blacklist.errorPolicy!; + assert.ok( + typeof unvalidatedConfig.options.blacklist.hostname !== "undefined", + "Blacklist enabled but no hostname provided", + ); + assert.ok( + typeof unvalidatedConfig.options.blacklist.path !== "undefined", + "Blacklist enabled but no path provided", + ); + assert.ok( + typeof unvalidatedConfig.options.blacklist.port !== "undefined", + "Blacklist enabled but no port provided", + ); + assert.ok( + typeof unvalidatedConfig.options.blacklist.apiKey !== "undefined", + "Blacklist enabled but no api key provided", + ); + assert.ok( + unvalidatedConfig.options.blacklist.errorPolicy === "AllowJoining" || + unvalidatedConfig.options.blacklist.errorPolicy === "DenyJoining", + "Blacklist errorPolicy must be either 'AllowJoining' or 'DenyJoining'", + ); + + validatedConfig.options.blacklist.hostname = + unvalidatedConfig.options.blacklist.hostname!; + validatedConfig.options.blacklist.path = + unvalidatedConfig.options.blacklist.path!; + validatedConfig.options.blacklist.port = + unvalidatedConfig.options.blacklist.port!; + validatedConfig.options.blacklist.apiKey = + unvalidatedConfig.options.blacklist.apiKey!; + validatedConfig.options.blacklist.errorPolicy = + unvalidatedConfig.options.blacklist.errorPolicy!; } } catch (e) { console.log("Error validating config:"); - throw e + throw e; } return validatedConfig; } export function reloadConfig(): Config { - if (fs.existsSync(path.resolve(configurationDirectory, 'config.yaml'))) { + if (fs.existsSync(path.resolve(configurationDirectory, "config.yaml"))) { usingOldConfig = false; - return validateConfig(yaml.parse(fs.readFileSync(path.resolve(configurationDirectory, 'config.yaml'), 'utf8'))); + return validateConfig( + yaml.parse( + fs.readFileSync( + path.resolve(configurationDirectory, "config.yaml"), + "utf8", + ), + ), + ); } else if (fs.existsSync(oldConfigFilePath)) { usingOldConfig = true; return validateConfig(loadLegacyConfigSync(true)); } else { - throw new Error("No config file found. Please use configuration/config.yaml or config.js"); + throw new Error( + "No config file found. Please use configuration/config.yaml or config.js", + ); } } diff --git a/app/dimensions/language.ts b/app/dimensions/language.ts index 1f26721..230e84e 100644 --- a/app/dimensions/language.ts +++ b/app/dimensions/language.ts @@ -1,50 +1,50 @@ export interface LanguageDefinition { - englishName: string, - name: string, - isoCode: string, - phrases: LanguagePhrases, + englishName: string; + name: string; + isoCode: string; + phrases: LanguagePhrases; } export interface LanguagePhrasesOverrides { - nameAlreadyOnServer?: string, - characterNameLengthOutOfRange?: string, - areYouEvenConnected?: string, - youAreAlreadyInthatDimension?: string, - shiftingToDimension?: string, - youNeedToWaitUntilConnected?: string, - playerCount?: string, - availableDimensions?: string, - youEnteredTheVoid?: string, - dimensionDisconnectedYou?: string, - reason?: string, - dimensionsCommandName?: string, - specifyADimensionToTravel?: string, - dimensionDropped?: string, - blacklisted?: string, - blacklistCheckError?: string, - invalidPacketLength?: string, - close?: string, + nameAlreadyOnServer?: string; + characterNameLengthOutOfRange?: string; + areYouEvenConnected?: string; + youAreAlreadyInthatDimension?: string; + shiftingToDimension?: string; + youNeedToWaitUntilConnected?: string; + playerCount?: string; + availableDimensions?: string; + youEnteredTheVoid?: string; + dimensionDisconnectedYou?: string; + reason?: string; + dimensionsCommandName?: string; + specifyADimensionToTravel?: string; + dimensionDropped?: string; + blacklisted?: string; + blacklistCheckError?: string; + invalidPacketLength?: string; + close?: string; } interface LanguagePhrases { - nameAlreadyOnServer: string, - characterNameLengthOutOfRange: string, - areYouEvenConnected: string, - youAreAlreadyInthatDimension: string, - shiftingToDimension: string, - youNeedToWaitUntilConnected: string, - playerCount: string, - availableDimensions: string, - youEnteredTheVoid: string, - dimensionDisconnectedYou: string, - reason: string, - dimensionsCommandName: string, - specifyADimensionToTravel: string, - dimensionDropped: string, - blacklisted: string, - blacklistCheckError: string, - invalidPacketLength: string, - close: string, + nameAlreadyOnServer: string; + characterNameLengthOutOfRange: string; + areYouEvenConnected: string; + youAreAlreadyInthatDimension: string; + shiftingToDimension: string; + youNeedToWaitUntilConnected: string; + playerCount: string; + availableDimensions: string; + youEnteredTheVoid: string; + dimensionDisconnectedYou: string; + reason: string; + dimensionsCommandName: string; + specifyADimensionToTravel: string; + dimensionDropped: string; + blacklisted: string; + blacklistCheckError: string; + invalidPacketLength: string; + close: string; } export const english: LanguageDefinition = { @@ -53,11 +53,13 @@ export const english: LanguageDefinition = { isoCode: "en", phrases: { nameAlreadyOnServer: "Someone called ${name} is already on the server.", - characterNameLengthOutOfRange: "A character name must be between (inclusive) 2 to 20 characters long.", + characterNameLengthOutOfRange: + "A character name must be between (inclusive) 2 to 20 characters long.", areYouEvenConnected: "Are you even connected?", youAreAlreadyInthatDimension: "You are already in that Dimension.", shiftingToDimension: "Shifting to the ${name} Dimension", - youNeedToWaitUntilConnected: "You need to wait until you have fully connected to your current Dimension.", + youNeedToWaitUntilConnected: + "You need to wait until you have fully connected to your current Dimension.", playerCount: "There are ${total} players across all Dimensions.", availableDimensions: "Available Dimensions: ", youEnteredTheVoid: "You have entered the Void. You will soon disappear.", @@ -80,25 +82,33 @@ export const chinese: LanguageDefinition = { name: "汉语", isoCode: "zn", phrases: { - nameAlreadyOnServer: "${name} 已经进入了服务器", + nameAlreadyOnServer: "${name} 已经在服务器中", characterNameLengthOutOfRange: "昵称长度必须在2至20个字符之间", - areYouEvenConnected: "请确保你已经连接", - youAreAlreadyInthatDimension: "你已经进入了那个世界", - shiftingToDimension: "正在传送到 ${name}", - youNeedToWaitUntilConnected: "请等待传送", - playerCount: "当前共有 ${total} 名玩家在线", - availableDimensions: "可传送的世界: ", - youEnteredTheVoid: "你已断线,即将被踢出世界", - dimensionDisconnectedYou: "你已与当前世界断开连接", - reason: "原因:: ", - dimensionsCommandName: "世界", - specifyADimensionToTravel: "请选择需要传送的[c/FF00CC:世界]:", - dimensionDropped: "你所在的世界已与您断开连接", + areYouEvenConnected: + "[i:3459]CSFT[i:3459] [c/ff8080:请][c/ffda80:确][c/c8ff80:保][c/6dff80:你][c/12ff80:已][c/49ffc8:经][c/a4daed:连][c/fe80c0:接]", + youAreAlreadyInthatDimension: + "[i:3459]CSFT[i:3459] [c/ff8080:你][c/ffda80:已][c/c8ff80:经][c/6dff80:进][c/12ff80:入][c/49ffc8:了][c/a4daed:此][c/fe80c0:服]", + shiftingToDimension: + "[i:3459]CSFT[i:3459] [c/ff8080:正][c/ffff80:在][c/80ff80:传][c/00ff80:送][c/80ffff:到] ${name}", + youNeedToWaitUntilConnected: + "[i:3459]CSFT[i:3459] [c/ff8080:请][c/ffda80:等][c/c8ff80:待][c/6dff80:传][c/12ff80:送][c/49ffc8:.][c/a4daed:.][c/fe80c0:.]", + playerCount: "[i:3459]CSFT[i:3459] 当前维度有 ${total} 玩家在线", + availableDimensions: + "[i:3459]CSFT[i:3459] [c/ff8080:可][c/ffcf80:传][c/dfff80:送][c/8fff80:的][c/40ff80:服][c/0fff8f:务][c/5fffdf:器][c/afcfe7::] ", + youEnteredTheVoid: + "[i:3459]CSFT[i:3459] [c/ff8080:你][c/ffaa80:已][c/ffd480:掉][c/ffff80:线][c/d4ff80:,][c/aaff80:即][c/80ff80:将][c/55ff80:被][c/2aff80:踢][c/00ff80:出][c/2affaa:服][c/55ffd4:务][c/80ffff:器][c/aad4ea:.][c/d4aad5:.][c/fe80c0:.]", + dimensionDisconnectedYou: + "[i:3459]CSFT[i:3459] [c/ff8080:你][c/ffb980:已][c/fff380:与][c/d0ff80:当][c/97ff80:前][c/5dff80:服][c/22ff80:务][c/17ff97:器][c/51ffd0:断][c/8bf3f9:开][c/c5b9dc:连][c/fe80c0:接]", + reason: "原因:", + dimensionsCommandName: "服务器", + specifyADimensionToTravel: + "[i:3459]CSFT[i:3459] [c/ff8080:请][c/ffda80:选][c/c8ff80:择][c/6dff80:需][c/12ff80:要][c/49ffc8:传][c/a4daed:送][c/fe80c0:的][c/FF00CC:分区]:", + dimensionDropped: + "[i:3459]CSFT[i:3459] [c/ff8080:服][c/ffc680:务][c/f0ff80:器][c/aaff80:暂][c/63ff80:时][c/1cff80:关][c/2affaa:闭][c/71fff0:.][c/b8c6e3:.][c/fe80c0:.]", blacklisted: "您已被服务器列入黑名单。", blacklistCheckError: "检查您是否被列入黑名单时出现错误。", - // TODO: This is google translated, should be reviewed by native speaker - invalidPacketLength: "客户端违反协议:数据包长度无效。", - // TODO: This is google translated, should be reviewed by native speaker - close: "服务器正在重新启动。 请重新加入。", + invalidPacketLength: + "[c/ff8080:客][c/ffaa80:户][c/ffd480:端][c/ffff80:违][c/d4ff80:反][c/aaff80:协][c/80ff80:议][c/55ff80::][c/2aff80:数][c/00ff80:据][c/2affaa:包][c/55ffd4:长][c/80ffff:度][c/aad4ea:无][c/d4aad5:效][c/fe80c0:。]", + close: "服务器正在重新启动,请重新加入。", }, }; diff --git a/app/dimensions/listenserver.ts b/app/dimensions/listenserver.ts index 94faeea..ca50f06 100644 --- a/app/dimensions/listenserver.ts +++ b/app/dimensions/listenserver.ts @@ -1,23 +1,26 @@ -import * as Net from 'net'; -import { v4 as uuidv4 } from 'uuid'; -import RawPacket from './packets/rawpacket.js'; -import { getProperIP } from './utils.js'; -import Client from './client.js'; -import ClientArgs from './clientargs.js'; -import ServerDetails from './serverdetails.js'; -import GlobalHandlers from './globalhandlers.js'; -import { ConfigListenServer, ConfigOptions } from './configloader.js'; -import RoutingServer from './routingserver.js'; -import Blacklist from './blacklist.js'; -import GlobalTracking from './globaltracking.js'; -import ListenServerArgs from './listenserverargs.js'; -import NetworkText from '@popstarfreas/packetfactory/networktext'; -import StringUtils from './stringutils.js'; -import ErrorHelper from './errorhelper.js'; -import BlacklistCheckClient from './blacklistcheckclient.js'; -import * as winston from 'winston'; -import { RawSocketWriteContext, RawSocketWriteReason } from './extension/index.js'; -import { DisconnectPacket, StatusPacket } from 'terraria-packet'; +import * as Net from "net"; +import { v4 as uuidv4 } from "uuid"; +import RawPacket from "./packets/rawpacket.js"; +import { getProperIP } from "./utils.js"; +import Client from "./client.js"; +import ClientArgs from "./clientargs.js"; +import ServerDetails from "./serverdetails.js"; +import GlobalHandlers from "./globalhandlers.js"; +import { ConfigListenServer, ConfigOptions } from "./configloader.js"; +import RoutingServer from "./routingserver.js"; +import Blacklist from "./blacklist.js"; +import GlobalTracking from "./globaltracking.js"; +import ListenServerArgs from "./listenserverargs.js"; +import NetworkText from "@popstarfreas/packetfactory/networktext"; +import StringUtils from "./stringutils.js"; +import ErrorHelper from "./errorhelper.js"; +import BlacklistCheckClient from "./blacklistcheckclient.js"; +import * as winston from "winston"; +import { + RawSocketWriteContext, + RawSocketWriteReason, +} from "./extension/index.js"; +import { DisconnectPacket, StatusPacket } from "terraria-packet"; /** * Listens on a specified port and routes users balancing amounts between routing servers it handles @@ -63,26 +66,24 @@ export class ListenServer { clientCount: 0, disabled: false, disabledTimeout: null, - failedConnAttempts: 0 + failedConnAttempts: 0, }; } - this.ServerHandleError = this.handleError.bind(this); this.ServerHandleStart = this.handleStart.bind(this); // Listen Server this.server = Net.createServer(); - this.server.on('connection', (socket) => { - this.handleSocket(socket) - .catch((e) => { - if (this.options.log.clientError) { - this.logging.error(`Socket Error: ${ErrorHelper.toMessage(e)}`); - } - }); + this.server.on("connection", (socket) => { + this.handleSocket(socket).catch((e) => { + if (this.options.log.clientError) { + this.logging.error(`Socket Error: ${ErrorHelper.toMessage(e)}`); + } + }); }); this.server.listen(this.port, this.ServerHandleStart); - this.server.on('error', this.ServerHandleError); + this.server.on("error", this.ServerHandleError); if (this.options.connectionRateLimit.enabled) { this.startConnectionRateLimitTimer(); @@ -110,7 +111,12 @@ export class ListenServer { // Even if the server has been disabled, if we have no current choice, we must use it if (!details.disabled || currentClientCount === null) { // Favour either lower player count or non-disability - if (currentClientCount === null || chosenServer === null || details.clientCount < currentClientCount || this.serversDetails[chosenServer.name].disabled) { + if ( + currentClientCount === null || + chosenServer === null || + details.clientCount < currentClientCount || + this.serversDetails[chosenServer.name].disabled + ) { chosenServer = this.routingServers[i]; currentClientCount = details.clientCount; } @@ -141,7 +147,7 @@ export class ListenServer { let details: ServerDetails; for (let i = 0; i < this.routingServers.length; i++) { if (this.serversDetails[this.routingServers[i].name]) { - details = this.serversDetails[this.routingServers[i].name] + details = this.serversDetails[this.routingServers[i].name]; details.disabled = false; details.failedConnAttempts = 0; } else { @@ -149,7 +155,7 @@ export class ListenServer { clientCount: 0, disabled: false, disabledTimeout: null, - failedConnAttempts: 0 + failedConnAttempts: 0, }; } } @@ -161,13 +167,22 @@ export class ListenServer { public shutdown(): void { this.logging.info(`Server on ${this.port} is now shutting down.`); for (let i: number = 0; i < this.clients.length; i++) { - this.clients[i].server.socket.removeListener('data', this.clients[i].ServerHandleData); - this.clients[i].server.socket.removeListener('error', this.clients[i].ServerHandleError); - this.clients[i].server.socket.removeListener('close', this.clients[i].ServerHandleClose); + this.clients[i].server.socket.removeListener( + "data", + this.clients[i].ServerHandleData, + ); + this.clients[i].server.socket.removeListener( + "error", + this.clients[i].ServerHandleError, + ); + this.clients[i].server.socket.removeListener( + "close", + this.clients[i].ServerHandleClose, + ); this.clients[i].disconnect(this.options.language.phrases.close); } this.clients = []; - this.server.removeListener('error', this.ServerHandleError); + this.server.removeListener("error", this.ServerHandleError); this.server.close(); // Reset counts @@ -196,14 +211,14 @@ export class ListenServer { private writeToSocketWithHooks( socket: Net.Socket, packet: Buffer, - reason: RawSocketWriteContext['reason'], - clientArgs?: ClientArgs + reason: RawSocketWriteContext["reason"], + clientArgs?: ClientArgs, ): boolean { const context: RawSocketWriteContext = { socket: socket, remoteAddress: socket.remoteAddress, reason: reason, - clientArgs: clientArgs + clientArgs: clientArgs, }; const packetWrapper = { packet: packet }; @@ -211,7 +226,10 @@ export class ListenServer { for (const extension of Object.values(this.globalHandlers.extensions)) { if (extension.rawSocketWritePreHandler) { try { - const blocked = extension.rawSocketWritePreHandler(context, packetWrapper); + const blocked = extension.rawSocketWritePreHandler( + context, + packetWrapper, + ); if (blocked) { return false; } @@ -255,11 +273,11 @@ export class ListenServer { private disconnectClient( socket: Net.Socket, reason: string, - hookReason: RawSocketWriteContext['reason'] = RawSocketWriteReason.Other + hookReason: RawSocketWriteContext["reason"] = RawSocketWriteReason.Other, ): void { let kickPacket = DisconnectPacket.toBuffer({ - reason: new NetworkText(0, reason) - }) + reason: new NetworkText(0, reason), + }); if (!socket.destroyed) { switch (kickPacket.TAG) { @@ -267,7 +285,9 @@ export class ListenServer { this.writeToSocketWithHooks(socket, kickPacket._0, hookReason); break; case "Error": - this.logging.error(`Error creating disconnect packet: ${kickPacket._0}`); + this.logging.error( + `Error creating disconnect packet: ${kickPacket._0}`, + ); break; } @@ -303,8 +323,12 @@ export class ListenServer { * @param socket The socket of a new client */ private async handleSocket(socket: Net.Socket): Promise { - if ((this.options.connectionLimit.enabled && this.enforceConnectionLimit(socket)) - || this.options.connectionRateLimit.enabled && this.enforceConnectionRateLimit(socket)) { + if ( + (this.options.connectionLimit.enabled && + this.enforceConnectionLimit(socket)) || + (this.options.connectionRateLimit.enabled && + this.enforceConnectionRateLimit(socket)) + ) { socket.removeAllListeners(); return; } @@ -326,8 +350,7 @@ export class ListenServer { } } } - } - catch (error) { + } catch (error) { console.log(error); } @@ -360,8 +383,11 @@ export class ListenServer { if (counter + 1 > this.options.connectionLimit.connectionLimitPerIP) { this.disconnectClient( socket, - StringUtils.format(this.options.connectionLimit.kickReason, this.options.connectionLimit.connectionLimitPerIP), - RawSocketWriteReason.ConnectionLimitExceeded + StringUtils.format( + this.options.connectionLimit.kickReason, + this.options.connectionLimit.connectionLimitPerIP, + ), + RawSocketWriteReason.ConnectionLimitExceeded, ); connectionDropped = true; } else { @@ -382,7 +408,10 @@ export class ListenServer { } const count = this.connectRateTracker.get(ip); if (typeof count !== "undefined") { - if (count + 1 > this.options.connectionRateLimit.connectionRateLimitPerIP) { + if ( + count + 1 > + this.options.connectionRateLimit.connectionRateLimitPerIP + ) { socket.destroy(); connectionDropped = true; } else { @@ -402,7 +431,9 @@ export class ListenServer { private async setupNewSocket(socket: Net.Socket): Promise { let chosenServer: RoutingServer | null = this.chooseServer(); if (chosenServer === null) { - this.logging.warn(`No servers available for ListenServer[Port: ${this.port}]`); + this.logging.warn( + `No servers available for ListenServer[Port: ${this.port}]`, + ); socket.destroy(); const ip = socket.remoteAddress; if (typeof ip !== "undefined") { @@ -425,7 +456,7 @@ export class ListenServer { servers: this.servers, options: this.options, globalTracking: this.globalTracking, - logging: this.logging + logging: this.logging, }; // When the blacklist is enabled, clients must first send their initial data @@ -439,7 +470,10 @@ export class ListenServer { }); client.setupCallbacks({ - clientAcceptedCb: (bufferPacket: Buffer, packetsReceived: RawPacket[]) => { + clientAcceptedCb: ( + bufferPacket: Buffer, + packetsReceived: RawPacket[], + ) => { const index = this.checkingClients.indexOf(client); if (index > -1) { this.checkingClients.splice(index, 1); @@ -453,14 +487,24 @@ export class ListenServer { } this.kickBlacklisted(clientArgs); }, - errorCheckingBlacklistCb: (bufferPacket: Buffer, packetsReceived: RawPacket[], e: Error) => { - this.logging.error(`Error checking blacklist: ${ErrorHelper.toMessage(e)}`); + errorCheckingBlacklistCb: ( + bufferPacket: Buffer, + packetsReceived: RawPacket[], + e: Error, + ) => { + this.logging.error( + `Error checking blacklist: ${ErrorHelper.toMessage(e)}`, + ); if (configuration.errorPolicy === "DenyJoining") { const index = this.checkingClients.indexOf(client); if (index > -1) { this.checkingClients.splice(index, 1); } - this.disconnectClient(socket, this.options.language.phrases.blacklistCheckError, RawSocketWriteReason.BlacklistCheck); + this.disconnectClient( + socket, + this.options.language.phrases.blacklistCheckError, + RawSocketWriteReason.BlacklistCheck, + ); } else { const index = this.checkingClients.indexOf(client); if (index > -1) { @@ -470,12 +514,18 @@ export class ListenServer { } }, packetErrorCheckingBlacklistCb: (e: Error) => { - this.logging.error(`Packet error checking blacklist: ${ErrorHelper.toMessage(e)}`); + this.logging.error( + `Packet error checking blacklist: ${ErrorHelper.toMessage(e)}`, + ); const index = this.checkingClients.indexOf(client); if (index > -1) { this.checkingClients.splice(index, 1); } - this.disconnectClient(socket, this.options.language.phrases.blacklistCheckError, RawSocketWriteReason.BlacklistCheck); + this.disconnectClient( + socket, + this.options.language.phrases.blacklistCheckError, + RawSocketWriteReason.BlacklistCheck, + ); }, disconnectCb: () => { const index = this.checkingClients.indexOf(client); @@ -486,7 +536,7 @@ export class ListenServer { if (typeof ip !== "undefined") { this.decrementConnectionTracker(ip); } - } + }, }); this.checkingClients.push(client); } else { @@ -494,12 +544,18 @@ export class ListenServer { } } - private setupNewClient(clientArgs: ClientArgs, bufferPacket: Buffer | undefined, packetsAlreadyReceived: RawPacket[]): Client { + private setupNewClient( + clientArgs: ClientArgs, + bufferPacket: Buffer | undefined, + packetsAlreadyReceived: RawPacket[], + ): Client { let client = new Client(clientArgs); this.clients.push(client); if (this.options.log.clientConnect) { - this.logging.info(`[Client: ${getProperIP(client.socket.remoteAddress)} connected [${clientArgs.server.name}: ${this.serversDetails[clientArgs.server.name].clientCount + 1}]`); + this.logging.info( + `[Client: ${getProperIP(client.socket.remoteAddress)} connected [${clientArgs.server.name}: ${this.serversDetails[clientArgs.server.name].clientCount + 1}]`, + ); } this.hookSocketError(client.socket, client); @@ -507,7 +563,9 @@ export class ListenServer { this.hookSocketClose(client.socket, client); this.hookSocketData(client.socket, client); - client.handleDataSend(Buffer.concat(packetsAlreadyReceived.map((packet) => packet.data))); + client.handleDataSend( + Buffer.concat(packetsAlreadyReceived.map((packet) => packet.data)), + ); if (bufferPacket !== undefined) { client.handleDataSend(bufferPacket); } @@ -522,10 +580,16 @@ export class ListenServer { * @return Whether or not the ip is blacklisted */ private kickBlacklisted(client: ClientArgs): void { - this.disconnectClient(client.socket, this.options.language.phrases.blacklisted, RawSocketWriteReason.BlacklistCheck); + this.disconnectClient( + client.socket, + this.options.language.phrases.blacklisted, + RawSocketWriteReason.BlacklistCheck, + ); if (this.options.log.clientBlocked) { - this.logging.info(`${process.pid}] Client: ${getProperIP(client.socket.remoteAddress)} was blocked from joining.`); + this.logging.info( + `${process.pid}] Client: ${getProperIP(client.socket.remoteAddress)} was blocked from joining.`, + ); } } @@ -542,13 +606,18 @@ export class ListenServer { flags: { hideStatusTextPercent: true, statusTextHasShadows: true, - runCheckBytes: false - } - }) + runCheckBytes: false, + }, + }); switch (statusPacket.TAG) { case "Ok": - this.writeToSocketWithHooks(client.socket, statusPacket._0, RawSocketWriteReason.BlacklistCheck, client); + this.writeToSocketWithHooks( + client.socket, + statusPacket._0, + RawSocketWriteReason.BlacklistCheck, + client, + ); break; case "Error": this.logging.error(`Error creating status packet: ${statusPacket._0}`); @@ -563,12 +632,12 @@ export class ListenServer { * @param client The client object associated with the socket */ private hookSocketError(socket: Net.Socket, client: Client): void { - socket.once('error', (e: Error) => { + socket.once("error", (e: Error) => { try { client.handleError(e); } catch (e) { if (this.options.log.clientError) { - this.logging.error(`handleError Error: ${ErrorHelper.toMessage(e)}`) + this.logging.error(`handleError Error: ${ErrorHelper.toMessage(e)}`); } } }); @@ -581,7 +650,7 @@ export class ListenServer { * @param client The client object associated with the socket */ private hookSocketTimeout(socket: Net.Socket, client: Client): void { - socket.once('timeout', () => { + socket.once("timeout", () => { if (this.options.log.clientTimeouts) { this.logging.warn(`Socket Timeout: ${client.getName()} ${client.ID}`); } @@ -621,7 +690,10 @@ export class ListenServer { } } - private extensionSocketClosePreHandlers(socket: Net.Socket, client: Client): boolean { + private extensionSocketClosePreHandlers( + socket: Net.Socket, + client: Client, + ): boolean { for (const extension of Object.values(this.globalHandlers.extensions)) { if (extension.socketClosePreHandler) { try { @@ -665,7 +737,7 @@ export class ListenServer { * @param client The client object associated with the socket */ private hookSocketClose(socket: Net.Socket, client: Client): void { - socket.once('close', () => { + socket.once("close", () => { if (this.extensionSocketClosePreHandlers(socket, client)) { return; } @@ -676,8 +748,10 @@ export class ListenServer { this.decrementConnectionTracker(ip); } if (this.options.log.clientDisconnect) { - const logMessage = `[${process.pid}] Client: ${getProperIP(ip)} disconnected ${client.server.name}: ${this.serversDetails[client.server.name].clientCount - 1}]`; - this.logging.info(logMessage); + //const logMessage = `[${process.pid}] Client: ${getProperIP(ip)} disconnected ${client.server.name}: ${this.serversDetails[client.server.name].clientCount - 1}]`; + //this.logging.info(logMessage); + //CSFT + //TODO } client.handleClose(); for (let i: number = 0; i < this.clients.length; i++) { @@ -688,7 +762,9 @@ export class ListenServer { } } catch (e) { if (this.options.log.clientError) { - this.logging.error(`SocketCloseEvent ERROR: ${ErrorHelper.toMessage(e)}`); + this.logging.error( + `SocketCloseEvent ERROR: ${ErrorHelper.toMessage(e)}`, + ); } } @@ -705,12 +781,14 @@ export class ListenServer { * @param client The client object associated with the socket */ private hookSocketData(socket: Net.Socket, client: Client): void { - socket.on('data', (data: Buffer) => { + socket.on("data", (data: Buffer) => { try { client.handleDataSend(data); } catch (e) { if (this.options.log.clientError) { - this.logging.error(`HandleDataSend ERROR: ${ErrorHelper.toMessage(e)}`); + this.logging.error( + `HandleDataSend ERROR: ${ErrorHelper.toMessage(e)}`, + ); } } }); @@ -724,7 +802,9 @@ export class ListenServer { * @param error The error object containing the error information */ private handleError(error: Error) { - this.logging.error(` Server on ${this.port} encountered an error: ${ErrorHelper.toMessage(error)}.`); + this.logging.error( + ` Server on ${this.port} encountered an error: ${ErrorHelper.toMessage(error)}.`, + ); } } diff --git a/app/dimensions/terrariaserver.ts b/app/dimensions/terrariaserver.ts index c99ccc3..6acb96c 100644 --- a/app/dimensions/terrariaserver.ts +++ b/app/dimensions/terrariaserver.ts @@ -1,16 +1,18 @@ -import { BuffersPackets, getPacketsFromBuffer } from './utils.js'; -import terrariaServerPacketHandler, { PacketSource } from './terrariaserverpackethandler.js'; -import PacketTypes from './packettypes.js'; -import Client from './client.js'; -import * as Net from 'net'; -import Point from './point.js'; -import RawPacket from './packets/rawpacket.js'; -import Entities from './entities.js'; -import ClientState from './clientstate.js'; -import ErrorHelper from './errorhelper.js'; +import { BuffersPackets, getPacketsFromBuffer } from "./utils.js"; +import terrariaServerPacketHandler, { + PacketSource, +} from "./terrariaserverpackethandler.js"; +import PacketTypes from "./packettypes.js"; +import Client from "./client.js"; +import * as Net from "net"; +import Point from "./point.js"; +import RawPacket from "./packets/rawpacket.js"; +import Entities from "./entities.js"; +import ClientState from "./clientstate.js"; +import ErrorHelper from "./errorhelper.js"; interface PacketQueueItem { - rawPacket: RawPacket, + rawPacket: RawPacket; } /* Used to track information specific to the current server that a client is on @@ -46,7 +48,7 @@ class TerrariaServer { this.name = ""; this.spawn = { x: 0, - y: 0 + y: 0, }; this.bufferPacket = Buffer.allocUnsafe(0); this.afterClosed = null; @@ -54,7 +56,7 @@ class TerrariaServer { items: [], NPCs: [], players: [], - pylons: [] + pylons: [], }; this.isSSC = false; this.packetQueue = []; @@ -73,19 +75,21 @@ class TerrariaServer { public sendDirect(buf: Buffer): void { if (this.socket.writable) { this.socket.write(buf); - Object.values(this.client.globalHandlers.extensions).forEach((extension) => { - if (extension.sendPacketToServerEvent) { - try { - extension.sendPacketToServerEvent(this, buf); - } catch (error) { - if (this.client.options.log.extensionError) { - const name = extension.name ?? "unknown"; - const logMessage = `[${process.pid}] Extension ${name} Server Send Packet Event Error: ${ErrorHelper.toMessage(error)}`; - this.client.logging.info(logMessage); + Object.values(this.client.globalHandlers.extensions).forEach( + (extension) => { + if (extension.sendPacketToServerEvent) { + try { + extension.sendPacketToServerEvent(this, buf); + } catch (error) { + if (this.client.options.log.extensionError) { + const name = extension.name ?? "unknown"; + const logMessage = `[${process.pid}] Extension ${name} Server Send Packet Event Error: ${ErrorHelper.toMessage(error)}`; + this.client.logging.info(logMessage); + } } } - } - }); + }, + ); } } @@ -102,10 +106,14 @@ class TerrariaServer { let entireDataInfo: BuffersPackets = getPacketsFromBuffer(entireData); if (entireDataInfo.type === "InvalidPacketLength") { - this.client.logging.error(`Terraria Server Packet Length Error: Received Packet Length ${entireDataInfo.length}`); - this.client.sendChatMessage("Disconnected from dimension due to a packet length error. Please try again."); + this.client.logging.error( + `Terraria Server Packet Length Error: Received Packet Length ${entireDataInfo.length}`, + ); + this.client.sendChatMessage( + "Disconnected from dimension due to a packet length error. Please try again.", + ); this.client.disconnectFromServer(); - return + return; } // Update buffer packet to the new incomplete packet (if any) @@ -118,14 +126,20 @@ class TerrariaServer { let packets: RawPacket[] = entireDataInfo.packets; packets.forEach((packet: RawPacket) => { try { - const buf = this.getPacketHandler().handlePacket(this, packet, PacketSource.TerrariaServer); + const buf = this.getPacketHandler().handlePacket( + this, + packet, + PacketSource.TerrariaServer, + ); //const buf = packet.data; if (buf !== null) { allowedPackets.push(buf); } } catch (e) { if (this.client.options.log.tServerError) { - this.client.logging.error(`TS handle packet error. PacketType: ${PacketTypes[packet.packetType]} (${packet.packetType}): ${ErrorHelper.toMessage(e)}. Data: ${packet.data.toString("hex")}`); + this.client.logging.error( + `TS handle packet error. PacketType: ${PacketTypes[packet.packetType]} (${packet.packetType}): ${ErrorHelper.toMessage(e)}. Data: ${packet.data.toString("hex")}`, + ); } } }); @@ -141,7 +155,9 @@ class TerrariaServer { } } catch (e) { if (this.client.options.log.tServerError) { - this.client.logging.error(`TS Handle Data Error: ${ErrorHelper.toMessage(e)}`); + this.client.logging.error( + `TS Handle Data Error: ${ErrorHelper.toMessage(e)}`, + ); } } } @@ -150,7 +166,11 @@ class TerrariaServer { public sendWaitingPackets(): void { if (!this.socket.destroyed && this.packetQueue.length > 0) { for (const packet of this.packetQueue) { - const packetData = this.getPacketHandler().handlePacket(this, packet.rawPacket, PacketSource.Dimensions); + const packetData = this.getPacketHandler().handlePacket( + this, + packet.rawPacket, + PacketSource.Dimensions, + ); if (packetData !== null) { this.client.sendDirect(packetData); } @@ -167,7 +187,7 @@ class TerrariaServer { let handled = false; for (let key in handlers) { let handler = handlers[key]; - if (typeof handler.serverDisconnectPreHandler !== 'undefined') { + if (typeof handler.serverDisconnectPreHandler !== "undefined") { try { handled = handler.serverDisconnectPreHandler(this); if (handled) { @@ -193,7 +213,7 @@ class TerrariaServer { let handled = false; for (let key in handlers) { let handler = handlers[key]; - if (typeof handler.serverDisconnectHandler !== 'undefined') { + if (typeof handler.serverDisconnectHandler !== "undefined") { try { handled = handler.serverDisconnectHandler(this); if (handled) { @@ -213,7 +233,7 @@ class TerrariaServer { } /* Decrements server counts when the socket connection to the TerrariaServer - * is closed, sends a message to the client and runs any handlers of this + * is closed, sends a message to the client and runs any handlers of this * event through extensions currently loaded */ public handleClose(): void { this.client.connected = false; @@ -225,7 +245,9 @@ class TerrariaServer { } } catch (e) { if (this.client.options.log.tServerError) { - this.client.logging.error(`handleClose ERROR: ${ErrorHelper.toMessage(e)}`); + this.client.logging.error( + `handleClose ERROR: ${ErrorHelper.toMessage(e)}`, + ); } } @@ -244,22 +266,17 @@ class TerrariaServer { return; } - let dimensionsList: string = ""; - let dimensionNames: string[] = Object.keys(this.client.servers); - for (var i = 0; i < dimensionNames.length; i++) { - let name: string = dimensionNames[i]; - let hidden: boolean = this.client.servers[name].hidden; - if (!hidden) { - dimensionsList += (i > 0 ? ", " : " ") + "/" + dimensionNames[i]; - } - } - + //CSFT - 标记 if (!this.client.wasKicked) { - this.client.sendChatMessage(this.client.options.language.phrases.dimensionDropped, "00BFFF"); - this.client.sendChatMessage(this.client.options.language.phrases.specifyADimensionToTravel + dimensionsList, "00BFFF"); - } else { - this.client.sendChatMessage(this.client.options.language.phrases.specifyADimensionToTravel + dimensionsList, "00BFFF"); - this.client.wasKicked = false; + this.client.sendChatMessage( + "[CSFT亚共体]你与服务器断开了连接!\n将在5秒后自动踢出地图..", + "FF6A6A", + ); + setTimeout(() => { + this.client.disconnect( + "[CSFT亚共体]服务器已关闭或无法进入\n请稍后重试...", + ); + }, 5000); } this.client.state = ClientState.Disconnected; @@ -268,8 +285,8 @@ class TerrariaServer { /* Checks the type of error, if it is because a server is down, the failed connection attempts * property is incremented until it reaches 3 at which point it is marked as closed and will not - * be used by clients. - * + * be used by clients. + * * TODO: Handle non-refused errors when the host itself is offline */ public handleError(error: Error): void { let matches: RegExpMatchArray | null = / E([A-z]*?) /.exec(error.message); @@ -287,7 +304,9 @@ class TerrariaServer { } if (this.client.options.log.tServerError) { - this.client.logging.error(`TerrariaServer Socket Error: ${ErrorHelper.toMessage(error)}`); + this.client.logging.error( + `TerrariaServer Socket Error: ${ErrorHelper.toMessage(error)}`, + ); } } } diff --git a/app/dimensions/terrariaserverpackethandler.ts b/app/dimensions/terrariaserverpackethandler.ts index 2aec713..3785f51 100644 --- a/app/dimensions/terrariaserverpackethandler.ts +++ b/app/dimensions/terrariaserverpackethandler.ts @@ -1,25 +1,55 @@ -import PacketTypes from './packettypes.js'; -import { getProperIP } from './utils.js'; -import NPC from './npc.js'; -import TerrariaServer from './terrariaserver.js'; -import Client from './client.js'; -import RawPacket from './packets/rawpacket.js'; -import * as Net from 'net'; -import Item from './item.js'; -import Player from './player.js'; -import ClientState from './clientstate.js'; -import ErrorHelper from './errorhelper.js'; - -import { WorldInfoPacket, PlayerInfoPacket, NpcUpdatePacket, ItemDropUpdatePacket, PlayerSpawnPacket, NetModuleLoadPacket, DisconnectPacket, PlayerActivePacket, PlayerInventorySlotPacket, DimensionsUpdatePacket, Parser, } from "terraria-packet"; -import NetworkText from '@popstarfreas/packetfactory/networktext'; -import PacketWriter from '@popstarfreas/packetfactory/packetwriter'; +import PacketTypes from "./packettypes.js"; +import { getProperIP } from "./utils.js"; +import NPC from "./npc.js"; +import TerrariaServer from "./terrariaserver.js"; +import Client from "./client.js"; +import RawPacket from "./packets/rawpacket.js"; +import * as Net from "net"; +import Item from "./item.js"; +import Player from "./player.js"; +import ClientState from "./clientstate.js"; +import ErrorHelper from "./errorhelper.js"; + +import { + WorldInfoPacket, + PlayerInfoPacket, + NpcUpdatePacket, + ItemDropUpdatePacket, + PlayerSpawnPacket, + NetModuleLoadPacket, + DisconnectPacket, + PlayerActivePacket, + PlayerInventorySlotPacket, + Parser, +} from "terraria-packet"; +import NetworkText from "@popstarfreas/packetfactory/networktext"; +import PacketWriter from "@popstarfreas/packetfactory/packetwriter"; +import PacketReader from "@popstarfreas/packetfactory/packetreader"; + +/*let arr = [ + Buffer.from("4f040a6d55bf8b244514ae7ad53fe6d7ce8ecd6ddfcd71c1a2230837e20ab2464251b72cd360b0b4a0e31ee2dd6920170c829c81260bee19191cf823d84065f10fb85ccb0bb6149954c30de5cc448cccc6ef5555f7ce8913f4eb7aef7bdffbdeab9aaebf7a427c2584f805cfa35f9f533b8e9c7e04635ca61f1997ab6da77fc062b622447ec41b7bced40110a4dd9d7b12d9422276665c82d86c25d5c8716e575b7e7c6f5c4f5b9f902b0d1cb9a3df734a4bf1b7d03f513a85a5742116781af1b3d05f70ec5ce81728656efd25db85d013f623de65dcbc417fce0ce72dc39c91cf7086f1c82718c6170ca598c678e9e32518392ed8df616ee1d9cab89aae693cf71a7d269965a1552cd36529f36851ee7e6c6418e548b3bca4899d0027710274a1dff88a735fb18c0482d7c3b80681cc6e9e22bddeab87f1cd2c49cbd850c2f282c8402dda3645a45b443d102f6999d4cc52301fed6d51fb8cdeb684e4129e4c357ae74ca9b8709851e32d63a132169aaf159afc6fa1896854b4858cef65ca56c56976e3e0ee5ff488addc885194d8ab933566159923a507ad513683f1fb7edc9cc279543d0daa7b13f158eed505387cbdb28124b1411509baeb1b762278752ca8e2a9933a64d5fd38289016e21f90e6d9d66ff8efd4d9d663e9f753669b857820eb6cf891f70f3f0be66b6f3605c7808216aa76fde99c8a875e50287812fb5a4459b029a9075c9c9e38169fa89135a4d43691da0de1ea301ec929eb6e29f1ae0a2e05cd3d8a9a8ba0b968359f06cd45d05c04cd45d07cda686e0a1e8a28fdd8cb79c813514d3b27de77cc439bb12049af315ced10324731bb01ce3d45c93350133f6896aa3f563b96acdab648b3c64a12fba22243b22dcf4c81ced7ad8a36194ee8dc5fd61bf853c1285a225919da246128d3f7667fa4fbb692c62a98949dc48f2414017a59e76816063d27c14884c619218c6f288fde925ee4235bbd5aad145b50f581790598e14bd56a50ada257cd56c98d11e0b9ef49bfc5b563a6c4fb00592fc30ef36d2cda34c823f48fe9610c4e703dce1c01bd0b7bc54f079f697264c965aec2c79b015b00bc087b75df55a9718a3ff46f463fc166b0d760bb99e03263c07a142013842e332484aefd27d485cd617978cf22d621c73701877743c6757825df398758bc1bc2843b032437e179071ba96ff30bc670877b043ecf50d071f61b84dd1e597d0bde3eafe304dec67a88f5eb9cce8b028b031ec82de398e21253058ee7991136c112c4586530e48c4d048adec67507df55209e82a5b6f666acc9d12ee4de451778bdecf727506ec06e04ce1edf9aef3d1d6e4d9df80ee11e03d5e392bc9b4c18d003f0bdef6f4e15c5f6d6280701d467ca0f423f99716944f479a29c616c1ef213607072432309677d685c47ab981a8e069f1a7c10ac9f3edfe8d808c56ede9c4f03340db4144ca6bff53d64ed48c8d81433e3e1e0ac69abbf09353b4867be541d581f1e605f387ebaa6a9e323192838f21de100bb4c60101d633b476757a4b821c7fd3f85fffd0b", "hex"), + Buffer.from("e00e0a6d5acd8e14c911aeccaccaeaff1e7a667a8061602858b6976d1610f45e7a65b54a5689917c5b1f30276eded33c830f3eecd127b7c4c9b7b9f8802cd9122f30f243f010164fe08c882f32b3863dd035f517df17ff9159fc6f54146b5314ff2dfe5efce5a2b155517c2976335b2d8bcfc5ee3fb65a172dcedf16bb7fd3f9e7c25697e1cc562d3d3ba577c2bd5ff0ce1fe858143b4fd7c37945cf15fcce65f87d4bcffe99aebde5b34b96b02cae8bdd4ff4c692df5c12aa1ef9e9353fbd86dc2f90bbe4abe19d2d31bbd6672cbdf9858f49e22538aef9dd75b8af9c5ac82be8de106c5ed271c932d6c4d9e67a85e37b60646c8bc8fa92e5ace9f812cf1bc57c0ba92a8d9f7e8fa73c50ab9ecef12ce35ba76bad5af2b9fa47adc0deb349369fbf87a61e5ef5c94afcdc105672d0708de74db27801e99789314bd5f37592a62883a47fc1b2f9e9359eb2c0b0373dc49abcff2d996dd268080d6cf2abc46788bfefd3bda445a17ef0f0ee3be8ea9205336ff81b9139c45b0e7cd637306cb230e7c69f7ada94797c2f6f4672cf0b2dbcd0b21e19334befb41a0b06381e3e29ddf966f7c75e92d9942005cc7bdd3754059794f6e27178c6bdb0d6ba736b2fded11b756ee26b25bbbe116236a994178ab762ac0dd1fa198f96c9b6ccb96f8500bd1012ad35b6083f44c51131b943efb7c886cfb04754a56c2f7eb545614413cb27f6e243f8dd3d84961626eb853169df6e2cd1b4eec5067fbe516697c9e2365d8b26ae60a20a3cc3d9c782c4865ff7386ad316c617d616e1a70d2a1d847f29053435d8f117ffb478a9a4c7580128e3e837bcc267bb3b78c57c5d0535a2d6acda18fa240f50ae917022d2ba37c4dbba1f956c1914ee0a5b82ad38811f72ef7a2a7e08c4de44e7108dbfb2a93eeaf58b1ff957fc417ab15f836cb2476b1d47daaf56e34ed5dd9da4f2ab56527fb1ff8252b35da71ac2034eec8dd009344c00f2de902eae254da672ad5237c41013d463245d1656d748b3cb54ea099b0cfafb9be95db0f6cbf00b439171d5f3de128d69672c313894d3492062e105a660768b945beb18b1450f7d0ef4e4058adbe4b75b0069483d4ae6dd2d5828ab8237abeef58db46d378b5d4bd6219f2c8ae4b9db903e13156662df006d04cb4013f1771eb8f667d6649097cdb737342c58c30569b8e398601d3873c87c7e45604350380a473af7383af6abd59022eb5731c2ac631f97370b6aaa64eb1e8723e2f03b6b2564c680b4ddcaba701ca823c3b1a6780e474f4145618d7724c6ed8d244e459c0d6d52654aa381a4ed3131f80956a06809182ee057e1ef1af88b701c06dc70a0cb53409ef7eb4680f880d0943497e2d3bffe0629fc5153589e61e723f13f2d62c95a32c52551345c341dfdf94a759f5258ac82a78230a21a33c020f2c3c503e24ffe9d93893838324a1f503eeda77f548bf0fb2ffa9548dc9c13d64b328ba6d5ca8e142a1c4772b904c883aed88a6782b3a8326c1e9180c3e617cbc3ebb1c80d8af5d98f20c320a96659728d834c0a8451bb7942c20e82302e598752fa522365fb7d62fba97896b12219d6668e9c2ab45c1ef2b1d896c96c75bb5911da8cd142e02e52af7449b865e18623f510822b1f73a54a01e3c83474f435df6d374f49fe844d13c27084f9de23068f11839a0f77c27184fc28599ea4e2a0ab95f03312380c02d126e3c8c5f12e6d48a827e14488df1fa6ba3942e63bbe1bac54c3117234305cbb794e803503aed384ef62b5035a8954a250a40a134e6bbfda8603c93903e2ad20de711fa9e92131130545007a4940150369658933924d0ba0563b49d9831627d0692d4af269a870297550636b516d08db5a39f5b0100856748c0c5956bb79bd734aaf8a5d52274cf7378d536145a803608cc8d0621422b214c826c32ae96ecd990dac608d96eb84d1d102439bcb7b234ac9c53b949b0f6cb154861e175fb54faeaf2be65466b5af228f51ac4957986857d090a9f154701c2a7460b9c9dbbe8c2d32f810d8501aa863adfb603e5cf3ec8b55d6f6e083f380314856dff88d8422cf38a2c01cc6adf465e14e9120b705a6148188fe7b41ee281a1862aba49f85040fc1a8b13854a8041ebea56be3541909ee404e8fc9afa555af028d353c84cb73186d44599e4e11378f306e91f159a006ac37db28d3880da1ab26708949021d75cc60127e4770c603b412922b872af3ac58b5cab49d8b483644208098465285b301ac33e9ae24118173a60385243d4c90a95120494b29188e0e620407159ce0692e11529921c16c542e89c05df86320d95b090ce23e987a6b394e565badf05a6b03d43650dc02a6e650ba6216a82a064fcfe4299d9d46a864e1aa28c4a61320d7c586a3215b72db305be618ab95e7027995d7df016c105a0e931df6ba7fada8e4bf006c682cdfbce0dc20e556d1601ee4b44a9a2d00a660849254262d1ddb576041ff6e9060f76de1ec2a34a290f72b97e969322c6a8455728c4f4a92f174e8355e024f2c3d04d88376af53fe2aa8f32cd6aba09284fa08f46ab4db2c0de978da6ffa35e4de169719ad09fba6db5b36d92ac4fb415067e3e887daff666a5776d3166504e6776f8ba533bb39f584cdcc675338a2a0cb38051e605d75fbc610054ff3784bbad64c25701af26ff8dbf3151b0c5e68ddb03008c1af40e36ef29bf7bd71c4c0f3c4590b995c257c0b7cc212e895097aaf1ca9ceeb3ccf1c682d14c38eea884949dbc5b9e20cded763a4936878bb1532873a40ed1b9793f0c1c5412de4a73c8b1a1d2c5211392efa18facaeeabe146de192807adfd127a1c6cc49d1f128578f2bcad16dc3725f1e9d0b8422d82f5989e06fa0c459b5e3d51e747604692d389dc3cc8aa0c5759b9ab03e36980ad32d881fa48cb8cb89d504ba92e74f77ecc68976597814667382f3342f01a5539a3b83ec39534124e3672c38c0b506e309867404792fd2efbc364dd126dc86c7590bd13f06ae099380b14db6127555b3cc649be54ab86a6516fa9d219f4de8446e773a629f74e82f8018bc72808d1a73130a9f784e2a7832f5444311cc365886f84d714f634701d8ad93c600da1ca009c8652d07a456f06ac632dd6c682850eac9aa6364ea4b15d7671f5e568942db4256bd90135175dac61b708ec4660b74ee5519630981018beb68808037423292a6ac0c79d0c7e12f86415e92e26e5a6064d15f34c4b33c6907d33069d237fa519e9ba3860724b4460a692a195ec18a84e1b006e67650f91cfe548c7bf803a01ea028d558791530d6192a4ed3061462f8ec49e351a9deb52a3aad0b84caa2226cd8a5a62a74a00b39cba93c51e656cd0b3bea2d2c59a56e9a24a6c5b673c061a47c17b5d11171e6acd7d33038d03507b8063234d6a9ba369bd9679712b6b943e96fa7bc036d8377388c78c88a055f51e0a0d3460786b124572bd8b754bd3c1ab5a3e46d3905fdd3707409b403e8aec2142ea3c0149af4840759ef5990e1e3ae628b7803282a996fd5dc706e24fc05fc5fab4e848e9e3a19c2eb9a9cf2c62f5d0852bd3bed52b1f98e3e7ba3f586c21f14e1a98ada2c7ee7a824ca811e5310c0e81596905440b110b4db46489267153b237149e62883659bf70308f889279d9c41928e01ec52663f281b8dfe0753242ea9d42532cab468c27b80eb82574ad58884934f7cd71d666e8f147aa856068fcdf27a962c60a3190ca822ccf510dd1dd0554977c8ab644d771d0c6c77e39c7d538de466f73195c66f85a2b499fd4a46a9d0152ad371a58fbe604c393c7a686804e11d25a22ee7b2c27a4082eb12437bd08706959804ea8f3ac6e9b7146ee9bdb30ed18686364aa26d0b758331d673012e486d940ba4f1e7199f43b90aefd57979f0345917a90adef515a79a9a64b70d91f486b250e787f9569e5fb8b1b252f5d58fbe1beb9cb7456d8974e542a4443dce1a4aa6764d6bbcaaa9f2cf4753dece001adb175865aa12e9c02d14b10b9df00d4dd99a120ae381856312a75e56d74b1a20510db9319d83d808d32943a4709cb5401a9d2fa31550cddb95c72c0897574b3a7376433f5dcce3a961289339018e26d5aee6bcba39e9616bcb1516107c2288ec9db9f9a07d14cbbcc2aba4ac4d42cbe87a13624dfdf07af41275d20e330cc7a8c98486a845888d989f419a6044ac9414cd4b8131047ad2e8d3580080353f3406381821a97238a6733417d191df0445c7ff2c23ad56fd1dee45e3acc22d1e826e8be3907303df218069621dc0aaae6b337da7aab2eed5c1fa0787971c83837754a7daa9c0f01444f3ff1bc27145718127a928b1a6516be209445a79fae24a642776b9a4cde489a629ac98f11d1d92113ff0dc47e878a4443f4238853ff549186d9ea98772f756c1f174568696951ca9660edc360d13ccee456ba6da00fa57db5918f0a57c9da4f40f4bec75e95f00d6351f30de4ca3096cb98a80c180bdb01f3142c24f2691a666985f32413773f6d627989ef6cf58b411fb3025a363a5a85472730d0bef916351e2f2f3a2c31e5b9556f37051310faa57e8889a33f46952074a5eb9d3863f5bef6fcd0e9d2f4aa57a64aad726c4b12f49d0e49f2220632fd5655220a90650f3bdda6bc8a5bdcbac7a7fb9ceca37df35467700d8adcfb1a5cf0c71d1d90cc5647357455fd7051c62aaedbabb8bc6fbe07d003652ea2cf6445394b9ba9951855275c8fd88565a81baf21ea357c73a4ddb2c6e41d623652d47d77b2c1988ffbe61904bcd231314e4eb20f3cf1f926c62c1b17c0c477b24df403049d89418f94903e2984b4d2a8556e675692217fdf3c8724bde7f42b440cfd05eeccb38598d60fdfa5ed5a92f602d2862955f9eb85089d43a2cdbf784dbafeca447bce11aeebca9276c68213b6b445f308b4c6bed089688600d78f77932e6d0320a2749c16536aae65d955c5c24da552b7879cc762357e44f029b90ffb2c7483228e71f06baf8f8f75cdeae37226519a40dd1a0ef0b1bb58e551600831b0ee637a257edd535759e0979dce00713f7f98ca2f86a603a04f153d9b43e945ec6c2103316f10d23d795f00f06911b3870e05628239ba97e448aa855951d1cc9b7769c0d3799049c84264abd392ee9a8cf1c1a8f279eeeb28adc9209f504cb2682a9877d34e2cbc7b10ee78ecd51154dcf7d39c16c4d4caa44c69fe5b5c79827e0029b7b09ab9b1f01b27f56532930974aa1110d783b2d4512fbe86d43aadc63c1a385d3e4abd6c1c93ad868f344062b7956e966f8a1b7ee075cc42b1a6aedd756cbec7a4f1451ed3e19a59ab93c1ff591abca4d1b066db2ecd41ba5db6d4250985628d915f3f91f2ad06f9992d4c5fd1cbfcff3c8aff03", "hex"), + Buffer.from("bb230a757c4bab5cc996de8e88bd63ef7c9c3c47a947d52d55a9d56a519d764f84a5a34982d9372b498ea14c6bd41871e981317852537b289af6ec428347d6cc8386ea1467e48169d0e40e2ee8e03fa01a19ec9f70471ed886f45adfb756c44eb51b4ab9df11df7a3f224efd61de34bf0f4df3df9affd41c9a980ecdb693632bc728c7ee10f45ebc97a35e5ec8e570e8711e78eb290f8f72b3c57741bf8d9772d4dbbd8c83ef6dbc98030e368dfcdb069b463ffb151f67b95cc871763862943fe61c73ce910dd3d23ef23932df9ad954adcdd1db1cd9207d2dc7596ea21e6c8e84c1f1f49fdae07339664798e396530f7c69b0299c5b3ab38ec889bece463a27d089f4f6954d14f9b8e3c15ebe4fc217f26eaf03cb8c36dafd093904c6fb2d8035e7a3cdec4a8691f7e2e33c95d43f87f0c8fcf4c5c893d786227a4eb4ace42ce5f8e78006a4513421c8b123ed892f3eb0ef1e16d972bcfb06ca84f947ae517cda43983db8d5723cdeeec1bc855d2f9d4e6a02d914a160e4f63f3923f94f5c356cf4a4d2ada30bf0ad5ef5108c2bce25bf6d21129030d83036bbd23ce4a008be77ed3729163c2df4a72fd20fe74c0682dccb7f5ba370b0b92e2abb5d9b3b7c4705840c83be89b76db83ff7b74c48feb82d8a99ea4cfdb9600733a31945b82d66a382552241fa9a2fffd9197b0b3f9a6d8bbb42b37c41354b6e77ca64aa73e57c3ab39847666c0a613e21de07becf79328dc4f4ec1eaf56bcbac4abca6b654e289a4ccb57caa63a1acfa5f1c0e6572deb8d6dc64dfdfe2b773f98f091ddfd238cc1d9dc306331c864aaa5f78425515e8bf8cc697e60ef299b0dbf6b2ad4818fcd219a735ad8c3ef5c0aa213619becee72ca70512d3c54df7578157d76578c6f6cdadeb967eefa4c9d0327ffe29def283a7d16209d81aa99bed0b91e94b72afbfcaa60a842bf6fb09d629bedc2d4b1b5e9fed81effcaee3fc6fd6a016a88fcd2efa8cf88fd88b95fc97f71323dbd39634c2cbe329b20201fe3b831c803e0c29850a5f6ff61a369487bee653ce699f61acae491508cb21ffbb81760c009e58b0e5351b7fd18e3ea553cb96a053098a1c295d41dcdac042e027f6af71f556c798ac6d5cdd0f0a3eaad7caa0491bb77f3cf42412f9fbe12a4f825d7057892abd4c6fe05829e4bc90286cd948fe7fc99194bb31de736d5da8ec6b72bfb3e7d315c719bfcba555b804b0ad459bb30b37145de400e42452215edb635ad8139bf8a5d8b13f5fb1ce618ed7389ea6132f19cb7b71569707f582f558fbb4aae73b4461d503f4952fa7e6c94b9146b60ecc9b070854b5b8c10adc2d9c8702a98a7108ca9bf9c2959aa617b19e215c4626e213bb6a3c5a230bddd4db4c19424135be0617060f6adbfd5dafd95a9aba8c29b6072eef7d1d3189873d5c7367f7cdb340def82c49c3711a9e086cadfeb18fafd2b9559c783abd9c1e2878a882ed363534bce9a9b2ee6d0135d2e373dc1f4344a8f0fc5a16092377885e17fab9a677eb67831201e5c253760aa1e55e0fdd360365261e7a2b0ad01712bd1634b7a10461c7037b1fe8ad89da62376083cb8fc48c644cbc07555af3d44b68210d41e883ae78a38538d4a2cc91f3c7f5893d3c537b4765c38625388d984ab6e0809b2ccc658ff3a105287db4c6a54fcada941a60aef65766252376f0ef343f18397a6006a30b4c9e87ec1c08589179d7f012e99a9937f7bd1135c9829520c7be8da85beb389975f80126569f0f907a8d350073324ee0f8a024ceee7f2f6250fa61d267c97f6c274614f4729088c632dd0a9702e04da051f2e08ac551e11989bace71ec9aedb29f1c16ccaccfda9516153f3b0dcc32707e310f12c65ead5d9d483f2e430097f551e9dc0993895ae92cd77877a73ee95870d71e1a2d3d99feaeccbfcf1a7662d9f7e7ca787aa51f3fe4598caa0558845c686725ab7b92ad86509381f8af872b5c7597d792618dd0126c68b99311955984ca6508c0ffbe2a9086e837c2bc96d68946516a1b22742a75d1542397e00c2ab0343d10540e88cc792298443cd101491d2eb4c29d2136c4d9c8c9f1125558dc4f4becadf3e6f7e32e90756aa1598b2415d43d0aa1f64e5291b59850d56da7bfedb9bc75287f8b051f79f1faef5203179024e04abc00e933c427dd5078851599bd5c5ee25db1184ef5c2ca52270162cab9886890df6863556ac018ee25852acde4438535b17dd02d0069a2678d70d0311f1d2d75e1c6a30646826dae2745f9237fd95be7dbfbfda44440dd52bb82d3307f97c65ae361b90683e2e1beeb6586c7625f9f85c211acf3466f241aab852e18b7052e5560e523322252240c91aee41cac2e8cd5623b282b2faa72de9272198bab6d5875c4df8db1daad60e875aefa5091c466b1f56931c9252f183cd5511a8a174ff1b77de245a798cc042e5b8dfbf0101d922aa95d23a1503b4f928f78be11f18dfcacc2b99ebb2dbc12dbfa6503852d2d32cbda423c1ac5ca54fb592d937f0cc64b10f4d43554ae02bf7f141bb751ffa65b21e74b4fced8dda68fef62d4cb546fbc0089643297f72c93be626194b109251d34e387028a93a28df7f112aaaf049d90cba2772a635dc13028408c9ec5e52f998fa29e0d5c71b15f0e9c778126368dc7472b0c2a3c40521ec1d097bd7984a6bca9e4b6c068dcc10ad4a28f83491e82cb738d470674f17132235dc73144f423c7a1715944cf5bf2ae4369a2e26fac7074adc4bab30c216c69148e2df8244485b513256b98e79c2bbf52b9a5d6ab79a205143d2b974907aef4b02e8e5482dd0ac935a889944d164beb5986c915de2c40fdbad9041b1d6bece1e232622ed0a67113b20362fbea6294e9a1c5bf23f7ba672a895878703cfcbe01938a2a765dd99aa6747a90a95bdbe9bdb3bb4770b9e562699b627e73f85a7a453fff66720bc0e089548536f12311cbc02cb4df40eef847e25626070ef962278d63f7276a8a14792b78752fe8308c0d77ca4c23eb05e9d70da798bd7dac3349939b3d54471cd88b1d65e81b551ebaf18966c615fb18a11fac01e1de177c8d107fb2db30866153df13a5855b4d6eaa21a92a6b9142cde4b8e30015f896c897d4eec06a1142c26e3969ced1595208ea651e1dc467a77fb9394e6c829f770e5b1588781dcb20e2d81d714e550db2ade475188f9e79be6f9c48038dda49fe550abd63a6003dbfd03b0eb8914c94f1aac86512449fe785b02e6846f2ee6b3a68e47ddd620bf9e44c8e2f426f25858bf3810b92bf6eae3ebeac326d9a14d505ccb3d389a9f5fd3a618f0e4e7fb7f171adebae2d8c1d2a55a3d6f1dc1305174f73e4ec401b1676633f330ab6c347b450b9e6cff31feeddcf2207de1f25057ab02bdcca50bf263c3a4294c4219b45ac9418ef7b5aaafebecc494db1a7702737e37b09a9020f1d3a96e1086ddeb580b6e1a6a270a78f0c6bedc16efb192eac662656f44b546946b28c8fef9adce62a4bdae454c79f0f5349d3e9c6533ddc4329470bc9f4ca26640c5dd55bf088936f44d7d35fda2ea72fcca7446d07793f9fde8a289b42f29e5fe23323f99f9ed99d3a00bfe7a52461d6af37092384980ec4c74fd9b6334af555cfb4483993835d0826fd77a505b23b3abfb9894833ad3439d59695911a1fa768b325787dadab388b1f0e1683f5245bc9e5c26f0ed57139b985648665daca38f91bdd35c12afd89800aa2be8a250f3ba51625e7fe970bdbf9d77c13371a446eafb57ecc19d26ea50e7d1b14b19990aea85a136f6b003f1a818d50d9b7a9314d1496b778d4787765f9330794f1ea4b169dad8c4e6dca991d6d808f674a52631b1ed0961ea0a77e011e7e84ced26ead6634cfb893f90199d0b8e606b795eaa682d838b5195f84850982eee36c8e171e7604d101b80748d6d924aa3a140e483e1a4f42490e6a1ae68a81ef571b76b3a232740edf2e40da1589fec7e08bbd0fa284a82db7b382f0bf6b6f04abf2324d95504db602532dd6ea3120287f6d6506b94578dd290774dbe39019abee57ed7f9801f4768e8f4378309244b23e1a0be2caee8674ed60d046b77213b491d58134bbc53e3fca21a707a6aa522d91538d3401154c83b51a8b0db64688ff834d52f736dcf61e3b119e713b286d3aef4d4941d8183096b926072cc823fdc9c54221e7f4d9a4aef0fadfef671f724e427f2db1e9ea0b9fb0459f8a44cd7ba940cab220f07ab687e8cbf5b0cbf93dfb86207e677ab6f5fe3b894d38f7f21a135fece03b36b9a31306e94e698c64d93a4c2dc60e0711e05bde870332e84e0f4b4115a55427a65e0036e04d2d1081dedcd698673b0527fa06ebbbe8d4f9afde35efec5f8644c7183d73bd5f09bd3d51c2d99d35c10cb21bda03c5d257afddd24813772d67196ae04f2b8dc35bd82527d9c83d527bc4d5517010cf81561c82403754bf0cba44fdac70225c497f90fff2b37cde3fc87a1c7e11b3d34ede3ddcbd03e16a49d98219411d24ea3a24d2f8e846622201308556e6cdab153907d7ab111aeaa9715318897fda919fb48e140344a27d4ad09a0424c668ed87f52adca788669e5df1cfc82d37bdadc9c3278249742d53c3e11b4859a35a959931a39ec1f0bf3231458818185e9e6748f8c7139d16ac1be183702f46778f930b66a0d42c33bd0f04e3c53fc166e5bcc4a5433b29b06c9c1009426b10ed32c2a59379253ee0926c62e485a20994d28699490e724e43909790e421aeaa4501fd5b747445539717d11b2d29be0d2b19f14291c019448d8ba51c18a740651a4d18651b25e83acd7144d4f4d1ea8904e8e9c0e55e13bd2a17aac12bc1223515c4d35825e79da0711c1cbfc979a3d3ece7fa9ed4b55b1bce28dd59a0735f5c7ede3fc1027f9a186ffc72ad927bb7079735acf219493694d67dc75538ce325cc570d45224c07b666fc46e738debda47198344ccba2ab717265a6679f605f13fbba605f13fb9ad8d78e7d4deceb821deeb43702d64a0027e9aaf38090e2f860b721dbfb8218bfcba2a04438f367c987805a177af55d19f1893ae82703ddb2492f898060c50d3d471d5484398c6b986d8f612ea8cef661e75c11d86d6559cb615cc562d13b1a837909196d31b13848223d15202bea0ec74ff45f6354656ca08c4d33b537717daafba2b918ece12e5c08e858c4160dda62caafae3cb2e0da5695a598816409ad0f709b11bfada6504f0310ac81607d6626e2e3ae20864dae92ccfe5b9d4ae72cea2b43a3732ed63752f17266d0837fe82c5798b1841d3fab6a90a307573500fdac571f4cc7467b9db9cc0b075c4aae2078472d4302eb5d7b978f771e50d24450b331d0d7f0c3142b809bd302eae54f5dc76dd2ea06f554710e74bc8c759ebb8ee1192b627cbc881f387f6eeef677ba967787b040a892bdc85d24734d73271712b683fb58d7f2e20804486c2c2404bfbf901839fff89f65c693db462cea50408b63e98b53a5cd289305ecd89a7e1f55fa1fc2046d646928672d714bbea88c6de4a77fd3dc41315acd07ef8c02a365dde0b9494a401ccbdc7373818c08a32b12f92af959afbee66d733219939439731ee57a5fe351a2b45dbc6298578d0b7ad87de0d7b152b2d6b38bacb855d9a530bab3de851245d885d4b43b4e4c1e1161126e43016e96632e5ef07b2eb9d4000c4569cd2a52d1dcaeb83a27c5b5cda460ce5c21eeefd687e68e526ba12e332121162d999a40a45e69682d163c0c2773df0cbf9a8708a28754884d05950c2bed01e3cd54c24738800fd44670f3a102d0d1058e73d06035c59c40d7022120880d28975ab87093a4326849ae2287d5d47c63b32f4704c97ba8f664e847d42664fb2a2bbdb78020dde3f4678ed388f7e4e50383fe650da51973cf64e69b13b2da33c794c5265e061f0ad0a568bbd3b5240593ab2954eab336196ec41024dbbaa1a9bf68ee1c63a4d51a7983abf6195095bac29ce9d55a0072ea8da65a0a7131f6bb97c5111417aa0e59c04503a738f7778316b26f154dc3c35a0f52cddf892e59ca604999db988b4c881d9411c634a0598c20e0a6014e611d7dfb4693ac2b09bb880c73512da91d5f2677afd54234c5ae0c7c08f90d40234c048f8a2d0f26c356338f0f8c36a9064acfe5ba511d0990b028d9cc8a8b11ae8e830850d36047923c44b0087628dfc9e48aa186b35ec57174072ad3596eaf07183891c68925cb8d71ae05c7e6a2ca45f56b90244caa1d77dda270e2081b4ae85b0822ac30a3389be8a48b1de85c6bcc35292c58a7b61316209356318f43dc6840a2bb891310172a4ff1f217958f433caa5b518f7743d5fd0e1288a6875ec1194f8eea7e51400d1ee819f2a4b4948a0e1e438276e5f366ae24f52d083757d2c16da40cbaa6334e5209197b8e68362b214bebb21ac1301fd27f0f8cd9661cc06a510996573629d200314cd861b8a3c0676c5ec1301a1a0678b08451f79e88b6966ef556750dee13098769ed3807ddc93407fe57e2936605fa561f51f3621cad61e8ccb53d0a232c9006369d00694d4860d2c280e3eb61a245ad438158669e558b2a8571a13e740ebfd1fa57015949a350066a10a1b4e60f127592687455524d8219c042cfdc5b41e717258e25cb414709280bf08ea5bb65108402d1584a4796865132795dd84d4857661ed5f497ce09dda0bbc0980c5969f12d07adbed5b3cad344e12e2c143241edd570a3f5274a6aade8cc57f6d1835b749055fb16d4e25da38a2b8ea3971f641443841a184c735b1cdfe2d1cf6f2940ada981f35dc33c495f44a307bc6cee42059bd5e6ad95c35245643947a7fa3447a7faa47c0a939aea3caa05f896fe2c8d2df5b98a497294be2dc2833eb6947c2a4458048972d119defddd4a13517970b93b96cc9955ab84a23922d96980477cae5e2accd19106ead7dacb293446b3a0568ba6465c76097ebd59b5ba5b89b67d28a1bcd77c9d308329407bf725de647805281732ee143575614edb3d96322fc71dfa01e7cc859fd50068a4bcf60e961741a996a4541692c0d512464344ba49376955034356e5e963df6821de946abf6f917996acff7533a12d7a0147e3bb343120b2b55ab6344e52bfdbe9cca4a9b21e0d9c391c69eb826bb548fcab28b48d6d44163cba2d23888c411d1ac5f18391d3274f95b491c080e289f4c795a43c0ab8a422b3b334f0d2c460a8ab1476bb92cf6b84b50c3e4cfa31197acc9625da856f34847b0506bd63817309a4c935de9cb5f9848ed858ed4d5354d8ef55b9d210a425b8e6b749659bac13a1a6b3637be94645ec21e858ba0614d415bc5ad44c642cb52c93aa06801eb93576da6977913fa76a3726f42b79f2e3dd4feac9faab464eff7b063435b82382f6c7ff2b78c71411afe93be0fbc7d675f3981c1305b7f0b2c2bcc2788576a4429c5369c1bbb9b7353c3c531f6b00d0972ea0b800795a2ac8a70a521bfb20e524e781c5a015f347ebac10f1ba8958a9183b4c3676a538eeb4c26a0a703d5dd5509a34dc5f9aa794a23d55f736e3f81a2f5eaabb0a6641a90e94bd5810740b7bac0b7277cbbbd38a6402b813c19a0f3673b42451e77fe85d0546d807529d5f69b1565d98a86729a00691c970326301b898dd522cf6f13df55a85913379213b16c5b5743809faa9c32b1086f369212988ee09a27beead065dbe114070639e0c83959a27e80da24a543c4f73bc03c2f81b04cab0faf8cfd4c84fc83e343644017571851b0a55efd385780347bb73e38322db09d859f1a192d8c56b896926564f38a5f665163bed7a48998862911138291f0d780974339ef5ba8274178a705fbb56b62c9b032908f2d098ceb3aa7ab1465443f200bea8772f3a94beaa42be06194fafc70b4d604ae1a0cf1adea9dd42b57d66db0abfd87c5b3b7c6c72d91339ace03f4c3b3ffe1e62a8759bd294bd61ca7994a1f7bd4f16264e807857c42b7a72e16d702a85c692e6aca6326ed0cfab699946782bc91b63a5834a3dd64496cc2f19dc8cc50dcb05adeb14e4bde8c56f696e575bed77d7ba46b7f416566f332e4bd146e79e98af3a8da457c26e2a95e4d22bc9dedcfa255baf4ca07a6fbe38a3a2ae5f508d76d702d095b1e4edd14b931e2e3c9d15d1ac978375203a5335e0584dbb334678ab85189331afb84b9eda9b47c9c2095f2969c645d1cdd63257f5324c42c804e50bf79ea8d80c9df505e2cef2c7c12571e9c29312a653eeb4ba12748395a09b66dabe6ee2c7bd689faf5849fef1af69ddc10351c7545989049e317ad3e91a11e61aad722d8d27b9e90cd1ab6126c29b8c3eea658a5876c1ed63e685c6f24c4b25a5ef89fd2db0bf3d6b76abd39002556a9e8e318860b9d83336250d2a1d65fd792f6c9292ce702e6a1b1df8317bae3c9e246c51f24b2bcfe79e752dce7ab78a1619466d6ea471a835f95c71aa00bcce8aef91e9734b119e28b233eb6e6a9113b903801b0088d44a7c64bc77dcf8a20d713874dd7b7164dd61f67c69b8e60e584b4534a215b5fb339dca3cbc98c717ea9bf416be90efdea343b3ac3cd0eaaf38470d385ce3e712bfb2b56465472447c86aefb8c7858dfc0ee9d760ad2b8274ef3b6deeb49e4373916a44897ad5945799c1bf2d556066a9aac82576bf57feaa915a09996db1898c37453dde45e6afdab49961cf4483dc5c7df2188d454ded36761e0bb84e84522a8cb9061f757d31725bcd64d5c85afdc9bd7bcbfd190b6654863831171c4896aafe583a2040661e75b0f2f5cd51196a9572000d9139618b8472aba2f32cad419a5815b92d8b23dbda571894f8da68b0c4eff5d99293b5fb03169e8277327a72e2c248699be93afb88352f998af52f73154bb47b46839e8908e8b09cb131e46c428e6db9dc32117205ce1c7855d709db113f730fdd06d69727fada0f8e6a150b2cbbc5ddfb6eba961eae9efd4d084fe5db17d7aa80d76acebd57fda62b2f581e74d6cd371a7a3134187ce022fea861528ba01ed162315939d6642bd03b680abaa82b60ae7209010815d8825fbd17968537125c76beb85597e1322b98defbed330b28becc9330aad68f5884879e2f50186ab38ff481dd57b55ab688d8c37bf46e0159574291d1bd7f2690d2d550eca474fab00d83a83c8dd7b44d3b62655129c04e6363ac5a167ded8d012db5abf702a86c98988854d065bec9d675d3b5cf802bef06e445d7a5c3ee5e858d414fd760726843055f914937bfb9e07acb6f6e7eb382e4062edf6a63039a730567112da4b72551ef3beb3d335678b40bfb67a1a06236e7fd86d6aa385fa1641e8442677f372fce5ef2e86fd28b1e08c2b4a1e337a48628783a578a1602ee102c24eb501fea4df8fdb3688850c25dd7307a85fe4b5d8fc85ad3ee914a629bd14aa48f66d0cd692ed3bb1ab23b38445fc6a96c72855e61f2cc8e74c3def2fe19d528a361af6ca113bac7d5e2dad47481b57745b1ed04c6a7cbf9b0eac0eeb3753cda62243afe9cf1849cba5fca2614dfc0b37fd61a9aa6f6e98709c707628ac6124dd30011972d4a929bd3728efc937bb6e2cda9f7454dc3e23912fb166e791559e7fb2c6c251f7db0d28d13c975b43c165fd7d5cc02d7aa952ee6f11d177683b5df2ccd67eb02cbf916b920cc387ff86f24793ecd1ffefbd8b01155f3f3583a64c7e4cbbf64ecb8aef96dad9a3cdfad2b3ab6086955edfe597622927a58b8d9c9a694992e6337594b855d83d6a6152802bbe490683329ea3551eb02e955ad2e2357df08389b99f4d05ecddc6249c9cba609b6a1acd4d5b3e90e325a4d670a129021e377f259f19d439508b3626d6e403b175edc78f2e5ad8563ac6614c79e99db3fb61ba599422ba1bb77d6ccb13b253667dbc748806938b077d5d3976288255367d9e28c766eefa5b2cda366f2b63836c2eeff91dd2a67383c8feb3de02dd0a1624ceddc21f404e8ada254a6cc93fd02b52ddba287ad9bb7b09711fadbc54dfcfb928e97248e823934b712956eab358288055414ebca46ef4a40f5d5595e435ad7d3ae4bf23ec86420fb7a935ccdd2d38dc25b131e56063b5f722baa224a721b3c53127012ee6ff7b7da9abbcdcdadb90a497fb9a4e1328b6ea66922454574f3d7e83723787900eaa1b6117424df4652d2632f13badadaddd862e246ac6da3db0e1474af7e45e035b7ed2d311b53e59c1b4e04775216170125468499799cd25e8e95803e39f4166bb4b31acd093b59ea735dfb9e8c66332bbc05e62a5dd93e04eeb620ccbde2d26871dbbf09ca4ceab0f1b8bde5de12792d939cde5c852b585df3298e2f3ade4cef098b3326ab77006160b6e2bd3037b8b48e598b95772c311bccfd6da773eb967a13fc42f553091b6dd1cb6bcf6ec7f5a1ded3a48cf98b3a445d397c8f168d3929b6baa347b28c31126b8c8d26ee9b85c74b0561a59cf223835d59f15c903759cb44f79be4ff85c4db9298495991675727dbb35c73b64d2789b6c2350dc4cb28832c052cdd7a5f9f866b150bbe6277e7ef05255c39841a6e2bca9e2b48b760e38a1b9954aacabfd65a18364733a97ae3e487bcd82a77d1032ebbc860c7592a1b55726c3fbe4e90afae6b5836cde4a075f67ea3a0b9533022d5341dfc863b940a3255b2398730eef5be1dd37b0c0320b5ecfd9a952b9ae40513e1484e29fffe64fc9b3076deb4ebd816e86d57c21acc79829953a998cba60d0ff0b0bb2d328932599cf8b66ba4f65a3e3001fe0af971575bd5d08c010e627ffb9da41d5362bddf92cee64411b28db68fc256e839b1bb354edb6b9153d28d075ae8a21f98eb9e768672d55a6ce5bde5565e81f218303a6b88e85e7991d297cb5702237973b357f7fd3eb89f549f19b7d1d6947437b30e8c6c02595e60e53559a9d2826f1b62f03dd92e134ea8d167852dbbb7dfafb067f7f6fb1f6fbfcf5ccfc0f9457fb5bd95bc47fea9d2e86fee6f6bbe68dd9aa78d37a226f61d6d762df3b9094c3532c4f796b9585414bdd96d5bb61ed64dcd88b438fff1eeaf98f42f946edbc7821d2daf7d478b5589d519248400c17914b8c7db1f6f759549b3e15b5bb4b93dc9bda5d07ed8de6a100e4a91b78afbb143c19e3d03b6d6ee36f95a8d05ff0d18700d5a68c0d7bb2db624eacec331eb286587bdaa05e93925fe5d882d8ba8716161a5fcd9425f037a3e1c25f40a0dcbdb9312a5b0578c21a7bcbd858d44345b1a0dc8f38fff258827987ffcbd1e4045dd3762bb82b767acd21d61e6a1ad23af14685fa06eb430d81d579314efcd69c07a269e7f25c09200bb50dddeb2454e48d1db9917deb2620f334085b756b37aed91f99727ca4531846db6aea9ef55c3fa87c0e8b9c36638c5ad5459064346ff46a6171483abaa9cab0ea8e07cd7c3d6c3807db272586c8b202ddb4e5e60d13f58a50c7f4ad16c75982d70bd64ada4ab1d58f1623948d166ad132c221a3c58962aa2fa249368403a10d4a6b6f05d5b4fa3a22d25dd88f234be7df5a115b3db9dafb8f4a97ad15cf1a5f852b8c7ec2a18f758cc7737a70b8438f5c452da2a3206fe4098d12079a7b4d7e6e1851adb03f3af905e1f27d95b368e080d2f3b0438539cb9957df84c443a47bb0345545871dfc0aae2082b541b62aaa83760aa01dc21b48473b417016526ca2a232c014e66fcb4ff245eed936164199ba1e004a88c7b99759d92cba6b64c8d78a0ec0fc4a5e1f45259a2b05628290809475020b628025526218713bdf36b98d22364fbf0bbf7894bfcd2a7602be1b65aa592d280ae8d2ae55acf9551dfd4188d0c566ddd1cf5167669fc84fd2668d8cb413bf642ff3301dc9aff33652e4822576fb10e733f034e6e3e2922d3bbcb12d3b23ab2802d0d22306c1078d9b0f32bd8d08a294b905927992c2505206de7a8b580f49d237d47a4ef14e90b9de757f29df9f1293226c92ac7fe45f34940429c0469ed9ba26a63f6363c8b2b422c9b0a965cf4f40545f17cdbb268541bbc3170ebce5bdd9e09dd84eb7ca59cffc6d8a63796f1b7e80dfe76cdc56970b153c4cd278430546dc993dde0fa36f6a57b63bbf5a6ebf40068ec84add966fa3f53bdd81464ba04fb5bfcff847ebbc64a54a25265e54e4383f73f146366088675327d4e2f34fedaeab5ab1694a2e11b9d6fda4cb51ffc18a9dd9fe2ef9f72f8dc7eb6ace273ffe6c3e7f8416eed3f8b317e1646fc10ff3a305e2b20994045e65d2665424696647fde71ed1ad75180f2686e9b677d6950a120bfc3c672eba97fab0c796a8b166a2902c0ffaf019ff3872fe184091c1a9ef792b6e8c46e21d1add612030e7c6a5687d638ac6f449b423764ea6e3e91a748f34ff1ee45e156837d0f8543fbcfbdccfa59ee44848da888d481099e9e589245dba0958526ce5b3a025d58da7afb3ac1b5db1f9ab4252a6fb0f74d7bcedf3306e828f3b1ecec51cd100c22f0cffa1dca399dd904633d319afb961e51d2ab6bc43ea4f3e3c2972a586082151d36d36fb07ed88307cac79976f3d53bd290209439c4d085b8ff3423b57542fa589d71c9f9a2cd97bce0488c2c4a77e9c60714ec5c51d760c74a911c50b31b46aac382337751c89d81d4cea3a5651c39fcc2adbbbfc8990a5d3b73e372b2d981f32b4a677899121b21c34395d33d8daf0d074d583893a9c5b4413e7cda672a23b00c85f5bacff2d7ce0745d3fec27dbbc4d30b43d2782c9b2c9833cdb57894b278445aa0255d562d9ce982719ac320feeee6ef26ebaaf1881eba428494a4eae196b1cf133629b4f69360562ba19cf0f747bfb6f02a5a24e80ee11744590175cf77e7024ff2d57af8c6167f0476b5d315f784ddc4f798c3b1b87cdda832ba6a70669d92fe53c7fd3577d9fdb2ff45722932a36b9bffddfcdbc5ff0ccdff68fec3e2ff84e651f8978b97b1f957cdbf58fc3a4233fe1f", "hex"), + Buffer.from("15000adbc1cdc0308d8181e104906cd02f65000300", "hex"), + Buffer.from("11110a8d594b6f244911aeccaaaeea97db76dbee79989df1ec7a9616b41624561652ef61d412a53508b4122bd1da1f801007ff0084561ce6bce2001ce684f6e2eb880362b4f7153f626edc38fa179011f14546648d57e2e2723d321e5f447c1199fdaf59556d4255fdbbfa5bf5a7938b385a556fab170bbade552ffe49d78aef37d50ddf57743fa56bfa2ed2fb7df56289efbfa6e71bbcdff0fb5d5a477fbfad5e1cd3d315bedaf157bc3ae2eb31b4f33569eb20f5afb84658c1eb929c40f76f93f43dd934a6bb1dafdaa5b7f474031915646c866b26747797ee6ed24a7ab6e72fd8aa31568ecd87645b4bd73d7f9b2cf98bf959d19a29f0515c3a92ffadda3286ff1d5dd3eaafe8ab84cb0878448f5e55e0c2df17eb93f48f81d79e6ccaf6377ab723cc02d64ee14df6ffc6e2d2dab3bc8efddc381419f98fd57b5ef71b60da18e2abc28b15bcd8c0fa95bf4644b586579a431758bf87f411dec702f971816cc4376320a74885c2b21a6f97b0ec025fb1a61d6b5a7969538d3c6b0c407d6cbad48f0ad2392b8f4b842da712620f11b16831121cd9ae11addc17fa350ff4ebb7963de34246b0b5ef683fb6fce20a6960cdd222c49eec81bc437cf54e566f0691dc43778e8543d9b2b8e2e75a6b7b67b5f24da1616fb1def14acd45274fad0a2ad76a6c83fcb83189357c5d1659140c11b37565b9391d44648febc838e1cea44c8711d17c711a3bcb4cc1ccd55c2c98e6d8d7c94d61e32ad743aee18959bec959999e7f6eb95565febbd38a703c6d0cb3077a7bd3edd05b799c1a544ef4f8edefa91ce757c05a679955ddced8dd65fd1d382adb3d366fad42362eb7ee90f58e451b8f4c35447b629e702c07f1e13cfc35d028b07c5b7e3331e99b8ce8065da972bcab4854df51317ba0bb3764eee105b57905fbd4e69c9bb5eadde7bcdeb9f81476b8da590daac155d8b175d08de1552bd3de58dd061fa50db46a7caa6123e247ced6cfadd6cc9a1dbc77523bf33af34ae71152eed9df53fdab5cfdfbc25fed99c57dc9daa760d14feee9a88de5f0701e62b41c831abf6c80a2cfe055c115cc7f8d67ed95593db1afca6eb917ac7300cfa06c0e925bc091a748b79119bb1a90c42aa7c9cad2c5514a3568b29bc235a57f4f9c45191780bb843f2d12630c7bf5ed02203ef509b301ed7852788bf2bcb9b7695445e21bed490355fd8ea81a4f9e5af27bd6518e43bb81c71562a0257e37f078879854f0e9062396d1879697c5c7254251c637f7c6678352c84345b036e970796758bcb34163eabdbe71a39643fbd4934355964818ea67293a083c2d9a5b3328c489c92b075df6a0fe9aded5579ec25cc3303f6f40b99a2b0389ae181d49862217a6be01eeed7a66d46205ee9a91c6f709bd7fcdd2e8efaa7a992cff94e4bff97240c15621aff8cbf4b75e5652db2fd94a7a72494faeaff8efefd2beaefe71dcc5505fc478ad1fbfc2c73951de49d1772693636fc60601d915e6adde29e0aab872c9ce90ee8dcd5dd6933745fa73a09fec6253d82e8ebe54d7eb2ff05c207c09087735690df19a5fbfd98f96e9efefd3dfb4e2cfbcee5b0688be6280c6bb2aec625d1f451b346e8ad81b1f6c9035ae61a83bae51ac32edef86439703d8b74a37ec8c00976e8d6a6394cdbbbce37237b7b603ac7cd25731b273cbf437dd346d8c7449773132b45fe4f1b0badf36e5b57b87bf9bc1c894b7d645cb534c34556eb0d9795be2acb835563dc5864c2d5940f6413ce75a39e7389f73ee9f73ddcfc96f71752297515b6dd3e52856540f479a5392472818e4438c7e70b87733a33128235bb47b771f2ca776c59670c583ad6d1f5d7495f35cf3771c7f60bd2d0f24138f4645374bbe5932344b86664982cfe5fd125f1980f73f4fa89df4d5d665cf34e9dc257805c26b5f84529c979eeb96c6a3bae5abdc31858d8af7751cddda284deddcf05601c7e1807b57a056591fd72d543da4b8b7c526e5ceac5ce4883a3819a94bbeb92cfe176c978ce1721081fb9f273097c0f62197e928d724e5684c8cf9cd0f129bc56f3ee1bfbfaa9d3baee0368356f3ff717291a45aa0b51f6ff6c5746cbb8dbdb56a86e692b3eb5212e78a9dbb2aff67aa4ec2532ade079d0af1cf4920651b0a18385d5005d7caed5fcadc76cdb59fa05a32544b866a895e3ad84a6ae51563ffc61c9b7a0057f70275ef3666e752dc8d084d3146d48316a14267ca47575c84e9e14bedf2d75c8aec49bafc57e83ac178c5105d51a2a04d4a3b7cf347ee775f8d9639024bfe5b7dc7ff97f9ff04ef22a1dd5611603f11eceb58b554f2d2313e95fad52c25e52957ebaae8c532a4bcd6910473877b7efd695515a05a96691074cfa52759d570b25ef9994e1354377b8d31cdb013e1988bb3e645d8b189815d19a7dbd889bb548fb3363000d23d2672a90595a33e4402e988bfe4a622b35b2dede4323798f43612b881bf9c70fb515864a4796dae6b87ccdb656248b737ae5c0aee3305fa6d819d04ee0187c2f20a149dbe1223d1ffcaa47bad9315a8bdc270fada4d5c2a49f27677fd8740d208c256268d519fe048d7b3845902b10d5b817292528baeab841e5d9754cf4954c8bd8547b10b0553d2dfdb264fb2cd96515221552e97f8e6ef5c0affa0bffff96d6ee8366a5fdde3fdcbca72f91582a345f8850536cbc983b8cc96857cfebea641bc942fd85f9abdc55b8b09037abaaba235df2784671505526460074c1fa4fbb18d3e0f91b5bbb8a09473c4c053ef7b22b76b237d75226bc67239e943220091fe7ebaa6a8c59f486864d5a17cf7335111c496da7d3deb6f79f5cc6a86cc2e94c865219747e09c91dc3e3391f8e038dd4ec9bb7e1d5be2a85b7e8ba21cb5b70c817cfb913c541e9b4aee3d16990d9bc5df9dc2da90648e93407ab580e51fcb2773b90491785c481a1947125a9d9344af5a59d3a65b27e940447c8889144e3e32898708b14682ad21b933b88c61762c2e7f0f5f1c1a4c1ca1746dd9620cc0593e709f60dd38499ec3e74e243f4231022c8898e39616ce1912843129aa145890632db6c5b6e3b0775b0b7b03bd93a4f780e191203d57406138a9991183c8f7391f69a287f4be633ce670394b142f9e43d0a199dfc1aba915d48216730d48aa20581c6f11dfa1274aa048099959a76b1c98df211c548407b86f45cd4c966fc5ce5b6a299c9d74811a289825c19ca2e935dd7f0837824e8a107ca4e184e41a92a378df24b132b407b53e27156c8ad0342e9a7ea370c96de7b245e43430a6b1b7f30cd1c8f253d30713dc18fe05a0f010af0f9215357f2e0a8f209a000e40aec3ba519fd9ed08d40374185b14754a3a04208204fc04b980950d223d82fe672ca18a88ebac14acf9f1c045a386b5c33cd438636cc834202b1bad62e87d9f254b5275489e5a49a520765688d831e7535d95aa1bacd06451a73b80dd4279032f3f20ae596f8952112225335512b8c441a78162abcd47342d285c92c59e334688a2d2d10869281c1b9dfa7ebde55b316304e843ef9b06fd75a4b84453415273b342f2425d8b645964bf0bf4a2926f690e0e0b1e4a44382a01e68dc83c71cf1165abf321a47ae16c0a1363615d2dbee455db666026997d90495bb828384e6dda350f50e8269c07eb2d3d5c6f6b582a9fcc74b9b78e4ca602113f1662111919c55e2de200c081b4061b76ca4b805aeb518b0e038e2ed74a7f0265cd94b3de6a61073533658f583bb55c43d861dbc4551d6bcee930672724ffadf1e7612368d7a114c750f43d8ef81a8597ae82d81c43aa58549b45322405764acc6990ac0a829ad699bec60c3acc642464a675798855e88dcb24b48359362f4dd40e8fd34c7aee88cc11b8294d27147c2e69aeacccd3c8a4917669c7854d9bbb036206171608fa3387556d2069f8e6982c84120260a28fa7b6685b437b8df70a5de885a98f7b9b0c0e2d7a183d3acd5da60b35e43d6b2cc10dacca9a0cd49a1a854f547173da677a8ed6e08e412afeb00bc24ed14eb92d0a551190cf19f7a45b3089a5ea63512d54aacc2ebd11a5999e1e18f008edb1ae51133a0c8e52746b3f683dcd942d582aa65abed90ee6afad70a3e4a026c124e316787c0a1aa6d29ac68ff053f49d235873a0f92b4b1b6dce89ce282fd644d94a7634214d1dd1a03255cf349522db2b09db61aa653d722ae138be55ecd7749296d3fd1c7cd1f4d6d046aedc665c67b40b45e3410debacef0973091963a1a24e0aeb4810d1943fb25454bd9ae90d4ac9b71f2e8e9cdd1825d9f20b77dc259b66a9ab4622bfe86de27cec94b9188ca06bca3b06f808e91365c9ce8d818b5cb659cd812598261432b6d3dedcdb94def4c5d4a8ca0fa05cd23143b5404897e5d499af6b766a62336440ceaa5a1d116a4150834783f9a1c1d0490219c748c29eb6d82e0a436a8a60f0904713eb846317c4da88c26d54a74efd9215ac750890a6a1fe4e746e5deb2c931b5d8bea867462546177c772232d0fe899eb04b2568a3904452d5ccb6537a3366696d9f679fb3bb21dd98cdb9a6075d96207b966465c00b2def60799072e75e0b0dd8c0488a67b4d2ca8f911e4cd8c1cdadeb607f2749567ad629e7b24e2e24f4168485b0acb297f4a3420f1d4445c530aa89c470aa288c1505043c47363103462e2982095c74710b4ab873489ea138bde0c65111563e3a1f775faa53d765b459d0dc4a8b9e24c4d38eb6a8d142646da4f6ce46d7bd7a5902463887ca664c565a4db91e0f96c6e69ecb79533d6975be54c6d93a8da21470baf312f20e4de8607c614d274e1f4da1716a1f300e0213f751bbad6f0e848759e7b5ba034e119aac5c8437b88525daf3bd4b5ec674c9ff37cd6e7568c90b5dc61b0d658e5502ba593530c8c9f82bec361061785636e75d693d98beb2e3ae31acece30c062eaca5ccd094996cd16f4e51995b9d8a222951917c81b3916b8cd36f29622cf6832283abde3dece105bf68144de46e5e5285ab79947629e71a4a4664871d233b1f30e1d32946a34558244538a6ec21886ad55e5012c9e142655d9a47388eae4ad9921fd48b6b22be86f7a1b7a6850ab7b3d4d08b474eb6af7c476d635d4ca080aab6c0b34428511708dfd1aa08d599952752ac5878c418bb724e8acf5bbb400ee37b7dd26478055b697e59dc0d8d9976d111f4cbbc08a9a92c2a5a6f196bed36f5b07263ade994c21d2d1da2a0b5ef3de41f7a138ec81aa133f2e8879f4c9a2cd0cc3730a6ef1f38084871f7605e13488da69895526b600e2a87b3b17fb2154ebae82023777ac42fa13416ca5e8bb3caf75bd6dd374f6ab0526f45241dc455d93fe033c3ee1c7b7517724daa1e7ad3b0e900d0b6b6e8b5963d2db8f4b233883d39160ce6b1e62867c0f6f1fb0c5b751db9b6ed8b03b93bd5a07973a53bc740a850265ff28c71e4b28fba038b95fe23029ebc93ca8ad45641f0f64cb714f30a4aaf80b39b88c79266c903093dea85da96caa85aa675e3a9ff1c1873a9b0f0ab1ea339dc872194f2cc5c79071e6022e1b99b54c45eeecb9723b46fae623b9c54f0f7a84a36a0fb174e5fa93ae76d59a3f93db2b407a0229dfc76a4dc893a2c67f6ec785bcdbb0fd4b9bdbe45335cf4e8fe9fea148f8256e2f349ba41b2da076aca7fa7cf9dcca4f061621484552a650340b3514c5b7809b9fc9e39513d12ab12c9384c624049dd8d18883d1b931d6a71a12fd1d836f1f6b14115ddea599703bb198b7c14135ee8b9f4e9ec0383f326b7dd6a4b0bdd59f1b7cea9c70651744b5e0a66363a07566c61e32f389954413f04af7bbd5260cb473be023aab8cbad7df36f24caea0b536f6379672c122e50ef5e28071b1d9c9366d31453dd4dd10b24d4b1d8f5bf713f099b5a0d6ac39c10a1c3e9d00f434fddcf2d88532415b29b7624f61f3919dc43ccac9df99f9faa373a7537af01ba6a0bda7380c68ecd8943576793fda583a34c56552be9c486b836133eb9764d963793ab6dd13cf1c660a0270005b5b1d7e32862324fac8eab48346ac7904ae7b2a386e4d0f7c1482c611b0f91134cac85e4b10f7b3ce1caa174a116871f8b5cc3e9a5a52ad5a3d3e6e7ae311e136b3420724f73b639f0945697466bbf0603fefb69a76d9e2a9dd1ee65d1cd73eff025f55ff03", "hex"), + Buffer.from("32240a7d7b3dab644976ed8988f3951ff7565656f5f4744fcda8180a71180951504d193acee95492ba4619850c1965ca10483432041ab38de73c18eb59cf922518f2225b146a575c74f503fa1f0c9240ff40566aefb5f68e88ac6e44d337bfce8958fb7bed1da7fe79d334ff129ae6df9affdf9c424ca7263e3f8539f54dec43944fbd7cdbcbebe7fc682fa98f11973673944bedcecfe5752daf7bbfb599935f1c625beefb5217cd5bace44bfd10f9417e6a6679f7197ffd52f790d720f744dbc4f61ce463cbdbd6f66be45a724f904fa3017b69dbdb86035fb6b6f21f72bf586de4d2047c1f6227af4fec7bbfceb7fc92083afbd87271dbca516f0dd11fc8eb506d72c39bc76a2ffdfaa9bd0653e1c67e9f144b51b8093f5080670e24e247b1c43cda9a6d254f2232dd22f461b66f8399923f4ebda10e710594d860e36007bb25caff5fc92af635f76b6dbfcff8b54bbab7fd685fdac8ec0853f5b532f766ddcdbbf887faeb4f644b2ca63f7f15cd57e48ab9c5ab5d4df06a98363bd42d3608732da419e5e7bcfe0b73f1d00fb8fd5dbce8c59ddf3fc464abe59d7bdbd93eb606606bfe651f6f206b74876d1056a69dde6c6650bec8ce45dd8ec3d2440112a1c86186fc54777433eb7a7136ef698b585bf744b3deca1c38980f2683d0d9e7be529bf9095cd320a412d18ace14915c722a62c50bcc433a0a17ccb60a200d8b2cafdfeef8636ba8928553575ddc5b56a8a22cd4e9211ab4d6fd0ad8222dd49a829241fba23f3320b16db2cbbbe287f205c009bc611766d89c9ae9cc90b1fa5c633425ec1ca3456b4f900a7078ddc024c54f72e4f45fcd0af7e8a2e926c56df72ec4597e07f21e01940326980071a618bd4a709cb3cedd626663d7367dbcff44906d49f62e48a8ef365d3f7308801f4502f92bfafeaa48a15a0f74cb9f123712a32af8187328d83262f9e32c0bd8b666dc2a79d36d87ac3cf708d5f2932c03afd7b8ed4a26197139819ebe9ac30f90fe024865dd38bc6f3c5341b347f1d6f7c1932c92463b1fe13a0673ae74a786b0a88d25e00b105fd69c25bab579f7e841e9f6a26a4496de9c1bbad334ab521c214f9661e3b99ca20c2f1bfd4dc1db7247a82d8b71d4150abeecff6efb02de5f93ddfe530b85ff05afbcfcfedf09976864b1c1e322b4c07eaca00fef07e6b2e2a57cb9b1f42c10cd24f2d65d956680213c87765eb82b094eb9bc7b9afb892b40af0e397deb1d6d29d2a1649575ef65d09385ae7864ad6be9b1fd77772a27653261fa4a65fa79a3bbc89af391e4a3c8a23bcded7cccb5b22dc9c973615b4996ae4be967d9466af65cc2b35455a2a069b639479a3c47a44a0813faef5e8928f3f07a98c56c26e6f03eba581e77be6c518da7d5d6244cc79c2155d9b0f92712b695842bd3197950fe393b2b25f32a034d6ee3c5f430a8afab68951feaba6a430d55f539e6fc4145546bcdad092d2673f9fa5c9075eb0dcca4b013ab42531bed4a9e8d21d5cfa30b92b5d3f1656517a330501c656f82ca84195e23cdac2a339588ac6318e204bd1c5f8a4883ead7931afec05e467db28dd68c2706be258623188ac914296c6799d5d8e5baf242e307bda58bb363f4741f5db2306fe78b9a485452314bb3946bac526d4b7bb8ea867978398837495d2952313d73c7ce508d95505d360b6942a0df4984fe8346a8aded815465376b40fadc4a243365b2b4a544ee5c1ba4055509a8b12ab424e6f9dd7c31b3a8bc0309ae2e6212e730d6d880b4565a8c6c136f11d7725f06561134a4e223543bbcd4db124dd7da17edccbcb4973bbefba02f95b496fdfa53690874d76a270f82d44ff90a73c8b6b63f95a52eddd3e222be2e7da34bd77686a7f5ca13befbd692a5c95e1043e2be14ac1c6c00f2467f1456860d6675a4190e1b2d6eccd545a7bb102b657bdb03fae0d5bca47b1379cad7aef33dda9d05e57f7451ad06b617c25ab1be6e1843d156657d07bc40798693b3e937ba7d7ba4d7a8fe20b31847b3d19c71bb87f265e44b7fedb62d65e928ef7b8f71b08f29e7d3a177571c2d39b3f5996ded8d5c0bff86a19a9a9199330c3b5a8ac4351d19e6ead7804f8ae14a8b22a15e7d9c5b335234201e4c1e98553f6cc57085ed27e4a2d1732683cba2102a6be792e17b714d93777839c13a95c541bbbffb27ba9aed395f31a3c168452a599ac4341de78e6a5b5119b72589572a32e46b01b0aee4db96182f0532b34928608a6385d63d3c7b7ccfa265e6f4d8d1f5472b42f07b5deb67d53d16d089d229ac9589a07721bc4ff719858956a5435a434d308996a0d015fcdb8578768cb1a08228eab28436406fe15853c727a542a66a1345d91ee17d639594526f64ef1e384df53fc4b71a16823b29493c536f2a74eb5c420cff0d0d0f57a255b739b7ddf28bde9c138aa38acc17fb176f9b6ff079ca53235848e4e9ef5df81b8f14b88a225bbb55a9a151c09973dde82aaaac9cfdfa4a93c453060437fcc2634175d71f5915dda347c5f8ada5d14981b6a6d9e0f5a51028a6e930d308930e522eabca28bd00b574783da538ea34c99cf9a632c3f698db339f1ec8a7d6dcc1dc5e418a37213417dd3698c2431f2c2f6e81d9b2e1a449c5d08d9e716cd30176ae29e7065683ab83669a91edbe3ade56fc6934be541c4af5dbb5a5b38398830255d04a5d8879a45b0c5820dfed831476a893fe0f07e86fbf7bab36ba9cca185054e3cd10237b6464743919252b2c928aff7e7bfbdd5e2ffdfb3a45d20756d5a2fad536ab5ad360495156eb86921a52f64ba768899d6b67d97c9a2d2ff5b60794d2c6bf34e0f407df3ce56cd97a19e796414b86de5dd5142b5e43ff67afa882c1b6b039d58e5d07a2622aecad026fda2fa9c5d835737a22fd422974ca85eaa0757dc6244fcabb3ad8a47f753480d54c4e93a358a81aaa1a371dae53258080a357eaaee04255fd55f2ec4bd6d49822011bdc81c93e3625eebb53e9b685daa6b6ac94abc284ce7ac8dea72e7bfb19649057b8d045a7867d1615267b17bfd9a2f451ff1a19fcb4d5f0f5acefca31a10b834fa0d3b0979aba6f7e1854ceb87ab877a084a8e59bd69a0cb3a64fbf1588fd146afea713c6c17c33dd7257c8b83719f5d502dba2e6f2892d28f24527bfdd1195fac6e46c6fd9155c7c61738a3e7b661ea0b5ac2ffd156da9f4d39619277230a5464e2d24468991f56d9e09f556d6828e15c89d90b38d6004ed56d03e3101b8a105fef0f2ac9204a38ed52c265d05499f05f382d2d686a3154114f3d756a92ce91de97614a673a790ef67c9a02691139a743422c2ee1b439a766623b153494c6747dc817a62e93db7c9a4974592cac1e04625ac4e57dcd80d7f745e6fdde829137df5b284c96e2126de885a594ea03211a84de28de949ed2a12dc14092c76aefac7563bc5aa4cf4c63bc9d93ea15c463539fecbbfba618e658c581f566075048cf55ba530de384e7280aaf0c4dc247a626e2d12b639cd6e4fa5f7ab0ef3fc482a8fcdc2ecad5496da8b6832e2df5b3670f318b9f6d0ccd32ad70784c1f23e363f39b99d58e4acaa75598e42df68978a7c05b62e3611aa07341cf87bfb8a9edb8a5819aaa5532676308c0a798f4c6fc5c283067e7af2ea376ba734bb13fc4008819e67a29878202124ffceb5595c56039209f9aaf938eab149994358400e315b8bb1a3bef3e2557327bb0959bccb4313172e5bae03c67be60555c66b9f77bb8d5a4ef04a96f39efe547a17f3a0bd2afe4f77ec2a1da01bc6fcadaf046295451dbcb5c5bd2ae8b03f0f1c6defac71e4d816127ea0841faed34041e8093be454c7cdefd99e1c9d87f0f4091e39731473faa4be9fca90bbbc08f2a795cd4fa50006f3cbd6b2689521343fc243adc3b1c46b12fb04d72e4728e59b37a7aa09bbcfbe78cc30cd2dab7e5f5f75749249b5a5f01b30f4265a05fcf5bbf8eb9b4f28f450c2206508016740a50cda49c9ad71c18d39e3950dcaa959351465f6e7eb9ab185cea5b762d3687da9bdbc22594b9578221bd6ad3956dd6eba954aa0b69efacad0b5cb61ea8cfcb664dbda3e556fe1d170fd48c258e5d7551911656fc7f3147e14e6edd069c98338d69742d9ae84f6aeeec673594edc4f32becae9ebf06cfd18cf4e17fa50a375e6a26bbc8bbfa2c30ef14a688c944ec65f43e59a7e56e22de258907944d433dbbea61da75c199e781ebc0eb2b62ca447fc01c7c6188e73b8dc7d72b9eee09488406b2fb0171765a7a219b97275f406797d2ad3c1e27c471cc379a469a65a1d230f78ae20e5c6b1488bd50e4e6ee1a2f25fd35fcd9cf75ac646f0f6dbdf7e685e292ddd0d60fad710399dcd3267be53695aa731e0e8aade72901532e576c76753fa2312784d1f0f6130a789e1904d848d0f8d471a062b8afa8e9d47821ceddd051c557f78abfc7cc889d85344d616a4b8af7c03404447509c496c4330cf53e43a2bd7c02178a187ef31984df2f50f255487701b7466a1111b1a9c43100913afd24faa8343d36644daf3c7bb4b27cbf22fed8aa5cf90441484db6525f7c69cb9ee399c7147d58b5abc4b4b5884e12d1823323ba7d7cdc1583ff264a33fe30edf41972468a846bb8d4a925326472d4ab3155ab9b9f2cc43d8447e79d065fa43e844fc43b84d2fc3dde526ede47b6c1a0ee7005fb08bd7877bb96cbfb4116edba4d7f27dab17ff564bf3321e9a15161b64a5861745facaf57e23001dd4926b7c9df0bec36a09607677978dac1fb1c3596e4bb6424f641592b70dd4a6b72e2b599f3a36099a55c36b1a7e3910319622828ee0e4dd0a7f23f66f0134faf7baf1dd65ad1776022736dc84d73e1b2fc015c3920c73272b2c5bdcd71a10b939bd6cf07e50a932cc0697098cb434ba52e730822ba305e8d68ca0bff6b05527b64abb46fefe9289157a3570e7506b3d618743d862877896ef97045c0624aa3b6ed3fbc6ee1860f59110614fe256388b69eeeee2664c8e50b1e1a26204e2e66f433cdf5d52b10e450cee82ca0f1ead483cf6e1b1e0c70a678d9a9761a17a60f05b45cc0b06801928406a54025569832b08b08765c40b0c76c7e563e39aa67661b215c456758572555b82f1b17d142ef428de2c408f8f12598f7ee97038ab8ca6600f1f09133d96560f69965b261d2216848430c817265d67616bde293870b59800e1a39b58fc30662cb5c0c8223c0ceeb805ecf13101a094e6c7feb77a20a598850340cde758c9180fe7f60a3dbe54f07b789678dc7213b1467aefdea6970a5d338f1fd49f769a03ee34fa7f8bde2478e86d0e430bfcc15d7bedd6818b88d590526c032c9ff01d56e367285d12a998819ac7174165db37668ffefc98c340d65f09a425ba4a55d5f0984069f6cde27612df518d32f5aed5e318d2193b5d9cc96ced4e1fdd47885ed65b097cf92cd54c00e9a9a738743c133954aff8b233aff5be0f88e20e7efbda94bbece020b491ba08342498c24de3f961e53a357b25d76bf484428f0f7fc1e812c9456cd500bd63142ca249685d9de524581d5ba7aab38cbe57931c747fcdfe010897a72866eac343a7eedbe6c401089d82b6b871edb766934d95b22c287579cb67b438f522afbbd85097eb1e0a563303a8aadfdd576db344684cb4b8b855b08614a9a72816a2c776899e7cd4d888c0de166899a24d8464d1d85a78b55e3de03ebdeef2d9f17167c124ea944b9a52df542471bb6669e10e8c16b1e96bcb0d83a4dea1ca6945fc862e6aa06e2ccecf55c91c326af550bb0d684ecda38604f5928bb106a2547a26fc97cddd3f8ef02a75a74d943efb9b26e2644cb67dd1e0d35efe422171d19086c49a784520984778805e7a874b19eb56d104f25a9defdce6cc39224589772c9f1f1f9f5b828f2cd0b2e5a402b948779711ae265549917d0b64df1ab26fb1ddb78df9bb9aa0cd5406b8de12d7075cf8a161449af605d9a87f35cf7596b288dba244d6ea96e1977f2c76bc5cfee28f439097ddeff3f5515e3b7d3d3eee2d93c2e429eda6e630213c992454861533cc0ad16281b8b00c3189eca84fab73e754c2843599c0910771bd0636a0ad04837c44514735125c23713db52c88fa34ad11974ca1883345d51824cb5b6a5b351bc9a157eb35898c276abbdc946d9f3216c695651f4fd90d28a72666413728babf342d76458d9b7e12989d284f8d396948240aafd44677144fbdc4f50b75eecbfa851e025cc60ba8d6b246d643dd340ded9d9104d771b2500dcc372a80477ab8c26f540fb955338b4ead04a074868fface6528d057c37bc78eda3640991a8ba2cff1a290f7842c2f40a1d56465e41ff9209a536442fabce4982153b83667f615fcc93c991467e94d0624b1354413ad6b0a1021a4497a4c3f0e7f24fc36bd9c2841cf2c89dcd4991da392ac4c0ebc0cb3f27ce95424d9c51441e27cd9682574e582a2d3b582574a8397f4159fb4ebf9119003dc634bc5aaba56faf6094930ea9019fe96d5371acf7adf14da5bbc3278c58c5af635e89795815c321ba54719c3b7cc5c01b430eba93d29a38d65c6d629b232f3e5066aebcdb2c95a0d042ccbe91d4807b25c2c8d82d78898499cf8bf2441728ad1c1d63ede66b0de70e85b3af054234eef2751652ec24a9219b4677620a2d02d8284784cb11b162f78d79883fc4675f8dce12487e325736d7da638e28fdbb56d80669d96a991d48fae5f338574fd8fc4451a0dcf57643785148ab1e1a0dba5c3a79131df30c097bda36ac1501134bd4b1ddc11553f7dad9eee30ad14cd610aa4f88646f3fb803bcfb9794cd922096072e6438d25f1e3fb65cc64d7eb2012cfce41b2231edd8b55618be7ccd1757598340ea4e778ad3e179d0905440fee346505269767c4b3ce4d81671849995eec588f92b6a0b43603e86906668d3ef0dfba14a4dea902073bf680277fec7182c7a0fc5c82064a2f3c63b4b2fa4b3ac2b2369618d07279355f53b9247ed066fa14acb1b03795260752899a14ed0436ca630f6328f261571b5d1d5b0c7e7cdcbaa9d996b5284125e0343a9a5fd28b1dbb58f60b6de6d0625a4bd99410888737d2a13a38aac084b1e46ddc1de95afc8f111b3e41372c93bcbb4508321db91e73dd8dc4364209200b7289936833ec5ea579a2615c02a1f59ec3be66b72f9e22c03b4d501fd886dc7dddfd8adb7d7df7f588944b9f8a79003090baaf31def20e47d9f9b540cfa4fa312cd6b9362053d71210d3e245679b8b8e361b64c9e4b41f1aeb4a7c8201e42d91dbe842b84332f01721956bdc26d4a945f930fc5129920f85cad0cb24d95112243370815aa23dfcaf628a513ef648d1349836732e5aefada689a8d975f9dc77964fda09bc518a88143e5e047954e4bfe2dafada9b2498d4f5ba8bc7c2d023485be68f56abab0f007e9c00ec9627d9bd97c1dacde86047030b90db4fe27674a7572abb6ce8d6184b91598e0b7a508554a650ee198dead3825a3358cd5e6f723c8b6bb326753e945c9263dbb3ca377c3197da148c812f3d44cbea1f8d487768d170eb72431aa57e423281b1cf1abd0d6826eb1ec50920a78d8dce5022f42fa589fe552cd26caea591fb64270e4ca53501f6171f1af61eeac09d437d4a7ee034615328bf8e116b81387b638cc895371c78d9f02aadd10cc9bb8d6a3295c9a75195688d776fb9b1320d75ae85e6dde392fec8bc67f91ba3f0cbfaaff96e79625eb958730d8c2add1ed2ed411c07c7fc8bac90c028aca561863372cbb36d1c6d2be16bc78b85eb0dc680ce92ac720e2c4c7dc38990f554f2fedda3febb91c7d324724d280c830f7092a97de5105ed4e084bbf9f72b1fd2e9f030f2481a27d20aac030855b752a5328d33366cb32aad854ef3e4ab5b0e5b2e826ec5832081f978b1b4a994ccb0d6d366b6931bab439670be3447b7b8cb98d71e233ebc95e07ce22d6f12d77ecfa6549b1523bf3698cb1343b4b63d75186e3975c988df360657142b0cee2cdae5da5bed9f39fe5e6bfe66d11ee9bca152aaf938e9d39096e1eeb233bed2e9e52d462f46de6df6c05a994a12e97b755085a4f630250e8f46efe21b1f8e1bbe08922894595a14d27cf39fc8dabcf61140e490a4d2a7e2244a9b70aebffb0f595b9271bb4606ba9431e0da27701da7ae18b7826060ee91bb9268e9438b8a48a1d2f500af752083e66cbab5fef1bad1ee166b6c1d753aa0c695b8c20d827ae529ba05ac32d2cec3b8e460ad2d2b8d450cd6b0e9231e8a345a5954154e8295641168f35a490b1e6af194e7653ec0fe4951ac50a3bbcb1a49be934edfc633f9cc67cb9eadd8dd6865357b366e4b5d46f100d6bec442fbb6f113862d07b9da9ee94c6f8a368a8cae4bbc7d566aa0686da3f3ec760d0672619100c258f965ccecd8d78ac1ad9a597a4909965cb53b0733cf53594ed43c416dd1b62c89ede5d4fa7c94f6ee0df346a509d729414fab7058a0ac688d4c73f1fa6688cb1f1f03b55e10d4a7620be3729c591a7c1bb826a40c1bb479ff75234d0aa76063694737f5562d483e8a60589461ab4f6268a21557b38ba22cc92ecfee3b6f7543767a7da2ebb1afca55e7a73a577d7ea8a7a4f1303daf8614651e98724d5890dd1b64779e712acad1aa77270941cf6282539f6a2e3c667fe0a4a42ffd723e6df2ce027f6fd82718241c4e46033ae282d18b2930ed81498b69babbf46200379d0f40ba8205b390b440f28d351b9f36051592fd61faac80c86747a39f4ed2be2b491c7ed6da15b956657cf19c84c426f1650a9821323c9e73bbc843ba724ee8233cf51c1e32de930c986a46490e00409edbfb2958d7b86facf2688c2eedf236663f3f4a6c5a3bb2a5b2d504d18e2982c7311383570ff6ce491071c2eb1249a4050ce799b6a0f14e1746bea81c62a573bfe29a36ba0b187186ea64a6292e197282ee51088852edda5c214d76b4edc795285b29de3b522f9d9104cacf165dd78375feb01949b14d7f354cd1b70e3eb234222ed01def12fd5d5da50369d6d701c093ef386d7891b63996081307befad440b3b4009c59693e65f37aa95fde8a4b3b2d8ed584207a9b213953d3133b8f16f36ac6b19233bc5bc101b63a3399623e659e86bab02ea1ca7a40d8937db5e6b5c90f70a3cf62bd03368baac6220f7df4ed703523d5d4b45802317c9274f1ba717441f537f9ec5ab076f6766010b280f7c343ffe6a16f1eec74bb4187a155778906b5ad5463a6064ee66251d08e27b81cadcc122d33a846030efc499fd2dbd130f38243b281cbd7f89431f725c9c6860f6a38f11066fe707af31005be37e15e1a1a2b130921953022a15996bda72cd7b99869f643329b57d59d0715abeacfa0ed898fc189a6b7737984b942daebdda384acab7645c7ed8381956f3fa732470ffc7179966ff0bea2c111f21c7c2eeb8faba805fdb0aecd47b384b5a96ad7e66aa8da530ff05181747c4882e610be500fa49a061d8a6b3763879d6b6b5a504cbfc6bc46979b59ba56d257bf0ee54184677ef6e49ece0998f31b94243dcc537520b965082333a10ae04ffca8f416bf2333eea84dea5a69b7179faf831d8dc6b3a9496b9e0a60f16ac0765e5b3d2039e9d10f231ec849fd59b008717df0cac9b139e485820d43a114cc672b2d0b98afaff2633bea1eb3b9d86f502b7fb3cf67834bf65ef3fcadd3d3c4e0452f2ef7239ce2795814d9e8c81431318d891c599d445f09003737ecd2670f8adfe01fc3fc66cf034c82f02a9953839165451ead4badfa98549e7b50d433729efe934801b70238f7309e7e40254808632887665b3be31bf4c3ca0dedd55fbf2c2014d49fc4ff13fc1981de3972cc47400283255f736bc8e4dbd366a3fe3c63c5ae57a06b015a281e8e07ad818d777fcbecb5f6b2284ee6d34bc53268b3e7f5329fff39d048a0d1a648da0ec79cdf42995fcdb0cbd0394312ec3694edf5371b09554813727aee0bb209aa84dc29302dea7af0c2b2c229555383e3b0b5b72b9eaa5aa44ef69903b4c6ee961e705ac04968880047047bd568fa1253fb6951321bdbe02a777a158366ed8c6f44ca5de3857b5bc8a44f7221d88cfbf51dc1723ee81cd8fe0e9d3dba62765c1014330fc70ad8b7cd0393ae279685866e3373896e81839f48bf49285bb3d3747501728359d9f8b63866d0036f8c3a7b9f34f3b5a94f98722767097968f361636b5e8d39bc5e3663fcd43c0c2fa1e5178daabb7fb1d797e3437f0a0f01c244cb929882584ad3246f5520408c52329899f5e22d520a667b59d1243f188a5f8d95f78d3deeb0849a1df12468b29232785551a39e59b0701633c752715a1a61fbf0bb5b11498c22af7bbcbe7bf89d8e711f4cb2187caac6b0131fdc786640ab877a2079eb4d34e90616ef0d8af7f2045a6e5ca6bdfad45fe11f42ffd5bfa2481a9b66c73545afb4239e0dc5f9f9c695068bb52701759a1fa4f37e3029aeda7456957e598bbe01812c13e710d3f2520fbd15c01e00f6ce2523bb70f7eab38d1897503f024397f6e1a2ec1f044794d76459a1ad4070dd41e7fe0a25bdff78f8e8cc1c974e61f93d47f30a685e351a5b10867db6fe39db392e0fd05e5e3dfba2e39f58d1f239963e3fe4c6b5f34cc0a948245b10b3add568ff7c68fea1891f4b3c08b2b4fc42e34a820e1a4cf9a15f3b2c56658df9b13ec3a00163fd9766917909655a9abb9d640542dc69764c9a8bbce42c4f3fc524ec861a9d3a35e0cf05d806c05041f9809165b5494a63de63d4c716074e41b5308a147353fa99e81745bb61f6d435b5cb67114fa4443c90d2d84f5f5337c3f24220e02464ce331b8e9d780431f57ad25c4ed2aa4e1aa1424c7848a441c8441ecd109acfc9c7434119584f21a281db039c0e580d44afb6bdc743360038f8f31e0d38419e6545c2d469de625332364dcbc0a9bdb290a49915e7cd2f1b9ec3e739c068b5529b2a7dba64798ea5e796e4728af769b91729eee3fdf285d2f53bd0757dd64ff64efacd2b7cf3aa61b128216a9ba9522629e5936a680244eb9c547563b433163ca9b49039834d34b913ed4447b270ae220225399c0fd8fc033a16262502d27197fef2b661234cd24aa21ff4315a041f3c4dc2431a36ce6d7b0e62564bc7a60ff3cdf851b7c7cc7dea973df49aaa046ca4ebe718f4b4c1ca06476eea8dd85a20b372a5f767a36d98cc4caa19ee3f20bfc0f77a9f7ee0acf486cad013dbca5cbd9aeb89f36f2398ee1c64792f8c20755eec9b62a2441ac8241e6d1471c347bb7247a7ee652745f0a45b3b96c39f155d9939490269eb1ec700350b2e3fc30aa35a07082c32931f22e1a11928c09a2a3cc4f29a710316b2cbcf1040411e3ea6519d658a26363e81b0d897555ed4ddbed6e1163ff9c452470f5a096df7e863c2ce4e107522b63c35b235b8b0a63b7fca5632e7dd7f6d7dbc48a1e73cbb15e9cbe439be517d2251da5365bab48a6ff30ad17e6b77de3bc9eb96d61f7108d6538f316446ae9658b4bedc5d0c049ff566538254f422d3a3371ab3e88b383b626d41c9d4243c6915b8275f4bec105af7a4679c8ea8d167ff9ef90421aad3e4bbcbc69b5ef6744ae8c0864c177af5cff2b8ec8d1a04858e03223246b50913efb2b129141e65dc59cbc2f61b09418255fb37da928ab4b2a127dbbb26db04f5ce9a6badb8528e64bf2f7c3221d51c336032615ae21916b2cd177b560863b66679ce935cd0246bdb079c9b45d783f42593a4cec6b945f4c9223cefa30210edffd41f8f8126769631f7181fad63537c5e3d150d727236bb53841a41ec55cd67a68e65518f0f76532607ea076c7383d166b7a55a4a488cc0f9b6597ee283f3c5e0447d041b5d26a68253ca6d920ff01040ccb81c99e2e9d4a869d51e5c1a2d754c681da6ce8f2e5d0cd25f52908fe2a240e16c94356647b4c224b4ae214b4cd60cf92cd4877c8168de6a038ca13bce3eec00e42d1f3e79a27a91e899d8db714a408b90a71c3e82b07cc0c7b7f82bd5eef3fc30cbef1a1e002920307ea6e0b57149c1266d9ff281ebc7b4a15d7d0c1663e4aeb178489c89af08895ee243838f72c547ee6f58f0e9ffa9ebe47fac92b890fe6bb3ffab8f5b6bb2d4870403bbaa0f2c788d3db9357bc3e0272dee656a8beae16da8cf1e50e398b4d56ec6ca66e3e33b9e5ab36b013d9d95df4e7e5017fddfa5a9a77fa49f391934affb3c277a8bae5bfde28c6c8720587c72a273462b7ef910cc7f534f6fd85b18d6833d2ae2ffa6ac7a84d32690b38ec8f028bb93f833d82f03c2dc41c9289eab5243a7fabca9a9cddb5cf1a06aec993481e39f109dc3dd9f6f98e0fc5fdb34f62f705aa55b8df71a5d114b81f9bfac59ecdfb4f9e41287722e884da7231fc41c4d866f98daab50fbd0989dfd84aaa98d7e4d9becf426b79e28d9b738d2dad2bf21df8019e7f77cc0fe7b7927448da30f0eba4af30bc23596a780ed5f67f03081f309cc7c459e9e533144815a658c1fd3f251f9a2c9cce4be2ae36816d8d99f80168cd6c061c06600f706d02bc94e0351b39637e346096c968b7f5cd0c33e4175ac0fbbef8a6e07c4d4a61c2cccda33bc1ea2d09141ef134dadd718e85c78060c186afaef8fdfc77efe3e370488cff8c6d80746ff05868e8bb56a7780d1da095b42a7b08a9620b414780ab2bf2d2777984142c91c969427ebd698df5cf23fb4342d015e37bc0fdffb32b7c61607f1e2f4f2bcf0386f8c70428515f5173c0a1c6dfc969df0dbc64e1ad757a6cac5d952d28c1247a7532390b1e0f18d50dfa2208fdf0f86cef37ea548d36260ca7f05d07a06ce0eaef17fde3162b648771b6219f3cd7642273f789d5edb19c7f6e0674e23cf9c46c455834721ca80fea0e7258271438cab838f13159b89a25512ca874b171fb0e7ce496ed0582ab6353d76c0a4306b55d8f797e855128e61c85e57e34520d99fac6d95ac57ab6e059991c632a68f7c7681c7c9ad15a0164f7a2a1953dd0c9881cf3602d6642e9509b16c20d2f5007ed041558727cf138653b2f713db3bf9133721d727ab265b2cb165939e071fd1671e0b82a30dcd7f36ffbdf9771e16ff0f", "hex"), + Buffer.from("15000a6be0616098c6c0c070024836e89732800100", "hex"), + Buffer.from("280e0a8d5abd6e24c711ee9f999ed9d95d92bbe4ad8ee4ddf97ef6eed6026d09362c085e082216c2404c6507049fc070c02770a040b1e1c80728b02f63eaf4a05cf01b5ca2d80f60f801dc55f555ff904740097ba6a7bbfeebabea5e7e3b33e6cc1af36ff377f3ed7f5eb97665fe6bced734fe64ceffe9da9d3933aebd342b73fec2b56771d6b5d76667e8cb8fbc62652eb1e3cc9cbf75adc1fb2e8eb4f3da9cff83de56fc76c6df0ccdbec5c87b77783778bfacde797cab3499f30b5a656ead8ad44f683e7edf41977348a6df8f550bd66947d492365fd29a55e678823d3b8caf601707da47989fba535aef4e97fc77cd7f3fe3bf5ff3dfab5bcf718750f2e078028e1b48ff9024ba56fbbd84851c561d55961888fb92b92f99fb92b92f99e39239d6cfe74d2923c9b0a337b188739ebf454edf19e21bff7a96f9235d779d3dfb9aac79cdf2b2df5f411f5b59f9085f9bca06fcb66629e8af299ed7fc7759ccd3b3c8addac537b1bedb394f02b08936a5402bb86c05975d431451ec7b55cc55c6b1ee825df7ee2f6d9c7bf7d7568cf719b3fd2c3d5fdd795ef3df65fc2b82aef90b8b2da16afda7ceb99db1c138b2e6598c21b2a6f8390a3f7dfe47c7ee3f81f42dc2e14013a858fc326bc66658ba0b326ee4b18b3e7cea4439a3462649c4b5fe20ae18e25f5154d42f448fe4fea59e175390cca37156ac1a79cda2a0148171b824c71ee774bf0489382eb20f3428140656f4f58203cd05b78d19b87396442aac186746feece2d04411b0c41b55935ec95f6b9df0ce44dbc61d64e16ef76446c1cdf140126b4e7320b44099db423accef8bfd84cf68b66c0017856ae2c3e0a260f44296116bb31071a998aa3defdc93afde0cee8948b0530914a1ce20c135e71f63c154d1f63ae7f6cb9ce93f1953c1e8bcf066142c8a08539dec92c364c257c2140edc25073e24217f64d28625635b78483aa3af97958d789c39bf14add9ee51041fd9ed0bd755e43ad0e7c459106d47c8392ddc61124b81b842c719be3e431eb85c8234da8ce6d1a520b15fab44143c649538be165996a6b0c2a558819d3dad81ba00c701de6b2189cfc01cf705f7c3ffa21eee87e0e9ef436f10e549d1fe3ecaf79586fbd7ff9cbd6519cacf2663cda50ae6004a0e3032482049e0a8dd66e2ca6331df3a9acf850392e34d382041de7cf5a68df6741a3dda19fc5478785a97a102c02739e814eb4c2e898d24babb100da2a1976ce8251b3afe65b82312df33cfef88e03daceeab84f7afff397bcbaa9b9f290038a83c4246abb6fd40b078e701da84270433627175c0b36019fde2e6f76c86f7ecdbf76b21d8d05c843979403d23937c0793880da3738901a16564f837c937542829b85fe7225b45fcf75c086239b820cabb0ba266cb0f3b2f53df18c5a23e617474e11d8db59e5f156caf8a3a7f55d4ff52b4bbcfeb3bcf57777a87f50779498aa61e868b8ae37ac8c6455c1555907df48108944ee9dd25370a7f8e7f530e091a320603155f3bff23afe63def395ede73ecbce75893285f2eb58a0c3b17625dd9316f6ba4bea8873da380840e57f3e8d7aa47297a112b201d299431e20772cc3b535033503bf95c66fe50ecba485e1e76864b0bca722a800807597ed76210ac6ca49685c057779e3fac544de7bebd779f797d4a12c50d7439ec7d64e2cecd44417263133b94f8f84272d1ef5c07e5e1e28e8d3091c996365348899d1ec4d95c1a076e745e08c82e31f94ae86acad338c4f111af45256ff191a269428080f766b48c177f121a5f802449d6f0c89f3167b977f9388e447f2a48f352767e1e3aa56fb986df9229a6ccb8899f698ce2c8ded3f8da9176965bb5896c028461cd5e305b799a41d6415e27911ed13a1e3bead7c8c20fd0ebc585d36069660d86162cfa9ac629a69761c356dec47d066dd054a40951b1d06d45bd4724f068b7e0c7d4457efada61dc806a2714fa48761ec7b9bcb641c4eda2a296b8caf449dc1488342c58d026eb07d07e22c2f5358b2eb2d82b59b0782a176ba966ce1c60ad294891899accb0912e18240e8422081fc034f81822f7199b4acc809a236b356cd00a78d29bb94a641c677dc00e32b4d8eb3930e057ea51d53ff0c144a3c5aab8e57e9845bd0e59ac4838872cbd503a29c22cdbc1653fd0d67dcd06d9b3e0c531d46c5481168b25f6c1705adb741219d2b8425b845516232d9b9279619b82c74ca2732f872e5293c5f57759cc64ef734c937d29ef97f03d8c77088e96e44e969d675b8241c76e85b01d54134aca61c0b2beca5eabb823fc96a40aec34cda93e2d0254f276ced960dca7794193a97af0226dc8cf87000e4c13d29037f68ac826bac0a4909295167f01d213b8f0100e3e2a360704790ba2b09ebda5bacf67a885e0c569709a284de18523d9bfca91a61c38d700baaa92857443303a0decb5754e21c58e10fd2e1babcfd1f4cb3a455a31ca6101f43371927857683cc06a35da273591204496c97f894e74dc16beffed2d71300252e0c2a31c092da7649c15c44ab19e7cc2f311ba4b407fc8d9c0f9d820a324220c16272e3dc76a597066ec3e16dfa9a3412350f4b09cbf57898024c977559ceb5e505e409d41bd999bf40ee2d078009893e13991b25b3fe60a468b1a5857710f9c26c2b8c76a05f5012c868c6d0c8c01a8cec39a858f8e8232caca8315040a9ad280142dc302710acaa9020becc4e2cdf18af26125c025b87e07fa0ab1b14d629d35027ce1b93dbc03d75a28d2c30f1df404236d6bf68b9e8838fe5a4e485bbc361a2d5257402b088d230dac8ae164d45a0fb9b22b53116571d15fc9ee634505ea6534c0ba32b84b236a5ff8f4c3ac033eaf885a0a75e5887cfa181ddca0dd8ab8d7238268ff2b1e65f721e626951b7b44ba2fe9b309f5920c01141260cd45297580451721d47db909e174a8c6172b5864694858f7d1286d8b8a3f2f04d1c6c9e75c6bd32d120bd2261817115a8c275003666a2a144905834df911331001548b321d18f4b785638e359040dc257c864822612fd8e81484004d492ec565515eb0ad45faf882dd43a46dd205157a1fc1a5f5dfe66039c8b22b855c20f7539cc878085914e356800760dd54bb3e2dab0af803aadd22e542ea2e90f42ef1a47adf851b7a7ca1392bcb978ab0f2fa2c57912912bc287d21d3f659a68605b901f71c750575dc391e67ea73c4bc0db7b04dbb8ab23f7200ea065c7c3c4fe4903c569ccb20ac45748f5775d2e3882a871a6618957ecb1871239664f287c852e066d237205070ee5ce5b6208c191b6dd189977c5df6c8849d895e7fc35f3faffb769f559226bf834445021f8e457797302e776113c4775bf73f5c96c86c1f8f553fd36b4b98c4de26d26c052d720a10d46c66d8d142a2d4d1523e4240058d3ad93b54e8144ff2b688e42e158c047281dbf4067c68dca022c9965fa0f338d59cca89d8222c6629ae365beacb7d76789f18dea40ea13a12d2f818a4fba2c87169847fa76ce6cdd6c1a3b7692384517cf44c0e62db5c3cfb5c6ac9da470abfa47e945ca8b6aa0084eb23759fdd7f4a254c11b0c1054151305ee0a700f8aa0be95c5b40c2a0de8867f8b0412a78b4eb7a4c1aa0d67345888d43bddf8a20253a68dba0c78e79d598a5a30df1d3a0237eda3b158bd61aed15d7366b7858a5c389267046ed968373937597b34d3a18c88e5735038ea38aa41e6b26da2ec8060a066ef588fa86ba482dc2f368306e425201dda473d723b47e381c4857a3d5c016713dc1e690ee57fa510f859bf435a5d34dba79c8037af449218686dc830a9735a11fa7ce11faa4b4d4f69b51ea86cdb048fd6326df82c06f34bcf496463a455ab6a8bca61b3cf390401e46bd5f10a5168859edd7d51641f26492dcc9567c8855cd98ae6c3408f7463d0f907a9b6d3600cf8c09201b7077f77027dba04c2149f5989a8e95556f7430e633557186a066c7ab7d4516799b8e7289351971dd604175431d0c8ba29d7bc1f420178b740119d12ed7a422e36dcddae3f2e52531d50ccb9e598cb931e833accb4d6a7d6d44dc6eb423d343e79c451213376005cbd31d482f50203d96c6df3e54e06a8cf84477448ace58891bbd5fd20b4ae5e60bc5e663aaaee97e329df8baa2930f58acfd3af089033da4bb06e63aea790267a950c4dc26dd0bed334ce84d1996a00494bc436aecb1597b3357f589cda85740374555c84a5b9856593f4ed012c4cadbeca881c04002a068cc87b13a8b872a57434a743de3de1451a1479d6842217b90a36dbf3acab41264db6cd2a71c2e5ba77081cef630a4963480bf1f73cf7087e354069e4d45699b6bed44ce2599efacaa82219f65d1efdb316107ca508ac84a06ed66121a6ff2555d96092de6513e9a02b2f4f2605f612ac9e1b21cc735e24bd04b9d51f6fb14f4c26f86d8b7d98b471a531c903887a43951eb71d18e316708a06d1ebb4f52bcece2f6906d16a376bc7b75bfe9f8821558a6e7f974cbfd2c736d60ad906e5aa55664ae7298d3be4190655addccf8fa7641ecd5a5e2df642f48a849bce0285d74b77a0d3bcfe8da42188ba0ec60fb46b1501a830e181bca43ea5ca18710cd26e829c4e11f6642f1f386e69d2630ae1b0006b1a1dd8259801c1e0018b06c82f0694739a1d2b8aaa31ca73fbaac9c8f82b061ac702d802075024336b34b890015fa8cad0d283692d1211f60d25522315bc0847370d14b38f9a54c5a692dbeda5927ec231e7c1295cbef36939e8d5507a47d7c1bd239630e260fea987800b27cf598ae327ac58a1bc501fcb2a7c73cb2680fa93a61e2f2be0e7e48900a80b5453718aaa3a8c6e10242a3df474992e8b3c9fac5ff1ce11a6b968dd423b4ba5140c9e6f0c6b14c6fa7119e2f61efa596bef23e291d2616b92ee8e99847698d66013fa8e92f228ae12e9b1a88f72b99e5b8561e7a06d31b214d0d747d3e47a6fec25afc94a3ed18ccfca5063b2eb0f275573efa2e6a82ca4eef9fcbdfb4b48809f8e0575b94f94f0237bb013f21e989dfba82a6fe94334db41562e4f6099e7b5677cc215f59e0c39322fef76ed1931a2f3f687d81c3869e6ef99f11ccff01", "hex"), + Buffer.from("f0220a7d7cbfaf24d9755edd5b55b7aaabfbfd987e3b4bae663c7c5a8da4161578a8594cc032c0dade87c60b16c6040e8c09152830189170460c0c27021c31b2730144bf50812160140a330ffe039c396066088e181b06dae77cdf39f7de9e354c82afa6bbab6f7de7d7777edcdbfc0f9ba6f9a7d034ffadf9cfcda1899b14a35cd221c44eae2bbe1ce5e5a5becd97adbcece52a77ccad5ca7c310f5daf1e33d2f1bf934c8f5822f2fe54b7ad38dadd59627e95af2bf595faf652dfdd617f2bebe5e52331b085eb6b6ce957c2097282f37d5f203eec25206fe462ea989f6f913bef953bec9359e9bb4026b4e29ccf272b425a10413e427f6fa964b445e7e5cd650392ef4e614e5adb9e3528382b54f4783f6c296fa13aef115d71878d9da97280ab41cb9a20af063fbea68afbf4a219a1ebaa2d69f981a27be9c4c84c427dc64099b59616d79576f3a5a558f1aa93a98c4d71ab25ea1062cd9979557b65480abf0c1c1047f668f98dcdcfcda549ed4d52e43bfdaf2a6d62dceaf465b5acd349957b6aa727efcb53d696daf03d7341dafcd20c1ae2f4ca80bd77970ff901555c3011a29ae45a5cb251da3d9ab33f45f9bee83b9b023180f677ea9b707ca9ca040bcfbe521e24aff0f73678b84e2d7231fde1e8ed9ea7d7182509ef0a509550369552a8b627151c469c87043b1810219864564fcc6bef0947a702f09592cfffe913628785a7bf04fcc90d15e9bace633bd19f2ca957ee6d04f0547387c037251385d1c5ec98711cf1c78ef70d8551a0fb37eb7a728111ee1d67740c06a37a829b88c1bf0b93106b5e737fda9cb798e4f432a835e27e8ab8b0ab87394133fec056557396ec88aa40ffa8aa6ee014c122a7cf6900d2ffa59719f0b43f927d95fb2055ad7f1ff13a6fcc53fe54f566a65864e40a7cada914f4d833d7b83a0811dbb73054dc59fd6f6ccca1783dd6c39635b96c877155f7416f9ffc0977f9a8d57b67214e4e014a78b01dab850ca28582aa5c8abab434e7216e28ed3de5dfbbb0d22333b59538c77a60bac226a1daea381acf00e6fe1c8d9f574959d784be31ee298af0e39fdf5d9eac174bc3d9076bb2c497be62e9d7d43b3887099c00b73b004905dd03dd450bbe90479faf0a6691a057d175d8058790b1d5061efe64ad5aebb8b02b68ab250de6ddddb9938725819b2d6fc2d1ba32b5f752f71a9ab178172f646cd1ec3d9ebec3ad6efdfc9daee55a90ea2fcd82c651dbebaf695b0d2dbe0d8f1491541313f371615b8befb8a781c666b817546eb9426f96d2997344e6a6d413b0cb741ecd56679d4d552ac15576c16eb851cbfd05a1c6e45860f2fd5fc6a9dca662ad2e537f194ce18d2d7e92d320a0555b4e004d0192ff4e5edd664ecfd5b45c68ad1eddd55259e982e304dd9672a5bad994a40cffa5359cd5d26c67f2d3285333fca12ebad2c058aa0c11436556e3515355568897d6ecd82b0113ff9425fde15427359c8ecae819df3d154b94e0656d9ac82cd006ecdcd5dee3ae33c713f739b306a6a753dd3ab3a42f739425b670d9a0dce4d95b6b3a58bd39bcc85aa5645afc3a22e1b104745f125cbf58752a1a26b1007d09bcf432a29bba5a3931cf0749e2443a52ccd033d0533990ede4dec8c33ac6676662292bbbc8209339a8221c6209f7795b6bbc3190f3bb151d6cdf0b661057554b0b3f346c1021c9b78323b0dc335987878d5b88eae343f2767f8600c7fe68ad142b93345de94606c094651f74660fef668ced6de21fd94e4fe00795864016ad169b16bf83e9eaaeaa40efa6076bc4a60543a8dae36bb3faffd1b4941434985a62f2862c52db5fb0cd59d93f9974870919e37cd2f05dcf3ad5e3abfa53f3c50151447bd60781572492e425459de19da32d0d6f0c34d24684ae4e56ee4ee338f0b339d8805e30cf00f0afe2eae8a2b17975f657d8e5436e4784f39de57727422879396393e571810be4b13459494d7adea15bdf72bb7448efe8090d9f07da3b0d815fa0f11fab6c0426f345389a2cfe1f64105b2c5d56f27d8471dfb423df8c0741ff466b93e317f19dde9d399c12defac89d0bdbc389fc62ba4861ebd62366b3a75987d9d4e1f18cd2c6deea05de5839587f845cd660fa68ea1c257c1cdd9eb5cb7d974fed5a01ac816cb55173de7ce1c67a75c4f32b8433c74f46b86bc2b9b06ef4a5c7fb85741c00ec8fb072b654d66b186e5f1ceb9632371dc557833435f7ef8adde785287a978c69c7e286e55e5f3a4107e893ae12c73477a944967a22050c4645a7bec6007483a53f3ad3ed3bd1f66d6186ddda4e9c3dfd164752a4becc1735ad46822f2ebaece3387527caade44c276b83e6be674b5eda1140a83bf2f82dcd1d214a2a71081427489969d5d3c2b580e9e720ab7abeba9653422aeddee25a11f8b4b251a822ecde8599534f2e5e1aca25ca51fc8f1c416ed5c1ed745abeef4b6a1bec7b4334b749676ee58de78e49c77d5de1d442d3073d1fb59b1e362580dac72767cda7428c593cf1dfa2c70ee41bcd273fd711dbe391560c6af5624a54abe0b0c0434907628aef8aed74a958792192853dfd5b27426c4a8ae227aaa63e1c08e8bed4be69b2ba7ff4a75b49b1b2ad505acf75f82ef92eac7fa77f00d53c4c0ac38b37ef09e7bcc388e75d1dffbd73fbcb750acebbcb672e72a925d1ff6b2671056289d32cfc0aacb88e3205daaffd0bac3a1cc73cfab91889158f6aa1cb8aae3ac97118b749621c2a18c4e33f673bf589fe97363de76eb5115ce3eae4ab0d60419de02bbdcddcd1a6652f72d3be6e2c62d506a4bcd30c8dc87f32eef503283bf540ce9c33b70f2abfc48bdcb67caa9dced06d9d8f58b22a4974b06f80a607b05ab8164092a513173899bae0a112302afe836f6c032c42bb3a7cdb971ae4a4cead4c31fdfe9e3bd725d7b94d78ff7363e54ebe3ca4236e77583e3302b5d5609272f7626dad9d2a99a10ac0853f3d17b674f29995eb2647a8964f9fc6573afafdfe845effcaf9e3ce9c217e9075ceac58c5755a66011c81930e569d854a9b43d0fd8cb43550e545ce362e8b7efe2b5c5d4700d4104fe1bc27fe3f0df11febb267702057d2f34619671d749e781702e8364c17a2aa275ef58382dcf172cb850f4fdd9bf0f0d958b3af8e043a052e7bb50c52ad7090913f5e81d14d2da94c8f6244a755a514eac5cfc92158c1626a569af9dc159e2b361433d77811f27b359dda997dbf380e55006223e71d736fd77efd48dd4affc5fab225aa78982a9d546dec967dce723ab4369b060814b2e76d21029b06dbc3d9a70990e92b73a3e7fb49b86cfb0f7a657220376fad0efeee1437c7da77d259b8f1d50759ef2de420ce7ab8a411913432e07c078975ceec430af72f8c6ade95722689a1f4a3154d1e22dc69979aaa9fd705727d53b56bb2642a2082bb3425f29e5c2fcae1aaec57d833e32d679a32e42f051d40dd24e6ec5abfca7f14512224225db4232fd1718c6bcca653336f71003dc2e5adf996b1ecd84fe169dcb2a11c94e439d20bc934867dd760729abd2cc95ba0fa944bffc17a2ab508d68a1db0715f08032303477d8560869a8cdd31e683b04409375a4eab4dc9bea363627729fe40e75c7a19e0022f35699a2aeb09897bc43099a2947c79037e9cea5138388108a138614f3a87c41448c2a67ab7f225e77edd238957571d1818c8b3464268353d968ea40d34270b97e367caa9afd7d70e640bc0cb96ac42c9e2d4c655c8c80d3b7317e2bf832b505d2a4339a27c3c015bd4ce643f76142f56bc2b5f80b4df4907b19a2607f29065d1bf843a9034cbc217d56a05665942c190bbbb7502a1f6d5132a07395f504c1b7aadb432edb99963a745b65467e36b4a62d450abd7f1faec505e1886a2715027f5a4a32aa246f9a855d5e9ed518e0d1a645e9ce8c1e4c0fe01d7f4020fc2cf068e863f6956437e56a5cbf2822f87ca92e945a7569630377343c127fda252ca17d2bcbae105f4930c97bf7ff6525f7deff2c7c1f7f962ec9c73f3336e8bcdb4353d398e746a8a4a573ab587ab75862970111fa200f95127eb19166777f0ae8e5df9ef14ba45d9bc27a0c7d858e3881d645dffd3e0c023c8bd42efbfbd3461e7f7f8a361febac1d67670decd85f8fa1bd6d1a28538c270b6958830edb3828d69dd6dcb14198e21eb9b35b446672903c521620fe306a5e194f5eae808ea907804df0136898560f4b32c7e10d1208e1fe7481dbda5bf967c47a5693229f83aeef329188eaef90a1b96617b9700b150fb0abdca2fa9317836a6d17dc73d508f1f5229aebef4f833c041e30e2e3b168180a5f46b75ba4b3ef859844c7ed2b65326065e1d235f1773ab1ea108f0f2068dfaa83efb52e69670e332c5d04a52d02d2e1453cabdbbf1615b5afc0fee2242b8737a919e4b315bf69528f6a7955a0a2324298c064a7566a4fc5bdf40a6f2b5fa126c3fe41dcf06199045216998ea0965d0494bc561f03c8adbe1e42dc45b7daa8b70b925e50d28c70871168f661ad4bbd6e95114069bfd3825ede5f4568ef3ae051c3b2da3f04e0c0324fb2e61b17250a447d309e217a106be2d65673843db1c79dfec8b540a03a724e51bbcb5b44b16870eb5d3724316180574de6a36552f71f20a05033e39ade2e4b8a4d1a636eb979a32efdba53258ef7a7cb422ef2157d009eb2d6f5564bb46fb6cb05cc28794783a087c412affdf4e17f4aba3b61e11ecfd03f837e2e8f58ab57abb817996c968d3c8d0e14c4cceaa9aa2ab951cd16ec79eaa38b50c214dd5bccb107787da2d46210093a5905701088094f6ecd32d1cc1ec02f2378b2f99a097ab9a091693360491400c588e849a50ffc0efd8f7923825af104c376095a1b5dc16ae1f104e2b8cc44b17f1d97363ebfd7cd82e7efe4afc44ff5aaa9a217782b013abaf6357d6781863bc0d83650c7e40a554f6819d1f41ab5e684755b28428c30aa779b5fb7662cf380c134183dc90bdf00f37ba07c6f98fd55938d2a843a4913f9cbe63449132997fb534bae31fccb1370c2d2394d69a04e34808695e0ee2cd40df200fd0da2669510f03aa7c240d09ebda6c64261074a3261fa9c7ac4b48aee3dd1bd5774014ea3aa736fd802d942d8644d55e53d94c8c2a133a56a04a8ef2d609321a7f75efd3d658f8d7c8021ef10ea4ae88ad1925fe7292632cf8ccb173493c06be287ffd398911832f2e48e88a013fa060802d65734ea1f21721c816984bb83f1fc7ea097f5f1a8797183855e10bdf95edcefc6acc164c0b4b05dd65fff5c74703ac9a597cb327a0d45187c2d614ce61d5d12e079073cef0ccf9678b6042404780c25e6b1cc0d53a2da57e1e0952b6a04929540884d23ff1a3318ab3440c0b005b4a5ee14eddd010f1489566ac0c9639d4e3fe8d775192a1884e805232867b702c3c2c6967f969569e5af7f2edf3b9d82bc4c7205912dc1e191b7c80ff2553e590dd9e4b7d68e32b84f0adbee07d61a2b7ae48a514f5693549498e8ee1b14848c31278086aadcad88d61d52681298557fe07765b8db1f40d52ab3e137970bc2541f40f2103d8e5555375a0e5a5bb8a837253884e6d7a5f1d0b6a2d5eaaadde055788e98e907c024badadba160bb3029d5d9b8ffcaed57f1b097f0b097ea61d527627b1470aed2d1fd2a58b44c9628c5380a598d205a2e3907504748c1a8838920de0fc18abb38d2eba8f6e3de2ab75605e31b607c6318fd13608c19e2cafb4a0b851635ebb2d2c7f39380d86595a15e2021ab0c65d953d328fdbaf96b2e193433a40aafca3048bced0ade1fed5face20d1ce7067c7183c878815a8951a5c60f5e120f4617502214a640542d1baff2e45f0d3d1c85e44efdaf608c74e5a537b955a6d1101aa3ee848f1ddf97f1c5ddb3d074cfa4ab7ed6c7174c8370c2fbbfdd188f263863cf30419f1003a3c1b3fd72e3dc2c5059524cd6520dae1ef0a3581d0aca009e2a802efde132fcb27996fe70cbcb5fe9a57b165fdf3d8b02aa655e89ac4627a40b49371bd096f9bb9040af4a1b3d96e18b4688cbd755412d2ee61684ca324250d86e28e0ae3ea3e6486dc9ff14f07b027e4fc07221dcd7f1c5fd7f0aa86a2504d0215c1ad56a98a8c2de295710821a526e7ab96f441a5527d49a1c9246f56e146bfe5c524066c30b6343eddb827d64181b852820ba670af1b784f85b42948b54e9626cb9693c911f3a740af4bfde2a43a320c1b36edf0e5a71070508c71c211281c2cd0ce840a093f3a117dac0b75160e9b93acf332d729a26fde3a19717e91fff2d2fff4e2f77cfdaf4f4256e7a8ab4fa3ac4170e4e332c2ad37555d091627200c5e51bfb0466fec2c18dfb9d08c6fe432b3470bc48f942ebae2deaae2d206d09694b485bf5410121ba1458fae653155ad42736f69229a217985038188bb02c573a585ee3b6a42abb71342b41a39a6d8c8c5e2410c4b30318427c0b1cf16cff42647bd17b9914d1f9a5524bb6f47056b1dfb06a937f6a7ecc8f9a90a7342cd6f8ce2abe186e6f741b4594dd41b4067f551e6670cac36cb56527a80ff89711e6a3f516adbe17cd614ff393da573b24f260f4ef294962a67b561e1ad31ffe1926ffc3ffe66593d4deaf4dd18ac3c69becfdbfbb0e7aa1d0a65fb6786097f6b308fd696d51b442ecb3be729c6bf152fa0a18d6d36c4064179841616e09734b985b8529da8a86512d33e69e1a18b5b41996e6e3dd472d89507438c85515aee096bf882cbc3876ba5622fab202299a4c0b29a3530d8a761c9d86f63ff72f09ee25c1c94568495ddaf5579ab9099dd5891e8321a200ec9664115423b42c52c17cf299829f6af91559cd5da364c8a82f809a2330a437a433492a11c17e1eebf17511a731526dcc0ba395a828eca8cf61e9adea4c3554baeb39395e6572cc55ec8f8d56ffc8a15e4a086a584ab1daba4ba5f1660205dc5c9202e40a0eb8196f36cf6eae0ceecdf7cf6ec274ada17a335d6ff582cfc71b70426cbcaa84e325d5f53a57d6dadc56e3850bc168cd4f56f88f8c59bf382f805b934ab2ff87bf0a68af95715987f92069b57f7169b40ecf898e60b34c0581575bad7606ecb797ce1fff97553583505e5b15a6059b5770629d29f3afb2835759f1373e07907f7296e2337c7864afb3d78f36cb175c97b936c94fc92c8a126824fd23cf306c7239015db26a5db5af8ed27ab11bdf65df614f71ffeb76facdafef7f1df137cff33a1fa3f20dbad9f86754fac7bfa1d63ff6a6fea1f41feccc44f9bf00c86b9442da8259f06885ea68fffcdc59d5b7bd8373a8a4b6c1e79eddf41ba96c22fe56698ed4ecdd79855657ff98403987f0319af369cbf50b9b1aa17b191dd19fd6fab629b85a7045ded0022d8f2a92cd03754cd7d7b8bc2ef4329079d53d99727e067178d528cc4efe113e0a526fbeb68a6e751ec1974e369ec7d838a2189fac34d1ef3eb1891ea7951123b15a7f4cc2c94b152390f60c9580693e0602fa32fa90a2f5642305f206debcd1486fce713ef1988cb9c7d40e1413227df5d456e9aa99685be1eb5003a4652dfc56390a06e66aca1f79862fa8a4a8a1bb29ac2d60699e332dc85dd24d7c5b7a759f7e99b2c73c9352bd20c1860a4f0f3cbde2e99b9a2f7e5446129c5c237206ed21312dbcba94a2077306dd19e3b359df18b052a179b7ce19a266a7f2fc640f6fc186f9e9cfcbf8c10320a28117b175d21a2e974df46a3b7070f352eb20cdaa3870b9ac4aa3beceee83f264859a3294e75fd78fee3961ca930a7e47837f68ab61e98562587be11cd886e91e3ba34cabe24bc43887a93aa76edacfd215f2657233baba7d4217b5aa1c622cf4c83aed02bb5501a53a987de9997fd728436de48469d9c0c28e85bdfbd590f5eaac5b0fd082c348ee43550b0f77a661d79a48575ab35da315b58a5de5b651f34420cc4919893cf4bbf81f83d7b22abd4e785deee9ccf36d9636b855068e97517b97cf3506c7ea8d56714dd004393fe7478ca5d4e95f353512d70ae8b72feeeabb3e6db6c81033390f65d62fc6f5152b77c7eec146e18cb50b464fdfc6ff500c37190827663974bafa2acf095a0b6d8c49498f30e5d01a8e1e778ed1b6d0b4677ed930a7f47cf65a67f312f29a39cdd8c2f6da3ee4d1d6eabccf0a797b91c921b2e51e4fbe67223297012cd3893e51d5a5bc3db480a303aba64c9e318c3335b5d8d671c06f9ad3782a9bc3b368d0aa539d1a1d9d507c527b91cd13f623f7c76884d236502b5374ac0b55bc72e5b64b95db38d5d5a7552bd434469f129f5794a4e9fd8cbd80c0d11e8a81ce8a1aeccbf91877a3de3e76b63990dda884def55ebf6df886b88baee66e210b77063002a069b435caede903e2b4bae64a505511bdc40cad1a4280442ea10541d6e669de3e9f50a8f6caf78d104f94af2c2c215a6a14282341fa80bc4c13f64367555d8ee9685504dda505ab63da34f746213604944210188565ff8d4e081c676f94cc9670b275079d7c7b894b2d769efa76c1e792b781bb805b9b08207af8504f51fa726d5138b80e5bdba99b1987c1b6aeb8ab254adc16971bc469473769eb4346ffdbf9965fd81f73a26e152fe6e588f11d95a3d59c7a1a9fe2cb47df26e9b97fcf42088087ce7c09ea98a34f0a6c32cccdb627701b23aaa4778d3049cf39828f043adfe7c2fefa71b0ce5577c58c0a7770032d88530a8f778f7d6a1e9dd257ee0218dc0c7d2e2a39bd9bad61d4f012d5d9bc91814451d58b11831cd687aa0ef19ae438f8820c6b67c89df2ec2f1c55125439f1974cda57e2b90fe426299091c48e8d0d57b923d84e1ffebb6ed182ee030e502c9d4d1867aba4d5639698b781e8474ea047f4c6815b74dc2e8134bbdee18ec3dbf0981961d757bc8e21ff50324ce1b9e1fef7ed8409e8efef7f1f4985eded389e26cc1d4ffc0752014e8ecc9a028c7c669fb4a1b8a14bf5c5699581d99b1f95ff1b87be45c6d4b14a14dcc36d78e4b1451161955402363ac18889c9c71dab27e8930aa3ac1308f08d6d2702d14c87d54d0856cc72af63b5bd9c31566e3187bced1aeba6b255c8bdd28bed921cc2631490d4b36e48d851069b40f495ebf762685910940d9fb5d34dace111188094bbaa0b333aa977c34d3c3e1f95073048cff618ee1e275153e4f35bcf2f9c30b5449ef2292baa10033a8d5055194b9a99ceb5b08867b428a2ccdc68e8176ca1afa8facf607599a27689e164e6bc7b5c0f6f778f6c578bd375a54feaf3a1209d52ca0a6334afb1cca3649b72b661116215054889ce35d69db993fab08e7fabbb3abf5ac55f4d1f2e65895fddffaab5bc6870f77a2cf291ca5c27859a4a6fdfef1ffa52e32623f65cab6be0096aa0b2adc139583cddffb9b90185589afb7d0b91983237ee605646c7077656658f2bdb73d7b6cbae893b799f5e4798a38dd2d8df3ee46f462863d26937972ba87c0ece7d645a17bb0c29cebed915182fe2851ebe5e1dec1f900ae6928e7c1801908d8e71673ddb83e0a0f55780aa3c6e4794b0ce50beaf734adfd4c7843e9749431fe75c25f30ffa0663db0673edf584583aa9407349b259b561ffd01a68773f2f4380597d5fd33a153b0eaf768f542e7b413daf64fb650fde0826cf02f4026d221adf32d3bbb9eba125278b5cad6335e22c47eb3727082c34b5f2da783fe7e32021b72e689d1e341ebfb5fb9c03072bb325794a29b1539549c0a587c7e1fa41b3d561f7a82c217eb3f8ad0fa3e1ef3d39fad31a1dd28055b9b7ab0d4b76789ce7c14e39fd02feb3313073ac7cb573c8f18c38126bab5dab22ee7056f7311c1e1ec370ab34df5650474d2710f461701b46f3369ecf8b681b15280e5259bb9e2b68ab6e909fb85d0eefd056624379e6bc6ab019573af330d7324f86b4700fcd6887e6b11dae778fdda3e095f792e2b59a513d6cf28a77b48342e8eb908dd8bbf6d698dceb341d876da0c706b169ce31e7d35145916e26d700b1a16c527daa1f2b7d0932dd7e5c838a54610fc1860fc2872b1e70cb899e783b04509c109836f212f036f26749eaa554807f7ab1c743287d2e73bbe8a4c1321a872467db038f3b6a6cd4cd6a3f14d66b27c583689db7a631066f1efbd290d80e493b211a4f23872b5a9ce7be74f42c3ddbb473e6c156d622d663eb62734eea9ad0e91b3aeb505da24ac2ddbb355bac66aa5acb5891d07583bd9baaccf3c9921d438d1a50de376b8397ac25d5779f6b0f82734fb8dcf36236f08243f9ca4fb9ceb9f41b421d5d146cf60c72ad5b28e00d11609dd3ff84e3bdc5a3ac85f5411c0b4236a206d2d5af2a7eae7b9700bb25d82dc16e1bb3833c642ec529a64373c8430d545773b1391233b281a0dcb4d73b05dab14c5efc9ce285b9fc588ee3961eb2b399a0962738e2d4e2792b404135a59f6cec792d9f078fdce8b3dcb13067f40dd10bb7615b0da79bd2212885e95c63667b0a52c0beac1f4a7a1d6c477626dc2756d5f8936375aee972e1d1e087e8d538e74d9c382c390468238fb59011b9dc523cceea253d5b87d7d444cc5511e6a68be96c67078288a8bdfdce8f2d79e6e541511ca1316c71ef87cffc409977f38b1fe02876ae2aa62ab5f4ba91891de01ee3d526be96aa7f8625f5cb366f2d67ae7ccf0105e52e65eab352277ec75e696577ea29461c467c70922a39a3f7e13ea7095b3c30d9f91dcf2936cdea173b87ba5ca39f59c40fac5d08de15b7a007ce63e49f30e177becf6587bd5929ea9c21641a9f9c6f3aeaaeb3a94a93078d2d76bc9717e6dd6b3f98034e9235774662df99905a0ee796d54a1dedb7accdd169e9313a9de251aba52f4619bcabc9c3283896806e963fe61cd554c3272b9982e0770ae6bb1c8c3cf7ea851b5b75b635890736314761a659f1b0d635bb9d2b3c76ad72e4b39e7fec27b9823fdc2caf2139773c3812cb20c1c6cdba464f6ff4534dc1b246d44a574fc76292f47cdbd8b1621e2c976abb9994719a926472e221ee0b95f08feaef2fd78a6b638d8399656e97cedb02162d36e78cedb51691fc612a7f974a478c36600340dcb0c50ddb7ce2b8f5d22f98f1c892a970f628d8eb13764fea65b8c1abc70d34cc71b018da190b93ecf21079169c3367621d464278c4ec1f630210f9f354fc3a55a4ca932867e786a6e5772637038ee8f5652fc74fd8565d1a09105b97388b874c833cd9dbb600b4a1e7d6bd57b6d8ad0400d5627fe3986b64f49126cf59315a720b292d2d836f35652a8b15f5f71e747954ad89405486b1af9fd4b4d36d30e0eb560ff1964206e31f2d85027d9983a2fdd19bdfe053674ff176445b3e4ccda7bb4fd29d7e22daf80f25730f12666f1b6f7babae86bd2fba4bf04775ae6d3957a6f7d804976cd0c7098dd521739ee00ce8a21ac192106e9f52f8445e1b96e693edca2b5affe7f05661b76e6faf57946afbc2c0de2c578d0f64ca6db66a4ff77cf2a12771ea0b1e7c43fddb13b9e597b03fa6dc3d59c61c980a0cf9b6200f50ef40a0c1133da8ecd85e1f9794a744b661e16351d61b0e3c5468f54728d70f76be1f3f30b8748a8ce770255b1f05f371f1457b9f20a733350a5879e36fec55b8fb34ca4b33f59ad5ee31b6b747a35764341dd3f4791c52fa7a603a56b85bfa26dde6a1e57480fb8f2f79ba36daaf24f4a8b91f0a0d9f49b22e03d162486d21d32128e2a097ef3ffd740d478635463af2b15b3a3f0ee263252d3b39583eb6a51b8a96bb1e028b557656da51e090b316114095b25d00ede21c570b021038f112b5f9a7d3e6d3e992fe7cfa5e5e25f3d95651cd3a59676ef5df2644ef7a7c1babcdd102b7ed510c3759552df6d1350451b11a3c2d2097ab7d7d364313fa2cb6363c6f0ab4adfff31e8005e554a16cb3f369f177646bec3635ca091c0d4a75776cc4fc4e2ef27efee10d3aaa6b3f7abd6cf2b46d97bb2254b7e2ce1ffe3e489ef9f04ffa976b63963787347f52cd0eb726c53da410bc2bfef2f1d3c9896ced6505a7c5793449533f59d863961ff97176dfe75f9ca1c4f25d313d8f55a68fb3b2c3ab594bf532a18f362663ec66a402879e795a31ca48d917a8373080ca494a5cfcd2cfa8b4be639a2bdef2838fc49fc638c1de9fae502724eb4d5bfc9c8223666620ad35d9641b1cfd3fd2f92490d241f078cf9490366a4cac062e71483affc2257af3c86464f32bfd81d80526bef84d90ba115a38b4a6fafa9eaf318f1a7014b3ada6d27ad26ce676ae2ecfcabfa5bf8174f3a12d049640ef55ab6a6ba9f466a60dc0ce66bdc47e7feef5ed7753cb57ca88b74785f48690de38c4777cfd0e2df2f4e17fb159ee60f9de605e2a4c9c5239d252566e6947681ba90accf48bab02c66fc50427c342d32f8632e18abf8d7c99fbc0b43cd38de3631e16e9b406d33df606d1e7d512da38ba7014d5fb160932a73c2ed1fb912d1efca99135f9bf60fb0566cb8cb73cb75df658e600eded0e23c51d072c3a0c43bda6f5299480875ff9ceac3cb6c5898ec0b1d0ec53db81fff8ca7e65c134b4dc5a8786f241e74c7c5cdc39abede888eaf9d27e5dcfaadc49f5f29a47fadfd901fa649b35c87c8cf2a12db3e32fe23fe4a7a6e52634ffa3f95717fafb04f9cfff05", "hex"), + Buffer.from("8d000af3e0656098c6c0c070024836486432ed3ec4c5c0c0b4fb0a987c06241930851c0e80c484c062426031218832542187034e07d81cf6321a381de0621470d8c128e074808f51c161abd3012187cd206121206f93d30111878d4e074481ecf54e07c481d45a909c2450cb6aa70332406a85d30139a0f03290b03c90b1d4e98092c312a703ca0e6e0c600000", "hex"), + Buffer.from("a6130a955acd8b1c4976cf88cc8cccfaecaeea0f8da496b6a515eb42964107b5304e30e57251b46ec60623060b7cf1b20c620e3e2e46a7392e664f3eec61bccc1eda6b1bef60efa5194cf7c11efc27ecc1f80f98e3d2c785f6fbf8bd88c8ee927656a0cc8accc878bff7fd5e449f4e8ae2992b8aff29fea158fed5ea72d75fac2e67cb9fad2ea7ab8b8ac6cb2f68bcba70f8d9f0fb015f74b8baa85797de5ff84bfdf0a7abcb097d182eafe81b7fb9baf0abcb313fa5356a1ed342231eefc5f9f8f58fabcb7d79ede4a3113f180a8652884ce401cff5ab8b52e9cdf9d94ce6df5c8a7fd57c19f270ccbf1c5f68b1060f69f561b8b8f29784965f094ea1405cca0be34bdeec090cf9b9b3ba08242196c0148808f80e8fe7f273bafc997f6182934141124ba3a59345e2d08bbc6938a141efc168e968f84f22d638fc39e190e16059d2f09fe9b90cdb6545c37f11d668582f1b1afe2b4da3a1cc6ce9ff2f5697c1c697bd1fff8d8b5ffe970ae082de5d2e2f3028fcf96e5d1023d5f23fe59108a45cfe07cd0978e0c41a98f42f785e854f19293d68cae30b62006bd3dcaa20daff4677126861cf3d8bcef1fc5a544b2af3f1cdf2df459b9b8b2b670f549c0d2f7ce3b1670ce5adc7152f5fdd7a3cfaf09382d86434cb9ff7dfb0527ea943813ba54f7944463122512fbfd491185fbc84e5dff8fab0f8df62f9077cff75b1bc6fd398993191f83ebf389409ab6255f095a6df8bd3081210b100f483823fb88715673c5ee52b335f61f5c2c9f40d003cf4f5339e760f9fcb67cf0ab2157d3feb7dde80d886f1bc2654afe9ba7cc073698dbbbcd6d7f4f4d3e253414c307679bd4f65bd82febd7f3d99f5905722ea1e4c1ce2f90ccfdbb49aa06bdeb7da33c6f510343d783b644c9f0aea95203bc45acf70cf245dfbea197d7b6bfdeef1dcbfa417743bd5dbc74c62c08b7c261f7f59f892c9fa57fcc6974ff8fd3d7e5388685e1b09c7000a305980c93e90af994505dfaeaf5cf20386f2478f67fead627859cc15d15c11d16de6e7fc6ee67fc36b372b5f95c75ed5ac2211a60f616633dc4bffd53703c2f9d56fe43a1d16825384de180faf79a6f35ffde590e7fc40ae3fe499e52d8c7f4800bea18fcae727ab93ba3c3e59864de1bd2f025f19518084ccf03265c97806c4826c2ec8e6828caeaf9e889c45da34eb27057341f27f759a9e430b2fe5b73c9738e65fe993f9ad6fe539f13617dee6c2db3ce76d2dccb4c48cff0b5e7059114be5aa70c4d1f9dfd5f4c5f9dff3b53c4d14322ce52e38f7fa6e39861d78732a719c001718f3f8d0a43e8b0e222baefc3ead5450e455741cc2ff74753262419740b5f28d2f84a24aa17cee1980087dc4cbbd86d09fc1d8e8f984ef5f8a487e02e1b102feba64d1fcad5c3fa3abfaa3cefb0ca64eecadbc23ba447e97096b64277c08992b11df9820c238fd9f9faa85d0172e784fb7a1e0aed491d8b932c333d335c71865743d0bb7548d1749ef047c2ec0e7027cceeeadda5189f0a7a5426e1540230008eefa8a52c5f24f04f38c5ecc49dadeb1fa06887a66b00d643804692f2b758e3f082c7d589cd86cf9e368150a9cfc9d569f28f5402323bd14d2737a3a08ce8b88cef856abc135c2b1986100100f2536164724dcb5ca6bd44da9a43cc3ab95fa3dd69552fd63d2cdafcc6ff758239b335f2a59af02506ba64caa6c3b56cbaf4d49c152c16b35ab57b03c251f8a8e963ada2cfc582118d9c1e3c6ff92577d4c3f7e45616c392120ef0cc8014b8180b0c3b15137a60c93eac7b954d51ab2289cfb3949e9fc2dbc0e91ef3ce944256a78bde23d548995047b2202bcf062da61ef65f1b9a35ba1b753b9a933b6c4c73b63e80b666884a82d0cdd618b23866a662834a25e987ea1115b8d49b851c57e66de96b12c404b010aacf709e41420d9efd6575edd0fd14f1d2b10a8b786ee2d670d48bd856b0ac8bb024e7877b002601d0bd6320f2d845a8da94c4076b60121c92e6ba1571480c1f7af198eb31ff7e5c71c33049f08b11160f769dd917ac37d41e2cd879d0f8ea90f15f60ea451dd928606c6d337d5535eb928de3c55286f5afec506f1e6f48db39f4f83e4fe379f04c9faf4020f4edfcc9fe259cbb34ef30fe7fcf01365e08d3b7d1304fc915894883128ca8f54623b704c489c1e864202495874303d424f3e901821b6345e0d45bbd7a7d7fe13e5e3dab5d760899ebaf4f3fe530574dd5e03dab53bbdae05da8314075b45581361270294a7878af42edf169da797884dd0720d5c0453844c9a013250fb9d815502ec21d3de340285a996496a40b60f3c0d7b824082ceebad5e4a2f4a06c9ad8b041222ce6ec5d05887d7bf13482f208f531a9b11d69ab5b7c8153cb29bf7fa8e4ba26ae320e15c82c4d55528ae24d2881805cf93df82e754f0b4d702e7b18a61877d82a835beacf82af014c8406ff720d060484830dc792920af80445af45085c65187425382c438b81655d2dfc34ab025cf43b36581c28c8f79c826667adc0aa6163034cec1341291f485da3ffbf2f55d21bec09723bacb978d8446329eaa03ff2d4802014267a91e97c297043c5e4582ff0502e1859782a6ba0a1587aeab8a8d29c863f15a915b21214963ccf59dd5c9538042f2ad60028a9005a24085f1a2cbc2280a03a86a02836ae9cb32138205e928218e0cd096de1ae45b457440bf4fc2895f9d94e4a8276461278fc0e810774625e996f094090f4c790c20039a5d4134a589e67d1e27a00a2fa0f48658518685654000dcd362f5647342825c3849c6ec61ae1f2295ae1abac8af2185368cd527411303954847193930c12ea06a36945295a6acf4e2abd011dcfcc1457cbc50e147b1ee12ea99a22e04b7e329ea7c6ca3ae5c9dd0d5715a55e58d90f719b4b86609f5135f1dacc0e1becfb6e9b5525c74b0db11c45141a2fd2497b0036b293a533b55eb24c4963ad9ce095ecba670980114f354a18a71902cd5930d9558a54a7694f98a5315bc0f928a53c02ca408aaa26deec44ad5a7f0311051b21ca9167462980371e6a693a016dd6740589aa465d846800d0cc15914c3367c958aac92f79a4a006d02fd425e03cd35aba2a63a5dddb912e76c94fda6ab3aba206f71bc0b292836c61c621002843502bc962e1fa19e79b8730f6d0d4fac9088a1d8f14a3008565e6c208277da40e46807701b0e8e647922c429c475c8c59413fd2070ed0acc0bf9b22244497e88369919641170103b00042f56d140bb1b07204db4b64a08a94d65101aabe95452cd36dd713c0a1a873be9114eaf434e7b646bad8a667532c9c80f943cfb59d321c8a9bcee8ab53b51d311786bb71117c6e01d205ee6c407d647e81ab3048021cd63c1570a319d148c690e0bce3c7f023b1e42168308c705d9beba4aa8d8525b0b25548fbda0745d9adb3f960d19f1ab31acb086f44386e20e637310cbd46cd3dbf2be0721934800edfa265d9822543a47b0e620cf11d293c44e6a888a9553f7027e0d7466c2fb37d105e02adf87ab06aec0a5c32ed5a8af6baa5d86e79fd452c27885a9b29d852cc5eeac5c1069951b2ddd2466831904471e29386661aab00e32582931c5c6e443291394142f37e1a578b5e1bbaf04028441c82b7f021e1dee1acd05570920a3986174da8ea98aa73516f986dba4e731f529622c5cd5d4611b0cda0e8e21871a865d2a0cb3fa56735d0ba31ecaccc6e2f656f20ee47f3f3a4c9385b3e4e71a6ca3325c4695c1b0efcd30d622bc31b31c6ecb5c4aa311896b266e96855f7bb7e6aa96427df36702a686863f329dc55a2d095c556f29ddc1ab0304b903f025d005ab73dee7e43a1d5e34521e068aae24746b5f1336f10f8d62fb29100d92fd56b1f28ce6ace1da0ac0581f56d076887eb145513aed91b50204a5ead6bea9d8555958be13d5c76453425e0db2549f6c009a011e4f32cdc9f8db38920a7e1cf2c83a3227567955ebae2479ad3bcfe87cccdc35ac6780605c018bcb5c093e33b0a005d6a7db8cf87b00539348889810f6e145175e40a263752eb50d755bc912a822100aad8519a377e917c4de6287c6f02d309a5e600d4a1315cac0cab93569af535b2a1522692fd282fec5b82c5eab81757566467305f5dd6d2846b7763e3692f9d9e7542d430140ae56ad6d5a932a25d6df91d1d3af6710fe773f90199fadfd986db2149d7b683b6b5a79ddef40d9ef5df7b634cb5e47bbaf1c1c0807be27bca1ca64576f7b30a3caa4219fd7c9a8f8fef8db258c11a40e0c7b8ae188a857a07d24493313fb5dfb04942c8142c30fb6313cee93198271a5b6d33c97b59b63cf54cf797f971e0f32108799c05c87e8bbdfb386d2928c5a96a5b9a16effb516f3b6809bf4d4d090f059d9527d00206b9c4c6cc3dbe9bbbe6390730519329077d598bd56a24e6401dfb46c8264b33993d7075bd120703943a5cddfdadf21312114568839141320bbe8d3f0d043ad45e5f731c87b84a556c577f0018bb7c8c53a6d960d31fa1113f15657c2e61cb44681309ccbb6b86d54c69e05e9b04cd08e2cfd030e772bfb1254b64863880d10d2c9c61369753da7cc57aca7493892839a70f4b1aac499487c2799d330d711ace34fdeea276fb96bb3657b22bc63db508afe9e25bf94d604bda29ea38efdb65ec704c972529464dba29f647c9288a70c71ae10e9c62c991172f1680aa8f82133c5d3dfe9f47785f1f36073d6b96482990e0eadf5ca4ca30537d238ee6a9bd66e5548b3a40f5ed0872f6443e285a7abf801eb631758ad9471d9dd120e5e3f64df3cc30c15eb21ee96aeab8db5950036cd77d346b7a0299cb5e07324dd048fa0cd58c81bcbe8a25c092879e30a7f3b14173deba49bd609b132b7fd9528aad193e287a4e5d14bbeadafc2cd07a562ec6e62e41c2e06c026c9d7361ca8171d20bc90a53e51e37ea90fa2256cb4e032d9359bacebc1feb70ef7848bc2b8d893722c46a8831e173f52d03f8a5ce40f8c8b588eb6192f52b30d293872342426e6cac43c32315726e6ec6e190fc3e464b919b4e06127d51be1acdba45d709cdbb46805f67b6c7caea83f8f6ce40f323636da6a1b0f5389322cc785c45367fb4ad6c042dc6ac45668dd4dbda674591bdb434175e6acd2d2d0f0e003a10198b2ed2cec8e19c231a22c229cc863e12dce4f0c8902fb0e101dc95833ce544ce5829ee57edda5ce507cdbc8b98a535d2fbceaf8898e9fdcb44846537387ed7cca3321f442cd10274533c0dbcbba9133ed9db5a0dbd80e0d2a801bd92107bd9668ca66d80f9b8af6a58e5fc630aa522225f316a96d9f386b5a3443eb66d99e12996b5018e170b34c75af196d90ac77bb15349bad224403a1352f9fed2c3ad9e3b4ddaa58c6d98c72136b9d895964d3af815b886ebcb17d4beccf98ffdc141e0c988a9d2a19536986ae476425dd652f38c4a332f33e4b1bd6972ed2feca42eb8706c71871afc54c334fa2d6e2ee6ddd5e459fad11989bd35e3d4f0ae4134f02da5542bf1f414c1235b035967a16d1522b41d6606c6d235a692c67777f03e9078f2e4089573e13d727ecf010a935179d581f480d52fb65c842025ccad9e90272c5b96a038bdb7fbffce29916ff358442e8b51453392956194dad4352cac633931e46d2c35e71647b37e83d80e7e043786ccbeb4cfa5fc5134b2f8613b7511586ed4779fc15c8cc1274742adbc0ddc45edd5ae9ad008646b327885de632a901209c46320bf45586e3a3d46898c1598de337daf8d90a9ac2b64019adb9f6f3289b20031c700e01e8e0169254ad2db871b3767862c5789c14eb442bc8a7c18e92abcc8334cedede9a277ccd31aba84c185399aaee603b1a0bc976fbd14d16f180c1da42b073849d07301541d6498943cb147ad4d32fc7259be8b946ff4835dbed036c14d2b96c5daf15e0e3d12609d03ad98093b8ec51052c8facf14177667b6063c4e7cc89b79e15d96eaa43baca10669285da20d876a35b7c920ff80cc559c993d51d032b645d0eadc412d99eb8b4e9dba04de953961b7ada3eb22df04ab02d9bc3993a93aa1a29d72c64da4ef6ef8135dbbdac613b7a4cb0ed98249369b2c5ae8f8be77063a8ead9813a64c3e4d6e14829551036ab14f97d8b868aadb5ad4d182d63abb76193c3a5646d55c7c0bacca529d72b3cb3b978a8a39505105ada1f031a837f0e1003cd150e2958128139d020093edc76608e3ad2d800a26c3df7016ae351f974845cc5ec9fcbcc76c4cb4dd670b031e2af12622731ea17458f34355bd0ccb63c9b6da79373f84514a48145fba67b091b4d14e9af365c028fdd92332bdd05989ce3237e3f10c600294646335dce1c5b8f8076423c20165d6f4717e259f708517591d52a62f2f1b8b0951db50e116192ac9cddfa088597870532aed9f63d44fda304f18c08ea9c8b75bafd58aae0f32fe4862daae4beb249d84beb5aff349d451807804a641758ef4934c41f74c1b53173f2018c431c3198a334cf3518a3dd82e05ae869a607cd31e6ab93786d2b427682c7e54bbbd1524f8d4babe912d17d8008b0151a9fa232a428933683518a4536b1c56407b073063b74b1f3805664a154ab4dfaebdb0d0e62ddf67dfd907642a52728fc26ed930c6c1b1122963d4f7422a95b12a7ec34c43759a6b46aac4a438dbb5c35a8f2e46f33ce6207a152d09acf9173fcd61ad7f64b83fd19d4689dd78323301fb2c386a962bc8788a1aeab5b3d4d6c2aecacba417e9820fe51e4eeca4c01b7722c927ba992b243c9b5cfa53a49e1a2df3dd550f55d184c96e80c9c6d5a723db06b9d07af75966f114cad2abedd6ef17966b7b1536af4f9e7efb0ab13e568ad252258429b5454e16f6ceef42d63a076f7c06820ca896fe06f123e74488a005ae660761284ba2fa9432ceb730d074530eb15d22de2988f4ab78efff69f46285c9edd36c72697a43c1f3d1fced3a47da88d757c1193c841ff127c6c42cda707545896811f6ead83e533fdbb6f3d120869e3e9b66a8e8528072c3d8c73daef6467b347a90aa813230f6ea74427691316dc9a513afb73406d8c94ea23577c53fcdfe4a78e6be2e2ff01", "hex"), + Buffer.from("c5240a8d7c3b8f645972de79dd736f3eead155d3b33b5bc36663b6482564480dd45639e5dccd4d24ca58080d1904d1262d8258579ed0a027470b2c6448820c1a041a5950393284266b9c0184ea85fec0003409c89149632c39a5882f22ce39f9e81d0ed0995937efe38bf717714ecedd9173ffcb3bf7bfdd7f766b17fcda87b8f6b761ed6e7df6b7f4a75f6f42a2af7e9a031d0da7eb051d726192dd2dbd055cc07fe2db59fe2eacbffbc1a71f7272f4df0fe907fe938e66c7a726bd04e7f6af5d58e166c3ce57416efe278ac6d39f0226e54dc81e38e4819715c61447e5f9ab1f823e9791cb4370f30aa3b33f49528620f7bba0a3c7f49ee4cf7f2db78f7ff0f62a87dcbaa723bd8881fbfebc023caa7a7a2d6f514ff2f5a4391fde56e6d6d35e66d5d0e33bd22f3d379aeee41e1da98790b0c670a23ef08f54b35e217eb50ef8fb8c10e8f92c357d3bc393055e0590bf2343428d17fd299dfe78274ff7664c3c1046936b07329858ea151dc8bf08f4405f653b92074ee8f2493d3ae567d0a3b6243ed12bf8b962c94e8e74aa3d55e6bf4a04eb7f2a2c151e6e4c7285fe2d5b9a4cebf9b202fbf8908a5f8b41f9e342eef385a03d1581723fe27b28a279da404f8b8270aa16d4c71ced3d46e565ad1517eccc62788ae7db27c8294fd77bea5926496f47cde027727932906fe056be05da335039a07af91787f4409e70ab717064eea3beae9775dbcef565955a028e4cce27bf09667d18503e3208765fbdd59f1e44605e756b20e0e90c029ac9f24015eb2b0369dedbfef593e6cb7e0ca60c411a0516b4bf623b866ff65c7f2bed4c55fb67ea1f9a058f915316669ff6790996c825c545118202a37f1bd41de8d93355c717f2b87e3f200431df96c356f4ae411da1946f4a12d540b4ac172875f39139a3e54364888457f91e3e0848a9462b5ccf4b5259213b9d231b371a0efba6939b5d182e95d5d2ccb0adb523c5962ae85e93333b31c014110822df0d206b56d11b736d588513d5a14fe6c55e01e543489106e5fa4c7f660415fefca31d805933569494f2c6d0da5db286ec2f8a93fbbc515d11ae73c535374176d1dca2d6e1f49f69cceaa90a48bd226d3f76625e8c7781c915358af6fa372ee8b72c4c0f80e6756c132ba9b13f65f59dedc04cfb30251e7f2697e9e391c2be5028bd3d6c3b24195d2ff1a6cf44224ea13aa095e7a8750cafac57d66a82c237168c5080826e004786c8488582ac7ef00d07119c499f36175cc9d46c85bb84d699798ec2ea0d9eead94b9a8daac186c7742253ee5ffbe2ad2b43ce622741fec272a926206354203da2f97c7ee3fec6ff90cf9dbcddf1db5621c97ad1c916b9a10a0cb739d2ba657eb7234b2259c8225677f2e30d1751cd7002a717e2d0c8d1bf6dec0366f6f8b7527bcf343370d63bfe457836aa639e50c4da4af55973053beea029471fef9502ae4b6d0c961dc538c904a9d7a426e5ada472a88fb5a03d09c617f76aa495b8540a669efce844a853cd4d74e93c3cb35816e16ce3cf0934a8b6e706cec3fb8eb7ed1155d516a1701ba4db939a01e4a41eef831114394fa512773a51f5fc3a3cfb7f4330b5ca0d87205a96d5e776d53719f8978d9a440c49a849afca46348b1b792ef7e7d57809e9b49770e847a84efda6848ca11717525639b79cc5521cd3d133474a27c6cbef74e6e4305da95ea0b157753d2de9a422b59c99440ced30d6a5dcb19ce2cef59693b53549f44ee8909d5e8b3ca2c86a32be8d1885397564e14f7ddb05a53d92b106716ed375d4b8c8071251d43ca92697277710f356ed52dd03b9f2b507d2afefdc259df5f53b7e5ba1b5b18c680d825759e4be2c299f20e2a826a5f7600fd49441d4a97fe343356c4920e4380798bd5587640a55cfcb0d16bbd5d0e8a4834337629adb5a01898867895d4a5824ee8d88cb6f74f88cdfcef88dbf75a28c33510655c1856549bbdf29cb3d87eb69dc91fb74aa6cd1496e6064f3ebd5216ea4f2d5d04ad56d269a01cd35fb75e9e81ad913bb2ad7603d01198c50b3283722ca8d54fe85713e336f5376cfb93b78ebc5923d7f32b5cb0341b31edfab0e25bbab032bda3e0b435bed5399d0b8269df9d3c6aaa991ab647839dc55c1b2fa47af94c1ef0ba015765a7d5cb20a39b4caa5ca31df426caf0ee7c2d6d7f48e7d0aaa95962380237232fb7548a50098b98a470e0dfcbc73fba47fcfecb8240f907f4d8ee8579b4cc277fa67003faf0db9d5dbaefa5ac751bc7f637b6f8a0dbb60934e9279517b7a5c6f3b7b39be0a2f41699a7c029986bd1450bd1ad64d36c5117644219cc28e34503e89915a77ad79eedcb03787855c56fc316f61b3bc622972157ea2565047909cd9efa7ea9a1e0427c6609af43a334a32d3606840c81b7b7cb58b762d5cace6726303a61414d155f86976ad1fd55ab54f446ab4f87acbf97a9b7048dfe855b713d5ed5c2e98d5d6485527022e5a96fb0216bec8beaac23800bd4936be93b48b22fe99c4586229177e67a1d5a6f89644afa50e4fd4cd1a7f52682862321a5b374c6c81737eb24293672cca0294d35cda472867e5d46a555573b4638b365808deb4c23bcd4643b2e201a5abaa45c4fce9bff3ca3c25a37d5505aaacd37d5689d3f576159d565d5844a90c0a4e03e8ac9ec0da9aa6066a612d6ba7b19ad81d1ba4ccda4ac4fc74db585905b09666b7376bc2715abdb55fb76cac09dda2643615c9c071d4ad4a229cff881c1a4c365004531b851b69a17be30b912052bdbaa5ba912ab999162dfe7887b62ecd6c49155e5385fa9d39d6b67d4b5e3c9622fccc79ef5828f2b3e24b823aa9176172bd1d092cf14c6543ee97f3e969452c11e9ecd06c24d71b5963663392a6cb322398d6344d19fbbb585736c84f9e72819633b3e81714ed4e28da9d383d5199df0895f90d0e006aac16284d93fef907d027458fc903b7b2c908466cd56c2857505f0df1afd65b3d8f5e361519ad1fe8cd48dc1d3efe0e1f8f49a877787f27ef8dedb20c3558ccf722e6fb46ccfaf0bd7ef6d0b8ad78579bf26cea64b35abf363edeb7fe587dce084e5be459195b21d8e7be6928387b7535b226d9f8e00447fa6d6250b5aaf340d153ba9598bb5dc11653f3afcf4d159b223e88fb949e8561f6415e575b2d44572f8a3bb2c1affb32ffc9ebda90c46da1b69b9ba2c84da3114be09859486760d54a6e92d41cb53d3a30e1ddce779355c16c84c0ec5a25b2d1d38ef5b232f8dc4e3d8c25f655c87533453103ac74ccddd3bf8de8c9bcd0dfaada7d5562e9bd0f3125b9f0785554ab3db885533bfa4bd5b2d1f29ce0174e682edb071b4c34052dad2b03eb540a25341da63d22c911db67a503634b0e610bfd16839234372abf298a16945df1ec4e41d6e142c3999a02ab13e4d2a5d824abdb5185cdf5a28cde45cdbda87323299f1ff7ebf097d64ce46ae9e2585b2b18d698b6628811fb9a2c627542f36873c17ec7d1b36b246e12a489a30a600b2e5d028aa5e013391c4f1e253372246d93d52ae709d597d9ba990dee4a0717f850ea47c34479b6a972a5d5564a6afb4fdf28a4af989b6f7db3f0b5f6caedf3365a1271e9bb65ef290331a5e0cefad4b5c30e5e4f0f1a72d0930a569bdc03926906da71079b45acdba9711d816e0bd3663f783df451b49d543db8991c0d7e499f7db85afa9e241aa94d701c321f50d674443769006ccdadb4df395ed7dcac92359b031a0939577dc040aadce175999d9546f040d3d796c6f516f7a10af367de19c923b78b4b2733cca8aba36e497fd38b5f5eb157f2c7380e2958c50bae094e237d41453bd1bcd21f0a3611a967916e0abb83dee73b71b5659e6e4798c651a398cc13e43892540ef8180b85d5c8d205c75f2caf122c47c9ca8f13928a845ecb4a069442c85f1c7f102a8247ece509ab12bda67c59799fe5c24a4a19dab2808d4ef52476f01ef91011c99a1652074e47a008ecd8850fccfc5c7ccb68c90c69f49162ccc11ae310dff0994588494df2a99585e7c25967be872de11dbc57859baed0b13585db343eb1604aa67fdb28a2437795e33de4a057374ec3071e7f9209aec21815fadd7f1bc806001e5511eb5aa579b07db69d5db8f284efb690c3fbe871e38e8b77f9ffd868f3c39d8d70b584ada5d9a34b3d1210c68070d3a1c90bac108a6a817ee6cc4dee9e87400ec4514f89acc2de2e2c59c6dacc743f28b3a5bb5c4a8712067e1df49985af2dbfab46a1cff42c808ca78eecfd86b01805c7481903830f98daae30d7b786630267d069913ec386239115264b9e745bb2421ebf0ca21f0e87bbe7a329bcfe19c2b519c45076a51190be8ba4ba11a948333cbf27a484f9b5bb7b66d092234521048a5ee9ce3b727a0f2f0023814da8edea46af99548b144452e7901b1178b7b691182c106cba20d8c85e1ceb141e2c35b218c50fe4bd21034695f6cc3d5364d1df4bff553c6529a6c8decfa273497daee3cc08e007fb581305997240854be3a98976c20ff6fc14b89024597a754848ac6b32ccf8b25a22c5d704e408c7f98e3fc3f969e97adc725f8547402860c753ca0e80200f1bc8c39bac327a3c15b76307a72bc307de2825006e0000199495318300f27cdc3e2fdd64eff114d390ae93da4ee819041f28e523398e238a8426318cb8a3386c561c678c431064b8d130a52beedcf3942eb913cf3409be59ba0c056de3e143630ef06453bae47b7609c6b6f433e4a0387a8a2fd142d49b46685d73efb9d8848c776a0a20b545004b532c8b0018bd0dcffcf73bf99bde4c1b5ff1459da8741faa1bfb002784da3b98976542d6184f96fe189036527a55885e4521802fc2e37ff494da1fffc6f33d6040386e50597ac23a7dfc0dbbf994fa9c33037a23406fd8cf452a53517452705885ec17ce1c0b2a134f685ef785929479e73489f0597d44c61c8ff1b041ccbe89b85f50c93b27978d3de526ce7bb00d7da6d7e199def9459ed959c13b965852a71070412c3f0ee24eaee80b36eef6c387f04afc07e084ff10d623447204525f64c9748b71529f295792f7be54ff19d8a9c4a6e304a690a45054883cb40741639dbee99370ac392e961249f13da1fa4ccffe4666890c6186e79ee395ed460f1ee7a69c29c7d37e6ce0418c9091f7c2eec6b9015cb0ec0b5ccc75d15be6e4abe4ceb1f8e1a4e85b5e5965740eee35df7f6e461e1e037253930d0344251b31a159f435c8bc1a65626e31d140ea4dc40c93940cd3b3fc4708d439ee0bd5edc2a007700a60d063b04c4c19cb8d297c8b25da6fc1fbbe7d07cf9f10a8094af1d3ea89cace93086fe006bed740be29254cbf489af832fcfa88c3788ea3fd2144083bbe8fd03b154e2ce4bc7cf1f83f98a47fcb4484f19d011f7c7b582e6654d1151dc12c31c7160d94c249cef06f2f3917bed1402bd12bda43f15896b2d48dc79aaabd5633ba66b26fd128494ad24d94d2d3952247c610cee7241ce566d370cfc65dccf2821083c038f7949ea8677c92f0d424434981241f9e99fa384d7c26960461299cf2b22cee94eede9e40f7de2a776fb1b723814752a173f13d79ced80978279c4fc318c45b93c73d7c663101fe94f13d74cff84329aac3b36b5264503d9a047d936c019098d70bf56e0aabd90e4eabc87dab638f4e664c9a0f8225c67be4b1c51400fbac6ad50079ed0ba5f8b2e423496a492a9edece7198a258de3d9f20c29033dcc09efcdd7e872a3c0ea1cf0e4bd271300e263da1bb67da6686a78ee9491590b419a8893c98f172d8207024bf92c3d13977cf5242124770cfbe7b804d7ac070e371d1febda52a0e164690a52d28a6f39697955c9420171ac57c8eaa80da208ae90c345369238bac5c30b7202af0c80d04f4f8736a2bcc6a6e788d80d0cb82e22edce77bd812c5fd893acda7b57bc2259dda2f14b878e9b4a21e03a7f9a074917f21062ed11ed40f406926538434eaacd88d12121b7dd0faf8a30c581ed87301bafbefc7567739c72d86e5bd57493c4b103817980493a2f54e2a99d3ec61d52d2b31bf6466351815217ac87d808667c90f08c45da665ea1170cf47e89b151f7cb317dfecdf2c9ee893b80563cc765ae53c12541c8041dbc3606e318037fb29ac6558d54d5886afef888413f77ac76fc60f846919159557cb167b6c9f0f9e2034e725050e9020a34c0debc5132c711f0c6a083543a0528dbd391a3203010611d71109fb4260a4ef05e97b872c093e0505976cc4a1b11b8167d436a0d6501a440112afa04cbbe0e12643ec55c9c438c428f7f0c36c049c9220730e0299a7689c9f394f4f1088515a4ba9ff962b50140e01a19bdc3d779c2cfddd5f1b8d5ff078d5138e8ef738a10241678b606a3324f48029310f8b31ae01c49fd1f58af17af16ec630335264647d4cfbf57ee9cfe1203ddc2f4a1372f73ca54712e5e2a2423dfb13470800a19edc9bba25e70d0408c1615c037710ce27fd9044b5c48518bac33c4c883188f02e2ae4e113904aaa464cf67241b3f64f9e6b34dbab0b5acab34c3eed19c2d804d999056436534d35b6cd09f94f0ce27c49b14c388535a7031d6e495513354ca7c2c7162c0325f12ac88ee257735d28145e61830606cc53258a2f150fc787017f11ca3162a888dd80d00df7c149637d876e56da9de9d8efd10ca9f05b25235937c2aa16fec4e40d195a78d18d1527ffa43dc6222ae9bdf79651ac27abc14d322197dc38d1e63ba4d11b2bf5bde9e50bc9a92437823c20c69d49f40e12bd732ccf70409e4a6c9078441411cbc31c495c39663c9ca38b0c637274a564dd07538c24bdc9e82d8b9e492ed52247e7be3a967339e70ecc4e76d3bb6ab7400a85e872c0f2a2c7b3e625289bd91bf8b242b3e67a8126e73e6cf19173c8f6056e770ce2a5a4f695f2a5d545a67f31bc924753eedad55944880c2def8e8849e16048b9a6b9f4a4ec2459b50ff08350102d7223e051113e36e45f6a287e0127dd3874401e4d5894d88657e16af4ab0b9fffe9ff76ce5de47ffa7ff236cff4962e5cba585ef92ad4015708d6e528f74663a79516258a125b82babdf217d637a91d02a51a5d92ebb665eaab3893d1eb74c86b16f39aa48d41709bf46a7915549c3311e74cc4a1b7d5453061c8d69b7176401a9b0f58a74b1770612713159acdf4ae471981ff40b52c1d89156a946923b0d0019bd5d664648aed3d06cb7742b6b54940db4d82c474c1725c8a1c9722c725cc42a2b01cd0f2669c1f12e34854771594992e9da43c9e5bc97c28fb4fba2fe9137de455892ac66058204106419a5822f5c68807f4ad3ce5782b295e7b6b7a2c3b95c31b3df68afc0c56e431cab8dbdd94e4d51bb5e21827fe11da3c27fc096320c19e3e65d440fea0f0259883d608e9b7b8d477d2e0c059c64af4a51f2706344e21c04472e9abfcf8d789fc253cfe277a0ba730fb29b38474c1c112f2e33f38840704c3f4a199c18d3a1dcde94bddbd65151693914e6433fe3260c59c643053885867f8d07f0ab007374b6e8baf43d7397bed9c6b14199be77c2053132fbcdffaf2ccdf5c49631c1e7fcf81342d83e2e5ab81b93d0917ce194dc6eb453e67e92fdc92a57d95c6fdf92f9e78223404fd7a4dc6d6a0f20ca57adcea5347e2d561ab85f9099b46d305f1efad2ac729a82b1d330588c468833f704cfd7bfaf7aa7f7dce3f73bde09cfccd0d97eb6fb874e76fb874d3d1145e1543fda179fe31bcb170584e480e2b28ad24fd9b9e7dd01a85d404ca5473844359dff2e734e6c2ced15cb340189d4f24555b5c4cb9bf14b1d245152cb060ef21d87b08f69e8f7a4a0e3e4c5d19805eee6539e5ef0c73681639bad242b10548ae7eed3f8949b67c2f7285925d0492d56f640412ca9015b8c732a68b9c10ccca94760b75309a6c931c12cf55f97c26f97e47de4702e2ed1dbf852b64bfe92527ce5e0414c2b3bf2851aac3397043285e7543c2b091ecb4e98245b23bb1565912c072c2a4b089682185c54c3155f1f134ce50ff0a2d6b4293f3d1abd1b7368c482861ca0b08b26261d2ec4d8895514fb90f7a015126b04f69c5a6cd54af9288b32a4a278bc5dee694dc60f1ecb7ae93a4661589c0933df2cb4b6480973792f20c3ae5c2fced9ac942f8f6cf3b1e4dfe55071a44055606404a3eef847c1e606bde4005ee0cb960b11d381b926848e97cd64c7980b46b473289416f95cb406c6a4d2b3997aca5c8ca079ff9ca8597ec2b2cc199246d9381d23789405f91049cef498433ae59740ed52b4d8eb1acfb7126a70c4f39bd1a24d4295eb0e445e514acab58485261afec1d3be54894e3aaebc81a8be0149d3a0eca2a46b1709c899cf9aaa37fbdcce56ccdffc77258307aa5cb019a9e668af38c710eb2abe453ffd64b224ba675a932aba713ed597876427031aa287445c383a510b833b0cc50974c776ba126c8649583376a7c929c4270a6b22304b5109aa42f2d4f5865a7e7891e4f0a9d2674364f9c497f89bd1d793c1267b405a38929247d66b652bac7b91285cca866a499feb537ad2546556783500f461bc71933348abf12fff4d2a7325e171e22259c573ee8e2976541e5335d4d57aa3ea71155d63bf8182734f633c6b6fac4dfc1b3e8ae320d3a0627a2c2df1772adf34b56d094c709d3660b8954d82328a83fa0a0ba0ca889fc0557767048ea55e04c8ef1f00fda55494e64ef01e744cb214698edd452e145a566db6bd6136515b2dea27c21152a9735834b5169667d07c1970264b139a38fac4202cba8999c20d23ff56f9c98195fafc90d75f221b29c6af1130e66c3639644b53cdb5eed8ea7f7e19e5e9c38a42cc808759665a06968b0bbbe2a3b96e1f2e82de42f3f4f5b3a8ddfa8acd6f336994f7ef52999b788f3e270d0eacf3229ab21d9ce74440f4df5b18e9967cbfbde7c191dc02967494d85616303a75e1a438c48f74bb324c3662814ad2a314e711d7627f1a59261f32eca933a291187920cd787c6b11c538efb2e14c7959ecc6132cfeeb661e7e441afcc513b9d1031f4fd3959b0e90d049e1a2922f7972020fcb1ccace9317094173a08c1308d7072eada6ea73a1de2461bda06689713ff72c38878948a41a097a5d8c073b25d70b17861a30099f7f999999d6f7d4a8718c4af9ffecba94ef8387bf121d700231c733438f786a953363834c0f01d678c70cbc4af3b90c4421882653359ef845f70e0ce80ee145984f03cbf904e8e5e19cd8890be9522a6d13e8c65dda869d9d1676f7a9bb3d525018eac5baa06b763fecc1a8c84c840b2d40c9146eb4d6cf02b738ea41dbe803dc3c2a5e7b52996a5578f97622e3608b795031eb1e24c6b3a0db590cfcbdb40a96d6f2bcb40113fb4899217aee3e9ad0d93828d1882714181f605da46e9c775cd1cabe5a355a2db5081cdd895374981f5b6704a27c50398964332ca0c3bc637b73091757172cf89acbd92eed8b35e7b0675aefa0281d172c3954a662197b202e2657f02cc6e4a74b8df9c37c2b8486d0bef22e830e0c090f23044713d2c212f6f93f9432cb7931c15d4db14a10ca64cbb9d50d91bb99176a3b726e2605b76241970f7fb1653fd2b4b22f01d1ebbfc5de66cbf0f5330f5eca3d1108aa7f4968a7862a6e886fe14d1c16341fab3d3b502901d3ceb74148d46387d28ad87b7d6ac2641e6953c8a900d19dc50cbc7a68cde3d1f98c5d70c83ec745ba9e591ad042454cc274f107b7ee709a642e64dd15afc47dcea844b5f288351847bbb4bce975d1e25590ebcaa147819e5f7839d238b5cb255e8cdc1ea5f6e32486f76dbac1996a95f36629aaa4b4468d93d1d3ff2ff06eee999250a22d36bf7347f7a96f54c587b6c5661c5961c21bcf9653904d55c6201e1622527e2a9cc6264f190352fdc5a96ddd1a1a24f2bdeb6b77e638ab02d8296c24b2a4ce266988571be67e98eb1d1e3e99984607980853fc8509cf747ff52a59952433f241d70dc7a5364aecb529157db708dd318d66eccba63bf5b4d6578067c739bb797019637de986b56e1644fd022c115cec5cdf8d552b73eeb6a49649ef4cb3a8a88583220f0aa8708f842b80708910879fb6426c6656e7560e388353eb8786a496c089b51ccceab51c732217966ff91451c5b6190d9367fa4f66428119f345ead06e5ca0223bc622a3b46a9f36d86d17bc8e6b862666d89e96d130a3242c44d5dd1f6007d745698a9f304831b8aca139530ed4fc2475522dd6e4e904467b2b79b237af81ce71645cf8a3386d2fd6d24de1459afb4335b9874d80dabb31ee76c48219c60b02d33c16648d0f2d6365ed935680e79a34b086c9cbab6756057c636c5100f3cb1cf0df4684443f00fd836d059aeb935023a96d5f354fc05e087e547e141dc3f33972277fde5786cc87e6cafc5b4f4a9dadbe7daf00753c9a645188f656d493c53d1b60563e2f0eb82b1b7596b615265275299f575e1631c3f86bf5ff2b0e723ce9ed90ec5e3fdd251a7b9afb47a98c960bf8d161e2d1de158568c383b71f5903a2d5bdceab2d12d92d3a46e7a356e9e4c099a5e0531d71200fe9de3d74bc08e9ac87897d3786a91bf5ff648bcaf30de88d233a903e892d656350806b533efa94c708238291d3450ddd645ea5a5c09a95b7ef436dde6f5b5b3431b9ec87d7e529be220a3ffb2974bc69574a2ae5a927d37bc99c9759aea0380f6d6c03216f624c99b9897b4c32ea5d48842529e6f6864d481eff9a1b6296ab22f3dca89d10c966ba28b61f0d601c5c89d6a53b8a04b16c1b6d34eaa67d71b18beae2e08c626a5f386fa03aa8b75085d3cc5b699da727d1829a51ed582de716a5ea400ddcc9b7a4b2ef0b0f60ffcd6ee679a40ecf1c5dec3bbdaacf173be50fa2eab9b47e062a09c5dd814717d91ec5ef21301b9e2f570a10abcbdd62b6dbc55deb4760f3c557d583df02ff31e0a3a4106b1c79343f3d9c656fa686ed8afb102ae40c95e2700dac39f8236beba49b2e3e5d389ae4349399ad2e3a9ad23309d01b1e63715deb907a780b12e1453e1e8547303d44b41b0b1a41d0d0556caafa28c00ebaf0118c6eaa10704cbd5d58bec59ca37c7d30344b245339052e047656940f6cf9a634c185d99176f421941b211b710b2ff4bf003201593873ab2b5e4968adfee236b268e22d1b0bc36e23b967b9870fcc5dd3fbe085e3385f17f8e41fb150b1f604bc6f145d121680a104eb27fd8fad54b28ae6365e0f4734db7d71f82d6f51776aedab379d31f37de77cfa753a8f69957865ddd20174940eb683c6f92b90af2ab8d3325cbbd00eddffa0780c532b4e0edf160a60a54091c6f7b4465202bccfe19d5d724d0cd88d772483712d9e6c2a14410167e8fca1afc5b35552e7e774d275ed796edaa931fc2b0a2653e4f624c59844d68339be43cf4431f894b4869dbdb8052a94cc40c91805fb3d2b095053b84949504d0e1235b17045a4b3db1d6ef54fc8a470a817f94f852dc5686f7c0ba794085ccc6b6a5b5e19a41958de01e42d98b52930b5f5f7275fefa06bfbfa16f18af8d4a34cc839b22bb3cf3864b3a1f3f13d203d8b4c7d471905fb834a41aaaded8076440f84ddafe9d54ddc84934f257a7e49509bf4c29d391e0b2876fbd11dfc220510b3266173c4cf1429bc5aff696cefb58075610f90622f38f78aec3f55ffcd67b3856e5359af0b093f4527e6ec4b98604ee6d80e9b582212236e0aa0ba01eb7e6d1f2f3129ec4c824d408afc8c4950cd787320fc2788f69041a6c30818f5efd6d7702b3ec6d52e89d4a31fa6f7ecb216ebf4c53a722d7edb0ab23a2fbc01682b2e1dedbe44dc263239bc819042f652e38de2dc478fc06e843ffc649a8d76ca75d424df1ca59cd20fb0d405f1213a9e77af4244440ee73c13739981751a6e84cc802d114a381826904ff72a7e7367143ac29be25cc14740b52d8a26c5e952e8f9ab207eac94982ac12e84cc5ab6574c72533ee719efee00a80c37a0ae9e43a90d27f3efe961718df5a033aa3a8974d7167924f7d357f54bc93e5a60fd7fc9320d2d1a2e606d937034567d44ff35ebef8c44a94e8978268befa21967685ee4b825eb31f8c5973b0c7e080f724c9e242f1611e7f4941b8e691a9e7d1ca82dac047725882954b970846064091b98ef2e04e0b60b2059ef8fae3d8fd3274ae413465ab423bb9d0402e38d8f2a1938999f506484d858b30320ecd8dee4159d866b568734dc194a0a4b2445907b52fa010729a59f80ff81de124f026e7fd0dc3c2c999628c73dd5c40c1c0ff7ca1f875582123bb766d625077bd96fb706d8f4ca5c91313ebb4267559baed80bb53365b80db18065ac5afcb7a9db63988b8b71a217519d96b8c9c03d06f5f93b52765b067e33d1b6327cdc9d7ec64ec7819bf753746d3e0eb4b41b75f5d75e157fcb7f3f6fbafc0a3f087f9c333f0ed2de55c5b51e14772bb7a4dd5e29af3a89bea2066509e06489d311462d3dfa7ef39823b8ee468cb0f8479a219c8b0456d082c1d9183fafcab87c4349ceb60a6ef93b23c9ccb4cdd10ef79c235b8ffb522e2a948e4dcd453ad585e6702ff2fff24384b45a7ae1232d9143b13ca748d17360ec561f6dfafbee7cd3a8bc4b248d21b4a3e6a65a1731938a607f401cd841c374e1f3205fdefe9f0470857044a0fba6287ebdfe907ca6fd4394b7352bf47ea730f4eb2d521058823a36eb1dca487ebb1c3c7c05e86aa49e5827460ab43b39a40d9ef7016eb416db9fa3ef08496ab09eb40ce925f1f88af0d5a0ce90fd9c46acd141fe75021b1ef203617443e4d1aae247b05482a9673a596e6ed13074443f3c3b6c108f93a916d59a6e47efef6b75e93b8857bb35060535ddb4c22fb4c70b6931415c4c89195c4b75ec4514e3a16fd94b263a686593c9b13e40a9d1249782312420d7c08741c16d5b344bc208b3b7be2590869dc45361b248d589392c96c6dda374860b1f05ceb0d3db412f33589944648bd90ca60fb5e4da6d32a13e3421b30efdf7e847b8a10271a7a7bfb2ec43b106b41017372009db8e6e51234ced7e577d332b9bab7051f0dbbc4618a781557ed608824ff470fc7e463219cfc44ca8e322fc7be326795ffaddb01bb3f436707bf8e4a47ae4343ad9adf061273bbd6b900482a439d825f833ef26faa748b01983d6c353115db6f99f10b84c83f89e0451e22f7fde986693ee7346816810ba8737ae5c387746b7917aad2de4d7e941a895fc3e539036dc02fefc3149c1830f9e754b240739de0380874523021ed5f33dae8dd77eebf1efd31b2a0fbff", "hex"), + Buffer.from("3f000a13e0636098c6c0c07002483a6c65daebb6d7938369af03b3522923030800d9105176a7bd4c0ebb9df672303bec7538c8b49701a1a241a208cc620000", "hex"), + Buffer.from("500c0a65594b8f1d5711ee73fa719f3377eeb5c7b1c74eecd879b49d89b193745e1d42ab311d4312649c0d62154424b2200221115845fc055e01b201c262362cd880040b8484c420dd3fc06fb8bf82aaafbeeaee3196ee9ceef3a8faea5da77db09f24c72149fe937c9afcf483657a671777e9b55df32f796a77511fff89c75c1fff81c7a93ea6f141f357bccef535caeb5ff0ba27af89bce5ed83d0fc09536b7bc965f68f98380f0a3ef739e62ef25c21bf3f60e6a9e6f7189f6e7e87f199e633598b787e16cf01cfa53e2778bcd97cd6ee8e9bdfe2e5b8f90dc6e79b5f63bcdd7c8af10bcdaf30de697e89f16ef30b8c2f343fc7f862f3338c2f35b5f0780fcf159e1fe2f9e5a656e80ff0f24a735b16e632a1e0bf8eb95731b7105032bc8ba9d76c5b7aa76aabd8bc83b9d73137c55c965eab9ab7db5ddd1cdbb11cd3539dfe2a76f72b213ebc9f2409d697bafe16d6df689e3310b6b2d295af60e58b38d956e774ea4d37b129ec4bcd73d87e41d7dec0d4bde626a62ee9d46b98ea9a67317545a75ec1d45bcd138225c3f4359dae307d1fd311d337747a22afb76c2926ba68f09ed135357589b5af15496c365064754b97e0304f63ed1d5f334d3dafcbf09db5ccadb1e5ed56b7c09fb0e78eee89b4c94ac7f64134edefdeed92189bb56e36f42feae6609b7060219b716026ebb132f20242f04fe27baa7b932102492feecbf20afb90cc525794cd04cba94829acb326bfbe8eff751a0590ba56ea1e47930e2ca2132a8c50dc29a8fb6d12549422a99b54486e9ce4cce4a9de743c3dc21948998a0a31619ba44241a8d4a10942e2df4e0287ec710d6a789c18e37bc218da903fc56994d351ce7eee671f2adbb6ba2a5e53eca24aac3b646f5bfda4dd7db9dbc590e9abcc66ca7d3c9dd5ba5766d3b6fab1701a6663d1563f52fe86a1f5957b0a3f66b2d8ee82ae1825998f930709493d7af09e621af3e8273e8ebbb60a129a62f17697d95a4e4a06b5c653229b525b2e7419f27d6c3c6a934e95d46f9ab8025e375f0281686b532cbc26d6308de2dcd935e199c7246e45c87efe6372da8a6e46d3aa08c8f0aa2de6ed36c45dbffc43e14f213380386c93acdd6df01cccb43c5508e55df453b960c2df8904c78e02c85f91398140d37657c8d2366d93c2a4503a71ab6422c9c056e0047d2b677124c1932ce1522a679bcce5a02a5cc75cce4eaee9f9495bfd404e8057018f50fed1cf1df35028b8f9fb0a8e34e1a771d6c2215fea620c9386db3eb22858c9a02ea4db6fdbf65428152f19f28fe45d3495cbf0824effed65f1775bfab00d82431de86d10bea6cbf76d59bcf6c32e443d766444f76cb86243d14de0426df5dd36a4b253a1bc696b2b19eec5f45e2d8ef3812836a450343755a25625607b0fba53e57c1a35268a2b49f2bd44860d86bfff59ea7bdd56df517d8422584c0628eb093b7e47920829e0f42776fa133bbd51d7fd761b323b7b6c6796c5241a867d3d9da88beb8084d09dd6701fe5fbbe1a3c04e1a8463fb45d3754703bbeb2999c694286b4dbd6128cefeb3e637668439453bac70e1ed8c11427d4f0f2baad733b78c5162fdac1b99d58cbd60952deb62ed4e0d5b7ccfaa47fd3b62914c53ae37687a6fe5a7d13dba1bda9f1784c3c2f8502c6269913d6be064df5d07ce7826dc9ece0a1c62ee86fa19b6b740a0d5189e16fc8f365221a0e4c0d3f00cc6cf57a5bbdab0ea2e1403c818937c052d835191d0ec3e1850de7541d0189ad6a35fb55c1ec66465fb9c421ba944034a7ec39acad113f87e28cc594ab6b3b9d0b44c1594d63d95677419e112cdc80cc7cfb827ae719c173af0b145c65597074460544d79355d4725b2edaea48a2a6ad24a514bd2422165c402549a2013bcf70a050196b533aa2aedc629fd62267a7437239d05254a9dd2489b465e84a215ec26bb430a9b36dda5018f748863728b1cba7de5fc041e8275b9b257f773419670800532b3947fd25d0638994133413ab720f44d1222f84d923cb2b3ceca2abcc855774603c832153d39af0292dac102e086fd1b71037e751065a0aaa39d9ecc122412b9529f752417eb69eba88162d394732770b6884ec0fba7637c0e8a01de9de481d9d656d6d77426af6578155501d1820d7894508d4cad0b3d06097820c755c6ab48c70598855d1d846e372a41aa97a21a9e48cef174d070bc3c094a98addd6e1acc3672393ab0b3c6e322e51e454f3f2b2e94e0062a229563ddf1381e7dc795146681ea49dabca92aac4a6044fa3ce51235bdde098aca2d599136a0a7ad79a04a74338f5fc829406302aa1693b7a8e7ce6ac87591f4c998a4d0461a4ec25945b8e1216f05fd78ee0204aa933c26bc1e41e15e0858624d31203862572c3f941fcbcebd361ed66cce0ac65b47aa835fc29ade1c6e3c83d8487a7b251ddff49bc1b3c853d813ef1a8f4905b4b3cee9b8f5eb4b443be511a065846c9f79acac96c2247e774cfdcec1e06ff2f849597ab2d40155e244d6d791f18578bdefb5066e88d738ece676246a56f16fd1e08e87d8331ee2b8cc9393739d55d36de3b9d9a076e6318b1ba6c54035ff77bdf2b1156a92d3f66fe931a1e2270b973634fdf99765e6d8cfd257aef943a5cb8df9ae8077d1d29c71974690c0b788d118803ab7096554a7f5af521aa429822a614ecae37332592261dd586e8d6988051c162a70a1eb1a6f2669d959d3e304be088ded648b99096cdfa20eb7f22a190cd53366c34ba0dd0142e60b191f72a1e208c0a61603dcf076b66455953790b148aadb66bc0fd982bd9e352138ae9eaa038d1a90b05cd18e2a0314b6caed9d5205d8a62b8ad33cff2657431159265731675ef1ca8db629042000c3daf5573cb179789228e50a09d31351d7456147358bff652837c640eed2d8b03504199a3cef5a164470821ed53af4178dc7a64329e03f9c4c49285adb5d303142b8cbd9583d1ee679902bb50ab4167886cc6973b81ed56b0602630903898f636f4686699099baa82ceb280dd4f106b29a9bb9d8caca5ba09de83fe80a2208a23ea2e703e6731c9796ecdb66bd1b75dc6b7f0bb9286f80934027863e7cf06ffce3aebdcc32310ac7fa56344cf0254a2db704aeba454fdb477643dcc382c1fe98d0e7180a810f9e590033d0f98b36b9315bce4d279a7a47fc860715f4abd532ad9fd051666dbce143ba33552ef984e98e94e5cb6c0505054651d08149f65461643860f1806849e2fcf5315c13b16af5425dab27404cdf402a34c59fe104134bda23bf12e6743749642cbda956817b01136d647923f203a47b5f2bcd157e75e4335833bf6ce733276f11501a404106982cc3247e6deab1ab17e11573a7edd19849863f6ff518c9431f1f4ada9f0a48e9e284a733d33c682fe35d271a02bc74107a3c2e63dd39a20b2c16756631adef330898c3a2d8f5cb42e3dae518101d5121e61b97f8ffa2b2c25973a1f87308466e04b8f405e91eb8666db23ad1973e8945710ea2d27b80cb7996d3dca7353cf080979477744af5b43664210585af42aa1de57c43e730cd9ccaa174caf0097b44831f44f91c9a5e8863e3527c0e025c298cfa9a2d351c3178893b3c5e88b895bda6b8ac1b58fa72eb75fec2e323e1ca4a2b9aab5ea11b5f5addab6cebcfb1d72f5fee83d1da98836603ac5070a96337ee7e92f8cdef42d886bcef4e08564daf78925afea5b605c509c41aa5567d974509fdb30675e822f2131397e4576e96c825d729cb3c25d8592f8dd83d7437a1d3d9c995cb94dbc79e8199a467a55a8a84784b5a6dbccfa0e4a3de5c2502266122ca8d558d0ef1885f7e3eead857f98510eec0dc1dbaeaab5ab0ed9c4f305863513e552f82e3b6bd07af21bf66729fbc73973c3797a083874d6f74369ec91cf92e68d6f363458f3c164137eccf4acc3ee85e1974237659fc2f78abe34e63c65e919d3c7ccc16b94bd50f333e3daeb59c966c1584c48db2d5e5867990eee3dbec8dd25ed73346646075dd131bd9f4ecfc4aaf98be732303286f695ef2476430a5d8e388293abad57a6552aeb450af7c181fdacb3e6b7201ff36c1bed568f6ea4b673a6c125d78efa06d3529ae9adf4cb06f30e7d8a5d453edc26dc55fd2bd6d84795fd61d777cb7d3730aa4cb4df937d06f61b45d7d7a1dcc06c3cb30d758117dad433b95ff0ecc2d4f70106626e3a3f843451157b93f14f69877b9db5708b9174b35e4371704fa57ad92f7fe3bb89a7530a77b918a7f49c1fb3fc73ce00deaaa956f7c49a2c457a5a1b6b65768b7dd90deafba8d793f75af87c63c4f119cd1434451a37e8654d1391e64d36aa4f320caf12e66cd470e322e965da0f9fefecbf17061936bc0a68c2d484b2f1ebbf27724bbb04b5a07d5223ac0add20924f105d4ee4e2f8c25b47c6e74838ebff8480de7aac0830b31c24f8f73f", "hex"), + Buffer.from("ee270a6d7c4d8b2459969dbdf7ccec997f848767646655d728a789a9c9a976ba37c9c4782230411b3e4e8c2f8a2169068162d988468b5a35cc2cb44806ed4bad9576da080a8fa516a2516eb410c9b8c005da54c0ac662b4804fd0b9ad03de7defbcca25a8b740bffb277eed7b91fef79ae5755f53f4255fdcfea3f56b7557cd91ea35ce66dc4e5e5ed262ee4fac7b7559fe41adb10db2ada9bed19972bf94cbcddf4a9cd728917f2d22fe4ba94eb2bffda6de037ec8babf6c467b7e718e49a6e8fbc36f2326ece3793dd35dc66fc930f6ce25c5eff33bdc5cf6c85c6efc84b23ebd5720db25e3bae77690b455908cf97f2e5847bb7b9b75b275998af56f16f4ce6a05ffe23b96723f79bc9dfcfec46416e54ffff6ea4b87197e7f2353c7f5b2b3085fb63dcac0dbddcaf93a76bdc0f2b1f013da67d6cf2bb0a5f0879a8e23ec67d1ff6aa1cb90396bc6aab5e2e37fa3e56aa0def9f40d9e3bd217474a15b7cb8c96bbbf71a4fa1cdd6eefa056c270054e4aff60ac76e9cf5d5ac9f5953c7819f00fee416e41ac9d6c8fb98e146720ba09fa9abfc7345dd9a90bec04a7437de1aa802bfb53411e466b8747b0a96cd41da4de467d5244b7db575a3e91d1a5d70e65ab5052f64c164ceb0321fcde2da86a3b375bb7c4d232804ac7da18b26fbc057ba9aa92d9997d7bae8c2165c8e8e38a393a8ef605d5d2c5387d9ed856fd3696a59da80b7babcfbdf9fdb4a4957e20a26da25c53ac2f7e87f9fcbedebe9ede5a65ffdadc47b856f9ad1fc055d149e5eab3ae73f5850debd30c3c17dd64216edb157caa84de72fc111194c61e6416c89fdb9d485ba6fd05566ba4a6dd81a7dfa4a9575616b36e230750fd77aa30865f119e33368e81f7bf3b264083ec3553d2ee92d83ac500b0035e8a2ee4de25e5e1bb5615e7a61709eebd31fcbddd2a86c62f1989edf2a27ca65a1a883c57f342c2f78552dc2068d4298ed7b5f586e37d795dcdf9fa9b6ec5b97a33a92fb874417600453c90fa05c1a86601a7c8eafea128ddcb4250710461618a20ff3b285ad6f4a5be1a92ede1a451b27364a059d2ee7cb1867754466865af276214ef4e46cabe25cf1cb9e08ce747378cbd7f19f9aaffb7f4a724dc5704e1c3378917ee70a96a56e8fc652312abafc03742ead439b1bd334b785e2d47881d475692e6398ead577ff261caaf8f8757cacbfee1f23ae1a4f9da16a884a38982efeccb8b133601d96c8a4e227fa0834ce0df899f0627e13fc1316d3b5517a731bcdaf36b1b114b732fdcda968e8edb13178c9b39587b4c2033188a5fa5af9f2acf6cdf4d2a2b7a387d78c9c182ced4daf37969da2c3a64d83cad7329d66fb020498f827bca8fd0be1b6e0d264936609cf0f90e74249c3c409b8e677c7680953b35724c711b51bb6319985f885f7e8096a86d6fc6d6e1cef3245c8d1def4fbbe35dd43a17ba2f2ef9803c333162dab1f41bf37356cc434ea44adc9adc1a6622c8b18b5890112488a3f1819aa0d2e8b6cd17cdabd7366b5055d19c1a019e226865a94df18357a4c01f79698b1f676e26a106049e851935d62fa1fd1e3c602d17df41c8d863a63a37c4d51a289622f431a95c448e6b2955821a336fa82f9526da1b5841cc6a674a1d47eb802051b9d25536c2d72b4356450494ae88c823456bf3526851457747d45e655e6425e0a061a290d49aeee11b6e3e756b099e0be2a44f3245255bb88c25097648f4f1ccd6196ed07a691f2598b04b347bcdd4aba972be5a935b18742d3ede43b2e5734b96a73d71a24b88c8fb642b10e9361318f4ab9502935717a953966a1602c6ba591f94af0c0107e8da3942c200344346a52acb1c807b9b880b89d889970d5f070f15c84225f6b7673b9edfd34aa61921720dccc0468cf3dc4cb6fce78da42643227cae456299a72b96fb663ed318701f5ceedadd72b4778e271229c53326b8aad58dc04cad75b8b790fcdf84361b269ac31ed5aea3767cc1e440345581a31058da0e7a337a63d2324bb6b90d6146b309ca38eea094e5c5be8611bfd4dfdaaa7cfdbaae0742d04ab9534de17234684d132afcfd3380ac656fbf8c23c107e5f623b4c10d71ae3749bd1b0ae552eb715dad8f67cf6e11b0dffad06c7289e678fbad50e66ace3f3e88513479be85b3da00f46583552c8c8b69d7ace73b340d4a7cecaee7faaa7ce3969d47e86202ac45a8568540f9d7e6e76abe93b3f716877f3c6561575f7026d2e416dd4b7541c2f159216a595e639d3ef18a534e35e88ebc37f66f1ae2c32b26509eaaddc4fb288322e0b0564ad276c9a6e35774a09dc43fe337041884b45f19906913ee157e543a11fc9c0ec24ee770968f8c4262a27bc5405d18f2d75c137d6f08dd6216c8a966b8f1ed551678d0cd7365bca976123a41c14a9a2efcf4adfac752d6f4ba5351e03b5b2c0dabcc9f5b8c570c38bfb40f39a2be56b204ca37abd72ec468ba6b630fec855599588850178d98b75cfa8bdccf740ce490dfdd9849fac5aeac3c842eefe4209577f80fbc704c9109bf8588262eb687df8ccca187fd303b92c0b1215b8a0344b2f12f33d5fe9c36889a8f2a0531313bff4b42ecf3ebcd6e6a929298f0e5482212a74369bf4c52f994b5209241da3d4234978873d33086d7b1f6fefd9d1ba53440d8a68e14a8a3af793405e595d51f2246a110bc5d08f4a6e58f500d04b94a38416a04118756b0efc27c6fdd4a92a71e53a1d3b2d0e9ed0104f5d377a1f5edfdecbdff7a52f375aed9387b7289df0d7e4a17d9f3471c7d6125a332ac73d432abf3f32e7dbc6d70059fbdbf0fa7affa42b0e9eac3de28f5e40c006b55570748ea26ff7e8a40e91e8c36c235ac3a6eaee14af858142d648f4715ca05fc411f917ee0d24877a24a854daa93f0883c694da58e10c64e3eca2f7306c8f5e2a28d67aa2602b84b4684886cf7c5c9e95357ba7ec76521ed18f0cf8c68cd1eaa4cbf31f6bf6924f33c1ca3384bfe5db11e054a3e6d744ebd34a325ba22737ed873b48594fc63ecd48dfad128839ae52eba580fc9979f9bed4269376407d39974275ac472681af818876f0d817854e43b19ebc7a8efe6a50bf08a83fa91e2bf74dbfca10aae116e5b6f55d4ab94b140240ef897dcc5ca19d96d7939a23b1b72e5278d562a66dc6b2806e71d411d9b98f9aec4c50771492b82c98df9d63f18d3c7ac3edd88b69ba6f2d7d87b1d89a9b1049dde5277a097693bde4fed22d3ac95304779d5273b556c81e7d9e16a97132b20a9146538dd6884ad58bd28bb9faccf2de489a6b8c7acdeb635c4941268dd0af571fdef3fa75fc756df2fcb94abc98b01e4b1ab4da4feaafd199747694acae3aa383f2d957895e054ce563985e79eb9838b21845617b366f4bf23549ccf94d933ebf40dbad7abf21b3cf273558e975588715daa64ad84d38b4baafe59faab17715e75e3f5fdbc5f07b63b68f9f8f2e8a6e735a6909360186a6f02f34b1742533078c85a9b5f6765272d7064918cb7430ea4ddd61b2ff7036ba3c973ec44b8dd2ccb2573a54070dc9efeeaa3b304b99af2a9f409f9c226d59d269934765722e027b6fad9a95ebafbe0dacec31adf57d868958d95dfd6ca5f11f48361d4c5bdc2130f3b45e19151e5d3c049b75ded295987c10e9a0c5b3f2dfad353b9ad5596e6f157eb6ee09c5957ecba234322b01f2d6aaedad6428da2b4de614538d99800cb4dd39ecaa3044ca060287a576670b5579af242b5e9add39ee349b0e09dc0d415c8a1645d615e47c8b8b9365c949b1b2c57721583d8381874a6a52428c197d520ccf44226fc72d5ea106d26e1bf4ebf842dce25d8d6f69d3b4b470ff90cf8c25c8ad56a5c4732e17f361d1403c430d120259aebb33aa985d95e2f9b6f4e6bb73cbb702049f57024c8741d88d0c635c8738ad8b632560b1da2ea4d5abd7d0d3e3ead55b5e312271d1c397df420f43686fa23d55d9f0c0920ceda7a844f45ac5addc6db7ade591efeb9f10774bed694db2638e050433fd4c3387929d9858e41bd0860fe2dd557a57895df5d51d9b6951096db63bcf0fbf97aef4f7f9f0fb3017d356d5ef93706fd53dfa3699195a44e562f812f1ec423d17a1ef2aca2cc1fb98c49fb75f8970bffc36043165b5bbc125deec44be7b29e8e23d2415cf5019c555eaddb6dd05d010bbb56d998ac23bc5176027d5bbc9e8e42656a461d3f579588ad6846076e75a6db8abdadd7991d6e7c36304c41f2542fc5182790e76bde315add1e1110e72c437b93f69aa75144915151622c597ca0d807e239699d39234993c138d640ad5f00575edadf64b142c51a406aeb75503aaff30b58807c21c6958c46a08e9dd99c158c459094ac8f2d3f43817597839e002497e2a92c8fb692d37687dff13e045bff2e59023a27d9bd2f5d6a1c30977db9960871313b682efe41170352243ebcd3cb1b770a030d3e2a0a6c2c5a19a611691c723c126024f02e542f40fa7bb47a49c5b71448eb7449d81b2cbdd56e2dee22d0034d051f8b01802416145a82a50b933b8912466e15b794b0350835054da08f278d6fa95b8045d0b5c833abd3c46f516a0049e4c74ca086260aaa1529571f56a0e9d3534f12fd5ee746af1725111de5b883ea958f9fc9c1095c0da5d468002196fbfd51b77bbb3524dc3606cd370367f1a727a5381a212df8187c89fe7196d7ea8fe058c7d874bf728b2017c53c5efee9870e4de997112093ad11513ad2b2a2d7f52891791a1b7829a83c55f4e506b158f0c708840c4044f82db9d3b6a3309dec3bf97bfe3508ba601020ff25d68b5a6c766093a20fe6b45fcd713c4a0a16ba2be12ffa643c26833ae290f93e022ee28b8ff7400f61744dc122cc3eca85124b2285d76ea23c6080af6f0580b96ee918a4d43038a50b543cf4dfc6e1f0ed510895a6963866f87742df9922abdc0cd617d0d9570f877400548dda3ac9ce98db4ba3ccee5f3bb6d860eb7164fc7446532d65bfd9caaad36988df8e3709eb3a6133dcd3ffc17fc91de9c1946f2ee200939d155a9bb610eb490ae536b132bb9bde1f2011e8d47c012d1d77997a124fe2b3e009df3f97a48e95d609ea1e8004a3e3e7bce6c04154b3daad1d24788440c700b38c382af66000b09ba6be0f6d2d70709ec5a4204eafa125ab3b4b0cbb5473715b549a64a0ff746506ad8c91720e184071aea3c7e12b57c0ace377884a38903d48a2e123630ce61fe85fb9611aa7300be080a494335d444de1239ac3cc43f7df72d641020dde34fff2ce2068faa5cd12b389a6c21cf36ad79c24235bb80ad6fe4af9b603a16bb741451e5fcd410f76a8784af5045ed73c613a2c9f18ac1e133ea37861b44269045b68a3151f0d6f625c0a6cb5a1e16be4cd7d9f086dd865037929ce481a82f14f1ee46e244394c95acbeac22a5dd27e1cff84943857c2ece3b74aa747994681b1674d02e7ef72f25b8085e341be443f4892197088f13a802523c70800700dacc00d22d918d986409ae5509983c2daa8220c20375f4c9e3181960e80411539168b693ac2ac8da61c57011bda5c3ff9e21f1c4ad78e8109ce001485607b113471b37d0536d86c69273281ba5cd30db85a543306ffc14e971a8c0e46d448c3352963245fe1216ad9cf52f09a6a303aeabc3e3cc2ca798126275d35111161d09a971860494f585ae64c74f748d4fa48bd6bca50169e49170a4e20599882ec440286ee3595dff8a4066007278944c16203ff3dc6ab7994d7c006b2d413b2d6b3de363046ca04d5057521302a8a6299011243908a9652490c1e20f5761b5610e28526903c9f0192d23ca078549c0fd878ac4287ffdd7f2d73fea5f5f6a62d8500d9b8601a661c6506c8d1d93c374ea311e51173a6b8704a4c9634b794f04d06a262731cd59d519d4b4501f9347777814351ce3516217e4272100d45705ebf8d737461d4a69bbed33226fe4b1d8363b2132f8e476f1f0dfc93e8261d0576a93510c6a98b5d29792eab4c7b1c5d30e0655b7cfe2a3629e338bfc0b78c20bc12b6c813f1beb084413990e26ecb96d3c1c458babb821be8847787df22a51385eccd308a70cc2292a4e4dd78cbb1d7df453533a0d348502ad965efa841c915133c6fbdd79a96850240dadd5fac412adec074509c73ee50959ea8fe96d12330263fe1d7a2dd16ba38962acd21abbc58e694de861bbfbd4128f9184aaac6d01ab1d61a94ab3b825d438b4f30f07b5e1fc15d97ffeea4a2fafab6f2a16e2dfe05d5630502aa1672bc3503fc83f128878e34698fe50bdae265afe4229b69de4e828cacda3c71b230c910ecf5acef860871214a196443c049d3e549a2f44b0fda993c7fa44b768882a68dabbb7129d95bb569e283644a7eb0041af54d02b15f4bd0afade0465f904677031593b480c8a8410f0ad54553f4c449f4f595baadae8025a81fbba62b9281779e34edf689117b062bbdb45b522ec230ca70c93f2001997104fca3509d0fbdabcbd654e95089d91f310c064baeb0a4d84e0cf31983e42c34a14097ca80bde19bc7fe5a4d229e06f2680df2ae0b7044c17716a92cf0ae7c01f91264c114513aa810089ee957e5a09179acbc64e22d2457e07993a6f00e334db46cd6e2c675598563a2656d0422190e3f0d346b358479259ed2c429bc36372521231b4b091175eaba79129ade63efc7ad405435a02fb9eec04bcc9dc6bdd1accc6ab13f53da49c687de03111a7444796824830d6a90475050503f1fc3bb7b9d362649daa238286f9208e9e81e9880449d169b3bb47f120f4f2a96e3f156033d6b149673f1d0b7d0d01a0c2dfaa34291414598a37038b4149f7b4d29cbd8ac2d2324f602dcd95f9915671bd2f3c4f9260e2ac11c81d0dc179c0bd06aba82ffef237217cf59b50802e555dec4f241eb52339d684586b744102a938e8ab520825d0b7a0ac682c9a75cc95ff68ee58995dd71b29c037685937e0978db65674ee8c1e6fab2ae6775f17fced9392d71700f1df976026ad06a62324cb9f0c22547dfac93bca7699df9c4fe057a92bcea677b82e7c7a988391c437e8c54229cdc0a24944ab45ae346ca47d7ba258fe25804d86e8add8e0ed994e2e82568c8af8ae20b6145b6bc8c5d1856bb5c312586fcfa740b0515b9d5c5aae338becb375c78d743c37714091b59136b37982f3ade254476ea5a8e5a0b3045349e23314ba52eaac2740b33660a219aa5300e66b807c0664d061e7638cb111381bc2649d0d1ca61996a84e39d0dc444578a7083db86a366781a56fe4e0719262a379f7dcba2d2d3c037d0429f78e41a8a107fb31f3c05d6f3f9dc2fe14fe6acdeb05936b70d8f4ef366a9310e9359cdbb032f17ed6ec7e55d08abdbdee280895a8d9a0104419c0c81d97e8fbb55f3c7ff91badeda440175a1034c0f72b3178f0b431f7c22f6a377e8de22eb84c36ed8ac30bf2c424ce9cf435d27dec838a5fa2ea864d0414b8e49b9d388fdc507db2f49018a18a6d93c0123e88ccdf829320e3feb4526e8dc1a745d17aef80f6700eb6528068a587e7cc63a249e5a32b0f965c5a4e29d6d85858efa28017c49739a950f3c62d8b544678bb85cee451632431aea9d3c8140c6de66bf0ed22bfd9b0cc001acd59ad473c7146d4f9dcea613810ad4eab04db51000ecf2d27e8544be7bed213cd3f5c07caa253aca87d0fe772e95aea6ebc7ac3ee032d5aeb6373ad918ed621e22b10269c18f30d05899f4414c8112107df58b41b7c480719e38ca0f53963d4d65b65b9aae81c099f46072e4ec27441e82de72f8a17c59ac65562f1bd95f47893ac11d82423d3c49ab5a33f4bab2ada27dabc9eb0145052ff411e49b6fbd39caadf9c488995cfc582cd8d6a9b42ea4e85583872031f75515d6686f21e4c10a5f0130349dd2785d373653aed7a396e55c7513b1cad431541904ae24d85160c84c6b22a9442dc66370b849fca437fc9680744066a7d79bb3929a3038de53b1d1946ad708424856db94128aac58c8b9f04e03b02be23609ddf24925ba53598545b1c2522792605194b23cb29144ad986e5b779660db2dd9f3ecfef362798a70ae60c2118cb0ad3c00dd846013510d9b414af5d599a486c5492df99e595662c4bc0db30cc30ecae2b0c638eb689403ecbe27315ca7ebd8953b4a043448613494c7599f21b71ed4fa7c26b84eda9ca4a1e4006d14a53232e800241fed04fc8ab28b2315104fa08adbea6565f5792e8307915612b73f932feae74483074967c455347641b1b1ce9944fa7a9b54ea4c9dc58f496408360ced75ba917f29b4f14e05b79ddbcfa8252b08993ec213c85c756e7c5e2ba900438df12a73c3229697725a6e628541baa2110ee423c1e2059f9694fabd65743b456f60e719c4b0abf6d4f915afd89bc65a0f27a23881d5a06c54b9de9fb0f3e836769c95648bd16d56718c7ebd10b2829852ba58679f4907205026b44b523f2343a8ad40ac2b126a34add8ac09b530dae11558233a2e00bdc0e81132cf8b7509975421a70202de4ae4c6c525e6a7e6dbdfe4718658b1ac450c78a91132a9f390eb5d1dfb611aadd9f9eebfa5df47125fe40f563c47fc14ea01a421b3edaabaa99446c982a87465fc0041163e9f88b83ee0e443423e8d7b4507554d8e7127f8383aaa6a421c7071a75b86782484af8998309fe87b5ea8ed13654eac10f027c24c43431dea0b4a9813d2c6d886ecd8d14e76b4db95f7eeb53542056cad0f0215362ecddba2b60fae24152b3ee52d46bf0bbea127986116699c8b83d5987d0fa4e1030480f1aea8f2e407e0709a2edb4d8bcdbd0ab5f5a7c633c8a5114e666369d874577f7f2354a83c2437b34adc1b58d98bba6b1afc7b62618e6c602aed2622fb9ed13caceb7209aefd0f6c7b0ff18a966db82f3aa94436bed7fd6f2e045429506a755bc0d7b441f517941789f26256dcd4b686caa558ada39079d6b1b76d6da13ab0ca27f74a7d2886972aa357418f817da3241048df1b73f104173ee8080426140f666560aecb874ec0307c15eaf8da04b8827cdb53a75b311bd607d2e6f928aea1311ce409c6311867a1235183d8bc36be9af36e6e065e7b9b8379d61ff912731eed4c725d30eaef188bdb1353724e1cf10e22df3e37a88aa25a4271b9743e5037dbef389acaaf70a1f35fab4331f98ba7d252fb437a7dbea547f7d7a4c2bdefaf4b83c3d8a0cf4ef8db48043214153bf6e1846dfeff4a06b448adbf0113e853d11d332534f3042bb02fa2bddfed1da339a074a04b44cb48cc2414787c1764710b91bdb9dbcc038ca44497a5603a2dcde9c226ac68033d02797e66a228d22da78ce1827665659043751d4da111fab3fe6370136d1d3371fd96c6aecf20361782959b152a9d00de8318a64ce4d178c59e78f6214771c692b8017cc12ab3c54c0dbbaf659f946c24ceeedc9958e1d1ceecfdc561f41d91853f83acea5f2d493c10b9c44c03d5a694fdf8491e492fa0e073fb56e40312db24c7e0af599b847128079aa50f418e49c4df0ac96bc966de8d5311495e2e83f94684c3737e5454d9cc34b3c5f280149865698fc7e3bf0ee9b6ce23bec11dc95808b026eae888c8f5b07946caec89c6076458d24784a9f7350d32122a7bab30df2174e8c16b246fbca35382ea087312c8921ed692e60e827cd07c0dbea4446408fe017f2625e87d38ac5c9e9115e2bf2040eeb4d28efcf9434dc1996d0c6a641a42d275914ddedff150d8298afd91fdd19b5bfd5dbc491d8b396ca3d73807f7f441ded541a1d40c20738c5f0b828bf21a62aa0cdd704db49723b4dbca0f51e428fecc8ab9b565234b0456d380f8f332d31668ed08b12f05fa5437c716024c18a5be350421f7567aa72dfaad81d688468da7bd66a2809ee564f66d14766e487e86b985e3713062b35babc9cd1608a798d1568dec3237601519e6b191c07ce386c71ba5c9f583a1100f776127257954675269687b0bec613c989498e8da09a3eae5efeeb083658bdfc3b5ee5b580335307bcf6ea8e5765b6523555760241bb8e7a7286a6518984bda6bd6580f1a39b1ad299cdb007fa0f8dbab6ee4bb6ce97c69ab442b2cc4b3ec7b810fe95c1a4c56d821ebca19bac2b3810e4ba32b9ae2672dd995c7713b9bc3e224bb0763152f4e4c13a59e4aab59daea69d287ca6f74fc33adcd94db1f3b6509e63280b834b1cbf2ba92d3a89c1d09586fdc68a3c954efd9ff91a542e29e2442af72350905f5e686e8f277c18d233129c53620937a693303813469c55c04bec60503960e9816c2ac2a0f92aac0aecddae2be90bac2091548e4e7584d2f210541d690206c7c553b84400a7f36d0fbc25c0ad8ad1147dadb91c983ad2dc60985118ee6e78f4450f295963270d794facad63c3625d340545e87c116dfe94952d4a4c86ca728b1824cb074c855268b5ec2fc9991a304bdbafac30a637adb52859e6f106b963295e3934762fd3c7249eb52ad1d5f484c482c50776a51a96376c7a78068271401fa88d53c87cfb53275092f14a37e6bda335a2dc921b567ccf8a766edc2925243f1918cbd4c56ed50c0b46203b64b579cd6ec82a10504fe5e7db543fa460d8cf6d5917abb2a84013ba3383405f607ff59e4d80d22eed1458ed087d6fce0a9e1ccda226aab6aab91e9b3696443a53a4ba48f7dda3bc990bbbc18e61b2cb168dad75b3ad368ca4ff2a7264db19dd0c1baf6bd616fd4abcb09866ba57ca0257dcd4a2d2727c52dfcd4dbb3e2812929f51cd91335a137991f48e07305fd0e25befa1c5daef2ead16faddf2f43b561e89a6f1236092a56a6b00af9c806783150d911bbb91fbbacc39d9ab944922698a6393750193ec2aa6b9605320913ea8535aeba979e3b94f7f4b09a8d8db5d0feb97132fcfcbc8d154e3e9afb28a2647abe2cad8523db428dd2b67d6f79dcdf17406eb091e96b9c081ceaa51742f88ae71108dd5353d0ce42fbeb4afe2d69597bd970070e1f50b9a5f9ed4d4cfe5345d376a5b4dcdd7880b59f7caca16e21af2386ad0a56375f8d9671a7f437822dcdad65d59c5a495ea5142011aac5b761acf59b3f9ada36748bbf3e3e22fd739ce3f7c1f7453fae901060e5183d6f546a16b5067a3753e2968783a12b6607ca6fabd725d21ee058c7826a66f4ca25e5792c7a70083a7f0a855cd4c272559325e9e7fd88772d2623c8060b3d5ae940b5bba19492d78b08622f6e8cc9dea43b2ed933931cf48ab9e2fa773fd30ac2dd55024d46ddd8ac1ad22f1c0c789c7c879e5f0c2a4c5a04faefcc1004b882278d40968ab1cd56b768987c76c055e392ba2c9a961d5ae0d57a51b26857763094a9f4374c68c6b370545f0d11084588e40bdde796df54e798ddc50647c6f32be3719ed2ddcccf926ea194159c4e4497a90c71a95e0b13aa9f020132a024ef262281b12a3f349427dce5483c3457f1ffe722d06b928d932983c8e28afb7ace95e99e65f1519f29b1e99a82b7e90c5e0397ef8b7812378167047016eece21ec002db321df6b71b2328db3e625546d2ceb1743365aaf24c3e473ef4f03dc652fc61a70f6c3429a05874a7dbfe545bb956668bb92e773f7ac912b8a997e7cc55d8cd7f579569c17da9c0c8d547cab22d0d70b4796467b9355a2e5f3129e8f3a36fd8717f924781b80db09cd445161281908336e02b2638e03d262fae0ce695ee6f6a8973af5a9a319f46f3606e952ecd03e69319baa65d5bd28e6e3787ff33d7561742f43a81ad8d1378628ca3272bc56a82126631b5dc53a533e234af9b4138b868ec8d65b33ec734dee7c17c5bee6f3fbaa8f408142f959efcbac285bfb908b8491fcb18470fa55b399cacb8eacbf80d072cc5b1ee6b4acd0157b0cf77982557dc42b396b4e7e95c9059d67a43e341a0daa133dc384bb79a01f34a615e29ccf70a532e20412390393bc530567df86d42339d12f6939d0a09ed05f4731f9c7abceef773e536da11e44cdd0d6f3f8fce5e7e674b92731ad50ba1de0b5e4cee352fb86f4321174ff70524c40e8f4b2776af7c8d48700e3594932919f3d425f548150112ea42dda0499321d14273876581acb3456e65a9f2cb78cb0a640ea9c698e3260afe3e3c5e6838dc44af58ac945268d4e04c07bd0b0546583cc0352bc56e2e156be715505f94d7119965bb320977684f4e8f165b7177b3776e0b8489fa707753471e9ef406949db67c0f38e71013486776ebbe140b01c193583a26db728846a7d1fd39994e2b3b6c1bcbce6665e78668de449f6e68d695faf4c01340ea9cf02104a8e9aef51f5a70d73146a1fe6f24a7dfc9e39074790dc13816dd1160391ae0de7049f51af871e206fcddc6517f0323d02b9264d2731da561b35a5938fd92b980e7c5245e9f0c7694a5ca89de2e724b0a70df13ee7b2f41121d7e0c4456d7abe2cdc664eee27df07a1ea9d68b8172de5547d4498743d4b19e3ee050d2eb06612a0920960869e831c1a516902b3b79a5d3cec3d64b762e953a6f18199ef68ab9194bbf45457059787daa075870ec27984eef8bd2b1f1e1f8abf2bb00eddbf590a4fd2c8c3b37959d933555c77b1d3a3ee2b44eafd9aad379a2c0d309d35c774673f93917197d5e8aa5cb58946fe9bdf7ea051b5e30f4bfe28fbf2a9c93ab7483a072c8e5a8a4f70fa5f4c1561fb12d31435ceafe550147466bf5605566b1578b3b0696fa3ce8703172ac666cef227b0fbcd233e9bbf79a10f0f301e8cf80ea76f6745f5b17c61752511e3c2eef3a65ed86b417273f36908264eb216e5bb6d7d6f20eb56ebab318841394ba288c3d8ed36a6315488b74ca82d4cf6cc3b718683c54ce8dafc3ffaacb2916b9e5bdce5df1bb049d2e5fa47719a041a0232f931867dcd58faa2dcd6af3389d9b277affd07841310d5b7e27aa59dcc3330e44b3e8c4f13cdbd2e5c01559dce4dc96125e733b4a362b5e3127ebad943b26ddd18b4ad2319b117b1795958add54d6d5725567baafc11a9a389b62f8a2ecde334d993219ff137328e7beb0b557917ced6759ba532ea6e081bf712e14d19a792d183970c4afbca2d70e8999ffc8989c29d58153d98c68fb9cd3b5ed8e0ed33ab2307369d990ff7514d5ebd83d70389ad60acf7f7dc5cdc6c8da2bb2f42ab9a4179ee36ffd7ca4b0b5c143a565a090a8d49a81d0a916fb3d4360ad7cd4a3bc9a21ad211cc8f59923866369109305374755792c235893aafaecf01c7eea76c1b340915558641166800104fd8641d593836ab25e55835e441a0af1a145b481b74f4a408f3cac046fdf9aedf1f6cabc7c9215c7a414b315e97c9e838f4c0b21589d1a393daa79748d3ec321018fab125aef5b4decc91dfc684e4dbf7cbfd15f5ddae8d2ced1f634b1fd14594feb65d0b7dea95af248d798276d1a0bf4f5c878b0cbac54466540563bfca4bf7296f47744f1cefde92a6ad8a92e7906975522def879987f7815f558b94f38c3f40c14445a63eea529a58ee5d4c352890743a6eff7df0779348d262d6b8fae99f263e0c564c33218e76eed179247b9f0e79bb5c746ad199d2760b5842247fc9cbea0c7b6a6e5a9f99b9f8982faaf218b9e8a6aa07f6d34f3f8ebdff04329f0dfa17e5fce956abab122107f2f757fad74cf4a08fe533c50dcb676447da9daf430748c5606fc3c4e6beae0fb1f0861fbfd88ac3113200fb11ce75882787faba0b5f1cc4310bc49507baa1c6724393e857d59cae4462bc554529cd7cb472f35d88ae5a17ad83f84b67fb003f2ad1d6dc3225b5de4e7d1a9217a8fa2c8e02e75fcc0ffcde1831e09edea07c8439b2e40c5f1bfed3093fa6d2c2225eabf863cbae1ce83946abf4365dd49f8c20a57091a3864ab67ddb1712b7813f6791ff408a9fe2c6ed28bbe60b9ce62f417af7d18328790db073d0ac3d372ef7eaba060b6361444d27ba0841924fa7e6c46a712b62b5555cda57ddd5657d5199512a71ea2eded8b201c51c99bed431b74f5c5409dfc43a5b5048da326ce6df8de83a6c39eed5b1d2f871f8d40e40b97cb87bfffabf5f6210818d5357fa0bd61a7ca5334cab3736d6526933f7249f44d34c1751b1ea61d95388bfd7a305d77a2b72e760a91fa097a4443feea3476d6b6a936b3d319b2e6174e3dfc6d6277bb7d88fb8786a2ebd965bae956c77adc9f5aa9b23ad513c599fc1a074e6b676fba8601d86e05c00535b5f2ed3e3bda2077fbcc330f1d9014b610b30303c13fd8aec8037d21fa4f5eb3b211b3c56754949460f098f030f86f60d0070155a744f0ab6fc3f761fffd92482efd48e6dc37daff19b384a50e0694a952be07cb3574090151959f2907fb9569ed6d041e7e644c5b3b1ab8e7dacea6b2a551df09d4cba5a2717289dcce8edcccc6373fdfa1dd7dc70c96edc433351444432d910924abf3dbcd43fd200fe5b734adcad3d82ca919bb1d9bf8487713fed36ac393ffd5ff03", "hex"), +]*/ // This describes who created this packet. Because when dimensions creates a packet // to send to the client, it puts it through the terraria server packet handlers // to make sure extensions can do something with it. export enum PacketSource { TerrariaServer, - Dimensions + Dimensions, } /** @@ -39,14 +69,25 @@ class TerrariaServerPacketHandler { * @param packet The packet that is being sent * @return Whether or not the packet was handled (and should not be sent) */ - private runPriorHandlers(server: TerrariaServer, packet: RawPacket, source: PacketSource): boolean { + private runPriorHandlers( + server: TerrariaServer, + packet: RawPacket, + source: PacketSource, + ): boolean { let handlers = server.client.globalHandlers.extensions; let handled = false; for (let key in handlers) { let handler = handlers[key]; - if (typeof handler.priorPacketHandlers !== 'undefined' && typeof handler.priorPacketHandlers.serverHandler !== 'undefined') { + if ( + typeof handler.priorPacketHandlers !== "undefined" && + typeof handler.priorPacketHandlers.serverHandler !== "undefined" + ) { try { - handled = handler.priorPacketHandlers.serverHandler.handlePacket(server, packet, source); + handled = handler.priorPacketHandlers.serverHandler.handlePacket( + server, + packet, + source, + ); if (handled) { break; } @@ -70,14 +111,25 @@ class TerrariaServerPacketHandler { * @param packet The packet that is being sent * @return Whether or not the packet was handled (and should not be sent) */ - private runPostHandlers(server: TerrariaServer, packet: RawPacket, source: PacketSource): boolean { + private runPostHandlers( + server: TerrariaServer, + packet: RawPacket, + source: PacketSource, + ): boolean { let handlers = server.client.globalHandlers.extensions; let handled = false; for (let key in handlers) { let handler = handlers[key]; - if (typeof handler.postPacketHandlers !== 'undefined' && typeof handler.postPacketHandlers.serverHandler !== 'undefined') { + if ( + typeof handler.postPacketHandlers !== "undefined" && + typeof handler.postPacketHandlers.serverHandler !== "undefined" + ) { try { - handled = handler.postPacketHandlers.serverHandler.handlePacket(server, packet, source); + handled = handler.postPacketHandlers.serverHandler.handlePacket( + server, + packet, + source, + ); if (handled) { break; } @@ -101,7 +153,11 @@ class TerrariaServerPacketHandler { * @param packet The packet that is being sent * @return The packet data (either origin or modified) */ - public handlePacket(server: TerrariaServer, packet: RawPacket, source: PacketSource): Buffer | null { + public handlePacket( + server: TerrariaServer, + packet: RawPacket, + source: PacketSource, + ): Buffer | null { this.currentServer = server; let priorHandled: boolean = this.runPriorHandlers(server, packet, source); @@ -110,9 +166,7 @@ class TerrariaServerPacketHandler { } // Parse everything except TileSectionSend because that is a big packet - const parsedResult = Parser.parse(packet.data, true, [ - "TileSectionSend" - ]); + const parsedResult = Parser.parse(packet.data, true, ["TileSectionSend"]); let handled: boolean = false; if (parsedResult.TAG === "Error") { @@ -121,22 +175,31 @@ class TerrariaServerPacketHandler { switch (parseError.TAG) { case "ReaderError": if (parseError._0.error instanceof Error) { - server.client.logging.error(`Error parsing packet: ${parseError._0.context} ${parseError._0.error.message}\n${packet.data.toString("hex")}`); + server.client.logging.error( + `Error parsing packet: ${parseError._0.context} ${parseError._0.error.message}`, + ); } else { - server.client.logging.error(`Error parsing packet: ${parseError._0.context}\n${packet.data.toString("hex")}`); + server.client.logging.error( + `Error parsing packet: ${parseError._0.context}`, + ); } break; default: - server.client.logging.error(`Error parsing packet: ${PacketTypes[packet.packetType]} ${parseError.TAG}\n${packet.data.toString("hex")}`); + server.client.logging.error( + `Error parsing packet: ${PacketTypes[packet.packetType]} ${parseError.TAG}`, + ); break; } } else { switch (parseError) { case "IgnoredPacket": - server.client.logging.debug(`Ignoring packet: ${PacketTypes[packet.packetType]}`); - break; + server.client.logging.info( + `Ignoring packet: ${PacketTypes[packet.packetType]}`, + ); default: - server.client.logging.error(`Error parsing packet: ${PacketTypes[packet.packetType]} ${parseError}\n${packet.data.toString("hex")}`); + server.client.logging.error( + `Error parsing packet: ${PacketTypes[packet.packetType]} ${parseError}`, + ); break; } } @@ -161,7 +224,7 @@ class TerrariaServerPacketHandler { handled = this.handleCompleteConnectionAndSpawn(); break; case "DimensionsUpdate": - handled = this.handleDimensionsUpdate(parsed._0); + handled = this.handleDimensionsUpdate(packet); break; case "NpcUpdate": handled = this.handleNPCUpdate(parsed._0); @@ -218,7 +281,7 @@ class TerrariaServerPacketHandler { if (!client.ingame) { client.disconnect(reason); } else { - var color = "C8FF00"; + var color = "FF6A6A"; var message = client.options.language.phrases.dimensionDisconnectedYou; const disconnectOnKick = client.options.disconnectOnKick; switch (disconnectOnKick.type) { @@ -236,11 +299,24 @@ class TerrariaServerPacketHandler { } break; } + //CSFT - 修改 client.sendChatMessage(message, color); - client.sendChatMessage(new NetworkText(1, client.options.language.phrases.reason + "{0}", [reason]), color); + client.sendChatMessage( + new NetworkText(1, client.options.language.phrases.reason + "{0}", [ + reason, + ]), + color, + ); client.wasKicked = true; client.connected = false; - + setTimeout(() => { + client.disconnect( + new NetworkText(1, client.options.language.phrases.reason + "{0}", [ + reason, + ]), + ); + }, 5000); + //client.disconnect_CSFT(new NetworkText(1, client.options.language.phrases.reason + "{0}", [reason])); if (this.socket) { this.socket.destroy(); } @@ -260,12 +336,13 @@ class TerrariaServerPacketHandler { // Send IP Address if (!this.currentServer.isVanilla) { - let ip: string = getProperIP(this.currentServer.client.socket.remoteAddress as string) as string; + let ip: string = getProperIP( + this.currentServer.client.socket.remoteAddress as string, + ) as string; const packetData = new PacketWriter() .setType(PacketTypes.DimensionsUpdate) .packInt16(0) // Type - .packString(ip) - .data; + .packString(ip).data; this.currentServer.sendDirect(packetData); } @@ -282,7 +359,10 @@ class TerrariaServerPacketHandler { private handleWorldInfo(worldInfo: WorldInfoPacket.t): boolean { this.currentServer.isSSC = worldInfo.eventInfo.serverSidedCharacters; - if (this.currentServer.client.waitingCharacterRestore && !this.currentServer.isSSC) { + if ( + this.currentServer.client.waitingCharacterRestore && + !this.currentServer.isSSC + ) { this.restoreInventory(this.currentServer.client); this.restoreLife(this.currentServer.client); this.restoreMana(this.currentServer.client); @@ -290,7 +370,10 @@ class TerrariaServerPacketHandler { } this.currentServer.client.waitingCharacterRestore = false; - if (this.currentServer.client.state === ClientState.ConnectionSwitchEstablished) { + if ( + this.currentServer.client.state === + ClientState.ConnectionSwitchEstablished + ) { this.currentServer.spawn.x = worldInfo.spawnX; this.currentServer.spawn.y = worldInfo.spawnY; @@ -300,7 +383,7 @@ class TerrariaServerPacketHandler { .setType(PacketTypes.GetSectionOrRequestSync) .packSingle(-1) .packSingle(-1) - .data; + .packSingle(0).data; this.currentServer.sendDirect(getSection); this.currentServer.client.state = ClientState.FinalisingSwitch; @@ -310,8 +393,7 @@ class TerrariaServerPacketHandler { let dimensionsUpdate = new PacketWriter() .setType(PacketTypes.DimensionsUpdate) .packInt16(this.currentServer.client.routingInformation.type) - .packString(this.currentServer.client.routingInformation.info) - .data; + .packString(this.currentServer.client.routingInformation.info).data; this.currentServer.sendDirect(dimensionsUpdate); this.currentServer.client.routingInformation = null; } @@ -340,14 +422,19 @@ class TerrariaServerPacketHandler { numberOfDeathsPve: 0, numberOfDeathsPvp: 0, team: 0, - }) + }); if (spawnPlayer.TAG === "Error") { - this.currentServer.client.logging.error(`Error creating spawn player packet: ${spawnPlayer._0}`); + this.currentServer.client.logging.error( + `Error creating spawn player packet: ${spawnPlayer._0}`, + ); return true; } - if (typeof server.client !== 'undefined' && typeof server.client.socket !== 'undefined') { + if ( + typeof server.client !== "undefined" && + typeof server.client.socket !== "undefined" + ) { server.sendDirect(spawnPlayer._0); if (!server.client.preventSpawnOnJoin) { @@ -356,7 +443,6 @@ class TerrariaServerPacketHandler { } } - if (server.client.state === ClientState.FinishinedSendingInventory) { server.client.state = ClientState.FullyConnected; server.client.sendWaitingPackets(); @@ -389,44 +475,75 @@ class TerrariaServerPacketHandler { * @param packet The dimensions update packet * @return Whether or not the packet has been handled (and is not to be sent) */ - private handleDimensionsUpdate(dimensionsUpdate: DimensionsUpdatePacket.t): boolean { - switch (dimensionsUpdate) { - case "GamemodesJoinMode": - return true; - default: - switch (dimensionsUpdate.TAG) { - case "RealIpAddress": - return true; - case "SwitchServer": - if (this.currentServer.client.servers[dimensionsUpdate._0.toLowerCase()]) { - const phrases = this.currentServer.client.options.language.phrases; - this.currentServer.client.sendChatMessage(phrases.shiftingToDimension.replace("${name}", dimensionsUpdate._0), "FF0000"); - this.currentServer.client.changeServer(this.currentServer.client.servers[dimensionsUpdate._0.toLowerCase()], { - preventSpawnOnJoin: false - }); - } - return true; - case "SwitchServerManual": - const currentServerIp = this.currentServer.socket.remoteAddress; - let ip = dimensionsUpdate._0; - const port: number = dimensionsUpdate._1; - if (ip === "127.0.0.1" && typeof currentServerIp !== "undefined") { - ip = currentServerIp; - } - this.currentServer.client.changeServer({ - name: `${ip}:${port}`, - serverIP: ip, - serverPort: port, - hidden: false, - isVanilla: false, - }, { - preventSpawnOnJoin: false - }); - return true; - default: - return true; + private handleDimensionsUpdate(packet: RawPacket): boolean { + const reader: PacketReader = new PacketReader(packet.data); + const messageType: number = reader.readInt16(); + const messageContent: string = reader.readString(); + + // Switch server + if (messageType === 2) { + if (this.currentServer.client.servers[messageContent.toLowerCase()]) { + if (messageContent.toLowerCase() === this.currentServer.name) { + this.currentServer.client.sendChatMessage( + "[i:3459]CSFT[i:3459] 你当前已经在此服务器了!", + "00CED1", + ); + } else { + const phrases = this.currentServer.client.options.language.phrases; + this.currentServer.client.sendChatMessage( + phrases.shiftingToDimension.replace("${name}", messageContent), + "00CED1", + ); + this.currentServer.client.changeServer( + this.currentServer.client.servers[messageContent.toLowerCase()], + { + preventSpawnOnJoin: false, + }, + ); } + } + } + + if (messageType === 3) { + const currentServerIp = this.currentServer.socket.remoteAddress; + let ip = messageContent; + const port: number = reader.readUInt16(); + if (ip === "127.0.0.1" && typeof currentServerIp !== "undefined") { + ip = currentServerIp; + } + this.currentServer.client.changeServer( + { + name: `${ip}:${port}`, + serverIP: ip, + serverPort: port, + hidden: false, + isVanilla: false, + }, + { + preventSpawnOnJoin: false, + }, + ); + } + + if (messageType === 4) { + if (this.currentServer.client.servers[messageContent.toLowerCase()]) { + const extraJoinInformation: string = reader.readString(); + const phrases = this.currentServer.client.options.language.phrases; + this.currentServer.client.sendChatMessage( + phrases.shiftingToDimension.replace("${name}", messageContent), + "00CED1", + ); + this.currentServer.client.changeServer( + this.currentServer.client.servers[messageContent.toLowerCase()], + { + preventSpawnOnJoin: false, + extraJoinInformation, + }, + ); + } } + + return true; } /** @@ -445,9 +562,14 @@ class TerrariaServerPacketHandler { if (npcTypeId === 0 || zeroLife) { this.currentServer.entityTracking.NPCs[npcSlotId] = undefined; } else { - let npc: NPC | undefined = this.currentServer.entityTracking.NPCs[npcSlotId] + let npc: NPC | undefined = + this.currentServer.entityTracking.NPCs[npcSlotId]; if (npc === undefined) { - this.currentServer.entityTracking.NPCs[npcSlotId] = new NPC(npcSlotId, npcTypeId, life === "Max" ? 1 : life._0); + this.currentServer.entityTracking.NPCs[npcSlotId] = new NPC( + npcSlotId, + npcTypeId, + life === "Max" ? 1 : life._0, + ); } else { npc.life = life === "Max" ? 1 : life._0; npc.type = npcTypeId; @@ -463,10 +585,17 @@ class TerrariaServerPacketHandler { * @param packet The update item drop packet * @return Whether or not this packet was handled (and should not be sent) */ - private handleUpdateItemDrop(itemDropUpdate: ItemDropUpdatePacket.t): boolean { + private handleUpdateItemDrop( + itemDropUpdate: ItemDropUpdatePacket.t, + ): boolean { const { itemDropId, stack, prefix, itemId } = itemDropUpdate; if (itemDropId > 0) { - this.currentServer.entityTracking.items[itemDropId] = new Item(itemDropId, stack, prefix, itemId); + this.currentServer.entityTracking.items[itemDropId] = new Item( + itemDropId, + stack, + prefix, + itemId, + ); } else { this.currentServer.entityTracking.items[itemDropId] = undefined; } @@ -495,7 +624,10 @@ class TerrariaServerPacketHandler { * * @param packet The player inventory slot packet */ - private handlePlayerInventorySlot(inventorySlot: PlayerInventorySlotPacket.t, rawPacket: RawPacket): boolean { + private handlePlayerInventorySlot( + inventorySlot: PlayerInventorySlotPacket.t, + rawPacket: RawPacket, + ): boolean { let handled = false; const { playerId } = inventorySlot; if (playerId === this.currentServer.client.player.id) { @@ -503,8 +635,8 @@ class TerrariaServerPacketHandler { this.currentServer.packetQueue.push({ rawPacket: { data: rawPacket.data, - packetType: rawPacket.packetType - } + packetType: rawPacket.packetType, + }, }); handled = true; } @@ -513,14 +645,30 @@ class TerrariaServerPacketHandler { return handled; } - private handlePlayerInfo(playerInfo: PlayerInfoPacket.t, rawPacket: RawPacket): boolean { - const nameMismatchesRequireRewrite = this.currentServer.client.options.nameChanges?.mode === "rewrite"; - const isAboutCurrentClient = playerInfo.playerId === this.currentServer.client.player.id; - const isMismatchedName = this.currentServer.client.player.name !== playerInfo.name; - const isAllowedToRename = (this.currentServer.client.options.nameChanges?.exclusions.indexOf(this.currentServer.name) ?? -1) > -1; - if (nameMismatchesRequireRewrite && isAboutCurrentClient && isMismatchedName) { + private handlePlayerInfo( + playerInfo: PlayerInfoPacket.t, + rawPacket: RawPacket, + ): boolean { + const nameMismatchesRequireRewrite = + this.currentServer.client.options.nameChanges?.mode === "rewrite"; + const isAboutCurrentClient = + playerInfo.playerId === this.currentServer.client.player.id; + const isMismatchedName = + this.currentServer.client.player.name !== playerInfo.name; + const isAllowedToRename = + (this.currentServer.client.options.nameChanges?.exclusions.indexOf( + this.currentServer.name, + ) ?? -1) > -1; + if ( + nameMismatchesRequireRewrite && + isAboutCurrentClient && + isMismatchedName + ) { if (!isAllowedToRename) { - const playerInfoPacket = PlayerInfoPacket.toBuffer({ ...playerInfo, name: this.currentServer.client.player.name }); + const playerInfoPacket = PlayerInfoPacket.toBuffer({ + ...playerInfo, + name: this.currentServer.client.player.name, + }); if (playerInfoPacket.TAG === "Ok") { rawPacket.data = playerInfoPacket._0; } @@ -542,13 +690,14 @@ class TerrariaServerPacketHandler { this.currentServer.entityTracking.pylons.push({ x: x, y: y, - type: pylonType + type: pylonType, }); break; case "Removed": - this.currentServer.entityTracking.pylons = this.currentServer.entityTracking.pylons.filter(pylon => { - pylon.x !== x || pylon.y !== y || pylon.type !== pylonType; - }); + this.currentServer.entityTracking.pylons = + this.currentServer.entityTracking.pylons.filter((pylon) => { + pylon.x !== x || pylon.y !== y || pylon.type !== pylonType; + }); break; case "RequestTeleport": break; @@ -604,6 +753,6 @@ class TerrariaServerPacketHandler { private restoreVisuals(client: Client): void { client.player.setVisuals(); } -}; +} export default TerrariaServerPacketHandler;