inputWire) {
solderTo(inputWire, SolderType.PUT);
}
+ /**
+ * A convenience method that should be used iff the order in which the {@code inputWires} are soldered is important.
+ * Using this method reduces the chance of inadvertent reordering when code is modified or reorganized. All
+ * invocations of this method should carefully document why the provided ordering is important.
+ *
+ * Since this method is specifically for input wires that require a certain order, at least two input wires must be
+ * provided.
+ *
+ * @param inputWires – an ordered list of the input wire to forward output data to
+ * @throws IllegalArgumentException if the size of {@code inputWires} is less than 2
+ * @see #solderTo(InputWire)
+ */
+ public void orderedSolderTo(@NonNull final List> inputWires) {
+ if (inputWires.size() < 2) {
+ throw new IllegalArgumentException("List must contain at least 2 input wires.");
+ }
+ inputWires.forEach(this::solderTo);
+ }
+
/**
* Specify an input wire where output data should be passed. This forwarding operation respects back pressure.
*
diff --git a/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/wiring/wires/OutputWireTests.java b/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/wiring/wires/OutputWireTests.java
new file mode 100644
index 000000000000..5ee65c7c23ee
--- /dev/null
+++ b/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/wiring/wires/OutputWireTests.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.swirlds.common.wiring.wires;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import com.swirlds.base.time.Time;
+import com.swirlds.common.context.PlatformContext;
+import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder;
+import com.swirlds.common.wiring.model.WiringModel;
+import com.swirlds.common.wiring.schedulers.TaskScheduler;
+import com.swirlds.common.wiring.schedulers.builders.TaskSchedulerType;
+import com.swirlds.common.wiring.wires.input.BindableInputWire;
+import com.swirlds.common.wiring.wires.input.InputWire;
+import java.util.List;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Tests the functionality of output wires
+ */
+public class OutputWireTests {
+
+ /**
+ * Test that the ordered solder to method forwards data in the proper order.
+ *
+ * @param count the number of data to send through the wires
+ */
+ @ParameterizedTest()
+ @ValueSource(ints = {10_000})
+ void orderedSolderToTest(final int count) {
+ final PlatformContext platformContext =
+ TestPlatformContextBuilder.create().build();
+ final WiringModel model = WiringModel.create(platformContext, Time.getCurrent(), ForkJoinPool.commonPool());
+
+ final TaskScheduler intForwarder = model.schedulerBuilder("intForwarder")
+ .withType(TaskSchedulerType.DIRECT)
+ .build()
+ .cast();
+ final TaskScheduler firstComponent = model.schedulerBuilder("firstComponent")
+ .withType(TaskSchedulerType.DIRECT)
+ .build()
+ .cast();
+ final TaskScheduler secondComponent = model.schedulerBuilder("secondComponent")
+ .withType(TaskSchedulerType.DIRECT)
+ .build()
+ .cast();
+
+ final BindableInputWire intInput = intForwarder.buildInputWire("intInput");
+ final BindableInputWire firstComponentInput = firstComponent.buildInputWire("ints");
+ final BindableInputWire secondComponentInput = secondComponent.buildInputWire("ints");
+
+ // Send integers to the first component before the second component
+ final List> inputList = List.of(firstComponentInput, secondComponentInput);
+ intForwarder.getOutputWire().orderedSolderTo(inputList);
+
+ intInput.bind((i -> i));
+
+ final AtomicInteger firstCompRecNum = new AtomicInteger();
+ final AtomicInteger secondCompRecNum = new AtomicInteger();
+ final AtomicInteger firstCompErrorCount = new AtomicInteger();
+ final AtomicInteger secondCompErrorCount = new AtomicInteger();
+
+ firstComponentInput.bind(i -> {
+ if (firstCompRecNum.incrementAndGet() <= secondCompRecNum.get()) {
+ firstCompErrorCount.incrementAndGet();
+ }
+ });
+ secondComponentInput.bind(i -> {
+ if (firstCompRecNum.get() != secondCompRecNum.incrementAndGet()) {
+ secondCompErrorCount.incrementAndGet();
+ }
+ });
+
+ for (int i = 0; i < count; i++) {
+ intInput.put(i);
+ }
+
+ assertEquals(0, firstCompErrorCount.get(), "The first component should always receive data first");
+ assertEquals(0, secondCompErrorCount.get(), "The second component should always receive data second");
+ }
+
+ /**
+ * Test that the expected exceptions are thrown
+ */
+ @Test
+ void orderedSolderToThrows() {
+ final PlatformContext platformContext =
+ TestPlatformContextBuilder.create().build();
+ final WiringModel model = WiringModel.create(platformContext, Time.getCurrent(), ForkJoinPool.commonPool());
+
+ final TaskScheduler schedulerA = model.schedulerBuilder("schedulerA")
+ .withType(TaskSchedulerType.DIRECT)
+ .build()
+ .cast();
+ final TaskScheduler schedulerB = model.schedulerBuilder("schedulerB")
+ .withType(TaskSchedulerType.DIRECT)
+ .build()
+ .cast();
+
+ InputWire inputWire = schedulerB.buildInputWire("inputWire");
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> schedulerA.getOutputWire().orderedSolderTo(List.of(inputWire)),
+ "Method should throw when provided less than two input wires.");
+ }
+}
From eaca230f8aeec2388c4d4a1ac5c46bbb22961e9b Mon Sep 17 00:00:00 2001
From: Lazar Petrovic
Date: Mon, 5 Feb 2024 17:49:40 +0100
Subject: [PATCH 03/16] chore: remove hashgraph demo (#11352)
Signed-off-by: Lazar Petrovic
---
hedera-node/config.txt | 7 -
.../src/test/resources/testfiles/config.txt | 6 -
.../src/main/resource/testfiles/config.txt | 7 -
.../updateConfigPortNumber/sdk/config.txt | 7 -
hedera-node/test-clients/testfiles/config.txt | 7 -
.../demos/HashgraphDemo/build.gradle.kts | 19 -
.../demo/hashgraph/HashgraphDemoMain.java | 506 ------------------
.../demo/hashgraph/HashgraphDemoState.java | 116 ----
.../src/main/java/module-info.java | 6 -
.../tests/AddressBookTestingTool/README.md | 2 +-
platform-sdk/sdk/config.txt | 9 +-
platform-sdk/sdk/docs/index.html | 3 -
platform-sdk/sdk/docs/installCLI.html | 7 +-
platform-sdk/sdk/docs/installEclipse.html | 131 -----
platform-sdk/sdk/docs/runBrowser.html | 1 -
.../LegacyConfigPropertiesLoaderTest.java | 5 +-
.../swirlds/platform/config/legacy/config.txt | 3 +-
17 files changed, 8 insertions(+), 834 deletions(-)
delete mode 100644 platform-sdk/platform-apps/demos/HashgraphDemo/build.gradle.kts
delete mode 100644 platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoMain.java
delete mode 100644 platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoState.java
delete mode 100644 platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/module-info.java
delete mode 100755 platform-sdk/sdk/docs/installEclipse.html
diff --git a/hedera-node/config.txt b/hedera-node/config.txt
index 839663e904b2..e9db3aec23f2 100755
--- a/hedera-node/config.txt
+++ b/hedera-node/config.txt
@@ -3,7 +3,6 @@
###############################################################################################
swirld, 123
-# app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all
# app, GameDemo.jar, 9000, 9000
# app, HelloSwirldDemo.jar
# app, CryptocurrencyDemo.jar
@@ -72,12 +71,6 @@ nextNodeId, 1
#
# FilesystemDemo.jar parameters: none
#
-# HashGraphDemo.jar takes parameters that give the initial checkbox settings,
-# in the same order they appear are on the screen, with 1 to check it
-# and 0 to not check it, followed by the number of events to display
-# (or “all”). The first parameter controls whether it runs
-# slowly (1) or runs at full speed (0).
-#
# GameDemo.jar parameters:
# height: height of the board (in cells). Player moves 1 cell at a time.
# width: width of the board (in cells). Player moves 1 cell at a time.
diff --git a/hedera-node/hedera-mono-service/src/test/resources/testfiles/config.txt b/hedera-node/hedera-mono-service/src/test/resources/testfiles/config.txt
index c07b64999488..c1f52c1cd832 100644
--- a/hedera-node/hedera-mono-service/src/test/resources/testfiles/config.txt
+++ b/hedera-node/hedera-mono-service/src/test/resources/testfiles/config.txt
@@ -63,12 +63,6 @@ TLS, on #on to enable TLS, off to disable.
#
# FilesystemDemo.jar parameters: none
#
-# HashGraphDemo.jar takes parameters that give the initial checkbox settings,
-# in the same order they appear are on the screen, with 1 to check it
-# and 0 to not check it, followed by the number of events to display
-# (or “all”). The first parameter controls whether it runs
-# slowly (1) or runs at full speed (0).
-#
# GameDemo.jar parameters:
# height: height of the board (in cells). Player moves 1 cell at a time.
# width: width of the board (in cells). Player moves 1 cell at a time.
diff --git a/hedera-node/test-clients/src/main/resource/testfiles/config.txt b/hedera-node/test-clients/src/main/resource/testfiles/config.txt
index 9b596e48e478..115c9a9b5b34 100644
--- a/hedera-node/test-clients/src/main/resource/testfiles/config.txt
+++ b/hedera-node/test-clients/src/main/resource/testfiles/config.txt
@@ -3,7 +3,6 @@
###############################################################################################
swirld, 123
-# app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all
# app, GameDemo.jar, 9000, 9000
# app, HelloSwirldDemo.jar
# app, CryptocurrencyDemo.jar
@@ -73,12 +72,6 @@ nextNodeId, 1
#
# FilesystemDemo.jar parameters: none
#
-# HashGraphDemo.jar takes parameters that give the initial checkbox settings,
-# in the same order they appear are on the screen, with 1 to check it
-# and 0 to not check it, followed by the number of events to display
-# (or “all”). The first parameter controls whether it runs
-# slowly (1) or runs at full speed (0).
-#
# GameDemo.jar parameters:
# height: height of the board (in cells). Player moves 1 cell at a time.
# width: width of the board (in cells). Player moves 1 cell at a time.
diff --git a/hedera-node/test-clients/src/main/resource/testfiles/updateFeature/updateConfigPortNumber/sdk/config.txt b/hedera-node/test-clients/src/main/resource/testfiles/updateFeature/updateConfigPortNumber/sdk/config.txt
index f54750bfc290..356ee32f83dc 100755
--- a/hedera-node/test-clients/src/main/resource/testfiles/updateFeature/updateConfigPortNumber/sdk/config.txt
+++ b/hedera-node/test-clients/src/main/resource/testfiles/updateFeature/updateConfigPortNumber/sdk/config.txt
@@ -3,7 +3,6 @@
###############################################################################################
swirld, 123
-# app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all
# app, GameDemo.jar, 9000, 9000
# app, HelloSwirldDemo.jar
# app, CryptocurrencyDemo.jar
@@ -71,12 +70,6 @@ swirld, 123
#
# FilesystemDemo.jar parameters: none
#
-# HashGraphDemo.jar takes parameters that give the initial checkbox settings,
-# in the same order they appear are on the screen, with 1 to check it
-# and 0 to not check it, followed by the number of events to display
-# (or “all”). The first parameter controls whether it runs
-# slowly (1) or runs at full speed (0).
-#
# GameDemo.jar parameters:
# height: height of the board (in cells). Player moves 1 cell at a time.
# width: width of the board (in cells). Player moves 1 cell at a time.
diff --git a/hedera-node/test-clients/testfiles/config.txt b/hedera-node/test-clients/testfiles/config.txt
index 586238c48b8f..a788f583b5eb 100644
--- a/hedera-node/test-clients/testfiles/config.txt
+++ b/hedera-node/test-clients/testfiles/config.txt
@@ -3,7 +3,6 @@
###############################################################################################
swirld, 123
-# app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all
# app, GameDemo.jar, 9000, 9000
# app, HelloSwirldDemo.jar
# app, CryptocurrencyDemo.jar
@@ -72,12 +71,6 @@ nextNodeId, 1
#
# FilesystemDemo.jar parameters: none
#
-# HashGraphDemo.jar takes parameters that give the initial checkbox settings,
-# in the same order they appear are on the screen, with 1 to check it
-# and 0 to not check it, followed by the number of events to display
-# (or “all”). The first parameter controls whether it runs
-# slowly (1) or runs at full speed (0).
-#
# GameDemo.jar parameters:
# height: height of the board (in cells). Player moves 1 cell at a time.
# width: width of the board (in cells). Player moves 1 cell at a time.
diff --git a/platform-sdk/platform-apps/demos/HashgraphDemo/build.gradle.kts b/platform-sdk/platform-apps/demos/HashgraphDemo/build.gradle.kts
deleted file mode 100644
index 8fa39e51d1e7..000000000000
--- a/platform-sdk/platform-apps/demos/HashgraphDemo/build.gradle.kts
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2020-2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-plugins { id("com.hedera.hashgraph.application") }
-
-application.mainClass.set("com.swirlds.demo.hashgraph.HashgraphDemoMain")
diff --git a/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoMain.java b/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoMain.java
deleted file mode 100644
index c2ea1f496fd1..000000000000
--- a/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoMain.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * Copyright (C) 2022-2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.swirlds.demo.hashgraph;
-/*
- * This file is public domain.
- *
- * SWIRLDS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
- * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SWIRLDS SHALL NOT BE LIABLE FOR
- * ANY DAMAGES SUFFERED AS A RESULT OF USING, MODIFYING OR
- * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
- */
-
-import static com.swirlds.metrics.api.Metrics.PLATFORM_CATEGORY;
-
-import com.swirlds.common.platform.NodeId;
-import com.swirlds.metrics.api.Metrics;
-import com.swirlds.platform.Browser;
-import com.swirlds.platform.ParameterProvider;
-import com.swirlds.platform.gui.GuiPlatformAccessor;
-import com.swirlds.platform.gui.SwirldsGui;
-import com.swirlds.platform.gui.model.GuiModel;
-import com.swirlds.platform.network.Network;
-import com.swirlds.platform.state.address.AddressBookNetworkUtils;
-import com.swirlds.platform.system.BasicSoftwareVersion;
-import com.swirlds.platform.system.Platform;
-import com.swirlds.platform.system.SwirldMain;
-import com.swirlds.platform.system.SwirldState;
-import com.swirlds.platform.system.address.Address;
-import com.swirlds.platform.system.address.AddressBook;
-import com.swirlds.platform.system.events.PlatformEvent;
-import java.awt.Checkbox;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
-import java.awt.Label;
-import java.awt.TextField;
-import java.awt.geom.Rectangle2D;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
-import java.util.Locale;
-import java.util.function.BiFunction;
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-
-/**
- * This app draws the hashgraph on the screen. Events are circles, with earlier ones lower. Events are color
- * coded: A non-witness is gray, and a witness has a color of green (famous), blue (not famous) or red
- * (undecided fame). When the event becomes part of the consensus, its color becomes darker.
- */
-public class HashgraphDemoMain implements SwirldMain {
- /** delay after each screen update in milliseconds (250 means update 4 times per second) */
- private static final long screenUpdateDelay = 250;
-
- /** color for outline of labels */
- static final Color LABEL_OUTLINE = new Color(255, 255, 255);
- /** color for unknown-fame witness, non-consensus */
- static final Color LIGHT_RED = new Color(192, 0, 0);
- /** color for unknown-fame witness, consensus (which never happens) */
- static final Color DARK_RED = new Color(128, 0, 0);
- /** color for famous witness, non-consensus */
- static final Color LIGHT_GREEN = new Color(0, 192, 0);
- /** color for famous witness, consensus */
- static final Color DARK_GREEN = new Color(0, 128, 0);
- /** color for non-famous witness, non-consensus */
- static final Color LIGHT_BLUE = new Color(0, 0, 192);
- /** color for non-famous witness, consensus */
- static final Color DARK_BLUE = new Color(0, 0, 128);
- /** color for non-witness, non-consensus */
- static final Color LIGHT_GRAY = new Color(160, 160, 160);
- /** non-witness, consensus */
- static final Color DARK_GRAY = new Color(0, 0, 0);
- /** the app is run by this */
- public Platform platform;
- /** ID for this member */
- public NodeId selfId;
- /** the entire window, including Swirlds menu, Picture, checkboxes */
- JFrame window;
- /** the JFrame with the hashgraph */
- Picture picture;
- /** a copy of the set of events at one moment, which paintComponent will draw */
- private PlatformEvent[] eventsCache;
- /** the number of members in the addressBook */
- private int numMembers = -1;
- /** number of columns (equals number of members) */
- private int numColumns;
- /** the nicknames of all the members */
- private String[] names;
-
- /** if checked, this member calls to gossip once per second */
- private Checkbox slowCheckbox;
- /** if checked, freeze the display (don't update it) */
- private Checkbox freezeCheckbox;
- /** if checked, color vertices only green (non-consensus) or blue (consensus) */
- private Checkbox simpleColorsCheckbox;
-
- // the following checkboxes control which labels to print on each vertex
-
- /** the round number for the event */
- private Checkbox labelRoundCheckbox;
- /** the consensus round received for the event */
- private Checkbox labelRoundRecCheckbox;
- /** the consensus order number for the event */
- private Checkbox labelConsOrderCheckbox;
- /** the consensus time stamp for the event */
- private Checkbox labelConsTimestampCheckbox;
- /** the generation number for the event */
- private Checkbox labelGenerationCheckbox;
- /** the ID number of the member who created the event */
- private Checkbox labelCreatorCheckbox;
-
- /** only draw this many events, at most */
- private TextField eventLimit;
-
- /** format the consensusTimestamp label */
- DateTimeFormatter formatter =
- DateTimeFormatter.ofPattern("H:m:s.n").withLocale(Locale.US).withZone(ZoneId.systemDefault());
-
- private static final BasicSoftwareVersion softwareVersion = new BasicSoftwareVersion(1);
-
- /**
- * Return the color for an event based on calculations in the consensus algorithm A non-witness is gray,
- * and a witness has a color of green (famous), blue (not famous) or red (undecided fame). When the
- * event becomes part of the consensus, its color becomes darker.
- *
- * @param event
- * the event to color
- * @return its color
- */
- private Color eventColor(final PlatformEvent event) {
- if (simpleColorsCheckbox.getState()) { // if checkbox checked
- return event.isConsensus() ? LIGHT_BLUE : LIGHT_GREEN;
- }
- if (!event.isWitness()) {
- return event.isConsensus() ? DARK_GRAY : LIGHT_GRAY;
- }
- if (!event.isFameDecided()) {
- return event.isConsensus() ? DARK_RED : LIGHT_RED;
- }
- if (event.isFamous()) {
- return event.isConsensus() ? DARK_GREEN : LIGHT_GREEN;
- }
- return event.isConsensus() ? DARK_BLUE : LIGHT_BLUE;
- }
-
- /**
- * This panel has the statistics and hashgraph picture, and appears in the window below all the
- * settings, right below "display last ___ events".
- */
- private class Picture extends JPanel {
- private static final long serialVersionUID = 1L;
- int ymin, ymax, width, n;
- double r;
- long minGen, maxGen;
- /** row to draw next in the window */
- int row;
- /** column to draw next in the window */
- int col;
- /** font height, in pixels */
- int textLineHeight;
-
- /**
- * find x position on the screen for the given event event
- *
- * @param event
- * the event (displayed as a circle on the screen)
- * @return the x coordinate for that event
- */
- private int xpos(final PlatformEvent event) {
- // To support Noncontiguous NodeId, the index of the NodeId in the address book is used.
- final int nodeIndex = platform.getAddressBook().getIndexOfNodeId(event.getCreatorId());
- return (nodeIndex + 1) * width / (numColumns + 1);
- }
-
- /**
- * find y position on the screen for the given event event
- *
- * @param event
- * the event (displayed as a circle on the screen)
- * @return the y coordinate for that event
- */
- private int ypos(final PlatformEvent event) {
- return (event == null) ? -100 : (int) (ymax - r * (1 + 2 * (event.getGeneration() - minGen)));
- }
-
- /**
- * called by paintComponent to draw text at the top of the window
- *
- * @param g
- * the graphics context passed to paintComponent
- * @param text
- * a String.format formatting string
- * @param value
- * the value to pass to String.format to be formatted
- */
- private void print(final Graphics g, final String text, final double value) {
- g.drawString(String.format(text, value), col, row++ * textLineHeight - 3);
- }
-
- /** {@inheritDoc} */
- public void paintComponent(final Graphics g) {
- super.paintComponent(g);
- g.setFont(new Font(Font.MONOSPACED, 12, 12));
- final FontMetrics fm = g.getFontMetrics();
- final int fa = fm.getMaxAscent();
- final int fd = fm.getMaxDescent();
- textLineHeight = fa + fd;
- final int numMem = platform.getAddressBook().getSize();
- calcNames();
- width = getWidth();
-
- row = 1;
- col = 10;
- final Metrics metrics = platform.getContext().getMetrics();
- final double createCons = (double) metrics.getValue(PLATFORM_CATEGORY, "secC2C");
- final double recCons = (double) metrics.getValue(PLATFORM_CATEGORY, "secR2C");
-
- print(g, "%5.0f trans_per_sec", (double) metrics.getValue(PLATFORM_CATEGORY, "trans_per_sec"));
- print(g, "%5.0f events_per_sec", (double) metrics.getValue(PLATFORM_CATEGORY, "events_per_sec"));
- print(g, "%4.0f%% duplicate events", (double) metrics.getValue(PLATFORM_CATEGORY, "dupEvPercent"));
-
- print(g, "%5.3f sec, propagation time", createCons - recCons);
- print(g, "%5.3f sec, create to consensus", createCons);
- print(g, "%5.3f sec, receive to consensus", recCons);
- final Address address = platform.getAddressBook().getAddress(platform.getSelfId());
- print(g, "Internal: " + Network.getInternalIPAddress() + " : " + address.getPortInternal(), 0);
-
- final int height1 = (row - 1) * textLineHeight; // text area at the top
- final int height2 = getHeight() - height1; // the main display, below the text
- g.setColor(Color.BLACK);
- ymin = (int) Math.round(height1 + 0.025 * height2);
- ymax = (int) Math.round(height1 + 0.975 * height2) - textLineHeight;
- for (int i = 0; i < numColumns; i++) {
- final int x = (i + 1) * width / (numColumns + 1);
- g.drawLine(x, ymin, x, ymax);
- final Rectangle2D rect = fm.getStringBounds(names[i], g);
- g.drawString(names[i], (int) (x - rect.getWidth() / 2), (int) (ymax + rect.getHeight()));
- }
-
- PlatformEvent[] events = eventsCache;
- if (events == null) { // in case a screen refresh happens before any events
- return;
- }
- int maxEvents;
- try {
- maxEvents = Math.max(0, Integer.parseInt(eventLimit.getText()));
- } catch (final NumberFormatException err) {
- maxEvents = 0;
- }
-
- if (maxEvents > 0) {
- events = Arrays.copyOfRange(events, Math.max(0, events.length - maxEvents), events.length);
- }
-
- minGen = Integer.MAX_VALUE;
- maxGen = Integer.MIN_VALUE;
- for (final PlatformEvent event : events) {
- minGen = Math.min(minGen, event.getGeneration());
- maxGen = Math.max(maxGen, event.getGeneration());
- }
- maxGen = Math.max(maxGen, minGen + 2);
- n = numMem + 1;
- final double gens = maxGen - minGen;
- final double dy = (ymax - ymin) * (gens - 1) / gens;
- r = Math.min(width / n / 4, dy / gens / 2);
- final int d = (int) (2 * r);
-
- // for each event, draw 2 downward lines to its parents
- for (final PlatformEvent event : events) {
- g.setColor(eventColor(event));
- final PlatformEvent e1 = event.getSelfParent();
- final PlatformEvent e2 = event.getOtherParent();
- if (e1 != null && e1.getGeneration() >= minGen) {
- g.drawLine(xpos(event), ypos(event), xpos(event), ypos(e1));
- }
- if (e2 != null && e2.getGeneration() >= minGen) {
- g.drawLine(xpos(event), ypos(event), xpos(e2), ypos(e2));
- }
- }
-
- // for each event, draw its circle
- for (final PlatformEvent event : events) {
- final PlatformEvent e1 = event.getSelfParent();
- final PlatformEvent e2 = event.getOtherParent();
- if (e1 == null || e2 == null) {
- continue; // discarded events have no parents, so skip them
- }
- final Color color = eventColor(event);
- g.setColor(color);
- g.fillOval(xpos(event) - d / 2, ypos(event) - d / 2, d, d);
- g.setFont(g.getFont().deriveFont(Font.BOLD));
-
- String s = "";
-
- if (labelRoundCheckbox.getState()) {
- s += " " + event.getRoundCreated();
- }
- if (labelRoundRecCheckbox.getState() && event.getRoundReceived() > 0) {
- s += " " + event.getRoundReceived();
- }
- // if not consensus, then there's no order yet
- if (labelConsOrderCheckbox.getState() && event.isConsensus()) {
- s += " " + event.getConsensusOrder();
- }
- if (labelConsTimestampCheckbox.getState()) {
- final Instant t = event.getConsensusTimestamp();
- if (t != null) {
- s += " " + formatter.format(t);
- }
- }
- if (labelGenerationCheckbox.getState()) {
- s += " " + event.getGeneration();
- }
- if (labelCreatorCheckbox.getState()) {
- s += " " + event.getCreatorId(); // ID number of member who created it
- }
- if (s != "") {
- final Rectangle2D rect = fm.getStringBounds(s, g);
- final int x = (int) (xpos(event) - rect.getWidth() / 2. - fa / 4.);
- final int y = (int) (ypos(event) + rect.getHeight() / 2. - fd / 2);
- g.setColor(LABEL_OUTLINE);
- g.drawString(s, x - 1, y - 1);
- g.drawString(s, x + 1, y - 1);
- g.drawString(s, x - 1, y + 1);
- g.drawString(s, x + 1, y + 1);
- g.setColor(color);
- g.drawString(s, x, y);
- }
- }
- }
- }
-
- /**
- * This is just for debugging: it allows the app to run in Eclipse. If the config.txt exists and lists a
- * particular SwirldMain class as the one to run, then it can run in Eclipse (with the green triangle
- * icon).
- *
- * @param args
- * these are not used
- */
- public static void main(final String[] args) {
- Browser.parseCommandLineArgsAndLaunch(args);
- }
-
- /** Fill in the names array, with the name of each member. Also set numColumns and numMembers. */
- private void calcNames() {
- final AddressBook addressBook = platform.getAddressBook();
- numColumns = addressBook.getSize();
- if (numColumns != numMembers) {
- numMembers = numColumns;
- names = new String[numColumns];
- for (int i = 0; i < numColumns; i++) {
- final NodeId nodeId = addressBook.getNodeId(i);
- names[i] = addressBook.getAddress(nodeId).getNickname();
- }
- }
- }
-
- // ////////////////////////////////////////////////////////////////////
- // the following are the methods required by the SwirldState interface
- // ////////////////////////////////////////////////////////////////////
-
- @Override
- public void init(final Platform platform, final NodeId id) {
- this.platform = platform;
- this.selfId = id;
- final String[] parameters = ParameterProvider.getInstance().getParameters();
-
- GuiModel.getInstance()
- .setAbout(
- platform.getSelfId(),
- "Hashgraph Demo v. 1.1\n" + "\n"
- + "trans_per_sec = # transactions added to the hashgraph per second\n"
- + "events_per_sec = # events added to the hashgraph per second\n"
- + "duplicate events = percentage of events a member receives that they already know.\n"
- + "bad events_per_sec = number of events per second received by a member that are invalid.\n"
- + "propagation time = average seconds from creating a new event to a given member receiving it.\n"
- + "create to consensus = average seconds from creating a new event to knowing its consensus order.\n"
- + "receive to consensus = average seconds from receiving an event to knowing its consensus order.\n"
- + "Witnesses are colored circles, non-witnesses are black/gray.\n"
- + "Dark circles are part of the consensus, light are not.\n"
- + "Fame is true for green, false for blue, unknown for red.\n");
- final Address address = platform.getAddressBook().getAddress(platform.getSelfId());
- final int winCount = AddressBookNetworkUtils.getLocalAddressCount(platform.getAddressBook());
- final int winNum = GuiModel.getInstance().getInstanceNumber(platform.getSelfId());
- window = SwirldsGui.createWindow(
- platform, address, winCount, winNum, false); // Uses BorderLayout. Size is chosen by the Platform
- window.setLayout(new GridBagLayout()); // use a layout more powerful than BorderLayout
- int p = 0; // which parameter to use
- final BiFunction cb = (n, s) -> new Checkbox(
- s, null, parameters.length > n && parameters[n].trim().equals("1"));
-
- slowCheckbox = cb.apply(p++, "Slow: this member initiates gossip once a second");
- freezeCheckbox = cb.apply(p++, "Freeze: don't change this window");
- simpleColorsCheckbox = cb.apply(p++, "Simple colors: blue for consensus, green for not");
- labelRoundCheckbox = cb.apply(p++, "Labels: Round created");
- labelRoundRecCheckbox = cb.apply(p++, "Labels: Round received (consensus)");
- labelConsOrderCheckbox = cb.apply(p++, "Labels: Order (consensus)");
- labelConsTimestampCheckbox = cb.apply(p++, "Labels: Timestamp (consensus)");
- labelGenerationCheckbox = cb.apply(p++, "Labels: Generation");
- labelCreatorCheckbox = cb.apply(p++, "Labels: Creator ID");
-
- eventLimit = new TextField(parameters.length <= p ? "" : parameters[p].trim(), 5);
- p++;
-
- final GridBagConstraints constr = new GridBagConstraints();
- constr.fill = GridBagConstraints.NONE; // don't stretch components
- constr.gridwidth = GridBagConstraints.REMAINDER; // each component uses all cells in a row
- constr.anchor = GridBagConstraints.WEST; // left align each component in its cell
- constr.weightx = 0; // don't put extra space in the middle
- constr.weighty = 0;
- constr.gridx = 0; // start in upper-left cell
- constr.gridy = 0;
- constr.insets = new Insets(0, 10, -4, 0); // add external padding on left, remove from bottom
-
- final Component[] comps = new Component[] {
- slowCheckbox,
- freezeCheckbox,
- simpleColorsCheckbox,
- labelRoundCheckbox,
- labelRoundRecCheckbox,
- labelConsOrderCheckbox,
- labelConsTimestampCheckbox,
- labelGenerationCheckbox,
- labelCreatorCheckbox
- };
- for (final Component c : comps) {
- window.add(c, constr);
- constr.gridy++;
- }
- constr.gridwidth = 1; // each component is one cell
- window.add(new Label("Display the last "), constr);
- constr.gridx++;
- constr.insets = new Insets(0, 0, -4, 0); // don't pad on left
- window.add(eventLimit, constr);
- constr.gridx++;
- window.add(new Label(" events"), constr);
- constr.gridx = 0;
- constr.gridy++; // skip a line betwen settings and stats
- window.add(new Label(" "), constr);
- constr.gridy++;
- constr.weighty = 1.0; // give the picture all leftover space
- constr.weightx = 1.0;
- constr.fill = GridBagConstraints.BOTH; // stretch the picture to fit
- constr.gridwidth = GridBagConstraints.REMAINDER; // picture uses a whole row
- picture = new Picture();
- window.add(picture, constr);
- window.setVisible(true);
- }
-
- @Override
- public void run() {
- while (true) {
- if (window != null && !freezeCheckbox.getState()) {
- eventsCache = GuiPlatformAccessor.getInstance().getAllEvents(platform.getSelfId());
- // after this getAllEvents call, the set of events to draw is frozen
- // for the duration of this screen redraw. But their status (consensus or not) may change
- // while it is being drawn. If an event is discarded while being drawn, then it forgets its
- // parents, and won't be drawn here.
- window.repaint();
- // the network will stop creating events if there are no user transactions. We will submit 1 byte
- // transactions in order to have events created continuously
- platform.createTransaction(new byte[1]);
- }
- try {
- Thread.sleep(screenUpdateDelay);
- } catch (final Exception e) {
- }
- }
- }
-
- @Override
- public SwirldState newState() {
- return new HashgraphDemoState();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public BasicSoftwareVersion getSoftwareVersion() {
- return softwareVersion;
- }
-}
diff --git a/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoState.java b/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoState.java
deleted file mode 100644
index 0bbe6c55cdb4..000000000000
--- a/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/com/swirlds/demo/hashgraph/HashgraphDemoState.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2016-2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.swirlds.demo.hashgraph;
-/*
- * This file is public domain.
- *
- * SWIRLDS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
- * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SWIRLDS SHALL NOT BE LIABLE FOR
- * ANY DAMAGES SUFFERED AS A RESULT OF USING, MODIFYING OR
- * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
- */
-
-import com.swirlds.common.io.streams.SerializableDataInputStream;
-import com.swirlds.common.io.streams.SerializableDataOutputStream;
-import com.swirlds.common.merkle.MerkleLeaf;
-import com.swirlds.common.merkle.impl.PartialMerkleLeaf;
-import com.swirlds.platform.state.PlatformState;
-import com.swirlds.platform.system.Round;
-import com.swirlds.platform.system.SwirldState;
-
-/**
- * The state for the hashgraph demo. See the comments for com.swirlds.demos.HashgraphDemoMain
- */
-public class HashgraphDemoState extends PartialMerkleLeaf implements SwirldState, MerkleLeaf {
-
- /**
- * The version history of this class.
- * Versions that have been released must NEVER be given a different value.
- */
- private static class ClassVersion {
- /**
- * In this version, serialization was performed by copyTo/copyToExtra and deserialization was performed by
- * copyFrom/copyFromExtra. This version is not supported by later deserialization methods and must be handled
- * specially by the platform.
- */
- public static final int ORIGINAL = 1;
- /**
- * In this version, serialization was performed by serialize/deserialize.
- */
- public static final int MIGRATE_TO_SERIALIZABLE = 2;
- }
-
- private static final long CLASS_ID = 0xe8cc5e77ddbe70e4L;
-
- // ///////////////////////////////////////////////////////////////////
-
- public HashgraphDemoState() {}
-
- private HashgraphDemoState(final HashgraphDemoState sourceState) {
- super(sourceState);
- }
-
- @Override
- public synchronized void handleConsensusRound(final Round round, final PlatformState platformState) {}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public synchronized HashgraphDemoState copy() {
- throwIfImmutable();
- return new HashgraphDemoState(this);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void serialize(final SerializableDataOutputStream out) {}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void deserialize(final SerializableDataInputStream in, final int version) {}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long getClassId() {
- return CLASS_ID;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int getVersion() {
- return ClassVersion.MIGRATE_TO_SERIALIZABLE;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int getMinimumSupportedVersion() {
- return ClassVersion.MIGRATE_TO_SERIALIZABLE;
- }
-}
diff --git a/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/module-info.java b/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/module-info.java
deleted file mode 100644
index 838c2212dc87..000000000000
--- a/platform-sdk/platform-apps/demos/HashgraphDemo/src/main/java/module-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-module com.swirlds.demo.hashgraph {
- requires com.swirlds.common;
- requires com.swirlds.metrics.api;
- requires com.swirlds.platform.core;
- requires java.desktop;
-}
diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md b/platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md
index b79276dc6957..61cd869e44a8 100644
--- a/platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md
+++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md
@@ -9,7 +9,7 @@ Be sure to clean and assemble the whole project if the java files have been modi
### config.txt
comment out the current app
```
-# app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all
+# app, StatsDemo.jar, 1, 3000, 0, 100, -1, 200
```
uncomment the AddressBookTestingTool.jar
```
diff --git a/platform-sdk/sdk/config.txt b/platform-sdk/sdk/config.txt
index fd344d3b4180..45efeda015ad 100644
--- a/platform-sdk/sdk/config.txt
+++ b/platform-sdk/sdk/config.txt
@@ -3,10 +3,9 @@
###############################################################################################
swirld, 123
- app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all
# app, HelloSwirldDemo.jar
# app, CryptocurrencyDemo.jar
-# app, StatsDemo.jar, 1, 3000, 0, 100, -1, 200
+ app, StatsDemo.jar, 1, 3000, 0, 100, -1, 200
# ** BEGIN REMOVE FROM SDK RELEASES **
@@ -95,12 +94,6 @@ nextNodeId, 4
#
# FilesystemDemo.jar parameters: none
#
-# HashGraphDemo.jar takes parameters that give the initial checkbox settings,
-# in the same order they appear are on the screen, with 1 to check it
-# and 0 to not check it, followed by the number of events to display
-# (or “all”). The first parameter controls whether it runs
-# slowly (1) or runs at full speed (0).
-#
# GameDemo.jar parameters:
# height: height of the board (in cells). Player moves 1 cell at a time.
# width: width of the board (in cells). Player moves 1 cell at a time.
diff --git a/platform-sdk/sdk/docs/index.html b/platform-sdk/sdk/docs/index.html
index 1bfac215d3ae..934d96505d65 100644
--- a/platform-sdk/sdk/docs/index.html
+++ b/platform-sdk/sdk/docs/index.html
@@ -51,9 +51,6 @@ Useful documentation
The Swirlds SDK demo apps
- How to install / recompile the example apps in Eclipse
-
-
How to install / recompile the example apps from the command line
diff --git a/platform-sdk/sdk/docs/installCLI.html b/platform-sdk/sdk/docs/installCLI.html
index 355dc5f580d8..cb85d9534b33 100755
--- a/platform-sdk/sdk/docs/installCLI.html
+++ b/platform-sdk/sdk/docs/installCLI.html
@@ -86,8 +86,8 @@
How to install / recompile the example apps from the command line
nano config.txt
- The file has a line near the top starting with HashgraphDemo, and another line near it starting with #
- HelloSwirldDemo. Add a # character at the start of the HashgraphDemo line to comment it out, and
+ The file has a line near the top starting with StatsDemo, and another line near it starting with #
+ HelloSwirldDemo. Add a # character at the start of the StatsDemo line to comment it out, and
delete the # from the start of the HelloSwirldDemo line to uncomment it.
@@ -123,8 +123,7 @@ How to install / recompile the example apps from the command line
that output, it needs to be run from a console rather than by double clicking on the swirlds.jar file icon.
The above steps can be repeated to develop a new app, entirely from the command line. Though it may be easier to
- use an Integrated Development Environment (IDE), such as developing the app in
- Eclipse.
+ use an Integrated Development Environment (IDE).
diff --git a/platform-sdk/sdk/docs/installEclipse.html b/platform-sdk/sdk/docs/installEclipse.html
deleted file mode 100755
index 8204044a563f..000000000000
--- a/platform-sdk/sdk/docs/installEclipse.html
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
-
-
-
-
-How to install / recompile the example apps in Eclipse
-
-These versions (or later) should be downloaded and installed:
-
-
-
-The above link is to OpenJDK 12, which is free. Oracle JDK 11+ requires a paid license.
-
-If the Swirlds browser freezes when it is first launched, it is possible that the operating system needs a utility
- installed to collect entropy, such as the utilities for
- Debian
- or
- Raspbian, which may also work on other versions of Linux or Unix.
-
-
The examples below use slashes in paths, but on Windows they will be backslashes.
-
-Eclipse creates a workspace directory on your drive. Find it and ensure the files from the SDK are there. The sdk
-directory should be put somewhere in the directory tree under the Eclipse workspace directory. For example, the
-needed files might be put here:
-
- - workspace/git/sdk/data/apps/
- - workspace/git/sdk/swirlds.jar
- - workspace/git/sdk/config.txt
-
-
-The last one, config.txt, is used to make the Swirlds browser automatically run a particular app. It can even
- make multiple instances of it run and communicate with each other, as if they were on multiple machines. The
- comments in the file explain how to change it.
-
-The demo apps are now ready to run.
-
-The apps come with source code, and can be recompiled in Eclipse. To import the projects into Eclipse and recompile
- them:
-
-
-
- -
- import the demo projects
-
- - choose menu WINDOW > SHOW VIEW > OTHER > JAVA > PACKAGE EXPLORER (if the package explorer isn't already
- visible)
-
- - select menu FILE > IMPORT
- - click the triangle by MAVEN if it doesn't have entries under it
- - click EXISTING MAVEN PROJECTS
- - click NEXT
- - click the BROWSE button in the upper-right, on the same line as ROOT DIRECTORY
- - go to and select the sdk directory, such as workspace/git/sdk/
- - click OPEN
- - make sure the checkboxes by all the listed projects are checked
- - click FINISH
-
-
-
- -
- recompile and run HashgraphDemo
-
- - edit the config.txt file to choose which app to run. The instructions are given as comments in
- the file.
-
- - in the package explorer, click on the triangle by HashgraphDemo to open it
-
- right click on the pom.xml under HashgraphDemo and choose RUN AS > MAVEN INSTALL
- - click on src under HashgraphDemo to select it
- - click the green arrow button on the top bar to run it
- - if it asks, click JAVA APPLICATION and click OK
- - it won't run yet, but that's ok
- - select menu RUN > RUN CONFIGURATIONS
- - select the ARGUMENTS tab
- - under WORKING DIRECTORY select OTHER
- - click FILESYSTEM and navigate to just inside the sdk directory
- - click OPEN
- - click APPLY
- - click CLOSE
- - in the package explorer click on src under HashgraphDemo to select it
- - click the green arrow button on the top bar to run it
- - the app should run. When you are done with it, choose QUIT under its menu, or close its window.
-
-
- - recompile and run each of the other demos, using the above steps
-
-
-At this point, all of the demos have been recompiled, and their new .jar files have been stored in the data/apps
- folder for the browser to use. If you change the source code of one of the apps, repeat the above steps to recompile
- it (do a MAVEN INSTALL) and run it (click the green arrow). It is possible to
- run the new app, either in the browser, or from the command line, or in Eclipse
- itself.
-
-
- Back
-
-
\ No newline at end of file
diff --git a/platform-sdk/sdk/docs/runBrowser.html b/platform-sdk/sdk/docs/runBrowser.html
index 2ae809061926..2c7a7b7ce15c 100644
--- a/platform-sdk/sdk/docs/runBrowser.html
+++ b/platform-sdk/sdk/docs/runBrowser.html
@@ -35,7 +35,6 @@ Install Java and Swirlds
The Swirlds browser needs both the Java runtime and the Swirlds SDK to be installed. This can be done by following the
instructions for either
-installing with Eclipse or
installing with Maven. To simply run the apps without creating new apps, there is no need
to install Eclipse or Maven. It is sufficient to just follow the installation instructions for the Java SDK and the JCE
security policy file.
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/config/legacy/LegacyConfigPropertiesLoaderTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/config/legacy/LegacyConfigPropertiesLoaderTest.java
index e180a219d01e..46b50281f948 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/config/legacy/LegacyConfigPropertiesLoaderTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/config/legacy/LegacyConfigPropertiesLoaderTest.java
@@ -72,10 +72,9 @@ void testRealisticConfig() throws UnknownHostException {
Assertions.assertEquals("123", properties.swirldName().get());
Assertions.assertTrue(properties.appConfig().isPresent(), "Value must be set");
- Assertions.assertEquals(
- "HashgraphDemo.jar", properties.appConfig().get().jarName());
+ Assertions.assertEquals("StatsDemo.jar", properties.appConfig().get().jarName());
Assertions.assertArrayEquals(
- new String[] {"1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "all"},
+ new String[] {"1", "3000", "0", "100", "-1", "200"},
properties.appConfig().get().params());
final AddressBook addressBook = properties.getAddressBook();
diff --git a/platform-sdk/swirlds-platform-core/src/test/resources/com/swirlds/platform/config/legacy/config.txt b/platform-sdk/swirlds-platform-core/src/test/resources/com/swirlds/platform/config/legacy/config.txt
index 5c1f3c58990b..31ea825ade2b 100644
--- a/platform-sdk/swirlds-platform-core/src/test/resources/com/swirlds/platform/config/legacy/config.txt
+++ b/platform-sdk/swirlds-platform-core/src/test/resources/com/swirlds/platform/config/legacy/config.txt
@@ -10,10 +10,9 @@
###############################################################################################
swirld, 123
- app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all
# app, HelloSwirldDemo.jar
# app, CryptocurrencyDemo.jar
-# app, StatsDemo.jar, 1, 3000, 0, 100, -1, 200
+ app, StatsDemo.jar, 1, 3000, 0, 100, -1, 200
# ** BEGIN REMOVE FROM SDK RELEASES **
From 9c1f467e2fae53be71e12e0152209bddd0ac6dae Mon Sep 17 00:00:00 2001
From: Austin Littley <102969658+alittley@users.noreply.github.com>
Date: Mon, 5 Feb 2024 12:01:11 -0500
Subject: [PATCH 04/16] feat: Migrate transaction handling to framework
(#11144)
Signed-off-by: Austin Littley
---
.../state/logging/TransactionStateLogger.java | 31 +-
.../common/stream/EventStreamManager.java | 166 +++----
.../common/stream/RunningEventHashUpdate.java | 28 ++
.../common/stream/EventStreamManagerTest.java | 121 +----
.../swirlds-platform-core/build.gradle.kts | 1 -
.../com/swirlds/platform/SwirldsPlatform.java | 50 +-
.../components/LinkedEventIntake.java | 39 +-
.../swirlds/platform/config/StateConfig.java | 4 -
.../eventhandling/ConsensusQueue.java | 378 ---------------
.../eventhandling/ConsensusRoundHandler.java | 447 +++++-------------
.../ConsensusRoundHandlerPhase.java | 64 +++
.../metrics/ConsensusHandlingMetrics.java | 161 -------
.../metrics/RoundHandlingMetrics.java | 111 +++++
.../wiring/LinkedEventIntakeWiring.java | 4 +
.../platform/wiring/PlatformCoordinator.java | 9 +-
.../platform/wiring/PlatformSchedulers.java | 27 ++
.../wiring/PlatformSchedulersConfig.java | 5 +
.../platform/wiring/PlatformWiring.java | 53 ++-
.../ConsensusRoundHandlerWiring.java | 62 +++
.../components/EventStreamManagerWiring.java | 60 +++
.../components/RunningHashUpdaterWiring.java | 53 +++
.../platform/wiring/diagram-commands.txt | 59 ---
.../wiring/generate-platform-diagram.sh | 31 ++
.../AbstractEventHandlerTests.java | 112 -----
.../eventhandling/ConsensusQueueTests.java | 248 ----------
.../ConsensusRoundHandlerTests.java | 294 ++++++------
.../platform/wiring/PlatformWiringTests.java | 4 +
.../platform/test/consensus/TestIntake.java | 8 +-
28 files changed, 887 insertions(+), 1743 deletions(-)
create mode 100644 platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/RunningEventHashUpdate.java
delete mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusQueue.java
create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerPhase.java
delete mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java
create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/RoundHandlingMetrics.java
create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/ConsensusRoundHandlerWiring.java
create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventStreamManagerWiring.java
create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/RunningHashUpdaterWiring.java
delete mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/diagram-commands.txt
create mode 100755 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/generate-platform-diagram.sh
delete mode 100644 platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/AbstractEventHandlerTests.java
delete mode 100644 platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusQueueTests.java
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java
index 80ab27736655..a75caa9103f6 100644
--- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java
@@ -16,8 +16,7 @@
package com.hedera.node.app.state.logging;
-import static com.swirlds.platform.SwirldsPlatform.PLATFORM_THREAD_POOL_NAME;
-import static com.swirlds.platform.eventhandling.ConsensusRoundHandler.THREAD_CONS_NAME;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandler.TRANSACTION_HANDLING_THREAD_NAME;
import com.hedera.hapi.node.base.*;
import com.hedera.hapi.node.transaction.TransactionBody;
@@ -60,8 +59,6 @@
public final class TransactionStateLogger {
/** The logger we are using for Transaction State log */
private static final Logger logger = LogManager.getLogger(TransactionStateLogger.class);
- /** The name of the handle transaction thread */
- private static final String HANDLE_THREAD_NAME = "<" + PLATFORM_THREAD_POOL_NAME + ": " + THREAD_CONS_NAME;
/**
* Log the start of a round if it contains any non-system transactions.
@@ -235,7 +232,7 @@ public static void logEndTransactionRecord(
* @param The type of the singleton
*/
public static void logSingletonRead(@NonNull final String label, @Nullable final ValueLeaf value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(" READ singleton {} value {}", label, value == null ? "null" : value.getValue());
}
}
@@ -247,7 +244,7 @@ public static void logSingletonRead(@NonNull final String label, @Nullable f
* @param value The value of the singleton
*/
public static void logSingletonWrite(@NonNull final String label, @Nullable final Object value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(" WRITTEN singleton {} value {}", label, value == null ? "null" : value.toString());
}
}
@@ -261,7 +258,7 @@ public static void logSingletonWrite(@NonNull final String label, @Nullable fina
* @param value The value added to the queue
*/
public static void logQueueAdd(@NonNull final String label, @Nullable final Object value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(" ADD to queue {} value {}", label, value == null ? "null" : value.toString());
}
}
@@ -273,7 +270,7 @@ public static void logQueueAdd(@NonNull final String label, @Nullable final Obje
* @param value The value removed from the queue
*/
public static void logQueueRemove(@NonNull final String label, @Nullable final Object value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(" REMOVE from queue {} value {}", label, value == null ? "null" : value.toString());
}
}
@@ -285,7 +282,7 @@ public static void logQueueRemove(@NonNull final String label, @Nullable final O
* @param value The value peeked from the queue
*/
public static void logQueuePeek(@NonNull final String label, @Nullable final Object value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(" PEEK on queue {} value {}", label, value == null ? "null" : value.toString());
}
}
@@ -298,7 +295,7 @@ public static void logQueuePeek(@NonNull final String label, @Nullable final Obj
* @param The type of the queue values
*/
public static void logQueueIterate(@NonNull final String label, @NonNull final FCQueue> queue) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
if (queue.size() == 0) {
logger.debug(" ITERATE queue {} size 0 values:EMPTY", label);
} else {
@@ -325,7 +322,7 @@ public static void logQueueIterate(@NonNull final String label, @NonNull fin
* @param The type of the value
*/
public static void logMapPut(@NonNull final String label, @NonNull final K key, @Nullable final V value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(
" PUT into map {} key {} value {}",
label,
@@ -345,7 +342,7 @@ public static void logMapPut(@NonNull final String label, @NonNull final
*/
public static void logMapRemove(
@NonNull final String label, @NonNull final K key, @Nullable final InMemoryValue value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(
" REMOVE from map {} key {} removed value {}",
label,
@@ -365,7 +362,7 @@ public static void logMapRemove(
*/
public static void logMapRemove(
@NonNull final String label, @NonNull final K key, @Nullable final OnDiskValue value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(
" REMOVE from map {} key {} removed value {}",
label,
@@ -381,7 +378,7 @@ public static void logMapRemove(
* @param size The size of the map
*/
public static void logMapGetSize(@NonNull final String label, final long size) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(" GET_SIZE on map {} size {}", label, size);
}
}
@@ -396,7 +393,7 @@ public static void logMapGetSize(@NonNull final String label, final long size) {
* @param The type of the value
*/
public static void logMapGet(@NonNull final String label, @NonNull final K key, @Nullable final V value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(
" GET on map {} key {} value {}",
label,
@@ -416,7 +413,7 @@ public static void logMapGet(@NonNull final String label, @NonNull final
*/
public static void logMapGetForModify(
@NonNull final String label, @NonNull final K key, @Nullable final V value) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
logger.debug(
" GET_FOR_MODIFY on map {} key {} value {}",
label,
@@ -433,7 +430,7 @@ public static void logMapGetForModify(
* @param The type of the key
*/
public static void logMapIterate(@NonNull final String label, @NonNull final Set> keySet) {
- if (logger.isDebugEnabled() && Thread.currentThread().getName().startsWith(HANDLE_THREAD_NAME)) {
+ if (logger.isDebugEnabled() && Thread.currentThread().getName().equals(TRANSACTION_HANDLING_THREAD_NAME)) {
final long size = keySet.size();
if (size == 0) {
logger.debug(" ITERATE map {} size 0 keys:EMPTY", label);
diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/EventStreamManager.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/EventStreamManager.java
index e810b3c1599c..4637c30bb5b3 100644
--- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/EventStreamManager.java
+++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/EventStreamManager.java
@@ -18,6 +18,7 @@
import static com.swirlds.base.units.UnitConstants.SECONDS_TO_MILLISECONDS;
import static com.swirlds.logging.legacy.LogMarker.EVENT_STREAM;
+import static com.swirlds.logging.legacy.LogMarker.EXCEPTION;
import static com.swirlds.metrics.api.Metrics.INFO_CATEGORY;
import com.swirlds.base.time.Time;
@@ -47,7 +48,6 @@
* runningHash for consensus Events.
*/
public class EventStreamManager {
- /** use this for all logging, as controlled by the optional data/log4j2.xml file */
private static final Logger logger = LogManager.getLogger(EventStreamManager.class);
/**
@@ -59,18 +59,21 @@ public class EventStreamManager isLastEventInFreezeCheck;
- /** receives consensus events from multiStream, then passes to hashCalculator */
+ /**
+ * receives consensus events from multiStream, then passes to hashCalculator
+ */
private QueueThreadObjectStream hashQueueThread;
/**
- * receives consensus events from hashQueueThread, calculates this event's Hash, then passes to
- * runningHashCalculator
+ * receives consensus events from multiStream, then passes to streamFileWriter
*/
- private HashCalculatorForStream hashCalculator;
- /** receives consensus events from multiStream, then passes to streamFileWriter */
private QueueThreadObjectStream writeQueueThread;
- /** receives consensus events from writeQueueThread, serializes consensus events to event stream files */
+ /**
+ * receives consensus events from writeQueueThread, serializes consensus events to event stream files
+ */
private TimestampStreamFileWriter streamFileWriter;
- /** initialHash loaded from signed state */
+ /**
+ * initialHash loaded from signed state
+ */
private Hash initialHash = new ImmutableHash(new byte[DigestType.SHA_384.digestLength()]);
/**
* When we freeze the platform, the last event to be written to EventStream file is the last event in the freeze
@@ -121,9 +124,9 @@ public EventStreamManager(
eventStreamDir,
eventsLogPeriod * SECONDS_TO_MILLISECONDS,
signer,
- /** when event streaming is started after reconnect, or at state recovering,
- * startWriteAtCompleteWindow should be set to be true; when event streaming is started after
- * restart, it should be set to be false */
+ // when event streaming is started after reconnect, or at state recovering,
+ // startWriteAtCompleteWindow should be set to be true; when event streaming is started after
+ // restart, it should be set to be false
false,
EventStreamType.getInstance());
@@ -153,7 +156,10 @@ public EventStreamManager(
// receives consensus events from hashCalculator, calculates and set runningHash for this event
final RunningHashCalculatorForStream runningHashCalculator = new RunningHashCalculatorForStream<>();
- hashCalculator = new HashCalculatorForStream<>(runningHashCalculator);
+
+ // receives consensus events from hashQueueThread, calculates this event's Hash, then passes to
+ // runningHashCalculator
+ final HashCalculatorForStream hashCalculator = new HashCalculatorForStream<>(runningHashCalculator);
hashQueueThread = new QueueThreadObjectStreamConfiguration(threadManager)
.setNodeId(selfId)
.setComponent("event-stream")
@@ -197,42 +203,61 @@ public void stop() {
multiStream.close();
}
- public void addEvents(final List events) {
- events.forEach(this::addEvent);
- }
-
/**
- * receives a consensus event from ConsensusRoundHandler each time, sends it to multiStream which then sends to two
- * queueThread for calculating runningHash and writing to file
+ * Adds a list of events to the event stream.
*
- * @param event the consensus event to be added
+ * @param events the list of events to add
*/
- public void addEvent(final T event) {
- if (!freezePeriodStarted) {
- multiStream.addObject(event);
- if (isLastEventInFreezeCheck.test(event)) {
- freezePeriodStarted = true;
- logger.info(
- EVENT_STREAM.getMarker(),
- "ConsensusTimestamp of the last Event to be written into file before restarting: " + "{}",
- event::getTimestamp);
- multiStream.close();
+ public void addEvents(@NonNull final List events) {
+ events.forEach(event -> {
+ if (!freezePeriodStarted) {
+ multiStream.addObject(event);
+ if (isLastEventInFreezeCheck.test(event)) {
+ freezePeriodStarted = true;
+ logger.info(
+ EVENT_STREAM.getMarker(),
+ "ConsensusTimestamp of the last Event to be written into file before restarting: {}",
+ event::getTimestamp);
+ multiStream.close();
+ }
+ } else {
+ eventAfterFreezeLogger.warn(
+ EVENT_STREAM.getMarker(), "Event {} dropped after freezePeriodStarted!", event.getTimestamp());
}
- } else {
- eventAfterFreezeLogger.warn(
- EVENT_STREAM.getMarker(), "Event {} dropped after freezePeriodStarted!", event.getTimestamp());
- }
+ });
}
/**
- * sets startWriteAtCompleteWindow: it should be set to be true after reconnect, or at state recovering; it should
- * be set to be false at restart
+ * Updates the running hash with the given event hash. Called when a state is loaded.
*
- * @param startWriteAtCompleteWindow whether the writer should not write until the first complete window
+ * @param runningEventHashUpdate the hash to update the running hash with
*/
- public void setStartWriteAtCompleteWindow(final boolean startWriteAtCompleteWindow) {
+ public void updateRunningHash(@NonNull final RunningEventHashUpdate runningEventHashUpdate) {
+ try {
+ if (hashQueueThread != null) {
+ hashQueueThread.pause();
+ }
+ if (writeQueueThread != null) {
+ writeQueueThread.pause();
+ }
+ } catch (final InterruptedException e) {
+ logger.error(EXCEPTION.getMarker(), "Failed to pause queue threads", e);
+ Thread.currentThread().interrupt();
+ }
+
if (streamFileWriter != null) {
- streamFileWriter.setStartWriteAtCompleteWindow(startWriteAtCompleteWindow);
+ streamFileWriter.setStartWriteAtCompleteWindow(runningEventHashUpdate.isReconnect());
+ }
+
+ initialHash = new Hash(runningEventHashUpdate.runningEventHash());
+ logger.info(EVENT_STREAM.getMarker(), "EventStreamManager::updateRunningHash: {}", initialHash);
+ multiStream.setRunningHash(initialHash);
+
+ if (hashQueueThread != null) {
+ hashQueueThread.resume();
+ }
+ if (writeQueueThread != null) {
+ writeQueueThread.resume();
}
}
@@ -241,11 +266,8 @@ public void setStartWriteAtCompleteWindow(final boolean startWriteAtCompleteWind
*
* @return current size of working queue for calculating hash and runningHash
*/
- public int getHashQueueSize() {
- if (hashQueueThread == null) {
- return 0;
- }
- return hashQueueThread.getQueue().size();
+ private int getHashQueueSize() {
+ return hashQueueThread == null ? 0 : hashQueueThread.getQueue().size();
}
/**
@@ -253,63 +275,7 @@ public int getHashQueueSize() {
*
* @return current size of working queue for writing to event stream files
*/
- public int getEventStreamingQueueSize() {
+ private int getEventStreamingQueueSize() {
return writeQueueThread == null ? 0 : writeQueueThread.getQueue().size();
}
-
- /**
- * for unit testing
- *
- * @return current multiStream instance
- */
- public MultiStream getMultiStream() {
- return multiStream;
- }
-
- /**
- * for unit testing
- *
- * @return current TimestampStreamFileWriter instance
- */
- public TimestampStreamFileWriter getStreamFileWriter() {
- return streamFileWriter;
- }
-
- /**
- * for unit testing
- *
- * @return current HashCalculatorForStream instance
- */
- public HashCalculatorForStream getHashCalculator() {
- return hashCalculator;
- }
-
- /**
- * for unit testing
- *
- * @return whether freeze period has started
- */
- public boolean getFreezePeriodStarted() {
- return freezePeriodStarted;
- }
-
- /**
- * for unit testing
- *
- * @return a copy of initialHash
- */
- public Hash getInitialHash() {
- return new Hash(initialHash);
- }
-
- /**
- * sets initialHash after loading from signed state
- *
- * @param initialHash current runningHash of all consensus events
- */
- public void setInitialHash(final Hash initialHash) {
- this.initialHash = initialHash;
- logger.info(EVENT_STREAM.getMarker(), "EventStreamManager::setInitialHash: {}", () -> initialHash);
- multiStream.setRunningHash(initialHash);
- }
}
diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/RunningEventHashUpdate.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/RunningEventHashUpdate.java
new file mode 100644
index 000000000000..1620192f3587
--- /dev/null
+++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/stream/RunningEventHashUpdate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.swirlds.common.stream;
+
+import com.swirlds.common.crypto.Hash;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * A record used to update the running event hash on various components when a new state is loaded
+ *
+ * @param runningEventHash the running event hash of the loaded state
+ * @param isReconnect whether or not this is a reconnect state
+ */
+public record RunningEventHashUpdate(@NonNull Hash runningEventHash, boolean isReconnect) {}
diff --git a/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/stream/EventStreamManagerTest.java b/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/stream/EventStreamManagerTest.java
index fe608d920448..305ebaaf6172 100644
--- a/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/stream/EventStreamManagerTest.java
+++ b/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/stream/EventStreamManagerTest.java
@@ -16,150 +16,63 @@
package com.swirlds.common.stream;
-import static com.swirlds.common.threading.manager.AdHocThreadManager.getStaticThreadManager;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import com.swirlds.base.time.Time;
import com.swirlds.common.crypto.Hash;
-import com.swirlds.common.platform.NodeId;
import com.swirlds.common.test.fixtures.RandomUtils;
-import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder;
import com.swirlds.common.test.fixtures.stream.ObjectForTestStream;
-import org.junit.jupiter.api.BeforeAll;
+import java.util.List;
+import java.util.Random;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class EventStreamManagerTest {
- private static final NodeId selfId = new NodeId(0);
- private static final String nodeName = "node0";
- private static final String eventsLogDir = "eventStream/";
- private static final long eventsLogPeriod = 5;
- private static final int eventStreamQueueCapacity = 100;
-
- private static final String INITIALIZE_NOT_NULL = "after initialization, the instance should not be null";
- private static final String INITIALIZE_QUEUE_EMPTY = "after initialization, hash queue should be empty";
- private static final String UNEXPECTED_VALUE = "unexpected value";
-
- private static EventStreamManager disableStreamingInstance;
- private static EventStreamManager enableStreamingInstance;
-
- private static final Hash initialHash = RandomUtils.randomHash();
-
private static final MultiStream multiStreamMock = mock(MultiStream.class);
- private static final EventStreamManager EVENT_STREAM_MANAGER =
+ private static final EventStreamManager eventStreamManager =
new EventStreamManager<>(Time.getCurrent(), multiStreamMock, EventStreamManagerTest::isFreezeEvent);
private static final ObjectForTestStream freezeEvent = mock(ObjectForTestStream.class);
- @BeforeAll
- static void init() throws Exception {
- disableStreamingInstance = new EventStreamManager<>(
- TestPlatformContextBuilder.create().build(),
- Time.getCurrent(),
- getStaticThreadManager(),
- selfId,
- mock(Signer.class),
- nodeName,
- false,
- eventsLogDir,
- eventsLogPeriod,
- eventStreamQueueCapacity,
- EventStreamManagerTest::isFreezeEvent);
-
- enableStreamingInstance = new EventStreamManager<>(
- TestPlatformContextBuilder.create().build(),
- Time.getCurrent(),
- getStaticThreadManager(),
- selfId,
- mock(Signer.class),
- nodeName,
- true,
- eventsLogDir,
- eventsLogPeriod,
- eventStreamQueueCapacity,
- EventStreamManagerTest::isFreezeEvent);
- }
-
- @Test
- void initializeTest() {
- assertNull(
- disableStreamingInstance.getStreamFileWriter(),
- "When eventStreaming is disabled, streamFileWriter instance should be null");
- assertNotNull(disableStreamingInstance.getMultiStream(), INITIALIZE_NOT_NULL);
- assertNotNull(disableStreamingInstance.getHashCalculator(), INITIALIZE_NOT_NULL);
- assertEquals(0, disableStreamingInstance.getHashQueueSize(), INITIALIZE_QUEUE_EMPTY);
- assertEquals(0, disableStreamingInstance.getEventStreamingQueueSize(), INITIALIZE_QUEUE_EMPTY);
-
- assertNotNull(
- enableStreamingInstance.getStreamFileWriter(),
- "When eventStreaming is enabled, streamFileWriter instance should not be null");
- assertNotNull(enableStreamingInstance.getMultiStream(), INITIALIZE_NOT_NULL);
- assertNotNull(enableStreamingInstance.getHashCalculator(), INITIALIZE_NOT_NULL);
- assertEquals(0, enableStreamingInstance.getHashQueueSize(), INITIALIZE_QUEUE_EMPTY);
- assertEquals(0, enableStreamingInstance.getEventStreamingQueueSize(), INITIALIZE_QUEUE_EMPTY);
- }
-
- @Test
- void setInitialHashTest() {
- EVENT_STREAM_MANAGER.setInitialHash(initialHash);
- verify(multiStreamMock).setRunningHash(initialHash);
- assertEquals(initialHash, EVENT_STREAM_MANAGER.getInitialHash(), "initialHash is not set");
- }
-
@Test
- void addEventTest() throws InterruptedException {
- EventStreamManager eventStreamManager =
- new EventStreamManager<>(Time.getCurrent(), multiStreamMock, EventStreamManagerTest::isFreezeEvent);
- assertFalse(
- eventStreamManager.getFreezePeriodStarted(),
- "freezePeriodStarted should be false after initialization");
+ void addEventTest() {
final int nonFreezeEventsNum = 10;
for (int i = 0; i < nonFreezeEventsNum; i++) {
- ObjectForTestStream event = mock(ObjectForTestStream.class);
- eventStreamManager.addEvent(event);
+ final ObjectForTestStream event = mock(ObjectForTestStream.class);
+ eventStreamManager.addEvents(List.of(event));
+
verify(multiStreamMock).addObject(event);
// for non-freeze event, multiStream should not be closed after adding it
verify(multiStreamMock, never()).close();
- assertFalse(
- eventStreamManager.getFreezePeriodStarted(),
- "freezePeriodStarted should be false after adding non-freeze event");
}
- eventStreamManager.addEvent(freezeEvent);
+
+ eventStreamManager.addEvents(List.of(freezeEvent));
verify(multiStreamMock).addObject(freezeEvent);
// for freeze event, multiStream should be closed after adding it
verify(multiStreamMock).close();
- assertTrue(
- eventStreamManager.getFreezePeriodStarted(), "freezePeriodStarted should be true adding freeze event");
- ObjectForTestStream eventAddAfterFrozen = mock(ObjectForTestStream.class);
- eventStreamManager.addEvent(eventAddAfterFrozen);
+ final ObjectForTestStream eventAddAfterFrozen = mock(ObjectForTestStream.class);
+ eventStreamManager.addEvents(List.of(eventAddAfterFrozen));
// after frozen, when adding event to the EventStreamManager, multiStream.add(event) should not be called
verify(multiStreamMock, never()).addObject(eventAddAfterFrozen);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
- void setStartWriteAtCompleteWindowTest(boolean startWriteAtCompleteWindow) {
- enableStreamingInstance.setStartWriteAtCompleteWindow(startWriteAtCompleteWindow);
- assertEquals(
- startWriteAtCompleteWindow,
- enableStreamingInstance.getStreamFileWriter().getStartWriteAtCompleteWindow(),
- UNEXPECTED_VALUE);
+ void setStartWriteAtCompleteWindowTest(final boolean startWriteAtCompleteWindow) {
+ final Random random = RandomUtils.getRandomPrintSeed();
+ final Hash runningHash = RandomUtils.randomHash(random);
+ eventStreamManager.updateRunningHash(new RunningEventHashUpdate(runningHash, startWriteAtCompleteWindow));
+ verify(multiStreamMock).setRunningHash(runningHash);
}
/**
* used for testing adding freeze event
*
- * @param event
- * the event to be added
+ * @param event the event to be added
* @return whether
*/
private static boolean isFreezeEvent(final ObjectForTestStream event) {
diff --git a/platform-sdk/swirlds-platform-core/build.gradle.kts b/platform-sdk/swirlds-platform-core/build.gradle.kts
index d71a882ed2b1..1f345fc4c399 100644
--- a/platform-sdk/swirlds-platform-core/build.gradle.kts
+++ b/platform-sdk/swirlds-platform-core/build.gradle.kts
@@ -44,7 +44,6 @@ testModuleInfo {
requires("com.swirlds.platform.core")
requires("com.swirlds.config.extensions.test.fixtures")
requires("org.assertj.core")
- requires("awaitility")
requires("org.junit.jupiter.api")
requires("org.junit.jupiter.params")
requires("org.mockito")
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
index bfebc937be34..14e0e8aec643 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
@@ -46,6 +46,7 @@
import com.swirlds.common.platform.NodeId;
import com.swirlds.common.scratchpad.Scratchpad;
import com.swirlds.common.stream.EventStreamManager;
+import com.swirlds.common.stream.RunningEventHashUpdate;
import com.swirlds.common.threading.framework.QueueThread;
import com.swirlds.common.threading.framework.config.QueueThreadConfiguration;
import com.swirlds.common.threading.framework.config.QueueThreadMetricsConfiguration;
@@ -114,7 +115,6 @@
import com.swirlds.platform.listeners.StateLoadedFromDiskCompleteListener;
import com.swirlds.platform.listeners.StateLoadedFromDiskNotification;
import com.swirlds.platform.metrics.AddedEventMetrics;
-import com.swirlds.platform.metrics.ConsensusHandlingMetrics;
import com.swirlds.platform.metrics.ConsensusMetrics;
import com.swirlds.platform.metrics.ConsensusMetricsImpl;
import com.swirlds.platform.metrics.EventIntakeMetrics;
@@ -236,9 +236,6 @@ public class SwirldsPlatform implements Platform {
*/
private final SignedStateNexus latestImmutableState = new LockFreeStateNexus();
- /** Stores and processes consensus events including sending them to {@link SwirldStateManager} for handling */
- private final ConsensusRoundHandler consensusRoundHandler;
-
private final TransactionPool transactionPool;
/** Handles all interaction with {@link SwirldState} */
private final SwirldStateManager swirldStateManager;
@@ -505,7 +502,7 @@ public class SwirldsPlatform implements Platform {
time,
platformWiring.getPcesReplayerEventOutput(),
platformWiring::flushIntakePipeline,
- this::waitUntilTransactionHandlingThreadIsNotBusy,
+ platformWiring::flushConsensusRoundHandler,
() -> latestImmutableState.getState("PCES replay"));
final EventDurabilityNexus eventDurabilityNexus = new EventDurabilityNexus();
@@ -575,24 +572,19 @@ public class SwirldsPlatform implements Platform {
.setMetricsConfiguration(new QueueThreadMetricsConfiguration(metrics).enableBusyTimeMetric())
.build());
- consensusRoundHandler = components.add(new ConsensusRoundHandler(
+ final ConsensusRoundHandler consensusRoundHandler = new ConsensusRoundHandler(
platformContext,
- threadManager,
- selfId,
swirldStateManager,
- new ConsensusHandlingMetrics(metrics, time),
- eventStreamManager,
stateHashSignQueue,
eventDurabilityNexus::waitUntilDurable,
platformStatusManager,
platformWiring.getIssDetectorWiring().roundCompletedInput()::put,
- appVersion));
+ appVersion);
final AddedEventMetrics addedEventMetrics = new AddedEventMetrics(this.selfId, metrics);
final PcesSequencer sequencer = new PcesSequencer();
- final List eventObservers =
- new ArrayList<>(List.of(consensusRoundHandler, addedEventMetrics, eventIntakeMetrics));
+ final List eventObservers = new ArrayList<>(List.of(addedEventMetrics, eventIntakeMetrics));
final EventObserverDispatcher eventObserverDispatcher = new EventObserverDispatcher(eventObservers);
@@ -619,8 +611,6 @@ public class SwirldsPlatform implements Platform {
final OrphanBuffer orphanBuffer = new OrphanBuffer(platformContext, intakeEventCounter);
final InOrderLinker inOrderLinker = new InOrderLinker(platformContext, time, intakeEventCounter);
final LinkedEventIntake linkedEventIntake = new LinkedEventIntake(
- platformContext,
- time,
consensusRef::get,
eventObserverDispatcher,
shadowGraph,
@@ -675,6 +665,8 @@ public class SwirldsPlatform implements Platform {
eventCreationManager,
swirldStateManager,
stateSignatureCollector,
+ consensusRoundHandler,
+ eventStreamManager,
futureEventBuffer,
issDetector);
@@ -740,7 +732,8 @@ public class SwirldsPlatform implements Platform {
latestImmutableState.setState(initialState.reserve("set latest immutable to initial state"));
stateManagementComponent.stateToLoad(initialState, SourceOfSignedState.DISK);
savedStateController.registerSignedStateFromDisk(initialState);
- consensusRoundHandler.loadDataFromSignedState(initialState, false);
+
+ platformWiring.updateRunningHash(new RunningEventHashUpdate(initialState.getHashEventsCons(), false));
loadStateIntoConsensus(initialState);
@@ -763,12 +756,20 @@ public class SwirldsPlatform implements Platform {
});
}
+ final Clearable clearStateHashSignQueue = () -> {
+ ReservedSignedState signedState = stateHashSignQueue.poll();
+ while (signedState != null) {
+ signedState.close();
+ signedState = stateHashSignQueue.poll();
+ }
+ };
+
clearAllPipelines = new LoggingClearables(
RECONNECT.getMarker(),
List.of(
Pair.of(platformWiring, "platformWiring"),
Pair.of(shadowGraph, "shadowGraph"),
- Pair.of(consensusRoundHandler, "consensusRoundHandler"),
+ Pair.of(clearStateHashSignQueue, "stateHashSignQueue"),
Pair.of(transactionPool, "transactionPool")));
if (platformContext.getConfiguration().getConfigData(ThreadConfig.class).jvmAnchor()) {
@@ -782,18 +783,6 @@ public class SwirldsPlatform implements Platform {
GuiPlatformAccessor.getInstance().setLatestImmutableStateComponent(selfId, latestImmutableState);
}
- /**
- * Wait until the consensus round handler is not busy.
- */
- private void waitUntilTransactionHandlingThreadIsNotBusy() {
- try {
- consensusRoundHandler.waitUntilNotBusy();
- } catch (final InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new RuntimeException("Interrupted waiting for transaction handling thread to not be busy", e);
- }
- }
-
/**
* Clears all pipelines in preparation for a reconnect. This method is needed to break a circular dependency.
*/
@@ -940,8 +929,7 @@ private void loadReconnectState(final SignedState signedState) {
signedState.getMinRoundGeneration(),
AncientMode.getAncientMode(platformContext)));
- consensusRoundHandler.loadDataFromSignedState(signedState, true);
-
+ platformWiring.updateRunningHash(new RunningEventHashUpdate(signedState.getHashEventsCons(), true));
platformWiring.getPcesWriterRegisterDiscontinuityInput().inject(signedState.getRound());
// Notify any listeners that the reconnect has been completed
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/LinkedEventIntake.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/LinkedEventIntake.java
index 1295cec88fbd..90d3b3921ea2 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/LinkedEventIntake.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/LinkedEventIntake.java
@@ -16,8 +16,6 @@
package com.swirlds.platform.components;
-import com.swirlds.base.time.Time;
-import com.swirlds.common.context.PlatformContext;
import com.swirlds.common.wiring.wires.output.StandardOutputWire;
import com.swirlds.platform.Consensus;
import com.swirlds.platform.gossip.IntakeEventCounter;
@@ -50,9 +48,6 @@ public class LinkedEventIntake {
*/
private final Shadowgraph shadowGraph;
- private final EventIntakeMetrics metrics;
- private final Time time;
-
/**
* Tracks the number of events from each peer have been received, but aren't yet through the intake pipeline
*/
@@ -73,22 +68,18 @@ public class LinkedEventIntake {
/**
* Constructor
*
- * @param platformContext the platform context
- * @param time provides the wall clock time
- * @param consensusSupplier provides the current consensus instance
- * @param dispatcher invokes event related callbacks
- * @param shadowGraph tracks events in the hashgraph
+ * @param consensusSupplier provides the current consensus instance
+ * @param dispatcher invokes event related callbacks
+ * @param shadowGraph tracks events in the hashgraph
* @param keystoneEventSequenceNumberOutput the secondary wire that outputs the keystone event sequence number
*/
public LinkedEventIntake(
- @NonNull final PlatformContext platformContext,
- @NonNull final Time time,
@NonNull final Supplier consensusSupplier,
@NonNull final EventObserverDispatcher dispatcher,
@NonNull final Shadowgraph shadowGraph,
@NonNull final IntakeEventCounter intakeEventCounter,
@NonNull final StandardOutputWire keystoneEventSequenceNumberOutput) {
- this.time = Objects.requireNonNull(time);
+
this.consensusSupplier = Objects.requireNonNull(consensusSupplier);
this.dispatcher = Objects.requireNonNull(dispatcher);
this.shadowGraph = Objects.requireNonNull(shadowGraph);
@@ -96,7 +87,6 @@ public LinkedEventIntake(
this.keystoneEventSequenceNumberOutput = Objects.requireNonNull(keystoneEventSequenceNumberOutput);
this.paused = false;
- metrics = new EventIntakeMetrics(platformContext, () -> -1);
}
/**
@@ -136,7 +126,9 @@ public List addEvent(@NonNull final EventImpl event) {
// PCES writer hasn't been notified yet that the event should be flushed.
keystoneEventSequenceNumberOutput.forward(
round.getKeystoneEvent().getBaseEvent().getStreamSequenceNumber());
- handleConsensus(round);
+ // Future work: this dispatcher now only handles metrics. Remove this and put the metrics where
+ // they belong
+ dispatcher.consensusRound(round);
});
}
@@ -190,21 +182,4 @@ private void handleStale(final long previousGenerationNonAncient) {
private static boolean isNotConsensus(@NonNull final EventImpl event) {
return !event.isConsensus();
}
-
- /**
- * Notify observers that an event has reach consensus.
- *
- * @param consensusRound the new consensus round
- */
- private void handleConsensus(final @NonNull ConsensusRound consensusRound) {
- // We need to wait for prehandles to finish before proceeding.
- // It is critically important that prehandle is always called prior to handleConsensusRound().
-
- final long start = time.nanoTime();
- consensusRound.forEach(event -> ((EventImpl) event).getBaseEvent().awaitPrehandleCompletion());
- final long end = time.nanoTime();
- metrics.reportTimeWaitedForPrehandlingTransaction(end - start);
-
- dispatcher.consensusRound(consensusRound);
- }
}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/StateConfig.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/StateConfig.java
index 644dc6c1625f..3ad109c034a4 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/StateConfig.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/StateConfig.java
@@ -66,9 +66,6 @@
* time a signed state reference count is changed, and logged if a signed state
* reference count bug is detected.
* @param emergencyStateFileName The name of the file that contains the emergency state.
- * @param signedStateFreq hash and sign a state every signedStateFreq rounds. 1 means that a state will be
- * signed every round, 2 means every other round, and so on. If the value is 0 or
- * less, no states will be signed
* @param deleteInvalidStateFiles At startup time, if a state can not be deserialized without errors, should we
* delete that state from disk and try another? If true then states that can't be
* parsed are deleted. If false then a node will crash if it can't parse a state
@@ -98,7 +95,6 @@ public record StateConfig(
@ConfigProperty(defaultValue = "false") boolean stateHistoryEnabled,
@ConfigProperty(defaultValue = "false") boolean debugStackTracesEnabled,
@ConfigProperty(defaultValue = "emergencyRecovery.yaml") String emergencyStateFileName,
- @ConfigProperty(defaultValue = "1") int signedStateFreq,
@ConfigProperty(defaultValue = "false") boolean deleteInvalidStateFiles,
@ConfigProperty(defaultValue = "true") boolean validateInitialState) {
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusQueue.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusQueue.java
deleted file mode 100644
index 5d3310c15bc2..000000000000
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusQueue.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2022-2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.swirlds.platform.eventhandling;
-
-import com.swirlds.platform.internal.ConsensusRound;
-import com.swirlds.platform.metrics.ConsensusHandlingMetrics;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * A specialized implementation of {@link BlockingQueue} for the consensus queue (q2) that stores consensus rounds
- * to be handled. Despite the queue storing rounds, the capacity to store additional rounds is determined by the total
- * number of events in those rounds.
- *
- * The implementation does not support multiple threads adding elements or multiple threads removing elements, but it
- * does support a single thread that adds elements that is different from the thread removing elements.
- */
-public class ConsensusQueue implements BlockingQueue {
-
- /** The total number of events in all the rounds in the queue at any given time. */
- private final AtomicInteger eventsInQueue = new AtomicInteger(0);
-
- /** The statistics instance to update */
- private final ConsensusHandlingMetrics consensusHandlingMetrics;
-
- /** The maximum number of events allowed in rounds in the queue */
- private final int eventCapacity;
-
- /** The queue that holds the rounds */
- private final LinkedBlockingQueue queue = new LinkedBlockingQueue<>();
-
- /**
- * Creates a new instance with no items in the queue.
- *
- * @param consensusHandlingMetrics
- * the stats object to record stats on
- * @param eventCapacity
- * the maximum number of events allowed in all the rounds in the queue, unless there is a single round in the
- * queue with more than this many events
- */
- ConsensusQueue(final ConsensusHandlingMetrics consensusHandlingMetrics, final int eventCapacity) {
- super();
- this.consensusHandlingMetrics = consensusHandlingMetrics;
- this.eventCapacity = eventCapacity;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean add(final ConsensusRound consensusRound) {
- throw new UnsupportedOperationException("use put() instead");
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean offer(final ConsensusRound consensusRound) {
- if (queueHasRoom(consensusRound)) {
- final boolean ans = queue.offer(consensusRound);
- if (ans) {
- incrementEventsInQueue(consensusRound);
- }
- return ans;
- }
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConsensusRound remove() {
- final ConsensusRound round = queue.remove();
- if (round == null) {
- throw new NoSuchElementException("queue is empty");
- }
- decrementEventsInQueue(round);
- return round;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConsensusRound poll() {
- final ConsensusRound round = queue.poll();
- if (round != null) {
- decrementEventsInQueue(round);
- }
- return round;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConsensusRound element() {
- final ConsensusRound round = queue.element();
- if (round == null) {
- throw new NoSuchElementException("queue is empty");
- }
- return round;
- }
-
- /**
- * Retrieves, but does not remove, the head of this queue,
- * or returns {@code null} if this queue is empty.
- *
- * @return the head of this queue, or {@code null} if this queue is empty
- */
- @Override
- public ConsensusRound peek() {
- return queue.peek();
- }
-
- /**
- * Adds the round to the queue (q2) if there is room according to the event capacity. If there is no room, wait
- * until there is.
- *
- * @param consensusRound
- * the round to add to the queue
- * @throws InterruptedException
- * if this thread is interrupted while waiting or adding to the queue
- */
- @Override
- public synchronized void put(final ConsensusRound consensusRound) throws InterruptedException {
- consensusHandlingMetrics.recordEventsPerRound(consensusRound.getNumEvents());
- while (!queueHasRoom(consensusRound)) {
- this.wait();
- }
-
- queue.put(consensusRound);
- incrementEventsInQueue(consensusRound);
- }
-
- private void incrementEventsInQueue(final ConsensusRound consensusRound) {
- eventsInQueue.updateAndGet(currValue -> currValue + consensusRound.getNumEvents());
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public synchronized boolean offer(final ConsensusRound consensusRound, final long timeout, final TimeUnit unit)
- throws InterruptedException {
- consensusHandlingMetrics.recordEventsPerRound(consensusRound.getNumEvents());
- final boolean ans;
- long millisWaited = 0;
- final long maxMillisToWait = unit.toMillis(timeout);
- while (!queueHasRoom(consensusRound) && millisWaited <= maxMillisToWait) {
- millisWaited++;
- this.wait(1);
- }
-
- if (!queueHasRoom(consensusRound)) {
- return false;
- }
-
- ans = queue.offer(consensusRound);
- if (ans) {
- incrementEventsInQueue(consensusRound);
- }
- return ans;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConsensusRound take() throws InterruptedException {
- final ConsensusRound round = queue.take();
- decrementEventsInQueue(round);
- return round;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConsensusRound poll(final long timeout, final TimeUnit unit) throws InterruptedException {
- final ConsensusRound round = queue.poll(timeout, unit);
- if (round != null) {
- decrementEventsInQueue(round);
- }
- return round;
- }
-
- /**
- * Returns the number of additional elements that this queue can ideally
- * (in the absence of memory or resource constraints) accept without
- * blocking, or {@code Integer.MAX_VALUE} if there is no intrinsic
- * limit.
- *
- * Note that you cannot always tell if an attempt to insert
- * an element will succeed by inspecting {@code remainingCapacity}
- * because it may be the case that another thread is about to
- * insert or remove an element.
- *
- * @return the remaining capacity
- */
- @Override
- public int remainingCapacity() {
- return eventCapacity - eventsInQueue.get();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean remove(final Object o) {
- throw new UnsupportedOperationException("remove() is not supported by ConsensusQueue");
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean containsAll(final Collection> c) {
- return queue.containsAll(c);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean contains(final Object o) {
- return queue.contains(o);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Iterator iterator() {
- return queue.iterator();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object[] toArray() {
- return queue.toArray();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public T[] toArray(final T[] a) {
- return queue.toArray(a);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean addAll(final Collection extends ConsensusRound> c) {
- throw new UnsupportedOperationException("addAll() is not supported by ConsensusQueue");
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean removeAll(final Collection> c) {
- throw new UnsupportedOperationException("removeAll() is not supported by ConsensusQueue");
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean retainAll(final Collection> c) {
- throw new UnsupportedOperationException("retainAll() is not supported by ConsensusQueue");
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void clear() {
- queue.clear();
- eventsInQueue.set(0);
- }
-
- /**
- * Returns the total number of events in all the rounds in this queue.
- */
- @Override
- public int size() {
- return eventsInQueue.get();
- }
-
- /**
- * Returns {@code true} if this collection contains no elements.
- *
- * @return {@code true} if this collection contains no elements
- */
- @Override
- public boolean isEmpty() {
- return queue.isEmpty();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int drainTo(final Collection super ConsensusRound> c) {
- final int numDrained = queue.drainTo(c);
- for (final Object round : c) {
- decrementEventsInQueue((ConsensusRound) round);
- }
- return numDrained;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int drainTo(final Collection super ConsensusRound> c, final int maxElements) {
- final int numDrained = queue.drainTo(c, maxElements);
- for (final Object round : c) {
- decrementEventsInQueue((ConsensusRound) round);
- }
- return numDrained;
- }
-
- /**
- * Determines if the {@code consensusRound} can be added to the queue based on the event capacity. If the queue is
- * currently empty, return true regardless of the number of events in the round. If the queue is not empty, check if
- * adding the round will exceed the maximum number of events.
- *
- * @param consensusRound
- * the round to add
- * @return true if the round can be added
- */
- private boolean queueHasRoom(final ConsensusRound consensusRound) {
- final int numEvents = consensusRound.getNumEvents();
- if (queue.isEmpty()) {
- return true;
- }
- return eventsInQueue.get() + numEvents <= eventCapacity;
- }
-
- /**
- * Decrement the number of events in the queue by the number of events in {@code consensusRound}. The handler of
- * items in the queue is responsible for calling this method.
- *
- * @param consensusRound
- * the round just removed from the queue
- */
- private synchronized void decrementEventsInQueue(final ConsensusRound consensusRound) {
- eventsInQueue.updateAndGet(currValue -> currValue - consensusRound.getNumEvents());
- this.notifyAll();
- }
-}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java
index 05706dc65ce9..a9ec2c52e8c0 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java
@@ -17,51 +17,40 @@
package com.swirlds.platform.eventhandling;
import static com.swirlds.logging.legacy.LogMarker.EXCEPTION;
-import static com.swirlds.logging.legacy.LogMarker.RECONNECT;
-import static com.swirlds.logging.legacy.LogMarker.STARTUP;
-import static com.swirlds.platform.SwirldsPlatform.PLATFORM_THREAD_POOL_NAME;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.CREATING_SIGNED_STATE;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.GETTING_STATE_TO_SIGN;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.HANDLING_CONSENSUS_ROUND;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.IDLE;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.MARKING_ROUND_COMPLETE;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.SETTING_EVENT_CONSENSUS_DATA;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.UPDATING_PLATFORM_STATE;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.UPDATING_PLATFORM_STATE_RUNNING_HASH;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.WAITING_FOR_EVENT_DURABILITY;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.WAITING_FOR_PREHANDLE;
import com.swirlds.base.function.CheckedConsumer;
-import com.swirlds.base.state.Startable;
import com.swirlds.common.context.PlatformContext;
-import com.swirlds.common.crypto.CryptographyHolder;
import com.swirlds.common.crypto.DigestType;
import com.swirlds.common.crypto.Hash;
import com.swirlds.common.crypto.ImmutableHash;
import com.swirlds.common.crypto.RunningHash;
import com.swirlds.common.metrics.RunningAverageMetric;
-import com.swirlds.common.platform.NodeId;
-import com.swirlds.common.stream.EventStreamManager;
-import com.swirlds.common.threading.framework.QueueThread;
-import com.swirlds.common.threading.framework.Stoppable;
-import com.swirlds.common.threading.framework.config.QueueThreadConfiguration;
-import com.swirlds.common.threading.framework.config.QueueThreadMetricsConfiguration;
-import com.swirlds.common.threading.manager.ThreadManager;
-import com.swirlds.common.utility.Clearable;
-import com.swirlds.metrics.api.FloatFormats;
+import com.swirlds.common.stream.RunningEventHashUpdate;
import com.swirlds.metrics.api.Metrics;
-import com.swirlds.platform.config.StateConfig;
-import com.swirlds.platform.config.ThreadConfig;
import com.swirlds.platform.consensus.ConsensusConfig;
import com.swirlds.platform.event.GossipEvent;
import com.swirlds.platform.internal.ConsensusRound;
import com.swirlds.platform.internal.EventImpl;
-import com.swirlds.platform.metrics.ConsensusHandlingMetrics;
-import com.swirlds.platform.observers.ConsensusRoundObserver;
+import com.swirlds.platform.metrics.RoundHandlingMetrics;
import com.swirlds.platform.state.PlatformState;
import com.swirlds.platform.state.State;
import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.state.signed.SignedState;
-import com.swirlds.platform.stats.AverageAndMax;
-import com.swirlds.platform.stats.AverageStat;
-import com.swirlds.platform.stats.CycleTimingStat;
-import com.swirlds.platform.system.PlatformStatNames;
import com.swirlds.platform.system.SoftwareVersion;
import com.swirlds.platform.system.status.StatusActionSubmitter;
import com.swirlds.platform.system.status.actions.FreezePeriodEnteredAction;
import edu.umd.cs.findbugs.annotations.NonNull;
-import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.function.Consumer;
@@ -69,50 +58,37 @@
import org.apache.logging.log4j.Logger;
/**
- * Created by a Platform to manage the flow of consensus events to SwirldState (1 instance or 3 depending on the
- * SwirldState implemented). It contains a thread queue that contains a queue of consensus events (q2) and a
- * SwirldStateManager which applies those events to the state. It also creates signed states at the appropriate times.
+ * Applies transactions from consensus rounds to the state
*/
-public class ConsensusRoundHandler implements ConsensusRoundObserver, Clearable, Startable {
+public class ConsensusRoundHandler {
- /**
- * use this for all logging, as controlled by the optional data/log4j2.xml file
- */
private static final Logger logger = LogManager.getLogger(ConsensusRoundHandler.class);
/**
- * The name of the thread that handles consensus events
+ * The name of the thread that handles transactions. For the sake of the app, to allow logging.
*/
- public static final String THREAD_CONS_NAME = "thread_cons";
+ public static final String TRANSACTION_HANDLING_THREAD_NAME = "";
/**
* The class responsible for all interactions with the swirld state
*/
private final SwirldStateManager swirldStateManager;
- private final ConsensusHandlingMetrics consensusHandlingMetrics;
-
- /**
- * The queue thread that stores consensus rounds and feeds them to this class for handling.
- */
- private final QueueThread queueThread;
-
- /**
- * Stores consensus events in the event stream.
- */
- private final EventStreamManager eventStreamManager;
+ private final RoundHandlingMetrics handlerMetrics;
/**
- * indicates whether a state was saved in the current freeze period. we are only saving the first state in the
- * freeze period. this variable is only used by threadCons so there is no synchronization needed
+ * Whether a round in a freeze period has been received. This may never be reset to false after it is set to true.
*/
- private boolean savedStateInFreeze = false;
+ private boolean freezeRoundReceived = false;
/**
* a RunningHash object which calculates running hash of all consensus events so far with their transactions handled
- * by stateCons
+ *
+ * Future work: this will need to be changed to a proper null hash when this component is updated to handle empty
+ * rounds, since a hash of all 0s isn't valid when deserializing a state. In the current system, this hash is
+ * always overwritten before being put into the state, so it doesn't matter that it starts as all 0s.
*/
- private RunningHash eventsConsRunningHash =
+ private RunningHash consensusEventsRunningHash =
new RunningHash(new ImmutableHash(new byte[DigestType.SHA_384.digestLength()]));
/**
@@ -125,8 +101,6 @@ public class ConsensusRoundHandler implements ConsensusRoundObserver, Clearable,
*/
private final StatusActionSubmitter statusActionSubmitter;
- private boolean addedFirstRoundInFreeze = false;
-
private final SoftwareVersion softwareVersion;
private final Consumer roundAppliedToStateConsumer;
@@ -134,7 +108,7 @@ public class ConsensusRoundHandler implements ConsensusRoundObserver, Clearable,
/**
* A method that blocks until an event becomes durable.
*/
- final CheckedConsumer waitForEventDurability;
+ private final CheckedConsumer waitForEventDurability;
/**
* The number of non-ancient rounds.
@@ -149,28 +123,20 @@ public class ConsensusRoundHandler implements ConsensusRoundObserver, Clearable,
.withUnit("count");
/**
- * Instantiate, but don't start any threads yet. The Platform should first instantiate the
- * {@link ConsensusRoundHandler}. Then the Platform should call start to start the queue thread.
+ * Constructor
*
- * @param platformContext contains various platform utilities
- * @param threadManager responsible for creating and managing threads
- * @param selfId the id of this node
- * @param swirldStateManager the swirld state manager to send events to
- * @param consensusHandlingMetrics statistics updated by {@link ConsensusRoundHandler}
- * @param eventStreamManager the event stream manager to send consensus events to
- * @param stateHashSignQueue the queue thread that handles hashing and collecting signatures of new
- * self-signed states
- * @param waitForEventDurability a method that blocks until an event becomes durable.
- * @param statusActionSubmitter enables submitting of platform status actions
- * @param softwareVersion the current version of the software
+ * @param platformContext contains various platform utilities
+ * @param swirldStateManager the swirld state manager to send events to
+ * @param stateHashSignQueue the queue thread that handles hashing and collecting signatures of new
+ * self-signed states
+ * @param waitForEventDurability a method that blocks until an event becomes durable
+ * @param statusActionSubmitter enables submitting of platform status actions
+ * @param roundAppliedToStateConsumer informs the consensus hash manager that a round has been applied to state
+ * @param softwareVersion the current version of the software
*/
public ConsensusRoundHandler(
@NonNull final PlatformContext platformContext,
- @NonNull final ThreadManager threadManager,
- @NonNull final NodeId selfId,
@NonNull final SwirldStateManager swirldStateManager,
- @NonNull final ConsensusHandlingMetrics consensusHandlingMetrics,
- @NonNull final EventStreamManager eventStreamManager,
@NonNull final BlockingQueue stateHashSignQueue,
@NonNull final CheckedConsumer waitForEventDurability,
@NonNull final StatusActionSubmitter statusActionSubmitter,
@@ -178,273 +144,109 @@ public ConsensusRoundHandler(
@NonNull final SoftwareVersion softwareVersion) {
this.platformContext = Objects.requireNonNull(platformContext);
- this.roundAppliedToStateConsumer = roundAppliedToStateConsumer;
- Objects.requireNonNull(selfId, "selfId must not be null");
- this.swirldStateManager = swirldStateManager;
- this.consensusHandlingMetrics = consensusHandlingMetrics;
- this.eventStreamManager = eventStreamManager;
- this.stateHashSignQueue = stateHashSignQueue;
+ this.swirldStateManager = Objects.requireNonNull(swirldStateManager);
+ this.stateHashSignQueue = Objects.requireNonNull(stateHashSignQueue);
+ this.waitForEventDurability = Objects.requireNonNull(waitForEventDurability);
this.statusActionSubmitter = Objects.requireNonNull(statusActionSubmitter);
+ this.roundAppliedToStateConsumer = Objects.requireNonNull(roundAppliedToStateConsumer);
+ this.softwareVersion = Objects.requireNonNull(softwareVersion);
- this.softwareVersion = softwareVersion;
-
- final EventConfig eventConfig = platformContext.getConfiguration().getConfigData(EventConfig.class);
- final ConsensusQueue queue = new ConsensusQueue(consensusHandlingMetrics, eventConfig.maxEventQueueForCons());
- final ThreadConfig threadConfig = platformContext.getConfiguration().getConfigData(ThreadConfig.class);
-
- queueThread = new QueueThreadConfiguration(threadManager)
- .setNodeId(selfId)
- .setHandler(this::applyConsensusRoundToState)
- .setComponent(PLATFORM_THREAD_POOL_NAME)
- .setThreadName(THREAD_CONS_NAME)
- .setStopBehavior(Stoppable.StopBehavior.BLOCKING)
- .setLogAfterPauseDuration(threadConfig.logStackTracePauseDuration())
- .setMetricsConfiguration(
- new QueueThreadMetricsConfiguration(platformContext.getMetrics()).enableBusyTimeMetric())
- .setQueue(queue)
- .build();
-
- roundsNonAncient = platformContext
+ this.roundsNonAncient = platformContext
.getConfiguration()
.getConfigData(ConsensusConfig.class)
.roundsNonAncient();
+ this.handlerMetrics = new RoundHandlingMetrics(platformContext);
- this.waitForEventDurability = waitForEventDurability;
-
- final AverageAndMax avgQ2ConsEvents = new AverageAndMax(
- platformContext.getMetrics(),
- Metrics.INTERNAL_CATEGORY,
- PlatformStatNames.CONSENSUS_QUEUE_SIZE,
- "average number of events in the consensus queue (q2) waiting to be handled",
- FloatFormats.FORMAT_10_3,
- AverageStat.WEIGHT_VOLATILE);
+ // Future work: This metric should be moved to a suitable component once the stateHashSignQueue is migrated
+ // to the framework
final RunningAverageMetric avgStateToHashSignDepth =
platformContext.getMetrics().getOrCreate(AVG_STATE_TO_HASH_SIGN_DEPTH_CONFIG);
- platformContext.getMetrics().addUpdater(() -> {
- avgQ2ConsEvents.update(queueThread.size());
- avgStateToHashSignDepth.update(getStateToHashSignSize());
- });
- }
-
- /**
- * Starts the queue thread.
- */
- @Override
- public void start() {
- queueThread.start();
- }
-
- /**
- * Stops the queue thread. For unit testing purposes only.
- */
- public void stop() {
- queueThread.stop();
+ platformContext.getMetrics().addUpdater(() -> avgStateToHashSignDepth.update(stateHashSignQueue.size()));
}
/**
- * Blocks until the handling thread has handled all available work and is no longer busy. May block indefinitely if
- * more work is continually added to the queue.
+ * Update the consensus event running hash
+ *
+ * Future work: in the current system, it isn't actually necessary to update this running hash when a new state
+ * is loaded. The running hash will be overwritten anyway by the first round that contains events, before the
+ * initial hash is ever set in the state. This method is being called anyway, though, since it will be a necessary
+ * workflow in the future to support handling of empty rounds by this component.
*
- * @throws InterruptedException if interrupted while waiting
- */
- public void waitUntilNotBusy() throws InterruptedException {
- queueThread.waitUntilNotBusy();
- }
-
- @Override
- public void clear() {
- logger.info(RECONNECT.getMarker(), "consensus handler: clearing queue thread");
- queueThread.clear();
-
- logger.info(RECONNECT.getMarker(), "consensus handler: clearing stateHashSignQueue queue");
- clearStateHashSignQueueThread();
-
- // clear running Hash info
- eventsConsRunningHash = new RunningHash(new ImmutableHash(new byte[DigestType.SHA_384.digestLength()]));
-
- logger.info(RECONNECT.getMarker(), "consensus handler: ready for reconnect");
- }
-
- /**
- * Clears and releases any signed states in the {@code stateHashSignQueueThread} queue.
+ * @param runningHashUpdate the update to the running hash
*/
- private void clearStateHashSignQueueThread() {
- ReservedSignedState signedState = stateHashSignQueue.poll();
- while (signedState != null) {
- signedState.close();
- signedState = stateHashSignQueue.poll();
- }
+ public void updateRunningHash(@NonNull final RunningEventHashUpdate runningHashUpdate) {
+ consensusEventsRunningHash = new RunningHash(runningHashUpdate.runningEventHash());
}
/**
- * Loads data from a SignedState, this is used on startup to load events and the running hash that have been
- * previously saved on disk
+ * Applies the transactions in the consensus round to the state
*
- * @param signedState the state to load data from
- * @param isReconnect if it is true, the reservedSignedState is loaded at reconnect; if it is false, the
- * reservedSignedState is loaded at startup
- */
- public void loadDataFromSignedState(final SignedState signedState, final boolean isReconnect) {
- // set initialHash of the RunningHash to be the hash loaded from signed state
- eventsConsRunningHash = new RunningHash(signedState.getHashEventsCons());
-
- logger.info(
- STARTUP.getMarker(),
- "consensus event handler minGenFamous after startup: {}",
- () -> Arrays.toString(signedState.getMinGenInfo().toArray()));
-
- // get startRunningHash from reservedSignedState
- final Hash initialHash = new Hash(signedState.getHashEventsCons());
- eventStreamManager.setInitialHash(initialHash);
-
- logger.info(STARTUP.getMarker(), "initialHash after startup {}", () -> initialHash);
- eventStreamManager.setStartWriteAtCompleteWindow(isReconnect);
- }
-
- /**
- * {@inheritDoc}
+ * @param consensusRound the consensus round to apply
*/
- @Override
- public void consensusRound(final ConsensusRound consensusRound) {
- if (consensusRound == null || consensusRound.getConsensusEvents().isEmpty()) {
- // we ignore rounds with no events for now
+ public void handleConsensusRound(@NonNull final ConsensusRound consensusRound) {
+ // consensus rounds with no events are ignored
+ if (consensusRound.isEmpty()) {
+ // Future work: the long term goal is for empty rounds to not be ignored here. For now, the way that the
+ // running hash of consensus events is calculated by the EventStreamManager prevents that from being
+ // possible.
return;
}
- if (!addedFirstRoundInFreeze && isRoundInFreezePeriod(consensusRound)) {
- addedFirstRoundInFreeze = true;
- statusActionSubmitter.submitStatusAction(new FreezePeriodEnteredAction(consensusRound.getRoundNum()));
- }
-
- addConsensusRound(consensusRound);
- }
-
- private boolean isRoundInFreezePeriod(final ConsensusRound round) {
- if (round.isEmpty()) {
- // there are no events in this round
- return false;
- }
- return swirldStateManager.isInFreezePeriod(round.getConsensusTimestamp());
- }
-
- /**
- * Add a consensus event to the queue (q2) for handling.
- *
- * @param consensusRound the consensus round to add
- */
- private void addConsensusRound(final ConsensusRound consensusRound) {
- try {
- // adds this consensus event to eventStreamHelper,
- // which will put it into a queue for calculating runningHash, and a queue for event streaming when enabled
- eventStreamManager.addEvents(consensusRound.getConsensusEvents());
- // this may block until the queue isn't full
- queueThread.put(consensusRound);
- } catch (final InterruptedException e) {
- logger.error(EXCEPTION.getMarker(), "addEvent interrupted");
- Thread.currentThread().interrupt();
- }
- }
-
- /**
- * Adds the consensus events in the round to the eventsAndGenerations queue and feeds their transactions to the
- * consensus state object (which is a SwirldState representing the effect of all consensus transactions so far). It
- * also creates the signed state if Settings.signedStateFreq > 0 and this is a round for which it should be done.
- *
- * @throws InterruptedException if this thread was interrupted while adding a signed state to the signed state
- * queue
- */
- private void applyConsensusRoundToState(final ConsensusRound round) throws InterruptedException {
- // If there has already been a saved state created in a freeze period, do not apply any more rounds to the
- // state until the node shuts down and comes back up (which resets this variable to false).
- if (savedStateInFreeze) {
+ // Once there is a saved state created in a freeze period, we will never apply any more rounds to the state.
+ if (freezeRoundReceived) {
return;
}
- final CycleTimingStat consensusTimingStat = consensusHandlingMetrics.getConsCycleStat();
- consensusTimingStat.startCycle();
-
- waitForEventDurability.accept(round.getKeystoneEvent().getBaseEvent());
-
- consensusTimingStat.setTimePoint(1);
-
- propagateConsensusData(round);
- updatePlatformState(round);
-
- if (round.getEventCount() > 0) {
- consensusHandlingMetrics.recordConsensusTime(round.getConsensusTimestamp());
+ if (swirldStateManager.isInFreezePeriod(consensusRound.getConsensusTimestamp())) {
+ statusActionSubmitter.submitStatusAction(new FreezePeriodEnteredAction(consensusRound.getRoundNum()));
+ freezeRoundReceived = true;
}
- swirldStateManager.handleConsensusRound(round);
- consensusTimingStat.setTimePoint(2);
+ handlerMetrics.recordEventsPerRound(consensusRound.getNumEvents());
+ handlerMetrics.recordConsensusTime(consensusRound.getConsensusTimestamp());
- roundAppliedToStateConsumer.accept(round.getRoundNum());
-
- consensusTimingStat.setTimePoint(3);
-
- consensusTimingStat.setTimePoint(4);
+ try {
+ handlerMetrics.setPhase(WAITING_FOR_EVENT_DURABILITY);
+ waitForEventDurability.accept(consensusRound.getKeystoneEvent().getBaseEvent());
- EventImpl lastEvent = null;
- for (final EventImpl event : round.getConsensusEvents()) {
- lastEvent = event;
- if (event.getHash() == null) {
- CryptographyHolder.get().digestSync(event);
+ handlerMetrics.setPhase(SETTING_EVENT_CONSENSUS_DATA);
+ for (final EventImpl event : consensusRound.getConsensusEvents()) {
+ event.consensusReached();
}
- }
- // update the running hash object
- // if there are no events, the running hash does not change
- if (lastEvent != null) {
- eventsConsRunningHash = lastEvent.getRunningHash();
- }
+ handlerMetrics.setPhase(UPDATING_PLATFORM_STATE);
+ // it is important to update the platform state before handling the consensus round, since the platform
+ // state is passed into the application handle method, and should contain the data for the current round
+ updatePlatformState(consensusRound);
- // time point 3 to the end is misleading on its own because it is recorded even when no signed state is created
- // . For an accurate stat on how much time it takes to create a signed state, refer to
- // newSignedStateCycleTiming in Statistics
- consensusTimingStat.setTimePoint(5);
- updateRunningEventHash();
+ handlerMetrics.setPhase(WAITING_FOR_PREHANDLE);
+ consensusRound.forEach(event -> ((EventImpl) event).getBaseEvent().awaitPrehandleCompletion());
- consensusTimingStat.setTimePoint(6);
+ handlerMetrics.setPhase(HANDLING_CONSENSUS_ROUND);
+ swirldStateManager.handleConsensusRound(consensusRound);
- // If the round should be signed (because the settings say so), create the signed state
- if (timeToSignState(round.getRoundNum())) {
- if (isRoundInFreezePeriod(round)) {
- // We are saving the first state in the freeze period.
- // This should never be set to false once it is true. It is reset by restarting the node
- savedStateInFreeze = true;
+ handlerMetrics.setPhase(MARKING_ROUND_COMPLETE);
+ // this calls into the ConsensusHashManager
+ roundAppliedToStateConsumer.accept(consensusRound.getRoundNum());
+
+ handlerMetrics.setPhase(UPDATING_PLATFORM_STATE_RUNNING_HASH);
+ updatePlatformStateRunningHash(consensusRound);
- // Let the swirld state manager know we are about to write the saved state for the freeze period
- swirldStateManager.savedStateInFreezePeriod();
- }
createSignedState();
+ } catch (final InterruptedException e) {
+ logger.error(EXCEPTION.getMarker(), "handleConsensusRound interrupted");
+ Thread.currentThread().interrupt();
+ } finally {
+ handlerMetrics.setPhase(IDLE);
}
- consensusTimingStat.stopCycle();
}
/**
- * Propagates consensus data from every event to every transaction.
+ * Populate the {@link com.swirlds.platform.state.PlatformState PlatformState} with all needed data for this round.
*
- * @param round the round of events to propagate data in
+ * @param round the consensus round
*/
- private void propagateConsensusData(final ConsensusRound round) {
- for (final EventImpl event : round.getConsensusEvents()) {
- event.consensusReached();
- }
- }
-
- private boolean timeToSignState(final long roundNum) {
- final StateConfig stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class);
- return stateConfig.signedStateFreq() > 0 // and we are signing states
-
- // the first round should be signed and every Nth should be signed, where N is signedStateFreq
- && (roundNum == 1 || roundNum % stateConfig.signedStateFreq() == 0);
- }
-
- /**
- * Populate the {@link com.swirlds.platform.state.PlatformState PlatformState} with all of its needed data for this
- * round, with the exception of the running event hash. Wait until transactions are handled before updating this.
- * This makes it less likely that we will have to wait for the hash to be computed.
- */
- private void updatePlatformState(final ConsensusRound round) {
+ private void updatePlatformState(@NonNull final ConsensusRound round) {
final PlatformState platformState =
swirldStateManager.getConsensusState().getPlatformState();
@@ -456,43 +258,46 @@ private void updatePlatformState(final ConsensusRound round) {
}
/**
- * Update the running event hash in the platform state.
+ * Update the running hash of the consensus events in the platform state
+ *
+ * This method is called after the consensus round has been applied to the state, to minimize the chance that
+ * we have to wait for the running hash to finish being calculated.
+ *
+ * @param round the consensus round
+ * @throws InterruptedException if this thread is interrupted
*/
- private void updateRunningEventHash() throws InterruptedException {
+ private void updatePlatformStateRunningHash(@NonNull final ConsensusRound round) throws InterruptedException {
final PlatformState platformState =
swirldStateManager.getConsensusState().getPlatformState();
- final Hash runningHash = eventsConsRunningHash.getFutureHash().getAndRethrow();
+
+ // Update the running hash object. If there are no events, the running hash does not change.
+ // Future work: this is a redundant check, since empty rounds are currently ignored entirely. The check is here
+ // anyway, for when that changes in the future.
+ if (!round.isEmpty()) {
+ consensusEventsRunningHash = round.getConsensusEvents().getLast().getRunningHash();
+ }
+
+ final Hash runningHash = consensusEventsRunningHash.getFutureHash().getAndRethrow();
platformState.setRunningEventHash(runningHash);
}
+ /**
+ * Create a signed state
+ *
+ * @throws InterruptedException if this thread is interrupted
+ */
private void createSignedState() throws InterruptedException {
- final CycleTimingStat ssTimingStat = consensusHandlingMetrics.getNewSignedStateCycleStat();
- ssTimingStat.startCycle();
+ if (freezeRoundReceived) {
+ // Let the swirld state manager know we are about to write the saved state for the freeze period
+ swirldStateManager.savedStateInFreezePeriod();
+ }
- // create a new signed state, sign it, and send out a new transaction with the signature
- // the signed state keeps a copy that never changes.
+ handlerMetrics.setPhase(GETTING_STATE_TO_SIGN);
final State immutableStateCons = swirldStateManager.getStateForSigning();
- ssTimingStat.setTimePoint(1);
-
+ handlerMetrics.setPhase(CREATING_SIGNED_STATE);
final SignedState signedState = new SignedState(
- platformContext, immutableStateCons, "ConsensusHandler.createSignedState()", savedStateInFreeze);
-
- ssTimingStat.setTimePoint(2);
-
- stateHashSignQueue.put(signedState.reserve("ConsensusHandler.createSignedState()"));
-
- ssTimingStat.stopCycle();
- }
-
- public int getRoundsInQueue() {
- return queueThread.size();
- }
-
- /**
- * {@inheritDoc}
- */
- public int getStateToHashSignSize() {
- return stateHashSignQueue.size();
+ platformContext, immutableStateCons, "ConsensusRoundHandler.createSignedState()", freezeRoundReceived);
+ stateHashSignQueue.put(signedState.reserve("ConsensusRoundHandler.createSignedState()"));
}
}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerPhase.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerPhase.java
new file mode 100644
index 000000000000..6532be696646
--- /dev/null
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerPhase.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.swirlds.platform.eventhandling;
+
+/**
+ * The phase of the consensus round handling process.
+ */
+public enum ConsensusRoundHandlerPhase {
+ /**
+ * Nothing is happening.
+ */
+ IDLE,
+ /**
+ * The handler is waiting for the keystone event of the round to become durable before applying the contained
+ * transactions to the state.
+ */
+ WAITING_FOR_EVENT_DURABILITY,
+ /**
+ * The consensus fields of the events in a round are being populated.
+ */
+ SETTING_EVENT_CONSENSUS_DATA,
+ /**
+ * The round handler is waiting for transaction prehandling to complete.
+ */
+ WAITING_FOR_PREHANDLE,
+ /**
+ * The transactions in the round are being applied to the state.
+ */
+ HANDLING_CONSENSUS_ROUND,
+ /**
+ * The platform state is being updated with results from the round.
+ */
+ UPDATING_PLATFORM_STATE,
+ /**
+ * The platform state is being updated with the running hash of the round.
+ */
+ UPDATING_PLATFORM_STATE_RUNNING_HASH,
+ /**
+ * The consensus hash manager is being informed that the round has been handled.
+ */
+ MARKING_ROUND_COMPLETE,
+ /**
+ * The handler is getting the state to sign.
+ */
+ GETTING_STATE_TO_SIGN,
+ /**
+ * The handler is creating a new signed state instance.
+ */
+ CREATING_SIGNED_STATE
+}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java
deleted file mode 100644
index 1e90cc49e4b9..000000000000
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2022-2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.swirlds.platform.metrics;
-
-import static com.swirlds.metrics.api.FloatFormats.FORMAT_8_1;
-import static com.swirlds.metrics.api.Metrics.INTERNAL_CATEGORY;
-
-import com.swirlds.base.time.Time;
-import com.swirlds.base.utility.Pair;
-import com.swirlds.metrics.api.LongGauge;
-import com.swirlds.metrics.api.Metrics;
-import com.swirlds.platform.eventhandling.ConsensusRoundHandler;
-import com.swirlds.platform.internal.ConsensusRound;
-import com.swirlds.platform.stats.AverageAndMax;
-import com.swirlds.platform.stats.AverageStat;
-import com.swirlds.platform.stats.CycleTimingStat;
-import com.swirlds.platform.stats.cycle.CycleDefinition;
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Provides access to statistics relevant to {@link ConsensusRoundHandler}
- */
-public class ConsensusHandlingMetrics {
- private final CycleTimingStat consensusCycleTiming;
-
- private final CycleTimingStat newSignedStateCycleTiming;
-
- private final AverageAndMax avgEventsPerRound;
-
- private static final LongGauge.Config consensusTimeConfig = new LongGauge.Config(INTERNAL_CATEGORY, "consensusTime")
- .withDescription("The consensus timestamp of the round currently being handled.")
- .withUnit("milliseconds");
- private final LongGauge consensusTime;
-
- private static final LongGauge.Config consensusTimeDeviationConfig = new LongGauge.Config(
- INTERNAL_CATEGORY, "consensusTimeDeviation")
- .withDescription("The difference between the consensus time of "
- + "the round currently being handled and this node's wall clock time. "
- + "Positive values mean that this node's clock is behind the consensus time, "
- + "negative values mean that it's ahead.")
- .withUnit("milliseconds");
- private final LongGauge consensusTimeDeviation;
-
- private final Time time;
-
- /**
- * Constructor of {@code ConsensusHandlingMetrics}
- *
- * @param metrics
- * a reference to the metrics-system
- * @param time provides wall clock time
- * @throws NullPointerException in case {@code metrics} parameter is {@code null}
- */
- public ConsensusHandlingMetrics(final Metrics metrics, final Time time) {
- Objects.requireNonNull(metrics, "metrics must not be null");
- this.time = time;
-
- consensusCycleTiming = new CycleTimingStat(
- metrics,
- ChronoUnit.MILLIS,
- new CycleDefinition(
- INTERNAL_CATEGORY,
- "consRound",
- List.of(
- Pair.of(
- "keystoneFlushMillis_per_round",
- "average time to flush a round's keystone event to disk"),
- Pair.of(
- "dataPropMillis_per_round",
- "average time to propagate consensus data to transactions"),
- Pair.of("handleMillis_per_round", "average time to handle a consensus round"),
- Pair.of(
- "storeMillis_per_round",
- "average time to add consensus round events to signed state storage"),
- Pair.of(
- "hashMillis_per_round",
- "average time spent hashing the consensus round events"),
- Pair.of("buildStateMillis", "average time spent building a signed state"),
- Pair.of(
- "forSigCleanMillis",
- "average time spent expiring signed state storage events"))));
- newSignedStateCycleTiming = new CycleTimingStat(
- metrics,
- ChronoUnit.MICROS,
- new CycleDefinition(
- INTERNAL_CATEGORY,
- "newSS",
- List.of(
- Pair.of("getStateMicros", "average time to get the state to sign"),
- Pair.of(
- "newSSInstanceMicros",
- "average time spent creating the new signed state instance"),
- Pair.of(
- "queueAdmitMicros",
- "average time spent admitting the signed state to the signing queue"))));
- avgEventsPerRound = new AverageAndMax(
- metrics,
- INTERNAL_CATEGORY,
- "events_per_round",
- "average number of events in a consensus round",
- FORMAT_8_1,
- AverageStat.WEIGHT_VOLATILE);
-
- consensusTime = metrics.getOrCreate(consensusTimeConfig);
- consensusTimeDeviation = metrics.getOrCreate(consensusTimeDeviationConfig);
- }
-
- /**
- * @return the cycle timing stat that keeps track of how much time is spent in various parts of {@link
- * ConsensusRoundHandler#consensusRound(ConsensusRound)}
- */
- public CycleTimingStat getConsCycleStat() {
- return consensusCycleTiming;
- }
-
- /**
- * @return the cycle timing stat that keeps track of how much time is spent creating a new signed state in {@link
- * ConsensusRoundHandler#consensusRound(ConsensusRound)}
- */
- public CycleTimingStat getNewSignedStateCycleStat() {
- return newSignedStateCycleTiming;
- }
-
- /**
- * Records the number of events in a round.
- *
- * @param numEvents
- * the number of events in the round
- */
- public void recordEventsPerRound(final int numEvents) {
- avgEventsPerRound.update(numEvents);
- }
-
- /**
- * Records the consensus time.
- *
- * @param consensusTime
- * the consensus time of the last transaction in the round that is currently being handled
- */
- public void recordConsensusTime(final Instant consensusTime) {
- this.consensusTime.set(consensusTime.toEpochMilli());
- consensusTimeDeviation.set(consensusTime.toEpochMilli() - time.now().toEpochMilli());
- }
-}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/RoundHandlingMetrics.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/RoundHandlingMetrics.java
new file mode 100644
index 000000000000..7df0c6feabe0
--- /dev/null
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/RoundHandlingMetrics.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022-2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.swirlds.platform.metrics;
+
+import static com.swirlds.metrics.api.Metrics.INTERNAL_CATEGORY;
+import static com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase.IDLE;
+
+import com.swirlds.base.time.Time;
+import com.swirlds.common.context.PlatformContext;
+import com.swirlds.common.metrics.extensions.PhaseTimer;
+import com.swirlds.common.metrics.extensions.PhaseTimerBuilder;
+import com.swirlds.metrics.api.LongGauge;
+import com.swirlds.metrics.api.Metrics;
+import com.swirlds.platform.eventhandling.ConsensusRoundHandler;
+import com.swirlds.platform.eventhandling.ConsensusRoundHandlerPhase;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Provides access to statistics relevant to {@link ConsensusRoundHandler}
+ */
+public class RoundHandlingMetrics {
+ private static final LongGauge.Config consensusTimeConfig = new LongGauge.Config(INTERNAL_CATEGORY, "consensusTime")
+ .withDescription("The consensus timestamp of the round currently being handled.")
+ .withUnit("milliseconds");
+ private final LongGauge consensusTime;
+
+ private static final LongGauge.Config consensusTimeDeviationConfig = new LongGauge.Config(
+ INTERNAL_CATEGORY, "consensusTimeDeviation")
+ .withDescription("The difference between the consensus time of the round currently being handled and this"
+ + " node's wall clock time. Positive values mean that this node's clock is behind the consensus"
+ + "time, negative values mean that it's ahead.")
+ .withUnit("milliseconds");
+ private final LongGauge consensusTimeDeviation;
+
+ private static final LongGauge.Config eventsPerRoundConfig = new LongGauge.Config(
+ INTERNAL_CATEGORY, "eventsPerRound")
+ .withDescription("The number of events per round")
+ .withUnit("count");
+ private final LongGauge eventsPerRound;
+
+ private final PhaseTimer roundHandlerPhase;
+
+ private final Time time;
+
+ /**
+ * Constructor
+ *
+ * @param platformContext the platform context
+ */
+ public RoundHandlingMetrics(@NonNull final PlatformContext platformContext) {
+ this.time = platformContext.getTime();
+
+ final Metrics metrics = platformContext.getMetrics();
+
+ consensusTime = metrics.getOrCreate(consensusTimeConfig);
+ consensusTimeDeviation = metrics.getOrCreate(consensusTimeDeviationConfig);
+ eventsPerRound = metrics.getOrCreate(eventsPerRoundConfig);
+
+ this.roundHandlerPhase = new PhaseTimerBuilder<>(
+ platformContext, time, "platform", ConsensusRoundHandlerPhase.class)
+ .enableFractionalMetrics()
+ .setInitialPhase(IDLE)
+ .setMetricsNamePrefix("consensus")
+ .build();
+ }
+
+ /**
+ * Records the number of events in a round.
+ *
+ * @param eventCount the number of events in the round
+ */
+ public void recordEventsPerRound(final int eventCount) {
+ eventsPerRound.set(eventCount);
+ }
+
+ /**
+ * Records the consensus time.
+ *
+ * @param consensusTime the consensus time of the last transaction in the round that is currently being handled
+ */
+ public void recordConsensusTime(@NonNull final Instant consensusTime) {
+ this.consensusTime.set(consensusTime.toEpochMilli());
+ consensusTimeDeviation.set(consensusTime.toEpochMilli() - time.now().toEpochMilli());
+ }
+
+ /**
+ * Activate a new phase of the consensus round handler.
+ *
+ * @param phase the new phase
+ */
+ public void setPhase(@NonNull final ConsensusRoundHandlerPhase phase) {
+ Objects.requireNonNull(phase);
+ roundHandlerPhase.activatePhase(phase);
+ }
+}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/LinkedEventIntakeWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/LinkedEventIntakeWiring.java
index 4fa5309be470..e222eab4d9fa 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/LinkedEventIntakeWiring.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/LinkedEventIntakeWiring.java
@@ -34,6 +34,8 @@
* @param eventInput the input wire for events to be added to the hashgraph
* @param pauseInput the input wire for pausing the linked event intake
* @param consensusRoundOutput the output wire for consensus rounds
+ * @param consensusEventsOutput the output wire for consensus events, transformed from the consensus round
+ * output
* @param keystoneEventSequenceNumberOutput the output wire for the keystone event sequence number
* @param flushRunnable the runnable to flush the intake
*/
@@ -41,6 +43,7 @@ public record LinkedEventIntakeWiring(
@NonNull InputWire eventInput,
@NonNull InputWire pauseInput,
@NonNull OutputWire consensusRoundOutput,
+ @NonNull OutputWire> consensusEventsOutput,
@NonNull StandardOutputWire keystoneEventSequenceNumberOutput,
@NonNull Runnable flushRunnable) {
@@ -58,6 +61,7 @@ public static LinkedEventIntakeWiring create(@NonNull final TaskScheduler applicationTransactionPrehandlerScheduler,
@NonNull TaskScheduler> stateSignatureCollectorScheduler,
@NonNull TaskScheduler shadowgraphScheduler,
+ @NonNull TaskScheduler consensusRoundHandlerScheduler,
+ @NonNull TaskScheduler eventStreamManagerScheduler,
+ @NonNull TaskScheduler runningHashUpdateScheduler,
@NonNull TaskScheduler> futureEventBufferScheduler,
@NonNull TaskScheduler> issDetectorScheduler) {
@@ -213,6 +219,27 @@ public static PlatformSchedulers create(
.withFlushingEnabled(true)
.build()
.cast(),
+ // the literal "consensusRoundHandler" is used by the app to log on the transaction handling thread.
+ // Do not modify, unless you also change the TRANSACTION_HANDLING_THREAD_NAME constant
+ model.schedulerBuilder("consensusRoundHandler")
+ .withType(config.consensusRoundHandlerSchedulerType())
+ .withUnhandledTaskCapacity(config.consensusRoundHandlerUnhandledCapacity())
+ .withMetricsBuilder(model.metricsBuilder()
+ .withUnhandledTaskMetricEnabled(true)
+ .withBusyFractionMetricsEnabled(true))
+ .withFlushingEnabled(true)
+ .build()
+ .cast(),
+ // though the eventStreamManager is of DIRECT_STATELESS type, it isn't actually stateless: it just
+ // is thread safe, and can therefore be treated as if it were stateless by the framework
+ model.schedulerBuilder("eventStreamManager")
+ .withType(TaskSchedulerType.DIRECT_STATELESS)
+ .build()
+ .cast(),
+ model.schedulerBuilder("runningHashUpdate")
+ .withType(TaskSchedulerType.DIRECT_STATELESS)
+ .build()
+ .cast(),
model.schedulerBuilder("futureEventBuffer")
.withType(config.futureEventBufferSchedulerType())
.withUnhandledTaskCapacity(config.futureEventBufferUnhandledCapacity())
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java
index 02753593fa06..9652be9f74d2 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java
@@ -78,6 +78,9 @@
* collector
* @param shadowgraphSchedulerType the shadowgraph scheduler type
* @param shadowgraphUnhandledCapacity number of unhandled tasks allowed for the shadowgraph
+ * @param consensusRoundHandlerSchedulerType the consensus round handler scheduler type
+ * @param consensusRoundHandlerUnhandledCapacity number of unhandled tasks allowed for the consensus round
+ * handler
* @param futureEventBufferSchedulerType the future event buffer scheduler type
* @param futureEventBufferUnhandledCapacity number of unhandled tasks allowed for the future event
* buffer
@@ -119,6 +122,8 @@ public record PlatformSchedulersConfig(
@ConfigProperty(defaultValue = "500") int stateSignatureCollectorUnhandledCapacity,
@ConfigProperty(defaultValue = "SEQUENTIAL") TaskSchedulerType shadowgraphSchedulerType,
@ConfigProperty(defaultValue = "500") int shadowgraphUnhandledCapacity,
+ @ConfigProperty(defaultValue = "SEQUENTIAL_THREAD") TaskSchedulerType consensusRoundHandlerSchedulerType,
+ @ConfigProperty(defaultValue = "5") int consensusRoundHandlerUnhandledCapacity,
@ConfigProperty(defaultValue = "SEQUENTIAL") TaskSchedulerType futureEventBufferSchedulerType,
@ConfigProperty(defaultValue = "500") int futureEventBufferUnhandledCapacity,
@ConfigProperty(defaultValue = "SEQUENTIAL") TaskSchedulerType issDetectorSchedulerType,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java
index 884651f44d2e..a13f84539d39 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java
@@ -24,6 +24,8 @@
import com.swirlds.base.time.Time;
import com.swirlds.common.context.PlatformContext;
import com.swirlds.common.io.IOIterator;
+import com.swirlds.common.stream.EventStreamManager;
+import com.swirlds.common.stream.RunningEventHashUpdate;
import com.swirlds.common.utility.Clearable;
import com.swirlds.common.wiring.counters.BackpressureObjectCounter;
import com.swirlds.common.wiring.counters.ObjectCounter;
@@ -49,9 +51,11 @@
import com.swirlds.platform.event.validation.AddressBookUpdate;
import com.swirlds.platform.event.validation.EventSignatureValidator;
import com.swirlds.platform.event.validation.InternalEventValidator;
+import com.swirlds.platform.eventhandling.ConsensusRoundHandler;
import com.swirlds.platform.eventhandling.TransactionPool;
import com.swirlds.platform.gossip.shadowgraph.Shadowgraph;
import com.swirlds.platform.internal.ConsensusRound;
+import com.swirlds.platform.internal.EventImpl;
import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.iss.IssDetector;
import com.swirlds.platform.state.nexus.LatestCompleteStateNexus;
@@ -61,9 +65,11 @@
import com.swirlds.platform.state.signed.StateSignatureCollector;
import com.swirlds.platform.system.status.PlatformStatusManager;
import com.swirlds.platform.wiring.components.ApplicationTransactionPrehandlerWiring;
+import com.swirlds.platform.wiring.components.ConsensusRoundHandlerWiring;
import com.swirlds.platform.wiring.components.EventCreationManagerWiring;
import com.swirlds.platform.wiring.components.EventDurabilityNexusWiring;
import com.swirlds.platform.wiring.components.EventHasherWiring;
+import com.swirlds.platform.wiring.components.EventStreamManagerWiring;
import com.swirlds.platform.wiring.components.EventWindowManagerWiring;
import com.swirlds.platform.wiring.components.FutureEventBufferWiring;
import com.swirlds.platform.wiring.components.GossipWiring;
@@ -72,6 +78,7 @@
import com.swirlds.platform.wiring.components.PcesSequencerWiring;
import com.swirlds.platform.wiring.components.PcesWriterWiring;
import com.swirlds.platform.wiring.components.PostHashCollectorWiring;
+import com.swirlds.platform.wiring.components.RunningHashUpdaterWiring;
import com.swirlds.platform.wiring.components.ShadowgraphWiring;
import com.swirlds.platform.wiring.components.StateSignatureCollectorWiring;
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -111,13 +118,11 @@ public class PlatformWiring implements Startable, Stoppable, Clearable {
private final FutureEventBufferWiring futureEventBufferWiring;
private final GossipWiring gossipWiring;
private final EventWindowManagerWiring eventWindowManagerWiring;
+ private final ConsensusRoundHandlerWiring consensusRoundHandlerWiring;
+ private final EventStreamManagerWiring eventStreamManagerWiring;
+ private final RunningHashUpdaterWiring runningHashUpdaterWiring;
private final IssDetectorWiring issDetectorWiring;
- /**
- * The object counter that spans the event hasher and the post hash collector.
- */
- private final ObjectCounter hashingObjectCounter;
-
private final PlatformCoordinator platformCoordinator;
/**
@@ -142,7 +147,7 @@ public PlatformWiring(@NonNull final PlatformContext platformContext, @NonNull f
// This counter spans both the event hasher and the post hash collector. This is a workaround for the current
// inability of concurrent schedulers to handle backpressure from an immediately subsequent scheduler.
// This counter is the on-ramp for the event hasher, and the off-ramp for the post hash collector.
- hashingObjectCounter = new BackpressureObjectCounter(
+ final ObjectCounter hashingObjectCounter = new BackpressureObjectCounter(
"hashingObjectCounter",
platformContext
.getConfiguration()
@@ -173,8 +178,10 @@ public PlatformWiring(@NonNull final PlatformContext platformContext, @NonNull f
signedStateFileManagerWiring =
SignedStateFileManagerWiring.create(model, schedulers.signedStateFileManagerScheduler());
stateSignerWiring = StateSignerWiring.create(schedulers.stateSignerScheduler());
-
shadowgraphWiring = ShadowgraphWiring.create(schedulers.shadowgraphScheduler());
+ consensusRoundHandlerWiring = ConsensusRoundHandlerWiring.create(schedulers.consensusRoundHandlerScheduler());
+ eventStreamManagerWiring = EventStreamManagerWiring.create(schedulers.eventStreamManagerScheduler());
+ runningHashUpdaterWiring = RunningHashUpdaterWiring.create(schedulers.runningHashUpdateScheduler());
platformCoordinator = new PlatformCoordinator(
hashingObjectCounter,
@@ -187,7 +194,8 @@ public PlatformWiring(@NonNull final PlatformContext platformContext, @NonNull f
linkedEventIntakeWiring,
eventCreationManagerWiring,
applicationTransactionPrehandlerWiring,
- stateSignatureCollectorWiring);
+ stateSignatureCollectorWiring,
+ consensusRoundHandlerWiring);
pcesReplayerWiring = PcesReplayerWiring.create(schedulers.pcesReplayerScheduler());
pcesWriterWiring = PcesWriterWiring.create(schedulers.pcesWriterScheduler());
@@ -257,13 +265,20 @@ private void wire() {
pcesReplayerWiring.doneStreamingPcesOutputWire().solderTo(pcesWriterWiring.doneStreamingPcesInputWire());
pcesReplayerWiring.eventOutput().solderTo(eventHasherWiring.eventInput());
linkedEventIntakeWiring.keystoneEventSequenceNumberOutput().solderTo(pcesWriterWiring.flushRequestInputWire());
+ linkedEventIntakeWiring.consensusRoundOutput().solderTo(consensusRoundHandlerWiring.roundInput());
linkedEventIntakeWiring.consensusRoundOutput().solderTo(eventWindowManagerWiring.consensusRoundInput());
+ linkedEventIntakeWiring.consensusEventsOutput().solderTo(eventStreamManagerWiring.eventsInput());
pcesWriterWiring
.latestDurableSequenceNumberOutput()
.solderTo(eventDurabilityNexusWiring.latestDurableSequenceNumber());
signedStateFileManagerWiring
.oldestMinimumGenerationOnDiskOutputWire()
.solderTo(pcesWriterWiring.minimumAncientIdentifierToStoreInputWire());
+
+ runningHashUpdaterWiring
+ .runningHashUpdateOutput()
+ .solderTo(consensusRoundHandlerWiring.runningHashUpdateInput());
+ runningHashUpdaterWiring.runningHashUpdateOutput().solderTo(eventStreamManagerWiring.runningHashUpdateInput());
}
/**
@@ -318,6 +333,8 @@ public void wireExternalComponents(
* @param eventCreationManager the event creation manager to bind
* @param swirldStateManager the swirld state manager to bind
* @param stateSignatureCollector the signed state manager to bind
+ * @param consensusRoundHandler the consensus round handler to bind
+ * @param eventStreamManager the event stream manager to bind
* @param futureEventBuffer the future event buffer to bind
* @param issDetector the ISS detector to bind
*/
@@ -339,6 +356,8 @@ public void bind(
@NonNull final EventCreationManager eventCreationManager,
@NonNull final SwirldStateManager swirldStateManager,
@NonNull final StateSignatureCollector stateSignatureCollector,
+ @NonNull final ConsensusRoundHandler consensusRoundHandler,
+ @NonNull final EventStreamManager eventStreamManager,
@NonNull final FutureEventBuffer futureEventBuffer,
@NonNull final IssDetector issDetector) {
@@ -359,6 +378,8 @@ public void bind(
eventCreationManagerWiring.bind(eventCreationManager);
applicationTransactionPrehandlerWiring.bind(swirldStateManager);
stateSignatureCollectorWiring.bind(stateSignatureCollector);
+ consensusRoundHandlerWiring.bind(consensusRoundHandler);
+ eventStreamManagerWiring.bind(eventStreamManager);
futureEventBufferWiring.bind(futureEventBuffer);
issDetectorWiring.bind(issDetector);
}
@@ -501,6 +522,15 @@ public StandardOutputWire getKeystoneEventSequenceNumberOutput() {
return linkedEventIntakeWiring.keystoneEventSequenceNumberOutput();
}
+ /**
+ * Update the running hash for all components that need it.
+ *
+ * @param runningHashUpdate the object containing necessary information to update the running hash
+ */
+ public void updateRunningHash(@NonNull final RunningEventHashUpdate runningHashUpdate) {
+ runningHashUpdaterWiring.runningHashUpdateInput().inject(runningHashUpdate);
+ }
+
/**
* @return the wiring wrapper for the ISS detector
*/
@@ -524,6 +554,13 @@ public void flushIntakePipeline() {
platformCoordinator.flushIntakePipeline();
}
+ /**
+ * Flush the consensus round handler.
+ */
+ public void flushConsensusRoundHandler() {
+ consensusRoundHandlerWiring.flushRunnable().run();
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/ConsensusRoundHandlerWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/ConsensusRoundHandlerWiring.java
new file mode 100644
index 000000000000..85823967708d
--- /dev/null
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/ConsensusRoundHandlerWiring.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.swirlds.platform.wiring.components;
+
+import com.swirlds.common.stream.RunningEventHashUpdate;
+import com.swirlds.common.wiring.schedulers.TaskScheduler;
+import com.swirlds.common.wiring.wires.input.BindableInputWire;
+import com.swirlds.common.wiring.wires.input.InputWire;
+import com.swirlds.platform.eventhandling.ConsensusRoundHandler;
+import com.swirlds.platform.internal.ConsensusRound;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * Wiring for the {@link com.swirlds.platform.eventhandling.ConsensusRoundHandler}
+ *
+ * @param roundInput the input wire for consensus rounds to be applied to the state
+ * @param runningHashUpdateInput the input wire for updating the running event hash
+ * @param flushRunnable the runnable to flush the task scheduler
+ */
+public record ConsensusRoundHandlerWiring(
+ @NonNull InputWire roundInput,
+ @NonNull InputWire runningHashUpdateInput,
+ @NonNull Runnable flushRunnable) {
+ /**
+ * Create a new instance of this wiring.
+ *
+ * @param taskScheduler the task scheduler for this wiring object
+ * @return the new wiring instance
+ */
+ @NonNull
+ public static ConsensusRoundHandlerWiring create(@NonNull final TaskScheduler taskScheduler) {
+ return new ConsensusRoundHandlerWiring(
+ taskScheduler.buildInputWire("rounds"),
+ taskScheduler.buildInputWire("running hash update"),
+ taskScheduler::flush);
+ }
+
+ /**
+ * Bind the consensus round handler to this wiring.
+ *
+ * @param consensusRoundHandler the consensus round handler to bind
+ */
+ public void bind(@NonNull final ConsensusRoundHandler consensusRoundHandler) {
+ ((BindableInputWire) roundInput).bind(consensusRoundHandler::handleConsensusRound);
+ ((BindableInputWire) runningHashUpdateInput)
+ .bind(consensusRoundHandler::updateRunningHash);
+ }
+}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventStreamManagerWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventStreamManagerWiring.java
new file mode 100644
index 000000000000..ed664decb0bc
--- /dev/null
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventStreamManagerWiring.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.swirlds.platform.wiring.components;
+
+import com.swirlds.common.stream.EventStreamManager;
+import com.swirlds.common.stream.RunningEventHashUpdate;
+import com.swirlds.common.wiring.schedulers.TaskScheduler;
+import com.swirlds.common.wiring.wires.input.BindableInputWire;
+import com.swirlds.common.wiring.wires.input.InputWire;
+import com.swirlds.platform.internal.EventImpl;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.util.List;
+
+/**
+ * The wiring for the {@link EventStreamManager}
+ *
+ * @param eventsInput the input wire for consensus events
+ * @param runningHashUpdateInput the input wire to update the running hash upon reconnect or restart
+ */
+public record EventStreamManagerWiring(
+ @NonNull InputWire> eventsInput,
+ @NonNull InputWire runningHashUpdateInput) {
+
+ /**
+ * Create a new wiring object
+ *
+ * @param taskScheduler the task scheduler to use
+ * @return the new wiring object
+ */
+ @NonNull
+ public static EventStreamManagerWiring create(@NonNull final TaskScheduler taskScheduler) {
+ return new EventStreamManagerWiring(
+ taskScheduler.buildInputWire("events"), taskScheduler.buildInputWire("running hash update"));
+ }
+
+ /**
+ * Bind the {@link EventStreamManager} to this wiring
+ *
+ * @param eventStreamManager the event stream manager to bind
+ */
+ public void bind(@NonNull final EventStreamManager eventStreamManager) {
+ ((BindableInputWire, Void>) eventsInput).bind(eventStreamManager::addEvents);
+ ((BindableInputWire) runningHashUpdateInput)
+ .bind(eventStreamManager::updateRunningHash);
+ }
+}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/RunningHashUpdaterWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/RunningHashUpdaterWiring.java
new file mode 100644
index 000000000000..c6c5ccbf79b4
--- /dev/null
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/RunningHashUpdaterWiring.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.swirlds.platform.wiring.components;
+
+import com.swirlds.common.stream.RunningEventHashUpdate;
+import com.swirlds.common.wiring.schedulers.TaskScheduler;
+import com.swirlds.common.wiring.wires.input.BindableInputWire;
+import com.swirlds.common.wiring.wires.input.InputWire;
+import com.swirlds.common.wiring.wires.output.OutputWire;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * A wiring object for distributing {@link RunningEventHashUpdate}s
+ *
+ * @param runningHashUpdateInput the input wire for running hash updates to be distributed
+ * @param runningHashUpdateOutput the output wire for running hash updates to be distributed
+ */
+public record RunningHashUpdaterWiring(
+ @NonNull InputWire runningHashUpdateInput,
+ @NonNull OutputWire runningHashUpdateOutput) {
+
+ /**
+ * Create a new wiring object
+ *
+ * @param taskScheduler the task scheduler to use
+ * @return the new wiring object
+ */
+ @NonNull
+ public static RunningHashUpdaterWiring create(@NonNull final TaskScheduler taskScheduler) {
+ final BindableInputWire inputWire =
+ taskScheduler.buildInputWire("running hash update");
+ final RunningHashUpdaterWiring wiring = new RunningHashUpdaterWiring(inputWire, taskScheduler.getOutputWire());
+
+ // this is just a pass through method
+ inputWire.bind(runningHashUpdate -> runningHashUpdate);
+
+ return wiring;
+ }
+}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/diagram-commands.txt b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/diagram-commands.txt
deleted file mode 100644
index 46a1bceb8aec..000000000000
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/diagram-commands.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-########################################################################################################################
-#### Slight Collapsing
-
-Groups high level grouping of components and component groups. Substitutes known spammy wires.
-
-pcli diagram \
- -l 'applicationTransactionPrehandler:futures:linkedEventIntake' \
- -s 'eventWindowManager:non-ancient event window:ʘ' \
- -s 'heartbeat:heartbeat:♡' \
- -s 'eventCreationManager:non-validated events:†' \
- -s 'applicationTransactionPrehandler:futures:★' \
- -s 'pcesReplayer:done streaming pces:@' \
- -s 'inOrderLinker:events to gossip:g' \
- -g 'Event Validation:internalEventValidator,eventDeduplicator,eventSignatureValidator' \
- -g 'Event Hashing:eventHasher,postHashCollector' \
- -g 'Orphan Buffer:orphanBuffer,orphanBufferSplitter' \
- -g 'Linked Event Intake:linkedEventIntake,linkedEventIntakeSplitter' \
- -g 'State File Management:saveToDiskFilter,signedStateFileManager,extractOldestMinimumGenerationOnDisk,toStateWrittenToDiskAction' \
- -g 'State Signature Collection:stateSignatureCollector,reservedStateSplitter,allStatesReserver,completeStateFilter,completeStatesReserver,extractConsensusSignatureTransactions,extractPreconsensusSignatureTransactions' \
- -g 'Intake Pipeline:Event Validation,Orphan Buffer,Event Hashing' \
- -g 'PCES:pcesSequencer,pcesWriter,eventDurabilityNexus' \
- -g 'Consensus Pipeline:inOrderLinker,Linked Event Intake,g' \
- -g 'Event Creation:futureEventBuffer,futureEventBufferSplitter,eventCreationManager' \
- -g 'Gossip:gossip,shadowgraph' \
- -c 'Orphan Buffer'
-
-########################################################################################################################
-#### Heavy Collapsing
-
-Same as 'Uncollapsed' but with low level things collapsed. Attempts to hide things like transformers and splitters.
-
-pcli diagram \
- -l 'applicationTransactionPrehandler:futures:linkedEventIntake' \
- -s 'eventWindowManager:non-ancient event window:ʘ' \
- -s 'heartbeat:heartbeat:♡' \
- -s 'eventCreationManager:non-validated events:†' \
- -s 'applicationTransactionPrehandler:futures:★' \
- -s 'pcesReplayer:done streaming pces:@' \
- -s 'extractOldestMinimumGenerationOnDisk:minimum identifier to store:s' \
- -s 'inOrderLinker:events to gossip:g' \
- -g 'Event Validation:internalEventValidator,eventDeduplicator,eventSignatureValidator' \
- -g 'Event Hashing:eventHasher,postHashCollector' \
- -g 'Orphan Buffer:orphanBuffer,orphanBufferSplitter' \
- -g 'Linked Event Intake:linkedEventIntake,linkedEventIntakeSplitter' \
- -g 'State File Management:saveToDiskFilter,signedStateFileManager,extractOldestMinimumGenerationOnDisk,toStateWrittenToDiskAction' \
- -g 'State Signature Collection:stateSignatureCollector,reservedStateSplitter,allStatesReserver,completeStateFilter,completeStatesReserver,extractConsensusSignatureTransactions,extractPreconsensusSignatureTransactions' \
- -g 'Intake Pipeline:Event Validation,Orphan Buffer,Event Hashing' \
- -g 'PCES Writer:pcesSequencer,pcesWriter,eventDurabilityNexus' \
- -g 'Consensus Pipeline:inOrderLinker,Linked Event Intake,g' \
- -g 'Event Creation:futureEventBuffer,futureEventBufferSplitter,eventCreationManager' \
- -g 'Gossip:gossip,shadowgraph' \
- -c 'Orphan Buffer' \
- -c 'Linked Event Intake' \
- -c 'State File Management' \
- -c 'State Signature Collection' \
- -c 'State Signature Collection' \
- -c 'State File Management' \
- -c 'Event Hashing' \
- -c 'PCES Writer'
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/generate-platform-diagram.sh b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/generate-platform-diagram.sh
new file mode 100755
index 000000000000..bcfb05c61b2a
--- /dev/null
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/generate-platform-diagram.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+pcli diagram \
+ -l 'applicationTransactionPrehandler:futures:consensusRoundHandler' \
+ -l 'eventDurabilityNexus:wait for durability:consensusRoundHandler' \
+ -s 'eventWindowManager:non-ancient event window:ʘ' \
+ -s 'heartbeat:heartbeat:♡' \
+ -s 'eventCreationManager:non-validated events:†' \
+ -s 'applicationTransactionPrehandler:futures:★' \
+ -s 'eventDurabilityNexus:wait for durability:🕑' \
+ -s 'pcesReplayer:done streaming pces:@' \
+ -s 'inOrderLinker:events to gossip:g' \
+ -s 'runningHashUpdate:running hash update:§' \
+ -s 'linkedEventIntake:flush request:Ξ' \
+ -g 'Event Validation:internalEventValidator,eventDeduplicator,eventSignatureValidator' \
+ -g 'Event Hashing:eventHasher,postHashCollector' \
+ -g 'Orphan Buffer:orphanBuffer,orphanBufferSplitter' \
+ -g 'Linked Event Intake:linkedEventIntake,linkedEventIntakeSplitter,eventWindowManager' \
+ -g 'State File Management:saveToDiskFilter,signedStateFileManager,extractOldestMinimumGenerationOnDisk,toStateWrittenToDiskAction' \
+ -g 'State Signature Collection:stateSignatureCollector,reservedStateSplitter,allStatesReserver,completeStateFilter,completeStatesReserver,extractConsensusSignatureTransactions,extractPreconsensusSignatureTransactions' \
+ -g 'Intake Pipeline:Event Validation,Orphan Buffer,Event Hashing' \
+ -g 'Preconsensus Event Stream:pcesSequencer,pcesWriter,eventDurabilityNexus' \
+ -g 'Consensus Event Stream:getEvents,eventStreamManager' \
+ -g 'Consensus Pipeline:inOrderLinker,Linked Event Intake,g' \
+ -g 'Event Creation:futureEventBuffer,futureEventBufferSplitter,eventCreationManager' \
+ -g 'Gossip:gossip,shadowgraph' \
+ -c 'Consensus Event Stream' \
+ -c 'Orphan Buffer' \
+ -c 'Linked Event Intake' \
+ -c 'State Signature Collection' \
+ -c 'State File Management'
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/AbstractEventHandlerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/AbstractEventHandlerTests.java
deleted file mode 100644
index c2954c298e5a..000000000000
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/AbstractEventHandlerTests.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2022-2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.swirlds.platform.eventhandling;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.swirlds.common.metrics.noop.NoOpMetrics;
-import com.swirlds.common.platform.NodeId;
-import com.swirlds.metrics.api.Metrics;
-import com.swirlds.platform.internal.ConsensusRound;
-import com.swirlds.platform.internal.EventImpl;
-import com.swirlds.platform.metrics.ConsensusHandlingMetrics;
-import com.swirlds.platform.metrics.ConsensusMetrics;
-import com.swirlds.platform.metrics.SwirldStateMetrics;
-import com.swirlds.platform.state.State;
-import com.swirlds.platform.stats.CycleTimingStat;
-import com.swirlds.platform.system.transaction.ConsensusTransactionImpl;
-import com.swirlds.platform.system.transaction.SwirldTransaction;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.function.BiConsumer;
-import java.util.function.Supplier;
-
-public abstract class AbstractEventHandlerTests {
-
- private static final int NUM_NODES = 10;
-
- protected NodeId selfId;
- protected Metrics metrics;
- protected SwirldStateMetrics ssStats;
- protected ConsensusMetrics consensusMetrics;
- protected ConsensusHandlingMetrics consensusHandlingMetrics;
- protected BiConsumer consensusSystemTransactionManager;
- protected Supplier consEstimateSupplier;
- protected Random random;
-
- protected void setup() {
- selfId = new NodeId(0L);
- metrics = new NoOpMetrics();
- ssStats = mock(SwirldStateMetrics.class);
- consensusMetrics = mock(ConsensusMetrics.class);
- consensusHandlingMetrics = mock(ConsensusHandlingMetrics.class);
- when(consensusHandlingMetrics.getConsCycleStat()).thenReturn(mock(CycleTimingStat.class));
- consensusSystemTransactionManager = (s, r) -> {};
- consEstimateSupplier = Instant::now;
- random = ThreadLocalRandom.current();
- }
-
- /**
- * Create mock events, some with mock transactions and some with no transactions.
- *
- * @param numEvents
- * the number of events to create
- * @param numTransactions
- * the number of transactions in each event that has transactions
- * @param includeEmptyEvents
- * true if empty events should be created
- * @return list of events
- */
- protected List createEvents(
- final int numEvents, final int numTransactions, final boolean includeEmptyEvents) {
- final List events = new ArrayList<>(numEvents);
- int numEmptyEvents = 0;
- if (includeEmptyEvents) {
- numEmptyEvents = numEvents / 3;
- }
- for (int i = 0; i < numEvents; i++) {
- final EventImpl event = mock(EventImpl.class);
- if (i > numEmptyEvents) {
- addTransactionsToEvent(event, numTransactions);
- } else {
- when(event.getTransactions()).thenReturn(new ConsensusTransactionImpl[0]);
- when(event.isEmpty()).thenReturn(true);
- }
- when(event.getCreatorId()).thenReturn(new NodeId(random.nextInt(NUM_NODES)));
- when(event.getConsensusTimestamp()).thenReturn(Instant.now());
- final boolean isConsensus = random.nextBoolean();
- when(event.isConsensus()).thenReturn(isConsensus);
- if (isConsensus) {
- when(event.getConsensusTimestamp()).thenReturn(Instant.now());
- }
- events.add(event);
- }
- return events;
- }
-
- private void addTransactionsToEvent(final EventImpl event, final int numTransactions) {
- final SwirldTransaction[] tx = new SwirldTransaction[numTransactions];
- for (int j = 0; j < numTransactions; j++) {
- tx[j] = mock(SwirldTransaction.class);
- }
- when(event.getTransactions()).thenReturn(tx);
- }
-}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusQueueTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusQueueTests.java
deleted file mode 100644
index 42231ffd1073..000000000000
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusQueueTests.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2022-2024 Hedera Hashgraph, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.swirlds.platform.eventhandling;
-
-import static com.swirlds.common.threading.manager.AdHocThreadManager.getStaticThreadManager;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.swirlds.common.platform.NodeId;
-import com.swirlds.common.test.fixtures.AssertionUtils;
-import com.swirlds.common.threading.framework.QueueThread;
-import com.swirlds.common.threading.framework.config.QueueThreadConfiguration;
-import com.swirlds.platform.internal.ConsensusRound;
-import com.swirlds.platform.internal.EventImpl;
-import com.swirlds.platform.metrics.ConsensusHandlingMetrics;
-import java.time.Duration;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class ConsensusQueueTests {
-
- private static final NodeId SELF_ID = new NodeId(0L);
-
- private static final int EVENT_CAPACITY = 100;
- public static final String PUT_ERROR = "put() did not update eventsInQueue.";
-
- private QueueThread queueThread;
-
- private final AtomicInteger roundsHandled = new AtomicInteger(0);
-
- private static ConsensusQueue newQueue() {
- return new ConsensusQueue(mock(ConsensusHandlingMetrics.class), EVENT_CAPACITY);
- }
-
- @BeforeEach
- public void setup() {
- queueThread = new QueueThreadConfiguration(getStaticThreadManager())
- .setQueue(newQueue())
- .setNodeId(SELF_ID)
- .setHandler(this::handleRound)
- .build();
- }
-
- private int getNumEventsInQueue() {
- return queueThread.size();
- }
-
- /**
- * Tests that a single round with a number of events that exceeds the event capacity can be added only when the
- * queue is empty.
- *
- * @throws InterruptedException
- * if this thread is interrupted
- */
- @Test
- void testPutLargeRoundEmptyQueue() throws InterruptedException {
- final ConsensusRound bigRound = createRound(EVENT_CAPACITY + 1);
-
- queueThread.put(bigRound);
-
- assertEquals(bigRound.getNumEvents(), queueThread.size(), PUT_ERROR);
-
- final ExecutorService executorService = Executors.newSingleThreadExecutor();
- final Future future = executorService.submit(() -> {
- queueThread.put(bigRound);
- return null;
- });
-
- boolean putBlocked = false;
- try {
- future.get(100, TimeUnit.MILLISECONDS);
- } catch (ExecutionException | TimeoutException | InterruptedException e) {
- putBlocked = true;
- }
-
- assertTrue(
- putBlocked,
- "Adding a round with more events than the" + " event capacity should block until the queue is empty.");
- }
-
- /**
- * Test that the q2 stat is updated when rounds are added and removed.
- *
- * @throws InterruptedException
- * if this thread is interrupted
- */
- @Test
- void testQ2StatUpdated() throws InterruptedException {
- final ConsensusRound smallRound = createRound(10);
-
- // The queue is empty. Add a round that will not exceed capacity
- queueThread.put(smallRound);
- assertEquals(smallRound.getNumEvents(), queueThread.size(), PUT_ERROR);
-
- // Add the round again and check the stats
- queueThread.put(smallRound);
- assertEquals(smallRound.getNumEvents() * 2, queueThread.size(), PUT_ERROR);
-
- queueThread.start();
-
- AssertionUtils.assertEventuallyEquals(
- 2, roundsHandled::get, Duration.ofMillis(1000), "Rounds were not handled");
- assertEquals(0, getNumEventsInQueue(), "eventsInQueue was not updated when a consensus was removed.");
- }
-
- /**
- * Test that adding another round is blocked when adding that round would exceed the event capacity.
- *
- * @throws InterruptedException
- * if this thread is interrupted
- */
- @Test
- void testBlockWhenQueueFull() throws InterruptedException {
- final ConsensusRound bigRound = createRound(EVENT_CAPACITY - 10);
- final ConsensusRound smallRound = createRound(10);
-
- // The queue is empty. Add a that will not exceed capacity
- queueThread.put(bigRound);
- assertEquals(EVENT_CAPACITY - 10, queueThread.size(), PUT_ERROR);
-
- // Try adding the big round again. It should block because the number of events would exceed the capacity.
- final ExecutorService executorService = Executors.newSingleThreadExecutor();
- final Future future = executorService.submit(() -> {
- queueThread.put(bigRound);
- return null;
- });
-
- boolean putBlocked = false;
- try {
- future.get(100, TimeUnit.MILLISECONDS);
- } catch (ExecutionException | TimeoutException | InterruptedException e) {
- putBlocked = true;
- }
-
- assertTrue(
- putBlocked,
- "Adding a with events that would exceed" + " event capacity should block until the queue is empty.");
-
- // Should succeed, because the number of events does not exceed the capacity
- queueThread.put(smallRound);
-
- assertEquals(bigRound.getNumEvents() + smallRound.getNumEvents(), queueThread.size(), PUT_ERROR);
- }
-
- @Test
- void testPoll() throws InterruptedException {
- assertNull(queueThread.poll());
- assertEquals(0, queueThread.size(), "eventsInQueue should not be modified when the queue is already empty.");
- queueThread.put(createRound(15));
- assertEquals(15, queueThread.size(), PUT_ERROR);
- assertNotNull(queueThread.poll());
- assertEquals(0, queueThread.size(), "poll() did not update eventsInQueue.");
- }
-
- @Test
- void testPollWithTimeout() throws InterruptedException {
- assertNull(queueThread.poll(10, TimeUnit.MILLISECONDS));
- assertEquals(0, queueThread.size(), "eventsInQueue should not be modified when the queue is already empty.");
- queueThread.put(createRound(15));
- assertEquals(15, queueThread.size(), PUT_ERROR);
- assertNotNull(queueThread.poll(10, TimeUnit.MILLISECONDS));
- assertEquals(0, queueThread.size(), "poll() did not update eventsInQueue.");
- }
-
- @Test
- void testOffer() throws InterruptedException {
- queueThread.put(createRound(100));
- assertFalse(queueThread.offer(createRound(1)), "offer() did not respect the event capacity.");
-
- queueThread.clear();
- queueThread.put(createRound(10));
- assertEquals(10, queueThread.size(), PUT_ERROR);
- assertTrue(queueThread.offer(createRound(2)));
- assertEquals(12, queueThread.size(), "offer() did not update eventsInQueue.");
- }
-
- @Test
- void testOfferWithTimeout() throws InterruptedException {
- queueThread.offer(createRound(10), 100, TimeUnit.MILLISECONDS);
- assertEquals(10, queueThread.size(), "offer() did not update eventsInQueue.");
-
- queueThread.put(createRound(80));
- assertEquals(90, queueThread.size(), PUT_ERROR);
-
- assertFalse(
- queueThread.offer(createRound(20), 100, TimeUnit.MILLISECONDS),
- "offer(timeout, unit) should return false if no room is available after the timeout.");
- assertEquals(
- 90,
- queueThread.size(),
- "offer(timeout, unit) should not change the eventsInQueue number when nothing was inserted.");
- }
-
- @Test
- void testRemove() throws InterruptedException {
- assertThrows(
- NoSuchElementException.class,
- () -> queueThread.remove(),
- "Attempting to remove from an empty queue should throw an exception.");
- final ConsensusRound round = createRound(10);
- queueThread.put(round);
- assertEquals(10, queueThread.size(), PUT_ERROR);
- assertEquals(round, queueThread.remove(), "remove() did not supply the correct round instance.");
-
- assertEquals(0, queueThread.size(), "remove() did not update eventsInQueue.");
- }
-
- void handleRound(final ConsensusRound round) {
- roundsHandled.addAndGet(1);
- }
-
- private ConsensusRound createRound(final int numEvents) {
- final ConsensusRound round = mock(ConsensusRound.class);
- final List eventList = mock(List.class);
- when(round.getNumEvents()).thenReturn(numEvents);
- when(round.getConsensusEvents()).thenReturn(eventList);
- return round;
- }
-}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java
index e67a6bb90e4a..b16ba0646645 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java
@@ -16,211 +16,187 @@
package com.swirlds.platform.eventhandling;
-import static com.swirlds.common.threading.manager.AdHocThreadManager.getStaticThreadManager;
-import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.swirlds.base.function.CheckedConsumer;
+import com.swirlds.base.time.Time;
import com.swirlds.common.context.PlatformContext;
-import com.swirlds.common.stream.EventStreamManager;
-import com.swirlds.common.test.fixtures.RandomAddressBookGenerator;
-import com.swirlds.common.test.fixtures.junit.tags.TestQualifierTags;
+import com.swirlds.common.crypto.Hash;
+import com.swirlds.common.crypto.RunningHash;
import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder;
-import com.swirlds.common.threading.framework.QueueThread;
-import com.swirlds.common.threading.utility.ThrowingRunnable;
-import com.swirlds.config.api.Configuration;
-import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder;
+import com.swirlds.common.threading.futures.StandardFuture;
+import com.swirlds.platform.event.GossipEvent;
import com.swirlds.platform.internal.ConsensusRound;
import com.swirlds.platform.internal.EventImpl;
-import com.swirlds.platform.metrics.SwirldStateMetrics;
import com.swirlds.platform.state.PlatformState;
import com.swirlds.platform.state.State;
import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.signed.ReservedSignedState;
-import com.swirlds.platform.system.BasicSoftwareVersion;
-import com.swirlds.platform.system.SwirldState;
-import com.swirlds.platform.system.address.AddressBook;
+import com.swirlds.platform.system.SoftwareVersion;
import com.swirlds.platform.system.status.StatusActionSubmitter;
-import com.swirlds.platform.test.fixtures.state.DummySwirldState;
-import java.time.Duration;
-import java.time.temporal.ChronoUnit;
+import com.swirlds.platform.system.status.actions.FreezePeriodEnteredAction;
+import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.RepeatedTest;
-import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
-class ConsensusRoundHandlerTests extends AbstractEventHandlerTests {
-
- private EventStreamManager eventStreamManager;
- private QueueThread stateHashSignQueue;
-
- private ConsensusRoundHandler consensusRoundHandler;
+/**
+ * Unit tests for {@link ConsensusRoundHandler}.
+ */
+class ConsensusRoundHandlerTests {
+ private ConsensusRound mockConsensusRound(
+ @NonNull final EventImpl keystoneEvent, @NonNull final List events, final long roundNumber) {
+ final ConsensusRound consensusRound = mock(ConsensusRound.class);
+ when(consensusRound.getConsensusEvents()).thenReturn(events);
+ when(consensusRound.getConsensusTimestamp())
+ .thenReturn(Time.getCurrent().now());
+ when(consensusRound.getKeystoneEvent()).thenReturn(keystoneEvent);
+ when(consensusRound.getRoundNum()).thenReturn(roundNumber);
+ when(consensusRound.isEmpty()).thenReturn(events.isEmpty());
+
+ return consensusRound;
+ }
- @Override
- @BeforeEach
- public void setup() {
- super.setup();
- eventStreamManager = mock(EventStreamManager.class);
- stateHashSignQueue = mock(QueueThread.class);
+ private static EventImpl mockEvent() throws InterruptedException {
+ final RunningHash runningHash = mock(RunningHash.class);
+ final Hash hash = mock(Hash.class);
+ final StandardFuture futureHash = mock(StandardFuture.class);
+ when(futureHash.getAndRethrow()).thenReturn(hash);
+ when(runningHash.getFutureHash()).thenReturn(futureHash);
+ final GossipEvent gossipEvent = mock(GossipEvent.class);
+ final EventImpl outputEvent = mock(EventImpl.class);
+ when(outputEvent.getRunningHash()).thenReturn(runningHash);
+ when(outputEvent.getBaseEvent()).thenReturn(gossipEvent);
+
+ return outputEvent;
}
- /**
- * Verify that the consensus handler thread does not make reconnect wait for it to drain the queue of
- * consensus rounds.
- */
- @RepeatedTest(10)
- @Tag(TestQualifierTags.TIME_CONSUMING)
- @DisplayName("Reconnect should not wait for queue to be drained")
- void queueNotDrainedOnReconnect() {
+ private static SwirldStateManager mockSwirldStateManager(@NonNull final PlatformState platformState) {
+ final State consensusState = mock(State.class);
+ final State stateForSigning = mock(State.class);
+ when(consensusState.getPlatformState()).thenReturn(platformState);
final SwirldStateManager swirldStateManager = mock(SwirldStateManager.class);
+ when(swirldStateManager.getConsensusState()).thenReturn(consensusState);
+ when(swirldStateManager.getStateForSigning()).thenReturn(stateForSigning);
- // The maximum number of events drained to the QueueThread buffer at a time
- final long maxRoundsInBuffer = 100;
- final long sleepMillisPerRound = 10;
-
- // Tracks the number of events handled from the queue
- final AtomicInteger numEventsHandled = new AtomicInteger(0);
-
- // sleep for a little while to pretend to handle an event
- doAnswer((e) -> {
- Thread.sleep(sleepMillisPerRound);
- numEventsHandled.incrementAndGet();
- return null;
- })
- .when(swirldStateManager)
- .handleConsensusRound(any(ConsensusRound.class));
-
- final ExecutorService executor = Executors.newFixedThreadPool(1);
-
- // Set up a separate thread to invoke clear
- final Callable clear = (ThrowingRunnable) () -> consensusRoundHandler.clear();
+ return swirldStateManager;
+ }
+ @Test
+ @DisplayName("Normal operation")
+ void normalOperation() throws InterruptedException {
final PlatformContext platformContext =
TestPlatformContextBuilder.create().build();
+ final PlatformState platformState = mock(PlatformState.class);
+ final SwirldStateManager swirldStateManager = mockSwirldStateManager(platformState);
+
+ final BlockingQueue stateHashSignQueue = mock(BlockingQueue.class);
+ final CheckedConsumer waitForEventDurability = mock(CheckedConsumer.class);
+ final StatusActionSubmitter statusActionSubmitter = mock(StatusActionSubmitter.class);
- consensusRoundHandler = new ConsensusRoundHandler(
+ final AtomicLong roundAppliedToState = new AtomicLong(0);
+ final Consumer roundAppliedToStateConsumer = roundAppliedToState::set;
+
+ final ConsensusRoundHandler consensusRoundHandler = new ConsensusRoundHandler(
platformContext,
- getStaticThreadManager(),
- selfId,
swirldStateManager,
- consensusHandlingMetrics,
- eventStreamManager,
stateHashSignQueue,
- e -> {},
- mock(StatusActionSubmitter.class),
- (round) -> {},
- new BasicSoftwareVersion(1));
-
- final int numRounds = 500;
- final ConsensusRound round = mock(ConsensusRound.class);
-
- // Start the consensus handler and add events to the queue for it to handle
- consensusRoundHandler.start();
- for (int i = 0; i < numRounds; i++) {
- consensusRoundHandler.consensusRound(round);
- }
+ waitForEventDurability,
+ statusActionSubmitter,
+ roundAppliedToStateConsumer,
+ mock(SoftwareVersion.class));
- // Make the separate thread invoke clear()
- final Future future = executor.submit(clear);
+ final EventImpl keystoneEvent = mockEvent();
+ final List events = List.of(mockEvent(), mockEvent(), mockEvent());
- // Wait up to the amount of time it would take for two full buffer of events to be handled. This entire time
- // shouldn't be needed, but it's a max time to allow for different systems and other programs running at the
- // same time. As long as this is less than the amount of time it takes to handle all the events in the queue,
- // the value is valid.
- final long maxMillisToWait = Math.round(maxRoundsInBuffer * (sleepMillisPerRound) * 2);
+ final long consensusRoundNumber = 5L;
+ final ConsensusRound consensusRound = mockConsensusRound(keystoneEvent, events, consensusRoundNumber);
- // Wait for clear() to complete
- await().atMost(Duration.of(maxMillisToWait, ChronoUnit.MILLIS)).until(future::isDone);
+ consensusRoundHandler.handleConsensusRound(consensusRound);
- // Verify that no more than 2 buffers worth of events were handled
- assertTrue(
- numEventsHandled.get() < maxRoundsInBuffer * 2,
- "Consensus handler should not enter another doWork() cycle after prepareForReconnect() is called");
-
- assertEquals(0, consensusRoundHandler.getRoundsInQueue(), "Consensus queue should be empty");
+ for (final EventImpl event : events) {
+ verify(event).consensusReached();
+ }
+ verify(statusActionSubmitter, never()).submitStatusAction(any(FreezePeriodEnteredAction.class));
+ verify(waitForEventDurability).accept(keystoneEvent.getBaseEvent());
+ verify(swirldStateManager).handleConsensusRound(consensusRound);
+ assertEquals(consensusRoundNumber, roundAppliedToState.get());
+ verify(swirldStateManager, never()).savedStateInFreezePeriod();
+ verify(stateHashSignQueue).put(any(ReservedSignedState.class));
+ verify(platformState)
+ .setRunningEventHash(
+ events.getLast().getRunningHash().getFutureHash().getAndRethrow());
}
- /**
- * Tests that consensus events are passed to {@link EventStreamManager#addEvents(List)} exactly once.
- */
@Test
- void testConsensusEventStream() {
- final SwirldState swirldState = new DummySwirldState();
- initConsensusHandler(swirldState);
- testEventStream(eventStreamManager, consensusRoundHandler::consensusRound);
- }
-
- /**
- * Verifies that {@link EventStreamManager#addEvents(List)} is called the desired number of times.
- *
- * @param eventStreamManager the instance of {@link EventStreamManager} used by {@link ConsensusRoundHandler}
- * @param roundConsumer the round consumer to test
- */
- private void testEventStream(
- final EventStreamManager eventStreamManager, final Consumer roundConsumer) {
- final List events = createEvents(10, 10, true);
- final ConsensusRound round = mock(ConsensusRound.class);
- when(round.getConsensusEvents()).thenReturn(events);
- roundConsumer.accept(round);
- verify(eventStreamManager, times(1)).addEvents(events);
- }
-
- private void initConsensusHandler(final SwirldState swirldState) {
- final State state = new State();
- state.setSwirldState(swirldState);
-
- final AddressBook addressBook = new RandomAddressBookGenerator().build();
-
+ @DisplayName("Round in freeze period")
+ void freezeHandling() throws InterruptedException {
+ final PlatformContext platformContext =
+ TestPlatformContextBuilder.create().build();
final PlatformState platformState = mock(PlatformState.class);
- when(platformState.getClassId()).thenReturn(PlatformState.CLASS_ID);
- when(platformState.copy()).thenReturn(platformState);
- when(platformState.getAddressBook()).thenReturn(addressBook);
+ final SwirldStateManager swirldStateManager = mockSwirldStateManager(platformState);
+ when(swirldStateManager.isInFreezePeriod(any())).thenReturn(true);
- state.setPlatformState(platformState);
+ final BlockingQueue stateHashSignQueue = mock(BlockingQueue.class);
+ final CheckedConsumer waitForEventDurability = mock(CheckedConsumer.class);
+ final StatusActionSubmitter statusActionSubmitter = mock(StatusActionSubmitter.class);
- final Configuration configuration = new TestConfigBuilder()
- .withValue(EventConfig_.MAX_EVENT_QUEUE_FOR_CONS, 500)
- .getOrCreateConfig();
- final PlatformContext platformContext = TestPlatformContextBuilder.create()
- .withConfiguration(configuration)
- .build();
+ final AtomicLong roundAppliedToState = new AtomicLong(0);
+ final Consumer roundAppliedToStateConsumer = roundAppliedToState::set;
- final SwirldStateManager swirldStateManager = new SwirldStateManager(
+ final ConsensusRoundHandler consensusRoundHandler = new ConsensusRoundHandler(
platformContext,
- addressBook,
- selfId,
- consensusSystemTransactionManager,
- mock(SwirldStateMetrics.class),
- mock(StatusActionSubmitter.class),
- state,
- new BasicSoftwareVersion(1));
-
- consensusRoundHandler = new ConsensusRoundHandler(
- platformContext,
- getStaticThreadManager(),
- selfId,
swirldStateManager,
- consensusHandlingMetrics,
- eventStreamManager,
stateHashSignQueue,
- e -> {},
- mock(StatusActionSubmitter.class),
- (round) -> {},
- new BasicSoftwareVersion(1));
- consensusRoundHandler.start();
+ waitForEventDurability,
+ statusActionSubmitter,
+ roundAppliedToStateConsumer,
+ mock(SoftwareVersion.class));
+
+ final EventImpl keystoneEvent = mockEvent();
+ final List events = List.of(mockEvent(), mockEvent(), mockEvent());
+
+ final long consensusRoundNumber = 5L;
+ final ConsensusRound consensusRound = mockConsensusRound(keystoneEvent, events, consensusRoundNumber);
+
+ consensusRoundHandler.handleConsensusRound(consensusRound);
+
+ for (final EventImpl event : events) {
+ verify(event, times(1)).consensusReached();
+ }
+ verify(statusActionSubmitter).submitStatusAction(any(FreezePeriodEnteredAction.class));
+ verify(waitForEventDurability).accept(keystoneEvent.getBaseEvent());
+ verify(swirldStateManager).handleConsensusRound(consensusRound);
+ assertEquals(consensusRoundNumber, roundAppliedToState.get());
+ verify(swirldStateManager).savedStateInFreezePeriod();
+ verify(stateHashSignQueue).put(any(ReservedSignedState.class));
+ verify(platformState)
+ .setRunningEventHash(
+ events.getLast().getRunningHash().getFutureHash().getAndRethrow());
+
+ final ConsensusRound postFreezeConsensusRound = mockConsensusRound(keystoneEvent, events, consensusRoundNumber);
+ consensusRoundHandler.handleConsensusRound(postFreezeConsensusRound);
+
+ // these methods were called once from the first round, and shouldn't have been called again from the second
+ for (final EventImpl event : events) {
+ verify(event).consensusReached();
+ }
+ verify(statusActionSubmitter).submitStatusAction(any(FreezePeriodEnteredAction.class));
+ verify(waitForEventDurability).accept(keystoneEvent.getBaseEvent());
+ verify(swirldStateManager).handleConsensusRound(consensusRound);
+ verify(swirldStateManager).savedStateInFreezePeriod();
+ verify(stateHashSignQueue).put(any(ReservedSignedState.class));
+ verify(platformState)
+ .setRunningEventHash(
+ events.getLast().getRunningHash().getFutureHash().getAndRethrow());
}
}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/PlatformWiringTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/PlatformWiringTests.java
index cc1ac5e5f533..4e375c51a7ad 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/PlatformWiringTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/PlatformWiringTests.java
@@ -21,6 +21,7 @@
import com.swirlds.base.test.fixtures.time.FakeTime;
import com.swirlds.common.context.PlatformContext;
+import com.swirlds.common.stream.EventStreamManager;
import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder;
import com.swirlds.platform.StateSigner;
import com.swirlds.platform.components.LinkedEventIntake;
@@ -36,6 +37,7 @@
import com.swirlds.platform.event.preconsensus.PcesWriter;
import com.swirlds.platform.event.validation.EventSignatureValidator;
import com.swirlds.platform.event.validation.InternalEventValidator;
+import com.swirlds.platform.eventhandling.ConsensusRoundHandler;
import com.swirlds.platform.gossip.shadowgraph.Shadowgraph;
import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.iss.IssDetector;
@@ -74,6 +76,8 @@ void testBindings() {
mock(EventCreationManager.class),
mock(SwirldStateManager.class),
mock(StateSignatureCollector.class),
+ mock(ConsensusRoundHandler.class),
+ mock(EventStreamManager.class),
mock(FutureEventBuffer.class),
mock(IssDetector.class));
diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java
index 41759e2e87cd..8d73cf5765ca 100644
--- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java
+++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java
@@ -127,13 +127,7 @@ public TestIntake(@NonNull final AddressBook addressBook, @NonNull final Consens
new EventObserverDispatcher(new ShadowGraphEventObserver(shadowGraph), output);
final LinkedEventIntake linkedEventIntake = new LinkedEventIntake(
- platformContext,
- time,
- () -> consensus,
- dispatcher,
- shadowGraph,
- intakeEventCounter,
- mock(StandardOutputWire.class));
+ () -> consensus, dispatcher, shadowGraph, intakeEventCounter, mock(StandardOutputWire.class));
linkedEventIntakeWiring = LinkedEventIntakeWiring.create(schedulers.linkedEventIntakeScheduler());
linkedEventIntakeWiring.bind(linkedEventIntake);
From 6c654d94944c2a7e772289d94f50b362240b7c37 Mon Sep 17 00:00:00 2001
From: Kore Aguda <157432197+kfa-aguda@users.noreply.github.com>
Date: Mon, 5 Feb 2024 12:58:28 -0600
Subject: [PATCH 05/16] fix: broken unit test (#11233)
Signed-off-by: Kore Aguda
---
.../com/swirlds/common/threading/locks/IndexLockTests.java | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/threading/locks/IndexLockTests.java b/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/threading/locks/IndexLockTests.java
index bbcec996628e..f8b03d63ecc9 100644
--- a/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/threading/locks/IndexLockTests.java
+++ b/platform-sdk/swirlds-common/src/test/java/com/swirlds/common/threading/locks/IndexLockTests.java
@@ -24,6 +24,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import com.swirlds.common.test.fixtures.AssertionUtils;
import com.swirlds.common.test.fixtures.junit.tags.TestComponentTags;
import com.swirlds.common.threading.framework.config.ThreadConfiguration;
import com.swirlds.common.threading.locks.locked.Locked;
@@ -78,10 +79,8 @@ void sameIndexBlocks() throws InterruptedException {
lock.unlock(object);
- // Give the thread time to acquire the lock if it can
- MILLISECONDS.sleep(20);
-
- assertTrue(threadIsLocked.get(), "thread should have been able to acquire lock");
+ AssertionUtils.assertEventuallyTrue(
+ threadIsLocked::get, Duration.ofSeconds(1), "thread should have been able to acquire lock");
}
/**
From c9ce0e847050b72dbc1ca5e680419730df2b7f12 Mon Sep 17 00:00:00 2001
From: Matt Hess
Date: Mon, 5 Feb 2024 15:23:21 -0600
Subject: [PATCH 06/16] fix: Return invalid token even if expected decimals are
present (#11342)
Signed-off-by: Matt Hess
---
.../mono/store/tokens/HederaTokenStore.java | 1 +
.../service/mono/store/tokens/TokenStore.java | 22 +++---
.../store/tokens/HederaTokenStoreTest.java | 69 +++++--------------
.../suites/crypto/CryptoTransferSuite.java | 21 +++++-
4 files changed, 50 insertions(+), 63 deletions(-)
diff --git a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStore.java b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStore.java
index b824a5574069..efcc361463c4 100644
--- a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStore.java
+++ b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStore.java
@@ -371,6 +371,7 @@ public ResponseCodeEnum changeOwnerWildCard(final NftId nftId, final AccountID f
@Override
public boolean matchesTokenDecimals(final TokenID tId, final int expectedDecimals) {
+ // Note: this method assumes that the token for tId exists! Otherwise get(tId) will throw an exception
return get(tId).decimals() == expectedDecimals;
}
diff --git a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/TokenStore.java b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/TokenStore.java
index aac13b6fe6ef..b7952f65a6ac 100644
--- a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/TokenStore.java
+++ b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/tokens/TokenStore.java
@@ -83,22 +83,22 @@ default ResponseCodeEnum delete(TokenID id) {
}
default ResponseCodeEnum tryTokenChange(BalanceChange change) {
- var validity = OK;
var tokenId = resolve(change.tokenId());
if (tokenId == MISSING_TOKEN) {
- validity = INVALID_TOKEN_ID;
+ return INVALID_TOKEN_ID;
}
+
if (change.hasExpectedDecimals() && !matchesTokenDecimals(change.tokenId(), change.getExpectedDecimals())) {
- validity = UNEXPECTED_TOKEN_DECIMALS;
+ return UNEXPECTED_TOKEN_DECIMALS;
}
- if (validity == OK) {
- if (change.isForNft()) {
- validity = changeOwner(change.nftId(), change.accountId(), change.counterPartyAccountId());
- } else {
- validity = adjustBalance(change.accountId(), tokenId, change.getAggregatedUnits());
- if (validity == INSUFFICIENT_TOKEN_BALANCE) {
- validity = change.codeForInsufficientBalance();
- }
+
+ var validity = OK;
+ if (change.isForNft()) {
+ validity = changeOwner(change.nftId(), change.accountId(), change.counterPartyAccountId());
+ } else {
+ validity = adjustBalance(change.accountId(), tokenId, change.getAggregatedUnits());
+ if (validity == INSUFFICIENT_TOKEN_BALANCE) {
+ validity = change.codeForInsufficientBalance();
}
}
return validity;
diff --git a/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStoreTest.java b/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStoreTest.java
index e30714a29fa9..4fcf68adec59 100644
--- a/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStoreTest.java
+++ b/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/store/tokens/HederaTokenStoreTest.java
@@ -28,12 +28,8 @@
import static com.hedera.node.app.service.mono.ledger.properties.TokenRelProperty.IS_KYC_GRANTED;
import static com.hedera.node.app.service.mono.ledger.properties.TokenRelProperty.TOKEN_BALANCE;
import static com.hedera.node.app.service.mono.state.submerkle.EntityId.MISSING_ENTITY_ID;
-import static com.hedera.test.factories.scenarios.TxnHandlingScenario.COMPLEX_KEY_ACCOUNT_KT;
-import static com.hedera.test.factories.scenarios.TxnHandlingScenario.MISC_ACCOUNT_KT;
import static com.hedera.test.factories.scenarios.TxnHandlingScenario.TOKEN_ADMIN_KT;
import static com.hedera.test.factories.scenarios.TxnHandlingScenario.TOKEN_FEE_SCHEDULE_KT;
-import static com.hedera.test.factories.scenarios.TxnHandlingScenario.TOKEN_FREEZE_KT;
-import static com.hedera.test.factories.scenarios.TxnHandlingScenario.TOKEN_KYC_KT;
import static com.hedera.test.factories.scenarios.TxnHandlingScenario.TOKEN_PAUSE_KT;
import static com.hedera.test.factories.scenarios.TxnHandlingScenario.TOKEN_TREASURY_KT;
import static com.hedera.test.mocks.TestContextValidator.CONSENSUS_NOW;
@@ -105,8 +101,6 @@
import com.hedera.node.app.service.mono.state.validation.UsageLimits;
import com.hedera.node.app.service.mono.store.models.Id;
import com.hedera.node.app.service.mono.store.models.NftId;
-import com.hedera.node.app.service.mono.utils.EntityNumPair;
-import com.hedera.node.app.service.mono.utils.NftNumPair;
import com.hedera.test.factories.scenarios.TxnHandlingScenario;
import com.hedera.test.utils.IdUtils;
import com.hederahashgraph.api.proto.java.AccountAmount;
@@ -115,13 +109,10 @@
import com.hederahashgraph.api.proto.java.Key;
import com.hederahashgraph.api.proto.java.ResponseCodeEnum;
import com.hederahashgraph.api.proto.java.Timestamp;
-import com.hederahashgraph.api.proto.java.TokenCreateTransactionBody;
import com.hederahashgraph.api.proto.java.TokenID;
import com.hederahashgraph.api.proto.java.TokenUpdateTransactionBody;
import java.util.EnumSet;
-import java.util.HashSet;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Consumer;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Assertions;
@@ -132,18 +123,12 @@
class HederaTokenStoreTest {
private static final Key newKey = TxnHandlingScenario.TOKEN_REPLACE_KT.asKey();
private static final JKey newFcKey = TxnHandlingScenario.TOKEN_REPLACE_KT.asJKeyUnchecked();
- private static final Key adminKey = TOKEN_ADMIN_KT.asKey();
- private static final Key kycKey = TOKEN_KYC_KT.asKey();
- private static final Key freezeKey = TOKEN_FREEZE_KT.asKey();
- private static final Key wipeKey = MISC_ACCOUNT_KT.asKey();
- private static final Key supplyKey = COMPLEX_KEY_ACCOUNT_KT.asKey();
private static final Key feeScheduleKey = TOKEN_FEE_SCHEDULE_KT.asKey();
private static final Key pauseKey = TOKEN_PAUSE_KT.asKey();
private static final String symbol = "NOTHBAR";
private static final String newSymbol = "REALLYSOM";
private static final String newMemo = "NEWMEMO";
- private static final String memo = "TOKENMEMO";
private static final String name = "TOKENNAME";
private static final String newName = "NEWNAME";
private static final int maxCustomFees = 5;
@@ -151,15 +136,12 @@ class HederaTokenStoreTest {
private static final int numPositiveBalances = 1;
private static final long expiry = CONSENSUS_NOW + 1_234_567L;
private static final long newExpiry = CONSENSUS_NOW + 1_432_765L;
- private static final long totalSupply = 1_000_000L;
- private static final int decimals = 10;
private static final long treasuryBalance = 50_000L;
private static final long sponsorBalance = 1_000L;
private static final TokenID misc = IdUtils.asToken("0.0.1");
private static final TokenID nonfungible = IdUtils.asToken("0.0.2");
private static final int maxAutoAssociations = 1234;
private static final int alreadyUsedAutoAssocitaions = 123;
- private static final boolean freezeDefault = true;
private static final long newAutoRenewPeriod = 2_000_000L;
private static final AccountID payer = IdUtils.asAccount("0.0.12345");
private static final AccountID autoRenewAccount = IdUtils.asAccount("0.0.5");
@@ -682,12 +664,6 @@ void changingOwnerDoesTheExpected() {
final long startSponsorANfts = 4;
final long startCounterpartyANfts = 1;
final var receiver = EntityId.fromGrpcAccountId(counterparty);
- final var nftNumPair1 = NftNumPair.fromLongs(1111, 111);
- final var nftId1 = nftNumPair1.nftId();
- final var nftNumPair2 = NftNumPair.fromLongs(1112, 112);
- final var nftId2 = nftNumPair2.nftId();
- final var nftNumPair3 = NftNumPair.fromLongs(1113, 113);
- final var nftId3 = nftNumPair3.nftId();
given(accountsLedger.get(sponsor, NUM_NFTS_OWNED)).willReturn(startSponsorNfts);
given(accountsLedger.get(counterparty, NUM_NFTS_OWNED)).willReturn(startCounterpartyNfts);
given(tokenRelsLedger.get(sponsorNft, TOKEN_BALANCE)).willReturn(startSponsorANfts);
@@ -716,9 +692,7 @@ void changingOwnerDoesTheExpectedWithTreasuryReturn() {
final long startCounterpartyNfts = 8;
final long startTreasuryTNfts = 4;
final long startCounterpartyTNfts = 1;
- final var sender = EntityId.fromGrpcAccountId(counterparty);
final var receiver = EntityId.fromGrpcAccountId(primaryTreasury);
- final var muti = EntityNumPair.fromLongs(tNft.tokenId().getTokenNum(), tNft.serialNo());
given(backingTokens.getImmutableRef(tNft.tokenId()).treasury()).willReturn(receiver);
given(accountsLedger.get(primaryTreasury, NUM_NFTS_OWNED)).willReturn(startTreasuryNfts);
given(accountsLedger.get(counterparty, NUM_NFTS_OWNED)).willReturn(startCounterpartyNfts);
@@ -751,8 +725,6 @@ void changingOwnerDoesTheExpectedWithTreasuryExit() {
final long startCounterpartyTNfts = 1;
final var sender = EntityId.fromGrpcAccountId(primaryTreasury);
final var receiver = EntityId.fromGrpcAccountId(counterparty);
- final var nftNumPair3 = NftNumPair.fromLongs(1113, 113);
- final var nftId3 = nftNumPair3.nftId();
given(accountsLedger.get(primaryTreasury, NUM_NFTS_OWNED)).willReturn(startTreasuryNfts);
given(accountsLedger.get(counterparty, NUM_NFTS_OWNED)).willReturn(startCounterpartyNfts);
given(tokenRelsLedger.get(treasuryNft, TOKEN_BALANCE)).willReturn(startTreasuryTNfts);
@@ -986,8 +958,6 @@ void updateRejectsInappropriateSupplyKey() {
@Test
void updateRejectsZeroTokenBalanceKey() {
- final Set tokenSet = new HashSet<>();
- tokenSet.add(nonfungible);
givenUpdateTarget(ALL_KEYS, nonfungibleToken);
final var op = updateWith(ALL_KEYS, nonfungible, true, true, true).toBuilder()
.setExpiry(Timestamp.newBuilder().setSeconds(0))
@@ -1000,8 +970,6 @@ void updateRejectsZeroTokenBalanceKey() {
@Test
void updateHappyPathIgnoresZeroExpiry() {
- final Set tokenSet = new HashSet<>();
- tokenSet.add(misc);
givenUpdateTarget(ALL_KEYS, token);
final var op = updateWith(ALL_KEYS, misc, true, true, true).toBuilder()
.setExpiry(Timestamp.newBuilder().setSeconds(0))
@@ -1447,6 +1415,23 @@ void failsIfMismatchingDecimals() {
Assertions.assertEquals(UNEXPECTED_TOKEN_DECIMALS, result);
}
+ @Test
+ void invalidTokenTakesPriorityOverDecimalCheck() {
+ final var aa =
+ AccountAmount.newBuilder().setAccountID(sponsor).setAmount(100).build();
+ final var fungibleChange = BalanceChange.changingFtUnits(Id.fromGrpcToken(misc), misc, aa, payer);
+ assertFalse(fungibleChange.hasExpectedDecimals());
+
+ // Here we set the expected decimals in the change, but also simulate the token not existing
+ fungibleChange.setExpectedDecimals(4);
+ given(backingTokens.contains(misc)).willReturn(false);
+
+ final var result = subject.tryTokenChange(fungibleChange);
+ // Even though the expected decimals don't match, the invalid token status takes priority (i.e. is returned
+ // instead of a decimal error status)
+ Assertions.assertEquals(INVALID_TOKEN_ID, result);
+ }
+
@Test
void decimalMatchingWorks() {
assertEquals(2, subject.get(misc).decimals());
@@ -1509,27 +1494,9 @@ void updateExpiryInfoRejectsMissingToken() {
assertEquals(INVALID_TOKEN_ID, outcome);
}
- TokenCreateTransactionBody.Builder fullyValidTokenCreateAttempt() {
- return TokenCreateTransactionBody.newBuilder()
- .setExpiry(Timestamp.newBuilder().setSeconds(expiry))
- .setMemo(memo)
- .setAdminKey(adminKey)
- .setKycKey(kycKey)
- .setFreezeKey(freezeKey)
- .setWipeKey(wipeKey)
- .setSupplyKey(supplyKey)
- .setFeeScheduleKey(feeScheduleKey)
- .setSymbol(symbol)
- .setName(name)
- .setInitialSupply(totalSupply)
- .setTreasury(treasury)
- .setDecimals(decimals)
- .setFreezeDefault(freezeDefault);
- }
-
private void assertSoleTokenChangesAreForNftTransfer(final NftId nft, final AccountID from, final AccountID to) {
final var tokenChanges = sideEffectsTracker.getNetTrackedTokenUnitAndOwnershipChanges();
- final var ownershipChange = tokenChanges.get(0);
+ final var ownershipChange = tokenChanges.getFirst();
assertEquals(nft.tokenId(), ownershipChange.getToken());
final var nftTransfer = ownershipChange.getNftTransfers(0);
assertEquals(nft.serialNo(), nftTransfer.getSerialNumber());
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoTransferSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoTransferSuite.java
index f3e1686ac4c0..cc6a7380c921 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoTransferSuite.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoTransferSuite.java
@@ -112,6 +112,7 @@
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_CUSTOM_FEE_COLLECTOR;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_SIGNATURE;
+import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.NO_REMAINING_AUTOMATIC_ASSOCIATIONS;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SPENDER_DOES_NOT_HAVE_ALLOWANCE;
@@ -241,7 +242,8 @@ public List getSpecsInSuite() {
hapiTransferFromForFungibleTokenWithCustomFeesWithAllowance(),
okToRepeatSerialNumbersInWipeList(),
okToRepeatSerialNumbersInBurnList(),
- canUseAliasAndAccountCombinations());
+ canUseAliasAndAccountCombinations(),
+ transferInvalidTokenIdWithDecimals());
}
@Override
@@ -2114,6 +2116,23 @@ final HapiSpec hapiTransferFromForFungibleTokenWithCustomFeesWithAllowance() {
.then();
}
+ @HapiTest
+ final HapiSpec transferInvalidTokenIdWithDecimals() {
+ return defaultHapiSpec("transferInvalidTokenIdWithDecimals", FULLY_NONDETERMINISTIC)
+ .given(cryptoCreate(TREASURY), withOpContext((spec, opLog) -> {
+ final var acctCreate = cryptoCreate(PAYER).balance(ONE_HUNDRED_HBARS);
+ allRunFor(spec, acctCreate);
+ // Here we take an account ID and store it as a token ID in the registry, so that when the "token
+ // number" is submitted by the test client, it will recreate the bug scenario:
+ final var bogusTokenId = TokenID.newBuilder().setTokenNum(acctCreate.numOfCreatedAccount());
+ spec.registry().saveTokenId("nonexistent", bogusTokenId.build());
+ }))
+ .when()
+ .then(sourcing(() -> cryptoTransfer(
+ movingWithDecimals(1L, "nonexistent", 2).betweenWithDecimals(PAYER, TREASURY))
+ .hasKnownStatus(INVALID_TOKEN_ID)));
+ }
+
@Override
protected Logger getResultsLogger() {
return LOG;
From 1c6c69fe1281c86a133ab48bd1fec1050c825946 Mon Sep 17 00:00:00 2001
From: artemananiev <33361937+artemananiev@users.noreply.github.com>
Date: Mon, 5 Feb 2024 16:09:23 -0800
Subject: [PATCH 07/16] fix: 11298: VirtualMapReconnectTest fails
intermittently with path not in range log message (#11370)
Reviewed-by: Anthony Petrov , Ivan Malygin
Signed-off-by: Artem Ananev
---
.../virtual/merkle/reconnect/VirtualMapReconnectTestBase.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/platform-sdk/swirlds-merkle/src/test/java/com/swirlds/virtual/merkle/reconnect/VirtualMapReconnectTestBase.java b/platform-sdk/swirlds-merkle/src/test/java/com/swirlds/virtual/merkle/reconnect/VirtualMapReconnectTestBase.java
index 245325e8c584..4f50c12e8254 100644
--- a/platform-sdk/swirlds-merkle/src/test/java/com/swirlds/virtual/merkle/reconnect/VirtualMapReconnectTestBase.java
+++ b/platform-sdk/swirlds-merkle/src/test/java/com/swirlds/virtual/merkle/reconnect/VirtualMapReconnectTestBase.java
@@ -225,6 +225,9 @@ protected void reconnectMultipleTimes(
final VirtualRoot root = learnerMap.getRight();
assertTrue(root.isHashed(), "Learner root node must be hashed");
} catch (Exception e) {
+ if (!failureExpected) {
+ e.printStackTrace(System.err);
+ }
assertTrue(failureExpected, "We did not expect an exception on this reconnect attempt! " + e);
}
From 3cf9f8c5a57afa372c6cf22aac68f8f5d1bb0c05 Mon Sep 17 00:00:00 2001
From: JivkoKelchev
Date: Tue, 6 Feb 2024 12:44:49 +0700
Subject: [PATCH 08/16] fix: 10315 halt on wrong token type (ERCPrecompileSuite
fuzzy match) (#11164)
Signed-off-by: Zhivko Kelchev
Signed-off-by: Valentin Tronkov <99957253+vtronkov@users.noreply.github.com>
Signed-off-by: Michael Tinker
Co-authored-by: Valentin Tronkov <99957253+vtronkov@users.noreply.github.com>
Co-authored-by: Michael Tinker
---
.../systemcontracts/HtsSystemContract.java | 2 +-
.../hts/AbstractNftViewCall.java | 20 +++++++++++++
.../hts/decimals/DecimalsCall.java | 30 +++++++++++++------
.../hts/tokenuri/TokenUriCall.java | 9 ------
.../hts/decimals/DecimalsCallTest.java | 11 +++----
.../hts/ownerof/OwnerOfCallTest.java | 17 +++++++++++
.../hts/tokenuri/TokenUriCallTest.java | 30 +++++++++++++++----
.../services/bdd/spec/HapiSpecSetup.java | 10 +++++++
.../bdd/spec/utilops/domain/ParsedItem.java | 5 ++++
.../utilops/records/AutoSnapshotModeOp.java | 2 +-
.../spec/utilops/records/SnapshotModeOp.java | 24 +++++++--------
.../precompile/ERCPrecompileSuite.java | 2 +-
.../src/main/resource/spec-default.properties | 1 +
13 files changed, 119 insertions(+), 44 deletions(-)
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java
index 06695f60b626..1cfac03152a8 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java
@@ -105,7 +105,7 @@ private static FullResult resultOfExecuting(
if (pricedResult.isViewCall()) {
final var proxyWorldUpdater = FrameUtils.proxyUpdaterFor(frame);
final var enhancement = proxyWorldUpdater.enhancement();
- final var responseCode = pricedResult.responseCode() != null ? pricedResult.responseCode() : null;
+ final var responseCode = pricedResult.responseCode();
if (responseCode == SUCCESS) {
enhancement
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbstractNftViewCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbstractNftViewCall.java
index 3bce2839701c..772c87b9c6c0 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbstractNftViewCall.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbstractNftViewCall.java
@@ -17,14 +17,19 @@
package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NFT_ID;
+import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
+import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.haltResult;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.revertResult;
+import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCall.PricedResult.gasOnly;
import static java.util.Objects.requireNonNull;
+import com.hedera.hapi.node.base.TokenType;
import com.hedera.hapi.node.state.token.Nft;
import com.hedera.hapi.node.state.token.Token;
import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult;
import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater;
+import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
@@ -43,6 +48,21 @@ protected AbstractNftViewCall(
this.serialNo = serialNo;
}
+ @Override
+ public @NonNull PricedResult execute() {
+ if (token != null && token.tokenType() == TokenType.FUNGIBLE_COMMON) {
+ // (FUTURE) consider removing this pattern, but for now match
+ // mono-service by halting on invalid token type
+ return gasOnly(
+ haltResult(
+ HederaExceptionalHaltReason.ERROR_DECODING_PRECOMPILE_INPUT,
+ gasCalculator.viewGasRequirement()),
+ INVALID_TOKEN_ID,
+ false);
+ }
+ return super.execute();
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/decimals/DecimalsCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/decimals/DecimalsCall.java
index 73ace233069a..0c91baeabe9d 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/decimals/DecimalsCall.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/decimals/DecimalsCall.java
@@ -17,8 +17,9 @@
package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.decimals;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
-import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.revertResult;
+import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.haltResult;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.successResult;
+import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCall.PricedResult.gasOnly;
import com.hedera.hapi.node.base.TokenType;
import com.hedera.hapi.node.state.token.Token;
@@ -26,6 +27,7 @@
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AbstractRevertibleTokenViewCall;
import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater;
+import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
@@ -42,18 +44,28 @@ public DecimalsCall(
super(gasCalculator, enhancement, token);
}
+ @Override
+ public @NonNull PricedResult execute() {
+ if (token != null && token.tokenType() != TokenType.FUNGIBLE_COMMON) {
+ // (FUTURE) consider removing this pattern, but for now match
+ // mono-service by halting on invalid token type
+ return gasOnly(
+ haltResult(
+ HederaExceptionalHaltReason.ERROR_DECODING_PRECOMPILE_INPUT,
+ gasCalculator.viewGasRequirement()),
+ INVALID_TOKEN_ID,
+ false);
+ }
+ return super.execute();
+ }
+
/**
* {@inheritDoc}
*/
@Override
protected @NonNull FullResult resultOfViewingToken(@NonNull final Token token) {
- if (token.tokenType() != TokenType.FUNGIBLE_COMMON) {
- return revertResult(INVALID_TOKEN_ID, gasCalculator.viewGasRequirement());
- } else {
- final var decimals = Math.min(MAX_REPORTABLE_DECIMALS, token.decimals());
- return successResult(
- DecimalsTranslator.DECIMALS.getOutputs().encodeElements(decimals),
- gasCalculator.viewGasRequirement());
- }
+ final var decimals = Math.min(MAX_REPORTABLE_DECIMALS, token.decimals());
+ return successResult(
+ DecimalsTranslator.DECIMALS.getOutputs().encodeElements(decimals), gasCalculator.viewGasRequirement());
}
}
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenuri/TokenUriCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenuri/TokenUriCall.java
index 0c3df42ec93b..ff8423cf2232 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenuri/TokenUriCall.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenuri/TokenUriCall.java
@@ -16,12 +16,9 @@
package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.tokenuri;
-import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.revertResult;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.successResult;
import static java.util.Objects.requireNonNull;
-import com.hedera.hapi.node.base.ResponseCodeEnum;
-import com.hedera.hapi.node.base.TokenType;
import com.hedera.hapi.node.state.token.Nft;
import com.hedera.hapi.node.state.token.Token;
import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator;
@@ -51,12 +48,6 @@ public TokenUriCall(
*/
@Override
protected @NonNull FullResult resultOfViewingNft(@NonNull final Token token, final Nft nft) {
- requireNonNull(token);
- // #10568 - We add this check to match mono behavior
- if (token.tokenType() == TokenType.FUNGIBLE_COMMON) {
- return revertResult(ResponseCodeEnum.INVALID_TOKEN_ID, gasCalculator.viewGasRequirement());
- }
-
String metadata;
if (nft != null) {
metadata = new String(nft.metadata().toByteArray());
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/decimals/DecimalsCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/decimals/DecimalsCallTest.java
index f39997d31e81..6e307f9bf5a8 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/decimals/DecimalsCallTest.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/decimals/DecimalsCallTest.java
@@ -16,16 +16,15 @@
package com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.decimals;
-import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_TOKEN;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_FUNGIBLE_TOKEN;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.UNREASONABLY_DIVISIBLE_TOKEN;
-import static com.hedera.node.app.service.contract.impl.test.TestHelpers.revertOutputFor;
import static org.junit.jupiter.api.Assertions.*;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.decimals.DecimalsCall;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.decimals.DecimalsTranslator;
import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.HtsCallTestBase;
+import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.junit.jupiter.api.Test;
@@ -35,13 +34,15 @@ class DecimalsCallTest extends HtsCallTestBase {
private DecimalsCall subject;
@Test
- void revertsWithNonfungibleToken() {
+ void haltWithNonfungibleToken() {
subject = new DecimalsCall(mockEnhancement(), gasCalculator, NON_FUNGIBLE_TOKEN);
final var result = subject.execute().fullResult().result();
- assertEquals(MessageFrame.State.REVERT, result.getState());
- assertEquals(revertOutputFor(INVALID_TOKEN_ID), result.getOutput());
+ assertEquals(MessageFrame.State.EXCEPTIONAL_HALT, result.getState());
+ assertEquals(
+ HederaExceptionalHaltReason.ERROR_DECODING_PRECOMPILE_INPUT,
+ result.getHaltReason().get());
}
@Test
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/ownerof/OwnerOfCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/ownerof/OwnerOfCallTest.java
index e89d44a5abcd..8ce5df95dcd6 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/ownerof/OwnerOfCallTest.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/ownerof/OwnerOfCallTest.java
@@ -21,6 +21,7 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.ALIASED_SOMEBODY;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CIVILIAN_OWNED_NFT;
+import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_TOKEN;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NFT_SERIAL_NO;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_FUNGIBLE_TOKEN;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_FUNGIBLE_TOKEN_ID;
@@ -35,6 +36,7 @@
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ownerof.OwnerOfTranslator;
import com.hedera.node.app.service.contract.impl.test.TestHelpers;
import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.HtsCallTestBase;
+import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.junit.jupiter.api.Test;
@@ -63,6 +65,21 @@ void revertsWithMissingNft() {
assertEquals(revertOutputFor(INVALID_NFT_ID), result.getOutput());
}
+ @Test
+ void haltWhenTokenIsNotERC721() {
+ // given
+ subject = new OwnerOfCall(gasCalculator, mockEnhancement(), FUNGIBLE_TOKEN, NFT_SERIAL_NO);
+
+ // when
+ final var result = subject.execute().fullResult().result();
+
+ // then
+ assertEquals(MessageFrame.State.EXCEPTIONAL_HALT, result.getState());
+ assertEquals(
+ HederaExceptionalHaltReason.ERROR_DECODING_PRECOMPILE_INPUT,
+ result.getHaltReason().get());
+ }
+
@Test
void revertsWithMissingOwner() {
subject = new OwnerOfCall(gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, NFT_SERIAL_NO);
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenuri/TokenUriCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenuri/TokenUriCallTest.java
index 5de3a834c130..d641a87bf4af 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenuri/TokenUriCallTest.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenuri/TokenUriCallTest.java
@@ -24,10 +24,10 @@
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.given;
-import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.tokenuri.TokenUriCall;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.tokenuri.TokenUriTranslator;
import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.HtsCallTestBase;
+import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.junit.jupiter.api.Test;
@@ -54,17 +54,35 @@ void returnsUnaliasedOwnerLongZeroForPresentTokenAndNonTreasuryNft() {
}
@Test
- void revertsWhenTokenIsNotERC721() {
+ void returnNonExistingTokenErrorMetadata() {
// given
- subject = new TokenUriCall(gasCalculator, mockEnhancement(), FUNGIBLE_TOKEN, NFT_SERIAL_NO);
- given(nativeOperations.getNft(FUNGIBLE_TOKEN.tokenId().tokenNum(), NFT_SERIAL_NO))
+ subject = new TokenUriCall(gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, NFT_SERIAL_NO);
+ given(nativeOperations.getNft(NON_FUNGIBLE_TOKEN.tokenId().tokenNum(), NFT_SERIAL_NO))
.willReturn(null);
+ // when
+ final var result = subject.execute().fullResult().result();
+ // then
+ assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState());
+ assertEquals(
+ Bytes.wrap(TokenUriTranslator.TOKEN_URI
+ .getOutputs()
+ .encodeElements(TokenUriCall.URI_QUERY_NON_EXISTING_TOKEN_ERROR)
+ .array()),
+ result.getOutput());
+ }
+
+ @Test
+ void haltWhenTokenIsNotERC721() {
+ // given
+ subject = new TokenUriCall(gasCalculator, mockEnhancement(), FUNGIBLE_TOKEN, NFT_SERIAL_NO);
// when
final var result = subject.execute().fullResult().result();
// then
- assertEquals(MessageFrame.State.REVERT, result.getState());
- assertEquals(Bytes.wrap(ResponseCodeEnum.INVALID_TOKEN_ID.name().getBytes()), result.getOutput());
+ assertEquals(MessageFrame.State.EXCEPTIONAL_HALT, result.getState());
+ assertEquals(
+ HederaExceptionalHaltReason.ERROR_DECODING_PRECOMPILE_INPUT,
+ result.getHaltReason().get());
}
}
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java
index 0e9c5e6bc2e7..25b823dfbed0 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java
@@ -318,6 +318,16 @@ public boolean autoSnapshotManagement() {
return props.getBoolean("recordStream.autoSnapshotManagement");
}
+ /**
+ * Returns whether a {@link HapiSpec} doing automatic snapshot management should
+ * override an existing snapshot.
+ *
+ * @return whether an auto-snapshot managing {@link HapiSpec} should override an existing snapshot
+ */
+ public boolean overrideExistingSnapshot() {
+ return props.getBoolean("recordStream.overrideExistingSnapshot");
+ }
+
/**
* Returns the record stream source for the {@link HapiSpec} to use when automatically taking snapshots
* with {@code recordStream.autoSnapshotManagement=true}.
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java
index d21eed81d5aa..8d3a3b5a3087 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java
@@ -21,6 +21,7 @@
import com.google.protobuf.InvalidProtocolBufferException;
import com.hedera.services.stream.proto.RecordStreamItem;
import com.hederahashgraph.api.proto.java.FileID;
+import com.hederahashgraph.api.proto.java.ResponseCodeEnum;
import com.hederahashgraph.api.proto.java.SignedTransaction;
import com.hederahashgraph.api.proto.java.TransactionBody;
import com.hederahashgraph.api.proto.java.TransactionRecord;
@@ -36,6 +37,10 @@ public record ParsedItem(TransactionBody itemBody, TransactionRecord itemRecord)
private static final FileID PROPERTIES_FILE_ID =
FileID.newBuilder().setFileNum(121).build();
+ public ResponseCodeEnum status() {
+ return itemRecord.getReceipt().getStatus();
+ }
+
public static ParsedItem parse(final RecordStreamItem item) throws InvalidProtocolBufferException {
final var txn = item.getTransaction();
final TransactionBody body;
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/AutoSnapshotModeOp.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/AutoSnapshotModeOp.java
index 24c799683c0c..8c19fd965ed2 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/AutoSnapshotModeOp.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/AutoSnapshotModeOp.java
@@ -64,7 +64,7 @@ public AutoSnapshotModeOp(
@Override
protected boolean submitOp(@NonNull final HapiSpec spec) throws Throwable {
final var maybeSnapshot = SnapshotModeOp.maybeLoadSnapshotFor(spec);
- if (maybeSnapshot.isPresent()) {
+ if (maybeSnapshot.isPresent() && !spec.setup().overrideExistingSnapshot()) {
final var snapshotMode = (autoMatchSource == MONO_SERVICE)
? SnapshotMode.FUZZY_MATCH_AGAINST_MONO_STREAMS
: SnapshotMode.FUZZY_MATCH_AGAINST_HAPI_TEST_STREAMS;
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java
index af805f055aa5..1bebb81d03f1 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java
@@ -53,6 +53,7 @@
import com.hederahashgraph.api.proto.java.AccountID;
import com.hederahashgraph.api.proto.java.ContractID;
import com.hederahashgraph.api.proto.java.FileID;
+import com.hederahashgraph.api.proto.java.ResponseCodeEnum;
import com.hederahashgraph.api.proto.java.ScheduleID;
import com.hederahashgraph.api.proto.java.TokenID;
import com.hederahashgraph.api.proto.java.TopicID;
@@ -184,8 +185,7 @@ public class SnapshotModeOp extends UtilOp implements SnapshotOp {
public static void main(String... args) throws IOException {
// Helper to review the snapshot saved for a particular HapiSuite-HapiSpec combination
- final var snapshotFileMeta =
- new SnapshotFileMeta("HelloWorldEthereum", "createWithSelfDestructInConstructorHasSaneRecord");
+ final var snapshotFileMeta = new SnapshotFileMeta("ERCPrecompile", "getErc721TokenURIFromErc20TokenFails");
final var maybeSnapshot = suiteSnapshotsFrom(
resourceLocOf(PROJECT_ROOT_SNAPSHOT_RESOURCES_LOC, snapshotFileMeta.suiteName()))
.flatMap(
@@ -256,8 +256,9 @@ static Optional maybeLoadSnapshotFor(@NonNull final HapiSpec spe
@Override
public boolean hasWorkToDo() {
- // We leave the spec name null in submitOp() if we are running against a target network that
- // doesn't match the SnapshotMode of this operation; or if the HapiSpec is non-deterministic
+ // We leave the snapshot file metadata null in submitOp() if we are running against a
+ // target network that doesn't match the SnapshotMode of this operation; or if the
+ // HapiSpec is non-deterministic
return snapshotFileMeta != null;
}
@@ -290,6 +291,12 @@ public void finishLifecycle(@NonNull final HapiSpec spec) {
.toList();
// We only want to snapshot or fuzzy-match the records that come after the placeholder creation
boolean placeholderFound = false;
+ // For statuses that only mono-service rejects at ingest, we need to skip fuzzy-matching;
+ // unless there is some special case in the spec where mono-service will still use them
+ // (primarily because they appear in a contract operation's child records)
+ final Set statusesToIgnore = !matchModes.contains(EXPECT_STREAMLINED_INGEST_RECORDS)
+ ? spec.setup().streamlinedIngestChecks()
+ : EnumSet.noneOf(ResponseCodeEnum.class);
for (final var item : allItems) {
final var parsedItem = ParsedItem.parse(item);
if (parsedItem.isPropertyOverride()) {
@@ -301,14 +308,7 @@ public void finishLifecycle(@NonNull final HapiSpec spec) {
// We cannot ever expect to match node stake update export sequencing
continue;
}
- if (spec.setup()
- .streamlinedIngestChecks()
- .contains(parsedItem.itemRecord().getReceipt().getStatus())
- && !matchModes.contains(EXPECT_STREAMLINED_INGEST_RECORDS)) {
- // There are no records written in mono-service when a transaction fails in ingest.
- // But in modular service we write them. While validating fuzzy records, we always skip the records
- // with status in spec.streamlinedIngestChecks. But for some error codes like INVALID_ACCOUNT_ID,
- // which are thrown in both ingest and handle, we need to validate the records.
+ if (statusesToIgnore.contains(parsedItem.status())) {
continue;
}
if (!placeholderFound) {
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ERCPrecompileSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ERCPrecompileSuite.java
index 38880b858de1..0113cf12c45f 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ERCPrecompileSuite.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ERCPrecompileSuite.java
@@ -913,7 +913,7 @@ final HapiSpec getErc20TokenDecimalsFromErc721TokenFails() {
@HapiTest
final HapiSpec getErc721TokenName() {
return defaultHapiSpec(
- "getErc721TokenName", NONDETERMINISTIC_TRANSACTION_FEES, NONDETERMINISTIC_FUNCTION_PARAMETERS)
+ "getErc721TokenName", HIGHLY_NON_DETERMINISTIC_FEES, NONDETERMINISTIC_FUNCTION_PARAMETERS)
.given(
newKeyNamed(MULTI_KEY),
cryptoCreate(ACCOUNT).balance(100 * ONE_HUNDRED_HBARS),
diff --git a/hedera-node/test-clients/src/main/resource/spec-default.properties b/hedera-node/test-clients/src/main/resource/spec-default.properties
index 19469637b562..9b6f6d475841 100644
--- a/hedera-node/test-clients/src/main/resource/spec-default.properties
+++ b/hedera-node/test-clients/src/main/resource/spec-default.properties
@@ -38,6 +38,7 @@ default.nodePayment.tinyBars=5000
default.payer=0.0.2
recordStream.path=hedera-node/hedera-app/build/node/data/recordStreams/record0.0.3
recordStream.autoSnapshotManagement=false
+recordStream.overrideExistingSnapshot=false
#recordStream.autoSnapshotTarget=MONO_SERVICE
recordStream.autoMatchTarget=HAPI_TEST
recordStream.autoSnapshotTarget=MONO_SERVICE
From 432434033c78325a63dc156c6d54fd849f8d9eaf Mon Sep 17 00:00:00 2001
From: Austin Littley <102969658+alittley@users.noreply.github.com>
Date: Tue, 6 Feb 2024 08:48:03 -0500
Subject: [PATCH 09/16] fix: Modify where components look to indicate
overloaded intake (#11369)
Signed-off-by: Austin Littley
---
.../com/swirlds/platform/SwirldsPlatform.java | 4 ++--
.../swirlds/platform/wiring/PlatformWiring.java | 12 ++++++++----
.../wiring/components/EventHasherWiring.java | 15 ++++-----------
.../components/PostHashCollectorWiring.java | 13 +++++++++----
4 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
index 14e0e8aec643..d9f06cf30d37 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
@@ -625,7 +625,7 @@ public class SwirldsPlatform implements Platform {
selfId,
appVersion,
transactionPool,
- platformWiring.getHasherUnprocessedTaskCountSupplier(),
+ platformWiring.getIntakeQueueSizeSupplier(),
platformStatusManager::getCurrentStatus,
latestReconnectRound::get);
@@ -702,7 +702,7 @@ public class SwirldsPlatform implements Platform {
emergencyRecoveryManager,
consensusRef,
platformWiring.getGossipEventInput()::put,
- platformWiring.getHasherUnprocessedTaskCountSupplier(),
+ platformWiring.getIntakeQueueSizeSupplier(),
swirldStateManager,
latestCompleteState,
syncMetrics,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java
index a13f84539d39..108ef1aba29f 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformWiring.java
@@ -502,13 +502,17 @@ public InputWire getPcesWriterRegisterDiscontinuityInput() {
}
/**
- * Get a supplier for the number of unprocessed tasks in the hasher.
+ * Get a supplier for the number of unprocessed tasks at the front of the intake pipeline. This is for the purpose
+ * of applying backpressure to the event creator and gossip when the intake pipeline is overloaded.
+ *
+ * Technically, the first component of the intake pipeline is the hasher, but tasks to be passed along actually
+ * accumulate in the post hash collector. This is due to how the concurrent hasher handles backpressure.
*
- * @return a supplier for the number of unprocessed tasks in the hasher
+ * @return a supplier for the number of unprocessed tasks in the PostHashCollector
*/
@NonNull
- public LongSupplier getHasherUnprocessedTaskCountSupplier() {
- return eventHasherWiring.unprocessedTaskCountSupplier();
+ public LongSupplier getIntakeQueueSizeSupplier() {
+ return postHashCollectorWiring.unprocessedTaskCountSupplier();
}
/**
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventHasherWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventHasherWiring.java
index db35a23c08a6..d4685b8bc93c 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventHasherWiring.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/EventHasherWiring.java
@@ -23,19 +23,15 @@
import com.swirlds.platform.event.GossipEvent;
import com.swirlds.platform.event.hashing.EventHasher;
import edu.umd.cs.findbugs.annotations.NonNull;
-import java.util.function.LongSupplier;
/**
* Wiring for the {@link EventHasher}.
*
- * @param eventInput the input wire for events to be hashed
- * @param eventOutput the output wire for hashed events
- * @param unprocessedTaskCountSupplier the supplier for the number of unprocessed tasks
+ * @param eventInput the input wire for events to be hashed
+ * @param eventOutput the output wire for hashed events
*/
public record EventHasherWiring(
- @NonNull InputWire eventInput,
- @NonNull OutputWire eventOutput,
- @NonNull LongSupplier unprocessedTaskCountSupplier) {
+ @NonNull InputWire eventInput, @NonNull OutputWire eventOutput) {
/**
* Create a new instance of this wiring.
*
@@ -43,10 +39,7 @@ public record EventHasherWiring(
* @return the new wiring instance
*/
public static EventHasherWiring create(@NonNull final TaskScheduler taskScheduler) {
- return new EventHasherWiring(
- taskScheduler.buildInputWire("events to hash"),
- taskScheduler.getOutputWire(),
- taskScheduler::getUnprocessedTaskCount);
+ return new EventHasherWiring(taskScheduler.buildInputWire("events to hash"), taskScheduler.getOutputWire());
}
/**
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/PostHashCollectorWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/PostHashCollectorWiring.java
index f5c869b300d3..ad1a7499995b 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/PostHashCollectorWiring.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/components/PostHashCollectorWiring.java
@@ -22,6 +22,7 @@
import com.swirlds.common.wiring.wires.output.OutputWire;
import com.swirlds.platform.event.GossipEvent;
import edu.umd.cs.findbugs.annotations.NonNull;
+import java.util.function.LongSupplier;
/**
* Wiring object that allows for the staging of events that have been hashed, but haven't been passed further down the
@@ -41,11 +42,14 @@
* The concurrent scheduler will refuse to accept additional work based on the number of tasks that are waiting in the
* sequential scheduler's queue.
*
- * @param eventInput the input wire for events that have been hashed
- * @param eventOutput the output wire for events to be passed further along the pipeline
+ * @param eventInput the input wire for events that have been hashed
+ * @param eventOutput the output wire for events to be passed further along the pipeline
+ * @param unprocessedTaskCountSupplier the supplier for the number of unprocessed tasks
*/
public record PostHashCollectorWiring(
- @NonNull InputWire eventInput, @NonNull OutputWire eventOutput) {
+ @NonNull InputWire eventInput,
+ @NonNull OutputWire eventOutput,
+ @NonNull LongSupplier unprocessedTaskCountSupplier) {
/**
* Create a new instance of this wiring.
@@ -60,6 +64,7 @@ public static PostHashCollectorWiring create(@NonNull final TaskScheduler hashedEvent);
- return new PostHashCollectorWiring(inputWire, taskScheduler.getOutputWire());
+ return new PostHashCollectorWiring(
+ inputWire, taskScheduler.getOutputWire(), taskScheduler::getUnprocessedTaskCount);
}
}
From 240d7b8abda7d5cecdbc5155fb789a4ae18ed56d Mon Sep 17 00:00:00 2001
From: Georgi Lazarov
Date: Tue, 6 Feb 2024 17:40:03 +0200
Subject: [PATCH 10/16] feat: enable fuzzy record matching for
`TokenUpdatePrecompileSuite` (#11008)
Signed-off-by: georgi-l95
---
.../scope/HandleSystemContractOperations.java | 5 ++++
.../HandleSystemContractOperationsTest.java | 1 +
.../TokenUpdatePrecompile.json | 1 +
.../TokenUpdatePrecompileSuite.java | 23 +++++++++++++++----
4 files changed, 26 insertions(+), 4 deletions(-)
create mode 100644 hedera-node/test-clients/record-snapshots/TokenUpdatePrecompile.json
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleSystemContractOperations.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleSystemContractOperations.java
index 5ad9856338a8..f6dc16a012ac 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleSystemContractOperations.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleSystemContractOperations.java
@@ -126,8 +126,13 @@ public Transaction syntheticTransactionForHtsCall(Bytes input, ContractID contra
if (isViewCall) {
contractCallBodyBuilder.gas(1L);
}
+ final var parentTxBody = context.body();
var transactionBody = TransactionBody.newBuilder()
+ .nodeAccountID(parentTxBody.nodeAccountID())
.transactionID(TransactionID.DEFAULT)
+ .transactionFee(parentTxBody.transactionFee())
+ .transactionValidDuration(parentTxBody.transactionValidDuration())
+ .memo(parentTxBody.memo())
.contractCall(contractCallBodyBuilder.build())
.build();
return transactionWith(transactionBody);
diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleSystemContractOperationsTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleSystemContractOperationsTest.java
index c596aee56865..446a8145c748 100644
--- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleSystemContractOperationsTest.java
+++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleSystemContractOperationsTest.java
@@ -194,6 +194,7 @@ void externalizeFailedResultTest() {
@Test
void syntheticTransactionForHtsCallTest() {
+ given(context.body()).willReturn(TransactionBody.DEFAULT);
assertNotNull(subject.syntheticTransactionForHtsCall(Bytes.EMPTY, ContractID.DEFAULT, true));
}
diff --git a/hedera-node/test-clients/record-snapshots/TokenUpdatePrecompile.json b/hedera-node/test-clients/record-snapshots/TokenUpdatePrecompile.json
new file mode 100644
index 000000000000..5c233194d88e
--- /dev/null
+++ b/hedera-node/test-clients/record-snapshots/TokenUpdatePrecompile.json
@@ -0,0 +1 @@
+{"specSnapshots":{"updateTokenWithInvalidKeyValues":{"placeholderNum":1001,"encodedItems":[{"b64Body":"Cg8KCQiy4J6tBhDLBhICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjloxCiISIFWNdzjCw2BKvPKpJetd3gFDvBNMdtsklnb2vPY8uUr+EICU69wDSgUIgM7aAw==","b64Record":"CiUIFhIDGOoHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjBeGLTpvsDk7c3EgvRIpiGxvVD0U+VgZRsTAIy/SMUXczsSWx+liYsX1kQTU+OQ6BwaDAju4J6tBhCLyOb7AiIPCgkIsuCerQYQywYSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIZCgoKAhgCEP+n1rkHCgsKAxjqBxCAqNa5Bw=="},{"b64Body":"Cg8KCQiz4J6tBhDNBhICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlozCiISIEt8vdPruwDQlHYszhCkb6hpArAxJUieUS1E6TPzioUtEICA6YOx3hZKBQiAztoD","b64Record":"CiUIFhIDGOsHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjAlHcawEGUpJMuwfsQ0AkmcQg5JhUX6PhnpYGJYdZ7w9uL/Dm9iV5Y9u5QLPlrD+N4aDAjv4J6tBhCLme+fASIPCgkIs+CerQYQzQYSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIdCgwKAhgCEP//0YfivC0KDQoDGOsHEICA0ofivC0="},{"b64Body":"Cg8KCQiz4J6tBhDPBhICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjloxCiISIDziEJTMjznYIzTqkmV3irYwTPKr38Cke9vq9EdHiI+MEICU69wDSgUIgM7aAw==","b64Record":"CiUIFhIDGOwHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjAzUL/WBIdiyNJ79BUBh/mXIGDXJHluU3azRP0+9Nae6nJriQAovlpNZYzLB4iapegaDAjv4J6tBhCThZuEAyIPCgkIs+CerQYQzwYSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIZCgoKAhgCEP+n1rkHCgsKAxjsBxCAqNa5Bw=="},{"b64Body":"Cg8KCQi04J6tBhDRBhICGAISAhgDGP7LzSwiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjooBjwESDAjwrvmwBhDIjeqbARptCiISIGAuCV1aCfq2JcZa0UNReVBTEN5VlVJ7bB5qVhAx67umCiM6IQNZoCrtlOqs5weWug61ak87OsYv3/k6tugiLqWacG+rCQoiEiBJc8gbyaX6f0CB4xCggqg0Nt07bvsgsorIfOnfUTZUfCIMSGVsbG8gV29ybGQhKgAyAA==","b64Record":"CiUIFhoDGO0HKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjB5zVKOWyg0Sm0p7NOD2GoDyo6tAEY0xGfrnDfcOJdGSEpJNppLosFmKLANH7+wYcsaDAjw4J6tBhDD0JnGASIPCgkItOCerQYQ0QYSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQi04J6tBhDVBhICGAISAhgDGIydjj4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjpoBiCAKAxjtByKAIDYwODA2MDQwNTI2MDQwNTE4MDYwNDAwMTYwNDA1MjgwNjAwOTgxNTI2MDIwMDE3Zjc0NmY2YjY1NmU0ZTYxNmQ2NTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNTA2MDAyOTA4MTYyMDAwMDRhOTE5MDYyMDAwNTQwNTY1YjUwNjA0MDUxODA2MDQwMDE2MDQwNTI4MDYwMGI4MTUyNjAyMDAxN2Y3NDZmNmI2NTZlNTM3OTZkNjI2ZjZjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODE1MjUwNjAwMzkwODE2MjAwMDA5MTkxOTA2MjAwMDU0MDU2NWI1MDYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MDA0ODE1MjYwMjAwMTdmNmQ2NTZkNmYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI1MDYwMDQ5MDgxNjIwMDAwZDg5MTkwNjIwMDA1NDA1NjViNTAzNDgwMTU2MjAwMDBlNjU3NjAwMDgwZmQ1YjUwNjAwMTgwNjAwMDgwNjAwNjgxMTExNTYyMDAwMTAyNTc2MjAwMDEwMTYyMDAwNjI3NTY1YjViNjAwNjgxMTExNTYyMDAwMTE3NTc2MjAwMDExNjYyMDAwNjI3NTY1YjViODE1MjYwMjAwMTkwODE1MjYwMjAwMTYwMDAyMDgxOTA1NTUwNjAwMjYwMDE2MDAwNjAwMTYwMDY4MTExMTU2MjAwMDE0NjU3NjIwMDAxNDU2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDE1YjU3NjIwMDAxNWE2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYwMDQ2MDAxNjAwMDYwMDI2MDA2ODExMTE1NjIwMDAxOGE1NzYyMDAwMTg5NjIwMDA2Mjc1NjViNWI2MDA2ODExMTE1NjIwMDAxOWY1NzYyMDAwMTllNjIwMDA2Mjc1NjViNWI4MTUyNjAyMDAxOTA4MTUyNjAyMDAxNjAwMDIwODE5MDU1NTA2MDA4NjAwMTYwMDA2MDAzNjAwNjgxMTExNTYyMDAwMWNlNTc2MjAwMDFjZDYyMDAwNjI3NTY1YjViNjAwNjgxMTExNTYyMDAwMWUzNTc2MjAwMDFlMjYyMDAwNjI3NTY1YjViODE1MjYwMjAwMTkwODE1MjYwMjAwMTYwMDAyMDgxOTA1NTUwNjAxMDYwMDE2MDAwNjAwNDYwMDY4MTExMTU2MjAwMDIxMjU3NjIwMDAyMTE2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDIyNzU3NjIwMDAyMjY2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYwMjA2MDAxNjAwMDYwMDU2MDA2ODExMTE1NjIwMDAyNTY1NzYyMDAwMjU1NjIwMDA2Mjc1NjViNWI2MDA2ODExMTE1NjIwMDAyNmI1NzYyMDAwMjZhNjIwMDA2Mjc1NjViNWI4MTUyNjAyMDAxOTA4MTUyNjAyMDAxNjAwMDIwODE5MDU1NTA2MDQwNjAwMTYwMDA2MDA2ODA4MTExMTU2MjAwMDI5OTU3NjIwMDAyOTg2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDJhZTU3NjIwMDAyYWQ2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYyMDAwNjU2NTY1YjYwMDA4MTUxOTA1MDkxOTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDQxNjAwNDUyNjAyNDYwMDBmZDViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjAyMjYwMDQ1MjYwMjQ2MDAwZmQ1YjYwMDA2MDAyODIwNDkwNTA2MDAxODIxNjgwNjIwMDAzNDg1NzYwN2Y4MjE2OTE1MDViNjAyMDgyMTA4MTAzNjIwMDAzNWU1NzYyMDAwMzVkNjIwMDAzMDA1NjViNWI1MDkxOTA1MDU2NWI2MDAwODE5MDUwODE2MDAwNTI2MDIwNjAwMDIwOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDYwMWY4MzAxMDQ5MDUwOTE5MDUwNTY1YjYwMDA4MjgyMWI5MDUwOTI5MTUwNTA1NjViNjAwMDYwMDg4MzAyNjIwMDAzYzg3ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY4MjYyMDAwMzg5NTY1YjYyMDAwM2Q0ODY4MzYyMDAwMzg5NTY1Yjk1NTA4MDE5ODQxNjkzNTA4MDg2MTY4NDE3OTI1MDUwNTA5MzkyNTA1MDUwNTY1YjYwMDA4MTkwNTA5MTkwNTA1NjViNjAwMDgxOTA1MDkxOTA1MDU2NWI2MDAwNjIwMDA0MjE2MjAwMDQxYjYyMDAwNDE1ODQ2MjAwMDNlYzU2NWI2MjAwMDNmNjU2NWI2MjAwMDNlYzU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MTkwNTA5MTkwNTA1NjViNjIwMDA0M2Q4MzYyMDAwNDAwNTY1YjYyMDAwNDU1NjIwMDA0NGM4MjYyMDAwNDI4NTY1Yjg0ODQ1NDYyMDAwMzk2NTY1YjgyNTU1MDUwNTA1MDU2NWI2MDAwOTA1NjViNjIwMDA0NmM2MjAwMDQ1ZDU2NWI2MjAwMDQ3OTgxODQ4NDYyMDAwNDMyNTY1YjUwNTA1MDU2NWI1YjgxODExMDE1NjIwMDA0YTE1NzYyMDAwNDk1NjAwMDgyNjIwMDA0NjI1NjViNjAwMTgxMDE5MDUwNjIwMDA0N2Y1NjViNTA1MDU2NWI2MDFmODIxMTE1NjIwMDA0ZjA1NzYyMDAwNGJhODE2MjAwMDM2NDU2NWI2MjAwMDRjNTg0NjIwMDAzNzk1NjViODEwMTYwMjA4NTEwMTU2MjAwMDRkNTU3ODE5MDUwNWI2MjAwMDRlZDYyMDAwNGU0ODU2MjAwMDM3OTU2NWI4MzAxODI2MjAwMDQ3ZTU2NWI1MDUwNWI1MDUwNTA1NjViNjAwMDgyODIxYzkwNTA5MjkxNTA1MDU2NWI2MDAwNjIwMDA1MTU2MDAwMTk4NDYwMDgwMjYyMDAwNGY1NTY1YjE5ODA4MzE2OTE1MDUwOTI5MTUwNTA1NjViNjAwMDYyMDAwNTMwODM4MzYyMDAwNTAyNTY1YjkxNTA4MjYwMDIwMjgyMTc5MDUwOTI5MTUwNTA1NjViNjIwMDA1NGI4MjYyMDAwMmM2NTY1YjY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYyMDAwNTY3NTc2MjAwMDU2NjYyMDAwMmQxNTY1YjViNjIwMDA1NzM4MjU0NjIwMDAzMmY1NjViNjIwMDA1ODA4MjgyODU2MjAwMDRhNTU2NWI2MDAwNjAyMDkwNTA2MDFmODMxMTYwMDE4MTE0NjIwMDA1Yjg1NzYwMDA4NDE1NjIwMDA1YTM1NzgyODcwMTUxOTA1MDViNjIwMDA1YWY4NTgyNjIwMDA1MjI1NjViODY1NTUwNjIwMDA2MWY1NjViNjAxZjE5ODQxNjYyMDAwNWM4ODY2MjAwMDM2NDU2NWI2MDAwNWI4MjgxMTAxNTYyMDAwNWYyNTc4NDg5MDE1MTgyNTU2MDAxODIwMTkxNTA2MDIwODUwMTk0NTA2MDIwODEwMTkwNTA2MjAwMDVjYjU2NWI4NjgzMTAxNTYyMDAwNjEyNTc4NDg5MDE1MTYyMDAwNjBlNjAxZjg5MTY4MjYyMDAwNTAyNTY1YjgzNTU1MDViNjAwMTYwMDI4ODAyMDE4ODU1NTA1MDUwNWI1MDUwNTA1MDUwNTA1NjViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjAyMTYwMDQ1MjYwMjQ2MDAwZmQ1YjYxMzk1NjgwNjIwMDA2NjY2MDAwMzk2MDAwZjNmZTYwODA2MDQwNTI2MDA0MzYxMDYxMDBjMjU3NjAwMDM1NjBlMDFjODA2MzdkY2JiYzcyMTE2MTAwN2Y1NzgwNjNlYWM2ZjNmZTExNjEwMDU5NTc4MDYzZWFjNmYzZmUxNDYxMDI1NDU3ODA2M2ViNTQ4ZWZmMTQ2MTAyOTE1NzgwNjNmMDA3Mjk5MDE0NjEwMmFkNTc4MDYzZjE3MzA3NjAxNDYxMDJjOTU3NjEwMGMyNTY1YjgwNjM3ZGNiYmM3MjE0NjEwMWRmNTc4MDYzOWIyM2QzZDkxNDYxMDFmYjU3ODA2M2U2MDhlMThkMTQ2MTAyMzg1NzYxMDBjMjU2NWI4MDYzMTFlMWZjMDcxNDYxMDBjNzU3ODA2MzE1ZGFjYmVhMTQ2MTAxMDQ1NzgwNjMzYWExMmVmNTE0NjEwMTQxNTc4MDYzNDEyOWI3ZGIxNDYxMDE1ZDU3ODA2MzYxOGRjNjVlMTQ2MTAxOWE1NzgwNjM3OGZlNzJhMTE0NjEwMWMzNTc1YjYwMDA4MGZkNWIzNDgwMTU2MTAwZDM1NzYwMDA4MGZkNWI1MDYxMDBlZTYwMDQ4MDM2MDM4MTAxOTA2MTAwZTk5MTkwNjEyMjQzNTY1YjYxMDJlNTU2NWI2MDQwNTE2MTAwZmI5MTkwNjEyMmM2NTY1YjYwNDA1MTgwOTEwMzkwZjM1YjM0ODAxNTYxMDExMDU3NjAwMDgwZmQ1YjUwNjEwMTJiNjAwNDgwMzYwMzgxMDE5MDYxMDEyNjkxOTA2MTIyNDM1NjViNjEwNDAxNTY1YjYwNDA1MTYxMDEzODkxOTA2MTIyYzY1NjViNjA0MDUxODA5MTAzOTBmMzViNjEwMTViNjAwNDgwMzYwMzgxMDE5MDYxMDE1NjkxOTA2MTI0NTM1NjViNjEwNTFmNTY1YjAwNWIzNDgwMTU2MTAxNjk1NzYwMDA4MGZkNWI1MDYxMDE4NDYwMDQ4MDM2MDM4MTAxOTA2MTAxN2Y5MTkwNjEyNTJkNTY1YjYxMDc3MDU2NWI2MDQwNTE2MTAxOTE5MTkwNjEyNjkzNTY1YjYwNDA1MTgwOTEwMzkwZjM=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwOIzROifcodkupftI7bMRuJyO+rHi/xaJKI4/u5BdSOb1SvxW9ft3/miEy9yhdtjJGgwI8OCerQYQ85G+qwMiDwoJCLTgnq0GENUGEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQi14J6tBhDbBhICGAISAhgDGPjR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxjtByKAIDViMzQ4MDE1NjEwMWE2NTc2MDAwODBmZDViNTA2MTAxYzE2MDA0ODAzNjAzODEwMTkwNjEwMWJjOTE5MDYxMjZiNTU2NWI2MTA3YTc1NjViMDA1YjYxMDFkZDYwMDQ4MDM2MDM4MTAxOTA2MTAxZDg5MTkwNjEyNzExNTY1YjYxMDhjZTU2NWIwMDViNjEwMWY5NjAwNDgwMzYwMzgxMDE5MDYxMDFmNDkxOTA2MTI3YjA1NjViNjEwYTY1NTY1YjAwNWIzNDgwMTU2MTAyMDc1NzYwMDA4MGZkNWI1MDYxMDIyMjYwMDQ4MDM2MDM4MTAxOTA2MTAyMWQ5MTkwNjEyMjQzNTY1YjYxMGI4NTU2NWI2MDQwNTE2MTAyMmY5MTkwNjEyMmM2NTY1YjYwNDA1MTgwOTEwMzkwZjM1YjYxMDI1MjYwMDQ4MDM2MDM4MTAxOTA2MTAyNGQ5MTkwNjEyOGE0NTY1YjYxMGNhMzU2NWIwMDViMzQ4MDE1NjEwMjYwNTc2MDAwODBmZDViNTA2MTAyN2I2MDA0ODAzNjAzODEwMTkwNjEwMjc2OTE5MDYxMjI0MzU2NWI2MTBlODA1NjViNjA0MDUxNjEwMjg4OTE5MDYxMjJjNjU2NWI2MDQwNTE4MDkxMDM5MGYzNWI2MTAyYWI2MDA0ODAzNjAzODEwMTkwNjEwMmE2OTE5MDYxMmEwZjU2NWI2MTBmOWM1NjViMDA1YjYxMDJjNzYwMDQ4MDM2MDM4MTAxOTA2MTAyYzI5MTkwNjEyYWMyNTY1YjYxMTMzNDU2NWIwMDViNjEwMmUzNjAwNDgwMzYwMzgxMDE5MDYxMDJkZTkxOTA2MTJiMDI1NjViNjExNTg3NTY1YjAwNWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzliMjNkM2Q5NjBlMDFiODg4ODg4ODg2MDQwNTE2MDI0MDE2MTAzMjI5NDkzOTI5MTkwNjEyYmJmNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMDM4YzkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxODU1YWY0OTE1MDUwM2Q4MDYwMDA4MTE0NjEwM2M3NTc2MDQwNTE5MTUwNjAxZjE5NjAzZjNkMDExNjgyMDE2MDQwNTIzZDgyNTIzZDYwMDA2MDIwODQwMTNlNjEwM2NjNTY1YjYwNjA5MTUwNWI1MDkxNTA5MTUwODE2MTAzZGQ1NzYwMTU2MTAzZjI1NjViODA4MDYwMjAwMTkwNTE4MTAxOTA2MTAzZjE5MTkwNjEyYzkwNTY1YjViNjAwMzBiOTI1MDUwNTA5NDkzNTA1MDUwNTA1NjViNjAwMDgwNjAwMDYxMDE2NzczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2NjMxNWRhY2JlYTYwZTAxYjg4ODg4ODg4NjA0MDUxNjAyNDAxNjEwNDNlOTQ5MzkyOTE5MDYxMmJiZjU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTA0YTg5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTA0ZTU1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTA0ZWE1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA4MTYxMDRmYjU3NjAxNTYxMDUxMDU2NWI4MDgwNjAyMDAxOTA1MTgxMDE5MDYxMDUwZjkxOTA2MTJjOTA1NjViNWI2MDAzMGI5MjUwNTA1MDk0OTM1MDUwNTA1MDU2NWI2MDAwNjAwNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMDUzYzU3NjEwNTNiNjEyMmZjNTY1YjViNjA0MDUxOTA4MDgyNTI4MDYwMjAwMjYwMjAwMTgyMDE2MDQwNTI4MDE1NjEwNTc1NTc4MTYwMjAwMTViNjEwNTYyNjEyMDcyNTY1YjgxNTI2MDIwMDE5MDYwMDE5MDAzOTA4MTYxMDU1YTU3OTA1MDViNTA5MDUwNjEwNTg3NjAwMDYwMDE2MDAyODk2MTE2Yzg1NjViODE2MDAwODE1MTgxMTA2MTA1OWI1NzYxMDU5YTYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA1YjQ2MDAyNjAwMzgwODg2MTE2Yzg1NjViODE2MDAxODE1MTgxMTA2MTA1Yzg1NzYxMDVjNzYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA1ZTA2MDA0NjAwMTg2NjExNzAxNTY1YjgxNjAwMjgxNTE4MTEwNjEwNWY0NTc2MTA1ZjM2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwNjBjNjAwNjYwMDE4NjYxMTcwMTU2NWI4MTYwMDM4MTUxODExMDYxMDYyMDU3NjEwNjFmNjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDYzODYwMDU2MDA0ODY2MTE3MDE1NjViODE2MDA0ODE1MTgxMTA2MTA2NGM1NzYxMDY0YjYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA2NWY2MTIwOTI1NjViNjAwMDgxNjAwMDAxOTA2MDA3MGI5MDgxNjAwNzBiODE1MjUwNTA4MzgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwODI4MTYwNDAwMTkwNjAwNzBiOTA4MTYwMDcwYjgxNTI1MDUwNjEwNmM0NjEyMGNmNTY1Yjg4ODE2MDQwMDE5MDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2OTA4MTczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwNTA4MjgxNjBlMDAxODE5MDUyNTA4MTgxNjEwMTAwMDE4MTkwNTI1MDYwMDA2MTA3MWI4YjgzNjExNzM4NTY1YjkwNTA2MDE2NjAwMzBiODExNDYxMDc2MzU3NjA0MDUxN2YwOGMzNzlhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODE1MjYwMDQwMTYxMDc1YTkwNjEyZDQ5NTY1YjYwNDA1MTgwOTEwMzkwZmQ1YjUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1NjViNjEwNzc4NjEyMTNlNTY1YjYwMDA4MDYxMDc4NTg1ODU2MTE4NTA1NjViOTE1MDYwMDcwYjkxNTA2MDE2NjAwMzBiODIxNDYxMDc5YzU3NjAwMDgwZmQ1YjgwOTI1MDUwNTA5MjkxNTA1MDU2NWI2MDAwODA2MTAxNjc3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjYzNjE4ZGM2NWU2MGUwMWI4NTg1NjA0MDUxNjAyNDAxNjEwN2RlOTI5MTkwNjEyZGIzNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMDg0ODkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxNjAwMDg2NWFmMTkxNTA1MDNkODA2MDAwODExNDYxMDg4NTU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMDg4YTU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDdmNGFmNDc4MGUwNmZlOGNiOWRmNjRiMDc5NGZhNmYwMTM5OWFmOTc5MTc1YmI5ODhlMzVlMGU1N2U1OTQ1NjdiYzgyODI2MDQwNTE2MTA4YzA5MjkxOTA2MTJkZjI1NjViNjA0MDUxODA5MTAzOTBhMTUwNTA1MDUwNTY1YjYwMDA2MDA1NjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwOGViNTc2MTA4ZWE2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTA5MjQ1NzgxNjAyMDAxNWI2MTA5MTE2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwOTA5NTc5MDUwNWI1MDkwNTA2MTA5MzY2MDAwNjAwMTYwMDI4NzYxMTZjODU2NWI4MTYwMDA4MTUxODExMDYxMDk0YTU3NjEwOTQ5NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDk2MzYwMDI2MDAzODA4NjYxMTZjODU2NWI4MTYwMDE4MTUxODExMDYxMDk3NzU3NjEwOTc2NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDk4ZjYwMDQ2MDAxODQ2MTE3MDE1NjViODE2MDAyODE1MTgxMTA2MTA5YTM=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwcd3PGCA5rUapGTu1nfjeu43B4liRHsEJw4VmAQW31CGr6q7onS9CX5OPwoNLhDpCGgwI8eCerQYQ26GLzwEiDwoJCLXgnq0GENsGEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQi14J6tBhDhBhICGAISAhgDGPjR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxjtByKAIDU3NjEwOWEyNjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDliYjYwMDY2MDAxODQ2MTE3MDE1NjViODE2MDAzODE1MTgxMTA2MTA5Y2Y1NzYxMDljZTYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA5ZTc2MDA1NjAwNDg0NjExNzAxNTY1YjgxNjAwNDgxNTE4MTEwNjEwOWZiNTc2MTA5ZmE2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjAwMDYxMGExMjg2ODM2MTE5ODQ1NjViNjAwNzBiOTA1MDYwMTY2MDAzMGI4MTE0NjEwYTVkNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjEwYTU0OTA2MTJlNmU1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1MDUwNTY1YjYwMDA2MDAxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwYTgyNTc2MTBhODE2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTBhYmI1NzgxNjAyMDAxNWI2MTBhYTg2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwYWEwNTc5MDUwNWI1MDkwNTA2MTBhYzY2MTIwNzI1NjViNjAwNDgxNjAwMDAxODE4MTUyNTA1MDYxMGFkOTYxMjEzZTU2NWIzMDgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwMTgxNjAwMDAxOTAxNTE1OTA4MTE1MTU4MTUyNTA1MDgwODI2MDIwMDE4MTkwNTI1MDgxODM2MDAwODE1MTgxMTA2MTBiNDA1NzYxMGIzZjYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MDAwNjEwYjViMzA2MDAwODg4ODg4NjExYTljNTY1YjkwNTA2MDAwNjEwYjY5ODg4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTBiN2I1NzYwMDA4MGZkNWI1MDUwNTA1MDUwNTA1MDUwNTY1YjYwMDA4MDYwMDA2MTAxNjc3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjYzOWIyM2QzZDk2MGUwMWI4ODg4ODg4ODYwNDA1MTYwMjQwMTYxMGJjMjk0OTM5MjkxOTA2MTJiYmY1NjViNjA0MDUxNjAyMDgxODMwMzAzODE1MjkwNjA0MDUyOTA3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTkxNjYwMjA4MjAxODA1MTdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY4MzgxODMxNjE3ODM1MjUwNTA1MDUwNjA0MDUxNjEwYzJjOTE5MDYxMmM0MDU2NWI2MDAwNjA0MDUxODA4MzAzODE2MDAwODY1YWYxOTE1MDUwM2Q4MDYwMDA4MTE0NjEwYzY5NTc2MDQwNTE5MTUwNjAxZjE5NjAzZjNkMDExNjgyMDE2MDQwNTIzZDgyNTIzZDYwMDA2MDIwODQwMTNlNjEwYzZlNTY1YjYwNjA5MTUwNWI1MDkxNTA5MTUwODE2MTBjN2Y1NzYwMTU2MTBjOTQ1NjViODA4MDYwMjAwMTkwNTE4MTAxOTA2MTBjOTM5MTkwNjEyYzkwNTY1YjViNjAwMzBiOTI1MDUwNTA5NDkzNTA1MDUwNTA1NjViNjAwMDYwMDU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTBjYzA1NzYxMGNiZjYxMjJmYzU2NWI1YjYwNDA1MTkwODA4MjUyODA2MDIwMDI2MDIwMDE4MjAxNjA0MDUyODAxNTYxMGNmOTU3ODE2MDIwMDE1YjYxMGNlNjYxMjA3MjU2NWI4MTUyNjAyMDAxOTA2MDAxOTAwMzkwODE2MTBjZGU1NzkwNTA1YjUwOTA1MDYxMGQwYjYwMDA2MDAxNjAwMjhjNjExNmM4NTY1YjgxNjAwMDgxNTE4MTEwNjEwZDFmNTc2MTBkMWU2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwZDM4NjAwMjYwMDM4MDhiNjExNmM4NTY1YjgxNjAwMTgxNTE4MTEwNjEwZDRjNTc2MTBkNGI2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwZDY0NjAwNDYwMDE4OTYxMTcwMTU2NWI4MTYwMDI4MTUxODExMDYxMGQ3ODU3NjEwZDc3NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMGQ5MDYwMDY2MDAxODk2MTE3MDE1NjViODE2MDAzODE1MTgxMTA2MTBkYTQ1NzYxMGRhMzYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTBkYmM2MDA1NjAwNDg5NjExNzAxNTY1YjgxNjAwNDgxNTE4MTEwNjEwZGQwNTc2MTBkY2Y2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwODM2MDAyOTA4MTYxMGRlYTkxOTA2MTMwYTU1NjViNTA4MjYwMDM5MDgxNjEwZGZhOTE5MDYxMzBhNTU2NWI1MDgxNjAwNDkwODE2MTBlMGE5MTkwNjEzMGE1NTY1YjUwNjAwMDYxMGUxYjhiNjAwMDg5ODk4NjYxMWE5YzU2NWI5MDUwNjAwMDYxMGUyOThkODM2MTE3Mzg1NjViOTA1MDYwMTY2MDAzMGI4MTE0NjEwZTcxNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjEwZTY4OTA2MTJkNDk1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1NjViNjAwMDgwNjAwMDYxMDE2NzczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2NjMxNWRhY2JlYTYwZTAxYjg4ODg4ODg4NjA0MDUxNjAyNDAxNjEwZWJkOTQ5MzkyOTE5MDYxMmJiZjU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTBmMjc5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTg1NWFmNDkxNTA1MDNkODA2MDAwODExNDYxMGY2MjU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMGY2NzU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDgxNjEwZjc4NTc2MDE1NjEwZjhkNTY1YjgwODA2MDIwMDE5MDUxODEwMTkwNjEwZjhjOTE5MDYxMmM5MDU2NWI1YjYwMDMwYjkyNTA1MDUwOTQ5MzUwNTA1MDUwNTY1YjYwMDA2MDA1NjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwZmI5NTc2MTBmYjg2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTBmZjI1NzgxNjAyMDAxNWI2MTBmZGY2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwZmQ3NTc5MDUwNWI1MDkwNTA2MTEwMDQ2MDAwNjAwMTYwMDI4NzYxMTZjODU2NWI4MTYwMDA4MTUxODExMDYxMTAxODU3NjExMDE3NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTAzMTYwMDI2MDAzODA4NjYxMTZjODU2NWI4MTYwMDE4MTUxODExMDYxMTA0NTU3NjExMDQ0NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTA1ZDYwMDQ2MDAxODQ2MTE3MDE1NjViODE2MDAyODE1MTgxMTA2MTEwNzE1NzYxMTA3MDYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTEwODk2MDA2NjAwMTg0NjExNzAxNTY1YjgxNjAwMzgxNTE4MTEwNjExMDlkNTc2MTEwOWM2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjExMGI1NjAwNTYwMDQ4NDYxMTcwMTU2NWI4MTYwMDQ4MTUxODExMDYxMTBjOTU3NjExMGM4NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTBkYzYxMjBjZjU2NWI4NTgxNjA0MDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwMjgwNTQ2MTExMjE5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExMTRkOTA2MTJlYzg1NjViODAxNTYxMTE5YTU3ODA2MDFmMTA2MTExNmY1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMTE5YTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExMTdkNTc4MjkwMDM2MDFmMTY4MjAxOTE=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw9g2F8fqVYnOrkhsYLhC5Bd/7FflRmTQNCpUtqSXL/Ht+HuQ/Cuf4rC55Eq8/ohwzGgwI8eCerQYQ26u31QMiDwoJCLXgnq0GEOEGEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQi24J6tBhDnBhICGAISAhgDGPfR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxjtByKAIDViNTA1MDUwNTA1MDgxNjAwMDAxODE5MDUyNTA2MDAzODA1NDYxMTFiNDkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTExZTA5MDYxMmVjODU2NWI4MDE1NjExMjJkNTc4MDYwMWYxMDYxMTIwMjU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExMjJkNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEyMTA1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAyMDAxODE5MDUyNTA4MTgxNjBlMDAxODE5MDUyNTA2MDA0ODA1NDYxMTI1MDkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTEyN2M5MDYxMmVjODU2NWI4MDE1NjExMmM5NTc4MDYwMWYxMDYxMTI5ZTU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExMmM5NTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEyYWM1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjA2MDAxODE5MDUyNTA2MDAwNjExMmUyODg4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTEzMmE1NzYwNDA1MTdmMDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI2MDA0MDE2MTEzMjE5MDYxMzFjMzU2NWI2MDQwNTE4MDkxMDM5MGZkNWI1MDUwNTA1MDUwNTA1MDUwNTY1YjYxMTMzYzYxMjBjZjU2NWI2MDAyODA1NDYxMTM0OTkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTEzNzU5MDYxMmVjODU2NWI4MDE1NjExM2MyNTc4MDYwMWYxMDYxMTM5NzU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExM2MyNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEzYTU1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAwMDAxODE5MDUyNTA2MDAzODA1NDYxMTNkYzkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTE0MDg5MDYxMmVjODU2NWI4MDE1NjExNDU1NTc4MDYwMWYxMDYxMTQyYTU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExNDU1NTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTE0Mzg1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAyMDAxODE5MDUyNTA4MTgxNjA0MDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwNDgwNTQ2MTE0YTc5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExNGQzOTA2MTJlYzg1NjViODAxNTYxMTUyMDU3ODA2MDFmMTA2MTE0ZjU1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMTUyMDU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExNTAzNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MTYwNjAwMTgxOTA1MjUwNjAwMDYxMTUzOTg0ODM2MTE3Mzg1NjViOTA1MDYwMTY2MDAzMGI4MTE0NjExNTgxNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjExNTc4OTA2MTMyNTU1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1NjViNjExNThmNjEyMGNmNTY1YjgyODE2MDAwMDE4MTkwNTI1MDgxODE2MDIwMDE4MTkwNTI1MDgzODE2MDQwMDE5MDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2OTA4MTczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwNTA2MDA0ODA1NDYxMTVlNjkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTE2MTI5MDYxMmVjODU2NWI4MDE1NjExNjVmNTc4MDYwMWYxMDYxMTYzNDU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExNjVmNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTE2NDI1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjA2MDAxODE5MDUyNTA2MDAwNjExNjc4ODY4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTE2YzA1NzYwNDA1MTdmMDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI2MDA0MDE2MTE2Yjc5MDYxMzJlNzU2NWI2MDQwNTE4MDkxMDM5MGZkNWI1MDUwNTA1MDUwNTA1NjViNjExNmQwNjEyMDcyNTY1YjYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MTE2ZTU4Nzg3NjExZDE2NTY1YjgxNTI2MDIwMDE2MTE2ZjQ4NTg1NjExZDZjNTY1YjgxNTI1MDkwNTA5NDkzNTA1MDUwNTA1NjViNjExNzA5NjEyMDcyNTY1YjYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MTE3MWQ4NjYxMWY0MTU2NWI4MTUyNjAyMDAxNjExNzJjODU4NTYxMWY4MjU2NWI4MTUyNTA5MDUwOTM5MjUwNTA1MDU2NWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzdkMzA1Y2ZhNjBlMDFiODY4NjYwNDA1MTYwMjQwMTYxMTc3MTkyOTE5MDYxMzYwNzU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTE3ZGI5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTE4MTg1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTE4MWQ1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA4MTYxMTgyZTU3NjAxNTYxMTg0MzU2NWI4MDgwNjAyMDAxOTA1MTgxMDE5MDYxMTg0MjkxOTA2MTJjOTA1NjViNWI2MDAzMGI5MjUwNTA1MDkyOTE1MDUwNTY1YjYwMDA2MTE4NWE2MTIxM2U1NjViNjAwMDgwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzNjNGRkMzJlNjBlMDFiODc4NzYwNDA1MTYwMjQwMTYxMTg5MTkyOTE5MDYxMzYzNzU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTE4ZmI5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTE5Mzg1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTE5M2Q1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA2MTE5NGE2MTIxM2U1NjViODI2MTE5NTc1NzYwMTU4MTYxMTk2YzU2NWI4MTgwNjAyMDAxOTA1MTgxMDE5MDYxMTk2YjkxOTA2MTM3ZGY1NjViNWI4MTYwMDMwYjkxNTA4MDk1NTA4MTk2NTA1MDUwNTA1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIweCdBa65y91cBvs4Yak8A9V4wpTntu5p1OO+hnJY12oTpsAeZblwFOxDsbb2lJLq4GgwI8uCerQYQ29z03QEiDwoJCLbgnq0GEOcGEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQi24J6tBhDtBhICGAISAhgDGPfR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxjtByKAIGZmZmZmZmZmZmZmZmZmZmYxNjYzNmZjM2NiYWY2MGUwMWI4Njg2NjA0MDUxNjAyNDAxNjExOWJkOTI5MTkwNjEzOGMxNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMWEyNzkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxNjAwMDg2NWFmMTkxNTA1MDNkODA2MDAwODExNDYxMWE2NDU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMWE2OTU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDgxNjExYTdhNTc2MDE1NjExYThmNTY1YjgwODA2MDIwMDE5MDUxODEwMTkwNjExYThlOTE5MDYxMmM5MDU2NWI1YjYwMDMwYjkyNTA1MDUwOTI5MTUwNTA1NjViNjExYWE0NjEyMGNmNTY1YjYxMWFhYzYxMjA5MjU2NWI4NTgxNjAwMDAxOTA2MDA3MGI5MDgxNjAwNzBiODE1MjUwNTA4NDgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwODM4MTYwNDAwMTkwNjAwNzBiOTA4MTYwMDcwYjgxNTI1MDUwNjAwMjgwNTQ2MTFiMTU5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExYjQxOTA2MTJlYzg1NjViODAxNTYxMWI4ZTU3ODA2MDFmMTA2MTFiNjM1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWI4ZTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExYjcxNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwMDAwMTgxOTA1MjUwNjAwMzgwNTQ2MTFiYTg5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExYmQ0OTA2MTJlYzg1NjViODAxNTYxMWMyMTU3ODA2MDFmMTA2MTFiZjY1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWMyMTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExYzA0NTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwMjAwMTgxOTA1MjUwODY4MjYwNDAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDgyODI2MGUwMDE4MTkwNTI1MDgwODI2MTAxMDAwMTgxOTA1MjUwNjAwNDgwNTQ2MTFjODY5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExY2IyOTA2MTJlYzg1NjViODAxNTYxMWNmZjU3ODA2MDFmMTA2MTFjZDQ1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWNmZjU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExY2UyNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwNjAwMTgxOTA1MjUwNTA5NTk0NTA1MDUwNTA1MDU2NWI2MDAwNjExZDNkODM2MDA2ODExMTE1NjExZDJlNTc2MTFkMmQ2MTM4ZjE1NjViNWI4MjYxMjA1ZTkwOTE5MDYzZmZmZmZmZmYxNjU2NWI5MDUwNjExZDY0ODI2MDA2ODExMTE1NjExZDU1NTc2MTFkNTQ2MTM4ZjE1NjViNWI4MjYxMjA1ZTkwOTE5MDYzZmZmZmZmZmYxNjU2NWI5MDUwOTI5MTUwNTA1NjViNjExZDc0NjEyMTNlNTY1YjYwMDA2MDA0ODExMTE1NjExZDg4NTc2MTFkODc2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFkOWI1NzYxMWQ5YTYxMzhmMTU2NWI1YjAzNjExZGI2NTc2MDAxODE2MDAwMDE5MDE1MTU5MDgxMTUxNTgxNTI1MDUwNjExZjNiNTY1YjYwMDE2MDA0ODExMTE1NjExZGNhNTc2MTFkYzk2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFkZGQ1NzYxMWRkYzYxMzhmMTU2NWI1YjAzNjExZTNmNTc2MDAwODA1NDkwNjEwMTAwMGE5MDA0NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTYwMjAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDYxMWYzYTU2NWI2MDAyNjAwNDgxMTExNTYxMWU1MzU3NjExZTUyNjEzOGYxNTY1YjViODM2MDA0ODExMTE1NjExZTY2NTc2MTFlNjU2MTM4ZjE1NjViNWIwMzYxMWU3OTU3ODE4MTYwNDAwMTgxOTA1MjUwNjExZjM5NTY1YjYwMDM2MDA0ODExMTE1NjExZThkNTc2MTFlOGM2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFlYTA1NzYxMWU5ZjYxMzhmMTU2NWI1YjAzNjExZWIzNTc4MTgxNjA2MDAxODE5MDUyNTA2MTFmMzg1NjViNjAwNDgwODExMTE1NjExZWM2NTc2MTFlYzU2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFlZDk1NzYxMWVkODYxMzhmMTU2NWI1YjAzNjExZjM3NTc2MDAwODA1NDkwNjEwMTAwMGE5MDA0NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTYwODAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDViNWI1YjViNWI5MjkxNTA1MDU2NWI2MDAwNjAwMTYwMDA4MzYwMDY4MTExMTU2MTFmNWE1NzYxMWY1OTYxMzhmMTU2NWI1YjYwMDY4MTExMTU2MTFmNmM1NzYxMWY2YjYxMzhmMTU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA1NDkwNTA5MTkwNTA1NjViNjExZjhhNjEyMTNlNTY1YjYwMDE2MDA0ODExMTE1NjExZjllNTc2MTFmOWQ2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFmYjE1NzYxMWZiMDYxMzhmMTU2NWI1YjAzNjExZmYzNTc4MTgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjEyMDU4NTY1YjYwMDQ4MDgxMTExNTYxMjAwNjU3NjEyMDA1NjEzOGYxNTY1YjViODM2MDA0ODExMTE1NjEyMDE5NTc2MTIwMTg2MTM4ZjE1NjViNWIwMzYxMjA1NzU3ODE4MTYwODAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDViNWI5MjkxNTA1MDU2NWI2MDAwODE2MGZmMTY2MDAxOTAxYjgzMTc5MDUwOTI5MTUwNTA1NjViNjA0MDUxODA2MDQwMDE2MDQwNTI4MDYwMDA4MTUyNjAyMDAxNjEyMDhjNjEyMTNlNTY1YjgxNTI1MDkwNTY1YjYwNDA1MTgwNjA2MDAxNjA0MDUyODA2MDAwNjAwNzBiODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDAwNjAwNzBiODE1MjUwOTA1NjViNjA0MDUxODA2MTAxMjAwMTYwNDA1MjgwNjA2MDgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwMDAxNTE1ODE1MjYwMjAwMTYwMDA2MDA3MGI4MTUyNjAyMDAxNjAwMDE1MTU4MTUyNjAyMDAxNjA2MDgxNTI2MDIwMDE2MTIxMzg2MTIwOTI1NjViODE1MjUwOTA1NjViNjA0MDUxODA2MGEwMDE2MDQwNTI4MDYwMDAxNTE1ODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwNjA4MTUyNjAyMDAxNjAwMDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwOTA=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwoSqvEcNq7d5I2ZGjwXe5DlFJdhuUJQs1a3wmwbldu0Jq9mqo41wsxni50ktpnH3lGgsI8+CerQYQy6i4AiIPCgkItuCerQYQ7QYSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQi34J6tBhDzBhICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxjtByKAIDU2NWI2MDAwNjA0MDUxOTA1MDkwNTY1YjYwMDA4MGZkNWI2MDAwODBmZDViNjAwMDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgyMTY5MDUwOTE5MDUwNTY1YjYwMDA2MTIxZGE4MjYxMjFhZjU2NWI5MDUwOTE5MDUwNTY1YjYxMjFlYTgxNjEyMWNmNTY1YjgxMTQ2MTIxZjU1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTIyMDc4MTYxMjFlMTU2NWI5MjkxNTA1MDU2NWI2MDAwODE5MDUwOTE5MDUwNTY1YjYxMjIyMDgxNjEyMjBkNTY1YjgxMTQ2MTIyMmI1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTIyM2Q4MTYxMjIxNzU2NWI5MjkxNTA1MDU2NWI2MDAwODA2MDAwODA2MDgwODU4NzAzMTIxNTYxMjI1ZDU3NjEyMjVjNjEyMWE1NTY1YjViNjAwMDYxMjI2Yjg3ODI4ODAxNjEyMWY4NTY1Yjk0NTA1MDYwMjA2MTIyN2M4NzgyODgwMTYxMjFmODU2NWI5MzUwNTA2MDQwNjEyMjhkODc4Mjg4MDE2MTIxZjg1NjViOTI1MDUwNjA2MDYxMjI5ZTg3ODI4ODAxNjEyMjJlNTY1YjkxNTA1MDkyOTU5MTk0NTA5MjUwNTY1YjYwMDA4MTYwMDcwYjkwNTA5MTkwNTA1NjViNjEyMmMwODE2MTIyYWE1NjViODI1MjUwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDYxMjJkYjYwMDA4MzAxODQ2MTIyYjc1NjViOTI5MTUwNTA1NjViNjAwMDgwZmQ1YjYwMDA4MGZkNWI2MDAwNjAxZjE5NjAxZjgzMDExNjkwNTA5MTkwNTA1NjViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjA0MTYwMDQ1MjYwMjQ2MDAwZmQ1YjYxMjMzNDgyNjEyMmViNTY1YjgxMDE4MTgxMTA2N2ZmZmZmZmZmZmZmZmZmZmY4MjExMTcxNTYxMjM1MzU3NjEyMzUyNjEyMmZjNTY1YjViODA2MDQwNTI1MDUwNTA1NjViNjAwMDYxMjM2NjYxMjE5YjU2NWI5MDUwNjEyMzcyODI4MjYxMjMyYjU2NWI5MTkwNTA1NjViNjAwMDY3ZmZmZmZmZmZmZmZmZmZmZjgyMTExNTYxMjM5MjU3NjEyMzkxNjEyMmZjNTY1YjViNjEyMzliODI2MTIyZWI1NjViOTA1MDYwMjA4MTAxOTA1MDkxOTA1MDU2NWI4MjgxODMzNzYwMDA4MzgzMDE1MjUwNTA1MDU2NWI2MDAwNjEyM2NhNjEyM2M1ODQ2MTIzNzc1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMjNlNjU3NjEyM2U1NjEyMmU2NTY1YjViNjEyM2YxODQ4Mjg1NjEyM2E4NTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEyNDBlNTc2MTI0MGQ2MTIyZTE1NjViNWI4MTM1NjEyNDFlODQ4MjYwMjA4NjAxNjEyM2I3NTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYxMjQzMDgxNjEyMmFhNTY1YjgxMTQ2MTI0M2I1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTI0NGQ4MTYxMjQyNzU2NWI5MjkxNTA1MDU2NWI2MDAwODA2MDAwODA2MDAwODA2MDAwNjBlMDg4OGEwMzEyMTU2MTI0NzI1NzYxMjQ3MTYxMjFhNTU2NWI1YjYwMDA2MTI0ODA4YTgyOGIwMTYxMjFmODU2NWI5NzUwNTA2MDIwNjEyNDkxOGE4MjhiMDE2MTIxZjg1NjViOTY1MDUwNjA0MDg4MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjRiMjU3NjEyNGIxNjEyMWFhNTY1YjViNjEyNGJlOGE4MjhiMDE2MTIzZjk1NjViOTU1MDUwNjA2MDg4MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjRkZjU3NjEyNGRlNjEyMWFhNTY1YjViNjEyNGViOGE4MjhiMDE2MTIzZjk1NjViOTQ1MDUwNjA4MDYxMjRmYzhhODI4YjAxNjEyMWY4NTY1YjkzNTA1MDYwYTA2MTI1MGQ4YTgyOGIwMTYxMjFmODU2NWI5MjUwNTA2MGMwNjEyNTFlOGE4MjhiMDE2MTI0M2U1NjViOTE1MDUwOTI5NTk4OTE5NDk3NTA5Mjk1NTA1NjViNjAwMDgwNjA0MDgzODUwMzEyMTU2MTI1NDQ1NzYxMjU0MzYxMjFhNTU2NWI1YjYwMDA2MTI1NTI4NTgyODYwMTYxMjFmODU2NWI5MjUwNTA2MDIwNjEyNTYzODU4Mjg2MDE2MTIyMmU1NjViOTE1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODExNTE1OTA1MDkxOTA1MDU2NWI2MTI1ODI4MTYxMjU2ZDU2NWI4MjUyNTA1MDU2NWI2MTI1OTE4MTYxMjFjZjU2NWI4MjUyNTA1MDU2NWI2MDAwODE1MTkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA1YjgzODExMDE1NjEyNWQxNTc4MDgyMDE1MTgxODQwMTUyNjAyMDgxMDE5MDUwNjEyNWI2NTY1YjYwMDA4NDg0MDE1MjUwNTA1MDUwNTY1YjYwMDA2MTI1ZTg4MjYxMjU5NzU2NWI2MTI1ZjI4MTg1NjEyNWEyNTY1YjkzNTA2MTI2MDI4MTg1NjAyMDg2MDE2MTI1YjM1NjViNjEyNjBiODE2MTIyZWI1NjViODQwMTkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MGEwODMwMTYwMDA4MzAxNTE2MTI2MmU2MDAwODYwMTgyNjEyNTc5NTY1YjUwNjAyMDgzMDE1MTYxMjY0MTYwMjA4NjAxODI2MTI1ODg1NjViNTA2MDQwODMwMTUxODQ4MjAzNjA0MDg2MDE1MjYxMjY1OTgyODI2MTI1ZGQ1NjViOTE1MDUwNjA2MDgzMDE1MTg0ODIwMzYwNjA4NjAxNTI2MTI2NzM4MjgyNjEyNWRkNTY1YjkxNTA1MDYwODA4MzAxNTE2MTI2ODg2MDgwODYwMTgyNjEyNTg4NTY1YjUwODA5MTUwNTA5MjkxNTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMjZhZDgxODQ2MTI2MTY1NjViOTA1MDkyOTE1MDUwNTY1YjYwMDA4MDYwNDA4Mzg1MDMxMjE1NjEyNmNjNTc2MTI2Y2I2MTIxYTU1NjViNWI2MDAwNjEyNmRhODU4Mjg2MDE2MTIxZjg1NjViOTI1MDUwNjAyMDgzMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjZmYjU3NjEyNmZhNjEyMWFhNTY1YjViNjEyNzA3ODU4Mjg2MDE2MTIzZjk1NjViOTE1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODA2MDAwODA2MDgwODU4NzAzMTIxNTYxMjcyYjU3NjEyNzJhNjEyMWE1NTY1YjViNjAwMDYxMjczOTg3ODI4ODAxNjEyMWY4NTY1Yjk0NTA1MDYwMjA4NTAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI3NWE1NzYxMjc1OTYxMjFhYTU2NWI1YjYxMjc2Njg3ODI4ODAxNjEyM2Y5NTY1YjkzNTA1MDYwNDA4NTAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI3ODc1NzYxMjc4NjYxMjFhYTU2NWI1YjYxMjc5Mzg3ODI4ODAxNjEyM2Y5NTY1YjkyNTA1MDYwNjA2MTI3YTQ4NzgyODgwMTYxMjFmODU2NWI5MTUwNTA5Mjk1OTE5NDUwOTI1MDU2NWI2MDAwODA2MDAwNjA2MDg0ODYwMzEyMTU2MTI3Yzk1NzYxMjdjODYxMjFhNTU2NWI1YjYwMDA2MTI3ZDc4NjgyODcwMTYxMjFmODU2NWI5MzUwNTA2MDIwNjEyN2U4ODY4Mjg3MDE2MTIxZjg1NjViOTI1MDUwNjA0MDYxMjdmOTg2ODI4NzAxNjEyNDNlNTY1YjkxNTA1MDkyNTA5MjUwOTI1NjViNjAwMDY3ZmZmZmZmZmZmZmZmZmZmZjgyMTExNTYxMjgxZTU3NjEyODFkNjEyMmZjNTY1YjViNjEyODI3ODI2MTIyZWI1NjViOTA1MDYwMjA4MTAxOTA1MDkxOTA1MDU2NWI2MDAwNjEyODQ3NjEyODQyODQ2MTI4MDM1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMjg2MzU3NjEyODYyNjEyMmU2NTY1YjViNjEyODZlODQ4Mjg1NjEyM2E4NTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEyODhiNTc2MTI4OGE2MTIyZTE1NjViNWI4MTM1NjEyODliODQ4MjYwMjA4NjAxNjEyODM0NTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA4MDYwMDA4MDYwMDA4MDYwMDA4MDYwMDA4MDYxMDE0MDhiOGQwMzEyMTU2MTI4Yzg1NzYxMjhjNzYxMjFhNTU2NWI1YjYwMDA2MTI4ZDY4ZDgyOGUwMTYxMjFmODU2NWI5YTUwNTA2MDIwNjEyOGU3OGQ4MjhlMDE2MTIxZjg1NjViOTk1MDUwNjA0MDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjkwODU3NjEyOTA3NjEyMWFhNTY1YjViNjEyOTE0OGQ4MjhlMDE2MTIzZjk1NjViOTg1MDUwNjA2MDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjkzNTU3NjEyOTM0NjEyMWFhNTY1YjViNjEyOTQxOGQ4MjhlMDE2MTIzZjk1NjViOTc1MDUwNjA4MDYxMjk1MjhkODI4ZTAxNjEyMWY4NTY1Yjk2NTA1MDYwYTA2MTI5NjM4ZDgyOGUwMTYxMjFmODU2NWI5NTUwNTA2MGMwNjEyOTc0OGQ4MjhlMDE2MTI0M2U1NjViOTQ1MDUwNjBlMDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjk5NTU3NjEyOTk0NjEyMWFhNTY1YjViNjEyOWExOGQ=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwEgtSOzk/SfDF/Q0B2c4VIvQDj6kvNw7BI0EtANGYflhOyPKzX7hvDkinNG7hS8GmGgwI8+CerQYQ29/7hAIiDwoJCLfgnq0GEPMGEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQi44J6tBhD5BhICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxjtByKAIDgyOGUwMTYxMjg3NjU2NWI5MzUwNTA2MTAxMDA4YjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI5YzM1NzYxMjljMjYxMjFhYTU2NWI1YjYxMjljZjhkODI4ZTAxNjEyODc2NTY1YjkyNTA1MDYxMDEyMDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjlmMTU3NjEyOWYwNjEyMWFhNTY1YjViNjEyOWZkOGQ4MjhlMDE2MTI4NzY1NjViOTE1MDUwOTI5NTk4OWI5MTk0OTc5YTUwOTI5NTk4NTA1NjViNjAwMDgwNjAwMDgwNjAwMDYwYTA4Njg4MDMxMjE1NjEyYTJiNTc2MTJhMmE2MTIxYTU1NjViNWI2MDAwNjEyYTM5ODg4Mjg5MDE2MTIxZjg1NjViOTU1MDUwNjAyMDYxMmE0YTg4ODI4OTAxNjEyMWY4NTY1Yjk0NTA1MDYwNDA4NjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTJhNmI1NzYxMmE2YTYxMjFhYTU2NWI1YjYxMmE3Nzg4ODI4OTAxNjEyM2Y5NTY1YjkzNTA1MDYwNjA4NjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTJhOTg1NzYxMmE5NzYxMjFhYTU2NWI1YjYxMmFhNDg4ODI4OTAxNjEyM2Y5NTY1YjkyNTA1MDYwODA2MTJhYjU4ODgyODkwMTYxMjFmODU2NWI5MTUwNTA5Mjk1NTA5Mjk1OTA5MzUwNTY1YjYwMDA4MDYwNDA4Mzg1MDMxMjE1NjEyYWQ5NTc2MTJhZDg2MTIxYTU1NjViNWI2MDAwNjEyYWU3ODU4Mjg2MDE2MTIxZjg1NjViOTI1MDUwNjAyMDYxMmFmODg1ODI4NjAxNjEyMWY4NTY1YjkxNTA1MDkyNTA5MjkwNTA1NjViNjAwMDgwNjAwMDgwNjA4MDg1ODcwMzEyMTU2MTJiMWM1NzYxMmIxYjYxMjFhNTU2NWI1YjYwMDA2MTJiMmE4NzgyODgwMTYxMjFmODU2NWI5NDUwNTA2MDIwNjEyYjNiODc4Mjg4MDE2MTIxZjg1NjViOTM1MDUwNjA0MDg1MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMmI1YzU3NjEyYjViNjEyMWFhNTY1YjViNjEyYjY4ODc4Mjg4MDE2MTI4NzY1NjViOTI1MDUwNjA2MDg1MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMmI4OTU3NjEyYjg4NjEyMWFhNTY1YjViNjEyYjk1ODc4Mjg4MDE2MTI4NzY1NjViOTE1MDUwOTI5NTkxOTQ1MDkyNTA1NjViNjEyYmFhODE2MTIxY2Y1NjViODI1MjUwNTA1NjViNjEyYmI5ODE2MTIyMGQ1NjViODI1MjUwNTA1NjViNjAwMDYwODA4MjAxOTA1MDYxMmJkNDYwMDA4MzAxODc2MTJiYTE1NjViNjEyYmUxNjAyMDgzMDE4NjYxMmJhMTU2NWI2MTJiZWU2MDQwODMwMTg1NjEyYmExNTY1YjYxMmJmYjYwNjA4MzAxODQ2MTJiYjA1NjViOTU5NDUwNTA1MDUwNTA1NjViNjAwMDgxOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTJjMWE4MjYxMjU5NzU2NWI2MTJjMjQ4MTg1NjEyYzA0NTY1YjkzNTA2MTJjMzQ4MTg1NjAyMDg2MDE2MTI1YjM1NjViODA4NDAxOTE1MDUwOTI5MTUwNTA1NjViNjAwMDYxMmM0YzgyODQ2MTJjMGY1NjViOTE1MDgxOTA1MDkyOTE1MDUwNTY1YjYwMDA4MTYwMDMwYjkwNTA5MTkwNTA1NjViNjEyYzZkODE2MTJjNTc1NjViODExNDYxMmM3ODU3NjAwMDgwZmQ1YjUwNTY1YjYwMDA4MTUxOTA1MDYxMmM4YTgxNjEyYzY0NTY1YjkyOTE1MDUwNTY1YjYwMDA2MDIwODI4NDAzMTIxNTYxMmNhNjU3NjEyY2E1NjEyMWE1NTY1YjViNjAwMDYxMmNiNDg0ODI4NTAxNjEyYzdiNTY1YjkxNTA1MDkyOTE1MDUwNTY1YjdmNGU0ODdiNzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDA1MjYwMzI2MDA0NTI2MDI0NjAwMGZkNWI2MDAwODI4MjUyNjAyMDgyMDE5MDUwOTI5MTUwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNjY2MTY5NmM2NTY0MjEwMDAwMDAwMDAwNjAwMDgyMDE1MjUwNTY1YjYwMDA2MTJkMzM2MDFiODM2MTJjZWM1NjViOTE1MDYxMmQzZTgyNjEyY2ZkNTY1YjYwMjA4MjAxOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMmQ2MjgxNjEyZDI2NTY1YjkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTJkODU4MjYxMjU5NzU2NWI2MTJkOGY4MTg1NjEyZDY5NTY1YjkzNTA2MTJkOWY4MTg1NjAyMDg2MDE2MTI1YjM1NjViNjEyZGE4ODE2MTIyZWI1NjViODQwMTkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTJkYzg2MDAwODMwMTg1NjEyYmExNTY1YjgxODEwMzYwMjA4MzAxNTI2MTJkZGE4MTg0NjEyZDdhNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjYxMmRlYzgxNjEyNTZkNTY1YjgyNTI1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTJlMDc2MDAwODMwMTg1NjEyZGUzNTY1YjgxODEwMzYwMjA4MzAxNTI2MTJlMTk4MTg0NjEyZDdhNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjdmNTU3MDY0NjE3NDY1MjA2ZjY2MjA3NDZmNmI2NTZlMjA2YjY1Nzk3MzIwNjY2MTY5NmM2NTY0MjEwMDAwMDAwMDYwMDA4MjAxNTI1MDU2NWI2MDAwNjEyZTU4NjAxYzgzNjEyY2VjNTY1YjkxNTA2MTJlNjM4MjYxMmUyMjU2NWI2MDIwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDgxODEwMzYwMDA4MzAxNTI2MTJlODc4MTYxMmU0YjU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MTUxOTA1MDkxOTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDIyNjAwNDUyNjAyNDYwMDBmZDViNjAwMDYwMDI4MjA0OTA1MDYwMDE4MjE2ODA2MTJlZTA1NzYwN2Y4MjE2OTE1MDViNjAyMDgyMTA4MTAzNjEyZWYzNTc2MTJlZjI2MTJlOTk1NjViNWI1MDkxOTA1MDU2NWI2MDAwODE5MDUwODE2MDAwNTI2MDIwNjAwMDIwOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDYwMWY4MzAxMDQ5MDUwOTE5MDUwNTY1YjYwMDA4MjgyMWI5MDUwOTI5MTUwNTA1NjViNjAwMDYwMDg4MzAyNjEyZjViN2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODI2MTJmMWU1NjViNjEyZjY1ODY4MzYxMmYxZTU2NWI5NTUwODAxOTg0MTY5MzUwODA4NjE2ODQxNzkyNTA1MDUwOTM5MjUwNTA1MDU2NWI2MDAwODE5MDUwOTE5MDUwNTY1YjYwMDA2MTJmYTI2MTJmOWQ2MTJmOTg4NDYxMjIwZDU2NWI2MTJmN2Q1NjViNjEyMjBkNTY1YjkwNTA5MTkwNTA1NjViNjAwMDgxOTA1MDkxOTA1MDU2NWI2MTJmYmM4MzYxMmY4NzU2NWI2MTJmZDA2MTJmYzg4MjYxMmZhOTU2NWI4NDg0NTQ2MTJmMmI1NjViODI1NTUwNTA1MDUwNTY1YjYwMDA5MDU2NWI2MTJmZTU2MTJmZDg1NjViNjEyZmYwODE4NDg0NjEyZmIzNTY1YjUwNTA1MDU2NWI1YjgxODExMDE1NjEzMDE0NTc2MTMwMDk2MDAwODI2MTJmZGQ1NjViNjAwMTgxMDE5MDUwNjEyZmY2NTY1YjUwNTA1NjViNjAxZjgyMTExNTYxMzA1OTU3NjEzMDJhODE2MTJlZjk1NjViNjEzMDMzODQ2MTJmMGU1NjViODEwMTYwMjA4NTEwMTU2MTMwNDI1NzgxOTA1MDViNjEzMDU2NjEzMDRlODU2MTJmMGU1NjViODMwMTgyNjEyZmY1NTY1YjUwNTA1YjUwNTA1MDU2NWI2MDAwODI4MjFjOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTMwN2M2MDAwMTk4NDYwMDgwMjYxMzA1ZTU2NWIxOTgwODMxNjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MTMwOTU4MzgzNjEzMDZiNTY1YjkxNTA4MjYwMDIwMjgyMTc5MDUwOTI5MTUwNTA1NjViNjEzMGFlODI2MTJlOGU1NjViNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzMGM3NTc2MTMwYzY2MTIyZmM1NjViNWI2MTMwZDE4MjU0NjEyZWM4NTY1YjYxMzBkYzgyODI4NTYxMzAxODU2NWI2MDAwNjAyMDkwNTA2MDFmODMxMTYwMDE4MTE0NjEzMTBmNTc2MDAwODQxNTYxMzBmZDU3ODI4NzAxNTE5MDUwNWI2MTMxMDc4NTgyNjEzMDg5NTY1Yjg2NTU1MDYxMzE2ZjU2NWI2MDFmMTk4NDE2NjEzMTFkODY2MTJlZjk1NjViNjAwMDViODI4MTEwMTU2MTMxNDU1Nzg0ODkwMTUxODI1NTYwMDE4MjAxOTE1MDYwMjA4NTAxOTQ1MDYwMjA4MTAxOTA1MDYxMzEyMDU2NWI4NjgzMTAxNTYxMzE2MjU3ODQ4OTAxNTE2MTMxNWU2MDFmODkxNjgyNjEzMDZiNTY1YjgzNTU1MDViNjAwMTYwMDI4ODAyMDE4ODU1NTA1MDUwNWI1MDUwNTA1MDUwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNmI2NTc5NzMyMDY2NjE2OTZjNjU2NDIxNjA=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwXcaiZCX0kAmcl1TGtHrdXiMiftMxIhteMoZAkEgTxD+R2zhIr6fuoiLnTLGHmk3AGgsI9OCerQYQ247pGSIPCgkIuOCerQYQ+QYSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQi44J6tBhD/BhICGAISAhgDGPKjnj4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBgB8SAxjtByL4HjAwODIwMTUyNTA1NjViNjAwMDYxMzFhZDYwMjA4MzYxMmNlYzU2NWI5MTUwNjEzMWI4ODI2MTMxNzc1NjViNjAyMDgyMDE5MDUwOTE5MDUwNTY1YjYwMDA2MDIwODIwMTkwNTA4MTgxMDM2MDAwODMwMTUyNjEzMWRjODE2MTMxYTA1NjViOTA1MDkxOTA1MDU2NWI3ZjU1NzA2NDYxNzQ2NTIwNmY2NjIwNzQ2ZjZiNjU2ZTQ5NmU2NjZmMmU3NDcyNjU2MTczNzU3Mjc5MjA2NjYxNjk2MDAwODIwMTUyN2Y2YzY1NjQyMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAyMDgyMDE1MjUwNTY1YjYwMDA2MTMyM2Y2MDI0ODM2MTJjZWM1NjViOTE1MDYxMzI0YTgyNjEzMWUzNTY1YjYwNDA4MjAxOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMzI2ZTgxNjEzMjMyNTY1YjkwNTA5MTkwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNmU2MTZkNjUyMDYxNmU2NDIwNzM3OTZkNjAwMDgyMDE1MjdmNjI2ZjZjMjA2NjYxNjk2YzY1NjQyMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMjA4MjAxNTI1MDU2NWI2MDAwNjEzMmQxNjAyYjgzNjEyY2VjNTY1YjkxNTA2MTMyZGM4MjYxMzI3NTU2NWI2MDQwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDgxODEwMzYwMDA4MzAxNTI2MTMzMDA4MTYxMzJjNDU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MjgyNTI2MDIwODIwMTkwNTA5MjkxNTA1MDU2NWI2MDAwNjEzMzIzODI2MTJlOGU1NjViNjEzMzJkODE4NTYxMzMwNzU2NWI5MzUwNjEzMzNkODE4NTYwMjA4NjAxNjEyNWIzNTY1YjYxMzM0NjgxNjEyMmViNTY1Yjg0MDE5MTUwNTA5MjkxNTA1MDU2NWI2MTMzNWE4MTYxMjJhYTU2NWI4MjUyNTA1MDU2NWI2MDAwODE1MTkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA4MTkwNTA2MDIwODIwMTkwNTA5MTkwNTA1NjViNjEzMzk1ODE2MTIyMGQ1NjViODI1MjUwNTA1NjViNjAwMDYwYTA4MzAxNjAwMDgzMDE1MTYxMzNiMzYwMDA4NjAxODI2MTI1Nzk1NjViNTA2MDIwODMwMTUxNjEzM2M2NjAyMDg2MDE4MjYxMjU4ODU2NWI1MDYwNDA4MzAxNTE4NDgyMDM2MDQwODYwMTUyNjEzM2RlODI4MjYxMjVkZDU2NWI5MTUwNTA2MDYwODMwMTUxODQ4MjAzNjA2MDg2MDE1MjYxMzNmODgyODI2MTI1ZGQ1NjViOTE1MDUwNjA4MDgzMDE1MTYxMzQwZDYwODA4NjAxODI2MTI1ODg1NjViNTA4MDkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODMwMTYwMDA4MzAxNTE2MTM0MzA2MDAwODYwMTgyNjEzMzhjNTY1YjUwNjAyMDgzMDE1MTg0ODIwMzYwMjA4NjAxNTI2MTM0NDg4MjgyNjEzMzliNTY1YjkxNTA1MDgwOTE1MDUwOTI5MTUwNTA1NjViNjAwMDYxMzQ2MTgzODM2MTM0MTg1NjViOTA1MDkyOTE1MDUwNTY1YjYwMDA2MDIwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYxMzQ4MTgyNjEzMzYwNTY1YjYxMzQ4YjgxODU2MTMzNmI1NjViOTM1MDgzNjAyMDgyMDI4NTAxNjEzNDlkODU2MTMzN2M1NjViODA2MDAwNWI4NTgxMTAxNTYxMzRkOTU3ODQ4NDAzODk1MjgxNTE2MTM0YmE4NTgyNjEzNDU1NTY1Yjk0NTA2MTM0YzU4MzYxMzQ2OTU2NWI5MjUwNjAyMDhhMDE5OTUwNTA2MDAxODEwMTkwNTA2MTM0YTE1NjViNTA4Mjk3NTA4Nzk1NTA1MDUwNTA1MDUwOTI5MTUwNTA1NjViNjA2MDgyMDE2MDAwODIwMTUxNjEzNTAxNjAwMDg1MDE4MjYxMzM1MTU2NWI1MDYwMjA4MjAxNTE2MTM1MTQ2MDIwODUwMTgyNjEyNTg4NTY1YjUwNjA0MDgyMDE1MTYxMzUyNzYwNDA4NTAxODI2MTMzNTE1NjViNTA1MDUwNTA1NjViNjAwMDYxMDE2MDgzMDE2MDAwODMwMTUxODQ4MjAzNjAwMDg2MDE1MjYxMzU0YjgyODI2MTMzMTg1NjViOTE1MDUwNjAyMDgzMDE1MTg0ODIwMzYwMjA4NjAxNTI2MTM1NjU4MjgyNjEzMzE4NTY1YjkxNTA1MDYwNDA4MzAxNTE2MTM1N2E2MDQwODYwMTgyNjEyNTg4NTY1YjUwNjA2MDgzMDE1MTg0ODIwMzYwNjA4NjAxNTI2MTM1OTI4MjgyNjEzMzE4NTY1YjkxNTA1MDYwODA4MzAxNTE2MTM1YTc2MDgwODYwMTgyNjEyNTc5NTY1YjUwNjBhMDgzMDE1MTYxMzViYTYwYTA4NjAxODI2MTMzNTE1NjViNTA2MGMwODMwMTUxNjEzNWNkNjBjMDg2MDE4MjYxMjU3OTU2NWI1MDYwZTA4MzAxNTE4NDgyMDM2MGUwODYwMTUyNjEzNWU1ODI4MjYxMzQ3NjU2NWI5MTUwNTA2MTAxMDA4MzAxNTE2MTM1ZmM2MTAxMDA4NjAxODI2MTM0ZWI1NjViNTA4MDkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTM2MWM2MDAwODMwMTg1NjEyYmExNTY1YjgxODEwMzYwMjA4MzAxNTI2MTM2MmU4MTg0NjEzNTJkNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTM2NGM2MDAwODMwMTg1NjEyYmExNTY1YjYxMzY1OTYwMjA4MzAxODQ2MTJiYjA1NjViOTM5MjUwNTA1MDU2NWI2MDAwODBmZDViNjAwMDgwZmQ1YjYxMzY3MzgxNjEyNTZkNTY1YjgxMTQ2MTM2N2U1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODE1MTkwNTA2MTM2OTA4MTYxMzY2YTU2NWI5MjkxNTA1MDU2NWI2MDAwODE1MTkwNTA2MTM2YTU4MTYxMjFlMTU2NWI5MjkxNTA1MDU2NWI2MDAwNjEzNmJlNjEzNmI5ODQ2MTIzNzc1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMzZkYTU3NjEzNmQ5NjEyMmU2NTY1YjViNjEzNmU1ODQ4Mjg1NjEyNWIzNTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEzNzAyNTc2MTM3MDE2MTIyZTE1NjViNWI4MTUxNjEzNzEyODQ4MjYwMjA4NjAxNjEzNmFiNTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MGEwODI4NDAzMTIxNTYxMzczMTU3NjEzNzMwNjEzNjYwNTY1YjViNjEzNzNiNjBhMDYxMjM1YzU2NWI5MDUwNjAwMDYxMzc0Yjg0ODI4NTAxNjEzNjgxNTY1YjYwMDA4MzAxNTI1MDYwMjA2MTM3NWY4NDgyODUwMTYxMzY5NjU2NWI2MDIwODMwMTUyNTA2MDQwODIwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzNzgzNTc2MTM3ODI2MTM2NjU1NjViNWI2MTM3OGY4NDgyODUwMTYxMzZlZDU2NWI2MDQwODMwMTUyNTA2MDYwODIwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzN2IzNTc2MTM3YjI2MTM2NjU1NjViNWI2MTM3YmY4NDgyODUwMTYxMzZlZDU2NWI2MDYwODMwMTUyNTA2MDgwNjEzN2QzODQ4Mjg1MDE2MTM2OTY1NjViNjA4MDgzMDE1MjUwOTI5MTUwNTA1NjViNjAwMDgwNjA0MDgzODUwMzEyMTU2MTM3ZjY1NzYxMzdmNTYxMjFhNTU2NWI1YjYwMDA2MTM4MDQ4NTgyODYwMTYxMmM3YjU2NWI5MjUwNTA2MDIwODMwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzODI1NTc2MTM4MjQ2MTIxYWE1NjViNWI2MTM4MzE4NTgyODYwMTYxMzcxYjU2NWI5MTUwNTA5MjUwOTI5MDUwNTY1YjYwMDA4MjgyNTI2MDIwODIwMTkwNTA5MjkxNTA1MDU2NWI2MDAwNjEzODU3ODI2MTMzNjA1NjViNjEzODYxODE4NTYxMzgzYjU2NWI5MzUwODM2MDIwODIwMjg1MDE2MTM4NzM4NTYxMzM3YzU2NWI4MDYwMDA1Yjg1ODExMDE1NjEzOGFmNTc4NDg0MDM4OTUyODE1MTYxMzg5MDg1ODI2MTM0NTU1NjViOTQ1MDYxMzg5YjgzNjEzNDY5NTY1YjkyNTA2MDIwOGEwMTk5NTA1MDYwMDE4MTAxOTA1MDYxMzg3NzU2NWI1MDgyOTc1MDg3OTU1MDUwNTA1MDUwNTA5MjkxNTA1MDU2NWI2MDAwNjA0MDgyMDE5MDUwNjEzOGQ2NjAwMDgzMDE4NTYxMmJhMTU2NWI4MTgxMDM2MDIwODMwMTUyNjEzOGU4ODE4NDYxMzg0YzU2NWI5MDUwOTM5MjUwNTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDIxNjAwNDUyNjAyNDYwMDBmZGZlYTI2NDY5NzA2NjczNTgyMjEyMjBlZDAxNzAwYmU1OTIxNmVjMDMxNjViOGEyMWZkOTVjY2NmOTQ3NmM1NmI5M2FiNTk5Nzg3MmE4ZGZkZWNiYTY0NjQ3MzZmNmM2MzQzMDAwODEyMDAzMw==","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwLbUu/cWeSf7+tiEe4IH0+QrxUjkFMFpnVMg1VAddWoJDQMbS/2fKKnV//zN2Z2m1GgwI9OCerQYQs+m2oAIiDwoJCLjgnq0GEP8GEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQi54J6tBhCBBxICGAISAhgDGJb7rp0CIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5CRgoDGO0HGiISIPOON+A+dAtymE/Vh9tBKZk6v4CjVKqdaFruNt5DLR6jIICS9AFCBQiAztoDUgBaAGoLY2VsbGFyIGRvb3I=","b64Record":"CiUIFiIDGO4HKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjA/DbyGSzCs79SistfmkVB+6hSSi0qkzLGXIXBIV4OjI+T3/JlbFtoi76+kwOPyRcEaCwj14J6tBhCDxr4pIg8KCQi54J6tBhCBBxICGAIqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOMICYq2xCjHUKAxjuBxLWcmCAYEBSYAQ2EGEAwldgADVg4ByAY33LvHIRYQB/V4Bj6sbz/hFhAFlXgGPqxvP+FGECVFeAY+tUjv8UYQKRV4Bj8AcpkBRhAq1XgGPxcwdgFGECyVdhAMJWW4Bjfcu8chRhAd9XgGObI9PZFGEB+1eAY+YI4Y0UYQI4V2EAwlZbgGMR4fwHFGEAx1eAYxXay+oUYQEEV4BjOqEu9RRhAUFXgGNBKbfbFGEBXVeAY2GNxl4UYQGaV4BjeP5yoRRhAcNXW2AAgP1bNIAVYQDTV2AAgP1bUGEA7mAEgDYDgQGQYQDpkZBhIkNWW2EC5VZbYEBRYQD7kZBhIsZWW2BAUYCRA5DzWzSAFWEBEFdgAID9W1BhAStgBIA2A4EBkGEBJpGQYSJDVlthBAFWW2BAUWEBOJGQYSLGVltgQFGAkQOQ81thAVtgBIA2A4EBkGEBVpGQYSRTVlthBR9WWwBbNIAVYQFpV2AAgP1bUGEBhGAEgDYDgQGQYQF/kZBhJS1WW2EHcFZbYEBRYQGRkZBhJpNWW2BAUYCRA5DzWzSAFWEBpldgAID9W1BhAcFgBIA2A4EBkGEBvJGQYSa1VlthB6dWWwBbYQHdYASANgOBAZBhAdiRkGEnEVZbYQjOVlsAW2EB+WAEgDYDgQGQYQH0kZBhJ7BWW2EKZVZbAFs0gBVhAgdXYACA/VtQYQIiYASANgOBAZBhAh2RkGEiQ1ZbYQuFVltgQFFhAi+RkGEixlZbYEBRgJEDkPNbYQJSYASANgOBAZBhAk2RkGEopFZbYQyjVlsAWzSAFWECYFdgAID9W1BhAntgBIA2A4EBkGECdpGQYSJDVlthDoBWW2BAUWECiJGQYSLGVltgQFGAkQOQ81thAqtgBIA2A4EBkGECppGQYSoPVlthD5xWWwBbYQLHYASANgOBAZBhAsKRkGEqwlZbYRM0VlsAW2EC42AEgDYDgQGQYQLekZBhKwJWW2EVh1ZbAFtgAIBgAGEBZ3P//////////////////////////xZjmyPT2WDgG4iIiIhgQFFgJAFhAyKUk5KRkGErv1ZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYQOMkZBhLEBWW2AAYEBRgIMDgYVa9JFQUD2AYACBFGEDx1dgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEDzFZbYGCRUFtQkVCRUIFhA91XYBVhA/JWW4CAYCABkFGBAZBhA/GRkGEskFZbW2ADC5JQUFCUk1BQUFBWW2AAgGAAYQFnc///////////////////////////FmMV2svqYOAbiIiIiGBAUWAkAWEEPpSTkpGQYSu/VltgQFFgIIGDAwOBUpBgQFKQe/////////////////////////////////////8ZFmAgggGAUXv/////////////////////////////////////g4GDFheDUlBQUFBgQFFhBKiRkGEsQFZbYABgQFGAgwOBYACGWvGRUFA9gGAAgRRhBOVXYEBRkVBgHxlgPz0BFoIBYEBSPYJSPWAAYCCEAT5hBOpWW2BgkVBbUJFQkVCBYQT7V2AVYQUQVluAgGAgAZBRgQGQYQUPkZBhLJBWW1tgAwuSUFBQlJNQUFBQVltgAGAFZ///////////gREVYQU8V2EFO2Ei/FZbW2BAUZCAglKAYCACYCABggFgQFKAFWEFdVeBYCABW2EFYmEgclZbgVJgIAGQYAGQA5CBYQVaV5BQW1CQUGEFh2AAYAFgAolhFshWW4FgAIFRgRBhBZtXYQWaYSy9VltbYCACYCABAYGQUlBhBbRgAmADgIhhFshWW4FgAYFRgRBhBchXYQXHYSy9VltbYCACYCABAYGQUlBhBeBgBGABhmEXAVZbgWACgVGBEGEF9FdhBfNhLL1WW1tgIAJgIAEBgZBSUGEGDGAGYAGGYRcBVluBYAOBUYEQYQYgV2EGH2EsvVZbW2AgAmAgAQGBkFJQYQY4YAVgBIZhFwFWW4FgBIFRgRBhBkxXYQZLYSy9VltbYCACYCABAYGQUlBhBl9hIJJWW2AAgWAAAZBgBwuQgWAHC4FSUFCDgWAgAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUIKBYEABkGAHC5CBYAcLgVJQUGEGxGEgz1ZbiIFgQAGQc///////////////////////////FpCBc///////////////////////////FoFSUFCCgWDgAYGQUlCBgWEBAAGBkFJQYABhBxuLg2EXOFZbkFBgFmADC4EUYQdjV2BAUX8Iw3mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFSYAQBYQdakGEtSVZbYEBRgJEDkP1bUFBQUFBQUFBQUFBWW2EHeGEhPlZbYACAYQeFhYVhGFBWW5FQYAcLkVBgFmADC4IUYQecV2AAgP1bgJJQUFCSkVBQVltgAIBhAWdz//////////////////////////8WY2GNxl5g4BuFhWBAUWAkAWEH3pKRkGEts1ZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYQhIkZBhLEBWW2AAYEBRgIMDgWAAhlrxkVBQPYBgAIEUYQiFV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YQiKVltgYJFQW1CRUJFQf0r0eA4G/oy532SweU+m8BOZr5eRdbuYjjXg5X5ZRWe8goJgQFFhCMCSkZBhLfJWW2BAUYCRA5ChUFBQUFZbYABgBWf//////////4ERFWEI61dhCOphIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhCSRXgWAgAVthCRFhIHJWW4FSYCABkGABkAOQgWEJCVeQUFtQkFBhCTZgAGABYAKHYRbIVluBYACBUYEQYQlKV2EJSWEsvVZbW2AgAmAgAQGBkFJQYQljYAJgA4CGYRbIVluBYAGBUYEQYQl3V2EJdmEsvVZbW2AgAmAgAQGBkFJQYQmPYARgAYRhFwFWW4FgAoFRgRBhCaNXYQmiYSy9VltbYCACYCABAYGQUlBhCbtgBmABhGEXAVZbgWADgVGBEGEJz1dhCc5hLL1WW1tgIAJgIAEBgZBSUGEJ52AFYASEYRcBVluBYASBUYEQYQn7V2EJ+mEsvVZbW2AgAmAgAQGBkFJQYABhChKGg2EZhFZbYAcLkFBgFmADC4EUYQpdV2BAUX8Iw3mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFSYAQBYQpUkGEublZbYEBRgJEDkP1bUFBQUFBQVltgAGABZ///////////gREVYQqCV2EKgWEi/FZbW2BAUZCAglKAYCACYCABggFgQFKAFWEKu1eBYCABW2EKqGEgclZbgVJgIAGQYAGQA5CBYQqgV5BQW1CQUGEKxmEgclZbYASBYAABgYFSUFBhCtlhIT5WWzCBYCABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQYAGBYAABkBUVkIEVFYFSUFCAgmAgAYGQUlCBg2AAgVGBEGELQFdhCz9hLL1WW1tgIAJgIAEBgZBSUGAAYQtbMGAAiIiIYRqcVluQUGAAYQtpiINhFzhWW5BQYBZgAwuBFGELe1dgAID9W1BQUFBQUFBQVltgAIBgAGEBZ3P//////////////////////////xZjmyPT2WDgG4iIiIhgQFFgJAFhC8KUk5KRkGErv1ZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYQwskZBhLEBWW2AAYEBRgIMDgWAAhlrxkVBQPYBgAIEUYQxpV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YQxuVltgYJFQW1CRUJFQgWEMf1dgFWEMlFZbgIBgIAGQUYEBkGEMk5GQYSyQVltbYAMLklBQUJSTUFBQUFZbYABgBWf//////////4ERFWEMwFdhDL9hIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhDPlXgWAgAVthDOZhIHJWW4FSYCABkGABkAOQgWEM3leQUFtQkFBhDQtgAGABYAKMYRbIVluBYACBUYEQYQ0fV2ENHmEsvVZbW2AgAmAgAQGBkFJQYQ04YAJgA4CLYRbIVluBYAGBUYEQYQ1MV2ENS2EsvVZbW2AgAmAgAQGBkFJQYQ1kYARgAYlhFwFWW4FgAoFRgRBhDXhXYQ13YSy9VltbYCACYCABAYGQUlBhDZBgBmABiWEXAVZbgWADgVGBEGENpFdhDaNhLL1WW1tgIAJgIAEBgZBSUGENvGAFYASJYRcBVluBYASBUYEQYQ3QV2ENz2EsvVZbW2AgAmAgAQGBkFJQg2ACkIFhDeqRkGEwpVZbUIJgA5CBYQ36kZBhMKVWW1CBYASQgWEOCpGQYTClVltQYABhDhuLYACJiYZhGpxWW5BQYABhDimNg2EXOFZbkFBgFmADC4EUYQ5xV2BAUX8Iw3mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFSYAQBYQ5okGEtSVZbYEBRgJEDkP1bUFBQUFBQUFBQUFBQUFZbYACAYABhAWdz//////////////////////////8WYxXay+pg4BuIiIiIYEBRYCQBYQ69lJOSkZBhK79WW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEPJ5GQYSxAVltgAGBAUYCDA4GFWvSRUFA9gGAAgRRhD2JXYEBRkVBgHxlgPz0BFoIBYEBSPYJSPWAAYCCEAT5hD2dWW2BgkVBbUJFQkVCBYQ94V2AVYQ+NVluAgGAgAZBRgQGQYQ+MkZBhLJBWW1tgAwuSUFBQlJNQUFBQVltgAGAFZ///////////gREVYQ+5V2EPuGEi/FZbW2BAUZCAglKAYCACYCABggFgQFKAFWEP8leBYCABW2EP32EgclZbgVJgIAGQYAGQA5CBYQ/XV5BQW1CQUGEQBGAAYAFgAodhFshWW4FgAIFRgRBhEBhXYRAXYSy9VltbYCACYCABAYGQUlBhEDFgAmADgIZhFshWW4FgAYFRgRBhEEVXYRBEYSy9VltbYCACYCABAYGQUlBhEF1gBGABhGEXAVZbgWACgVGBEGEQcVdhEHBhLL1WW1tgIAJgIAEBgZBSUGEQiWAGYAGEYRcBVluBYAOBUYEQYRCdV2EQnGEsvVZbW2AgAmAgAQGBkFJQYRC1YAVgBIRhFwFWW4FgBIFRgRBhEMlXYRDIYSy9VltbYCACYCABAYGQUlBhENxhIM9WW4WBYEABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQYAKAVGERIZBhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRFNkGEuyFZbgBVhEZpXgGAfEGERb1dhAQCAg1QEAoNSkWAgAZFhEZpWW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYRF9V4KQA2AfFoIBkVtQUFBQUIFgAAGBkFJQYAOAVGERtJBhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRHgkGEuyFZbgBVhEi1XgGAfEGESAldhAQCAg1QEAoNSkWAgAZFhEi1WW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYRIQV4KQA2AfFoIBkVtQUFBQUIFgIAGBkFJQgYFg4AGBkFJQYASAVGESUJBhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRJ8kGEuyFZbgBVhEslXgGAfEGESnldhAQCAg1QEAoNSkWAgAZFhEslWW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYRKsV4KQA2AfFoIBkVtQUFBQUIFgYAGBkFJQYABhEuKIg2EXOFZbkFBgFmADC4EUYRMqV2BAUX8Iw3mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFSYAQBYRMhkGExw1ZbYEBRgJEDkP1bUFBQUFBQUFBWW2ETPGEgz1ZbYAKAVGETSZBhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRN1kGEuyFZbgBVhE8JXgGAfEGETl1dhAQCAg1QEAoNSkWAgAZFhE8JWW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYROlV4KQA2AfFoIBkVtQUFBQUIFgAAGBkFJQYAOAVGET3JBhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRQIkGEuyFZbgBVhFFVXgGAfEGEUKldhAQCAg1QEAoNSkWAgAZFhFFVWW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYRQ4V4KQA2AfFoIBkVtQUFBQUIFgIAGBkFJQgYFgQAGQc///////////////////////////FpCBc///////////////////////////FoFSUFBgBIBUYRSnkGEuyFZbgGAfAWAggJEEAmAgAWBAUZCBAWBAUoCSkZCBgVJgIAGCgFRhFNOQYS7IVluAFWEVIFeAYB8QYRT1V2EBAICDVAQCg1KRYCABkWEVIFZbggGRkGAAUmAgYAAgkFuBVIFSkGABAZBgIAGAgxFhFQNXgpADYB8WggGRW1BQUFBQgWBgAYGQUlBgAGEVOYSDYRc4VluQUGAWYAMLgRRhFYFXYEBRfwjDeaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgVJgBAFhFXiQYTJVVltgQFGAkQOQ/VtQUFBQVlthFY9hIM9WW4KBYAABgZBSUIGBYCABgZBSUIOBYEABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQYASAVGEV5pBhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRYSkGEuyFZbgBVhFl9XgGAfEGEWNFdhAQCAg1QEAoNSkWAgAZFhFl9WW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYRZCV4KQA2AfFoIBkVtQUFBQUIFgYAGBkFJQYABhFniGg2EXOFZbkFBgFmADC4EUYRbAV2BAUX8Iw3mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFSYAQBYRa3kGEy51ZbYEBRgJEDkP1bUFBQUFBQVlthFtBhIHJWW2BAUYBgQAFgQFKAYRblh4dhHRZWW4FSYCABYRb0hYVhHWxWW4FSUJBQlJNQUFBQVlthFwlhIHJWW2BAUYBgQAFgQFKAYRcdhmEfQVZbgVJgIAFhFyyFhWEfglZbgVJQkFCTklBQUFZbYACAYABhAWdz//////////////////////////8WY30wXPpg4BuGhmBAUWAkAWEXcZKRkGE2B1ZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYRfbkZBhLEBWW2AAYEBRgIMDgWAAhlrxkVBQPYBgAIEUYRgYV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YRgdVltgYJFQW1CRUJFQgWEYLldgFWEYQ1ZbgIBgIAGQUYEBkGEYQpGQYSyQVltbYAMLklBQUJKRUFBWW2AAYRhaYSE+VltgAIBhAWdz//////////////////////////8WYzxN0y5g4BuHh2BAUWAkAWEYkZKRkGE2N1ZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYRj7kZBhLEBWW2AAYEBRgIMDgWAAhlrxkVBQPYBgAIEUYRk4V2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YRk9VltgYJFQW1CRUJFQYRlKYSE+VluCYRlXV2AVgWEZbFZbgYBgIAGQUYEBkGEZa5GQYTffVltbgWADC5FQgJVQgZZQUFBQUFCSUJKQUFZbYACAYABhAWdz//////////////////////////8WY2/Dy69g4BuGhmBAUWAkAWEZvZKRkGE4wVZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYRonkZBhLEBWW2AAYEBRgIMDgWAAhlrxkVBQPYBgAIEUYRpkV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YRppVltgYJFQW1CRUJFQgWEaeldgFWEaj1ZbgIBgIAGQUYEBkGEajpGQYSyQVltbYAMLklBQUJKRUFBWW2EapGEgz1ZbYRqsYSCSVluFgWAAAZBgBwuQgWAHC4FSUFCEgWAgAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUIOBYEABkGAHC5CBYAcLgVJQUGACgFRhGxWQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGEbQZBhLshWW4AVYRuOV4BgHxBhG2NXYQEAgINUBAKDUpFgIAGRYRuOVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWEbcVeCkANgHxaCAZFbUFBQUFCCYAABgZBSUGADgFRhG6iQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGEb1JBhLshWW4AVYRwhV4BgHxBhG/ZXYQEAgINUBAKDUpFgIAGRYRwhVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWEcBFeCkANgHxaCAZFbUFBQUFCCYCABgZBSUIaCYEABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQgoJg4AGBkFJQgIJhAQABgZBSUGAEgFRhHIaQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGEcspBhLshWW4AVYRz/V4BgHxBhHNRXYQEAgINUBAKDUpFgIAGRYRz/VluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWEc4leCkANgHxaCAZFbUFBQUFCCYGABgZBSUFCVlFBQUFBQVltgAGEdPYNgBoERFWEdLldhHS1hOPFWW1uCYSBekJGQY/////8WVluQUGEdZIJgBoERFWEdVVdhHVRhOPFWW1uCYSBekJGQY/////8WVluQUJKRUFBWW2EddGEhPlZbYABgBIERFWEdiFdhHYdhOPFWW1uDYASBERVhHZtXYR2aYTjxVltbA2EdtldgAYFgAAGQFRWQgRUVgVJQUGEfO1ZbYAFgBIERFWEdyldhHclhOPFWW1uDYASBERVhHd1XYR3cYTjxVltbA2EeP1dgAIBUkGEBAAqQBHP//////////////////////////xaBYCABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQYR86VltgAmAEgREVYR5TV2EeUmE48VZbW4NgBIERFWEeZldhHmVhOPFWW1sDYR55V4GBYEABgZBSUGEfOVZbYANgBIERFWEejVdhHoxhOPFWW1uDYASBERVhHqBXYR6fYTjxVltbA2Ees1eBgWBgAYGQUlBhHzhWW2AEgIERFWEexldhHsVhOPFWW1uDYASBERVhHtlXYR7YYTjxVltbA2EfN1dgAIBUkGEBAAqQBHP//////////////////////////xaBYIABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQW1tbW1uSkVBQVltgAGABYACDYAaBERVhH1pXYR9ZYTjxVltbYAaBERVhH2xXYR9rYTjxVltbgVJgIAGQgVJgIAFgACBUkFCRkFBWW2EfimEhPlZbYAFgBIERFWEfnldhH51hOPFWW1uDYASBERVhH7FXYR+wYTjxVltbA2Ef81eBgWAgAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGEgWFZbYASAgREVYSAGV2EgBWE48VZbW4NgBIERFWEgGVdhIBhhOPFWW1sDYSBXV4GBYIABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQW1uSkVBQVltgAIFg/xZgAZAbgxeQUJKRUFBWW2BAUYBgQAFgQFKAYACBUmAgAWEgjGEhPlZbgVJQkFZbYEBRgGBgAWBAUoBgAGAHC4FSYCABYABz//////////////////////////8WgVJgIAFgAGAHC4FSUJBWW2BAUYBhASABYEBSgGBggVJgIAFgYIFSYCABYABz//////////////////////////8WgVJgIAFgYIFSYCABYAAVFYFSYCABYABgBwuBUmAgAWAAFRWBUmAgAWBggVJgIAFhIThhIJJWW4FSUJBWW2BAUYBgoAFgQFKAYAAVFYFSYCABYABz//////////////////////////8WgVJgIAFgYIFSYCABYGCBUmAgAWAAc///////////////////////////FoFSUJBWW2AAYEBRkFCQVltgAID9W2AAgP1bYABz//////////////////////////+CFpBQkZBQVltgAGEh2oJhIa9WW5BQkZBQVlthIeqBYSHPVluBFGEh9VdgAID9W1BWW2AAgTWQUGEiB4FhIeFWW5KRUFBWW2AAgZBQkZBQVlthIiCBYSINVluBFGEiK1dgAID9W1BWW2AAgTWQUGEiPYFhIhdWW5KRUFBWW2AAgGAAgGCAhYcDEhVhIl1XYSJcYSGlVltbYABhImuHgogBYSH4VluUUFBgIGEifIeCiAFhIfhWW5NQUGBAYSKNh4KIAWEh+FZbklBQYGBhIp6HgogBYSIuVluRUFCSlZGUUJJQVltgAIFgBwuQUJGQUFZbYSLAgWEiqlZbglJQUFZbYABgIIIBkFBhIttgAIMBhGEit1ZbkpFQUFZbYACA/VtgAID9W2AAYB8ZYB+DARaQUJGQUFZbf05Ie3EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYABSYEFgBFJgJGAA/VthIzSCYSLrVluBAYGBEGf//////////4IRFxVhI1NXYSNSYSL8VltbgGBAUlBQUFZbYABhI2ZhIZtWW5BQYSNygoJhIytWW5GQUFZbYABn//////////+CERVhI5JXYSORYSL8VltbYSObgmEi61ZbkFBgIIEBkFCRkFBWW4KBgzdgAIODAVJQUFBWW2AAYSPKYSPFhGEjd1ZbYSNcVluQUIKBUmAggQGEhIQBERVhI+ZXYSPlYSLmVltbYSPxhIKFYSOoVltQk5JQUFBWW2AAgmAfgwESYSQOV2EkDWEi4VZbW4E1YSQehIJgIIYBYSO3VluRUFCSkVBQVlthJDCBYSKqVluBFGEkO1dgAID9W1BWW2AAgTWQUGEkTYFhJCdWW5KRUFBWW2AAgGAAgGAAgGAAYOCIigMSFWEkcldhJHFhIaVWW1tgAGEkgIqCiwFhIfhWW5dQUGAgYSSRioKLAWEh+FZbllBQYECIATVn//////////+BERVhJLJXYSSxYSGqVltbYSS+ioKLAWEj+VZblVBQYGCIATVn//////////+BERVhJN9XYSTeYSGqVltbYSTrioKLAWEj+VZblFBQYIBhJPyKgosBYSH4VluTUFBgoGElDYqCiwFhIfhWW5JQUGDAYSUeioKLAWEkPlZbkVBQkpWYkZSXUJKVUFZbYACAYECDhQMSFWElRFdhJUNhIaVWW1tgAGElUoWChgFhIfhWW5JQUGAgYSVjhYKGAWEiLlZbkVBQklCSkFBWW2AAgRUVkFCRkFBWW2ElgoFhJW1WW4JSUFBWW2ElkYFhIc9WW4JSUFBWW2AAgVGQUJGQUFZbYACCglJgIIIBkFCSkVBQVltgAFuDgRAVYSXRV4CCAVGBhAFSYCCBAZBQYSW2VltgAISEAVJQUFBQVltgAGEl6IJhJZdWW2El8oGFYSWiVluTUGEmAoGFYCCGAWEls1ZbYSYLgWEi61ZbhAGRUFCSkVBQVltgAGCggwFgAIMBUWEmLmAAhgGCYSV5VltQYCCDAVFhJkFgIIYBgmEliFZbUGBAgwFRhIIDYECGAVJhJlmCgmEl3VZbkVBQYGCDAVGEggNgYIYBUmEmc4KCYSXdVluRUFBggIMBUWEmiGCAhgGCYSWIVltQgJFQUJKRUFBWW2AAYCCCAZBQgYEDYACDAVJhJq2BhGEmFlZbkFCSkVBQVltgAIBgQIOFAxIVYSbMV2Emy2EhpVZbW2AAYSbahYKGAWEh+FZbklBQYCCDATVn//////////+BERVhJvtXYSb6YSGqVltbYScHhYKGAWEj+VZbkVBQklCSkFBWW2AAgGAAgGCAhYcDEhVhJytXYScqYSGlVltbYABhJzmHgogBYSH4VluUUFBgIIUBNWf//////////4ERFWEnWldhJ1lhIapWW1thJ2aHgogBYSP5VluTUFBgQIUBNWf//////////4ERFWEnh1dhJ4ZhIapWW1thJ5OHgogBYSP5VluSUFBgYGEnpIeCiAFhIfhWW5FQUJKVkZRQklBWW2AAgGAAYGCEhgMSFWEnyVdhJ8hhIaVWW1tgAGEn14aChwFhIfhWW5NQUGAgYSfohoKHAWEh+FZbklBQYEBhJ/mGgocBYSQ+VluRUFCSUJJQklZbYABn//////////+CERVhKB5XYSgdYSL8VltbYSgngmEi61ZbkFBgIIEBkFCRkFBWW2AAYShHYShChGEoA1ZbYSNcVluQUIKBUmAggQGEhIQBERVhKGNXYShiYSLmVltbYShuhIKFYSOoVltQk5JQUFBWW2AAgmAfgwESYSiLV2EoimEi4VZbW4E1YSibhIJgIIYBYSg0VluRUFCSkVBQVltgAIBgAIBgAIBgAIBgAIBhAUCLjQMSFWEoyFdhKMdhIaVWW1tgAGEo1o2CjgFhIfhWW5pQUGAgYSjnjYKOAWEh+FZbmVBQYECLATVn//////////+BERVhKQhXYSkHYSGqVltbYSkUjYKOAWEj+VZbmFBQYGCLATVn//////////+BERVhKTVXYSk0YSGqVltbYSlBjYKOAWEj+VZbl1BQYIBhKVKNgo4BYSH4VluWUFBgoGEpY42CjgFhIfhWW5VQUGDAYSl0jYKOAWEkPlZblFBQYOCLATVn//////////+BERVhKZVXYSmUYSGqVltbYSmhjYKOAWEodlZbk1BQYQEAiwE1Z///////////gREVYSnDV2EpwmEhqlZbW2Epz42CjgFhKHZWW5JQUGEBIIsBNWf//////////4ERFWEp8VdhKfBhIapWW1thKf2Ngo4BYSh2VluRUFCSlZibkZSXmlCSlZhQVltgAIBgAIBgAGCghogDEhVhKitXYSoqYSGlVltbYABhKjmIgokBYSH4VluVUFBgIGEqSoiCiQFhIfhWW5RQUGBAhgE1Z///////////gREVYSprV2EqamEhqlZbW2Eqd4iCiQFhI/lWW5NQUGBghgE1Z///////////gREVYSqYV2Eql2EhqlZbW2EqpIiCiQFhI/lWW5JQUGCAYSq1iIKJAWEh+FZbkVBQkpVQkpWQk1BWW2AAgGBAg4UDEhVhKtlXYSrYYSGlVltbYABhKueFgoYBYSH4VluSUFBgIGEq+IWChgFhIfhWW5FQUJJQkpBQVltgAIBgAIBggIWHAxIVYSscV2ErG2EhpVZbW2AAYSsqh4KIAWEh+FZblFBQYCBhKzuHgogBYSH4VluTUFBgQIUBNWf//////////4ERFWErXFdhK1thIapWW1thK2iHgogBYSh2VluSUFBgYIUBNWf//////////4ERFWEriVdhK4hhIapWW1thK5WHgogBYSh2VluRUFCSlZGUUJJQVlthK6qBYSHPVluCUlBQVlthK7mBYSINVluCUlBQVltgAGCAggGQUGEr1GAAgwGHYSuhVlthK+FgIIMBhmEroVZbYSvuYECDAYVhK6FWW2Er+2BggwGEYSuwVluVlFBQUFBQVltgAIGQUJKRUFBWW2AAYSwagmEll1ZbYSwkgYVhLARWW5NQYSw0gYVgIIYBYSWzVluAhAGRUFCSkVBQVltgAGEsTIKEYSwPVluRUIGQUJKRUFBWW2AAgWADC5BQkZBQVlthLG2BYSxXVluBFGEseFdgAID9W1BWW2AAgVGQUGEsioFhLGRWW5KRUFBWW2AAYCCChAMSFWEspldhLKVhIaVWW1tgAGEstISChQFhLHtWW5FQUJKRUFBWW39OSHtxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAUmAyYARSYCRgAP1bYACCglJgIIIBkFCSkVBQVlt/VXBkYXRlIG9mIHRv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"},{"b64Body":"Cg8KCQi54J6tBhCDBxICGAISAhgDGNyRtfsFIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo7qAaYCCgZUb2tlbkQSCEtCSENRS1ZSIOgHKgMY6gcyIhIgS3y90+u7ANCUdizOEKRvqGkCsDElSJ5RLUTpM/OKhS06IhIgS3y90+u7ANCUdizOEKRvqGkCsDElSJ5RLUTpM/OKhS1CIhIgS3y90+u7ANCUdizOEKRvqGkCsDElSJ5RLUTpM/OKhS1KIhIgS3y90+u7ANCUdizOEKRvqGkCsDElSJ5RLUTpM/OKhS1SIhIgS3y90+u7ANCUdizOEKRvqGkCsDElSJ5RLUTpM/OKhS1qDAj1rvmwBhDwh5KiAqIBIhIgS3y90+u7ANCUdizOEKRvqGkCsDElSJ5RLUTpM/OKhS2yASISIEt8vdPruwDQlHYszhCkb6hpArAxJUieUS1E6TPzioUt","b64Record":"CiUIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkD1IDGO8HEjAXCEWSbPlA3wEL/WJD+DJQqwc3SIJkyqqwDypYBD+A6q8KEgxcpFtXg3o2o0wgPxkaDAj14J6tBhDbyrnGAiIPCgkIueCerQYQgwcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIAWg8KAxjvBxIICgMY6gcQ0A9yCgoDGO8HEgMY6gc="},{"b64Body":"Cg8KCQi64J6tBhCJBxICGAISAhgDGOC2iyAiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjsICCgoDGOsHEgMY7wc=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwYO+Ro7PFHxDvAfW0IJDZuP666bHz31k4+ZY5t6IeP/OAP9RGGZWxn48U9tv+F8ZGGgsI9uCerQYQg9GbayIPCgkIuuCerQYQiQcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQi64J6tBhCLBxICGAISAhgDGKX7UiICCHgyIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOigIKCgMY7wcSAxjrBw==","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw+zxjN+ZiFfbYwN9LLILx5CN9qXeT++SqIPVbTJ4mc2jK95OIY32gUjc++kiES/giGgwI9uCerQYQq+Xq7AIiDwoJCLrgnq0GEIsHEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQi74J6tBhCNBxICGAISAhgDGOufNiICCHgyIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOchsSGQoDGO8HEggKAxjqBxDnBxIICgMY6wcQ6Ac=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwgmleara0l0L3rVbIayQmQLqySFQyHeQLrSzkEPQ7orS5wkYXb9PdKHMYWF4evBhNGgsI9+CerQYQ69j9dCIPCgkIu+CerQYQjQcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIAWhkKAxjvBxIICgMY6gcQ5wcSCAoDGOsHEOgH"},{"b64Body":"ChAKCQi74J6tBhCPBxIDGOsHEgIYAxiA/rWHASICCHgyIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOOnYKAxjuBxCAkvQBGICo1rkHImR9y7xyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAehIA","b64Record":"CiUIISIDGO4HKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjAOZ7NUKXYHXI4Xd2vgiJ09ICMYOZMDEjcnupWqoWrR2yUHcbbA6UbCO0ZDpj8NH4AaDAj34J6tBhDrw+D6AiIQCgkIu+CerQYQjwcSAxjrByogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo4wtt6qhQE6CRoCMHgomrDwAVIZCgoKAhhiEOy81YoCCgsKAxjrBxDrvNWKAg=="}]},"updateNftTokenKeysWithWrongTokenIdAndMissingAdminKey":{"placeholderNum":1008,"encodedItems":[{"b64Body":"Cg8KCQjA4J6tBhCnBxICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjloxCiISIAfk5r6KbkrQsSXlKkhg3McszRMlq++exh/Aa9vbEyvDEICU69wDSgUIgM7aAw==","b64Record":"CiUIFhIDGPEHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjB6V9XdcXbYSogAjfXb1BuSw6trUsUtdPhLPMT8TN/qp5fsaO4XYYV9E/wmCwsBnYoaDAj84J6tBhCr9sOIAiIPCgkIwOCerQYQpwcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIZCgoKAhgCEP+n1rkHCgsKAxjxBxCAqNa5Bw=="},{"b64Body":"Cg8KCQjB4J6tBhCpBxICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlozCiISIGR8v9b6YBgq0K4Bi5BUcKtCTQuzdLkW4qH3YdWhEfWfEICA6YOx3hZKBQiAztoD","b64Record":"CiUIFhIDGPIHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjDD9wkVZFZJ5oQk6t30QQilBGndJ0xIhPYbMRTH0P/BZ1zuJWITvOLBiovCxc2bMGcaCwj94J6tBhDD8I8tIg8KCQjB4J6tBhCpBxICGAIqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOUh0KDAoCGAIQ///Rh+K8LQoNCgMY8gcQgIDSh+K8LQ=="},{"b64Body":"Cg8KCQjB4J6tBhCrBxICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjloxCiISICnADX3PCNp/yAHxxe/7FGMe4sFWBMscmvoG7+FPiTPoEICU69wDSgUIgM7aAw==","b64Record":"CiUIFhIDGPMHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjD0aHQ3N8LCjkfO2v/zPt1HAemOWL757IV0B+AalyOYptrG9waFgnKTTcaONABndvMaDAj94J6tBhCThKSSAiIPCgkIweCerQYQqwcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIZCgoKAhgCEP+n1rkHCgsKAxjzBxCAqNa5Bw=="},{"b64Body":"Cg8KCQjC4J6tBhCtBxICGAISAhgDGP7LzSwiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjooBjgESCwj+rvmwBhCI3LIhGm0KIhIgks54S+yXF7zsHmg9EzMh3CBNwYCWcNhIpn0h1mKshX0KIzohAgTLqdX1kqFBhZoO6TTHhus43rJPiPzTlMpTdoYptuNBCiISIAZiauaF+APJJFSQmwKT4MBf9x2KwOgcrSUtRIPWYCOQIgxIZWxsbyBXb3JsZCEqADIA","b64Record":"CiUIFhoDGPQHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjDThjY7QUuI79tos2Awfd/RN0to8URZWwCSlcarW/kBvAfYw89cmRCNp5exdSdEhXIaCwj+4J6tBhCLu+w7Ig8KCQjC4J6tBhCtBxICGAIqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOUgA="},{"b64Body":"Cg8KCQjC4J6tBhCxBxICGAISAhgDGIydjj4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjpoBiCAKAxj0ByKAIDYwODA2MDQwNTI2MDQwNTE4MDYwNDAwMTYwNDA1MjgwNjAwOTgxNTI2MDIwMDE3Zjc0NmY2YjY1NmU0ZTYxNmQ2NTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNTA2MDAyOTA4MTYyMDAwMDRhOTE5MDYyMDAwNTQwNTY1YjUwNjA0MDUxODA2MDQwMDE2MDQwNTI4MDYwMGI4MTUyNjAyMDAxN2Y3NDZmNmI2NTZlNTM3OTZkNjI2ZjZjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODE1MjUwNjAwMzkwODE2MjAwMDA5MTkxOTA2MjAwMDU0MDU2NWI1MDYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MDA0ODE1MjYwMjAwMTdmNmQ2NTZkNmYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI1MDYwMDQ5MDgxNjIwMDAwZDg5MTkwNjIwMDA1NDA1NjViNTAzNDgwMTU2MjAwMDBlNjU3NjAwMDgwZmQ1YjUwNjAwMTgwNjAwMDgwNjAwNjgxMTExNTYyMDAwMTAyNTc2MjAwMDEwMTYyMDAwNjI3NTY1YjViNjAwNjgxMTExNTYyMDAwMTE3NTc2MjAwMDExNjYyMDAwNjI3NTY1YjViODE1MjYwMjAwMTkwODE1MjYwMjAwMTYwMDAyMDgxOTA1NTUwNjAwMjYwMDE2MDAwNjAwMTYwMDY4MTExMTU2MjAwMDE0NjU3NjIwMDAxNDU2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDE1YjU3NjIwMDAxNWE2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYwMDQ2MDAxNjAwMDYwMDI2MDA2ODExMTE1NjIwMDAxOGE1NzYyMDAwMTg5NjIwMDA2Mjc1NjViNWI2MDA2ODExMTE1NjIwMDAxOWY1NzYyMDAwMTllNjIwMDA2Mjc1NjViNWI4MTUyNjAyMDAxOTA4MTUyNjAyMDAxNjAwMDIwODE5MDU1NTA2MDA4NjAwMTYwMDA2MDAzNjAwNjgxMTExNTYyMDAwMWNlNTc2MjAwMDFjZDYyMDAwNjI3NTY1YjViNjAwNjgxMTExNTYyMDAwMWUzNTc2MjAwMDFlMjYyMDAwNjI3NTY1YjViODE1MjYwMjAwMTkwODE1MjYwMjAwMTYwMDAyMDgxOTA1NTUwNjAxMDYwMDE2MDAwNjAwNDYwMDY4MTExMTU2MjAwMDIxMjU3NjIwMDAyMTE2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDIyNzU3NjIwMDAyMjY2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYwMjA2MDAxNjAwMDYwMDU2MDA2ODExMTE1NjIwMDAyNTY1NzYyMDAwMjU1NjIwMDA2Mjc1NjViNWI2MDA2ODExMTE1NjIwMDAyNmI1NzYyMDAwMjZhNjIwMDA2Mjc1NjViNWI4MTUyNjAyMDAxOTA4MTUyNjAyMDAxNjAwMDIwODE5MDU1NTA2MDQwNjAwMTYwMDA2MDA2ODA4MTExMTU2MjAwMDI5OTU3NjIwMDAyOTg2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDJhZTU3NjIwMDAyYWQ2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYyMDAwNjU2NTY1YjYwMDA4MTUxOTA1MDkxOTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDQxNjAwNDUyNjAyNDYwMDBmZDViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjAyMjYwMDQ1MjYwMjQ2MDAwZmQ1YjYwMDA2MDAyODIwNDkwNTA2MDAxODIxNjgwNjIwMDAzNDg1NzYwN2Y4MjE2OTE1MDViNjAyMDgyMTA4MTAzNjIwMDAzNWU1NzYyMDAwMzVkNjIwMDAzMDA1NjViNWI1MDkxOTA1MDU2NWI2MDAwODE5MDUwODE2MDAwNTI2MDIwNjAwMDIwOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDYwMWY4MzAxMDQ5MDUwOTE5MDUwNTY1YjYwMDA4MjgyMWI5MDUwOTI5MTUwNTA1NjViNjAwMDYwMDg4MzAyNjIwMDAzYzg3ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY4MjYyMDAwMzg5NTY1YjYyMDAwM2Q0ODY4MzYyMDAwMzg5NTY1Yjk1NTA4MDE5ODQxNjkzNTA4MDg2MTY4NDE3OTI1MDUwNTA5MzkyNTA1MDUwNTY1YjYwMDA4MTkwNTA5MTkwNTA1NjViNjAwMDgxOTA1MDkxOTA1MDU2NWI2MDAwNjIwMDA0MjE2MjAwMDQxYjYyMDAwNDE1ODQ2MjAwMDNlYzU2NWI2MjAwMDNmNjU2NWI2MjAwMDNlYzU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MTkwNTA5MTkwNTA1NjViNjIwMDA0M2Q4MzYyMDAwNDAwNTY1YjYyMDAwNDU1NjIwMDA0NGM4MjYyMDAwNDI4NTY1Yjg0ODQ1NDYyMDAwMzk2NTY1YjgyNTU1MDUwNTA1MDU2NWI2MDAwOTA1NjViNjIwMDA0NmM2MjAwMDQ1ZDU2NWI2MjAwMDQ3OTgxODQ4NDYyMDAwNDMyNTY1YjUwNTA1MDU2NWI1YjgxODExMDE1NjIwMDA0YTE1NzYyMDAwNDk1NjAwMDgyNjIwMDA0NjI1NjViNjAwMTgxMDE5MDUwNjIwMDA0N2Y1NjViNTA1MDU2NWI2MDFmODIxMTE1NjIwMDA0ZjA1NzYyMDAwNGJhODE2MjAwMDM2NDU2NWI2MjAwMDRjNTg0NjIwMDAzNzk1NjViODEwMTYwMjA4NTEwMTU2MjAwMDRkNTU3ODE5MDUwNWI2MjAwMDRlZDYyMDAwNGU0ODU2MjAwMDM3OTU2NWI4MzAxODI2MjAwMDQ3ZTU2NWI1MDUwNWI1MDUwNTA1NjViNjAwMDgyODIxYzkwNTA5MjkxNTA1MDU2NWI2MDAwNjIwMDA1MTU2MDAwMTk4NDYwMDgwMjYyMDAwNGY1NTY1YjE5ODA4MzE2OTE1MDUwOTI5MTUwNTA1NjViNjAwMDYyMDAwNTMwODM4MzYyMDAwNTAyNTY1YjkxNTA4MjYwMDIwMjgyMTc5MDUwOTI5MTUwNTA1NjViNjIwMDA1NGI4MjYyMDAwMmM2NTY1YjY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYyMDAwNTY3NTc2MjAwMDU2NjYyMDAwMmQxNTY1YjViNjIwMDA1NzM4MjU0NjIwMDAzMmY1NjViNjIwMDA1ODA4MjgyODU2MjAwMDRhNTU2NWI2MDAwNjAyMDkwNTA2MDFmODMxMTYwMDE4MTE0NjIwMDA1Yjg1NzYwMDA4NDE1NjIwMDA1YTM1NzgyODcwMTUxOTA1MDViNjIwMDA1YWY4NTgyNjIwMDA1MjI1NjViODY1NTUwNjIwMDA2MWY1NjViNjAxZjE5ODQxNjYyMDAwNWM4ODY2MjAwMDM2NDU2NWI2MDAwNWI4MjgxMTAxNTYyMDAwNWYyNTc4NDg5MDE1MTgyNTU2MDAxODIwMTkxNTA2MDIwODUwMTk0NTA2MDIwODEwMTkwNTA2MjAwMDVjYjU2NWI4NjgzMTAxNTYyMDAwNjEyNTc4NDg5MDE1MTYyMDAwNjBlNjAxZjg5MTY4MjYyMDAwNTAyNTY1YjgzNTU1MDViNjAwMTYwMDI4ODAyMDE4ODU1NTA1MDUwNWI1MDUwNTA1MDUwNTA1NjViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjAyMTYwMDQ1MjYwMjQ2MDAwZmQ1YjYxMzk1NjgwNjIwMDA2NjY2MDAwMzk2MDAwZjNmZTYwODA2MDQwNTI2MDA0MzYxMDYxMDBjMjU3NjAwMDM1NjBlMDFjODA2MzdkY2JiYzcyMTE2MTAwN2Y1NzgwNjNlYWM2ZjNmZTExNjEwMDU5NTc4MDYzZWFjNmYzZmUxNDYxMDI1NDU3ODA2M2ViNTQ4ZWZmMTQ2MTAyOTE1NzgwNjNmMDA3Mjk5MDE0NjEwMmFkNTc4MDYzZjE3MzA3NjAxNDYxMDJjOTU3NjEwMGMyNTY1YjgwNjM3ZGNiYmM3MjE0NjEwMWRmNTc4MDYzOWIyM2QzZDkxNDYxMDFmYjU3ODA2M2U2MDhlMThkMTQ2MTAyMzg1NzYxMDBjMjU2NWI4MDYzMTFlMWZjMDcxNDYxMDBjNzU3ODA2MzE1ZGFjYmVhMTQ2MTAxMDQ1NzgwNjMzYWExMmVmNTE0NjEwMTQxNTc4MDYzNDEyOWI3ZGIxNDYxMDE1ZDU3ODA2MzYxOGRjNjVlMTQ2MTAxOWE1NzgwNjM3OGZlNzJhMTE0NjEwMWMzNTc1YjYwMDA4MGZkNWIzNDgwMTU2MTAwZDM1NzYwMDA4MGZkNWI1MDYxMDBlZTYwMDQ4MDM2MDM4MTAxOTA2MTAwZTk5MTkwNjEyMjQzNTY1YjYxMDJlNTU2NWI2MDQwNTE2MTAwZmI5MTkwNjEyMmM2NTY1YjYwNDA1MTgwOTEwMzkwZjM1YjM0ODAxNTYxMDExMDU3NjAwMDgwZmQ1YjUwNjEwMTJiNjAwNDgwMzYwMzgxMDE5MDYxMDEyNjkxOTA2MTIyNDM1NjViNjEwNDAxNTY1YjYwNDA1MTYxMDEzODkxOTA2MTIyYzY1NjViNjA0MDUxODA5MTAzOTBmMzViNjEwMTViNjAwNDgwMzYwMzgxMDE5MDYxMDE1NjkxOTA2MTI0NTM1NjViNjEwNTFmNTY1YjAwNWIzNDgwMTU2MTAxNjk1NzYwMDA4MGZkNWI1MDYxMDE4NDYwMDQ4MDM2MDM4MTAxOTA2MTAxN2Y5MTkwNjEyNTJkNTY1YjYxMDc3MDU2NWI2MDQwNTE2MTAxOTE5MTkwNjEyNjkzNTY1YjYwNDA1MTgwOTEwMzkwZjM=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwhVLTTLzY254motMqsxubGiDkTKnwJ+ExQeDV1dAy7EmTI7Ac3o2taB9lpDqniBQRGgwI/uCerQYQo4+6oAIiDwoJCMLgnq0GELEHEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjD4J6tBhC3BxICGAISAhgDGPjR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj0ByKAIDViMzQ4MDE1NjEwMWE2NTc2MDAwODBmZDViNTA2MTAxYzE2MDA0ODAzNjAzODEwMTkwNjEwMWJjOTE5MDYxMjZiNTU2NWI2MTA3YTc1NjViMDA1YjYxMDFkZDYwMDQ4MDM2MDM4MTAxOTA2MTAxZDg5MTkwNjEyNzExNTY1YjYxMDhjZTU2NWIwMDViNjEwMWY5NjAwNDgwMzYwMzgxMDE5MDYxMDFmNDkxOTA2MTI3YjA1NjViNjEwYTY1NTY1YjAwNWIzNDgwMTU2MTAyMDc1NzYwMDA4MGZkNWI1MDYxMDIyMjYwMDQ4MDM2MDM4MTAxOTA2MTAyMWQ5MTkwNjEyMjQzNTY1YjYxMGI4NTU2NWI2MDQwNTE2MTAyMmY5MTkwNjEyMmM2NTY1YjYwNDA1MTgwOTEwMzkwZjM1YjYxMDI1MjYwMDQ4MDM2MDM4MTAxOTA2MTAyNGQ5MTkwNjEyOGE0NTY1YjYxMGNhMzU2NWIwMDViMzQ4MDE1NjEwMjYwNTc2MDAwODBmZDViNTA2MTAyN2I2MDA0ODAzNjAzODEwMTkwNjEwMjc2OTE5MDYxMjI0MzU2NWI2MTBlODA1NjViNjA0MDUxNjEwMjg4OTE5MDYxMjJjNjU2NWI2MDQwNTE4MDkxMDM5MGYzNWI2MTAyYWI2MDA0ODAzNjAzODEwMTkwNjEwMmE2OTE5MDYxMmEwZjU2NWI2MTBmOWM1NjViMDA1YjYxMDJjNzYwMDQ4MDM2MDM4MTAxOTA2MTAyYzI5MTkwNjEyYWMyNTY1YjYxMTMzNDU2NWIwMDViNjEwMmUzNjAwNDgwMzYwMzgxMDE5MDYxMDJkZTkxOTA2MTJiMDI1NjViNjExNTg3NTY1YjAwNWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzliMjNkM2Q5NjBlMDFiODg4ODg4ODg2MDQwNTE2MDI0MDE2MTAzMjI5NDkzOTI5MTkwNjEyYmJmNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMDM4YzkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxODU1YWY0OTE1MDUwM2Q4MDYwMDA4MTE0NjEwM2M3NTc2MDQwNTE5MTUwNjAxZjE5NjAzZjNkMDExNjgyMDE2MDQwNTIzZDgyNTIzZDYwMDA2MDIwODQwMTNlNjEwM2NjNTY1YjYwNjA5MTUwNWI1MDkxNTA5MTUwODE2MTAzZGQ1NzYwMTU2MTAzZjI1NjViODA4MDYwMjAwMTkwNTE4MTAxOTA2MTAzZjE5MTkwNjEyYzkwNTY1YjViNjAwMzBiOTI1MDUwNTA5NDkzNTA1MDUwNTA1NjViNjAwMDgwNjAwMDYxMDE2NzczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2NjMxNWRhY2JlYTYwZTAxYjg4ODg4ODg4NjA0MDUxNjAyNDAxNjEwNDNlOTQ5MzkyOTE5MDYxMmJiZjU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTA0YTg5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTA0ZTU1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTA0ZWE1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA4MTYxMDRmYjU3NjAxNTYxMDUxMDU2NWI4MDgwNjAyMDAxOTA1MTgxMDE5MDYxMDUwZjkxOTA2MTJjOTA1NjViNWI2MDAzMGI5MjUwNTA1MDk0OTM1MDUwNTA1MDU2NWI2MDAwNjAwNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMDUzYzU3NjEwNTNiNjEyMmZjNTY1YjViNjA0MDUxOTA4MDgyNTI4MDYwMjAwMjYwMjAwMTgyMDE2MDQwNTI4MDE1NjEwNTc1NTc4MTYwMjAwMTViNjEwNTYyNjEyMDcyNTY1YjgxNTI2MDIwMDE5MDYwMDE5MDAzOTA4MTYxMDU1YTU3OTA1MDViNTA5MDUwNjEwNTg3NjAwMDYwMDE2MDAyODk2MTE2Yzg1NjViODE2MDAwODE1MTgxMTA2MTA1OWI1NzYxMDU5YTYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA1YjQ2MDAyNjAwMzgwODg2MTE2Yzg1NjViODE2MDAxODE1MTgxMTA2MTA1Yzg1NzYxMDVjNzYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA1ZTA2MDA0NjAwMTg2NjExNzAxNTY1YjgxNjAwMjgxNTE4MTEwNjEwNWY0NTc2MTA1ZjM2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwNjBjNjAwNjYwMDE4NjYxMTcwMTU2NWI4MTYwMDM4MTUxODExMDYxMDYyMDU3NjEwNjFmNjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDYzODYwMDU2MDA0ODY2MTE3MDE1NjViODE2MDA0ODE1MTgxMTA2MTA2NGM1NzYxMDY0YjYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA2NWY2MTIwOTI1NjViNjAwMDgxNjAwMDAxOTA2MDA3MGI5MDgxNjAwNzBiODE1MjUwNTA4MzgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwODI4MTYwNDAwMTkwNjAwNzBiOTA4MTYwMDcwYjgxNTI1MDUwNjEwNmM0NjEyMGNmNTY1Yjg4ODE2MDQwMDE5MDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2OTA4MTczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwNTA4MjgxNjBlMDAxODE5MDUyNTA4MTgxNjEwMTAwMDE4MTkwNTI1MDYwMDA2MTA3MWI4YjgzNjExNzM4NTY1YjkwNTA2MDE2NjAwMzBiODExNDYxMDc2MzU3NjA0MDUxN2YwOGMzNzlhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODE1MjYwMDQwMTYxMDc1YTkwNjEyZDQ5NTY1YjYwNDA1MTgwOTEwMzkwZmQ1YjUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1NjViNjEwNzc4NjEyMTNlNTY1YjYwMDA4MDYxMDc4NTg1ODU2MTE4NTA1NjViOTE1MDYwMDcwYjkxNTA2MDE2NjAwMzBiODIxNDYxMDc5YzU3NjAwMDgwZmQ1YjgwOTI1MDUwNTA5MjkxNTA1MDU2NWI2MDAwODA2MTAxNjc3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjYzNjE4ZGM2NWU2MGUwMWI4NTg1NjA0MDUxNjAyNDAxNjEwN2RlOTI5MTkwNjEyZGIzNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMDg0ODkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxNjAwMDg2NWFmMTkxNTA1MDNkODA2MDAwODExNDYxMDg4NTU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMDg4YTU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDdmNGFmNDc4MGUwNmZlOGNiOWRmNjRiMDc5NGZhNmYwMTM5OWFmOTc5MTc1YmI5ODhlMzVlMGU1N2U1OTQ1NjdiYzgyODI2MDQwNTE2MTA4YzA5MjkxOTA2MTJkZjI1NjViNjA0MDUxODA5MTAzOTBhMTUwNTA1MDUwNTY1YjYwMDA2MDA1NjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwOGViNTc2MTA4ZWE2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTA5MjQ1NzgxNjAyMDAxNWI2MTA5MTE2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwOTA5NTc5MDUwNWI1MDkwNTA2MTA5MzY2MDAwNjAwMTYwMDI4NzYxMTZjODU2NWI4MTYwMDA4MTUxODExMDYxMDk0YTU3NjEwOTQ5NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDk2MzYwMDI2MDAzODA4NjYxMTZjODU2NWI4MTYwMDE4MTUxODExMDYxMDk3NzU3NjEwOTc2NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDk4ZjYwMDQ2MDAxODQ2MTE3MDE1NjViODE2MDAyODE1MTgxMTA2MTA5YTM=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwn2PltKGqXM1J42aHgrT67F/n0d/oGpnYxImcmyeljW9pMV4dkFRxaHX103DKW+dnGgsI/+CerQYQq/TUTiIPCgkIw+CerQYQtwcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQjD4J6tBhC9BxICGAISAhgDGPjR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj0ByKAIDU3NjEwOWEyNjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDliYjYwMDY2MDAxODQ2MTE3MDE1NjViODE2MDAzODE1MTgxMTA2MTA5Y2Y1NzYxMDljZTYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA5ZTc2MDA1NjAwNDg0NjExNzAxNTY1YjgxNjAwNDgxNTE4MTEwNjEwOWZiNTc2MTA5ZmE2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjAwMDYxMGExMjg2ODM2MTE5ODQ1NjViNjAwNzBiOTA1MDYwMTY2MDAzMGI4MTE0NjEwYTVkNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjEwYTU0OTA2MTJlNmU1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1MDUwNTY1YjYwMDA2MDAxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwYTgyNTc2MTBhODE2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTBhYmI1NzgxNjAyMDAxNWI2MTBhYTg2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwYWEwNTc5MDUwNWI1MDkwNTA2MTBhYzY2MTIwNzI1NjViNjAwNDgxNjAwMDAxODE4MTUyNTA1MDYxMGFkOTYxMjEzZTU2NWIzMDgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwMTgxNjAwMDAxOTAxNTE1OTA4MTE1MTU4MTUyNTA1MDgwODI2MDIwMDE4MTkwNTI1MDgxODM2MDAwODE1MTgxMTA2MTBiNDA1NzYxMGIzZjYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MDAwNjEwYjViMzA2MDAwODg4ODg4NjExYTljNTY1YjkwNTA2MDAwNjEwYjY5ODg4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTBiN2I1NzYwMDA4MGZkNWI1MDUwNTA1MDUwNTA1MDUwNTY1YjYwMDA4MDYwMDA2MTAxNjc3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjYzOWIyM2QzZDk2MGUwMWI4ODg4ODg4ODYwNDA1MTYwMjQwMTYxMGJjMjk0OTM5MjkxOTA2MTJiYmY1NjViNjA0MDUxNjAyMDgxODMwMzAzODE1MjkwNjA0MDUyOTA3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTkxNjYwMjA4MjAxODA1MTdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY4MzgxODMxNjE3ODM1MjUwNTA1MDUwNjA0MDUxNjEwYzJjOTE5MDYxMmM0MDU2NWI2MDAwNjA0MDUxODA4MzAzODE2MDAwODY1YWYxOTE1MDUwM2Q4MDYwMDA4MTE0NjEwYzY5NTc2MDQwNTE5MTUwNjAxZjE5NjAzZjNkMDExNjgyMDE2MDQwNTIzZDgyNTIzZDYwMDA2MDIwODQwMTNlNjEwYzZlNTY1YjYwNjA5MTUwNWI1MDkxNTA5MTUwODE2MTBjN2Y1NzYwMTU2MTBjOTQ1NjViODA4MDYwMjAwMTkwNTE4MTAxOTA2MTBjOTM5MTkwNjEyYzkwNTY1YjViNjAwMzBiOTI1MDUwNTA5NDkzNTA1MDUwNTA1NjViNjAwMDYwMDU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTBjYzA1NzYxMGNiZjYxMjJmYzU2NWI1YjYwNDA1MTkwODA4MjUyODA2MDIwMDI2MDIwMDE4MjAxNjA0MDUyODAxNTYxMGNmOTU3ODE2MDIwMDE1YjYxMGNlNjYxMjA3MjU2NWI4MTUyNjAyMDAxOTA2MDAxOTAwMzkwODE2MTBjZGU1NzkwNTA1YjUwOTA1MDYxMGQwYjYwMDA2MDAxNjAwMjhjNjExNmM4NTY1YjgxNjAwMDgxNTE4MTEwNjEwZDFmNTc2MTBkMWU2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwZDM4NjAwMjYwMDM4MDhiNjExNmM4NTY1YjgxNjAwMTgxNTE4MTEwNjEwZDRjNTc2MTBkNGI2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwZDY0NjAwNDYwMDE4OTYxMTcwMTU2NWI4MTYwMDI4MTUxODExMDYxMGQ3ODU3NjEwZDc3NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMGQ5MDYwMDY2MDAxODk2MTE3MDE1NjViODE2MDAzODE1MTgxMTA2MTBkYTQ1NzYxMGRhMzYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTBkYmM2MDA1NjAwNDg5NjExNzAxNTY1YjgxNjAwNDgxNTE4MTEwNjEwZGQwNTc2MTBkY2Y2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwODM2MDAyOTA4MTYxMGRlYTkxOTA2MTMwYTU1NjViNTA4MjYwMDM5MDgxNjEwZGZhOTE5MDYxMzBhNTU2NWI1MDgxNjAwNDkwODE2MTBlMGE5MTkwNjEzMGE1NTY1YjUwNjAwMDYxMGUxYjhiNjAwMDg5ODk4NjYxMWE5YzU2NWI5MDUwNjAwMDYxMGUyOThkODM2MTE3Mzg1NjViOTA1MDYwMTY2MDAzMGI4MTE0NjEwZTcxNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjEwZTY4OTA2MTJkNDk1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1NjViNjAwMDgwNjAwMDYxMDE2NzczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2NjMxNWRhY2JlYTYwZTAxYjg4ODg4ODg4NjA0MDUxNjAyNDAxNjEwZWJkOTQ5MzkyOTE5MDYxMmJiZjU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTBmMjc5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTg1NWFmNDkxNTA1MDNkODA2MDAwODExNDYxMGY2MjU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMGY2NzU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDgxNjEwZjc4NTc2MDE1NjEwZjhkNTY1YjgwODA2MDIwMDE5MDUxODEwMTkwNjEwZjhjOTE5MDYxMmM5MDU2NWI1YjYwMDMwYjkyNTA1MDUwOTQ5MzUwNTA1MDUwNTY1YjYwMDA2MDA1NjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwZmI5NTc2MTBmYjg2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTBmZjI1NzgxNjAyMDAxNWI2MTBmZGY2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwZmQ3NTc5MDUwNWI1MDkwNTA2MTEwMDQ2MDAwNjAwMTYwMDI4NzYxMTZjODU2NWI4MTYwMDA4MTUxODExMDYxMTAxODU3NjExMDE3NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTAzMTYwMDI2MDAzODA4NjYxMTZjODU2NWI4MTYwMDE4MTUxODExMDYxMTA0NTU3NjExMDQ0NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTA1ZDYwMDQ2MDAxODQ2MTE3MDE1NjViODE2MDAyODE1MTgxMTA2MTEwNzE1NzYxMTA3MDYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTEwODk2MDA2NjAwMTg0NjExNzAxNTY1YjgxNjAwMzgxNTE4MTEwNjExMDlkNTc2MTEwOWM2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjExMGI1NjAwNTYwMDQ4NDYxMTcwMTU2NWI4MTYwMDQ4MTUxODExMDYxMTBjOTU3NjExMGM4NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTBkYzYxMjBjZjU2NWI4NTgxNjA0MDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwMjgwNTQ2MTExMjE5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExMTRkOTA2MTJlYzg1NjViODAxNTYxMTE5YTU3ODA2MDFmMTA2MTExNmY1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMTE5YTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExMTdkNTc4MjkwMDM2MDFmMTY4MjAxOTE=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwEhk2zVEnnuVb+6gUlHMcHnBOMGGpItiC94bSVheQS3h8CcV2GFC0odbPbyFkKcSoGgwI/+CerQYQy67hwQIiDwoJCMPgnq0GEL0HEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjE4J6tBhDDBxICGAISAhgDGPfR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj0ByKAIDViNTA1MDUwNTA1MDgxNjAwMDAxODE5MDUyNTA2MDAzODA1NDYxMTFiNDkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTExZTA5MDYxMmVjODU2NWI4MDE1NjExMjJkNTc4MDYwMWYxMDYxMTIwMjU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExMjJkNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEyMTA1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAyMDAxODE5MDUyNTA4MTgxNjBlMDAxODE5MDUyNTA2MDA0ODA1NDYxMTI1MDkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTEyN2M5MDYxMmVjODU2NWI4MDE1NjExMmM5NTc4MDYwMWYxMDYxMTI5ZTU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExMmM5NTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEyYWM1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjA2MDAxODE5MDUyNTA2MDAwNjExMmUyODg4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTEzMmE1NzYwNDA1MTdmMDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI2MDA0MDE2MTEzMjE5MDYxMzFjMzU2NWI2MDQwNTE4MDkxMDM5MGZkNWI1MDUwNTA1MDUwNTA1MDUwNTY1YjYxMTMzYzYxMjBjZjU2NWI2MDAyODA1NDYxMTM0OTkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTEzNzU5MDYxMmVjODU2NWI4MDE1NjExM2MyNTc4MDYwMWYxMDYxMTM5NzU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExM2MyNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEzYTU1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAwMDAxODE5MDUyNTA2MDAzODA1NDYxMTNkYzkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTE0MDg5MDYxMmVjODU2NWI4MDE1NjExNDU1NTc4MDYwMWYxMDYxMTQyYTU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExNDU1NTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTE0Mzg1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAyMDAxODE5MDUyNTA4MTgxNjA0MDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwNDgwNTQ2MTE0YTc5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExNGQzOTA2MTJlYzg1NjViODAxNTYxMTUyMDU3ODA2MDFmMTA2MTE0ZjU1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMTUyMDU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExNTAzNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MTYwNjAwMTgxOTA1MjUwNjAwMDYxMTUzOTg0ODM2MTE3Mzg1NjViOTA1MDYwMTY2MDAzMGI4MTE0NjExNTgxNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjExNTc4OTA2MTMyNTU1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1NjViNjExNThmNjEyMGNmNTY1YjgyODE2MDAwMDE4MTkwNTI1MDgxODE2MDIwMDE4MTkwNTI1MDgzODE2MDQwMDE5MDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2OTA4MTczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwNTA2MDA0ODA1NDYxMTVlNjkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTE2MTI5MDYxMmVjODU2NWI4MDE1NjExNjVmNTc4MDYwMWYxMDYxMTYzNDU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExNjVmNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTE2NDI1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjA2MDAxODE5MDUyNTA2MDAwNjExNjc4ODY4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTE2YzA1NzYwNDA1MTdmMDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI2MDA0MDE2MTE2Yjc5MDYxMzJlNzU2NWI2MDQwNTE4MDkxMDM5MGZkNWI1MDUwNTA1MDUwNTA1NjViNjExNmQwNjEyMDcyNTY1YjYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MTE2ZTU4Nzg3NjExZDE2NTY1YjgxNTI2MDIwMDE2MTE2ZjQ4NTg1NjExZDZjNTY1YjgxNTI1MDkwNTA5NDkzNTA1MDUwNTA1NjViNjExNzA5NjEyMDcyNTY1YjYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MTE3MWQ4NjYxMWY0MTU2NWI4MTUyNjAyMDAxNjExNzJjODU4NTYxMWY4MjU2NWI4MTUyNTA5MDUwOTM5MjUwNTA1MDU2NWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzdkMzA1Y2ZhNjBlMDFiODY4NjYwNDA1MTYwMjQwMTYxMTc3MTkyOTE5MDYxMzYwNzU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTE3ZGI5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTE4MTg1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTE4MWQ1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA4MTYxMTgyZTU3NjAxNTYxMTg0MzU2NWI4MDgwNjAyMDAxOTA1MTgxMDE5MDYxMTg0MjkxOTA2MTJjOTA1NjViNWI2MDAzMGI5MjUwNTA1MDkyOTE1MDUwNTY1YjYwMDA2MTE4NWE2MTIxM2U1NjViNjAwMDgwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzNjNGRkMzJlNjBlMDFiODc4NzYwNDA1MTYwMjQwMTYxMTg5MTkyOTE5MDYxMzYzNzU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTE4ZmI5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTE5Mzg1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTE5M2Q1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA2MTE5NGE2MTIxM2U1NjViODI2MTE5NTc1NzYwMTU4MTYxMTk2YzU2NWI4MTgwNjAyMDAxOTA1MTgxMDE5MDYxMTk2YjkxOTA2MTM3ZGY1NjViNWI4MTYwMDMwYjkxNTA4MDk1NTA4MTk2NTA1MDUwNTA1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwoMAI+cQVfzsUaOHe7SetHksDS/96B6oCB14wjhqo6iDJJqhXDT5i6rzZRc1UqUXNGgsIgOGerQYQs7naSSIPCgkIxOCerQYQwwcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQjE4J6tBhDJBxICGAISAhgDGPfR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj0ByKAIGZmZmZmZmZmZmZmZmZmZmYxNjYzNmZjM2NiYWY2MGUwMWI4Njg2NjA0MDUxNjAyNDAxNjExOWJkOTI5MTkwNjEzOGMxNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMWEyNzkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxNjAwMDg2NWFmMTkxNTA1MDNkODA2MDAwODExNDYxMWE2NDU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMWE2OTU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDgxNjExYTdhNTc2MDE1NjExYThmNTY1YjgwODA2MDIwMDE5MDUxODEwMTkwNjExYThlOTE5MDYxMmM5MDU2NWI1YjYwMDMwYjkyNTA1MDUwOTI5MTUwNTA1NjViNjExYWE0NjEyMGNmNTY1YjYxMWFhYzYxMjA5MjU2NWI4NTgxNjAwMDAxOTA2MDA3MGI5MDgxNjAwNzBiODE1MjUwNTA4NDgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwODM4MTYwNDAwMTkwNjAwNzBiOTA4MTYwMDcwYjgxNTI1MDUwNjAwMjgwNTQ2MTFiMTU5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExYjQxOTA2MTJlYzg1NjViODAxNTYxMWI4ZTU3ODA2MDFmMTA2MTFiNjM1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWI4ZTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExYjcxNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwMDAwMTgxOTA1MjUwNjAwMzgwNTQ2MTFiYTg5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExYmQ0OTA2MTJlYzg1NjViODAxNTYxMWMyMTU3ODA2MDFmMTA2MTFiZjY1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWMyMTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExYzA0NTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwMjAwMTgxOTA1MjUwODY4MjYwNDAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDgyODI2MGUwMDE4MTkwNTI1MDgwODI2MTAxMDAwMTgxOTA1MjUwNjAwNDgwNTQ2MTFjODY5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExY2IyOTA2MTJlYzg1NjViODAxNTYxMWNmZjU3ODA2MDFmMTA2MTFjZDQ1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWNmZjU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExY2UyNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwNjAwMTgxOTA1MjUwNTA5NTk0NTA1MDUwNTA1MDU2NWI2MDAwNjExZDNkODM2MDA2ODExMTE1NjExZDJlNTc2MTFkMmQ2MTM4ZjE1NjViNWI4MjYxMjA1ZTkwOTE5MDYzZmZmZmZmZmYxNjU2NWI5MDUwNjExZDY0ODI2MDA2ODExMTE1NjExZDU1NTc2MTFkNTQ2MTM4ZjE1NjViNWI4MjYxMjA1ZTkwOTE5MDYzZmZmZmZmZmYxNjU2NWI5MDUwOTI5MTUwNTA1NjViNjExZDc0NjEyMTNlNTY1YjYwMDA2MDA0ODExMTE1NjExZDg4NTc2MTFkODc2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFkOWI1NzYxMWQ5YTYxMzhmMTU2NWI1YjAzNjExZGI2NTc2MDAxODE2MDAwMDE5MDE1MTU5MDgxMTUxNTgxNTI1MDUwNjExZjNiNTY1YjYwMDE2MDA0ODExMTE1NjExZGNhNTc2MTFkYzk2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFkZGQ1NzYxMWRkYzYxMzhmMTU2NWI1YjAzNjExZTNmNTc2MDAwODA1NDkwNjEwMTAwMGE5MDA0NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTYwMjAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDYxMWYzYTU2NWI2MDAyNjAwNDgxMTExNTYxMWU1MzU3NjExZTUyNjEzOGYxNTY1YjViODM2MDA0ODExMTE1NjExZTY2NTc2MTFlNjU2MTM4ZjE1NjViNWIwMzYxMWU3OTU3ODE4MTYwNDAwMTgxOTA1MjUwNjExZjM5NTY1YjYwMDM2MDA0ODExMTE1NjExZThkNTc2MTFlOGM2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFlYTA1NzYxMWU5ZjYxMzhmMTU2NWI1YjAzNjExZWIzNTc4MTgxNjA2MDAxODE5MDUyNTA2MTFmMzg1NjViNjAwNDgwODExMTE1NjExZWM2NTc2MTFlYzU2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFlZDk1NzYxMWVkODYxMzhmMTU2NWI1YjAzNjExZjM3NTc2MDAwODA1NDkwNjEwMTAwMGE5MDA0NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTYwODAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDViNWI1YjViNWI5MjkxNTA1MDU2NWI2MDAwNjAwMTYwMDA4MzYwMDY4MTExMTU2MTFmNWE1NzYxMWY1OTYxMzhmMTU2NWI1YjYwMDY4MTExMTU2MTFmNmM1NzYxMWY2YjYxMzhmMTU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA1NDkwNTA5MTkwNTA1NjViNjExZjhhNjEyMTNlNTY1YjYwMDE2MDA0ODExMTE1NjExZjllNTc2MTFmOWQ2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFmYjE1NzYxMWZiMDYxMzhmMTU2NWI1YjAzNjExZmYzNTc4MTgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjEyMDU4NTY1YjYwMDQ4MDgxMTExNTYxMjAwNjU3NjEyMDA1NjEzOGYxNTY1YjViODM2MDA0ODExMTE1NjEyMDE5NTc2MTIwMTg2MTM4ZjE1NjViNWIwMzYxMjA1NzU3ODE4MTYwODAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDViNWI5MjkxNTA1MDU2NWI2MDAwODE2MGZmMTY2MDAxOTAxYjgzMTc5MDUwOTI5MTUwNTA1NjViNjA0MDUxODA2MDQwMDE2MDQwNTI4MDYwMDA4MTUyNjAyMDAxNjEyMDhjNjEyMTNlNTY1YjgxNTI1MDkwNTY1YjYwNDA1MTgwNjA2MDAxNjA0MDUyODA2MDAwNjAwNzBiODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDAwNjAwNzBiODE1MjUwOTA1NjViNjA0MDUxODA2MTAxMjAwMTYwNDA1MjgwNjA2MDgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwMDAxNTE1ODE1MjYwMjAwMTYwMDA2MDA3MGI4MTUyNjAyMDAxNjAwMDE1MTU4MTUyNjAyMDAxNjA2MDgxNTI2MDIwMDE2MTIxMzg2MTIwOTI1NjViODE1MjUwOTA1NjViNjA0MDUxODA2MGEwMDE2MDQwNTI4MDYwMDAxNTE1ODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwNjA4MTUyNjAyMDAxNjAwMDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwOTA=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwJWL2A2F5/oWAJlMO0SewQjSpa42aKRfotdHU0vtWkZJUO6mQDVECfxpmzSbkM28mGgwIgOGerQYQs4729gIiDwoJCMTgnq0GEMkHEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjF4J6tBhDPBxICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj0ByKAIDU2NWI2MDAwNjA0MDUxOTA1MDkwNTY1YjYwMDA4MGZkNWI2MDAwODBmZDViNjAwMDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgyMTY5MDUwOTE5MDUwNTY1YjYwMDA2MTIxZGE4MjYxMjFhZjU2NWI5MDUwOTE5MDUwNTY1YjYxMjFlYTgxNjEyMWNmNTY1YjgxMTQ2MTIxZjU1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTIyMDc4MTYxMjFlMTU2NWI5MjkxNTA1MDU2NWI2MDAwODE5MDUwOTE5MDUwNTY1YjYxMjIyMDgxNjEyMjBkNTY1YjgxMTQ2MTIyMmI1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTIyM2Q4MTYxMjIxNzU2NWI5MjkxNTA1MDU2NWI2MDAwODA2MDAwODA2MDgwODU4NzAzMTIxNTYxMjI1ZDU3NjEyMjVjNjEyMWE1NTY1YjViNjAwMDYxMjI2Yjg3ODI4ODAxNjEyMWY4NTY1Yjk0NTA1MDYwMjA2MTIyN2M4NzgyODgwMTYxMjFmODU2NWI5MzUwNTA2MDQwNjEyMjhkODc4Mjg4MDE2MTIxZjg1NjViOTI1MDUwNjA2MDYxMjI5ZTg3ODI4ODAxNjEyMjJlNTY1YjkxNTA1MDkyOTU5MTk0NTA5MjUwNTY1YjYwMDA4MTYwMDcwYjkwNTA5MTkwNTA1NjViNjEyMmMwODE2MTIyYWE1NjViODI1MjUwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDYxMjJkYjYwMDA4MzAxODQ2MTIyYjc1NjViOTI5MTUwNTA1NjViNjAwMDgwZmQ1YjYwMDA4MGZkNWI2MDAwNjAxZjE5NjAxZjgzMDExNjkwNTA5MTkwNTA1NjViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjA0MTYwMDQ1MjYwMjQ2MDAwZmQ1YjYxMjMzNDgyNjEyMmViNTY1YjgxMDE4MTgxMTA2N2ZmZmZmZmZmZmZmZmZmZmY4MjExMTcxNTYxMjM1MzU3NjEyMzUyNjEyMmZjNTY1YjViODA2MDQwNTI1MDUwNTA1NjViNjAwMDYxMjM2NjYxMjE5YjU2NWI5MDUwNjEyMzcyODI4MjYxMjMyYjU2NWI5MTkwNTA1NjViNjAwMDY3ZmZmZmZmZmZmZmZmZmZmZjgyMTExNTYxMjM5MjU3NjEyMzkxNjEyMmZjNTY1YjViNjEyMzliODI2MTIyZWI1NjViOTA1MDYwMjA4MTAxOTA1MDkxOTA1MDU2NWI4MjgxODMzNzYwMDA4MzgzMDE1MjUwNTA1MDU2NWI2MDAwNjEyM2NhNjEyM2M1ODQ2MTIzNzc1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMjNlNjU3NjEyM2U1NjEyMmU2NTY1YjViNjEyM2YxODQ4Mjg1NjEyM2E4NTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEyNDBlNTc2MTI0MGQ2MTIyZTE1NjViNWI4MTM1NjEyNDFlODQ4MjYwMjA4NjAxNjEyM2I3NTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYxMjQzMDgxNjEyMmFhNTY1YjgxMTQ2MTI0M2I1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTI0NGQ4MTYxMjQyNzU2NWI5MjkxNTA1MDU2NWI2MDAwODA2MDAwODA2MDAwODA2MDAwNjBlMDg4OGEwMzEyMTU2MTI0NzI1NzYxMjQ3MTYxMjFhNTU2NWI1YjYwMDA2MTI0ODA4YTgyOGIwMTYxMjFmODU2NWI5NzUwNTA2MDIwNjEyNDkxOGE4MjhiMDE2MTIxZjg1NjViOTY1MDUwNjA0MDg4MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjRiMjU3NjEyNGIxNjEyMWFhNTY1YjViNjEyNGJlOGE4MjhiMDE2MTIzZjk1NjViOTU1MDUwNjA2MDg4MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjRkZjU3NjEyNGRlNjEyMWFhNTY1YjViNjEyNGViOGE4MjhiMDE2MTIzZjk1NjViOTQ1MDUwNjA4MDYxMjRmYzhhODI4YjAxNjEyMWY4NTY1YjkzNTA1MDYwYTA2MTI1MGQ4YTgyOGIwMTYxMjFmODU2NWI5MjUwNTA2MGMwNjEyNTFlOGE4MjhiMDE2MTI0M2U1NjViOTE1MDUwOTI5NTk4OTE5NDk3NTA5Mjk1NTA1NjViNjAwMDgwNjA0MDgzODUwMzEyMTU2MTI1NDQ1NzYxMjU0MzYxMjFhNTU2NWI1YjYwMDA2MTI1NTI4NTgyODYwMTYxMjFmODU2NWI5MjUwNTA2MDIwNjEyNTYzODU4Mjg2MDE2MTIyMmU1NjViOTE1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODExNTE1OTA1MDkxOTA1MDU2NWI2MTI1ODI4MTYxMjU2ZDU2NWI4MjUyNTA1MDU2NWI2MTI1OTE4MTYxMjFjZjU2NWI4MjUyNTA1MDU2NWI2MDAwODE1MTkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA1YjgzODExMDE1NjEyNWQxNTc4MDgyMDE1MTgxODQwMTUyNjAyMDgxMDE5MDUwNjEyNWI2NTY1YjYwMDA4NDg0MDE1MjUwNTA1MDUwNTY1YjYwMDA2MTI1ZTg4MjYxMjU5NzU2NWI2MTI1ZjI4MTg1NjEyNWEyNTY1YjkzNTA2MTI2MDI4MTg1NjAyMDg2MDE2MTI1YjM1NjViNjEyNjBiODE2MTIyZWI1NjViODQwMTkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MGEwODMwMTYwMDA4MzAxNTE2MTI2MmU2MDAwODYwMTgyNjEyNTc5NTY1YjUwNjAyMDgzMDE1MTYxMjY0MTYwMjA4NjAxODI2MTI1ODg1NjViNTA2MDQwODMwMTUxODQ4MjAzNjA0MDg2MDE1MjYxMjY1OTgyODI2MTI1ZGQ1NjViOTE1MDUwNjA2MDgzMDE1MTg0ODIwMzYwNjA4NjAxNTI2MTI2NzM4MjgyNjEyNWRkNTY1YjkxNTA1MDYwODA4MzAxNTE2MTI2ODg2MDgwODYwMTgyNjEyNTg4NTY1YjUwODA5MTUwNTA5MjkxNTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMjZhZDgxODQ2MTI2MTY1NjViOTA1MDkyOTE1MDUwNTY1YjYwMDA4MDYwNDA4Mzg1MDMxMjE1NjEyNmNjNTc2MTI2Y2I2MTIxYTU1NjViNWI2MDAwNjEyNmRhODU4Mjg2MDE2MTIxZjg1NjViOTI1MDUwNjAyMDgzMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjZmYjU3NjEyNmZhNjEyMWFhNTY1YjViNjEyNzA3ODU4Mjg2MDE2MTIzZjk1NjViOTE1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODA2MDAwODA2MDgwODU4NzAzMTIxNTYxMjcyYjU3NjEyNzJhNjEyMWE1NTY1YjViNjAwMDYxMjczOTg3ODI4ODAxNjEyMWY4NTY1Yjk0NTA1MDYwMjA4NTAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI3NWE1NzYxMjc1OTYxMjFhYTU2NWI1YjYxMjc2Njg3ODI4ODAxNjEyM2Y5NTY1YjkzNTA1MDYwNDA4NTAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI3ODc1NzYxMjc4NjYxMjFhYTU2NWI1YjYxMjc5Mzg3ODI4ODAxNjEyM2Y5NTY1YjkyNTA1MDYwNjA2MTI3YTQ4NzgyODgwMTYxMjFmODU2NWI5MTUwNTA5Mjk1OTE5NDUwOTI1MDU2NWI2MDAwODA2MDAwNjA2MDg0ODYwMzEyMTU2MTI3Yzk1NzYxMjdjODYxMjFhNTU2NWI1YjYwMDA2MTI3ZDc4NjgyODcwMTYxMjFmODU2NWI5MzUwNTA2MDIwNjEyN2U4ODY4Mjg3MDE2MTIxZjg1NjViOTI1MDUwNjA0MDYxMjdmOTg2ODI4NzAxNjEyNDNlNTY1YjkxNTA1MDkyNTA5MjUwOTI1NjViNjAwMDY3ZmZmZmZmZmZmZmZmZmZmZjgyMTExNTYxMjgxZTU3NjEyODFkNjEyMmZjNTY1YjViNjEyODI3ODI2MTIyZWI1NjViOTA1MDYwMjA4MTAxOTA1MDkxOTA1MDU2NWI2MDAwNjEyODQ3NjEyODQyODQ2MTI4MDM1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMjg2MzU3NjEyODYyNjEyMmU2NTY1YjViNjEyODZlODQ4Mjg1NjEyM2E4NTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEyODhiNTc2MTI4OGE2MTIyZTE1NjViNWI4MTM1NjEyODliODQ4MjYwMjA4NjAxNjEyODM0NTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA4MDYwMDA4MDYwMDA4MDYwMDA4MDYwMDA4MDYxMDE0MDhiOGQwMzEyMTU2MTI4Yzg1NzYxMjhjNzYxMjFhNTU2NWI1YjYwMDA2MTI4ZDY4ZDgyOGUwMTYxMjFmODU2NWI5YTUwNTA2MDIwNjEyOGU3OGQ4MjhlMDE2MTIxZjg1NjViOTk1MDUwNjA0MDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjkwODU3NjEyOTA3NjEyMWFhNTY1YjViNjEyOTE0OGQ4MjhlMDE2MTIzZjk1NjViOTg1MDUwNjA2MDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjkzNTU3NjEyOTM0NjEyMWFhNTY1YjViNjEyOTQxOGQ4MjhlMDE2MTIzZjk1NjViOTc1MDUwNjA4MDYxMjk1MjhkODI4ZTAxNjEyMWY4NTY1Yjk2NTA1MDYwYTA2MTI5NjM4ZDgyOGUwMTYxMjFmODU2NWI5NTUwNTA2MGMwNjEyOTc0OGQ4MjhlMDE2MTI0M2U1NjViOTQ1MDUwNjBlMDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjk5NTU3NjEyOTk0NjEyMWFhNTY1YjViNjEyOWExOGQ=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw8bCY/+xVUJ6M4kuKmtLjSwuwKv6j9Y9AgUpkHfVXB5z3fXMyyiiFvDHdERkr7+kbGgsIgeGerQYQi7b4fSIPCgkIxeCerQYQzwcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQjF4J6tBhDVBxICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj0ByKAIDgyOGUwMTYxMjg3NjU2NWI5MzUwNTA2MTAxMDA4YjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI5YzM1NzYxMjljMjYxMjFhYTU2NWI1YjYxMjljZjhkODI4ZTAxNjEyODc2NTY1YjkyNTA1MDYxMDEyMDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjlmMTU3NjEyOWYwNjEyMWFhNTY1YjViNjEyOWZkOGQ4MjhlMDE2MTI4NzY1NjViOTE1MDUwOTI5NTk4OWI5MTk0OTc5YTUwOTI5NTk4NTA1NjViNjAwMDgwNjAwMDgwNjAwMDYwYTA4Njg4MDMxMjE1NjEyYTJiNTc2MTJhMmE2MTIxYTU1NjViNWI2MDAwNjEyYTM5ODg4Mjg5MDE2MTIxZjg1NjViOTU1MDUwNjAyMDYxMmE0YTg4ODI4OTAxNjEyMWY4NTY1Yjk0NTA1MDYwNDA4NjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTJhNmI1NzYxMmE2YTYxMjFhYTU2NWI1YjYxMmE3Nzg4ODI4OTAxNjEyM2Y5NTY1YjkzNTA1MDYwNjA4NjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTJhOTg1NzYxMmE5NzYxMjFhYTU2NWI1YjYxMmFhNDg4ODI4OTAxNjEyM2Y5NTY1YjkyNTA1MDYwODA2MTJhYjU4ODgyODkwMTYxMjFmODU2NWI5MTUwNTA5Mjk1NTA5Mjk1OTA5MzUwNTY1YjYwMDA4MDYwNDA4Mzg1MDMxMjE1NjEyYWQ5NTc2MTJhZDg2MTIxYTU1NjViNWI2MDAwNjEyYWU3ODU4Mjg2MDE2MTIxZjg1NjViOTI1MDUwNjAyMDYxMmFmODg1ODI4NjAxNjEyMWY4NTY1YjkxNTA1MDkyNTA5MjkwNTA1NjViNjAwMDgwNjAwMDgwNjA4MDg1ODcwMzEyMTU2MTJiMWM1NzYxMmIxYjYxMjFhNTU2NWI1YjYwMDA2MTJiMmE4NzgyODgwMTYxMjFmODU2NWI5NDUwNTA2MDIwNjEyYjNiODc4Mjg4MDE2MTIxZjg1NjViOTM1MDUwNjA0MDg1MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMmI1YzU3NjEyYjViNjEyMWFhNTY1YjViNjEyYjY4ODc4Mjg4MDE2MTI4NzY1NjViOTI1MDUwNjA2MDg1MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMmI4OTU3NjEyYjg4NjEyMWFhNTY1YjViNjEyYjk1ODc4Mjg4MDE2MTI4NzY1NjViOTE1MDUwOTI5NTkxOTQ1MDkyNTA1NjViNjEyYmFhODE2MTIxY2Y1NjViODI1MjUwNTA1NjViNjEyYmI5ODE2MTIyMGQ1NjViODI1MjUwNTA1NjViNjAwMDYwODA4MjAxOTA1MDYxMmJkNDYwMDA4MzAxODc2MTJiYTE1NjViNjEyYmUxNjAyMDgzMDE4NjYxMmJhMTU2NWI2MTJiZWU2MDQwODMwMTg1NjEyYmExNTY1YjYxMmJmYjYwNjA4MzAxODQ2MTJiYjA1NjViOTU5NDUwNTA1MDUwNTA1NjViNjAwMDgxOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTJjMWE4MjYxMjU5NzU2NWI2MTJjMjQ4MTg1NjEyYzA0NTY1YjkzNTA2MTJjMzQ4MTg1NjAyMDg2MDE2MTI1YjM1NjViODA4NDAxOTE1MDUwOTI5MTUwNTA1NjViNjAwMDYxMmM0YzgyODQ2MTJjMGY1NjViOTE1MDgxOTA1MDkyOTE1MDUwNTY1YjYwMDA4MTYwMDMwYjkwNTA5MTkwNTA1NjViNjEyYzZkODE2MTJjNTc1NjViODExNDYxMmM3ODU3NjAwMDgwZmQ1YjUwNTY1YjYwMDA4MTUxOTA1MDYxMmM4YTgxNjEyYzY0NTY1YjkyOTE1MDUwNTY1YjYwMDA2MDIwODI4NDAzMTIxNTYxMmNhNjU3NjEyY2E1NjEyMWE1NTY1YjViNjAwMDYxMmNiNDg0ODI4NTAxNjEyYzdiNTY1YjkxNTA1MDkyOTE1MDUwNTY1YjdmNGU0ODdiNzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDA1MjYwMzI2MDA0NTI2MDI0NjAwMGZkNWI2MDAwODI4MjUyNjAyMDgyMDE5MDUwOTI5MTUwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNjY2MTY5NmM2NTY0MjEwMDAwMDAwMDAwNjAwMDgyMDE1MjUwNTY1YjYwMDA2MTJkMzM2MDFiODM2MTJjZWM1NjViOTE1MDYxMmQzZTgyNjEyY2ZkNTY1YjYwMjA4MjAxOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMmQ2MjgxNjEyZDI2NTY1YjkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTJkODU4MjYxMjU5NzU2NWI2MTJkOGY4MTg1NjEyZDY5NTY1YjkzNTA2MTJkOWY4MTg1NjAyMDg2MDE2MTI1YjM1NjViNjEyZGE4ODE2MTIyZWI1NjViODQwMTkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTJkYzg2MDAwODMwMTg1NjEyYmExNTY1YjgxODEwMzYwMjA4MzAxNTI2MTJkZGE4MTg0NjEyZDdhNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjYxMmRlYzgxNjEyNTZkNTY1YjgyNTI1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTJlMDc2MDAwODMwMTg1NjEyZGUzNTY1YjgxODEwMzYwMjA4MzAxNTI2MTJlMTk4MTg0NjEyZDdhNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjdmNTU3MDY0NjE3NDY1MjA2ZjY2MjA3NDZmNmI2NTZlMjA2YjY1Nzk3MzIwNjY2MTY5NmM2NTY0MjEwMDAwMDAwMDYwMDA4MjAxNTI1MDU2NWI2MDAwNjEyZTU4NjAxYzgzNjEyY2VjNTY1YjkxNTA2MTJlNjM4MjYxMmUyMjU2NWI2MDIwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDgxODEwMzYwMDA4MzAxNTI2MTJlODc4MTYxMmU0YjU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MTUxOTA1MDkxOTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDIyNjAwNDUyNjAyNDYwMDBmZDViNjAwMDYwMDI4MjA0OTA1MDYwMDE4MjE2ODA2MTJlZTA1NzYwN2Y4MjE2OTE1MDViNjAyMDgyMTA4MTAzNjEyZWYzNTc2MTJlZjI2MTJlOTk1NjViNWI1MDkxOTA1MDU2NWI2MDAwODE5MDUwODE2MDAwNTI2MDIwNjAwMDIwOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDYwMWY4MzAxMDQ5MDUwOTE5MDUwNTY1YjYwMDA4MjgyMWI5MDUwOTI5MTUwNTA1NjViNjAwMDYwMDg4MzAyNjEyZjViN2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODI2MTJmMWU1NjViNjEyZjY1ODY4MzYxMmYxZTU2NWI5NTUwODAxOTg0MTY5MzUwODA4NjE2ODQxNzkyNTA1MDUwOTM5MjUwNTA1MDU2NWI2MDAwODE5MDUwOTE5MDUwNTY1YjYwMDA2MTJmYTI2MTJmOWQ2MTJmOTg4NDYxMjIwZDU2NWI2MTJmN2Q1NjViNjEyMjBkNTY1YjkwNTA5MTkwNTA1NjViNjAwMDgxOTA1MDkxOTA1MDU2NWI2MTJmYmM4MzYxMmY4NzU2NWI2MTJmZDA2MTJmYzg4MjYxMmZhOTU2NWI4NDg0NTQ2MTJmMmI1NjViODI1NTUwNTA1MDUwNTY1YjYwMDA5MDU2NWI2MTJmZTU2MTJmZDg1NjViNjEyZmYwODE4NDg0NjEyZmIzNTY1YjUwNTA1MDU2NWI1YjgxODExMDE1NjEzMDE0NTc2MTMwMDk2MDAwODI2MTJmZGQ1NjViNjAwMTgxMDE5MDUwNjEyZmY2NTY1YjUwNTA1NjViNjAxZjgyMTExNTYxMzA1OTU3NjEzMDJhODE2MTJlZjk1NjViNjEzMDMzODQ2MTJmMGU1NjViODEwMTYwMjA4NTEwMTU2MTMwNDI1NzgxOTA1MDViNjEzMDU2NjEzMDRlODU2MTJmMGU1NjViODMwMTgyNjEyZmY1NTY1YjUwNTA1YjUwNTA1MDU2NWI2MDAwODI4MjFjOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTMwN2M2MDAwMTk4NDYwMDgwMjYxMzA1ZTU2NWIxOTgwODMxNjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MTMwOTU4MzgzNjEzMDZiNTY1YjkxNTA4MjYwMDIwMjgyMTc5MDUwOTI5MTUwNTA1NjViNjEzMGFlODI2MTJlOGU1NjViNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzMGM3NTc2MTMwYzY2MTIyZmM1NjViNWI2MTMwZDE4MjU0NjEyZWM4NTY1YjYxMzBkYzgyODI4NTYxMzAxODU2NWI2MDAwNjAyMDkwNTA2MDFmODMxMTYwMDE4MTE0NjEzMTBmNTc2MDAwODQxNTYxMzBmZDU3ODI4NzAxNTE5MDUwNWI2MTMxMDc4NTgyNjEzMDg5NTY1Yjg2NTU1MDYxMzE2ZjU2NWI2MDFmMTk4NDE2NjEzMTFkODY2MTJlZjk1NjViNjAwMDViODI4MTEwMTU2MTMxNDU1Nzg0ODkwMTUxODI1NTYwMDE4MjAxOTE1MDYwMjA4NTAxOTQ1MDYwMjA4MTAxOTA1MDYxMzEyMDU2NWI4NjgzMTAxNTYxMzE2MjU3ODQ4OTAxNTE2MTMxNWU2MDFmODkxNjgyNjEzMDZiNTY1YjgzNTU1MDViNjAwMTYwMDI4ODAyMDE4ODU1NTA1MDUwNWI1MDUwNTA1MDUwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNmI2NTc5NzMyMDY2NjE2OTZjNjU2NDIxNjA=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwvEToTPPAcXrDTTybLE6i0cSbtFYQvawW07MrI4jraTQVb6dd4rLgpzZLDuV2/vEoGgwIgeGerQYQk+/a/wIiDwoJCMXgnq0GENUHEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjG4J6tBhDbBxICGAISAhgDGPKjnj4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBgB8SAxj0ByL4HjAwODIwMTUyNTA1NjViNjAwMDYxMzFhZDYwMjA4MzYxMmNlYzU2NWI5MTUwNjEzMWI4ODI2MTMxNzc1NjViNjAyMDgyMDE5MDUwOTE5MDUwNTY1YjYwMDA2MDIwODIwMTkwNTA4MTgxMDM2MDAwODMwMTUyNjEzMWRjODE2MTMxYTA1NjViOTA1MDkxOTA1MDU2NWI3ZjU1NzA2NDYxNzQ2NTIwNmY2NjIwNzQ2ZjZiNjU2ZTQ5NmU2NjZmMmU3NDcyNjU2MTczNzU3Mjc5MjA2NjYxNjk2MDAwODIwMTUyN2Y2YzY1NjQyMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAyMDgyMDE1MjUwNTY1YjYwMDA2MTMyM2Y2MDI0ODM2MTJjZWM1NjViOTE1MDYxMzI0YTgyNjEzMWUzNTY1YjYwNDA4MjAxOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMzI2ZTgxNjEzMjMyNTY1YjkwNTA5MTkwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNmU2MTZkNjUyMDYxNmU2NDIwNzM3OTZkNjAwMDgyMDE1MjdmNjI2ZjZjMjA2NjYxNjk2YzY1NjQyMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMjA4MjAxNTI1MDU2NWI2MDAwNjEzMmQxNjAyYjgzNjEyY2VjNTY1YjkxNTA2MTMyZGM4MjYxMzI3NTU2NWI2MDQwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDgxODEwMzYwMDA4MzAxNTI2MTMzMDA4MTYxMzJjNDU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MjgyNTI2MDIwODIwMTkwNTA5MjkxNTA1MDU2NWI2MDAwNjEzMzIzODI2MTJlOGU1NjViNjEzMzJkODE4NTYxMzMwNzU2NWI5MzUwNjEzMzNkODE4NTYwMjA4NjAxNjEyNWIzNTY1YjYxMzM0NjgxNjEyMmViNTY1Yjg0MDE5MTUwNTA5MjkxNTA1MDU2NWI2MTMzNWE4MTYxMjJhYTU2NWI4MjUyNTA1MDU2NWI2MDAwODE1MTkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA4MTkwNTA2MDIwODIwMTkwNTA5MTkwNTA1NjViNjEzMzk1ODE2MTIyMGQ1NjViODI1MjUwNTA1NjViNjAwMDYwYTA4MzAxNjAwMDgzMDE1MTYxMzNiMzYwMDA4NjAxODI2MTI1Nzk1NjViNTA2MDIwODMwMTUxNjEzM2M2NjAyMDg2MDE4MjYxMjU4ODU2NWI1MDYwNDA4MzAxNTE4NDgyMDM2MDQwODYwMTUyNjEzM2RlODI4MjYxMjVkZDU2NWI5MTUwNTA2MDYwODMwMTUxODQ4MjAzNjA2MDg2MDE1MjYxMzNmODgyODI2MTI1ZGQ1NjViOTE1MDUwNjA4MDgzMDE1MTYxMzQwZDYwODA4NjAxODI2MTI1ODg1NjViNTA4MDkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODMwMTYwMDA4MzAxNTE2MTM0MzA2MDAwODYwMTgyNjEzMzhjNTY1YjUwNjAyMDgzMDE1MTg0ODIwMzYwMjA4NjAxNTI2MTM0NDg4MjgyNjEzMzliNTY1YjkxNTA1MDgwOTE1MDUwOTI5MTUwNTA1NjViNjAwMDYxMzQ2MTgzODM2MTM0MTg1NjViOTA1MDkyOTE1MDUwNTY1YjYwMDA2MDIwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYxMzQ4MTgyNjEzMzYwNTY1YjYxMzQ4YjgxODU2MTMzNmI1NjViOTM1MDgzNjAyMDgyMDI4NTAxNjEzNDlkODU2MTMzN2M1NjViODA2MDAwNWI4NTgxMTAxNTYxMzRkOTU3ODQ4NDAzODk1MjgxNTE2MTM0YmE4NTgyNjEzNDU1NTY1Yjk0NTA2MTM0YzU4MzYxMzQ2OTU2NWI5MjUwNjAyMDhhMDE5OTUwNTA2MDAxODEwMTkwNTA2MTM0YTE1NjViNTA4Mjk3NTA4Nzk1NTA1MDUwNTA1MDUwOTI5MTUwNTA1NjViNjA2MDgyMDE2MDAwODIwMTUxNjEzNTAxNjAwMDg1MDE4MjYxMzM1MTU2NWI1MDYwMjA4MjAxNTE2MTM1MTQ2MDIwODUwMTgyNjEyNTg4NTY1YjUwNjA0MDgyMDE1MTYxMzUyNzYwNDA4NTAxODI2MTMzNTE1NjViNTA1MDUwNTA1NjViNjAwMDYxMDE2MDgzMDE2MDAwODMwMTUxODQ4MjAzNjAwMDg2MDE1MjYxMzU0YjgyODI2MTMzMTg1NjViOTE1MDUwNjAyMDgzMDE1MTg0ODIwMzYwMjA4NjAxNTI2MTM1NjU4MjgyNjEzMzE4NTY1YjkxNTA1MDYwNDA4MzAxNTE2MTM1N2E2MDQwODYwMTgyNjEyNTg4NTY1YjUwNjA2MDgzMDE1MTg0ODIwMzYwNjA4NjAxNTI2MTM1OTI4MjgyNjEzMzE4NTY1YjkxNTA1MDYwODA4MzAxNTE2MTM1YTc2MDgwODYwMTgyNjEyNTc5NTY1YjUwNjBhMDgzMDE1MTYxMzViYTYwYTA4NjAxODI2MTMzNTE1NjViNTA2MGMwODMwMTUxNjEzNWNkNjBjMDg2MDE4MjYxMjU3OTU2NWI1MDYwZTA4MzAxNTE4NDgyMDM2MGUwODYwMTUyNjEzNWU1ODI4MjYxMzQ3NjU2NWI5MTUwNTA2MTAxMDA4MzAxNTE2MTM1ZmM2MTAxMDA4NjAxODI2MTM0ZWI1NjViNTA4MDkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTM2MWM2MDAwODMwMTg1NjEyYmExNTY1YjgxODEwMzYwMjA4MzAxNTI2MTM2MmU4MTg0NjEzNTJkNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTM2NGM2MDAwODMwMTg1NjEyYmExNTY1YjYxMzY1OTYwMjA4MzAxODQ2MTJiYjA1NjViOTM5MjUwNTA1MDU2NWI2MDAwODBmZDViNjAwMDgwZmQ1YjYxMzY3MzgxNjEyNTZkNTY1YjgxMTQ2MTM2N2U1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODE1MTkwNTA2MTM2OTA4MTYxMzY2YTU2NWI5MjkxNTA1MDU2NWI2MDAwODE1MTkwNTA2MTM2YTU4MTYxMjFlMTU2NWI5MjkxNTA1MDU2NWI2MDAwNjEzNmJlNjEzNmI5ODQ2MTIzNzc1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMzZkYTU3NjEzNmQ5NjEyMmU2NTY1YjViNjEzNmU1ODQ4Mjg1NjEyNWIzNTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEzNzAyNTc2MTM3MDE2MTIyZTE1NjViNWI4MTUxNjEzNzEyODQ4MjYwMjA4NjAxNjEzNmFiNTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MGEwODI4NDAzMTIxNTYxMzczMTU3NjEzNzMwNjEzNjYwNTY1YjViNjEzNzNiNjBhMDYxMjM1YzU2NWI5MDUwNjAwMDYxMzc0Yjg0ODI4NTAxNjEzNjgxNTY1YjYwMDA4MzAxNTI1MDYwMjA2MTM3NWY4NDgyODUwMTYxMzY5NjU2NWI2MDIwODMwMTUyNTA2MDQwODIwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzNzgzNTc2MTM3ODI2MTM2NjU1NjViNWI2MTM3OGY4NDgyODUwMTYxMzZlZDU2NWI2MDQwODMwMTUyNTA2MDYwODIwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzN2IzNTc2MTM3YjI2MTM2NjU1NjViNWI2MTM3YmY4NDgyODUwMTYxMzZlZDU2NWI2MDYwODMwMTUyNTA2MDgwNjEzN2QzODQ4Mjg1MDE2MTM2OTY1NjViNjA4MDgzMDE1MjUwOTI5MTUwNTA1NjViNjAwMDgwNjA0MDgzODUwMzEyMTU2MTM3ZjY1NzYxMzdmNTYxMjFhNTU2NWI1YjYwMDA2MTM4MDQ4NTgyODYwMTYxMmM3YjU2NWI5MjUwNTA2MDIwODMwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzODI1NTc2MTM4MjQ2MTIxYWE1NjViNWI2MTM4MzE4NTgyODYwMTYxMzcxYjU2NWI5MTUwNTA5MjUwOTI5MDUwNTY1YjYwMDA4MjgyNTI2MDIwODIwMTkwNTA5MjkxNTA1MDU2NWI2MDAwNjEzODU3ODI2MTMzNjA1NjViNjEzODYxODE4NTYxMzgzYjU2NWI5MzUwODM2MDIwODIwMjg1MDE2MTM4NzM4NTYxMzM3YzU2NWI4MDYwMDA1Yjg1ODExMDE1NjEzOGFmNTc4NDg0MDM4OTUyODE1MTYxMzg5MDg1ODI2MTM0NTU1NjViOTQ1MDYxMzg5YjgzNjEzNDY5NTY1YjkyNTA2MDIwOGEwMTk5NTA1MDYwMDE4MTAxOTA1MDYxMzg3NzU2NWI1MDgyOTc1MDg3OTU1MDUwNTA1MDUwNTA5MjkxNTA1MDU2NWI2MDAwNjA0MDgyMDE5MDUwNjEzOGQ2NjAwMDgzMDE4NTYxMmJhMTU2NWI4MTgxMDM2MDIwODMwMTUyNjEzOGU4ODE4NDYxMzg0YzU2NWI5MDUwOTM5MjUwNTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDIxNjAwNDUyNjAyNDYwMDBmZGZlYTI2NDY5NzA2NjczNTgyMjEyMjBlZDAxNzAwYmU1OTIxNmVjMDMxNjViOGEyMWZkOTVjY2NmOTQ3NmM1NmI5M2FiNTk5Nzg3MmE4ZGZkZWNiYTY0NjQ3MzZmNmM2MzQzMDAwODEyMDAzMw==","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwsVxbZxyZgDrvxoz9TixGzONvi4Zc6f52+SdtmaW13V7Q3ySt299pqy6CluOZC84JGgwIguGerQYQ686pnwEiDwoJCMbgnq0GENsHEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjG4J6tBhDdBxICGAISAhgDGJb7rp0CIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5CRgoDGPQHGiISIL3fOlVtJMXaxiOg8DNWoQwOozkXPIQuH8XbgRZzDDkDIICS9AFCBQiAztoDUgBaAGoLY2VsbGFyIGRvb3I=","b64Record":"CiUIFiIDGPUHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjCpvkZW2uh4U56AVCp9zgji/QLh/vdjIYRBjcQucfwxxWRStXG7c2uerKd19eJgB0kaDAiC4Z6tBhDbr4KFAyIPCgkIxuCerQYQ3QcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjjCAmKtsQox1CgMY9QcS1nJggGBAUmAENhBhAMJXYAA1YOAcgGN9y7xyEWEAf1eAY+rG8/4RYQBZV4Bj6sbz/hRhAlRXgGPrVI7/FGECkVeAY/AHKZAUYQKtV4Bj8XMHYBRhAslXYQDCVluAY33LvHIUYQHfV4BjmyPT2RRhAftXgGPmCOGNFGECOFdhAMJWW4BjEeH8BxRhAMdXgGMV2svqFGEBBFeAYzqhLvUUYQFBV4BjQSm32xRhAV1XgGNhjcZeFGEBmleAY3j+cqEUYQHDV1tgAID9WzSAFWEA01dgAID9W1BhAO5gBIA2A4EBkGEA6ZGQYSJDVlthAuVWW2BAUWEA+5GQYSLGVltgQFGAkQOQ81s0gBVhARBXYACA/VtQYQErYASANgOBAZBhASaRkGEiQ1ZbYQQBVltgQFFhATiRkGEixlZbYEBRgJEDkPNbYQFbYASANgOBAZBhAVaRkGEkU1ZbYQUfVlsAWzSAFWEBaVdgAID9W1BhAYRgBIA2A4EBkGEBf5GQYSUtVlthB3BWW2BAUWEBkZGQYSaTVltgQFGAkQOQ81s0gBVhAaZXYACA/VtQYQHBYASANgOBAZBhAbyRkGEmtVZbYQenVlsAW2EB3WAEgDYDgQGQYQHYkZBhJxFWW2EIzlZbAFthAflgBIA2A4EBkGEB9JGQYSewVlthCmVWWwBbNIAVYQIHV2AAgP1bUGECImAEgDYDgQGQYQIdkZBhIkNWW2ELhVZbYEBRYQIvkZBhIsZWW2BAUYCRA5DzW2ECUmAEgDYDgQGQYQJNkZBhKKRWW2EMo1ZbAFs0gBVhAmBXYACA/VtQYQJ7YASANgOBAZBhAnaRkGEiQ1ZbYQ6AVltgQFFhAoiRkGEixlZbYEBRgJEDkPNbYQKrYASANgOBAZBhAqaRkGEqD1ZbYQ+cVlsAW2ECx2AEgDYDgQGQYQLCkZBhKsJWW2ETNFZbAFthAuNgBIA2A4EBkGEC3pGQYSsCVlthFYdWWwBbYACAYABhAWdz//////////////////////////8WY5sj09lg4BuIiIiIYEBRYCQBYQMilJOSkZBhK79WW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEDjJGQYSxAVltgAGBAUYCDA4GFWvSRUFA9gGAAgRRhA8dXYEBRkVBgHxlgPz0BFoIBYEBSPYJSPWAAYCCEAT5hA8xWW2BgkVBbUJFQkVCBYQPdV2AVYQPyVluAgGAgAZBRgQGQYQPxkZBhLJBWW1tgAwuSUFBQlJNQUFBQVltgAIBgAGEBZ3P//////////////////////////xZjFdrL6mDgG4iIiIhgQFFgJAFhBD6Uk5KRkGErv1ZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYQSokZBhLEBWW2AAYEBRgIMDgWAAhlrxkVBQPYBgAIEUYQTlV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YQTqVltgYJFQW1CRUJFQgWEE+1dgFWEFEFZbgIBgIAGQUYEBkGEFD5GQYSyQVltbYAMLklBQUJSTUFBQUFZbYABgBWf//////////4ERFWEFPFdhBTthIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhBXVXgWAgAVthBWJhIHJWW4FSYCABkGABkAOQgWEFWleQUFtQkFBhBYdgAGABYAKJYRbIVluBYACBUYEQYQWbV2EFmmEsvVZbW2AgAmAgAQGBkFJQYQW0YAJgA4CIYRbIVluBYAGBUYEQYQXIV2EFx2EsvVZbW2AgAmAgAQGBkFJQYQXgYARgAYZhFwFWW4FgAoFRgRBhBfRXYQXzYSy9VltbYCACYCABAYGQUlBhBgxgBmABhmEXAVZbgWADgVGBEGEGIFdhBh9hLL1WW1tgIAJgIAEBgZBSUGEGOGAFYASGYRcBVluBYASBUYEQYQZMV2EGS2EsvVZbW2AgAmAgAQGBkFJQYQZfYSCSVltgAIFgAAGQYAcLkIFgBwuBUlBQg4FgIAGQc///////////////////////////FpCBc///////////////////////////FoFSUFCCgWBAAZBgBwuQgWAHC4FSUFBhBsRhIM9WW4iBYEABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQgoFg4AGBkFJQgYFhAQABgZBSUGAAYQcbi4NhFzhWW5BQYBZgAwuBFGEHY1dgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEHWpBhLUlWW2BAUYCRA5D9W1BQUFBQUFBQUFBQVlthB3hhIT5WW2AAgGEHhYWFYRhQVluRUGAHC5FQYBZgAwuCFGEHnFdgAID9W4CSUFBQkpFQUFZbYACAYQFnc///////////////////////////FmNhjcZeYOAbhYVgQFFgJAFhB96SkZBhLbNWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEISJGQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEIhVdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEIilZbYGCRUFtQkVCRUH9K9HgOBv6Mud9ksHlPpvATma+XkXW7mI414OV+WUVnvIKCYEBRYQjAkpGQYS3yVltgQFGAkQOQoVBQUFBWW2AAYAVn//////////+BERVhCOtXYQjqYSL8VltbYEBRkICCUoBgIAJgIAGCAWBAUoAVYQkkV4FgIAFbYQkRYSByVluBUmAgAZBgAZADkIFhCQlXkFBbUJBQYQk2YABgAWACh2EWyFZbgWAAgVGBEGEJSldhCUlhLL1WW1tgIAJgIAEBgZBSUGEJY2ACYAOAhmEWyFZbgWABgVGBEGEJd1dhCXZhLL1WW1tgIAJgIAEBgZBSUGEJj2AEYAGEYRcBVluBYAKBUYEQYQmjV2EJomEsvVZbW2AgAmAgAQGBkFJQYQm7YAZgAYRhFwFWW4FgA4FRgRBhCc9XYQnOYSy9VltbYCACYCABAYGQUlBhCedgBWAEhGEXAVZbgWAEgVGBEGEJ+1dhCfphLL1WW1tgIAJgIAEBgZBSUGAAYQoShoNhGYRWW2AHC5BQYBZgAwuBFGEKXVdgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEKVJBhLm5WW2BAUYCRA5D9W1BQUFBQUFZbYABgAWf//////////4ERFWEKgldhCoFhIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhCrtXgWAgAVthCqhhIHJWW4FSYCABkGABkAOQgWEKoFeQUFtQkFBhCsZhIHJWW2AEgWAAAYGBUlBQYQrZYSE+VlswgWAgAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGABgWAAAZAVFZCBFRWBUlBQgIJgIAGBkFJQgYNgAIFRgRBhC0BXYQs/YSy9VltbYCACYCABAYGQUlBgAGELWzBgAIiIiGEanFZbkFBgAGELaYiDYRc4VluQUGAWYAMLgRRhC3tXYACA/VtQUFBQUFBQUFZbYACAYABhAWdz//////////////////////////8WY5sj09lg4BuIiIiIYEBRYCQBYQvClJOSkZBhK79WW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEMLJGQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEMaVdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEMblZbYGCRUFtQkVCRUIFhDH9XYBVhDJRWW4CAYCABkFGBAZBhDJORkGEskFZbW2ADC5JQUFCUk1BQUFBWW2AAYAVn//////////+BERVhDMBXYQy/YSL8VltbYEBRkICCUoBgIAJgIAGCAWBAUoAVYQz5V4FgIAFbYQzmYSByVluBUmAgAZBgAZADkIFhDN5XkFBbUJBQYQ0LYABgAWACjGEWyFZbgWAAgVGBEGENH1dhDR5hLL1WW1tgIAJgIAEBgZBSUGENOGACYAOAi2EWyFZbgWABgVGBEGENTFdhDUthLL1WW1tgIAJgIAEBgZBSUGENZGAEYAGJYRcBVluBYAKBUYEQYQ14V2ENd2EsvVZbW2AgAmAgAQGBkFJQYQ2QYAZgAYlhFwFWW4FgA4FRgRBhDaRXYQ2jYSy9VltbYCACYCABAYGQUlBhDbxgBWAEiWEXAVZbgWAEgVGBEGEN0FdhDc9hLL1WW1tgIAJgIAEBgZBSUINgApCBYQ3qkZBhMKVWW1CCYAOQgWEN+pGQYTClVltQgWAEkIFhDgqRkGEwpVZbUGAAYQ4bi2AAiYmGYRqcVluQUGAAYQ4pjYNhFzhWW5BQYBZgAwuBFGEOcVdgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEOaJBhLUlWW2BAUYCRA5D9W1BQUFBQUFBQUFBQUFBWW2AAgGAAYQFnc///////////////////////////FmMV2svqYOAbiIiIiGBAUWAkAWEOvZSTkpGQYSu/VltgQFFgIIGDAwOBUpBgQFKQe/////////////////////////////////////8ZFmAgggGAUXv/////////////////////////////////////g4GDFheDUlBQUFBgQFFhDyeRkGEsQFZbYABgQFGAgwOBhVr0kVBQPYBgAIEUYQ9iV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YQ9nVltgYJFQW1CRUJFQgWEPeFdgFWEPjVZbgIBgIAGQUYEBkGEPjJGQYSyQVltbYAMLklBQUJSTUFBQUFZbYABgBWf//////////4ERFWEPuVdhD7hhIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhD/JXgWAgAVthD99hIHJWW4FSYCABkGABkAOQgWEP11eQUFtQkFBhEARgAGABYAKHYRbIVluBYACBUYEQYRAYV2EQF2EsvVZbW2AgAmAgAQGBkFJQYRAxYAJgA4CGYRbIVluBYAGBUYEQYRBFV2EQRGEsvVZbW2AgAmAgAQGBkFJQYRBdYARgAYRhFwFWW4FgAoFRgRBhEHFXYRBwYSy9VltbYCACYCABAYGQUlBhEIlgBmABhGEXAVZbgWADgVGBEGEQnVdhEJxhLL1WW1tgIAJgIAEBgZBSUGEQtWAFYASEYRcBVluBYASBUYEQYRDJV2EQyGEsvVZbW2AgAmAgAQGBkFJQYRDcYSDPVluFgWBAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGACgFRhESGQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGERTZBhLshWW4AVYRGaV4BgHxBhEW9XYQEAgINUBAKDUpFgIAGRYRGaVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWERfVeCkANgHxaCAZFbUFBQUFCBYAABgZBSUGADgFRhEbSQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGER4JBhLshWW4AVYRItV4BgHxBhEgJXYQEAgINUBAKDUpFgIAGRYRItVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWESEFeCkANgHxaCAZFbUFBQUFCBYCABgZBSUIGBYOABgZBSUGAEgFRhElCQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGESfJBhLshWW4AVYRLJV4BgHxBhEp5XYQEAgINUBAKDUpFgIAGRYRLJVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWESrFeCkANgHxaCAZFbUFBQUFCBYGABgZBSUGAAYRLiiINhFzhWW5BQYBZgAwuBFGETKldgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWETIZBhMcNWW2BAUYCRA5D9W1BQUFBQUFBQVlthEzxhIM9WW2ACgFRhE0mQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGETdZBhLshWW4AVYRPCV4BgHxBhE5dXYQEAgINUBAKDUpFgIAGRYRPCVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWETpVeCkANgHxaCAZFbUFBQUFCBYAABgZBSUGADgFRhE9yQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGEUCJBhLshWW4AVYRRVV4BgHxBhFCpXYQEAgINUBAKDUpFgIAGRYRRVVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWEUOFeCkANgHxaCAZFbUFBQUFCBYCABgZBSUIGBYEABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQYASAVGEUp5BhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRTTkGEuyFZbgBVhFSBXgGAfEGEU9VdhAQCAg1QEAoNSkWAgAZFhFSBWW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYRUDV4KQA2AfFoIBkVtQUFBQUIFgYAGBkFJQYABhFTmEg2EXOFZbkFBgFmADC4EUYRWBV2BAUX8Iw3mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFSYAQBYRV4kGEyVVZbYEBRgJEDkP1bUFBQUFZbYRWPYSDPVluCgWAAAYGQUlCBgWAgAYGQUlCDgWBAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGAEgFRhFeaQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGEWEpBhLshWW4AVYRZfV4BgHxBhFjRXYQEAgINUBAKDUpFgIAGRYRZfVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWEWQleCkANgHxaCAZFbUFBQUFCBYGABgZBSUGAAYRZ4hoNhFzhWW5BQYBZgAwuBFGEWwFdgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEWt5BhMudWW2BAUYCRA5D9W1BQUFBQUFZbYRbQYSByVltgQFGAYEABYEBSgGEW5YeHYR0WVluBUmAgAWEW9IWFYR1sVluBUlCQUJSTUFBQUFZbYRcJYSByVltgQFGAYEABYEBSgGEXHYZhH0FWW4FSYCABYRcshYVhH4JWW4FSUJBQk5JQUFBWW2AAgGAAYQFnc///////////////////////////FmN9MFz6YOAbhoZgQFFgJAFhF3GSkZBhNgdWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEX25GQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEYGFdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEYHVZbYGCRUFtQkVCRUIFhGC5XYBVhGENWW4CAYCABkFGBAZBhGEKRkGEskFZbW2ADC5JQUFCSkVBQVltgAGEYWmEhPlZbYACAYQFnc///////////////////////////FmM8TdMuYOAbh4dgQFFgJAFhGJGSkZBhNjdWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEY+5GQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEZOFdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEZPVZbYGCRUFtQkVCRUGEZSmEhPlZbgmEZV1dgFYFhGWxWW4GAYCABkFGBAZBhGWuRkGE331ZbW4FgAwuRUICVUIGWUFBQUFBQklCSkFBWW2AAgGAAYQFnc///////////////////////////FmNvw8uvYOAbhoZgQFFgJAFhGb2SkZBhOMFWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEaJ5GQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEaZFdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEaaVZbYGCRUFtQkVCRUIFhGnpXYBVhGo9WW4CAYCABkFGBAZBhGo6RkGEskFZbW2ADC5JQUFCSkVBQVlthGqRhIM9WW2EarGEgklZbhYFgAAGQYAcLkIFgBwuBUlBQhIFgIAGQc///////////////////////////FpCBc///////////////////////////FoFSUFCDgWBAAZBgBwuQgWAHC4FSUFBgAoBUYRsVkGEuyFZbgGAfAWAggJEEAmAgAWBAUZCBAWBAUoCSkZCBgVJgIAGCgFRhG0GQYS7IVluAFWEbjleAYB8QYRtjV2EBAICDVAQCg1KRYCABkWEbjlZbggGRkGAAUmAgYAAgkFuBVIFSkGABAZBgIAGAgxFhG3FXgpADYB8WggGRW1BQUFBQgmAAAYGQUlBgA4BUYRuokGEuyFZbgGAfAWAggJEEAmAgAWBAUZCBAWBAUoCSkZCBgVJgIAGCgFRhG9SQYS7IVluAFWEcIVeAYB8QYRv2V2EBAICDVAQCg1KRYCABkWEcIVZbggGRkGAAUmAgYAAgkFuBVIFSkGABAZBgIAGAgxFhHARXgpADYB8WggGRW1BQUFBQgmAgAYGQUlCGgmBAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUIKCYOABgZBSUICCYQEAAYGQUlBgBIBUYRyGkGEuyFZbgGAfAWAggJEEAmAgAWBAUZCBAWBAUoCSkZCBgVJgIAGCgFRhHLKQYS7IVluAFWEc/1eAYB8QYRzUV2EBAICDVAQCg1KRYCABkWEc/1ZbggGRkGAAUmAgYAAgkFuBVIFSkGABAZBgIAGAgxFhHOJXgpADYB8WggGRW1BQUFBQgmBgAYGQUlBQlZRQUFBQUFZbYABhHT2DYAaBERVhHS5XYR0tYTjxVltbgmEgXpCRkGP/////FlZbkFBhHWSCYAaBERVhHVVXYR1UYTjxVltbgmEgXpCRkGP/////FlZbkFCSkVBQVlthHXRhIT5WW2AAYASBERVhHYhXYR2HYTjxVltbg2AEgREVYR2bV2EdmmE48VZbWwNhHbZXYAGBYAABkBUVkIEVFYFSUFBhHztWW2ABYASBERVhHcpXYR3JYTjxVltbg2AEgREVYR3dV2Ed3GE48VZbWwNhHj9XYACAVJBhAQAKkARz//////////////////////////8WgWAgAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGEfOlZbYAJgBIERFWEeU1dhHlJhOPFWW1uDYASBERVhHmZXYR5lYTjxVltbA2EeeVeBgWBAAYGQUlBhHzlWW2ADYASBERVhHo1XYR6MYTjxVltbg2AEgREVYR6gV2Een2E48VZbWwNhHrNXgYFgYAGBkFJQYR84VltgBICBERVhHsZXYR7FYTjxVltbg2AEgREVYR7ZV2Ee2GE48VZbWwNhHzdXYACAVJBhAQAKkARz//////////////////////////8WgWCAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUFtbW1tbkpFQUFZbYABgAWAAg2AGgREVYR9aV2EfWWE48VZbW2AGgREVYR9sV2Efa2E48VZbW4FSYCABkIFSYCABYAAgVJBQkZBQVlthH4phIT5WW2ABYASBERVhH55XYR+dYTjxVltbg2AEgREVYR+xV2EfsGE48VZbWwNhH/NXgYFgIAGQc///////////////////////////FpCBc///////////////////////////FoFSUFBhIFhWW2AEgIERFWEgBldhIAVhOPFWW1uDYASBERVhIBlXYSAYYTjxVltbA2EgV1eBgWCAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUFtbkpFQUFZbYACBYP8WYAGQG4MXkFCSkVBQVltgQFGAYEABYEBSgGAAgVJgIAFhIIxhIT5WW4FSUJBWW2BAUYBgYAFgQFKAYABgBwuBUmAgAWAAc///////////////////////////FoFSYCABYABgBwuBUlCQVltgQFGAYQEgAWBAUoBgYIFSYCABYGCBUmAgAWAAc///////////////////////////FoFSYCABYGCBUmAgAWAAFRWBUmAgAWAAYAcLgVJgIAFgABUVgVJgIAFgYIFSYCABYSE4YSCSVluBUlCQVltgQFGAYKABYEBSgGAAFRWBUmAgAWAAc///////////////////////////FoFSYCABYGCBUmAgAWBggVJgIAFgAHP//////////////////////////xaBUlCQVltgAGBAUZBQkFZbYACA/VtgAID9W2AAc///////////////////////////ghaQUJGQUFZbYABhIdqCYSGvVluQUJGQUFZbYSHqgWEhz1ZbgRRhIfVXYACA/VtQVltgAIE1kFBhIgeBYSHhVluSkVBQVltgAIGQUJGQUFZbYSIggWEiDVZbgRRhIitXYACA/VtQVltgAIE1kFBhIj2BYSIXVluSkVBQVltgAIBgAIBggIWHAxIVYSJdV2EiXGEhpVZbW2AAYSJrh4KIAWEh+FZblFBQYCBhInyHgogBYSH4VluTUFBgQGEijYeCiAFhIfhWW5JQUGBgYSKeh4KIAWEiLlZbkVBQkpWRlFCSUFZbYACBYAcLkFCRkFBWW2EiwIFhIqpWW4JSUFBWW2AAYCCCAZBQYSLbYACDAYRhIrdWW5KRUFBWW2AAgP1bYACA/VtgAGAfGWAfgwEWkFCRkFBWW39OSHtxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAUmBBYARSYCRgAP1bYSM0gmEi61ZbgQGBgRBn//////////+CERcVYSNTV2EjUmEi/FZbW4BgQFJQUFBWW2AAYSNmYSGbVluQUGEjcoKCYSMrVluRkFBWW2AAZ///////////ghEVYSOSV2EjkWEi/FZbW2Ejm4JhIutWW5BQYCCBAZBQkZBQVluCgYM3YACDgwFSUFBQVltgAGEjymEjxYRhI3dWW2EjXFZbkFCCgVJgIIEBhISEAREVYSPmV2Ej5WEi5lZbW2Ej8YSChWEjqFZbUJOSUFBQVltgAIJgH4MBEmEkDldhJA1hIuFWW1uBNWEkHoSCYCCGAWEjt1ZbkVBQkpFQUFZbYSQwgWEiqlZbgRRhJDtXYACA/VtQVltgAIE1kFBhJE2BYSQnVluSkVBQVltgAIBgAIBgAIBgAGDgiIoDEhVhJHJXYSRxYSGlVltbYABhJICKgosBYSH4VluXUFBgIGEkkYqCiwFhIfhWW5ZQUGBAiAE1Z///////////gREVYSSyV2EksWEhqlZbW2EkvoqCiwFhI/lWW5VQUGBgiAE1Z///////////gREVYSTfV2Ek3mEhqlZbW2Ek64qCiwFhI/lWW5RQUGCAYST8ioKLAWEh+FZbk1BQYKBhJQ2KgosBYSH4VluSUFBgwGElHoqCiwFhJD5WW5FQUJKVmJGUl1CSlVBWW2AAgGBAg4UDEhVhJURXYSVDYSGlVltbYABhJVKFgoYBYSH4VluSUFBgIGElY4WChgFhIi5WW5FQUJJQkpBQVltgAIEVFZBQkZBQVlthJYKBYSVtVluCUlBQVlthJZGBYSHPVluCUlBQVltgAIFRkFCRkFBWW2AAgoJSYCCCAZBQkpFQUFZbYABbg4EQFWEl0VeAggFRgYQBUmAggQGQUGEltlZbYACEhAFSUFBQUFZbYABhJeiCYSWXVlthJfKBhWElolZbk1BhJgKBhWAghgFhJbNWW2EmC4FhIutWW4QBkVBQkpFQUFZbYABgoIMBYACDAVFhJi5gAIYBgmEleVZbUGAggwFRYSZBYCCGAYJhJYhWW1BgQIMBUYSCA2BAhgFSYSZZgoJhJd1WW5FQUGBggwFRhIIDYGCGAVJhJnOCgmEl3VZbkVBQYICDAVFhJohggIYBgmEliFZbUICRUFCSkVBQVltgAGAgggGQUIGBA2AAgwFSYSatgYRhJhZWW5BQkpFQUFZbYACAYECDhQMSFWEmzFdhJsthIaVWW1tgAGEm2oWChgFhIfhWW5JQUGAggwE1Z///////////gREVYSb7V2Em+mEhqlZbW2EnB4WChgFhI/lWW5FQUJJQkpBQVltgAIBgAIBggIWHAxIVYScrV2EnKmEhpVZbW2AAYSc5h4KIAWEh+FZblFBQYCCFATVn//////////+BERVhJ1pXYSdZYSGqVltbYSdmh4KIAWEj+VZbk1BQYECFATVn//////////+BERVhJ4dXYSeGYSGqVltbYSeTh4KIAWEj+VZbklBQYGBhJ6SHgogBYSH4VluRUFCSlZGUUJJQVltgAIBgAGBghIYDEhVhJ8lXYSfIYSGlVltbYABhJ9eGgocBYSH4VluTUFBgIGEn6IaChwFhIfhWW5JQUGBAYSf5hoKHAWEkPlZbkVBQklCSUJJWW2AAZ///////////ghEVYSgeV2EoHWEi/FZbW2EoJ4JhIutWW5BQYCCBAZBQkZBQVltgAGEoR2EoQoRhKANWW2EjXFZbkFCCgVJgIIEBhISEAREVYShjV2EoYmEi5lZbW2EoboSChWEjqFZbUJOSUFBQVltgAIJgH4MBEmEoi1dhKIphIuFWW1uBNWEom4SCYCCGAWEoNFZbkVBQkpFQUFZbYACAYACAYACAYACAYACAYQFAi40DEhVhKMhXYSjHYSGlVltbYABhKNaNgo4BYSH4VluaUFBgIGEo542CjgFhIfhWW5lQUGBAiwE1Z///////////gREVYSkIV2EpB2EhqlZbW2EpFI2CjgFhI/lWW5hQUGBgiwE1Z///////////gREVYSk1V2EpNGEhqlZbW2EpQY2CjgFhI/lWW5dQUGCAYSlSjYKOAWEh+FZbllBQYKBhKWONgo4BYSH4VluVUFBgwGEpdI2CjgFhJD5WW5RQUGDgiwE1Z///////////gREVYSmVV2EplGEhqlZbW2EpoY2CjgFhKHZWW5NQUGEBAIsBNWf//////////4ERFWEpw1dhKcJhIapWW1thKc+Ngo4BYSh2VluSUFBhASCLATVn//////////+BERVhKfFXYSnwYSGqVltbYSn9jYKOAWEodlZbkVBQkpWYm5GUl5pQkpWYUFZbYACAYACAYABgoIaIAxIVYSorV2EqKmEhpVZbW2AAYSo5iIKJAWEh+FZblVBQYCBhKkqIgokBYSH4VluUUFBgQIYBNWf//////////4ERFWEqa1dhKmphIapWW1thKneIgokBYSP5VluTUFBgYIYBNWf//////////4ERFWEqmFdhKpdhIapWW1thKqSIgokBYSP5VluSUFBggGEqtYiCiQFhIfhWW5FQUJKVUJKVkJNQVltgAIBgQIOFAxIVYSrZV2Eq2GEhpVZbW2AAYSrnhYKGAWEh+FZbklBQYCBhKviFgoYBYSH4VluRUFCSUJKQUFZbYACAYACAYICFhwMSFWErHFdhKxthIaVWW1tgAGErKoeCiAFhIfhWW5RQUGAgYSs7h4KIAWEh+FZbk1BQYECFATVn//////////+BERVhK1xXYStbYSGqVltbYStoh4KIAWEodlZbklBQYGCFATVn//////////+BERVhK4lXYSuIYSGqVltbYSuVh4KIAWEodlZbkVBQkpWRlFCSUFZbYSuqgWEhz1ZbglJQUFZbYSu5gWEiDVZbglJQUFZbYABggIIBkFBhK9RgAIMBh2EroVZbYSvhYCCDAYZhK6FWW2Er7mBAgwGFYSuhVlthK/tgYIMBhGErsFZblZRQUFBQUFZbYACBkFCSkVBQVltgAGEsGoJhJZdWW2EsJIGFYSwEVluTUGEsNIGFYCCGAWEls1ZbgIQBkVBQkpFQUFZbYABhLEyChGEsD1ZbkVCBkFCSkVBQVltgAIFgAwuQUJGQUFZbYSxtgWEsV1ZbgRRhLHhXYACA/VtQVltgAIFRkFBhLIqBYSxkVluSkVBQVltgAGAggoQDEhVhLKZXYSylYSGlVltbYABhLLSEgoUBYSx7VluRUFCSkVBQVlt/Tkh7cQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAFJgMmAEUmAkYAD9W2AAgoJSYCCCAZBQkpFQUFZbf1VwZGF0ZSBvZiB0b2tlbkluZm8gZmFpbGVkIQAAAAAAYACCAVJQVltgAGEtM2Abg2Es7FZbkVBhLT6CYSz9VltgIIIBkFCRkFBWW2AAYCCCAZBQgYEDYACDAVJhLWKBYS0mVluQUJGQUFZbYACCglJgIIIBkFCSkVBQVltgAGEthYJhJZdWW2Etj4GFYS1pVluTUGEtn4GFYCCGAWEls1ZbYS2ogWEi61ZbhAGRUFCSkVBQVltgAGBAggGQUGEtyGAAgwGFYSuhVluBgQNgIIMBUmEt2oGEYS16VluQUJOSUFBQVlthLeyBYSVtVluCUlBQVltgAGBAggGQUGEuB2AAgwGFYS3jVluBgQNgIIMBUmEuGYGEYS16VluQUJOSUFBQVlt/VXBkYXRlIG9mIHRva2VuIGtleXMgZmFpbGVkIQAAAABgAIIBUlBWW2AAYS5YYByDYSzsVluRUGEuY4JhLiJWW2AgggGQUJGQUFZbYABgIIIBkFCBgQNgAIMBUmEuh4FhLktWW5BQkZBQVltgAIFRkFCRkFBWW39OSHtxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAUmAiYARSYCRgAP1bYABgAoIEkFBgAYIWgGEu4Fdgf4IWkVBbYCCCEIEDYS7zV2Eu8mEumVZbW1CRkFBWW2AAgZBQgWAAUmAgYAAgkFCRkFBWW2AAYCBgH4MBBJBQkZBQVltgAIKCG5BQkpFQUFZbYABgCIMCYS9bf///////////////////////////////////////////gmEvHlZbYS9lhoNhLx5WW5VQgBmEFpNQgIYWhBeSUFBQk5JQUFBWW2AAgZBQkZBQVltgAGEvomEvnWEvmIRhIg1WW2EvfVZbYSINVluQUJGQUFZbYACBkFCRkFBWW2EvvINhL4dWW2Ev0GEvyIJhL6lWW4SEVGEvK1ZbglVQUFBQVltgAJBWW2Ev5WEv2FZbYS/wgYSEYS+zVltQUFBWW1uBgRAVYTAUV2EwCWAAgmEv3VZbYAGBAZBQYS/2VltQUFZbYB+CERVhMFlXYTAqgWEu+VZbYTAzhGEvDlZbgQFgIIUQFWEwQleBkFBbYTBWYTBOhWEvDlZbgwGCYS/1VltQUFtQUFBWW2AAgoIckFCSkVBQVltgAGEwfGAAGYRgCAJhMF5WWxmAgxaRUFCSkVBQVltgAGEwlYODYTBrVluRUIJgAgKCF5BQkpFQUFZbYTCugmEujlZbZ///////////gREVYTDHV2EwxmEi/FZbW2Ew0YJUYS7IVlthMNyCgoVhMBhWW2AAYCCQUGAfgxFgAYEUYTEPV2AAhBVhMP1XgocBUZBQW2ExB4WCYTCJVluGVVBhMW9WW2AfGYQWYTEdhmEu+VZbYABbgoEQFWExRVeEiQFRglVgAYIBkVBgIIUBlFBgIIEBkFBhMSBWW4aDEBVhMWJXhIkBUWExXmAfiRaCYTBrVluDVVBbYAFgAogCAYhVUFBQW1BQUFBQUFZbf1VwZGF0ZSBvZiB0b2tlbkluZm8ga2V5cyBmYWlsZWQhYACCAVJQVltgAGExrWAgg2Es7FZbkVBhMbiCYTF3VltgIIIBkFCRkFBWW2AAYCCCAZBQgYEDYACDAVJhMdyBYTGgVluQUJGQUFZbf1VwZGF0ZSBvZiB0b2tlbkluZm8udHJlYXN1cnkgZmFpYACCAVJ/bGVkIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgIIIBUlBWW2AAYTI/YCSDYSzsVluRUGEySoJhMeNWW2BAggGQUJGQUFZbYABgIIIBkFCBgQNgAIMBUmEyboFhMjJWW5BQkZBQVlt/VXBkYXRlIG9mIHRva2VuSW5mbyBuYW1lIGFuZCBzeW1gAIIBUn9ib2wgZmFpbGVkIQAAAAAAAAAAAAAAAAAAAAAAAAAAAGAgggFSUFZbYABhMtFgK4NhLOxWW5FQYTLcgmEydVZbYECCAZBQkZBQVltgAGAgggGQUIGBA2AAgwFSYTMAgWEyxFZbkFCRkFBWW2AAgoJSYCCCAZBQkpFQUFZbYABhMyOCYS6OVlthMy2BhWEzB1Zbk1BhMz2BhWAghgFhJbNWW2EzRoFhIutWW4QBkVBQkpFQUFZbYTNagWEiqlZbglJQUFZbYACBUZBQkZBQVltgAIKCUmAgggGQUJKRUFBWW2AAgZBQYCCCAZBQkZBQVlthM5WBYSINVluCUlBQVltgAGCggwFgAIMBUWEzs2AAhgGCYSV5VltQYCCDAVFhM8ZgIIYBgmEliFZbUGBAgwFRhIIDYECGAVJhM96CgmEl3VZbkVBQYGCDAVGEggNgYIYBUmEz+IKCYSXdVluRUFBggIMBUWE0DWCAhgGCYSWIVltQgJFQUJKRUFBWW2AAYECDAWAAgwFRYTQwYACGAYJhM4xWW1BgIIMBUYSCA2AghgFSYTRIgoJhM5tWW5FQUICRUFCSkVBQVltgAGE0YYODYTQYVluQUJKRUFBWW2AAYCCCAZBQkZBQVltgAGE0gYJhM2BWW2E0i4GFYTNrVluTUINgIIIChQFhNJ2FYTN8VluAYABbhYEQFWE02VeEhAOJUoFRYTS6hYJhNFVWW5RQYTTFg2E0aVZbklBgIIoBmVBQYAGBAZBQYTShVltQgpdQh5VQUFBQUFCSkVBQVltgYIIBYACCAVFhNQFgAIUBgmEzUVZbUGAgggFRYTUUYCCFAYJhJYhWW1BgQIIBUWE1J2BAhQGCYTNRVltQUFBQVltgAGEBYIMBYACDAVGEggNgAIYBUmE1S4KCYTMYVluRUFBgIIMBUYSCA2AghgFSYTVlgoJhMxhWW5FQUGBAgwFRYTV6YECGAYJhJYhWW1BgYIMBUYSCA2BghgFSYTWSgoJhMxhWW5FQUGCAgwFRYTWnYICGAYJhJXlWW1BgoIMBUWE1umCghgGCYTNRVltQYMCDAVFhNc1gwIYBgmEleVZbUGDggwFRhIIDYOCGAVJhNeWCgmE0dlZbkVBQYQEAgwFRYTX8YQEAhgGCYTTrVltQgJFQUJKRUFBWW2AAYECCAZBQYTYcYACDAYVhK6FWW4GBA2AggwFSYTYugYRhNS1WW5BQk5JQUFBWW2AAYECCAZBQYTZMYACDAYVhK6FWW2E2WWAggwGEYSuwVluTklBQUFZbYACA/VtgAID9W2E2c4FhJW1WW4EUYTZ+V2AAgP1bUFZbYACBUZBQYTaQgWE2alZbkpFQUFZbYACBUZBQYTalgWEh4VZbkpFQUFZbYABhNr5hNrmEYSN3VlthI1xWW5BQgoFSYCCBAYSEhAERFWE22ldhNtlhIuZWW1thNuWEgoVhJbNWW1CTklBQUFZbYACCYB+DARJhNwJXYTcBYSLhVltbgVFhNxKEgmAghgFhNqtWW5FQUJKRUFBWW2AAYKCChAMSFWE3MVdhNzBhNmBWW1thNztgoGEjXFZbkFBgAGE3S4SChQFhNoFWW2AAgwFSUGAgYTdfhIKFAWE2llZbYCCDAVJQYECCAVFn//////////+BERVhN4NXYTeCYTZlVltbYTePhIKFAWE27VZbYECDAVJQYGCCAVFn//////////+BERVhN7NXYTeyYTZlVltbYTe/hIKFAWE27VZbYGCDAVJQYIBhN9OEgoUBYTaWVltggIMBUlCSkVBQVltgAIBgQIOFAxIVYTf2V2E39WEhpVZbW2AAYTgEhYKGAWEse1ZbklBQYCCDAVFn//////////+BERVhOCVXYTgkYSGqVltbYTgxhYKGAWE3G1ZbkVBQklCSkFBWW2AAgoJSYCCCAZBQkpFQUFZbYABhOFeCYTNgVlthOGGBhWE4O1Zbk1CDYCCCAoUBYThzhWEzfFZbgGAAW4WBEBVhOK9XhIQDiVKBUWE4kIWCYTRVVluUUGE4m4NhNGlWW5JQYCCKAZlQUGABgQGQUGE4d1ZbUIKXUIeVUFBQUFBQkpFQUFZbYABgQIIBkFBhONZgAIMBhWEroVZbgYEDYCCDAVJhOOiBhGE4TFZbkFCTklBQUFZbf05Ie3EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYABSYCFgBFJgJGAA/f6iZGlwZnNYIhIg7QFwC+WSFuwDFluKIf2VzM+UdsVrk6tZl4cqjf3sumRkc29sY0MACBIAMyKAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAogKjDAToDGPUHShYKFAAAAAAAAAAAAAAAAAAAAAAAAAP1cgcKAxj1BxABUhgKCgoCGAIQ/6/W2AEKCgoCGGIQgLDW2AE="},{"b64Body":"Cg8KCQjH4J6tBhDfBxICGAISAhgDGOLSn/kFIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo7qAYICCgZUb2tlbkQSCEtGSFNDQ01TKgMY8Qc6IhIgZHy/1vpgGCrQrgGLkFRwq0JNC7N0uRbiofdh1aER9Z9CIhIgZHy/1vpgGCrQrgGLkFRwq0JNC7N0uRbiofdh1aER9Z9KIhIgZHy/1vpgGCrQrgGLkFRwq0JNC7N0uRbiofdh1aER9Z9SIhIgZHy/1vpgGCrQrgGLkFRwq0JNC7N0uRbiofdh1aER9Z9qDAiDr/mwBhCogeeMAYgBAaIBIhIgZHy/1vpgGCrQrgGLkFRwq0JNC7N0uRbiofdh1aER9Z+yASISIGR8v9b6YBgq0K4Bi5BUcKtCTQuzdLkW4qH3YdWhEfWf","b64Record":"CiUIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkD1IDGPYHEjDvcFTETsIvLoTOUV5FsA0N8/d1Jfuv/GSqc3hxFnhjeYYRhBk74PtOjQkC7Z2zQIQaDAiD4Z6tBhC7maKpASIPCgkIx+CerQYQ3wcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIAcgoKAxj2BxIDGPEH"},{"b64Body":"Cg8KCQjH4J6tBhDlBxICGAISAhgDGNPtlwgiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjqoCCwoDGPYHGgRuZnQ0","b64Record":"CiUIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkD1gBcgEBEjDePJsqQaqvpzCSvOfloiLNFv03XWxiq1E95VJD9zJqtjyY6rxaBqI5BzltrTVupQ0aDAiD4Z6tBhCb4OWrAyIPCgkIx+CerQYQ5QcSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIAWhIKAxj2BxoLCgIYABIDGPEHGAE="},{"b64Body":"Cg8KCQjI4J6tBhDtBxICGAISAhgDGOC2iyAiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjsICCgoDGPIHEgMY9gc=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwtc8kLyKndMu8KmDFT7oxpRTBD7Se5RsY/6jmkOjpEv61miB+X8+zUoxA1pur2XeUGgwIhOGerQYQ47S8swEiDwoJCMjgnq0GEO0HEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"ChAKCQjI4J6tBhDvBxIDGPIHEgIYAxiA/rWHASICCHgyIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOOrcCCgMY9QcQgJL0ARiAqNa5ByKkAnj+cqEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg8Ppq2b4GM98dAEAAN+ZlR+q52GROzRhZMU5MGqjiSuQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQJpLPPfACaB3BjOwUbKP9yiNO3fNp5Jot0rIq4cmWVI1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","b64Record":"CiUIISIDGPUHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjAGMXlLJCO+XO0MXEUj9G08SjkRmFrevu2jN/j0DnQGhQAyGBy/oAUDrw2rGNCHTFAaDAiE4Z6tBhCDuMu5AyIQCgkIyOCerQYQ7wcSAxjyByogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo4wgJirbDrSARrKATB4MDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxYzU1NzA2NDYxNzQ2NTIwNmY2NjIwNzQ2ZjZiNjU2ZTIwNmI2NTc5NzMyMDY2NjE2OTZjNjU2NDIxMDAwMDAwMDAogKjDAVIZCgoKAhhiEICw1tgBCgsKAxjyBxD/r9bYAQ=="},{"b64Body":"ChIKCQjI4J6tBhDvBxIDGPIHIAGiAqkBCgAqIhIg8Ppq2b4GM98dAEAAN+ZlR+q52GROzRhZMU5MGqjiSuQyIhIg8Ppq2b4GM98dAEAAN+ZlR+q52GROzRhZMU5MGqjiSuQ6IzohAmks898AJoHcGM7BRso/3KI07d82nkmi3SsirhyZZUjWQiM6IQJpLPPfACaB3BjOwUbKP9yiNO3fNp5Jot0rIq4cmWVI1koFCgMY9QdyBUIDGPUHegUKAxj1Bw==","b64Record":"CgMIpwESMO1ci4U5BZb8n7uXF+cwhZNxWbC8IZUc6pfxoENFPOebS4ePjWbvH17Rp2n6kuVIYhoMCIThnq0GEIS4y7kDIhIKCQjI4J6tBhDvBxIDGPIHIAE6zQ4KAxjnAhIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKcaEElOVkFMSURfVE9LRU5fSUQohW5Q54PtAWKEDm/Dy68AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPD6atm+BjPfHQBAADfmZUfqudhkTs0YWTFOTBqo4krkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACECaSzz3wAmgdwYzsFGyj/cojTt3zaeSaLdKyKuHJllSNYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAagMY9QdSAHoMCIThnq0GEIO4y7kD"},{"b64Body":"ChAKCQjJ4J6tBhDxBxIDGPIHEgIYAxiA/rWHASICCHgyIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOOrcCCgMY9QcQgJL0ARiAqNa5ByKkAnj+cqEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg8Ppq2b4GM98dAEAAN+ZlR+q52GROzRhZMU5MGqjiSuQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQJpLPPfACaB3BjOwUbKP9yiNO3fNp5Jot0rIq4cmWVI1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","b64Record":"CiUIISIDGPUHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjBQK4aTDdtfEkzsr0kFvuXKuUCbfdcAH4KGyBdU5kmau514vWVYQZFgWrWUkMLUI8IaDAiF4Z6tBhD7zMviASIQCgkIyeCerQYQ8QcSAxjyByogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo4wgJirbDrSARrKATB4MDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxYzU1NzA2NDYxNzQ2NTIwNmY2NjIwNzQ2ZjZiNjU2ZTIwNmI2NTc5NzMyMDY2NjE2OTZjNjU2NDIxMDAwMDAwMDAogKjDAVIZCgoKAhhiEICw1tgBCgsKAxjyBxD/r9bYAQ=="},{"b64Body":"ChIKCQjJ4J6tBhDxBxIDGPIHIAGiAqwBCgMY9gcqIhIg8Ppq2b4GM98dAEAAN+ZlR+q52GROzRhZMU5MGqjiSuQyIhIg8Ppq2b4GM98dAEAAN+ZlR+q52GROzRhZMU5MGqjiSuQ6IzohAmks898AJoHcGM7BRso/3KI07d82nkmi3SsirhyZZUjWQiM6IQJpLPPfACaB3BjOwUbKP9yiNO3fNp5Jot0rIq4cmWVI1koFCgMY9QdyBUIDGPUHegUKAxj1Bw==","b64Record":"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"}]},"getTokenKeyForNonFungibleNegative":{"placeholderNum":1015,"encodedItems":[{"b64Body":"Cg8KCQjN4J6tBhCRCBICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjloxCiISIMbqALPpnZpSVsOX7Ez1GVYoj9n7p1sizFtI82JKArT+EICU69wDSgUIgM7aAw==","b64Record":"CiUIFhIDGPgHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjAoBDH+VLI6zgaYTmVwMqVYxO6At88X/zjoxguCHkn4NcBI/Ecu+a3jbmgrIg1NW0kaCwiK4Z6tBhC7k6UCIg8KCQjN4J6tBhCRCBICGAIqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOUhkKCgoCGAIQ/6fWuQcKCwoDGPgHEICo1rkH"},{"b64Body":"Cg8KCQjO4J6tBhCTCBICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlozCiISILzuZdaTmTc9MKIRqUVTUqDolZTaZc4bSBpXfZp5z6r4EICA6YOx3hZKBQiAztoD","b64Record":"CiUIFhIDGPkHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjAjLJWYHbYxJWdBldBWDN+r0nNxAHGwFf+8g3/TSNxB+By3hTpK2sw+XDWZ87KPZMEaDAiK4Z6tBhD76tuDAiIPCgkIzuCerQYQkwgSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIdCgwKAhgCEP//0YfivC0KDQoDGPkHEICA0ofivC0="},{"b64Body":"Cg8KCQjP4J6tBhCVCBICGAISAhgDGP7LzSwiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjooBjwESDAiKr/mwBhCwoY7cAxptCiISIDr6c3HstD3FiLbfZLQty2FbtSD0HhoL2Kc7s0uqii2TCiM6IQMqrPKW3y+B/prus+a07230jMBClG6ZpID+LTr4vuEqTAoiEiDO+aTTKUNzIct+aurPD6MsyI5GmQkaz0I4wztcJOGdLiIMSGVsbG8gV29ybGQhKgAyAA==","b64Record":"CiUIFhoDGPoHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjB6bDs6GIIormrrCnzBLPnafVrHHOX/JPgPJO0dWksDI92ZejfERAGDrjAC7w9UEFMaCwiL4Z6tBhDDzdcLIg8KCQjP4J6tBhCVCBICGAIqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOUgA="},{"b64Body":"Cg8KCQjP4J6tBhCZCBICGAISAhgDGIudjj4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjpoBiCAKAxj6ByKAIDYwODA2MDQwNTI2MDQwNTE4MDYwNDAwMTYwNDA1MjgwNjAwOTgxNTI2MDIwMDE3Zjc0NmY2YjY1NmU0ZTYxNmQ2NTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNTA2MDAyOTA4MTYyMDAwMDRhOTE5MDYyMDAwNTQwNTY1YjUwNjA0MDUxODA2MDQwMDE2MDQwNTI4MDYwMGI4MTUyNjAyMDAxN2Y3NDZmNmI2NTZlNTM3OTZkNjI2ZjZjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODE1MjUwNjAwMzkwODE2MjAwMDA5MTkxOTA2MjAwMDU0MDU2NWI1MDYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MDA0ODE1MjYwMjAwMTdmNmQ2NTZkNmYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI1MDYwMDQ5MDgxNjIwMDAwZDg5MTkwNjIwMDA1NDA1NjViNTAzNDgwMTU2MjAwMDBlNjU3NjAwMDgwZmQ1YjUwNjAwMTgwNjAwMDgwNjAwNjgxMTExNTYyMDAwMTAyNTc2MjAwMDEwMTYyMDAwNjI3NTY1YjViNjAwNjgxMTExNTYyMDAwMTE3NTc2MjAwMDExNjYyMDAwNjI3NTY1YjViODE1MjYwMjAwMTkwODE1MjYwMjAwMTYwMDAyMDgxOTA1NTUwNjAwMjYwMDE2MDAwNjAwMTYwMDY4MTExMTU2MjAwMDE0NjU3NjIwMDAxNDU2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDE1YjU3NjIwMDAxNWE2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYwMDQ2MDAxNjAwMDYwMDI2MDA2ODExMTE1NjIwMDAxOGE1NzYyMDAwMTg5NjIwMDA2Mjc1NjViNWI2MDA2ODExMTE1NjIwMDAxOWY1NzYyMDAwMTllNjIwMDA2Mjc1NjViNWI4MTUyNjAyMDAxOTA4MTUyNjAyMDAxNjAwMDIwODE5MDU1NTA2MDA4NjAwMTYwMDA2MDAzNjAwNjgxMTExNTYyMDAwMWNlNTc2MjAwMDFjZDYyMDAwNjI3NTY1YjViNjAwNjgxMTExNTYyMDAwMWUzNTc2MjAwMDFlMjYyMDAwNjI3NTY1YjViODE1MjYwMjAwMTkwODE1MjYwMjAwMTYwMDAyMDgxOTA1NTUwNjAxMDYwMDE2MDAwNjAwNDYwMDY4MTExMTU2MjAwMDIxMjU3NjIwMDAyMTE2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDIyNzU3NjIwMDAyMjY2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYwMjA2MDAxNjAwMDYwMDU2MDA2ODExMTE1NjIwMDAyNTY1NzYyMDAwMjU1NjIwMDA2Mjc1NjViNWI2MDA2ODExMTE1NjIwMDAyNmI1NzYyMDAwMjZhNjIwMDA2Mjc1NjViNWI4MTUyNjAyMDAxOTA4MTUyNjAyMDAxNjAwMDIwODE5MDU1NTA2MDQwNjAwMTYwMDA2MDA2ODA4MTExMTU2MjAwMDI5OTU3NjIwMDAyOTg2MjAwMDYyNzU2NWI1YjYwMDY4MTExMTU2MjAwMDJhZTU3NjIwMDAyYWQ2MjAwMDYyNzU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA4MTkwNTU1MDYyMDAwNjU2NTY1YjYwMDA4MTUxOTA1MDkxOTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDQxNjAwNDUyNjAyNDYwMDBmZDViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjAyMjYwMDQ1MjYwMjQ2MDAwZmQ1YjYwMDA2MDAyODIwNDkwNTA2MDAxODIxNjgwNjIwMDAzNDg1NzYwN2Y4MjE2OTE1MDViNjAyMDgyMTA4MTAzNjIwMDAzNWU1NzYyMDAwMzVkNjIwMDAzMDA1NjViNWI1MDkxOTA1MDU2NWI2MDAwODE5MDUwODE2MDAwNTI2MDIwNjAwMDIwOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDYwMWY4MzAxMDQ5MDUwOTE5MDUwNTY1YjYwMDA4MjgyMWI5MDUwOTI5MTUwNTA1NjViNjAwMDYwMDg4MzAyNjIwMDAzYzg3ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY4MjYyMDAwMzg5NTY1YjYyMDAwM2Q0ODY4MzYyMDAwMzg5NTY1Yjk1NTA4MDE5ODQxNjkzNTA4MDg2MTY4NDE3OTI1MDUwNTA5MzkyNTA1MDUwNTY1YjYwMDA4MTkwNTA5MTkwNTA1NjViNjAwMDgxOTA1MDkxOTA1MDU2NWI2MDAwNjIwMDA0MjE2MjAwMDQxYjYyMDAwNDE1ODQ2MjAwMDNlYzU2NWI2MjAwMDNmNjU2NWI2MjAwMDNlYzU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MTkwNTA5MTkwNTA1NjViNjIwMDA0M2Q4MzYyMDAwNDAwNTY1YjYyMDAwNDU1NjIwMDA0NGM4MjYyMDAwNDI4NTY1Yjg0ODQ1NDYyMDAwMzk2NTY1YjgyNTU1MDUwNTA1MDU2NWI2MDAwOTA1NjViNjIwMDA0NmM2MjAwMDQ1ZDU2NWI2MjAwMDQ3OTgxODQ4NDYyMDAwNDMyNTY1YjUwNTA1MDU2NWI1YjgxODExMDE1NjIwMDA0YTE1NzYyMDAwNDk1NjAwMDgyNjIwMDA0NjI1NjViNjAwMTgxMDE5MDUwNjIwMDA0N2Y1NjViNTA1MDU2NWI2MDFmODIxMTE1NjIwMDA0ZjA1NzYyMDAwNGJhODE2MjAwMDM2NDU2NWI2MjAwMDRjNTg0NjIwMDAzNzk1NjViODEwMTYwMjA4NTEwMTU2MjAwMDRkNTU3ODE5MDUwNWI2MjAwMDRlZDYyMDAwNGU0ODU2MjAwMDM3OTU2NWI4MzAxODI2MjAwMDQ3ZTU2NWI1MDUwNWI1MDUwNTA1NjViNjAwMDgyODIxYzkwNTA5MjkxNTA1MDU2NWI2MDAwNjIwMDA1MTU2MDAwMTk4NDYwMDgwMjYyMDAwNGY1NTY1YjE5ODA4MzE2OTE1MDUwOTI5MTUwNTA1NjViNjAwMDYyMDAwNTMwODM4MzYyMDAwNTAyNTY1YjkxNTA4MjYwMDIwMjgyMTc5MDUwOTI5MTUwNTA1NjViNjIwMDA1NGI4MjYyMDAwMmM2NTY1YjY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYyMDAwNTY3NTc2MjAwMDU2NjYyMDAwMmQxNTY1YjViNjIwMDA1NzM4MjU0NjIwMDAzMmY1NjViNjIwMDA1ODA4MjgyODU2MjAwMDRhNTU2NWI2MDAwNjAyMDkwNTA2MDFmODMxMTYwMDE4MTE0NjIwMDA1Yjg1NzYwMDA4NDE1NjIwMDA1YTM1NzgyODcwMTUxOTA1MDViNjIwMDA1YWY4NTgyNjIwMDA1MjI1NjViODY1NTUwNjIwMDA2MWY1NjViNjAxZjE5ODQxNjYyMDAwNWM4ODY2MjAwMDM2NDU2NWI2MDAwNWI4MjgxMTAxNTYyMDAwNWYyNTc4NDg5MDE1MTgyNTU2MDAxODIwMTkxNTA2MDIwODUwMTk0NTA2MDIwODEwMTkwNTA2MjAwMDVjYjU2NWI4NjgzMTAxNTYyMDAwNjEyNTc4NDg5MDE1MTYyMDAwNjBlNjAxZjg5MTY4MjYyMDAwNTAyNTY1YjgzNTU1MDViNjAwMTYwMDI4ODAyMDE4ODU1NTA1MDUwNWI1MDUwNTA1MDUwNTA1NjViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjAyMTYwMDQ1MjYwMjQ2MDAwZmQ1YjYxMzk1NjgwNjIwMDA2NjY2MDAwMzk2MDAwZjNmZTYwODA2MDQwNTI2MDA0MzYxMDYxMDBjMjU3NjAwMDM1NjBlMDFjODA2MzdkY2JiYzcyMTE2MTAwN2Y1NzgwNjNlYWM2ZjNmZTExNjEwMDU5NTc4MDYzZWFjNmYzZmUxNDYxMDI1NDU3ODA2M2ViNTQ4ZWZmMTQ2MTAyOTE1NzgwNjNmMDA3Mjk5MDE0NjEwMmFkNTc4MDYzZjE3MzA3NjAxNDYxMDJjOTU3NjEwMGMyNTY1YjgwNjM3ZGNiYmM3MjE0NjEwMWRmNTc4MDYzOWIyM2QzZDkxNDYxMDFmYjU3ODA2M2U2MDhlMThkMTQ2MTAyMzg1NzYxMDBjMjU2NWI4MDYzMTFlMWZjMDcxNDYxMDBjNzU3ODA2MzE1ZGFjYmVhMTQ2MTAxMDQ1NzgwNjMzYWExMmVmNTE0NjEwMTQxNTc4MDYzNDEyOWI3ZGIxNDYxMDE1ZDU3ODA2MzYxOGRjNjVlMTQ2MTAxOWE1NzgwNjM3OGZlNzJhMTE0NjEwMWMzNTc1YjYwMDA4MGZkNWIzNDgwMTU2MTAwZDM1NzYwMDA4MGZkNWI1MDYxMDBlZTYwMDQ4MDM2MDM4MTAxOTA2MTAwZTk5MTkwNjEyMjQzNTY1YjYxMDJlNTU2NWI2MDQwNTE2MTAwZmI5MTkwNjEyMmM2NTY1YjYwNDA1MTgwOTEwMzkwZjM1YjM0ODAxNTYxMDExMDU3NjAwMDgwZmQ1YjUwNjEwMTJiNjAwNDgwMzYwMzgxMDE5MDYxMDEyNjkxOTA2MTIyNDM1NjViNjEwNDAxNTY1YjYwNDA1MTYxMDEzODkxOTA2MTIyYzY1NjViNjA0MDUxODA5MTAzOTBmMzViNjEwMTViNjAwNDgwMzYwMzgxMDE5MDYxMDE1NjkxOTA2MTI0NTM1NjViNjEwNTFmNTY1YjAwNWIzNDgwMTU2MTAxNjk1NzYwMDA4MGZkNWI1MDYxMDE4NDYwMDQ4MDM2MDM4MTAxOTA2MTAxN2Y5MTkwNjEyNTJkNTY1YjYxMDc3MDU2NWI2MDQwNTE2MTAxOTE5MTkwNjEyNjkzNTY1YjYwNDA1MTgwOTEwMzkwZjM=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwKVOPeQbmez9Tc2qhLdVVGrqptIKZRRWEV+KWA4GBabpR2ikkns2+fJmqCUX/rd20GgwIi+GerQYQi+mujQIiDwoJCM/gnq0GEJkIEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjQ4J6tBhCfCBICGAISAhgDGPfR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj6ByKAIDViMzQ4MDE1NjEwMWE2NTc2MDAwODBmZDViNTA2MTAxYzE2MDA0ODAzNjAzODEwMTkwNjEwMWJjOTE5MDYxMjZiNTU2NWI2MTA3YTc1NjViMDA1YjYxMDFkZDYwMDQ4MDM2MDM4MTAxOTA2MTAxZDg5MTkwNjEyNzExNTY1YjYxMDhjZTU2NWIwMDViNjEwMWY5NjAwNDgwMzYwMzgxMDE5MDYxMDFmNDkxOTA2MTI3YjA1NjViNjEwYTY1NTY1YjAwNWIzNDgwMTU2MTAyMDc1NzYwMDA4MGZkNWI1MDYxMDIyMjYwMDQ4MDM2MDM4MTAxOTA2MTAyMWQ5MTkwNjEyMjQzNTY1YjYxMGI4NTU2NWI2MDQwNTE2MTAyMmY5MTkwNjEyMmM2NTY1YjYwNDA1MTgwOTEwMzkwZjM1YjYxMDI1MjYwMDQ4MDM2MDM4MTAxOTA2MTAyNGQ5MTkwNjEyOGE0NTY1YjYxMGNhMzU2NWIwMDViMzQ4MDE1NjEwMjYwNTc2MDAwODBmZDViNTA2MTAyN2I2MDA0ODAzNjAzODEwMTkwNjEwMjc2OTE5MDYxMjI0MzU2NWI2MTBlODA1NjViNjA0MDUxNjEwMjg4OTE5MDYxMjJjNjU2NWI2MDQwNTE4MDkxMDM5MGYzNWI2MTAyYWI2MDA0ODAzNjAzODEwMTkwNjEwMmE2OTE5MDYxMmEwZjU2NWI2MTBmOWM1NjViMDA1YjYxMDJjNzYwMDQ4MDM2MDM4MTAxOTA2MTAyYzI5MTkwNjEyYWMyNTY1YjYxMTMzNDU2NWIwMDViNjEwMmUzNjAwNDgwMzYwMzgxMDE5MDYxMDJkZTkxOTA2MTJiMDI1NjViNjExNTg3NTY1YjAwNWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzliMjNkM2Q5NjBlMDFiODg4ODg4ODg2MDQwNTE2MDI0MDE2MTAzMjI5NDkzOTI5MTkwNjEyYmJmNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMDM4YzkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxODU1YWY0OTE1MDUwM2Q4MDYwMDA4MTE0NjEwM2M3NTc2MDQwNTE5MTUwNjAxZjE5NjAzZjNkMDExNjgyMDE2MDQwNTIzZDgyNTIzZDYwMDA2MDIwODQwMTNlNjEwM2NjNTY1YjYwNjA5MTUwNWI1MDkxNTA5MTUwODE2MTAzZGQ1NzYwMTU2MTAzZjI1NjViODA4MDYwMjAwMTkwNTE4MTAxOTA2MTAzZjE5MTkwNjEyYzkwNTY1YjViNjAwMzBiOTI1MDUwNTA5NDkzNTA1MDUwNTA1NjViNjAwMDgwNjAwMDYxMDE2NzczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2NjMxNWRhY2JlYTYwZTAxYjg4ODg4ODg4NjA0MDUxNjAyNDAxNjEwNDNlOTQ5MzkyOTE5MDYxMmJiZjU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTA0YTg5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTA0ZTU1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTA0ZWE1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA4MTYxMDRmYjU3NjAxNTYxMDUxMDU2NWI4MDgwNjAyMDAxOTA1MTgxMDE5MDYxMDUwZjkxOTA2MTJjOTA1NjViNWI2MDAzMGI5MjUwNTA1MDk0OTM1MDUwNTA1MDU2NWI2MDAwNjAwNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMDUzYzU3NjEwNTNiNjEyMmZjNTY1YjViNjA0MDUxOTA4MDgyNTI4MDYwMjAwMjYwMjAwMTgyMDE2MDQwNTI4MDE1NjEwNTc1NTc4MTYwMjAwMTViNjEwNTYyNjEyMDcyNTY1YjgxNTI2MDIwMDE5MDYwMDE5MDAzOTA4MTYxMDU1YTU3OTA1MDViNTA5MDUwNjEwNTg3NjAwMDYwMDE2MDAyODk2MTE2Yzg1NjViODE2MDAwODE1MTgxMTA2MTA1OWI1NzYxMDU5YTYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA1YjQ2MDAyNjAwMzgwODg2MTE2Yzg1NjViODE2MDAxODE1MTgxMTA2MTA1Yzg1NzYxMDVjNzYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA1ZTA2MDA0NjAwMTg2NjExNzAxNTY1YjgxNjAwMjgxNTE4MTEwNjEwNWY0NTc2MTA1ZjM2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwNjBjNjAwNjYwMDE4NjYxMTcwMTU2NWI4MTYwMDM4MTUxODExMDYxMDYyMDU3NjEwNjFmNjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDYzODYwMDU2MDA0ODY2MTE3MDE1NjViODE2MDA0ODE1MTgxMTA2MTA2NGM1NzYxMDY0YjYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA2NWY2MTIwOTI1NjViNjAwMDgxNjAwMDAxOTA2MDA3MGI5MDgxNjAwNzBiODE1MjUwNTA4MzgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwODI4MTYwNDAwMTkwNjAwNzBiOTA4MTYwMDcwYjgxNTI1MDUwNjEwNmM0NjEyMGNmNTY1Yjg4ODE2MDQwMDE5MDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2OTA4MTczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwNTA4MjgxNjBlMDAxODE5MDUyNTA4MTgxNjEwMTAwMDE4MTkwNTI1MDYwMDA2MTA3MWI4YjgzNjExNzM4NTY1YjkwNTA2MDE2NjAwMzBiODExNDYxMDc2MzU3NjA0MDUxN2YwOGMzNzlhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODE1MjYwMDQwMTYxMDc1YTkwNjEyZDQ5NTY1YjYwNDA1MTgwOTEwMzkwZmQ1YjUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1NjViNjEwNzc4NjEyMTNlNTY1YjYwMDA4MDYxMDc4NTg1ODU2MTE4NTA1NjViOTE1MDYwMDcwYjkxNTA2MDE2NjAwMzBiODIxNDYxMDc5YzU3NjAwMDgwZmQ1YjgwOTI1MDUwNTA5MjkxNTA1MDU2NWI2MDAwODA2MTAxNjc3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjYzNjE4ZGM2NWU2MGUwMWI4NTg1NjA0MDUxNjAyNDAxNjEwN2RlOTI5MTkwNjEyZGIzNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMDg0ODkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxNjAwMDg2NWFmMTkxNTA1MDNkODA2MDAwODExNDYxMDg4NTU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMDg4YTU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDdmNGFmNDc4MGUwNmZlOGNiOWRmNjRiMDc5NGZhNmYwMTM5OWFmOTc5MTc1YmI5ODhlMzVlMGU1N2U1OTQ1NjdiYzgyODI2MDQwNTE2MTA4YzA5MjkxOTA2MTJkZjI1NjViNjA0MDUxODA5MTAzOTBhMTUwNTA1MDUwNTY1YjYwMDA2MDA1NjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwOGViNTc2MTA4ZWE2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTA5MjQ1NzgxNjAyMDAxNWI2MTA5MTE2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwOTA5NTc5MDUwNWI1MDkwNTA2MTA5MzY2MDAwNjAwMTYwMDI4NzYxMTZjODU2NWI4MTYwMDA4MTUxODExMDYxMDk0YTU3NjEwOTQ5NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDk2MzYwMDI2MDAzODA4NjYxMTZjODU2NWI4MTYwMDE4MTUxODExMDYxMDk3NzU3NjEwOTc2NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDk4ZjYwMDQ2MDAxODQ2MTE3MDE1NjViODE2MDAyODE1MTgxMTA2MTA5YTM=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwykbUYvqcf+uhTDVxdfvzePR9SDjXItKndn6aOjS9wWpsOe+ke0YhvwaAmIQmrqlfGgsIjOGerQYQ+4LHNyIPCgkI0OCerQYQnwgSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQjQ4J6tBhClCBICGAISAhgDGPfR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj6ByKAIDU3NjEwOWEyNjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMDliYjYwMDY2MDAxODQ2MTE3MDE1NjViODE2MDAzODE1MTgxMTA2MTA5Y2Y1NzYxMDljZTYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTA5ZTc2MDA1NjAwNDg0NjExNzAxNTY1YjgxNjAwNDgxNTE4MTEwNjEwOWZiNTc2MTA5ZmE2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjAwMDYxMGExMjg2ODM2MTE5ODQ1NjViNjAwNzBiOTA1MDYwMTY2MDAzMGI4MTE0NjEwYTVkNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjEwYTU0OTA2MTJlNmU1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1MDUwNTY1YjYwMDA2MDAxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwYTgyNTc2MTBhODE2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTBhYmI1NzgxNjAyMDAxNWI2MTBhYTg2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwYWEwNTc5MDUwNWI1MDkwNTA2MTBhYzY2MTIwNzI1NjViNjAwNDgxNjAwMDAxODE4MTUyNTA1MDYxMGFkOTYxMjEzZTU2NWIzMDgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwMTgxNjAwMDAxOTAxNTE1OTA4MTE1MTU4MTUyNTA1MDgwODI2MDIwMDE4MTkwNTI1MDgxODM2MDAwODE1MTgxMTA2MTBiNDA1NzYxMGIzZjYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MDAwNjEwYjViMzA2MDAwODg4ODg4NjExYTljNTY1YjkwNTA2MDAwNjEwYjY5ODg4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTBiN2I1NzYwMDA4MGZkNWI1MDUwNTA1MDUwNTA1MDUwNTY1YjYwMDA4MDYwMDA2MTAxNjc3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjYzOWIyM2QzZDk2MGUwMWI4ODg4ODg4ODYwNDA1MTYwMjQwMTYxMGJjMjk0OTM5MjkxOTA2MTJiYmY1NjViNjA0MDUxNjAyMDgxODMwMzAzODE1MjkwNjA0MDUyOTA3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTkxNjYwMjA4MjAxODA1MTdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY4MzgxODMxNjE3ODM1MjUwNTA1MDUwNjA0MDUxNjEwYzJjOTE5MDYxMmM0MDU2NWI2MDAwNjA0MDUxODA4MzAzODE2MDAwODY1YWYxOTE1MDUwM2Q4MDYwMDA4MTE0NjEwYzY5NTc2MDQwNTE5MTUwNjAxZjE5NjAzZjNkMDExNjgyMDE2MDQwNTIzZDgyNTIzZDYwMDA2MDIwODQwMTNlNjEwYzZlNTY1YjYwNjA5MTUwNWI1MDkxNTA5MTUwODE2MTBjN2Y1NzYwMTU2MTBjOTQ1NjViODA4MDYwMjAwMTkwNTE4MTAxOTA2MTBjOTM5MTkwNjEyYzkwNTY1YjViNjAwMzBiOTI1MDUwNTA5NDkzNTA1MDUwNTA1NjViNjAwMDYwMDU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTBjYzA1NzYxMGNiZjYxMjJmYzU2NWI1YjYwNDA1MTkwODA4MjUyODA2MDIwMDI2MDIwMDE4MjAxNjA0MDUyODAxNTYxMGNmOTU3ODE2MDIwMDE1YjYxMGNlNjYxMjA3MjU2NWI4MTUyNjAyMDAxOTA2MDAxOTAwMzkwODE2MTBjZGU1NzkwNTA1YjUwOTA1MDYxMGQwYjYwMDA2MDAxNjAwMjhjNjExNmM4NTY1YjgxNjAwMDgxNTE4MTEwNjEwZDFmNTc2MTBkMWU2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwZDM4NjAwMjYwMDM4MDhiNjExNmM4NTY1YjgxNjAwMTgxNTE4MTEwNjEwZDRjNTc2MTBkNGI2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjEwZDY0NjAwNDYwMDE4OTYxMTcwMTU2NWI4MTYwMDI4MTUxODExMDYxMGQ3ODU3NjEwZDc3NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMGQ5MDYwMDY2MDAxODk2MTE3MDE1NjViODE2MDAzODE1MTgxMTA2MTBkYTQ1NzYxMGRhMzYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTBkYmM2MDA1NjAwNDg5NjExNzAxNTY1YjgxNjAwNDgxNTE4MTEwNjEwZGQwNTc2MTBkY2Y2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwODM2MDAyOTA4MTYxMGRlYTkxOTA2MTMwYTU1NjViNTA4MjYwMDM5MDgxNjEwZGZhOTE5MDYxMzBhNTU2NWI1MDgxNjAwNDkwODE2MTBlMGE5MTkwNjEzMGE1NTY1YjUwNjAwMDYxMGUxYjhiNjAwMDg5ODk4NjYxMWE5YzU2NWI5MDUwNjAwMDYxMGUyOThkODM2MTE3Mzg1NjViOTA1MDYwMTY2MDAzMGI4MTE0NjEwZTcxNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjEwZTY4OTA2MTJkNDk1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1NjViNjAwMDgwNjAwMDYxMDE2NzczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2NjMxNWRhY2JlYTYwZTAxYjg4ODg4ODg4NjA0MDUxNjAyNDAxNjEwZWJkOTQ5MzkyOTE5MDYxMmJiZjU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTBmMjc5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTg1NWFmNDkxNTA1MDNkODA2MDAwODExNDYxMGY2MjU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMGY2NzU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDgxNjEwZjc4NTc2MDE1NjEwZjhkNTY1YjgwODA2MDIwMDE5MDUxODEwMTkwNjEwZjhjOTE5MDYxMmM5MDU2NWI1YjYwMDMwYjkyNTA1MDUwOTQ5MzUwNTA1MDUwNTY1YjYwMDA2MDA1NjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEwZmI5NTc2MTBmYjg2MTIyZmM1NjViNWI2MDQwNTE5MDgwODI1MjgwNjAyMDAyNjAyMDAxODIwMTYwNDA1MjgwMTU2MTBmZjI1NzgxNjAyMDAxNWI2MTBmZGY2MTIwNzI1NjViODE1MjYwMjAwMTkwNjAwMTkwMDM5MDgxNjEwZmQ3NTc5MDUwNWI1MDkwNTA2MTEwMDQ2MDAwNjAwMTYwMDI4NzYxMTZjODU2NWI4MTYwMDA4MTUxODExMDYxMTAxODU3NjExMDE3NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTAzMTYwMDI2MDAzODA4NjYxMTZjODU2NWI4MTYwMDE4MTUxODExMDYxMTA0NTU3NjExMDQ0NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTA1ZDYwMDQ2MDAxODQ2MTE3MDE1NjViODE2MDAyODE1MTgxMTA2MTEwNzE1NzYxMTA3MDYxMmNiZDU2NWI1YjYwMjAwMjYwMjAwMTAxODE5MDUyNTA2MTEwODk2MDA2NjAwMTg0NjExNzAxNTY1YjgxNjAwMzgxNTE4MTEwNjExMDlkNTc2MTEwOWM2MTJjYmQ1NjViNWI2MDIwMDI2MDIwMDEwMTgxOTA1MjUwNjExMGI1NjAwNTYwMDQ4NDYxMTcwMTU2NWI4MTYwMDQ4MTUxODExMDYxMTBjOTU3NjExMGM4NjEyY2JkNTY1YjViNjAyMDAyNjAyMDAxMDE4MTkwNTI1MDYxMTBkYzYxMjBjZjU2NWI4NTgxNjA0MDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwMjgwNTQ2MTExMjE5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExMTRkOTA2MTJlYzg1NjViODAxNTYxMTE5YTU3ODA2MDFmMTA2MTExNmY1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMTE5YTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExMTdkNTc4MjkwMDM2MDFmMTY4MjAxOTE=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwoi577JmDjZgTKVbZ4vqRGhKgpD/2W2KLWq0MksST0aKAMNpRGoAxOBGflTDCMONmGgwIjOGerQYQ84SomwIiDwoJCNDgnq0GEKUIEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjR4J6tBhCrCBICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj6ByKAIDViNTA1MDUwNTA1MDgxNjAwMDAxODE5MDUyNTA2MDAzODA1NDYxMTFiNDkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTExZTA5MDYxMmVjODU2NWI4MDE1NjExMjJkNTc4MDYwMWYxMDYxMTIwMjU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExMjJkNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEyMTA1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAyMDAxODE5MDUyNTA4MTgxNjBlMDAxODE5MDUyNTA2MDA0ODA1NDYxMTI1MDkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTEyN2M5MDYxMmVjODU2NWI4MDE1NjExMmM5NTc4MDYwMWYxMDYxMTI5ZTU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExMmM5NTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEyYWM1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjA2MDAxODE5MDUyNTA2MDAwNjExMmUyODg4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTEzMmE1NzYwNDA1MTdmMDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI2MDA0MDE2MTEzMjE5MDYxMzFjMzU2NWI2MDQwNTE4MDkxMDM5MGZkNWI1MDUwNTA1MDUwNTA1MDUwNTY1YjYxMTMzYzYxMjBjZjU2NWI2MDAyODA1NDYxMTM0OTkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTEzNzU5MDYxMmVjODU2NWI4MDE1NjExM2MyNTc4MDYwMWYxMDYxMTM5NzU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExM2MyNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTEzYTU1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAwMDAxODE5MDUyNTA2MDAzODA1NDYxMTNkYzkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTE0MDg5MDYxMmVjODU2NWI4MDE1NjExNDU1NTc4MDYwMWYxMDYxMTQyYTU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExNDU1NTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTE0Mzg1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjAyMDAxODE5MDUyNTA4MTgxNjA0MDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjAwNDgwNTQ2MTE0YTc5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExNGQzOTA2MTJlYzg1NjViODAxNTYxMTUyMDU3ODA2MDFmMTA2MTE0ZjU1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMTUyMDU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExNTAzNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MTYwNjAwMTgxOTA1MjUwNjAwMDYxMTUzOTg0ODM2MTE3Mzg1NjViOTA1MDYwMTY2MDAzMGI4MTE0NjExNTgxNTc2MDQwNTE3ZjA4YzM3OWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MTUyNjAwNDAxNjExNTc4OTA2MTMyNTU1NjViNjA0MDUxODA5MTAzOTBmZDViNTA1MDUwNTA1NjViNjExNThmNjEyMGNmNTY1YjgyODE2MDAwMDE4MTkwNTI1MDgxODE2MDIwMDE4MTkwNTI1MDgzODE2MDQwMDE5MDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2OTA4MTczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwNTA2MDA0ODA1NDYxMTVlNjkwNjEyZWM4NTY1YjgwNjAxZjAxNjAyMDgwOTEwNDAyNjAyMDAxNjA0MDUxOTA4MTAxNjA0MDUyODA5MjkxOTA4MTgxNTI2MDIwMDE4MjgwNTQ2MTE2MTI5MDYxMmVjODU2NWI4MDE1NjExNjVmNTc4MDYwMWYxMDYxMTYzNDU3NjEwMTAwODA4MzU0MDQwMjgzNTI5MTYwMjAwMTkxNjExNjVmNTY1YjgyMDE5MTkwNjAwMDUyNjAyMDYwMDAyMDkwNWI4MTU0ODE1MjkwNjAwMTAxOTA2MDIwMDE4MDgzMTE2MTE2NDI1NzgyOTAwMzYwMWYxNjgyMDE5MTViNTA1MDUwNTA1MDgxNjA2MDAxODE5MDUyNTA2MDAwNjExNjc4ODY4MzYxMTczODU2NWI5MDUwNjAxNjYwMDMwYjgxMTQ2MTE2YzA1NzYwNDA1MTdmMDhjMzc5YTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgxNTI2MDA0MDE2MTE2Yjc5MDYxMzJlNzU2NWI2MDQwNTE4MDkxMDM5MGZkNWI1MDUwNTA1MDUwNTA1NjViNjExNmQwNjEyMDcyNTY1YjYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MTE2ZTU4Nzg3NjExZDE2NTY1YjgxNTI2MDIwMDE2MTE2ZjQ4NTg1NjExZDZjNTY1YjgxNTI1MDkwNTA5NDkzNTA1MDUwNTA1NjViNjExNzA5NjEyMDcyNTY1YjYwNDA1MTgwNjA0MDAxNjA0MDUyODA2MTE3MWQ4NjYxMWY0MTU2NWI4MTUyNjAyMDAxNjExNzJjODU4NTYxMWY4MjU2NWI4MTUyNTA5MDUwOTM5MjUwNTA1MDU2NWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzdkMzA1Y2ZhNjBlMDFiODY4NjYwNDA1MTYwMjQwMTYxMTc3MTkyOTE5MDYxMzYwNzU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTE3ZGI5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTE4MTg1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTE4MWQ1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA4MTYxMTgyZTU3NjAxNTYxMTg0MzU2NWI4MDgwNjAyMDAxOTA1MTgxMDE5MDYxMTg0MjkxOTA2MTJjOTA1NjViNWI2MDAzMGI5MjUwNTA1MDkyOTE1MDUwNTY1YjYwMDA2MTE4NWE2MTIxM2U1NjViNjAwMDgwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY2MzNjNGRkMzJlNjBlMDFiODc4NzYwNDA1MTYwMjQwMTYxMTg5MTkyOTE5MDYxMzYzNzU2NWI2MDQwNTE2MDIwODE4MzAzMDM4MTUyOTA2MDQwNTI5MDdiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxOTE2NjAyMDgyMDE4MDUxN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgzODE4MzE2MTc4MzUyNTA1MDUwNTA2MDQwNTE2MTE4ZmI5MTkwNjEyYzQwNTY1YjYwMDA2MDQwNTE4MDgzMDM4MTYwMDA4NjVhZjE5MTUwNTAzZDgwNjAwMDgxMTQ2MTE5Mzg1NzYwNDA1MTkxNTA2MDFmMTk2MDNmM2QwMTE2ODIwMTYwNDA1MjNkODI1MjNkNjAwMDYwMjA4NDAxM2U2MTE5M2Q1NjViNjA2MDkxNTA1YjUwOTE1MDkxNTA2MTE5NGE2MTIxM2U1NjViODI2MTE5NTc1NzYwMTU4MTYxMTk2YzU2NWI4MTgwNjAyMDAxOTA1MTgxMDE5MDYxMTk2YjkxOTA2MTM3ZGY1NjViNWI4MTYwMDMwYjkxNTA4MDk1NTA4MTk2NTA1MDUwNTA1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODA2MDAwNjEwMTY3NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw9IuH+xyJpUyK4SFt8k7p1Js/8hK+Tolz/vY491YzhupmtNwpf68M3fHjuqny3XCDGgsIjeGerQYQy+r2YyIPCgkI0eCerQYQqwgSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQjR4J6tBhCxCBICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj6ByKAIGZmZmZmZmZmZmZmZmZmZmYxNjYzNmZjM2NiYWY2MGUwMWI4Njg2NjA0MDUxNjAyNDAxNjExOWJkOTI5MTkwNjEzOGMxNTY1YjYwNDA1MTYwMjA4MTgzMDMwMzgxNTI5MDYwNDA1MjkwN2JmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE5MTY2MDIwODIwMTgwNTE3YmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODM4MTgzMTYxNzgzNTI1MDUwNTA1MDYwNDA1MTYxMWEyNzkxOTA2MTJjNDA1NjViNjAwMDYwNDA1MTgwODMwMzgxNjAwMDg2NWFmMTkxNTA1MDNkODA2MDAwODExNDYxMWE2NDU3NjA0MDUxOTE1MDYwMWYxOTYwM2YzZDAxMTY4MjAxNjA0MDUyM2Q4MjUyM2Q2MDAwNjAyMDg0MDEzZTYxMWE2OTU2NWI2MDYwOTE1MDViNTA5MTUwOTE1MDgxNjExYTdhNTc2MDE1NjExYThmNTY1YjgwODA2MDIwMDE5MDUxODEwMTkwNjExYThlOTE5MDYxMmM5MDU2NWI1YjYwMDMwYjkyNTA1MDUwOTI5MTUwNTA1NjViNjExYWE0NjEyMGNmNTY1YjYxMWFhYzYxMjA5MjU2NWI4NTgxNjAwMDAxOTA2MDA3MGI5MDgxNjAwNzBiODE1MjUwNTA4NDgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwODM4MTYwNDAwMTkwNjAwNzBiOTA4MTYwMDcwYjgxNTI1MDUwNjAwMjgwNTQ2MTFiMTU5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExYjQxOTA2MTJlYzg1NjViODAxNTYxMWI4ZTU3ODA2MDFmMTA2MTFiNjM1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWI4ZTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExYjcxNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwMDAwMTgxOTA1MjUwNjAwMzgwNTQ2MTFiYTg5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExYmQ0OTA2MTJlYzg1NjViODAxNTYxMWMyMTU3ODA2MDFmMTA2MTFiZjY1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWMyMTU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExYzA0NTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwMjAwMTgxOTA1MjUwODY4MjYwNDAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDgyODI2MGUwMDE4MTkwNTI1MDgwODI2MTAxMDAwMTgxOTA1MjUwNjAwNDgwNTQ2MTFjODY5MDYxMmVjODU2NWI4MDYwMWYwMTYwMjA4MDkxMDQwMjYwMjAwMTYwNDA1MTkwODEwMTYwNDA1MjgwOTI5MTkwODE4MTUyNjAyMDAxODI4MDU0NjExY2IyOTA2MTJlYzg1NjViODAxNTYxMWNmZjU3ODA2MDFmMTA2MTFjZDQ1NzYxMDEwMDgwODM1NDA0MDI4MzUyOTE2MDIwMDE5MTYxMWNmZjU2NWI4MjAxOTE5MDYwMDA1MjYwMjA2MDAwMjA5MDViODE1NDgxNTI5MDYwMDEwMTkwNjAyMDAxODA4MzExNjExY2UyNTc4MjkwMDM2MDFmMTY4MjAxOTE1YjUwNTA1MDUwNTA4MjYwNjAwMTgxOTA1MjUwNTA5NTk0NTA1MDUwNTA1MDU2NWI2MDAwNjExZDNkODM2MDA2ODExMTE1NjExZDJlNTc2MTFkMmQ2MTM4ZjE1NjViNWI4MjYxMjA1ZTkwOTE5MDYzZmZmZmZmZmYxNjU2NWI5MDUwNjExZDY0ODI2MDA2ODExMTE1NjExZDU1NTc2MTFkNTQ2MTM4ZjE1NjViNWI4MjYxMjA1ZTkwOTE5MDYzZmZmZmZmZmYxNjU2NWI5MDUwOTI5MTUwNTA1NjViNjExZDc0NjEyMTNlNTY1YjYwMDA2MDA0ODExMTE1NjExZDg4NTc2MTFkODc2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFkOWI1NzYxMWQ5YTYxMzhmMTU2NWI1YjAzNjExZGI2NTc2MDAxODE2MDAwMDE5MDE1MTU5MDgxMTUxNTgxNTI1MDUwNjExZjNiNTY1YjYwMDE2MDA0ODExMTE1NjExZGNhNTc2MTFkYzk2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFkZGQ1NzYxMWRkYzYxMzhmMTU2NWI1YjAzNjExZTNmNTc2MDAwODA1NDkwNjEwMTAwMGE5MDA0NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTYwMjAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDYxMWYzYTU2NWI2MDAyNjAwNDgxMTExNTYxMWU1MzU3NjExZTUyNjEzOGYxNTY1YjViODM2MDA0ODExMTE1NjExZTY2NTc2MTFlNjU2MTM4ZjE1NjViNWIwMzYxMWU3OTU3ODE4MTYwNDAwMTgxOTA1MjUwNjExZjM5NTY1YjYwMDM2MDA0ODExMTE1NjExZThkNTc2MTFlOGM2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFlYTA1NzYxMWU5ZjYxMzhmMTU2NWI1YjAzNjExZWIzNTc4MTgxNjA2MDAxODE5MDUyNTA2MTFmMzg1NjViNjAwNDgwODExMTE1NjExZWM2NTc2MTFlYzU2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFlZDk1NzYxMWVkODYxMzhmMTU2NWI1YjAzNjExZjM3NTc2MDAwODA1NDkwNjEwMTAwMGE5MDA0NzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTYwODAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDViNWI1YjViNWI5MjkxNTA1MDU2NWI2MDAwNjAwMTYwMDA4MzYwMDY4MTExMTU2MTFmNWE1NzYxMWY1OTYxMzhmMTU2NWI1YjYwMDY4MTExMTU2MTFmNmM1NzYxMWY2YjYxMzhmMTU2NWI1YjgxNTI2MDIwMDE5MDgxNTI2MDIwMDE2MDAwMjA1NDkwNTA5MTkwNTA1NjViNjExZjhhNjEyMTNlNTY1YjYwMDE2MDA0ODExMTE1NjExZjllNTc2MTFmOWQ2MTM4ZjE1NjViNWI4MzYwMDQ4MTExMTU2MTFmYjE1NzYxMWZiMDYxMzhmMTU2NWI1YjAzNjExZmYzNTc4MTgxNjAyMDAxOTA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjkwODE3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI1MDUwNjEyMDU4NTY1YjYwMDQ4MDgxMTExNTYxMjAwNjU3NjEyMDA1NjEzOGYxNTY1YjViODM2MDA0ODExMTE1NjEyMDE5NTc2MTIwMTg2MTM4ZjE1NjViNWIwMzYxMjA1NzU3ODE4MTYwODAwMTkwNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY5MDgxNzNmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMTY4MTUyNTA1MDViNWI5MjkxNTA1MDU2NWI2MDAwODE2MGZmMTY2MDAxOTAxYjgzMTc5MDUwOTI5MTUwNTA1NjViNjA0MDUxODA2MDQwMDE2MDQwNTI4MDYwMDA4MTUyNjAyMDAxNjEyMDhjNjEyMTNlNTY1YjgxNTI1MDkwNTY1YjYwNDA1MTgwNjA2MDAxNjA0MDUyODA2MDAwNjAwNzBiODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDAwNjAwNzBiODE1MjUwOTA1NjViNjA0MDUxODA2MTAxMjAwMTYwNDA1MjgwNjA2MDgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwMDAxNTE1ODE1MjYwMjAwMTYwMDA2MDA3MGI4MTUyNjAyMDAxNjAwMDE1MTU4MTUyNjAyMDAxNjA2MDgxNTI2MDIwMDE2MTIxMzg2MTIwOTI1NjViODE1MjUwOTA1NjViNjA0MDUxODA2MGEwMDE2MDQwNTI4MDYwMDAxNTE1ODE1MjYwMjAwMTYwMDA3M2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYxNjgxNTI2MDIwMDE2MDYwODE1MjYwMjAwMTYwNjA4MTUyNjAyMDAxNjAwMDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjE2ODE1MjUwOTA=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw1jpVpBGsAQGEtF7Sji+9jZp8Eep5DZHOQc7ROPJG+ay6SqV2y0NoMwItaSixcVNoGgwIjeGerQYQu+zn5wIiDwoJCNHgnq0GELEIEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjS4J6tBhC3CBICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj6ByKAIDU2NWI2MDAwNjA0MDUxOTA1MDkwNTY1YjYwMDA4MGZkNWI2MDAwODBmZDViNjAwMDczZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZjgyMTY5MDUwOTE5MDUwNTY1YjYwMDA2MTIxZGE4MjYxMjFhZjU2NWI5MDUwOTE5MDUwNTY1YjYxMjFlYTgxNjEyMWNmNTY1YjgxMTQ2MTIxZjU1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTIyMDc4MTYxMjFlMTU2NWI5MjkxNTA1MDU2NWI2MDAwODE5MDUwOTE5MDUwNTY1YjYxMjIyMDgxNjEyMjBkNTY1YjgxMTQ2MTIyMmI1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTIyM2Q4MTYxMjIxNzU2NWI5MjkxNTA1MDU2NWI2MDAwODA2MDAwODA2MDgwODU4NzAzMTIxNTYxMjI1ZDU3NjEyMjVjNjEyMWE1NTY1YjViNjAwMDYxMjI2Yjg3ODI4ODAxNjEyMWY4NTY1Yjk0NTA1MDYwMjA2MTIyN2M4NzgyODgwMTYxMjFmODU2NWI5MzUwNTA2MDQwNjEyMjhkODc4Mjg4MDE2MTIxZjg1NjViOTI1MDUwNjA2MDYxMjI5ZTg3ODI4ODAxNjEyMjJlNTY1YjkxNTA1MDkyOTU5MTk0NTA5MjUwNTY1YjYwMDA4MTYwMDcwYjkwNTA5MTkwNTA1NjViNjEyMmMwODE2MTIyYWE1NjViODI1MjUwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDYxMjJkYjYwMDA4MzAxODQ2MTIyYjc1NjViOTI5MTUwNTA1NjViNjAwMDgwZmQ1YjYwMDA4MGZkNWI2MDAwNjAxZjE5NjAxZjgzMDExNjkwNTA5MTkwNTA1NjViN2Y0ZTQ4N2I3MTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDUyNjA0MTYwMDQ1MjYwMjQ2MDAwZmQ1YjYxMjMzNDgyNjEyMmViNTY1YjgxMDE4MTgxMTA2N2ZmZmZmZmZmZmZmZmZmZmY4MjExMTcxNTYxMjM1MzU3NjEyMzUyNjEyMmZjNTY1YjViODA2MDQwNTI1MDUwNTA1NjViNjAwMDYxMjM2NjYxMjE5YjU2NWI5MDUwNjEyMzcyODI4MjYxMjMyYjU2NWI5MTkwNTA1NjViNjAwMDY3ZmZmZmZmZmZmZmZmZmZmZjgyMTExNTYxMjM5MjU3NjEyMzkxNjEyMmZjNTY1YjViNjEyMzliODI2MTIyZWI1NjViOTA1MDYwMjA4MTAxOTA1MDkxOTA1MDU2NWI4MjgxODMzNzYwMDA4MzgzMDE1MjUwNTA1MDU2NWI2MDAwNjEyM2NhNjEyM2M1ODQ2MTIzNzc1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMjNlNjU3NjEyM2U1NjEyMmU2NTY1YjViNjEyM2YxODQ4Mjg1NjEyM2E4NTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEyNDBlNTc2MTI0MGQ2MTIyZTE1NjViNWI4MTM1NjEyNDFlODQ4MjYwMjA4NjAxNjEyM2I3NTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYxMjQzMDgxNjEyMmFhNTY1YjgxMTQ2MTI0M2I1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODEzNTkwNTA2MTI0NGQ4MTYxMjQyNzU2NWI5MjkxNTA1MDU2NWI2MDAwODA2MDAwODA2MDAwODA2MDAwNjBlMDg4OGEwMzEyMTU2MTI0NzI1NzYxMjQ3MTYxMjFhNTU2NWI1YjYwMDA2MTI0ODA4YTgyOGIwMTYxMjFmODU2NWI5NzUwNTA2MDIwNjEyNDkxOGE4MjhiMDE2MTIxZjg1NjViOTY1MDUwNjA0MDg4MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjRiMjU3NjEyNGIxNjEyMWFhNTY1YjViNjEyNGJlOGE4MjhiMDE2MTIzZjk1NjViOTU1MDUwNjA2MDg4MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjRkZjU3NjEyNGRlNjEyMWFhNTY1YjViNjEyNGViOGE4MjhiMDE2MTIzZjk1NjViOTQ1MDUwNjA4MDYxMjRmYzhhODI4YjAxNjEyMWY4NTY1YjkzNTA1MDYwYTA2MTI1MGQ4YTgyOGIwMTYxMjFmODU2NWI5MjUwNTA2MGMwNjEyNTFlOGE4MjhiMDE2MTI0M2U1NjViOTE1MDUwOTI5NTk4OTE5NDk3NTA5Mjk1NTA1NjViNjAwMDgwNjA0MDgzODUwMzEyMTU2MTI1NDQ1NzYxMjU0MzYxMjFhNTU2NWI1YjYwMDA2MTI1NTI4NTgyODYwMTYxMjFmODU2NWI5MjUwNTA2MDIwNjEyNTYzODU4Mjg2MDE2MTIyMmU1NjViOTE1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODExNTE1OTA1MDkxOTA1MDU2NWI2MTI1ODI4MTYxMjU2ZDU2NWI4MjUyNTA1MDU2NWI2MTI1OTE4MTYxMjFjZjU2NWI4MjUyNTA1MDU2NWI2MDAwODE1MTkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA1YjgzODExMDE1NjEyNWQxNTc4MDgyMDE1MTgxODQwMTUyNjAyMDgxMDE5MDUwNjEyNWI2NTY1YjYwMDA4NDg0MDE1MjUwNTA1MDUwNTY1YjYwMDA2MTI1ZTg4MjYxMjU5NzU2NWI2MTI1ZjI4MTg1NjEyNWEyNTY1YjkzNTA2MTI2MDI4MTg1NjAyMDg2MDE2MTI1YjM1NjViNjEyNjBiODE2MTIyZWI1NjViODQwMTkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MGEwODMwMTYwMDA4MzAxNTE2MTI2MmU2MDAwODYwMTgyNjEyNTc5NTY1YjUwNjAyMDgzMDE1MTYxMjY0MTYwMjA4NjAxODI2MTI1ODg1NjViNTA2MDQwODMwMTUxODQ4MjAzNjA0MDg2MDE1MjYxMjY1OTgyODI2MTI1ZGQ1NjViOTE1MDUwNjA2MDgzMDE1MTg0ODIwMzYwNjA4NjAxNTI2MTI2NzM4MjgyNjEyNWRkNTY1YjkxNTA1MDYwODA4MzAxNTE2MTI2ODg2MDgwODYwMTgyNjEyNTg4NTY1YjUwODA5MTUwNTA5MjkxNTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMjZhZDgxODQ2MTI2MTY1NjViOTA1MDkyOTE1MDUwNTY1YjYwMDA4MDYwNDA4Mzg1MDMxMjE1NjEyNmNjNTc2MTI2Y2I2MTIxYTU1NjViNWI2MDAwNjEyNmRhODU4Mjg2MDE2MTIxZjg1NjViOTI1MDUwNjAyMDgzMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjZmYjU3NjEyNmZhNjEyMWFhNTY1YjViNjEyNzA3ODU4Mjg2MDE2MTIzZjk1NjViOTE1MDUwOTI1MDkyOTA1MDU2NWI2MDAwODA2MDAwODA2MDgwODU4NzAzMTIxNTYxMjcyYjU3NjEyNzJhNjEyMWE1NTY1YjViNjAwMDYxMjczOTg3ODI4ODAxNjEyMWY4NTY1Yjk0NTA1MDYwMjA4NTAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI3NWE1NzYxMjc1OTYxMjFhYTU2NWI1YjYxMjc2Njg3ODI4ODAxNjEyM2Y5NTY1YjkzNTA1MDYwNDA4NTAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI3ODc1NzYxMjc4NjYxMjFhYTU2NWI1YjYxMjc5Mzg3ODI4ODAxNjEyM2Y5NTY1YjkyNTA1MDYwNjA2MTI3YTQ4NzgyODgwMTYxMjFmODU2NWI5MTUwNTA5Mjk1OTE5NDUwOTI1MDU2NWI2MDAwODA2MDAwNjA2MDg0ODYwMzEyMTU2MTI3Yzk1NzYxMjdjODYxMjFhNTU2NWI1YjYwMDA2MTI3ZDc4NjgyODcwMTYxMjFmODU2NWI5MzUwNTA2MDIwNjEyN2U4ODY4Mjg3MDE2MTIxZjg1NjViOTI1MDUwNjA0MDYxMjdmOTg2ODI4NzAxNjEyNDNlNTY1YjkxNTA1MDkyNTA5MjUwOTI1NjViNjAwMDY3ZmZmZmZmZmZmZmZmZmZmZjgyMTExNTYxMjgxZTU3NjEyODFkNjEyMmZjNTY1YjViNjEyODI3ODI2MTIyZWI1NjViOTA1MDYwMjA4MTAxOTA1MDkxOTA1MDU2NWI2MDAwNjEyODQ3NjEyODQyODQ2MTI4MDM1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMjg2MzU3NjEyODYyNjEyMmU2NTY1YjViNjEyODZlODQ4Mjg1NjEyM2E4NTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEyODhiNTc2MTI4OGE2MTIyZTE1NjViNWI4MTM1NjEyODliODQ4MjYwMjA4NjAxNjEyODM0NTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA4MDYwMDA4MDYwMDA4MDYwMDA4MDYwMDA4MDYxMDE0MDhiOGQwMzEyMTU2MTI4Yzg1NzYxMjhjNzYxMjFhNTU2NWI1YjYwMDA2MTI4ZDY4ZDgyOGUwMTYxMjFmODU2NWI5YTUwNTA2MDIwNjEyOGU3OGQ4MjhlMDE2MTIxZjg1NjViOTk1MDUwNjA0MDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjkwODU3NjEyOTA3NjEyMWFhNTY1YjViNjEyOTE0OGQ4MjhlMDE2MTIzZjk1NjViOTg1MDUwNjA2MDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjkzNTU3NjEyOTM0NjEyMWFhNTY1YjViNjEyOTQxOGQ4MjhlMDE2MTIzZjk1NjViOTc1MDUwNjA4MDYxMjk1MjhkODI4ZTAxNjEyMWY4NTY1Yjk2NTA1MDYwYTA2MTI5NjM4ZDgyOGUwMTYxMjFmODU2NWI5NTUwNTA2MGMwNjEyOTc0OGQ4MjhlMDE2MTI0M2U1NjViOTQ1MDUwNjBlMDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjk5NTU3NjEyOTk0NjEyMWFhNTY1YjViNjEyOWExOGQ=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw289wGGSbcef5aEbvkEzMThuecMLjoWnF74UnUMUnDEBdWs7PZ/H1XQ4DMieWaFhYGgwIjuGerQYQ6/GLswEiDwoJCNLgnq0GELcIEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjS4J6tBhC9CBICGAISAhgDGPbR6j4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBiCASAxj6ByKAIDgyOGUwMTYxMjg3NjU2NWI5MzUwNTA2MTAxMDA4YjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTI5YzM1NzYxMjljMjYxMjFhYTU2NWI1YjYxMjljZjhkODI4ZTAxNjEyODc2NTY1YjkyNTA1MDYxMDEyMDhiMDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMjlmMTU3NjEyOWYwNjEyMWFhNTY1YjViNjEyOWZkOGQ4MjhlMDE2MTI4NzY1NjViOTE1MDUwOTI5NTk4OWI5MTk0OTc5YTUwOTI5NTk4NTA1NjViNjAwMDgwNjAwMDgwNjAwMDYwYTA4Njg4MDMxMjE1NjEyYTJiNTc2MTJhMmE2MTIxYTU1NjViNWI2MDAwNjEyYTM5ODg4Mjg5MDE2MTIxZjg1NjViOTU1MDUwNjAyMDYxMmE0YTg4ODI4OTAxNjEyMWY4NTY1Yjk0NTA1MDYwNDA4NjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTJhNmI1NzYxMmE2YTYxMjFhYTU2NWI1YjYxMmE3Nzg4ODI4OTAxNjEyM2Y5NTY1YjkzNTA1MDYwNjA4NjAxMzU2N2ZmZmZmZmZmZmZmZmZmZmY4MTExMTU2MTJhOTg1NzYxMmE5NzYxMjFhYTU2NWI1YjYxMmFhNDg4ODI4OTAxNjEyM2Y5NTY1YjkyNTA1MDYwODA2MTJhYjU4ODgyODkwMTYxMjFmODU2NWI5MTUwNTA5Mjk1NTA5Mjk1OTA5MzUwNTY1YjYwMDA4MDYwNDA4Mzg1MDMxMjE1NjEyYWQ5NTc2MTJhZDg2MTIxYTU1NjViNWI2MDAwNjEyYWU3ODU4Mjg2MDE2MTIxZjg1NjViOTI1MDUwNjAyMDYxMmFmODg1ODI4NjAxNjEyMWY4NTY1YjkxNTA1MDkyNTA5MjkwNTA1NjViNjAwMDgwNjAwMDgwNjA4MDg1ODcwMzEyMTU2MTJiMWM1NzYxMmIxYjYxMjFhNTU2NWI1YjYwMDA2MTJiMmE4NzgyODgwMTYxMjFmODU2NWI5NDUwNTA2MDIwNjEyYjNiODc4Mjg4MDE2MTIxZjg1NjViOTM1MDUwNjA0MDg1MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMmI1YzU3NjEyYjViNjEyMWFhNTY1YjViNjEyYjY4ODc4Mjg4MDE2MTI4NzY1NjViOTI1MDUwNjA2MDg1MDEzNTY3ZmZmZmZmZmZmZmZmZmZmZjgxMTExNTYxMmI4OTU3NjEyYjg4NjEyMWFhNTY1YjViNjEyYjk1ODc4Mjg4MDE2MTI4NzY1NjViOTE1MDUwOTI5NTkxOTQ1MDkyNTA1NjViNjEyYmFhODE2MTIxY2Y1NjViODI1MjUwNTA1NjViNjEyYmI5ODE2MTIyMGQ1NjViODI1MjUwNTA1NjViNjAwMDYwODA4MjAxOTA1MDYxMmJkNDYwMDA4MzAxODc2MTJiYTE1NjViNjEyYmUxNjAyMDgzMDE4NjYxMmJhMTU2NWI2MTJiZWU2MDQwODMwMTg1NjEyYmExNTY1YjYxMmJmYjYwNjA4MzAxODQ2MTJiYjA1NjViOTU5NDUwNTA1MDUwNTA1NjViNjAwMDgxOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTJjMWE4MjYxMjU5NzU2NWI2MTJjMjQ4MTg1NjEyYzA0NTY1YjkzNTA2MTJjMzQ4MTg1NjAyMDg2MDE2MTI1YjM1NjViODA4NDAxOTE1MDUwOTI5MTUwNTA1NjViNjAwMDYxMmM0YzgyODQ2MTJjMGY1NjViOTE1MDgxOTA1MDkyOTE1MDUwNTY1YjYwMDA4MTYwMDMwYjkwNTA5MTkwNTA1NjViNjEyYzZkODE2MTJjNTc1NjViODExNDYxMmM3ODU3NjAwMDgwZmQ1YjUwNTY1YjYwMDA4MTUxOTA1MDYxMmM4YTgxNjEyYzY0NTY1YjkyOTE1MDUwNTY1YjYwMDA2MDIwODI4NDAzMTIxNTYxMmNhNjU3NjEyY2E1NjEyMWE1NTY1YjViNjAwMDYxMmNiNDg0ODI4NTAxNjEyYzdiNTY1YjkxNTA1MDkyOTE1MDUwNTY1YjdmNGU0ODdiNzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDA1MjYwMzI2MDA0NTI2MDI0NjAwMGZkNWI2MDAwODI4MjUyNjAyMDgyMDE5MDUwOTI5MTUwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNjY2MTY5NmM2NTY0MjEwMDAwMDAwMDAwNjAwMDgyMDE1MjUwNTY1YjYwMDA2MTJkMzM2MDFiODM2MTJjZWM1NjViOTE1MDYxMmQzZTgyNjEyY2ZkNTY1YjYwMjA4MjAxOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMmQ2MjgxNjEyZDI2NTY1YjkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTJkODU4MjYxMjU5NzU2NWI2MTJkOGY4MTg1NjEyZDY5NTY1YjkzNTA2MTJkOWY4MTg1NjAyMDg2MDE2MTI1YjM1NjViNjEyZGE4ODE2MTIyZWI1NjViODQwMTkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTJkYzg2MDAwODMwMTg1NjEyYmExNTY1YjgxODEwMzYwMjA4MzAxNTI2MTJkZGE4MTg0NjEyZDdhNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjYxMmRlYzgxNjEyNTZkNTY1YjgyNTI1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTJlMDc2MDAwODMwMTg1NjEyZGUzNTY1YjgxODEwMzYwMjA4MzAxNTI2MTJlMTk4MTg0NjEyZDdhNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjdmNTU3MDY0NjE3NDY1MjA2ZjY2MjA3NDZmNmI2NTZlMjA2YjY1Nzk3MzIwNjY2MTY5NmM2NTY0MjEwMDAwMDAwMDYwMDA4MjAxNTI1MDU2NWI2MDAwNjEyZTU4NjAxYzgzNjEyY2VjNTY1YjkxNTA2MTJlNjM4MjYxMmUyMjU2NWI2MDIwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDgxODEwMzYwMDA4MzAxNTI2MTJlODc4MTYxMmU0YjU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MTUxOTA1MDkxOTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDIyNjAwNDUyNjAyNDYwMDBmZDViNjAwMDYwMDI4MjA0OTA1MDYwMDE4MjE2ODA2MTJlZTA1NzYwN2Y4MjE2OTE1MDViNjAyMDgyMTA4MTAzNjEyZWYzNTc2MTJlZjI2MTJlOTk1NjViNWI1MDkxOTA1MDU2NWI2MDAwODE5MDUwODE2MDAwNTI2MDIwNjAwMDIwOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDYwMWY4MzAxMDQ5MDUwOTE5MDUwNTY1YjYwMDA4MjgyMWI5MDUwOTI5MTUwNTA1NjViNjAwMDYwMDg4MzAyNjEyZjViN2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmODI2MTJmMWU1NjViNjEyZjY1ODY4MzYxMmYxZTU2NWI5NTUwODAxOTg0MTY5MzUwODA4NjE2ODQxNzkyNTA1MDUwOTM5MjUwNTA1MDU2NWI2MDAwODE5MDUwOTE5MDUwNTY1YjYwMDA2MTJmYTI2MTJmOWQ2MTJmOTg4NDYxMjIwZDU2NWI2MTJmN2Q1NjViNjEyMjBkNTY1YjkwNTA5MTkwNTA1NjViNjAwMDgxOTA1MDkxOTA1MDU2NWI2MTJmYmM4MzYxMmY4NzU2NWI2MTJmZDA2MTJmYzg4MjYxMmZhOTU2NWI4NDg0NTQ2MTJmMmI1NjViODI1NTUwNTA1MDUwNTY1YjYwMDA5MDU2NWI2MTJmZTU2MTJmZDg1NjViNjEyZmYwODE4NDg0NjEyZmIzNTY1YjUwNTA1MDU2NWI1YjgxODExMDE1NjEzMDE0NTc2MTMwMDk2MDAwODI2MTJmZGQ1NjViNjAwMTgxMDE5MDUwNjEyZmY2NTY1YjUwNTA1NjViNjAxZjgyMTExNTYxMzA1OTU3NjEzMDJhODE2MTJlZjk1NjViNjEzMDMzODQ2MTJmMGU1NjViODEwMTYwMjA4NTEwMTU2MTMwNDI1NzgxOTA1MDViNjEzMDU2NjEzMDRlODU2MTJmMGU1NjViODMwMTgyNjEyZmY1NTY1YjUwNTA1YjUwNTA1MDU2NWI2MDAwODI4MjFjOTA1MDkyOTE1MDUwNTY1YjYwMDA2MTMwN2M2MDAwMTk4NDYwMDgwMjYxMzA1ZTU2NWIxOTgwODMxNjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MTMwOTU4MzgzNjEzMDZiNTY1YjkxNTA4MjYwMDIwMjgyMTc5MDUwOTI5MTUwNTA1NjViNjEzMGFlODI2MTJlOGU1NjViNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzMGM3NTc2MTMwYzY2MTIyZmM1NjViNWI2MTMwZDE4MjU0NjEyZWM4NTY1YjYxMzBkYzgyODI4NTYxMzAxODU2NWI2MDAwNjAyMDkwNTA2MDFmODMxMTYwMDE4MTE0NjEzMTBmNTc2MDAwODQxNTYxMzBmZDU3ODI4NzAxNTE5MDUwNWI2MTMxMDc4NTgyNjEzMDg5NTY1Yjg2NTU1MDYxMzE2ZjU2NWI2MDFmMTk4NDE2NjEzMTFkODY2MTJlZjk1NjViNjAwMDViODI4MTEwMTU2MTMxNDU1Nzg0ODkwMTUxODI1NTYwMDE4MjAxOTE1MDYwMjA4NTAxOTQ1MDYwMjA4MTAxOTA1MDYxMzEyMDU2NWI4NjgzMTAxNTYxMzE2MjU3ODQ4OTAxNTE2MTMxNWU2MDFmODkxNjgyNjEzMDZiNTY1YjgzNTU1MDViNjAwMTYwMDI4ODAyMDE4ODU1NTA1MDUwNWI1MDUwNTA1MDUwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNmI2NTc5NzMyMDY2NjE2OTZjNjU2NDIxNjA=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwy6QH5gCEbc7z10msAq3iGM+OPRNHAQSoWHQyoaVniDsSmBG4bqTPhyrSotfZ7eT0GgwIjuGerQYQk+/lpQMiDwoJCNLgnq0GEL0IEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjT4J6tBhDDCBICGAISAhgDGPKjnj4iAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjoIBgB8SAxj6ByL4HjAwODIwMTUyNTA1NjViNjAwMDYxMzFhZDYwMjA4MzYxMmNlYzU2NWI5MTUwNjEzMWI4ODI2MTMxNzc1NjViNjAyMDgyMDE5MDUwOTE5MDUwNTY1YjYwMDA2MDIwODIwMTkwNTA4MTgxMDM2MDAwODMwMTUyNjEzMWRjODE2MTMxYTA1NjViOTA1MDkxOTA1MDU2NWI3ZjU1NzA2NDYxNzQ2NTIwNmY2NjIwNzQ2ZjZiNjU2ZTQ5NmU2NjZmMmU3NDcyNjU2MTczNzU3Mjc5MjA2NjYxNjk2MDAwODIwMTUyN2Y2YzY1NjQyMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAyMDgyMDE1MjUwNTY1YjYwMDA2MTMyM2Y2MDI0ODM2MTJjZWM1NjViOTE1MDYxMzI0YTgyNjEzMWUzNTY1YjYwNDA4MjAxOTA1MDkxOTA1MDU2NWI2MDAwNjAyMDgyMDE5MDUwODE4MTAzNjAwMDgzMDE1MjYxMzI2ZTgxNjEzMjMyNTY1YjkwNTA5MTkwNTA1NjViN2Y1NTcwNjQ2MTc0NjUyMDZmNjYyMDc0NmY2YjY1NmU0OTZlNjY2ZjIwNmU2MTZkNjUyMDYxNmU2NDIwNzM3OTZkNjAwMDgyMDE1MjdmNjI2ZjZjMjA2NjYxNjk2YzY1NjQyMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMjA4MjAxNTI1MDU2NWI2MDAwNjEzMmQxNjAyYjgzNjEyY2VjNTY1YjkxNTA2MTMyZGM4MjYxMzI3NTU2NWI2MDQwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYwMjA4MjAxOTA1MDgxODEwMzYwMDA4MzAxNTI2MTMzMDA4MTYxMzJjNDU2NWI5MDUwOTE5MDUwNTY1YjYwMDA4MjgyNTI2MDIwODIwMTkwNTA5MjkxNTA1MDU2NWI2MDAwNjEzMzIzODI2MTJlOGU1NjViNjEzMzJkODE4NTYxMzMwNzU2NWI5MzUwNjEzMzNkODE4NTYwMjA4NjAxNjEyNWIzNTY1YjYxMzM0NjgxNjEyMmViNTY1Yjg0MDE5MTUwNTA5MjkxNTA1MDU2NWI2MTMzNWE4MTYxMjJhYTU2NWI4MjUyNTA1MDU2NWI2MDAwODE1MTkwNTA5MTkwNTA1NjViNjAwMDgyODI1MjYwMjA4MjAxOTA1MDkyOTE1MDUwNTY1YjYwMDA4MTkwNTA2MDIwODIwMTkwNTA5MTkwNTA1NjViNjEzMzk1ODE2MTIyMGQ1NjViODI1MjUwNTA1NjViNjAwMDYwYTA4MzAxNjAwMDgzMDE1MTYxMzNiMzYwMDA4NjAxODI2MTI1Nzk1NjViNTA2MDIwODMwMTUxNjEzM2M2NjAyMDg2MDE4MjYxMjU4ODU2NWI1MDYwNDA4MzAxNTE4NDgyMDM2MDQwODYwMTUyNjEzM2RlODI4MjYxMjVkZDU2NWI5MTUwNTA2MDYwODMwMTUxODQ4MjAzNjA2MDg2MDE1MjYxMzNmODgyODI2MTI1ZGQ1NjViOTE1MDUwNjA4MDgzMDE1MTYxMzQwZDYwODA4NjAxODI2MTI1ODg1NjViNTA4MDkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODMwMTYwMDA4MzAxNTE2MTM0MzA2MDAwODYwMTgyNjEzMzhjNTY1YjUwNjAyMDgzMDE1MTg0ODIwMzYwMjA4NjAxNTI2MTM0NDg4MjgyNjEzMzliNTY1YjkxNTA1MDgwOTE1MDUwOTI5MTUwNTA1NjViNjAwMDYxMzQ2MTgzODM2MTM0MTg1NjViOTA1MDkyOTE1MDUwNTY1YjYwMDA2MDIwODIwMTkwNTA5MTkwNTA1NjViNjAwMDYxMzQ4MTgyNjEzMzYwNTY1YjYxMzQ4YjgxODU2MTMzNmI1NjViOTM1MDgzNjAyMDgyMDI4NTAxNjEzNDlkODU2MTMzN2M1NjViODA2MDAwNWI4NTgxMTAxNTYxMzRkOTU3ODQ4NDAzODk1MjgxNTE2MTM0YmE4NTgyNjEzNDU1NTY1Yjk0NTA2MTM0YzU4MzYxMzQ2OTU2NWI5MjUwNjAyMDhhMDE5OTUwNTA2MDAxODEwMTkwNTA2MTM0YTE1NjViNTA4Mjk3NTA4Nzk1NTA1MDUwNTA1MDUwOTI5MTUwNTA1NjViNjA2MDgyMDE2MDAwODIwMTUxNjEzNTAxNjAwMDg1MDE4MjYxMzM1MTU2NWI1MDYwMjA4MjAxNTE2MTM1MTQ2MDIwODUwMTgyNjEyNTg4NTY1YjUwNjA0MDgyMDE1MTYxMzUyNzYwNDA4NTAxODI2MTMzNTE1NjViNTA1MDUwNTA1NjViNjAwMDYxMDE2MDgzMDE2MDAwODMwMTUxODQ4MjAzNjAwMDg2MDE1MjYxMzU0YjgyODI2MTMzMTg1NjViOTE1MDUwNjAyMDgzMDE1MTg0ODIwMzYwMjA4NjAxNTI2MTM1NjU4MjgyNjEzMzE4NTY1YjkxNTA1MDYwNDA4MzAxNTE2MTM1N2E2MDQwODYwMTgyNjEyNTg4NTY1YjUwNjA2MDgzMDE1MTg0ODIwMzYwNjA4NjAxNTI2MTM1OTI4MjgyNjEzMzE4NTY1YjkxNTA1MDYwODA4MzAxNTE2MTM1YTc2MDgwODYwMTgyNjEyNTc5NTY1YjUwNjBhMDgzMDE1MTYxMzViYTYwYTA4NjAxODI2MTMzNTE1NjViNTA2MGMwODMwMTUxNjEzNWNkNjBjMDg2MDE4MjYxMjU3OTU2NWI1MDYwZTA4MzAxNTE4NDgyMDM2MGUwODYwMTUyNjEzNWU1ODI4MjYxMzQ3NjU2NWI5MTUwNTA2MTAxMDA4MzAxNTE2MTM1ZmM2MTAxMDA4NjAxODI2MTM0ZWI1NjViNTA4MDkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTM2MWM2MDAwODMwMTg1NjEyYmExNTY1YjgxODEwMzYwMjA4MzAxNTI2MTM2MmU4MTg0NjEzNTJkNTY1YjkwNTA5MzkyNTA1MDUwNTY1YjYwMDA2MDQwODIwMTkwNTA2MTM2NGM2MDAwODMwMTg1NjEyYmExNTY1YjYxMzY1OTYwMjA4MzAxODQ2MTJiYjA1NjViOTM5MjUwNTA1MDU2NWI2MDAwODBmZDViNjAwMDgwZmQ1YjYxMzY3MzgxNjEyNTZkNTY1YjgxMTQ2MTM2N2U1NzYwMDA4MGZkNWI1MDU2NWI2MDAwODE1MTkwNTA2MTM2OTA4MTYxMzY2YTU2NWI5MjkxNTA1MDU2NWI2MDAwODE1MTkwNTA2MTM2YTU4MTYxMjFlMTU2NWI5MjkxNTA1MDU2NWI2MDAwNjEzNmJlNjEzNmI5ODQ2MTIzNzc1NjViNjEyMzVjNTY1YjkwNTA4MjgxNTI2MDIwODEwMTg0ODQ4NDAxMTExNTYxMzZkYTU3NjEzNmQ5NjEyMmU2NTY1YjViNjEzNmU1ODQ4Mjg1NjEyNWIzNTY1YjUwOTM5MjUwNTA1MDU2NWI2MDAwODI2MDFmODMwMTEyNjEzNzAyNTc2MTM3MDE2MTIyZTE1NjViNWI4MTUxNjEzNzEyODQ4MjYwMjA4NjAxNjEzNmFiNTY1YjkxNTA1MDkyOTE1MDUwNTY1YjYwMDA2MGEwODI4NDAzMTIxNTYxMzczMTU3NjEzNzMwNjEzNjYwNTY1YjViNjEzNzNiNjBhMDYxMjM1YzU2NWI5MDUwNjAwMDYxMzc0Yjg0ODI4NTAxNjEzNjgxNTY1YjYwMDA4MzAxNTI1MDYwMjA2MTM3NWY4NDgyODUwMTYxMzY5NjU2NWI2MDIwODMwMTUyNTA2MDQwODIwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzNzgzNTc2MTM3ODI2MTM2NjU1NjViNWI2MTM3OGY4NDgyODUwMTYxMzZlZDU2NWI2MDQwODMwMTUyNTA2MDYwODIwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzN2IzNTc2MTM3YjI2MTM2NjU1NjViNWI2MTM3YmY4NDgyODUwMTYxMzZlZDU2NWI2MDYwODMwMTUyNTA2MDgwNjEzN2QzODQ4Mjg1MDE2MTM2OTY1NjViNjA4MDgzMDE1MjUwOTI5MTUwNTA1NjViNjAwMDgwNjA0MDgzODUwMzEyMTU2MTM3ZjY1NzYxMzdmNTYxMjFhNTU2NWI1YjYwMDA2MTM4MDQ4NTgyODYwMTYxMmM3YjU2NWI5MjUwNTA2MDIwODMwMTUxNjdmZmZmZmZmZmZmZmZmZmZmODExMTE1NjEzODI1NTc2MTM4MjQ2MTIxYWE1NjViNWI2MTM4MzE4NTgyODYwMTYxMzcxYjU2NWI5MTUwNTA5MjUwOTI5MDUwNTY1YjYwMDA4MjgyNTI2MDIwODIwMTkwNTA5MjkxNTA1MDU2NWI2MDAwNjEzODU3ODI2MTMzNjA1NjViNjEzODYxODE4NTYxMzgzYjU2NWI5MzUwODM2MDIwODIwMjg1MDE2MTM4NzM4NTYxMzM3YzU2NWI4MDYwMDA1Yjg1ODExMDE1NjEzOGFmNTc4NDg0MDM4OTUyODE1MTYxMzg5MDg1ODI2MTM0NTU1NjViOTQ1MDYxMzg5YjgzNjEzNDY5NTY1YjkyNTA2MDIwOGEwMTk5NTA1MDYwMDE4MTAxOTA1MDYxMzg3NzU2NWI1MDgyOTc1MDg3OTU1MDUwNTA1MDUwNTA5MjkxNTA1MDU2NWI2MDAwNjA0MDgyMDE5MDUwNjEzOGQ2NjAwMDgzMDE4NTYxMmJhMTU2NWI4MTgxMDM2MDIwODMwMTUyNjEzOGU4ODE4NDYxMzg0YzU2NWI5MDUwOTM5MjUwNTA1MDU2NWI3ZjRlNDg3YjcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2MDAwNTI2MDIxNjAwNDUyNjAyNDYwMDBmZGZlYTI2NDY5NzA2NjczNTgyMjEyMjBlZDAxNzAwYmU1OTIxNmVjMDMxNjViOGEyMWZkOTVjY2NmOTQ3NmM1NmI5M2FiNTk5Nzg3MmE4ZGZkZWNiYTY0NjQ3MzZmNmM2MzQzMDAwODEyMDAzMw==","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwIQAZfNYFpZi2CixgzFa8eD2TV+T9wLQ7Ue6NH/Lpsduz0rkaOnwcSeqgolrTzL4sGgwIj+GerQYQg4ub1AEiDwoJCNPgnq0GEMMIEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjT4J6tBhDFCBICGAISAhgDGJb7rp0CIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5CRgoDGPoHGiISIFubmvxDH2eR+bvtkrFPRL8rNvEfUIcGEOodOl0vth4JIICS9AFCBQiAztoDUgBaAGoLY2VsbGFyIGRvb3I=","b64Record":"CiUIFiIDGPsHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjADGtTy9j1SPdUQ7QPIz4qokUCRyFXOvuQQiRNBYNa3M8UzEAF9juCs626fhi1m5YwaDAiP4Z6tBhD7hLHDAyIPCgkI0+CerQYQxQgSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjjCAmKtsQox1CgMY+wcS1nJggGBAUmAENhBhAMJXYAA1YOAcgGN9y7xyEWEAf1eAY+rG8/4RYQBZV4Bj6sbz/hRhAlRXgGPrVI7/FGECkVeAY/AHKZAUYQKtV4Bj8XMHYBRhAslXYQDCVluAY33LvHIUYQHfV4BjmyPT2RRhAftXgGPmCOGNFGECOFdhAMJWW4BjEeH8BxRhAMdXgGMV2svqFGEBBFeAYzqhLvUUYQFBV4BjQSm32xRhAV1XgGNhjcZeFGEBmleAY3j+cqEUYQHDV1tgAID9WzSAFWEA01dgAID9W1BhAO5gBIA2A4EBkGEA6ZGQYSJDVlthAuVWW2BAUWEA+5GQYSLGVltgQFGAkQOQ81s0gBVhARBXYACA/VtQYQErYASANgOBAZBhASaRkGEiQ1ZbYQQBVltgQFFhATiRkGEixlZbYEBRgJEDkPNbYQFbYASANgOBAZBhAVaRkGEkU1ZbYQUfVlsAWzSAFWEBaVdgAID9W1BhAYRgBIA2A4EBkGEBf5GQYSUtVlthB3BWW2BAUWEBkZGQYSaTVltgQFGAkQOQ81s0gBVhAaZXYACA/VtQYQHBYASANgOBAZBhAbyRkGEmtVZbYQenVlsAW2EB3WAEgDYDgQGQYQHYkZBhJxFWW2EIzlZbAFthAflgBIA2A4EBkGEB9JGQYSewVlthCmVWWwBbNIAVYQIHV2AAgP1bUGECImAEgDYDgQGQYQIdkZBhIkNWW2ELhVZbYEBRYQIvkZBhIsZWW2BAUYCRA5DzW2ECUmAEgDYDgQGQYQJNkZBhKKRWW2EMo1ZbAFs0gBVhAmBXYACA/VtQYQJ7YASANgOBAZBhAnaRkGEiQ1ZbYQ6AVltgQFFhAoiRkGEixlZbYEBRgJEDkPNbYQKrYASANgOBAZBhAqaRkGEqD1ZbYQ+cVlsAW2ECx2AEgDYDgQGQYQLCkZBhKsJWW2ETNFZbAFthAuNgBIA2A4EBkGEC3pGQYSsCVlthFYdWWwBbYACAYABhAWdz//////////////////////////8WY5sj09lg4BuIiIiIYEBRYCQBYQMilJOSkZBhK79WW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEDjJGQYSxAVltgAGBAUYCDA4GFWvSRUFA9gGAAgRRhA8dXYEBRkVBgHxlgPz0BFoIBYEBSPYJSPWAAYCCEAT5hA8xWW2BgkVBbUJFQkVCBYQPdV2AVYQPyVluAgGAgAZBRgQGQYQPxkZBhLJBWW1tgAwuSUFBQlJNQUFBQVltgAIBgAGEBZ3P//////////////////////////xZjFdrL6mDgG4iIiIhgQFFgJAFhBD6Uk5KRkGErv1ZbYEBRYCCBgwMDgVKQYEBSkHv/////////////////////////////////////GRZgIIIBgFF7/////////////////////////////////////4OBgxYXg1JQUFBQYEBRYQSokZBhLEBWW2AAYEBRgIMDgWAAhlrxkVBQPYBgAIEUYQTlV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YQTqVltgYJFQW1CRUJFQgWEE+1dgFWEFEFZbgIBgIAGQUYEBkGEFD5GQYSyQVltbYAMLklBQUJSTUFBQUFZbYABgBWf//////////4ERFWEFPFdhBTthIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhBXVXgWAgAVthBWJhIHJWW4FSYCABkGABkAOQgWEFWleQUFtQkFBhBYdgAGABYAKJYRbIVluBYACBUYEQYQWbV2EFmmEsvVZbW2AgAmAgAQGBkFJQYQW0YAJgA4CIYRbIVluBYAGBUYEQYQXIV2EFx2EsvVZbW2AgAmAgAQGBkFJQYQXgYARgAYZhFwFWW4FgAoFRgRBhBfRXYQXzYSy9VltbYCACYCABAYGQUlBhBgxgBmABhmEXAVZbgWADgVGBEGEGIFdhBh9hLL1WW1tgIAJgIAEBgZBSUGEGOGAFYASGYRcBVluBYASBUYEQYQZMV2EGS2EsvVZbW2AgAmAgAQGBkFJQYQZfYSCSVltgAIFgAAGQYAcLkIFgBwuBUlBQg4FgIAGQc///////////////////////////FpCBc///////////////////////////FoFSUFCCgWBAAZBgBwuQgWAHC4FSUFBhBsRhIM9WW4iBYEABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQgoFg4AGBkFJQgYFhAQABgZBSUGAAYQcbi4NhFzhWW5BQYBZgAwuBFGEHY1dgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEHWpBhLUlWW2BAUYCRA5D9W1BQUFBQUFBQUFBQVlthB3hhIT5WW2AAgGEHhYWFYRhQVluRUGAHC5FQYBZgAwuCFGEHnFdgAID9W4CSUFBQkpFQUFZbYACAYQFnc///////////////////////////FmNhjcZeYOAbhYVgQFFgJAFhB96SkZBhLbNWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEISJGQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEIhVdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEIilZbYGCRUFtQkVCRUH9K9HgOBv6Mud9ksHlPpvATma+XkXW7mI414OV+WUVnvIKCYEBRYQjAkpGQYS3yVltgQFGAkQOQoVBQUFBWW2AAYAVn//////////+BERVhCOtXYQjqYSL8VltbYEBRkICCUoBgIAJgIAGCAWBAUoAVYQkkV4FgIAFbYQkRYSByVluBUmAgAZBgAZADkIFhCQlXkFBbUJBQYQk2YABgAWACh2EWyFZbgWAAgVGBEGEJSldhCUlhLL1WW1tgIAJgIAEBgZBSUGEJY2ACYAOAhmEWyFZbgWABgVGBEGEJd1dhCXZhLL1WW1tgIAJgIAEBgZBSUGEJj2AEYAGEYRcBVluBYAKBUYEQYQmjV2EJomEsvVZbW2AgAmAgAQGBkFJQYQm7YAZgAYRhFwFWW4FgA4FRgRBhCc9XYQnOYSy9VltbYCACYCABAYGQUlBhCedgBWAEhGEXAVZbgWAEgVGBEGEJ+1dhCfphLL1WW1tgIAJgIAEBgZBSUGAAYQoShoNhGYRWW2AHC5BQYBZgAwuBFGEKXVdgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEKVJBhLm5WW2BAUYCRA5D9W1BQUFBQUFZbYABgAWf//////////4ERFWEKgldhCoFhIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhCrtXgWAgAVthCqhhIHJWW4FSYCABkGABkAOQgWEKoFeQUFtQkFBhCsZhIHJWW2AEgWAAAYGBUlBQYQrZYSE+VlswgWAgAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGABgWAAAZAVFZCBFRWBUlBQgIJgIAGBkFJQgYNgAIFRgRBhC0BXYQs/YSy9VltbYCACYCABAYGQUlBgAGELWzBgAIiIiGEanFZbkFBgAGELaYiDYRc4VluQUGAWYAMLgRRhC3tXYACA/VtQUFBQUFBQUFZbYACAYABhAWdz//////////////////////////8WY5sj09lg4BuIiIiIYEBRYCQBYQvClJOSkZBhK79WW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEMLJGQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEMaVdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEMblZbYGCRUFtQkVCRUIFhDH9XYBVhDJRWW4CAYCABkFGBAZBhDJORkGEskFZbW2ADC5JQUFCUk1BQUFBWW2AAYAVn//////////+BERVhDMBXYQy/YSL8VltbYEBRkICCUoBgIAJgIAGCAWBAUoAVYQz5V4FgIAFbYQzmYSByVluBUmAgAZBgAZADkIFhDN5XkFBbUJBQYQ0LYABgAWACjGEWyFZbgWAAgVGBEGENH1dhDR5hLL1WW1tgIAJgIAEBgZBSUGENOGACYAOAi2EWyFZbgWABgVGBEGENTFdhDUthLL1WW1tgIAJgIAEBgZBSUGENZGAEYAGJYRcBVluBYAKBUYEQYQ14V2ENd2EsvVZbW2AgAmAgAQGBkFJQYQ2QYAZgAYlhFwFWW4FgA4FRgRBhDaRXYQ2jYSy9VltbYCACYCABAYGQUlBhDbxgBWAEiWEXAVZbgWAEgVGBEGEN0FdhDc9hLL1WW1tgIAJgIAEBgZBSUINgApCBYQ3qkZBhMKVWW1CCYAOQgWEN+pGQYTClVltQgWAEkIFhDgqRkGEwpVZbUGAAYQ4bi2AAiYmGYRqcVluQUGAAYQ4pjYNhFzhWW5BQYBZgAwuBFGEOcVdgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEOaJBhLUlWW2BAUYCRA5D9W1BQUFBQUFBQUFBQUFBWW2AAgGAAYQFnc///////////////////////////FmMV2svqYOAbiIiIiGBAUWAkAWEOvZSTkpGQYSu/VltgQFFgIIGDAwOBUpBgQFKQe/////////////////////////////////////8ZFmAgggGAUXv/////////////////////////////////////g4GDFheDUlBQUFBgQFFhDyeRkGEsQFZbYABgQFGAgwOBhVr0kVBQPYBgAIEUYQ9iV2BAUZFQYB8ZYD89ARaCAWBAUj2CUj1gAGAghAE+YQ9nVltgYJFQW1CRUJFQgWEPeFdgFWEPjVZbgIBgIAGQUYEBkGEPjJGQYSyQVltbYAMLklBQUJSTUFBQUFZbYABgBWf//////////4ERFWEPuVdhD7hhIvxWW1tgQFGQgIJSgGAgAmAgAYIBYEBSgBVhD/JXgWAgAVthD99hIHJWW4FSYCABkGABkAOQgWEP11eQUFtQkFBhEARgAGABYAKHYRbIVluBYACBUYEQYRAYV2EQF2EsvVZbW2AgAmAgAQGBkFJQYRAxYAJgA4CGYRbIVluBYAGBUYEQYRBFV2EQRGEsvVZbW2AgAmAgAQGBkFJQYRBdYARgAYRhFwFWW4FgAoFRgRBhEHFXYRBwYSy9VltbYCACYCABAYGQUlBhEIlgBmABhGEXAVZbgWADgVGBEGEQnVdhEJxhLL1WW1tgIAJgIAEBgZBSUGEQtWAFYASEYRcBVluBYASBUYEQYRDJV2EQyGEsvVZbW2AgAmAgAQGBkFJQYRDcYSDPVluFgWBAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGACgFRhESGQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGERTZBhLshWW4AVYRGaV4BgHxBhEW9XYQEAgINUBAKDUpFgIAGRYRGaVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWERfVeCkANgHxaCAZFbUFBQUFCBYAABgZBSUGADgFRhEbSQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGER4JBhLshWW4AVYRItV4BgHxBhEgJXYQEAgINUBAKDUpFgIAGRYRItVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWESEFeCkANgHxaCAZFbUFBQUFCBYCABgZBSUIGBYOABgZBSUGAEgFRhElCQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGESfJBhLshWW4AVYRLJV4BgHxBhEp5XYQEAgINUBAKDUpFgIAGRYRLJVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWESrFeCkANgHxaCAZFbUFBQUFCBYGABgZBSUGAAYRLiiINhFzhWW5BQYBZgAwuBFGETKldgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWETIZBhMcNWW2BAUYCRA5D9W1BQUFBQUFBQVlthEzxhIM9WW2ACgFRhE0mQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGETdZBhLshWW4AVYRPCV4BgHxBhE5dXYQEAgINUBAKDUpFgIAGRYRPCVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWETpVeCkANgHxaCAZFbUFBQUFCBYAABgZBSUGADgFRhE9yQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGEUCJBhLshWW4AVYRRVV4BgHxBhFCpXYQEAgINUBAKDUpFgIAGRYRRVVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWEUOFeCkANgHxaCAZFbUFBQUFCBYCABgZBSUIGBYEABkHP//////////////////////////xaQgXP//////////////////////////xaBUlBQYASAVGEUp5BhLshWW4BgHwFgIICRBAJgIAFgQFGQgQFgQFKAkpGQgYFSYCABgoBUYRTTkGEuyFZbgBVhFSBXgGAfEGEU9VdhAQCAg1QEAoNSkWAgAZFhFSBWW4IBkZBgAFJgIGAAIJBbgVSBUpBgAQGQYCABgIMRYRUDV4KQA2AfFoIBkVtQUFBQUIFgYAGBkFJQYABhFTmEg2EXOFZbkFBgFmADC4EUYRWBV2BAUX8Iw3mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFSYAQBYRV4kGEyVVZbYEBRgJEDkP1bUFBQUFZbYRWPYSDPVluCgWAAAYGQUlCBgWAgAYGQUlCDgWBAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGAEgFRhFeaQYS7IVluAYB8BYCCAkQQCYCABYEBRkIEBYEBSgJKRkIGBUmAgAYKAVGEWEpBhLshWW4AVYRZfV4BgHxBhFjRXYQEAgINUBAKDUpFgIAGRYRZfVluCAZGQYABSYCBgACCQW4FUgVKQYAEBkGAgAYCDEWEWQleCkANgHxaCAZFbUFBQUFCBYGABgZBSUGAAYRZ4hoNhFzhWW5BQYBZgAwuBFGEWwFdgQFF/CMN5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBUmAEAWEWt5BhMudWW2BAUYCRA5D9W1BQUFBQUFZbYRbQYSByVltgQFGAYEABYEBSgGEW5YeHYR0WVluBUmAgAWEW9IWFYR1sVluBUlCQUJSTUFBQUFZbYRcJYSByVltgQFGAYEABYEBSgGEXHYZhH0FWW4FSYCABYRcshYVhH4JWW4FSUJBQk5JQUFBWW2AAgGAAYQFnc///////////////////////////FmN9MFz6YOAbhoZgQFFgJAFhF3GSkZBhNgdWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEX25GQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEYGFdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEYHVZbYGCRUFtQkVCRUIFhGC5XYBVhGENWW4CAYCABkFGBAZBhGEKRkGEskFZbW2ADC5JQUFCSkVBQVltgAGEYWmEhPlZbYACAYQFnc///////////////////////////FmM8TdMuYOAbh4dgQFFgJAFhGJGSkZBhNjdWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEY+5GQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEZOFdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEZPVZbYGCRUFtQkVCRUGEZSmEhPlZbgmEZV1dgFYFhGWxWW4GAYCABkFGBAZBhGWuRkGE331ZbW4FgAwuRUICVUIGWUFBQUFBQklCSkFBWW2AAgGAAYQFnc///////////////////////////FmNvw8uvYOAbhoZgQFFgJAFhGb2SkZBhOMFWW2BAUWAggYMDA4FSkGBAUpB7/////////////////////////////////////xkWYCCCAYBRe/////////////////////////////////////+DgYMWF4NSUFBQUGBAUWEaJ5GQYSxAVltgAGBAUYCDA4FgAIZa8ZFQUD2AYACBFGEaZFdgQFGRUGAfGWA/PQEWggFgQFI9glI9YABgIIQBPmEaaVZbYGCRUFtQkVCRUIFhGnpXYBVhGo9WW4CAYCABkFGBAZBhGo6RkGEskFZbW2ADC5JQUFCSkVBQVlthGqRhIM9WW2EarGEgklZbhYFgAAGQYAcLkIFgBwuBUlBQhIFgIAGQc///////////////////////////FpCBc///////////////////////////FoFSUFCDgWBAAZBgBwuQgWAHC4FSUFBgAoBUYRsVkGEuyFZbgGAfAWAggJEEAmAgAWBAUZCBAWBAUoCSkZCBgVJgIAGCgFRhG0GQYS7IVluAFWEbjleAYB8QYRtjV2EBAICDVAQCg1KRYCABkWEbjlZbggGRkGAAUmAgYAAgkFuBVIFSkGABAZBgIAGAgxFhG3FXgpADYB8WggGRW1BQUFBQgmAAAYGQUlBgA4BUYRuokGEuyFZbgGAfAWAggJEEAmAgAWBAUZCBAWBAUoCSkZCBgVJgIAGCgFRhG9SQYS7IVluAFWEcIVeAYB8QYRv2V2EBAICDVAQCg1KRYCABkWEcIVZbggGRkGAAUmAgYAAgkFuBVIFSkGABAZBgIAGAgxFhHARXgpADYB8WggGRW1BQUFBQgmAgAYGQUlCGgmBAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUIKCYOABgZBSUICCYQEAAYGQUlBgBIBUYRyGkGEuyFZbgGAfAWAggJEEAmAgAWBAUZCBAWBAUoCSkZCBgVJgIAGCgFRhHLKQYS7IVluAFWEc/1eAYB8QYRzUV2EBAICDVAQCg1KRYCABkWEc/1ZbggGRkGAAUmAgYAAgkFuBVIFSkGABAZBgIAGAgxFhHOJXgpADYB8WggGRW1BQUFBQgmBgAYGQUlBQlZRQUFBQUFZbYABhHT2DYAaBERVhHS5XYR0tYTjxVltbgmEgXpCRkGP/////FlZbkFBhHWSCYAaBERVhHVVXYR1UYTjxVltbgmEgXpCRkGP/////FlZbkFCSkVBQVlthHXRhIT5WW2AAYASBERVhHYhXYR2HYTjxVltbg2AEgREVYR2bV2EdmmE48VZbWwNhHbZXYAGBYAABkBUVkIEVFYFSUFBhHztWW2ABYASBERVhHcpXYR3JYTjxVltbg2AEgREVYR3dV2Ed3GE48VZbWwNhHj9XYACAVJBhAQAKkARz//////////////////////////8WgWAgAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUGEfOlZbYAJgBIERFWEeU1dhHlJhOPFWW1uDYASBERVhHmZXYR5lYTjxVltbA2EeeVeBgWBAAYGQUlBhHzlWW2ADYASBERVhHo1XYR6MYTjxVltbg2AEgREVYR6gV2Een2E48VZbWwNhHrNXgYFgYAGBkFJQYR84VltgBICBERVhHsZXYR7FYTjxVltbg2AEgREVYR7ZV2Ee2GE48VZbWwNhHzdXYACAVJBhAQAKkARz//////////////////////////8WgWCAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUFtbW1tbkpFQUFZbYABgAWAAg2AGgREVYR9aV2EfWWE48VZbW2AGgREVYR9sV2Efa2E48VZbW4FSYCABkIFSYCABYAAgVJBQkZBQVlthH4phIT5WW2ABYASBERVhH55XYR+dYTjxVltbg2AEgREVYR+xV2EfsGE48VZbWwNhH/NXgYFgIAGQc///////////////////////////FpCBc///////////////////////////FoFSUFBhIFhWW2AEgIERFWEgBldhIAVhOPFWW1uDYASBERVhIBlXYSAYYTjxVltbA2EgV1eBgWCAAZBz//////////////////////////8WkIFz//////////////////////////8WgVJQUFtbkpFQUFZbYACBYP8WYAGQG4MXkFCSkVBQVltgQFGAYEABYEBSgGAAgVJgIAFhIIxhIT5WW4FSUJBWW2BAUYBgYAFgQFKAYABgBwuBUmAgAWAAc///////////////////////////FoFSYCABYABgBwuBUlCQVltgQFGAYQEgAWBAUoBgYIFSYCABYGCBUmAgAWAAc///////////////////////////FoFSYCABYGCBUmAgAWAAFRWBUmAgAWAAYAcLgVJgIAFgABUVgVJgIAFgYIFSYCABYSE4YSCSVluBUlCQVltgQFGAYKABYEBSgGAAFRWBUmAgAWAAc///////////////////////////FoFSYCABYGCBUmAgAWBggVJgIAFgAHP//////////////////////////xaBUlCQVltgAGBAUZBQkFZbYACA/VtgAID9W2AAc///////////////////////////ghaQUJGQUFZbYABhIdqCYSGvVluQUJGQUFZbYSHqgWEhz1ZbgRRhIfVXYACA/VtQVltgAIE1kFBhIgeBYSHhVluSkVBQVltgAIGQUJGQUFZbYSIggWEiDVZbgRRhIitXYACA/VtQVltgAIE1kFBhIj2BYSIXVluSkVBQVltgAIBgAIBggIWHAxIVYSJdV2EiXGEhpVZbW2AAYSJrh4KIAWEh+FZblFBQYCBhInyHgogBYSH4VluTUFBgQGEijYeCiAFhIfhWW5JQUGBgYSKeh4KIAWEiLlZbkVBQkpWRlFCSUFZbYACBYAcLkFCRkFBWW2EiwIFhIqpWW4JSUFBWW2AAYCCCAZBQYSLbYACDAYRhIrdWW5KRUFBWW2AAgP1bYACA/VtgAGAfGWAfgwEWkFCRkFBWW39OSHtxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAUmBBYARSYCRgAP1bYSM0gmEi61ZbgQGBgRBn//////////+CERcVYSNTV2EjUmEi/FZbW4BgQFJQUFBWW2AAYSNmYSGbVluQUGEjcoKCYSMrVluRkFBWW2AAZ///////////ghEVYSOSV2EjkWEi/FZbW2Ejm4JhIutWW5BQYCCBAZBQkZBQVluCgYM3YACDgwFSUFBQVltgAGEjymEjxYRhI3dWW2EjXFZbkFCCgVJgIIEBhISEAREVYSPmV2Ej5WEi5lZbW2Ej8YSChWEjqFZbUJOSUFBQVltgAIJgH4MBEmEkDldhJA1hIuFWW1uBNWEkHoSCYCCGAWEjt1ZbkVBQkpFQUFZbYSQwgWEiqlZbgRRhJDtXYACA/VtQVltgAIE1kFBhJE2BYSQnVluSkVBQVltgAIBgAIBgAIBgAGDgiIoDEhVhJHJXYSRxYSGlVltbYABhJICKgosBYSH4VluXUFBgIGEkkYqCiwFhIfhWW5ZQUGBAiAE1Z///////////gREVYSSyV2EksWEhqlZbW2EkvoqCiwFhI/lWW5VQUGBgiAE1Z///////////gREVYSTfV2Ek3mEhqlZbW2Ek64qCiwFhI/lWW5RQUGCAYST8ioKLAWEh+FZbk1BQYKBhJQ2KgosBYSH4VluSUFBgwGElHoqCiwFhJD5WW5FQUJKVmJGUl1CSlVBWW2AAgGBAg4UDEhVhJURXYSVDYSGlVltbYABhJVKFgoYBYSH4VluSUFBgIGElY4WChgFhIi5WW5FQUJJQkpBQVltgAIEVFZBQkZBQVlthJYKBYSVtVluCUlBQVlthJZGBYSHPVluCUlBQVltgAIFRkFCRkFBWW2AAgoJSYCCCAZBQkpFQUFZbYABbg4EQFWEl0VeAggFRgYQBUmAggQGQUGEltlZbYACEhAFSUFBQUFZbYABhJeiCYSWXVlthJfKBhWElolZbk1BhJgKBhWAghgFhJbNWW2EmC4FhIutWW4QBkVBQkpFQUFZbYABgoIMBYACDAVFhJi5gAIYBgmEleVZbUGAggwFRYSZBYCCGAYJhJYhWW1BgQIMBUYSCA2BAhgFSYSZZgoJhJd1WW5FQUGBggwFRhIIDYGCGAVJhJnOCgmEl3VZbkVBQYICDAVFhJohggIYBgmEliFZbUICRUFCSkVBQVltgAGAgggGQUIGBA2AAgwFSYSatgYRhJhZWW5BQkpFQUFZbYACAYECDhQMSFWEmzFdhJsthIaVWW1tgAGEm2oWChgFhIfhWW5JQUGAggwE1Z///////////gREVYSb7V2Em+mEhqlZbW2EnB4WChgFhI/lWW5FQUJJQkpBQVltgAIBgAIBggIWHAxIVYScrV2EnKmEhpVZbW2AAYSc5h4KIAWEh+FZblFBQYCCFATVn//////////+BERVhJ1pXYSdZYSGqVltbYSdmh4KIAWEj+VZbk1BQYECFATVn//////////+BERVhJ4dXYSeGYSGqVltbYSeTh4KIAWEj+VZbklBQYGBhJ6SHgogBYSH4VluRUFCSlZGUUJJQVltgAIBgAGBghIYDEhVhJ8lXYSfIYSGlVltbYABhJ9eGgocBYSH4VluTUFBgIGEn6IaChwFhIfhWW5JQUGBAYSf5hoKHAWEkPlZbkVBQklCSUJJWW2AAZ///////////ghEVYSgeV2EoHWEi/FZbW2EoJ4JhIutWW5BQYCCBAZBQkZBQVltgAGEoR2EoQoRhKANWW2EjXFZbkFCCgVJgIIEBhISEAREVYShjV2EoYmEi5lZbW2EoboSChWEjqFZbUJOSUFBQVltgAIJgH4MBEmEoi1dhKIphIuFWW1uBNWEom4SCYCCGAWEoNFZbkVBQkpFQUFZbYACAYACAYACAYACAYACAYQFAi40DEhVhKMhXYSjHYSGlVltbYABhKNaNgo4BYSH4VluaUFBgIGEo542CjgFhIfhWW5lQUGBAiwE1Z///////////gREVYSkIV2EpB2EhqlZbW2EpFI2CjgFhI/lWW5hQUGBgiwE1Z///////////gREVYSk1V2EpNGEhqlZbW2EpQY2CjgFhI/lWW5dQUGCAYSlSjYKOAWEh+FZbllBQYKBhKWONgo4BYSH4VluVUFBgwGEpdI2CjgFhJD5WW5RQUGDgiwE1Z///////////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"},{"b64Body":"Cg8KCQjU4J6tBhDHCBICGAISAhgDGP7v5OgCIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo7qAUwKBlRva2VuRBIIRk5XRFZLRUQqAxj4B1IiEiC87mXWk5k3PTCiEalFU1Kg6JWU2mXOG0gaV32aec+q+GoMCJCv+bAGEOjQvNABiAEB","b64Record":"CiUIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkD1IDGPwHEjAO5A2vZmSCkMLTCXPoKmXfgWiUQVXGApS4eODfjNj48fDkw7AnQJf9K2/YXTmX3xgaDAiQ4Z6tBhC7mrjnASIPCgkI1OCerQYQxwgSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIAcgoKAxj8BxIDGPgH"},{"b64Body":"Cg8KCQjU4J6tBhDNCBICGAISAhgDGNPtlwgiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjqoCCwoDGPwHGgRuZnQ1","b64Record":"CiUIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkD1gBcgEBEjDdPKbLVBT98SYB2SLt/ZfmncwaF1C7xjdc3nvG4JOQXkoJeQmp6rSxGWh727sbXKoaDAiQ4Z6tBhCrtIHMAyIPCgkI1OCerQYQzQgSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIAWhIKAxj8BxoLCgIYABIDGPgHGAE="},{"b64Body":"Cg8KCQjV4J6tBhDVCBICGAISAhgDGOC2iyAiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjsICCgoDGPkHEgMY/Ac=","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwbHgOwAZyjtj2kbWXwkavPYOTCGbmo0PbdSJpy3oa1TsrSi8NiDlOQcWPHjZ2NkIrGgwIkeGerQYQo4u/gwIiDwoJCNXgnq0GENUIEgIYAiogw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo5SAA=="},{"b64Body":"Cg8KCQjW4J6tBhDbCBICGAISAhgDGOCssQMiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjjpPCgMY+wcQoI0GIkRBKbfbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQ==","b64Record":"CiUIISIDGPsHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjAR/Hn2DQqbvJSwyfp56F6HSyOWXbY9u3d4UATcK3Tn27lt7bJRjxSp39mqpcQamaIaCwiS4Z6tBhDT0dExIg8KCQjW4J6tBhDbCBICGAIqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOMLnwrQM6CBoCMHgo/4YGUhYKCQoCGAIQ8eDbBgoJCgIYYhDy4NsG"},{"b64Body":"Cg8KCQjW4J6tBhDdCBICGAISAhgDGOCssQMiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjjpPCgMY+wcQoI0GIkRBKbfbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA==","b64Record":"CiUIISIDGPsHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjCe1yFoCzbscPN8gU2iBtT/GFPBZijWsJVISHn2vq1fYrKB6sqWFRtMkfMHTrZH7H8aDAiS4Z6tBhCLqKWzAiIPCgkI1uCerQYQ3QgSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjjCA19oCOggaAjB4KIDxBFIWCgkKAhgCEP+ttQUKCQoCGGIQgK61BQ=="},{"b64Body":"ChEKCQjW4J6tBhDdCBICGAIgATpNCgMY5wIQASJEPE3TLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA=","b64Record":"CgMIpwESMGrqksQXzTR/Fhmxq7BCtkHdvdKgL+j8RpXQhJ/E1x8albVAivE1agW3+tnBg3CHSxoMCJLhnq0GEIyopbMCIhEKCQjW4J6tBhDdCBICGAIgATpACgMY5wISIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACnGhBJTlZBTElEX1RPS0VOX0lEKGRqAxj7B1IAegwIkuGerQYQi6ilswI="},{"b64Body":"Cg8KCQjX4J6tBhDfCBICGAISAhgDGOCssQMiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjjpPCgMY+wcQoI0GIkRBKbfbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==","b64Record":"CiUIISIDGPsHKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjDkg1CqqC0nQj4fS7qAxKzCvhuSm3e3E69rYzLQPF313VBK7vU+GuWPsEkoK2inMcwaCwiT4Z6tBhDDi9o7Ig8KCQjX4J6tBhDfCBICGAIqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOMIDX2gI6CBoCMHgogPEEUhYKCQoCGAIQ/621BQoJCgIYYhCArrUF"},{"b64Body":"ChEKCQjX4J6tBhDfCBICGAIgATpNCgMY5wIQASJEPE3TLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=","b64Record":"CgIILBIwmNzr1+AONPIp6PWeOhhofUvHxQQvvLb2e4j55Oug3pMP+ZBfkcHt3lfbZtrGnYkgGgsIk+GerQYQxIvaOyIRCgkI1+CerQYQ3wgSAhgCIAE6QAoDGOcCEiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALBoQS0VZX05PVF9QUk9WSURFRChkagMY+wdSAHoLCJPhnq0GEMOL2js="}]}}}
\ No newline at end of file
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/TokenUpdatePrecompileSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/TokenUpdatePrecompileSuite.java
index cd1a7a3c51b9..1aaa08b393a7 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/TokenUpdatePrecompileSuite.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/TokenUpdatePrecompileSuite.java
@@ -40,6 +40,10 @@
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.newKeyNamed;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.sourcing;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext;
+import static com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode.HIGHLY_NON_DETERMINISTIC_FEES;
+import static com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode.NONDETERMINISTIC_CONTRACT_CALL_RESULTS;
+import static com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode.NONDETERMINISTIC_FUNCTION_PARAMETERS;
+import static com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode.NONDETERMINISTIC_TRANSACTION_FEES;
import static com.hedera.services.bdd.suites.contract.Utils.asAddress;
import static com.hedera.services.bdd.suites.contract.Utils.asToken;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED;
@@ -64,7 +68,7 @@
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Tag;
-@HapiTestSuite
+@HapiTestSuite(fuzzyMatch = true)
@Tag(SMART_CONTRACT)
public class TokenUpdatePrecompileSuite extends HapiSuite {
@@ -123,7 +127,10 @@ List negativeCases() {
@HapiTest
final HapiSpec updateTokenWithInvalidKeyValues() {
final AtomicReference vanillaTokenID = new AtomicReference<>();
- return defaultHapiSpec("updateTokenWithInvalidKeyValues")
+ return defaultHapiSpec(
+ "updateTokenWithInvalidKeyValues",
+ NONDETERMINISTIC_TRANSACTION_FEES,
+ NONDETERMINISTIC_FUNCTION_PARAMETERS)
.given(
newKeyNamed(ED25519KEY).shape(ED25519),
newKeyNamed(ECDSA_KEY).shape(SECP256K1),
@@ -171,7 +178,11 @@ final HapiSpec updateTokenWithInvalidKeyValues() {
@HapiTest
public HapiSpec updateNftTokenKeysWithWrongTokenIdAndMissingAdminKey() {
final AtomicReference nftToken = new AtomicReference<>();
- return defaultHapiSpec("updateNftTokenKeysWithWrongTokenIdAndMissingAdminKey")
+ return defaultHapiSpec(
+ "updateNftTokenKeysWithWrongTokenIdAndMissingAdminKey",
+ HIGHLY_NON_DETERMINISTIC_FEES,
+ NONDETERMINISTIC_FUNCTION_PARAMETERS,
+ NONDETERMINISTIC_CONTRACT_CALL_RESULTS)
.given(
cryptoCreate(TOKEN_TREASURY),
newKeyNamed(ED25519KEY).shape(ED25519),
@@ -250,7 +261,11 @@ public HapiSpec updateNftTokenKeysWithWrongTokenIdAndMissingAdminKey() {
@HapiTest
public HapiSpec getTokenKeyForNonFungibleNegative() {
final AtomicReference nftToken = new AtomicReference<>();
- return defaultHapiSpec("getTokenKeyForNonFungibleNegative")
+ return defaultHapiSpec(
+ "getTokenKeyForNonFungibleNegative",
+ HIGHLY_NON_DETERMINISTIC_FEES,
+ NONDETERMINISTIC_FUNCTION_PARAMETERS,
+ NONDETERMINISTIC_CONTRACT_CALL_RESULTS)
.given(
cryptoCreate(TOKEN_TREASURY),
newKeyNamed(MULTI_KEY).shape(ED25519_ON),
From 00e33b1a50a42440f6e3184f5298ad9fbb98c15f Mon Sep 17 00:00:00 2001
From: Petar Tonev
Date: Tue, 6 Feb 2024 17:45:03 +0200
Subject: [PATCH 11/16] fix: token associations modular dumper (#11242)
---
.../com/hedera/node/app/bbm/StateDumper.java | 12 ++
.../bbm/associations/TokenAssociation.java | 90 +++++++++++
.../bbm/associations/TokenAssociationId.java | 52 ++++++
.../TokenAssociationsDumpUtils.java | 148 ++++++++++++++++++
4 files changed, 302 insertions(+)
create mode 100644 hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociation.java
create mode 100644 hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociationId.java
create mode 100644 hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociationsDumpUtils.java
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/StateDumper.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/StateDumper.java
index 60327fff241d..b6a06ef05681 100644
--- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/StateDumper.java
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/StateDumper.java
@@ -16,17 +16,23 @@
package com.hedera.node.app.bbm;
+import static com.hedera.node.app.bbm.associations.TokenAssociationsDumpUtils.dumpModTokenRelations;
+import static com.hedera.node.app.bbm.associations.TokenAssociationsDumpUtils.dumpMonoTokenRelations;
import static com.hedera.node.app.bbm.nfts.UniqueTokenDumpUtils.dumpModUniqueTokens;
import static com.hedera.node.app.bbm.nfts.UniqueTokenDumpUtils.dumpMonoUniqueTokens;
import static com.hedera.node.app.records.BlockRecordService.BLOCK_INFO_STATE_KEY;
import static com.hedera.node.app.service.mono.state.migration.StateChildIndices.NETWORK_CTX;
+import static com.hedera.node.app.service.mono.state.migration.StateChildIndices.TOKEN_ASSOCIATIONS;
import static com.hedera.node.app.service.mono.state.migration.StateChildIndices.UNIQUE_TOKENS;
import static com.hedera.node.app.service.token.impl.TokenServiceImpl.NFTS_KEY;
+import static com.hedera.node.app.service.token.impl.TokenServiceImpl.TOKEN_RELS_KEY;
import static java.util.Objects.requireNonNull;
import com.hedera.hapi.node.base.NftID;
+import com.hedera.hapi.node.base.TokenAssociation;
import com.hedera.hapi.node.state.blockrecords.BlockInfo;
import com.hedera.hapi.node.state.token.Nft;
+import com.hedera.hapi.node.state.token.TokenRelation;
import com.hedera.node.app.records.BlockRecordService;
import com.hedera.node.app.service.mono.state.merkle.MerkleNetworkContext;
import com.hedera.node.app.service.token.TokenService;
@@ -47,12 +53,15 @@
*/
public class StateDumper {
private static final String SEMANTIC_UNIQUE_TOKENS = "uniqueTokens.txt";
+ private static final String SEMANTIC_TOKEN_RELATIONS = "tokenRelations.txt";
public static void dumpMonoChildrenFrom(
@NonNull final MerkleHederaState state, @NonNull final DumpCheckpoint checkpoint) {
final MerkleNetworkContext networkContext = state.getChild(NETWORK_CTX);
final var dumpLoc = getExtantDumpLoc("mono", networkContext.consensusTimeOfLastHandledTxn());
dumpMonoUniqueTokens(Paths.get(dumpLoc, SEMANTIC_UNIQUE_TOKENS), state.getChild(UNIQUE_TOKENS), checkpoint);
+ dumpMonoTokenRelations(
+ Paths.get(dumpLoc, SEMANTIC_TOKEN_RELATIONS), state.getChild(TOKEN_ASSOCIATIONS), checkpoint);
}
public static void dumpModChildrenFrom(
@@ -68,6 +77,9 @@ public static void dumpModChildrenFrom(
final VirtualMap, OnDiskValue> uniqueTokens =
requireNonNull(state.getChild(state.findNodeIndex(TokenService.NAME, NFTS_KEY)));
dumpModUniqueTokens(Paths.get(dumpLoc, SEMANTIC_UNIQUE_TOKENS), uniqueTokens, checkpoint);
+ final VirtualMap, OnDiskValue> tokenRelations =
+ requireNonNull(state.getChild(state.findNodeIndex(TokenService.NAME, TOKEN_RELS_KEY)));
+ dumpModTokenRelations(Paths.get(dumpLoc, SEMANTIC_TOKEN_RELATIONS), tokenRelations, checkpoint);
}
private static String getExtantDumpLoc(
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociation.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociation.java
new file mode 100644
index 000000000000..ffbab0e0bbf4
--- /dev/null
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociation.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.hedera.node.app.bbm.associations;
+
+import com.hedera.hapi.node.state.token.TokenRelation;
+import com.hedera.node.app.service.mono.state.submerkle.EntityId;
+import com.hedera.node.app.service.mono.state.virtual.entities.OnDiskTokenRel;
+import com.hedera.node.app.service.mono.utils.EntityNumPair;
+import com.hedera.node.app.state.merkle.disk.OnDiskValue;
+import com.hederahashgraph.api.proto.java.AccountID;
+import com.hederahashgraph.api.proto.java.TokenID;
+import com.swirlds.base.utility.Pair;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
+record TokenAssociation(
+ EntityId accountId,
+ EntityId tokenId,
+ long balance,
+ boolean isFrozen,
+ boolean isKycGranted,
+ boolean isAutomaticAssociation,
+ EntityId prev,
+ EntityId next) {
+
+ @NonNull
+ static TokenAssociation fromMono(@NonNull final OnDiskTokenRel tokenRel) {
+ final var at = toLongsPair(toPair(tokenRel.getKey()));
+
+ return new TokenAssociation(
+ entityIdFrom(at.left()),
+ entityIdFrom(at.right()),
+ tokenRel.getBalance(),
+ tokenRel.isFrozen(),
+ tokenRel.isKycGranted(),
+ tokenRel.isAutomaticAssociation(),
+ entityIdFrom(tokenRel.getPrev()),
+ entityIdFrom(tokenRel.getNext()));
+ }
+
+ static TokenAssociation fromMod(@NonNull final OnDiskValue wrapper) {
+ final var value = wrapper.getValue();
+ return new TokenAssociation(
+ accountIdFromMod(value.accountId()),
+ tokenIdFromMod(value.tokenId()),
+ value.balance(),
+ value.frozen(),
+ value.kycGranted(),
+ value.automaticAssociation(),
+ tokenIdFromMod(value.previousToken()),
+ tokenIdFromMod(value.nextToken()));
+ }
+
+ @NonNull
+ static Pair toPair(@NonNull final EntityNumPair enp) {
+ final var at = enp.asAccountTokenRel();
+ return Pair.of(at.getLeft(), at.getRight());
+ }
+
+ @NonNull
+ static Pair toLongsPair(@NonNull final Pair pat) {
+ return Pair.of(pat.left().getAccountNum(), pat.right().getTokenNum());
+ }
+
+ static EntityId accountIdFromMod(@Nullable final com.hedera.hapi.node.base.AccountID accountId) {
+ return null == accountId ? EntityId.MISSING_ENTITY_ID : new EntityId(0L, 0L, accountId.accountNumOrThrow());
+ }
+
+ static EntityId tokenIdFromMod(@Nullable final com.hedera.hapi.node.base.TokenID tokenId) {
+ return null == tokenId ? EntityId.MISSING_ENTITY_ID : new EntityId(0L, 0L, tokenId.tokenNum());
+ }
+
+ static EntityId entityIdFrom(long num) {
+ return new EntityId(0L, 0L, num);
+ }
+}
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociationId.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociationId.java
new file mode 100644
index 000000000000..cf1d05af3341
--- /dev/null
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/bbm/associations/TokenAssociationId.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.hedera.node.app.bbm.associations;
+
+import static com.hedera.node.app.bbm.associations.TokenAssociation.toLongsPair;
+import static com.hedera.node.app.bbm.associations.TokenAssociation.toPair;
+
+import com.google.common.collect.ComparisonChain;
+import com.hedera.node.app.bbm.utils.Writer;
+import com.hedera.node.app.service.mono.state.virtual.entities.OnDiskTokenRel;
+import com.hedera.node.app.state.merkle.disk.OnDiskKey;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+record TokenAssociationId(long accountId, long tokenId) implements Comparable