From 8a145ec7ff1f075b5ff39c6a2c72c2ff1df82ee7 Mon Sep 17 00:00:00 2001 From: Kammerlo Date: Thu, 30 May 2024 11:22:42 +0200 Subject: [PATCH 1/2] feat: Implemented txSize calculation to BlockSerializer based on raw block bytes --- .../cardano/yaci/core/config/YaciConfig.java | 18 ++ .../cardano/yaci/core/model/Block.java | 2 + .../model/serializers/BlockSerializer.java | 7 + .../util/TransactionSizeExtractor.java | 161 ++++++++++++++++++ gradle.properties | 2 +- .../cardano/yaci/helper/BlockFetcherIT.java | 9 +- .../BlockFetchAgentListenerAdapter.java | 2 + .../yaci/helper/model/Transaction.java | 2 + 8 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java index 46ee2bb0..7abbb981 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java @@ -8,10 +8,12 @@ public enum YaciConfig { private boolean returnBlockCbor; private boolean returnTxBodyCbor; + private boolean returnTxSize; YaciConfig() { returnBlockCbor = false; returnTxBodyCbor = false; + returnTxSize = false; } /** @@ -45,4 +47,20 @@ public boolean isReturnTxBodyCbor() { public void setReturnTxBodyCbor(boolean returnTxBodyCbor) { this.returnTxBodyCbor = returnTxBodyCbor; } + + /** + * Returns true if the transaction size and scriptSize is returned + * @return + */ + public boolean isReturnTxSize() { + return returnTxSize; + } + + /** + * + * @param returnTxSize + */ + public void setReturnTxSize(boolean returnTxSize) { + this.returnTxSize = returnTxSize; + } } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/Block.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/Block.java index 8c1a34b1..d90c6a7b 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/Block.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/Block.java @@ -20,6 +20,8 @@ public class Block { private List transactionWitness = new ArrayList<>(); private Map auxiliaryDataMap = new LinkedHashMap(); private List invalidTransactions = new ArrayList<>(); + private List txSizes = new ArrayList<>(); + private List txScriptSizes = new ArrayList<>(); private String cbor; } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java index b3bf4daf..52b61b03 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java @@ -5,6 +5,7 @@ import com.bloxbean.cardano.yaci.core.config.YaciConfig; import com.bloxbean.cardano.yaci.core.model.*; import com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionBodyExtractor; +import com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionSizeExtractor; import com.bloxbean.cardano.yaci.core.model.serializers.util.WitnessUtil; import com.bloxbean.cardano.yaci.core.protocol.Serializer; import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil; @@ -115,6 +116,12 @@ private Block deserializeBlock(DataItem di, byte[] blockBody) { blockBuilder.cbor(HexUtil.encodeHexString(blockBody)); } + if(YaciConfig.INSTANCE.isReturnTxSize()) { + Tuple, List> txSizes = TransactionSizeExtractor.getSizesForTransactions(blockBody); + blockBuilder.txSizes(txSizes._1); + blockBuilder.txScriptSizes(txSizes._2); + } + return blockBuilder.build(); } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java new file mode 100644 index 00000000..9e6e1e9a --- /dev/null +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java @@ -0,0 +1,161 @@ +package com.bloxbean.cardano.yaci.core.model.serializers.util; + +import co.nstant.in.cbor.CborDecoder; +import co.nstant.in.cbor.CborException; +import co.nstant.in.cbor.model.DataItem; +import co.nstant.in.cbor.model.Map; +import co.nstant.in.cbor.model.Special; +import co.nstant.in.cbor.model.UnsignedInteger; +import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil; +import com.bloxbean.cardano.yaci.core.util.Tuple; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionBodyExtractor.getLength; +import static com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionBodyExtractor.getSymbolBytes; + +@Slf4j +public class TransactionSizeExtractor { + + /** + * Extracts TransactionSizes and ScriptSizes out of the byte array blockbody. + * @param blockBody raw blockbody bytes + * @return Tuple containing lists for transactionSizes and scriptSizes + */ + @SneakyThrows + public static Tuple, List> getSizesForTransactions(byte[] blockBody) { + ByteArrayInputStream bais = new ByteArrayInputStream(blockBody); + CborDecoder decoder = new CborDecoder(bais); + + //era and body not needed + bais.read(); + decoder.decodeNext(); + //body not needed + bais.read(); + decoder.decodeNext();// skip header + + // txbody + int transactionBodyArraySize = getArraySize(bais, blockBody); + List txByteSizes; + Tuple, List> witnessSetByteSizesAndScriptSize; + + if(transactionBodyArraySize >= 0) { + txByteSizes = getTxSizesFromDataItem(transactionBodyArraySize, bais, decoder); + bais.read(); // reading witnessSetArraySize. Not needed since txSize and witnessSize are equal length + witnessSetByteSizesAndScriptSize = getTxSizesAndScriptSizes(transactionBodyArraySize, bais, decoder); + } else { + txByteSizes = new ArrayList<>(); + int start = bais.available(); + for(;;) { + + var dataItem = decoder.decodeNext(); + if(dataItem == null) { + throw new CborException("Unexpected end of stream"); + } + if(Special.BREAK.equals(dataItem)) { + break; + } + + int end = bais.available(); + txByteSizes.add(start - end); + start = end; + } + + List witnessSizeList = new ArrayList<>(); + List scriptSizeList = new ArrayList<>(); + bais.read(); // reading witnessSetArraySize. Not needed since txSize and witnessSize are equal length + start = bais.available(); + for(;;) { + var dataItem = decoder.decodeNext(); + if(dataItem == null) { + throw new CborException("Unexpected end of stream"); + } + if(Special.BREAK.equals(dataItem)) { + break; + } + + Map witnessMap = (Map) dataItem; + int scriptSize = getScriptSize(witnessMap, 3); + scriptSize += getScriptSize(witnessMap, 6); + scriptSize += getScriptSize(witnessMap, 7); + + int end = bais.available(); + witnessSizeList.add(start - end); + scriptSizeList.add(scriptSize); + start = end; + } + witnessSetByteSizesAndScriptSize = new Tuple<>(witnessSizeList, scriptSizeList); + } + // Processing Auxiliary Data and adding transaction size to respective transactions + transactionBodyArraySize = getArraySize(bais, blockBody); + for(int i = 0; i < transactionBodyArraySize; i++) { + int key = ((UnsignedInteger) decoder.decodeNext()).getValue().intValue(); + txByteSizes.set(key, txByteSizes.get(key) + getByteSizeOfNextDataItem(bais, decoder)); + } + // summing up byteSizes and WitnessByteSizes elementwise + for(int i = 0; i < txByteSizes.size(); i++) { + txByteSizes.set(i, txByteSizes.get(i) + witnessSetByteSizesAndScriptSize._1.get(i)); + } + + return new Tuple<>(txByteSizes, witnessSetByteSizesAndScriptSize._2); + } + + private static Tuple, List> getTxSizesAndScriptSizes(int length, ByteArrayInputStream bais, CborDecoder decoder) { + List txSizes = new ArrayList<>(); + List scriptSizes = new ArrayList<>(); + for(int i = 0; i < length;i++) { + Tuple txSizeAndScriptSize = getByteSizeAndScriptSizeOfNextDataItem(bais, decoder); + txSizes.add(txSizeAndScriptSize._1); + scriptSizes.add(txSizeAndScriptSize._2); + } + return new Tuple<>(txSizes,scriptSizes); + } + + private static List getTxSizesFromDataItem(int length, ByteArrayInputStream bais, CborDecoder decoder) { + List txSizes = new ArrayList<>(); + for(int i = 0; i < length; i++) { + txSizes.add(getByteSizeOfNextDataItem(bais, decoder)); + } + return txSizes; + } + + @SneakyThrows + private static int getByteSizeOfNextDataItem(ByteArrayInputStream bais, CborDecoder decoder) { + int previous = bais.available(); + decoder.decodeNext(); + return previous - bais.available(); + } + + @SneakyThrows + private static Tuple getByteSizeAndScriptSizeOfNextDataItem(ByteArrayInputStream bais, CborDecoder decoder) { + int previous = bais.available(); + Map witnessMap = (Map) decoder.decodeNext(); + int scriptSize = getScriptSize(witnessMap, 3); + scriptSize += getScriptSize(witnessMap, 6); + scriptSize += getScriptSize(witnessMap, 7); + return new Tuple<>(previous - bais.available(), scriptSize); + } + + private static int getScriptSize(Map witnessMap, int index) { + Collection keys = witnessMap.getKeys(); + UnsignedInteger unsignedInteger = new UnsignedInteger(BigInteger.valueOf(index)); + if(keys.contains(unsignedInteger)) { + return CborSerializationUtil.serialize(witnessMap.get(unsignedInteger)).length; + } else { + return 0; + } + } + + @SneakyThrows + private static int getArraySize(ByteArrayInputStream bais, byte[] blockBody) { + int arrTxBodySize = bais.read(); + // tx bodies + return (int) getLength(arrTxBodySize,getSymbolBytes(blockBody.length - bais.available(),blockBody)); + } +} diff --git a/gradle.properties b/gradle.properties index b56d19af..b34d24cb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = com.bloxbean.cardano artifactId = yaci -version = 0.3.0-beta15-SNAPSHOT +version = 0.3.0-beta16-SNAPSHOT diff --git a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java index 73862332..89e3882a 100644 --- a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java +++ b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java @@ -2,6 +2,7 @@ import com.bloxbean.cardano.client.util.JsonUtil; import com.bloxbean.cardano.yaci.core.common.Constants; +import com.bloxbean.cardano.yaci.core.config.YaciConfig; import com.bloxbean.cardano.yaci.core.model.Amount; import com.bloxbean.cardano.yaci.core.model.Block; import com.bloxbean.cardano.yaci.core.model.PlutusScript; @@ -30,6 +31,8 @@ class BlockFetcherIT extends BaseTest { @Test public void fetchBlock() throws InterruptedException { + // will be removed before merge + YaciConfig.INSTANCE.setReturnTxSize(true); VersionTable versionTable = N2NVersionTableConstant.v4AndAbove(protocolMagic); BlockFetcher blockFetcher = new BlockFetcher(node, nodePort, versionTable); @@ -46,14 +49,14 @@ public void fetchBlock() throws InterruptedException { // Point from = new Point(0, "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f"); // Point to = new Point(5, "365201e928da50760fce4bdad09a7338ba43a43aff1c0e8d3ec458388c932ec8"); - Point from = new Point(13006114, "86dabb90d316b104af0bb926a999fecd17c59be3fa377302790ad70495c4b509"); - Point to = new Point(13006114, "86dabb90d316b104af0bb926a999fecd17c59be3fa377302790ad70495c4b509"); + Point from = new Point(86760, "f9fea05ea91d5b413fd138ddc68cc5586f23dcae6019ea7aadc60889000fd240"); + Point to = new Point(2088544, "272193884b33dac2642c0f9d8a37303989a68f868d11227d1204380f14e264a2"); blockFetcher.fetch(from, to); countDownLatch.await(10, TimeUnit.SECONDS); blockFetcher.shutdown(); - assertThat(blocks.get(0).getHeader().getHeaderBody().getBlockNumber()).isEqualTo(287622); +// assertThat(blocks.get(0).getHeader().getHeaderBody().getBlockNumber()).isEqualTo(287622); } diff --git a/helper/src/main/java/com/bloxbean/cardano/yaci/helper/listener/BlockFetchAgentListenerAdapter.java b/helper/src/main/java/com/bloxbean/cardano/yaci/helper/listener/BlockFetchAgentListenerAdapter.java index 2e2cf911..98b4081a 100644 --- a/helper/src/main/java/com/bloxbean/cardano/yaci/helper/listener/BlockFetchAgentListenerAdapter.java +++ b/helper/src/main/java/com/bloxbean/cardano/yaci/helper/listener/BlockFetchAgentListenerAdapter.java @@ -52,6 +52,8 @@ public void blockFound(Block block) { .witnesses(witnesses) .auxData(auxData) .invalid(invalidTxn) + .txSize(block.getTxSizes() != null ? block.getTxSizes().get(i) : 0) + .txScriptSize(block.getTxScriptSizes() != null ? block.getTxScriptSizes().get(i) : 0) .build(); transactionEvents.add(transactionEvent); diff --git a/helper/src/main/java/com/bloxbean/cardano/yaci/helper/model/Transaction.java b/helper/src/main/java/com/bloxbean/cardano/yaci/helper/model/Transaction.java index aba512fa..0df22ee1 100644 --- a/helper/src/main/java/com/bloxbean/cardano/yaci/helper/model/Transaction.java +++ b/helper/src/main/java/com/bloxbean/cardano/yaci/helper/model/Transaction.java @@ -24,4 +24,6 @@ public class Transaction { private Witnesses witnesses; private AuxData auxData; private boolean invalid; + private int txSize; + private int txScriptSize; } From 68f19ab0f7af35eb11ceb5bc08415c366fbb878b Mon Sep 17 00:00:00 2001 From: Kammerlo Date: Thu, 30 May 2024 12:36:04 +0200 Subject: [PATCH 2/2] feat: made txSize calculation standard and integrated into the current deserializing process --- .../cardano/yaci/core/config/YaciConfig.java | 18 -- .../model/serializers/BlockSerializer.java | 35 ++-- .../util/TransactionBodyExtractor.java | 32 ++-- .../util/TransactionSizeExtractor.java | 161 ------------------ .../model/serializers/util/WitnessUtil.java | 22 +++ .../TransactionBodyExtractorTest.java | 4 +- .../cardano/yaci/helper/BlockFetcherIT.java | 9 +- 7 files changed, 72 insertions(+), 209 deletions(-) delete mode 100644 core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java index 7abbb981..46ee2bb0 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/config/YaciConfig.java @@ -8,12 +8,10 @@ public enum YaciConfig { private boolean returnBlockCbor; private boolean returnTxBodyCbor; - private boolean returnTxSize; YaciConfig() { returnBlockCbor = false; returnTxBodyCbor = false; - returnTxSize = false; } /** @@ -47,20 +45,4 @@ public boolean isReturnTxBodyCbor() { public void setReturnTxBodyCbor(boolean returnTxBodyCbor) { this.returnTxBodyCbor = returnTxBodyCbor; } - - /** - * Returns true if the transaction size and scriptSize is returned - * @return - */ - public boolean isReturnTxSize() { - return returnTxSize; - } - - /** - * - * @param returnTxSize - */ - public void setReturnTxSize(boolean returnTxSize) { - this.returnTxSize = returnTxSize; - } } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java index 52b61b03..d45f1b66 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java @@ -1,11 +1,11 @@ package com.bloxbean.cardano.yaci.core.model.serializers; import co.nstant.in.cbor.model.*; +import com.bloxbean.cardano.client.util.Triple; import com.bloxbean.cardano.yaci.core.common.EraUtil; import com.bloxbean.cardano.yaci.core.config.YaciConfig; import com.bloxbean.cardano.yaci.core.model.*; import com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionBodyExtractor; -import com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionSizeExtractor; import com.bloxbean.cardano.yaci.core.model.serializers.util.WitnessUtil; import com.bloxbean.cardano.yaci.core.protocol.Serializer; import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil; @@ -61,10 +61,14 @@ private Block deserializeBlock(DataItem di, byte[] blockBody) { **/ //Extract transaction bodies from block bytes directly to keep the tx hash same - List> txBodyTuples = TransactionBodyExtractor.getTxBodiesFromBlock(blockBody); + List> txBodyTriples = TransactionBodyExtractor.getTxBodiesFromBlock(blockBody); List txnBodies = new ArrayList<>(); - for (var tuple: txBodyTuples) { - TransactionBody txBody = TransactionBodySerializer.INSTANCE.deserializeDI(tuple._1, tuple._2); + List transactionSizes = new ArrayList<>(); + List scriptSizes = new ArrayList<>(); + + for (var triple: txBodyTriples) { + TransactionBody txBody = TransactionBodySerializer.INSTANCE.deserializeDI(triple._1, triple._2); + transactionSizes.add(triple._3); txnBodies.add(txBody); } blockBuilder.transactionBodies(txnBodies); @@ -72,10 +76,17 @@ private Block deserializeBlock(DataItem di, byte[] blockBody) { //witnesses List witnessesSet = new ArrayList<>(); Array witnessesListArr = (Array) blockArray.getDataItems().get(2); - for (DataItem witnessesDI: witnessesListArr.getDataItems()) { + for (int i = 0; i < witnessesListArr.getDataItems().size(); i++) { + DataItem witnessesDI = witnessesListArr.getDataItems().get(i); if (witnessesDI == SimpleValue.BREAK) continue; Witnesses witnesses = WitnessesSerializer.INSTANCE.deserializeDI(witnessesDI); + + // serializing only the witness we need to add it to transaction size + int witnessByteSize = CborSerializationUtil.serialize(witnessesDI).length; + transactionSizes.add(i, transactionSizes.get(i) + witnessByteSize); + scriptSizes.add(WitnessUtil.getScriptSizes(witnessesDI)); + witnessesSet.add(witnesses); } @@ -91,7 +102,12 @@ private Block deserializeBlock(DataItem di, byte[] blockBody) { if (txIdDI == SimpleValue.BREAK) continue; AuxData auxData = AuxDataSerializer.INSTANCE.deserializeDI(auxDataMapDI.get(txIdDI)); - auxDataMap.put(toInt(txIdDI), auxData); + int auxDataIndex = toInt(txIdDI); + auxDataMap.put(auxDataIndex, auxData); + + // Getting the Size and adding it to the respective transaction size + int auxDataSize = CborSerializationUtil.serialize(auxDataMapDI.get(txIdDI)).length; + transactionSizes.add(auxDataIndex, transactionSizes.get(auxDataIndex) + auxDataSize); } blockBuilder.auxiliaryDataMap(auxDataMap); @@ -116,11 +132,8 @@ private Block deserializeBlock(DataItem di, byte[] blockBody) { blockBuilder.cbor(HexUtil.encodeHexString(blockBody)); } - if(YaciConfig.INSTANCE.isReturnTxSize()) { - Tuple, List> txSizes = TransactionSizeExtractor.getSizesForTransactions(blockBody); - blockBuilder.txSizes(txSizes._1); - blockBuilder.txScriptSizes(txSizes._2); - } + blockBuilder.txSizes(transactionSizes); + blockBuilder.txScriptSizes(scriptSizes); return blockBuilder.build(); } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionBodyExtractor.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionBodyExtractor.java index 3db98067..bbf51957 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionBodyExtractor.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionBodyExtractor.java @@ -5,6 +5,7 @@ import co.nstant.in.cbor.model.AdditionalInformation; import co.nstant.in.cbor.model.DataItem; import co.nstant.in.cbor.model.Special; +import com.bloxbean.cardano.client.util.Triple; import com.bloxbean.cardano.yaci.core.util.Tuple; import lombok.SneakyThrows; @@ -17,9 +18,14 @@ public class TransactionBodyExtractor { public static final int INFINITY = -1; + /** + * Extracting TxBodies from raw block body. + * @param blockBody raw block body + * @return Triple: Deserialized Dataitem, Raw Bytes and Size + */ @SneakyThrows - public static List> getTxBodiesFromBlock(byte[] blockBody) { - List> txBodyTuples = new ArrayList<>(); + public static List> getTxBodiesFromBlock(byte[] blockBody) { + List> txBodyTuples = new ArrayList<>(); ByteArrayInputStream bais = new ByteArrayInputStream(blockBody); CborDecoder decoder = new CborDecoder(bais); @@ -35,14 +41,6 @@ public static List> getTxBodiesFromBlock(byte[] blockBod long length = getLength(arrTxBodySize,getSymbolBytes(blockBody.length - bais.available(),blockBody)); int start = blockBody.length - bais.available(); - for(int i = 0 ; i < length; i++){ - int previous = bais.available(); - DataItem dataItem = decoder.decodeNext(); - byte[] txBodyRaw = new byte[previous - bais.available()]; - System.arraycopy(blockBody,start,txBodyRaw,0,txBodyRaw.length); - txBodyTuples.add(new Tuple<>(dataItem, txBodyRaw)); - start = blockBody.length - bais.available(); - } if(AdditionalInformation.INDEFINITE.equals(AdditionalInformation.ofByte(arrTxBodySize))) { for (;;) { int previous = bais.available(); @@ -53,12 +51,24 @@ public static List> getTxBodiesFromBlock(byte[] blockBod if (Special.BREAK.equals(dataItem)) { break; } + int txSize = previous - bais.available(); byte[] txBodyRaw = new byte[previous - bais.available()]; System.arraycopy(blockBody, start, txBodyRaw, 0, txBodyRaw.length); - txBodyTuples.add(new Tuple<>(dataItem, txBodyRaw)); + txBodyTuples.add(new Triple<>(dataItem, txBodyRaw, txSize)); + start = blockBody.length - bais.available(); + } + } else { + for (int i = 0; i < length; i++) { + int previous = bais.available(); + DataItem dataItem = decoder.decodeNext(); + int txSize = previous - bais.available(); + byte[] txBodyRaw = new byte[txSize]; + System.arraycopy(blockBody, start, txBodyRaw, 0, txBodyRaw.length); + txBodyTuples.add(new Triple<>(dataItem, txBodyRaw, txSize)); start = blockBody.length - bais.available(); } } + return txBodyTuples; } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java deleted file mode 100644 index 9e6e1e9a..00000000 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/TransactionSizeExtractor.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.bloxbean.cardano.yaci.core.model.serializers.util; - -import co.nstant.in.cbor.CborDecoder; -import co.nstant.in.cbor.CborException; -import co.nstant.in.cbor.model.DataItem; -import co.nstant.in.cbor.model.Map; -import co.nstant.in.cbor.model.Special; -import co.nstant.in.cbor.model.UnsignedInteger; -import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil; -import com.bloxbean.cardano.yaci.core.util.Tuple; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -import java.io.ByteArrayInputStream; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import static com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionBodyExtractor.getLength; -import static com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionBodyExtractor.getSymbolBytes; - -@Slf4j -public class TransactionSizeExtractor { - - /** - * Extracts TransactionSizes and ScriptSizes out of the byte array blockbody. - * @param blockBody raw blockbody bytes - * @return Tuple containing lists for transactionSizes and scriptSizes - */ - @SneakyThrows - public static Tuple, List> getSizesForTransactions(byte[] blockBody) { - ByteArrayInputStream bais = new ByteArrayInputStream(blockBody); - CborDecoder decoder = new CborDecoder(bais); - - //era and body not needed - bais.read(); - decoder.decodeNext(); - //body not needed - bais.read(); - decoder.decodeNext();// skip header - - // txbody - int transactionBodyArraySize = getArraySize(bais, blockBody); - List txByteSizes; - Tuple, List> witnessSetByteSizesAndScriptSize; - - if(transactionBodyArraySize >= 0) { - txByteSizes = getTxSizesFromDataItem(transactionBodyArraySize, bais, decoder); - bais.read(); // reading witnessSetArraySize. Not needed since txSize and witnessSize are equal length - witnessSetByteSizesAndScriptSize = getTxSizesAndScriptSizes(transactionBodyArraySize, bais, decoder); - } else { - txByteSizes = new ArrayList<>(); - int start = bais.available(); - for(;;) { - - var dataItem = decoder.decodeNext(); - if(dataItem == null) { - throw new CborException("Unexpected end of stream"); - } - if(Special.BREAK.equals(dataItem)) { - break; - } - - int end = bais.available(); - txByteSizes.add(start - end); - start = end; - } - - List witnessSizeList = new ArrayList<>(); - List scriptSizeList = new ArrayList<>(); - bais.read(); // reading witnessSetArraySize. Not needed since txSize and witnessSize are equal length - start = bais.available(); - for(;;) { - var dataItem = decoder.decodeNext(); - if(dataItem == null) { - throw new CborException("Unexpected end of stream"); - } - if(Special.BREAK.equals(dataItem)) { - break; - } - - Map witnessMap = (Map) dataItem; - int scriptSize = getScriptSize(witnessMap, 3); - scriptSize += getScriptSize(witnessMap, 6); - scriptSize += getScriptSize(witnessMap, 7); - - int end = bais.available(); - witnessSizeList.add(start - end); - scriptSizeList.add(scriptSize); - start = end; - } - witnessSetByteSizesAndScriptSize = new Tuple<>(witnessSizeList, scriptSizeList); - } - // Processing Auxiliary Data and adding transaction size to respective transactions - transactionBodyArraySize = getArraySize(bais, blockBody); - for(int i = 0; i < transactionBodyArraySize; i++) { - int key = ((UnsignedInteger) decoder.decodeNext()).getValue().intValue(); - txByteSizes.set(key, txByteSizes.get(key) + getByteSizeOfNextDataItem(bais, decoder)); - } - // summing up byteSizes and WitnessByteSizes elementwise - for(int i = 0; i < txByteSizes.size(); i++) { - txByteSizes.set(i, txByteSizes.get(i) + witnessSetByteSizesAndScriptSize._1.get(i)); - } - - return new Tuple<>(txByteSizes, witnessSetByteSizesAndScriptSize._2); - } - - private static Tuple, List> getTxSizesAndScriptSizes(int length, ByteArrayInputStream bais, CborDecoder decoder) { - List txSizes = new ArrayList<>(); - List scriptSizes = new ArrayList<>(); - for(int i = 0; i < length;i++) { - Tuple txSizeAndScriptSize = getByteSizeAndScriptSizeOfNextDataItem(bais, decoder); - txSizes.add(txSizeAndScriptSize._1); - scriptSizes.add(txSizeAndScriptSize._2); - } - return new Tuple<>(txSizes,scriptSizes); - } - - private static List getTxSizesFromDataItem(int length, ByteArrayInputStream bais, CborDecoder decoder) { - List txSizes = new ArrayList<>(); - for(int i = 0; i < length; i++) { - txSizes.add(getByteSizeOfNextDataItem(bais, decoder)); - } - return txSizes; - } - - @SneakyThrows - private static int getByteSizeOfNextDataItem(ByteArrayInputStream bais, CborDecoder decoder) { - int previous = bais.available(); - decoder.decodeNext(); - return previous - bais.available(); - } - - @SneakyThrows - private static Tuple getByteSizeAndScriptSizeOfNextDataItem(ByteArrayInputStream bais, CborDecoder decoder) { - int previous = bais.available(); - Map witnessMap = (Map) decoder.decodeNext(); - int scriptSize = getScriptSize(witnessMap, 3); - scriptSize += getScriptSize(witnessMap, 6); - scriptSize += getScriptSize(witnessMap, 7); - return new Tuple<>(previous - bais.available(), scriptSize); - } - - private static int getScriptSize(Map witnessMap, int index) { - Collection keys = witnessMap.getKeys(); - UnsignedInteger unsignedInteger = new UnsignedInteger(BigInteger.valueOf(index)); - if(keys.contains(unsignedInteger)) { - return CborSerializationUtil.serialize(witnessMap.get(unsignedInteger)).length; - } else { - return 0; - } - } - - @SneakyThrows - private static int getArraySize(ByteArrayInputStream bais, byte[] blockBody) { - int arrTxBodySize = bais.read(); - // tx bodies - return (int) getLength(arrTxBodySize,getSymbolBytes(blockBody.length - bais.available(),blockBody)); - } -} diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/WitnessUtil.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/WitnessUtil.java index f1107e36..b1d4eeb4 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/WitnessUtil.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/util/WitnessUtil.java @@ -3,13 +3,17 @@ import co.nstant.in.cbor.CborDecoder; import co.nstant.in.cbor.CborException; import co.nstant.in.cbor.model.AdditionalInformation; +import co.nstant.in.cbor.model.DataItem; +import co.nstant.in.cbor.model.Map; import co.nstant.in.cbor.model.Special; import co.nstant.in.cbor.model.UnsignedInteger; +import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil; import com.bloxbean.cardano.yaci.core.util.Tuple; import java.io.ByteArrayInputStream; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Objects; @@ -211,4 +215,22 @@ public static List getRedeemerFields(byte[] redeemer) throws CborExcepti return insideRedeemer; } + + public static int getScriptSize(Map witnessMap, int index) { + Collection keys = witnessMap.getKeys(); + UnsignedInteger unsignedInteger = new UnsignedInteger(BigInteger.valueOf(index)); + if(keys.contains(unsignedInteger)) { + return CborSerializationUtil.serialize(witnessMap.get(unsignedInteger)).length; + } else { + return 0; + } + } + + public static int getScriptSizes(DataItem witnessesDI) { + Map witnessMap = (Map) witnessesDI; + int scriptSize = getScriptSize(witnessMap, 3); + scriptSize += getScriptSize(witnessMap, 6); + scriptSize += getScriptSize(witnessMap, 7); + return scriptSize; + } } diff --git a/core/src/test/java/com/bloxbean/cardano/yaci/core/model/serializers/TransactionBodyExtractorTest.java b/core/src/test/java/com/bloxbean/cardano/yaci/core/model/serializers/TransactionBodyExtractorTest.java index 466e1efc..5e2bd809 100644 --- a/core/src/test/java/com/bloxbean/cardano/yaci/core/model/serializers/TransactionBodyExtractorTest.java +++ b/core/src/test/java/com/bloxbean/cardano/yaci/core/model/serializers/TransactionBodyExtractorTest.java @@ -1,9 +1,9 @@ package com.bloxbean.cardano.yaci.core.model.serializers; import co.nstant.in.cbor.model.DataItem; +import com.bloxbean.cardano.client.util.Triple; import com.bloxbean.cardano.yaci.core.model.serializers.util.TransactionBodyExtractor; import com.bloxbean.cardano.yaci.core.util.HexUtil; -import com.bloxbean.cardano.yaci.core.util.Tuple; import com.bloxbean.cardano.yaci.core.util.TxUtil; import org.junit.jupiter.api.Test; @@ -18,7 +18,7 @@ class TransactionBodyExtractorTest { void getTxBodiesFromBlock() { String blockCbor = "820585828f1a0063c65a1a02bfabe558201fb4ddcb8f1dfa76eab5e489b12a24355c5d41e7521696a7d89a13f3b2446ba45820512bd316b734324b56c53b467d2bbef5b6be183dd2ae1ccdd7a59d0a3f03e9ec5820b96b4b4a5c265e31a8cdbeb515f4d802686dd5d8d9c6053bdfc7d61a621ee770825840531de63f3b6d6a9acff74c03aa75f0cbd2bc47830b7e1b2e0924c4cd4bde3147fe7aa7e39e0e191b923be549ef8c96a9f8fc24018d5c6455934d05d2991b3fbc58501b40d62f9ef137a4adb1ab4216b78dbc535ea72ff57fc4bc6aefdf07ec807d1e8fb75a9dfb1d2f8501959b6d8218895570e1771b406322781fb13522026430f78aa7568f4d28bfc2b7ff4a4e56bba80882584000007d02f2e1b55910833dd901c349f77ffc64223b034427e4b54dae66984af2fb129447a848a932c2d516374fba7f15072f334e60977be8dc953982b2d82c125850a9e6521d6fe1c9427bb18de94eaa3d9ce8f345348b35c5c3c9fd34b98def5f18c77baa7dc78b05f84d6cd38a0624365f3259ba7fea9cc7c737cf31ae7c425c91f79f6fcddd57a5e6984315d3d541c10a196f295820301728fcfb8e4bd8080eb43149611ab0eff3cbdf90d3022f2ef942670612159f58209cfcb250559f10b9a5b5fec3a1e6409b09b762ef76b66b082a2808accd93c0fb031901285840d36c281e6c81b9892b65a1c5a46ef19a7dbb5fc9c8ea2d8049858ff5d1d01382c2e4a2e4c6ed6406add150e95fd0afb483c6e8499b1e5e87450bf79c62baa40006005901c096a4faaadde2671d97f5dc738ceff1bf442f4461b080d625025eb3ba5290428419978b9edd04bd0700642c782d594cb9e754dceed097adcea07fd546453af606031a7e2412c5775848bae1fac9329842891381f3a6ef96981c6a1c8a1943ab77127b4cdf72bbdd991ab58d658f4d279091400a6a1aa29242930bcd0fb49fa8fd5484f399ab4d61561416b860458465040f0d6e6caf8fea9232c723104febaa6d4fec7e661d6f16a39de7a19e2d26dc49ff0c1677de58783cb16a8c10ef3a48a75e2c02ce797b2eb584d00fce21b8494d1b09700e608e131b31892bf981cabe4edd21e84cf155b429141c5b17c419503a6ff0b99064b7b5eb6fbc1a543801cbf6a498f99123c05a269110ce16c674557b8faee1616a665a3704bb52b83009891f328e38ff52c36ca50d83bf708656e3a860a50aecc934b152a5f0123cd8c255bd63e78e0876eafab48dfe03e27f03d019f1d06a02a20eaa2bee011f9ad61845a68d36ffe3761d1aea8d72754421950255005f788521713bc568b4f6afd68f369f8d97c08aae6615db7e8d795a98c4a37cc47d5894f08dd98ac46a019bae6c0d086383682ab3c02cc537dba2285a599b702d21a6bd3511ed6a5adc385f8b9ea1dd9fa400818258200dacede06323234ad195041492edf0beef009582d149cc90bff717fcefe933ab00018182581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480021a0007a120031a02bfc7d0a4008182582008952824ec18b200db85559501863b9f5f0e4a8c62af0104bb0547f010d9d11c01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390167c533f4514b05348c37bef66234c8c43761c0e1aba763b5ffca6d0afdc0c9b2dfebb12a5fbe6e7567301e1dc2e452554c8d4c64c6852be9821a1b2055c6a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000759aa8274021a0002b06d031a02c1326ea4008182582069765f94176fc9e360b650e99c924017f807b69195b52ac55225927cf3868bfc01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901ad23ce00940626f72da54e853a27f4297b528d13b2f6a670ce38dfd4b14fd4e941537a4ab24afdc4ac6cdedb07186ca46cb562cf606b661e821a00296c63a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a63a73331021a0002943d031a02bfc7c9a400818258200dcbcee0d52333ec26803f8d6ae365dfce85f2e2332bca4798bfd10dce6458c901018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390142038f251f29f66fa3fa0364590d758960717c1179a686555d88c3c34fe36c0093b45928481fc808502f17e2a400535bf504aef5328deac2821a1ae2751aa1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000770cf202f021a0002b06d031a02c1326ea40083825820bc8907996cbce642306909803ea093a02d583a59aa352979b0570be1bcd59e4b184682582008d58caa6148df59ef67efc1da5caf94b168da31322e6279a697364d7a4fed580f8258206a3f4c1253021715a749e26d8738a0f7f1c87875974fb6cfa693d5853ddfb13e15018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390171cb680a306d4989c538c374f3174389847777a7b3db3af18686626ca16f400a9f1cb1b2cf36ef1f2ab7fdd9eb855a7bf0611cd35e2b3249821a0023624ba1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a121b9934021a0002c355031a02bfc7a9a40081825820ae11db4c428f503b68193a429dcfe21592c1175d75c9bd4509f250dbd3f8ce2100018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839014e7c08d34c80555e42e832e1e7c8d73e67c26e2ccbcf26c1ec07f78d5af4faeaca72b2b74e6241f392d3462ae41be8b5d482b3c49fdab4bb1a00778553021a00028cad031a02bfc7aca60081825820f9758cc9edb8f57081ea8c3229b3ccbc81d2e41b91a137cfe25de496a83b3e4f010d80018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390196633115395d742e46b55decc8e659a0a190c2244d47f317163bb7f245e63524f7b246ecf189c1e9417417070b6f61a2b6b023fc365234d71a18ecdbb6021a00028f6d031a02bfc7f80e80a40081825820cb147867dc23a98ca46de679863199bd64066debcce5f351621a4196494c44be01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901e26ea090b2043d8cd8502a0bf66584bc5baa1031c9bc11312d2ad9e66737a582d8b4ac8a8bda48ac4d14fd93f4a4df7443b6561fd2d2b1bb1a0225d0a3021a00028c81031a02bfc7eda4008282582000e62e6da4277527c225bbf02c78db6dc5978630952b6d5f8848f9473d4e2e5b01825820474bc2cfc669e924d8ea297bf9f2531b05e3925bf8f41c65d581245d71ece21a183c018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839017bd4505c48a21a991b0b0945f4bed2a016dbeffe042587c9cfd55c18c108a89fc2daefd649e5e642ee537d4b8ba5f5c3b7266e79ac4a99cb821a0017faa2a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a48833cf3021a0002abc9031a02bfc7dca40082825820f97a41915d79448b0d53cf1c319eb9d968dfdf53cfab1992f4d872599d468d1a0182582016c456225d750d18c670dfa6331825616320bfca36577f7af681d6c830d9294201018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839019710ab67e580b3ae79c595ea55843f9e76528c1ce6853709b4858445b570f6fe5046c9085e5bbd8699a94cd34f6246e8eb38753cdb859449821a0024efe9a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a2224e18f021a0002ab9d031a02bfc7b8a60081825820f65a050c66d048485eee678b66e0a8dddd6617f26c846db791afc250bb9e1338010d80018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839011b964c7a85846a19526503081d4cc81c3d3a50a2612a3fec390214b1bceb27f9a7d6b64f1c8ce6d99130422511e6c21e585ce38f10c900d61a04dd5f53021a00028f6d031a02bfc7f90e80a60081825820d2b58256cacec6888e855ef01eb16fc30a539c4c0c45c0295e4e6d27f70348e8010d80018282583901bd92f6544b413c7b00db37c4573997c5997cd2b8027495924b23de75bd92f6544b413c7b00db37c4573997c5997cd2b8027495924b23de751a00d406e882583901f81031a1b262c02765d14c4218f17127b1e7875ab3753da6eb12f1452d1653c688d7eb6eb296773a195fc4458b6a7d111ffa0dcba9a318c31b00000016f31ee786021a0002943d031a02bfc7f40e80a5008182582080aa254b951c57b9cc3cac7fd87da8021839ee47d120c0e7ae228cebe81c675400018182583901fffccbc83fa07f331208521b5044d776f4c9f3d5467bc4637420dbb5d12961711c56c030d9d559f0ca71fd41e576b2c2a01c24a11f8867441a00be9d97021a0002bdd5031a02bfaf5905a1581de1d12961711c56c030d9d559f0ca71fd41e576b2c2a01c24a11f8867441b0000000000000000a40081825820dfacb5758fb83f5b196deda9afa213b92f1774ae4316ab97c43e436f3eccef4901018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901f66cf28d3bd86b7dbc0ceb3b1a0d5626e67fc3a53c4d2ff46ea7e9825bcc8626fc3f258430927a98962708bddc06a1c0a7c307789c5b3030821a00857b94a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a331bc9ff021a00029411031a02bfc75ca40082825820449327940bdd4e797b5cc89050a43af7d44dbcee5cd2bed723936125c38ec54601825820e2dc69f0a12ff1eb66884cd971dbd22ccf0b92cc069c7ee758548fbba19b8b0601018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901fdd04f18b8f020035efc08d40b271fdd9e4182583f5838c981aec692f1b6e29e6c34374138e9293885d82b24fbf92bd52731cf379fae1921821a0020b0cea1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a5bb87d79021a0002ab9d031a02bfc7d1a400818258203c2aff021b23618e4f7cc2951305680f99dd3edeb51ad06ed3d4154756dd972101018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901745ff3bcb3f4e499f625e6b62f36f8a7bbe9a4dde412bf1ab109c74ac0a631fa7b2872fc39b848cef3a4c155e0946c7cb896fc5a5336b1e0821a1ab965a5a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b000000078a599e61021a0002b06d031a02c13275a40081825820ea4e1fed1591675b8d78f36f45fcdab926f6e0b7b026c5c2b9cd3c3bd67912c201018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390192c34900f4c438bc151011ba49388fed463f2a2b7b74a4310cc63466ae4d582a8d93c04369dbb6abf02941a9614394045286c116e1103149821a1b079f0da1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b00000007d9710fbd021a0002b06d031a02c13275a400818258203324959d0c47aaa8bdcb6d5bc6f70ab36e048845c136c5165e6dadd48c9b3d6401018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390146d3b58536dc7a87ff8d6e1b3346446ef3d972780926a3abf8fb72a5a2ee4fb60f1799c9d590687b949fe94f3298db12d7f69bb5142a6e681a11d11ed3021a00028cad031a02bfc7eea4008382582030da8b255d91034ebd811baa0e1173bb01bdd77f5d89f3e64893398a59582ffe0182582030da8b255d91034ebd811baa0e1173bb01bdd77f5d89f3e64893398a59582ffe02825820ae57bddbe94be648868b9527ce452541a85338c5407a135df6d7a72842e0af600c018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390133761577f020b74b1afa2f030a8eb210249a214e5a767fffb5ec7c41dfed1aa73e4299455dee0a49ac469645e4fdd74f6caed33bc18e08fe821a001ccceca1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a3fdb0d39021a0002d8d7031a02bfb9e5a4008282582056760adbb28a25829590c3ae43400484a0fc0e4f0ad435c14352aff1e85a311f01825820fd5c070a320969ece42b12ffe1082416a9b5cd02c9ecf55318c16785da489cca01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901bffb4452907a97e8aeb73c106d1b1d9a035cf6c709ad1432a7ddd4c69912599ab89dad40af37f80c95060e53c709e16e366640fcbab9837e821a0cd9a257a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000660c6cec3021a00029b1d031a02bfc7eea40081825820018d3c401e75354e0415dd51b5d66ec143a906226085268893cb19f26586c59301018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901498a4712d33c4440760094ea02c1848307a9824866a23daa0233e5ee25868d1d3c1ae292a944832a5b8de3019690064261bbf1245816399d821a19e65469a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000754e8fec2021a0002b06d031a02c13275a40082825820f509e3550248d2d8e75529782596d8089daa98bd416c0981b794bc158ceff031018258207e706bcbae1968c16dbe27e863ac4f03e7af84f63b782569ce10b05043e6f96c01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901db569db72fecc032eaa3c31323639e9600fd801780216692ec35c578657330346d5157b2770c4dbdcec24ab0af5dc97b5871bbd03b05ba26821a001cfdd0a2581c006d11a192d575164b4ce994f2ad4d5cb2a4b840cf9480186629e5b2a1476d6d633235363101581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591aa2aab5d7021a0002b27d031a02bfc6e8a40081825820ddb6461fad44c6812330b3799eef2efd0b8a47aff95cf594e00d9d4dd416d95a01018282583901dfe42dd4296be8cbce5115a140ed93ffa76fca25149e6ac45961b6bfdfe42dd4296be8cbce5115a140ed93ffa76fca25149e6ac45961b6bf1a41a91e0a82581d616904e8b2c26f3dda6c4a5db4b3ec9e31d581c9960977cfe9c6917a431a1eede6bd021a00029cd5031a02bfb9e5a4008182582069f3439a2a2593a2e52bbe987b96de3646244ac2dddd04613a9fc09bf34c3d1b0101828258390132db88995701a02979e2f2c277f6f3d4e01461114fbef95cec565669481614a23615f6bf6aaa9766957db4c74f44f4e5042db083a54e7c62821a00160a5ba1581cf10b16b705ef9baac04ca79c0da011633c8c20356b6a42262a1691daa14543484546461903e8825839015b34c56e48e708a9ecb8b737313daadbd6c1a47ed48bf155a36fc72b6f6ee12dcdc0e9dc1f229424a1bb740b5d74366926a6915253529215821a1095d0d1a1581cf10b16b705ef9baac04ca79c0da011633c8c20356b6a42262a1691daa14543484546461a000f3a70021a0002a019031a02bfc7eca400818258202ea08bba6c22860ef608dcf99eab7595120e915a15a3855f7b4684ce25dad36601018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390114fff339d9720a9d75ca935b161dd834555fd4b26ced681da044131ad0f5910cd833d293faf134485fc3dd492e43ff76974270a4d48106621a03d2f816021a00028c81031a02bfc7eda400818258208c70f71eeff659f0e389c59e34f5932811cee0429d8995697dbc69518f4d332d01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901e70a1dbe7ba3312f0187931416c7d165502b939dfe57a9fa3051492b0428ad76b810178a021192bcc948911594b32a4d6a80d5b11144c4a51a001e5399021a00028cad031a02bfc7c9a40082825820d40189e4bcd444f63d0286d75e025b87b6550e107bb7a1549cec3ceccad0cde412825820f66b800d631dadf844724a7cb913d8a6174b58b1de2511c9d8e5d8a8ea9563a601018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390167382253356a29df3a801f4209d0c8d472a97f769ed62b1bdbdec43b876069e255f25094e209cc1e1d0d934fad7e2671d122ee6200bf2887821a00194976a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a067027c6021a00029a41031a02bfc701a400818258208d25d2754bdacec7fd778a1a37684adbb84ed95b0c42b9afb8d5970c6be18f9400018282583901c4fb623e0a8a81f745f5f2a15e5d8b44ba0450f30d04973a05873f5783c8286542e9cb6f186d985c99fa724759bd4c795984d4117741f8da1a00554ca882581d616904e8b2c26f3dda6c4a5db4b3ec9e31d581c9960977cfe9c6917a431a18b0c783021a00029cd5031a02bfb9e5a400828258205a40500de68f4485548d2403634833242b83fbfe72a1f417947b7b9e5044d44a0282582022b055741470b9ef558765df1cc49cb2884e65c0e05e89e4b89a1d30c0406d9401018282583901a856bd4471069517b720c111e3380ac8f247ea3c5ee5f5bb00fb26015e21bea0efa38712ab040f55fc843bcef6e1deb61ba1ca827128875b821a00169b08a1581cd4cdb1ba9933ca9737afe8873bb6448f85aaeb8b3194b74bebd1fb8ba14a41646145676773343831018258390103cbff0a369a57f111d1323fb69f3ed2339848ceac9c866f3de62b855af9ab91e11684c021aac3609a54b7e1d668e2f0942972a617ba7f1a821a01effac7b81d581c0a85dd1543465407852c90e66c074a3b52ea2d7c77a2346ddc20550aa1534e4654553030353155676c7942726f7330343001581c0f1c8c9f8964fdf1375c6c00d6f66df5cb6245dea776db2672a8719fa14c4d6f6f6e4672696b7331333601581c0f411d2a85e84c8c44382c5c48f96a4b2c11f416cd13226db5b96e1aa14f7a416d62696562416279533131333601581c10bce6401940929888f0d514bc59fcecd5a4cf36d152f58b11365e5ba14f54687265616450756e6b733033363101581c15509d4cb60f066ca4c7e982d764d6ceb4324cb33776d1711da1beeea14e42616279416c69656e303230323101581c1fa70d2ac412b5bbf220aac84152781c0c517f7aee4667587a31e395a3494d5554414e5431303401484d5554414e54313301494d5554414e5431343501581c3d193eec8d59640cf458008694713a5ad2f82dc36a674b8602e14018a255576f6f644c6f7264734561726c79426972643034320155576f6f644c6f7264734561726c794269726430343901581c48766b78dbb52c7fc4617acf4503a9f40b2f0fb3448ba0ab0e1fda09b81e4c5374696b4672696b30353438014c5374696b4672696b30393432014c5374696b4672696b31313932014c5374696b4672696b31323039014c5374696b4672696b31323539014c5374696b4672696b31343330014c5374696b4672696b31353034014c5374696b4672696b32313930014c5374696b4672696b32353436014c5374696b4672696b32383539014c5374696b4672696b32383830014c5374696b4672696b32393133014c5374696b4672696b33313531014c5374696b4672696b33383136014c5374696b4672696b33383235014c5374696b4672696b33383939014c5374696b4672696b34303034014c5374696b4672696b34333133014c5374696b4672696b34343534014c5374696b4672696b34383933014c5374696b4672696b35333631014c5374696b4672696b35363930014c5374696b4672696b35373033014c5374696b4672696b35373434014c5374696b4672696b35383333014c5374696b4672696b35393837014c5374696b4672696b36303439014c5374696b4672696b36323436014c5374696b4672696b36373930014c5374696b4672696b3639353801581c545dc1601b5d469508698f7966a3c1c8cf207073041ad84b28d373c1a44954494745523330343801495449474552333730350149544947455234363731014954494745523737343301581c7fd73f364a5199a15ba8ca6bb315ba8f1ad301ddf288836ef22e4861a149524f4745523231343501581c82b257802c1221050a0f74390bdbb8a08da2fba93c08139d8a52c117a14843465249454e443101581c89fa6dc66a24799ccaee43a3a16930bb045a8152fdf2a2642034774fa24f506c616e657450616c7a3131383637014e506c616e657450616c7a3731383201581c91c8a0919806700127fd87889d88eb0f42282e183f7accba2a9eaa83a44c4c6f636f4e75747a32313333014c4c6f636f4e75747a34313232014c4c6f636f4e75747a34323137014c4c6f636f4e75747a3538323501581c96580bbc4fe27ac0d127db3f8a0dc698c58d303d8cae870f5771f336a2515a6f6d626965436861696e73303131343301515a6f6d626965436861696e73303936343201581c9cc83ea54377108dd022f18095040e8d5808a3061c4fe54f3fd64a7aa85048616c6c6f7765656e41706533323632015048616c6c6f7765656e41706534343633015048616c6c6f7765656e41706537373735015048616c6c6f7765656e41706538313031015048616c6c6f7765656e4170653837363401524d6f73744162756e64616e7442616e616e6101565a6f6d62696542616e616e61363739436f6d6d6f6e3101555a6f6d62696542616e616e613931436f6d6d6f6e3201581c9f82018506cc1042287394c15b7165ac7529ee6254c062de849675f2a151436f6c6c61626f74726f7474657233373401581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a1c34515e581ca706fc87764cde4ac018c38bf61630c1065932db49e6f495be3b29f8a35818436f636f4c6f636f426c756550616c6d53314e465438353101581c436f636f4c6f636f476f6c64436f636f6e75747353314e465431333901581a436f636f4c6f636f53696c76657250616c6d53314e465435383001581cadc5716393953403109c335e68c0384238fd19653e960e03afa1fb1fae4f546865526566726573683031353030014f546865526566726573683031373934014f546865526566726573683032313832014f546865526566726573683032373733014f546865526566726573683032383430014f546865526566726573683033343235014f546865526566726573683033373631014f546865526566726573683034353739014f546865526566726573683035363134014f546865526566726573683036343939014f546865526566726573683036363133014f546865526566726573683037313638014f546865526566726573683037343836014f54686552656672657368303735393101581cb9e464d1fb4ac58f54e4f8c4d404123126f53e463dc5ee87108abe6ca14d7a416d6269655331453131333601581cbe8e242b0419d87a15b23f037eff046796fc60c281dce89bb7966bc0a251456e63657068616c69634672616d653533014556616e653701581cc7fe3a3c3443a2a3d453efb7df0c276712bb22f8470f12965f5cae42a250477265656e466f756e64657231353837014f477265656e466f756e64657239373201581cd4cdb1ba9933ca9737afe8873bb6448f85aaeb8b3194b74bebd1fb8bb04a41646145676773303138014a41646145676773313036014a41646145676773313233014a41646145676773313838014a41646145676773313933014a41646145676773323437014a41646145676773323735014a41646145676773323836014a41646145676773323838014a41646145676773323932014a41646145676773333039014a41646145676773333238014a41646145676773343330014a41646145676773343337014a41646145676773343835014a4164614567677334393901581cd9589c1a401765c82dd3536bf8f9f4337e15dfbf708e3a7f7e2f37e4a24d436f736d696350616c37303130015818436f736d696350616c73563250726f746f7479706533353701581cdb9092a340fa079b9ec6066b646046efc0f846e96abf7eeb5f3959e0a15153757065724f47486f6c6465723030323301581cdf3aa00401528bc2c2f01394bbf132fadf101d56adeeeb3056572e07a148504f54494f4e363601581ce3532fa5f339587f9576a019877f80f0b7a282bf971a48241718c6d0a25043415244414e4f434c4f434b31333731014f43415244414e4f434c4f434b34323001581ce58cdf80aa522cbb2e5975b11c3ef9e2c4a80f71770aeda4232803faa151756e736967647265616d3030333430303701581cfe29b1951d2439cb4f8c05ad564728333e762c24bee36d7f1a72419fa24843535333323030340148435353333231303401021a00046ffd031a02bfc7b6a60081825820e00d1c7c7a1f7d028083e5185b84804b0265ba6e1ead9b1d478b3c9085050550010d80018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901f7c0d2648a41d97631e63320aa87d1ff0c6aea7b9776a27e3575bdd439658d3732f3e9f1c349aa2d9d1773feca136d27c367e8827ef84d601ada90acaf021a00028f6d031a02bfc7f80e80a400818258201b03ec0b73a2beb53f6f1d198f5d1dc4a3aa8e04e42bef1ad52dcc06cb7d358201018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390169eb7003411e24c656cd815a3b60e5c3a7cb74454b5593e940b6815621d0edffdffb4c5730218a61649da215843bb4a5701b8fb82c8edc48821a0a060936ae581c101dcbab5b3c6d18f9121613cb999d12600e5e7e77c147d455b51443b357634e6332314c504143686164694e617373617231363032015818634e6332314c5041436f726e4e667269656e6473323135300154634e6332314c50414372797074696573313437360157634e6332314c50414379706865726b69636b73303836300153634e6332314c504144726163616e6f323431380156634e6332314c5041466f7274476f7474656e323235360154634e6332314c50414b6e697474696573313531390157634e6332314c50414d6f6e7374726f63697479303530340156634e6332314c50414e464d656d6f72696573303038380156634e6332314c50414e696674795465646479313039370150634e6332314c50414f726373323035380151634e6332314c5041526f6e696e323232370153634e6332314c504152756770756c6c303334390156634e6332314c504153757368694279746573323233390157634e6332314c5041537765657466656c6c617330373737015818634e6332314c504154696d6554726f7474657273323339330153634e6332314c5041566974616c696b31313134015819634e6332314c50416e66744e455753757064617465323430370156634e6332314c5041776974687370616365733131373001581c15509d4cb60f066ca4c7e982d764d6ceb4324cb33776d1711da1beeea34e42616279416c69656e3031383231014e42616279416c69656e3035323334014e42616279416c69656e303536343701581c50a6205cf3071ac23e4daf02b26d5e7b8345eff36fb23a458c7474bea3581841444153504143455348495053314d4f524f43434f313431015541444153504143455348495053314f4e5958303634015541444153504143455348495053315255535430373201581c5d5b205252b9f5016422d0eace869d7fd45074a4ea4b6c1dc78d1705a24e4d6f636f73736949746f33363736014e4d6f636f73736949746f3432383901581c5fa72fbeecbe80a3e15de1cacab54ba5e310e2c36ae85351132ed4ada14e4c6567616379506173733138353801581c7d2d1ee92be476d47043d26ac5a45402e3bdb50a34aa8d716babeefca34f53706f6f70794e6172753033393633014f53706f6f70794e6172753038343635014f53706f6f70794e617275303931363701581c7fdd25a4d6d8d8801034dbe4940248f802f6fb98dea4303f1c51ded0a249567564753036333633014956756475303732393501581c805977fe98a844f2e9536e1017a8e0b03c2f738ebfb831f1832cad46a55442616279526166666c655469636b657431353332015442616279526166666c655469636b657432303431015442616279526166666c655469636b657432353837015442616279526166666c655469636b657433333930015442616279526166666c655469636b65743430303101581c86bee6e88ac8fe59b1501e5bd9143f67d3ac80078b35747c25cfdff2a1494e464253433432373201581c92218acfedc4726cf66f88e1e174debf1d085fad115e790c2392e67caa4c416461436172643133333030014c416461436172643133353432014c416461436172643134383837014c416461436172643232373335014c416461436172643233303637014c416461436172643233363737014a41646143617264333235014b4164614361726436353539014b4164614361726437373231014b416461436172643938353601581c9cc83ea54377108dd022f18095040e8d5808a3061c4fe54f3fd64a7aa1524d6f73744162756e64616e7442616e616e6101581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591ae709b386581cb92f6473f18d4b78733d022fd89f3cacc1484fab6eddfd3c5d4b9494a1474450303430383201581cbe8e242b0419d87a15b23f037eff046796fc60c281dce89bb7966bc0a252526162646f6d616e745370686572653136380145536169313601021a00038535031a02bfc738a3008182582007717c1d01fff109116c2dc9c58e761a330ec7c70b9b3a0385568bb7c6efede500018282581d610dec016bdd4393be3c3789e19301f130cfaa1a6c75e84480192b316f1a019ac5d282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480021a0002bedda40083825820dcd6945e50a4e689ea164405f5e132fbba571d36f59a0dc5e91b59bc40fc596e18218258202e9389291af5b1cc0fe852fab069825851d68c90d64cbab934caa9ca0814220518438258204e7b593f48d815e880a530f22c7b046fe70788c1df1a1b0e9c034f32fd8498dc1838018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839014d89960031465d02261c474ab96f047f0a529ae6039bb82ab79750553249e929f4e3b9fc3f4dc0721ae6127951432cd205d9c6a253bddf09821a0023734fa1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a08cdc432021a0002b251031a02bfc7bfa400818258206308a6bffc0d8d3ef25228d0f11011f2f100ffefe4fd84eae0262e7a714412fa01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082581d61b0952b88d77cda1b32177d4cd7df9dacb9d3d21928864ade6b4c64141a0076a379021a0002b74d031a02bfae26a400828258200ca13ffe1b356b1906f4ce5e7fbbb0dee36e8241a6fab659049287d9cb15ea4013825820b89eb1ce15e20c5559e69a1d210e17f844cb778209ed1c01fdf8d00cb7ddbaab01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082581d61ca375e0fdfefb9751d9949ec85e26bad0f3525f289486db4b4f3bc93821a005fb939a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a0b51953f021a0002be2d031a02bfae26a5008182582091c41705523aa11e45e2dde8a822b433cc5a0964091bcd39800148e83fbc265a000181825839013a899b4951d3cda996b42e14d0f299e2df89db31ee9ec5234a03de286b167c1b6a62afd0fbfc75786c2b6aeed4a774f3a76f2627ad440a861b00000007310971f9021a00029eb9031a02bfc7ac05a1581de16b167c1b6a62afd0fbfc75786c2b6aeed4a774f3a76f2627ad440a861a35211905a40081825820e12258fe7734a1a932fa3897c2e12fdd7d9031bf4bef6d7055249ea1e6efcc1f01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839015d199f8402786ecb9a65032c37b86e4fb4439bdc7f575ca9cf5b44b35e65dd9758154372d525ff97d11fa206c3750160236258984b80b21e821a59917179aa581c13a8b644670a6e77690dc774dcf0b00b4e2e769533cded62a9321e64a15343617264616e6f4170654c616e64313330393301581c20ff04da680249a01e4bb70228ebc4c4207dda7305b04532c84aacf8a3524e656f6e426c6f636b73533130314331333501524e656f6e426c6f636b73533130384331353201524e656f6e426c6f636b73533131305531313001581c2b62bb0d57c3577d624cb976f58352c38d967a30436a6acad44f23f2a1474445313031343701581c545dc1601b5d469508698f7966a3c1c8cf207073041ad84b28d373c1a249544947455234393534014954494745523930393701581c5d5b205252b9f5016422d0eace869d7fd45074a4ea4b6c1dc78d1705a14e4d6f636f73736949746f3833303701581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a68a76269581cb2d25f829ebb7f4c97b5e847923a1115b23ebf78000722c229c9c9f7a2474452303631373301474452303631373401581cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cca14c53706163654275643330313501581ce09e4f4217669b7f735b7a3724e835d8d6344db128eb03d6ea72885ea14643543334313201581ce3ac0dd93edbe6bafec38fb120cf7c3e223686a97261008c2bfe0d6da24f43617264616e6f5370616365583332015043617264616e6f5370616365415a393801021a0002e715031a02bfc7f5a40081825820dfb4643f3f366bf472c35ef6c672f070a32bf788abfc64e0549b17d91423497401018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901baddbb5c03c44fb53223c788a4ad9ba4057ea013c97f45f2ccd31e35285d02bbd02926b50649f23bf8660b997d74246262cbcca9f18622ab1a040ab4c3021a00028c81031a02bfc7daa40081825820aae577a165bfc317706fbcf14e8966b396272842d71d949a3232dfbaaa30765801018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901cfd67aa265225633e94637fa2b944a9d1c54b356dcdf6ee7233692f50448011db665a856db6f5cdb17d75282eee632821136250fe70e6b07821a080f0ad7a5581c160b85e53e25ef49272c421f04b702bc32184d102865fd1dc8815cdea157486f72726f63756265303230363178383030303030313201581c95c248e17f0fc35be4d2a7d186a84cdcda5b99d7ad2799ebe98a9865a14b61776f6b656e313339303001581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000116a42390581ceb5380c2449087b16a0eecf0139aed4a39632ef27fe59b06074a971aa150466c6f6174696e67486561643030363201581cf217f1369229c314c5377935f95097b4b10b83c779c53888e0b07f45ad4950756e6b4173733237014950756e6b4173733631014a50756e6b417373323032014a50756e6b417373333630014a50756e6b417373343235014a50756e6b417373343330014a50756e6b417373343339014a50756e6b417373353931014a50756e6b417373383731014a50756e6b417373383931014a50756e6b417373393433014b50756e6b41737331303531014b50756e6b4173733130383301021a0002cead031a02bfc7eea4008182582054df67610874be3f5ece458c674494ec4ed2929b82684023de19b8fdee9fde2b01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901d2c3879c608758d416a974cd7a22632d5cc70e17d900887ceb8b9ea2ec4b385c08bec637d1d02a94272ab350f0363b51c15e3d66dd43fa65821a1b918918a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b00000007d01adc33021a0002b06d031a02c13275a500818258206a8fe15757efbff249e507b82cb1d96f7e6be0edc0d484ca5c28fe6f3d998c29000181825839010c5c6eff6e6455bb95d925445b124cfb24a77fdd0a20ad9a2cd948d227900278c1b1178634b1cab0aaa5a3bc9e4ba6045184a5af0ac7750c1a299fe116021a0002ac35031a02bfb9b905a1581de127900278c1b1178634b1cab0aaa5a3bc9e4ba6045184a5af0ac7750c1a0236017fa40082825820216648a78921dbf5fb467a400ec5d18da3c39e6f213b20905a3ea4f74e9b48c600825820b26b9b0abf4637765fb8b1a16ff4b390e047b5c73bdadcb16cf49e60e81872fd00018382583901ecbf6d0bcd477ba650fc3031ab27e572ab8a44f833ec7ed41704a07ac750e8dda744a926f96a8d728890b2ae1e521f0bb53e098995e4250c1a0743aa3882583901e7fd4b6633546871d39057643ed214f662a8388fd9f4226802a7c5ffc2c2b7075e7954c00f25e0238aa783d84494eadeb7fd968dbfeab651821a00169b08a1581c5d5b205252b9f5016422d0eace869d7fd45074a4ea4b6c1dc78d1705a14e4d6f636f73736949746f363536300182583901815f778a2ba10aa0fdc4acf1436e701f78d6f717361d4164c95dcfb77e9edebd75b1908b72519186c997cd98e6d62e5636f0e3aea1ee51401a002c9f0d021a00030ffb031a032d88d5a40081825820f3f90c1ce2d4d0e9943887b03284d702dd28888298bbeecef338a1eba4fcf66101018282582b82d818582183581c1670a9ded8384b3eb5346baec9df7a9cdfc220f08369e06cf4bf4cbca0001a5fa135c31a16e79da182584c82d818584283581c776eb96b96e2cbce528a47278a736ab725dd13ce98c027ccb548ed70a101581e581c1afbc57540db1561adeaccd7f4894065965cd230f6bdc70722aecd8b001a6a14bf9a1b0000005b6bf12d8b021a0004aba1031a02bfc7c9a4008a825820a0c5925650f61dcb6d8bdd146fe875e32cc1a78f1098b1885952dfbe62bdbea101825820e098a3b8b3cadb7ceab1e1f37953971fdd5fbe14bdf5f8fc1ee13eaf1d5c86a600825820e979d09b96571ef02a3c5ef57983213fa19e4654035a84e90c3257ba205a272100825820cf913ff6d6ee86212ff4e7de0e5fc4f4a03c251c3c891dc0b00d953f714ff6d40082582054c99efa08228a854e135d925ecdcbb67506f652cf0c5682bc628022a621a5f3008258203118db1d84fa35cdcacf508a2adf4f514b7cc9bf687ad8b9e95dfb80bc5add2a00825820b545fb86aba06b1737b8261611fc65f68ccc9310e65d9d842d77875a6d5c3d6700825820a6f9c62a4b6521dfdd33595d8e5f6f56574bacc3102146f75a242c2c2b6180b60082582092edd857ecb9d436d5740106641adf029ad1c8dc52fd9bda9f6ef3a79d02950900825820c92d600321a0ea23a839a04a79bb5a7fa52b46028081959beebfec8474162d7d00018282581d61d2dea77d642285f6a8d80a037b1e762357fc9473d20fb27ab19c26061a017d784082583901ca805aa0ae23a2b55cfa7b63e5fa6694d478ee1fffcc21dd5962bdc1a07ae5842ab0f57ad945e80c75343f5d0c0b2f9185aa42b5dad847d1821a003002aaa1581cf217f1369229c314c5377935f95097b4b10b83c779c53888e0b07f45a94b50756e6b41737331303935014a50756e6b417373323835014a50756e6b417373353033014a50756e6b417373353133014a50756e6b417373383038014a50756e6b417373383834014a50756e6b417373393131014a50756e6b417373393132014a50756e6b41737339363701021a0002edf5031a02bfc7d0a40081825820a2d8acedbe4dab4c55a57677b1c01cd3e63ed7b9d0ed6e97b2125ed12dc7ba1e01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839015b49137d9381c83d83df89cf4e470371754e8040fb2759002570df6ed66ed1da1a5e784481ee8e969206a97741c4f496f2c05836c093e42a821a1bbe6545a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000760789133021a0002b06d031a02c13275a40081825820fb3721116d8a21de92c13973277add25b6125a8e6763317892634f80353a9d1f01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839012bc0708a7930c733e28a2c4bceca040ea0dd5f390aa9442ca94705ad982cc2d70db59798df95324d6df2eabcaa77905ae40c35d7edb8712a1a0f037e47021a00028c81031a02bfc7a4a4008182582045658de5ef82243a77cc2f1797a6fe4f8bf1f725b899780be88935741307835c01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839017e414ca5f98fa6704d9b9f7377e86382ee0dcebf96080ca047f5f1d1b4c7e2be65989d5bdfd22de893dc1bd15b09b93b21cc76be03b1c0911a02559a9f021a00028cad031a02bfc7f5a4008182582077968a0bd757fe2c7f93cccd01b69f4b09edd0972f5def67b212580b9193a76101018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839012f4c4b165250cfe77983aa515ffeefdef5a06d56ea852a98dc45472a9353a791ad3329be627acf65d6c642e34249f759e052a7c90ef54a4c821a17e38e3fa1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b000000077983e966021a0002b06d031a02c13275a40083825820cb6861be0fcbff4c3a5e604c12689d52acdf2bf907f2db92f20efe7f4f44b8e400825820903632bfed52df1695f09f1eaaf4171b85a383545568164cdff90f5e65948a8200825820d21cc65c214f62e11836b5ce017760d8195d584df8e3671d4085d592c4f6c6b600018482583901e772604064448785bc316613515d860b02f4c6c19fca831cb663f4c5bea3318c5385099372bac2d3781a7c10a8cdd28c59a05d78a81b0bd01a000f424082583901662ebd0e03078f408307584c4719ffa0beda35c9c8c48b8c909726d5cfee62e2b07604e842cfdde9c2a7dc8b4b5c0defc79329f9492500c61a010df55782583901d10a19872b9e94408a321b4210c0e3aa6ef8a483fbb8b6f619bd5c4fbea3318c5385099372bac2d3781a7c10a8cdd28c59a05d78a81b0bd0821a00169b08a1581c89fa6dc66a24799ccaee43a3a16930bb045a8152fdf2a2642034774fa14e506c616e657450616c7a343232300182583901815f778a2ba10aa0fdc4acf1436e701f78d6f717361d4164c95dcfb77e9edebd75b1908b72519186c997cd98e6d62e5636f0e3aea1ee51401a000f4240021a000323f1031a032d88d5a40081825820861b445deaa5291c46464b57cc03edc1fe297319484ecca57cd4a1c80a9c01d001018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901716fadd9099b5bf78b020ab1b235caa8f1e560c9bd7513a9586c941ee3b158716416bb3d65f631245b0ac50eea0b07678a00031f212384051a00cb6d7c021a00028c81031a02bfc7e9a60081825820409d255b724ce46d75e1455e8c36b2147f6741844a83c00ce9b03343658fe0ba040d8001948258390195fe93470ef842863ae237f9489eea3a7e3891b8b354f86a25a741dc34dcd74cab21790527a92e5bb39f1687f3f954dc34df066382dc09cf1a010659a182583901ec4e35331f7d62372b0ef421be13c4de69b75c76a032e629f2bc6219ef8564f3e0c966ef696ed6095980e787d68c87ea1fa78805c560d20e1a1e620a57825839012c2a207233957cfb888c60423ef67e2ef09b8ad76b360400a581e2d1a19f265a2c9a19be06a70d1f54ae278573d39157e0f98a6fe9ffc1071a146c3fe9825839013cf9270ed965dfc017756d34c10a52e7805e9f1f98b7240be4e553195288282c1c27abc00e3584f3940abc2826ad133e40054932d37f7b831a162589b58258390151e43d230a90b326d528ddfa5f644b7ab0b29958272eec3ede498436e0b8c4a1c15edd10dee806193b6956f43bf7fad10439074db5a6510c1a380ba05b82583901cfae108c913cbf95615f8c8149dc727d8c75b7a5f9394a4bb4a5d58c0aba508a08644d3b98a83debf7f99b05536800b17845fa5c6855b28c1b000000012550804082583901a0ff7cfc1d1c19d3b1006833723f0545e003e9ebc407a9ef6bb8968b4fa46e41a1111c816ca03188dee83445cd3b0b2fd5ab7a18367836011b000000012a71c2ff82583901b4c83dc14b6bd2f02923a55f8ed211dfb05334d4d58b7c26ead0a5c1b4c83dc14b6bd2f02923a55f8ed211dfb05334d4d58b7c26ead0a5c11a00bca98382584c82d818584283581c9a49ac02acc60e2ba6be151038bc44675e5ed53203387b967dc0cc99a101581e581ce6945e2c0be0cbd453269e8c63ad89ab6f153342c1c480fda81c61f7001acd297ab91b000000023c43a2408258390176d46e9119f188bdb08ac469553e4d174a62393074839c077185cd7c0401181cb4b25f27b82a8f1476f292ba1a5090968860767805db84571a00c8fd9c82584c82d818584283581c87749ed99ca020977286f9055c59ef6e954ecb560dcf63b600c56de9a101581e581c2b0b011ba3683d190dac282a5f62f9efa4ace67fe1937ea7e5722eb7001a6287f2601a011c8e7082584c82d818584283581c790ac6240dfe61191a16be2ba8ee3d540220fa91afd8590ae1b08076a101581e581c2b0b011ba3683d7e8d36be2a701f8704c115045f772182b57c800b9b001a8e9f5afc1a1f3f490182584c82d818584283581c775c8cffff1f54d396fe262fc5f9870dc8a65aa721d0cabedbcf59aba101581e581c2b0b011ba3683d706d73fa2a51057ba8faee81abf3bb011e45c4df63001a5ffb31f11a1505f7da82584c82d818584283581c99942b6408ca09e7d14dbf646259eb562d0b3bad8be8c8bb53f21d27a101581e581c2b0b011ba3683d1b169b6c2a7d5b13adb46da8e55bc0340bf022c7f7001a7ecbef1d1a16caf16f82584c82d818584283581c05fe352248799ca1d21b731d9987c48d29c46ba46aabd77da303dd77a101581e581c2b0b011ba3683d54024cd62a54877cb5284752735a01ed9db41a203d001addf4d33b1a3996da9c82584c82d818584283581cbe02c212d3ec7623d1c028807fde9aea799939111f80bf3d48f7de43a101581e581c2b0b011ba3683d11195e472ade1588d9bf9e917343258a1d8365a913001aec326cc51b000000012d24553c82584c82d818584283581c7635fab73e4b77f6be87737f8aeed4dfc7e0d4af0a30ddc5f49ff090a101581e581c2b0b011ba3683d5f7beeff2a6590589a6eb43c7f5bd7ea309e0a4c6a001aa488b0c51b0000000132685f7282584c82d818584283581caf44df2c4923d404e0707bff22631505be2233d4bbcda5abb6eddcf2a101581e581c2b0b011ba3683d606c15d32aed5db68c107cb7cd16129d9e5fbc313d001a8b4516331a00d0eabe82584c82d818584283581c64d35170d696364bc78a3544ae43166c1657b79d76a047dcdde79cc6a101581e581c2b0b011ba3683d52f2c9d22af1b12a473267d55fcf6ac4b54264900d001a73ebfd091b000000024b7aa6bb82584c82d818584283581c614590e91c511e1914bad0b141cc73e222a9855ee83a3f2fd8755fcca101581e581c2b0b011ba3683d361d6eb72ab21a0b46f8feb45fef7fe303d7788f2a001a8db766011a00dd926c021a0003a635031a02bfc7ad0e80a400828258206579d63ee68f3733549df055426f181a0357a62f9c370e49f83010d50a98a12100825820251c22e78986bc888f7c1519e41acab28cd87617945a0b300732f1e49708443f00018382583901f16b3c3147fd7799383f06ce2fedd7b3b0d206559e65e7870c2413c8de729df8bc6b782d53a1e61fccd31a6507d9a54f95a13943379b64021a029a6abd82583901a4efeed45f4b6c725214c45347109b23a49ca83a263ca41d21121feb093cf6dc0bd040f5eda2dacb08bbbbd62c748efd32aa9abd5f5c4d88821a00169b08a1581c5d5b205252b9f5016422d0eace869d7fd45074a4ea4b6c1dc78d1705a14e4d6f636f73736949746f303937380182583901815f778a2ba10aa0fdc4acf1436e701f78d6f717361d4164c95dcfb77e9edebd75b1908b72519186c997cd98e6d62e5636f0e3aea1ee51401a00112a88021a00030ffb031a032d88d5a40081825820577d74ee6337d69e6939d7b0b9cfdbec28645f09adc6695dc6043d86616d0be101018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839019f2caf0384b9c3928d9e4cfd548041a60a8fe73543f5b8e4ae080a4a4ab75d749fafe171825252fc67f49c5db58cc23899de2a5d5159e4aa821a026c4599a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b000000038a83d61e021a000294ed031a02bfc7f5a400818258209772f042b718a89e1d29340c90ee61ac1e2cd1b82c961e18fcd517f34ccb3a4101018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082581d61a7639ab89d06d6246e57c431f43f80a340faced018274c2f9d02868d1a0034549b021a0002a98d031a02bfae2da400818258208fbbfe613f041913a6092ef9855d6aabe9f766376b24f3da5e6dfddc4d70636301018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901543f099d9a5cf11bf348d254589d3a068e2e0a5c19235073403075bdf462136e794e102dc8d60feeb54bba003f2540e7618e236925366899821a19c5360aa1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b00000007c4446900021a0002b06d031a02c13275a400818258209fd5bf2b2ff2e541df9c3904a8ec0c83f94222d9e1f44d8d02c07928796a509b01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e84808258390134fa30819d48e5385b754db9af18638ee24c696fa0c24e498ec3c8681f2eaeef5f9c6aeb106dd8d5651ccaa23ef3795b1894d4c853d4b2f61a0ff8accb021a00028cad031a02bfc7f5a40081825820ffcb72d8c585c372a5eb78e9bb28ccfbc998d9d2bcea9fd67c1570c513ab4dfb0101828258390130838f3501645dabc77b6ee1d0c9667b5d871dc7fe60a9a987f80a8d57400bd21cb9e72f8fe6b9b9764b4818913f8c6cf0efd3fd1a593e561a005b8d808258390131fcbd1bff28e43abd772d265d56d4f20442eb8dd726cfee4a9dc65dcc7bfd3c735d99f35d4470a235d87701709bc16b3bb181a9c3d6eac11a04844f83021a0002bfc7031a055d4a80a40081825820f83e249bc86f4ac1b983f574793ea62b3ed9593dfe6dcf6039a28ec5590121e901018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082581d614982e999fbca1760a03c38ba550beefdf5a407ad1d6f05e6b576d8121a0286ca9d021a0002a82d031a02c13275a60081825820e76f8958e325250804e70769968a7edd64e586c863fd356fca388f9b94fc067a010d80018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901e560257c13fc3ea800252ea3f7aafd7d3e41263f716c8000428cf0cb5777b908dca26ac64a873f70710c30161183aaab0d261adf25c328d81a00ebd7e5021a00028f6d031a02bfc8000e80a40081825820793662fbf2d368e27bbe1991e9e2d01c1c35c5c97343bda31b23d3e3bcb3aa9d01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901c37d97d695900aac5cbc78bbb7ec3f9a8a1cd7325d26481492c3b23a99288f773a3bdcfe3d0a3e68f845d7011d74c99a719436262ce446f4821a10a0c96ca2581c4c9f7d6c24ba8e2b12f3269ac38d706025e39a50a524afe6eaf79d95a14f5665676769656d617465303630343201581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000191a7c9cf021a00029d2d031a02bfc7f5a4008182582094dfc3f4f67d85a9c09944542dbc08337f63b10a5d61cab1c0b61a2964c0f2d401018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901c1ca70ffa0d96c35c34bb4dd9013fdf536a1103a5e964d027a2edd41af4d70a6af81c69549c14e173aa5988c03e61083dc24be2cbfc0e218821a013a8c7ba3581c828d65a3552cd6b117a1e2e85d2fbd186f4b1471cce619e4955f0dbda24e4b6165727546726f673030313231014e4b6165727546726f67303733313001581c86d318effcb8b09f4a8c7c4bddb70b9745d9772b35235853f0104624ac4b5368617065733031383131014b5368617065733035373439014b5368617065733133353737014b5368617065733136313137014b5368617065733137343236014b5368617065733238333236014b5368617065733239313236014b5368617065733332363736014b5368617065733334383236014b5368617065733338303932014b5368617065733339363232014b536861706573343339353701581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a001e376e021a0002bf35031a02bfc7f5a40081825820afb403a817099adb33ab5113e7ba9c46065bdf56b4913e827b48a399d136551601018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839017690cd528f19b1facc8e09f2bffbab884319a4650916629cf96f3196e0b8f6c8ee21dd02ea1b11712e8aa27d2955dca875041a3f6530ffe6821a18ca92f2a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b000000078bae0302021a0002b06d031a02c13275a400818258209037cbf5319424ac65fa128cbc8a45bb497194e59bc5994a1971219e3d2ad5ac01018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e848082583901ea49f789a303d5b20a899cd5d60889ed0a26fdf6be389b21a2ef1f20d16a5a0c2a00b2db8e594652ed066a5de4bc10df6ca02059f2beda75821a0557408fa4581c2b62bb0d57c3577d624cb976f58352c38d967a30436a6acad44f23f2a7474445303834343701474445303834343801474445303834343901474445303834353001474445303834353201474445303936363101474445303936363201581c547ceed647f57e64dc40a29b16be4f36b0d38b5aa3cd7afb286fc094a1476262486f736b791a002c4020581c91073759dc9eaff922791f6204d42749541e25d2c3e2d301925e8ffda24f4e46544c696e6b434e784743353433014f4e46544c696e6b434e78474335353601581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b0000000f0c70fc40021a0002b7d1031a02bfc7f5a40081825820ead70f29bfb0180c43889229d050c20ce8eeb3c48c81670fd7997b1cd0198a1801018282581d613d578fd1329d16f14632fdac42b76eff513958a5fcb6b7044c65de121a001e8480825839017729b1d5fac57596d9f6cfd086bfd094a8b1d43fb45432b0654a6a9bc55d1563fb2bdb0685b420560d829baf2b9fb9adbfa9a4e08fc8309d821a1c1ba9c2a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591b00000008155363bd021a0002b06d031a02c13275a50081825820044a5804d53b7d900d3f46fb02f7c97508d08078f642e1b70fb54b32e64f8550000181825839012da31bd233a08c8c56bce2ae6cfcba233ed0365304bdc85f03584b251143e2048c1a6e968f139b943ad50a1f76faed8789f428a983fd93761a008835d6021a0002a8b1031a02bfc7c5048282008200581c1143e2048c1a6e968f139b943ad50a1f76faed8789f428a983fd937683028200581c1143e2048c1a6e968f139b943ad50a1f76faed8789f428a983fd9376581c37a4728a33f13c83c0967ea7dbe06eb3437bbb4d26d671c5093f3e4bff9fa10081825820be1b25b33a92ce8230a65aa16130477ffdb9f79f8d97cc435ac2035437a96f4f58400d1247febcbab6440ec30dcb84ede00114bec73f2b3584b781468cf8c8f58572fb686e780708a5efb4b9532e88eab6f8a126d44821f135d0d6c594366165f301a10081825820d0aef342aa415cc389615f11417167687fd372be4eb30e3d786576b31f2f51135840dce509121574fd1f3c04a44a4be761784e6a1178f17b75cc7b1140db1b69662c8e677884662a5db6198db3a9378885fabf5b26fe2888f42fcc41d5a77a672b06a10081825820e60bc14f7866a5cec0fb5e1c150ab7dda9cf47d1c9d728c785e14b144701df50584002edb50a7e1d48ea68d3d511cd8c8d7d3134e5f3bd7f28a96f82169afce35ca2b8efe2833608ab584e01ea26aec98e79b8fa1237380578ba34205e61ff9cc908a1008182582078db7936aea986f19bab0235a9a364d4e63cad815d92d435b2efaf59bbe5b2aa5840de078b14127ce5e0c41aeb9d03d405d3daacd5c600b88298b06e2d63244be39f8b76923d03996a79b82b209853e7477815251da85e1251061fd3f0257615b70ea100838258206b69b3aad5207c76a7061f146a9cdf8f9643de4dcd0e42db70027ae033f2f7265840839d2c7432f6fa653196efd372266457e7e9c18a15da1d3972d703890d9f6c80efc2802bd55b6cc153ad4bc31128a05364567c1e93bc0ac83a29b14bd22c280e82582016bf7c500369bb6715f181702793fdfaa7686debad74921d91137e2c04b38be1584069175fc945219981604ab17b0dac650596a1e9bae3a80d4425ec5cb8650bd2dad3a53d90f5c13df577362971144e9a2ef75ab2859e0ab813295f43a16dbee80c82582079f361d96106b7c2f6b4e0cb304e2cce36fcc76c3855349bbc341bb8eafe811658401e27780d35cf05d0b71e84c149614dad5b747ec5a028f4ac3be2fad310968d13a7ae6dd0dbcc2fbb8c6f3ad7824a8feb90f9c0a12e3aa19f5638975b41fded0ca10081825820d55e6cc05f752af30cdeccaf8fda559030720ff45ff97a508e7993747a2213e45840418d21e3404f5643f1fd1ca300baa2e460c4d95659e5334e5056094892f233fbf506121e0b24498c29a85232fba15ea8bc228e1733266ea8fdf169b0d9aa9d0ea100818258200ccbb502206d203e3c5bf5d06bf008984c7822a74b2f4ccd4303fa50cc3b0d96584056467d1e09018b5ab71e6fb6fcfc909aac3d3a56ac9b3e183c5f8486628f61755fb6690192d52e05f499cffe7bd106dbcea4dcc9252bd75184c698ea4d625901a10081825820dd5e46ce02a8db8156ab2da36b54439faaa81db71e644a53e0c63ead36cc61b5584081c28fa473dd797f57e23b0e6ee7304ec01610c652a360a9b5aa5bf6622273646bfb4be228850f30d481b9bf2a76a6d7847f451522265feb955c6963369ad908a1008282582047bd272208181f1024c5c6f72d091a8288309951656323e6d4b2e5eef8ca908758400a6396db4c9c250a82081c49b673eca3548c924671c45b640756c294ecf7a6f421ad3327377e2033ea9bfbdf9a8213f7e9be8a52d03ae531bbe8c584f8d9ac06825820121a9def2c87c3a1fb6305d648ec5eb9767b14fdfe3334791525a6dfbbfabec15840c2541038a560e76a450d0e6c4353db76f599affa13145c211be3e65628d62b496d59f3ff23470bc60b550ffb3d8bb63d1577bba5160a71cd7fb5cf7f1fd98d05a10082825820be274449fbf99afa25382e341fd734b5f78e16925bace3add10bc1dd83632f865840ebcbfcfd2cd32111bf0a34a8cabb8588a638e85e35a44ee0eb274792de19012b953e4753aee20133daeb92aae5789ed5804931483308c23c4db68c2176927a0082582051aec8c6e509a236ac66aeff3a4edaae8d088617c5c5af9f2b37ed70b72d7085584001c02602f48c1cdf8bc43549edd8f6b108dee308d01ac1be8bc91af693e4ca4d818b05bc517b6c5b0a835270a3e2d2e776ca991a0d90284d28e6ce4da23b620ba1008182582062c63dd1ec683f4e96350b84e988de05ff66290afebc0bfbf56489650d0159eb58403ccfbbb13c90283923f9cef2fde09cbee4ecedd12de1235040ca2ea122e90d75d6c8efda070f8c1e4b3d38f2bef03a4d499ad60cb4ebc28fc339a3292245d70fa10081825820a34c1abc950e178008285df8c25dec4cc5d2178c4714726009e103d6bc00a65c5840149c090626d1a024308e8ec33349c8175f29dde1cc0dcc931001fb31476401234eea6cb5ace02a04bb9f12f9dda8dfc3eb57b2026051e9899dfdccbd06e59f09a100828258209ef21831c5b20f51901d3c81d8d5b1f8093d77d818d7d400b673a3172c4b74415840210c02549567e4322a30a6a42b343883245ebfa30d720fedf8855f967939dd0187e349eb700c57720987dc1f58b6c704543b1543835d8c579b5c09a5b28c660a825820847931a23a3559b829ba248b6263daa4e4ef7a3f4cc3e61ccf345b7caf6d483f584011322f641060ff48551c445bb95369cdbdad81b3a45b444ad7f2bc37f6062b08be468cac1423824a0e21b13e5fb83474469d0bcbc822543f75a0d128ca7f2408a100818258200b7411cb29e220accd89ec9cdac418c34bb287ebef04e72bfd3c62be576fbcbc58406f9e961b796c5ce522ad980cffbbe2ae416b32340ac032b1ff7dfb087ba4eb62e65ebc8cc944ab44f6e765052607eafd5fbd7ee149ffa2fe79416a9d49eb360da10082825820835119a4f348a34e45fcdeded5f367deb1818973ed265da3608fdd0e1ee3b2865840865d312ae643495789a0d8e321f13f4f631bb591b14ecca3cbcf5e84ed7688046605160bc71a982d027dd7a962ed35c15dc93fd1fe2387b179a689014bedb60b825820e9e51a49ae87c9068e90f6be142bb5859e5c2ced10115140e8ff9189c28095c85840d71e127f5bf77d935d960f1e429db390da1c31422a33cfb8da5caa38c9b8cf80d1743b0cc9fd016911244ccf16e2239945fdf6905565e4a84368ac65fce49504a10081825820404088bee6f060bc891ce84a9c8bf24ad57a659e8d62c6a7eb6cdfade559a4685840d45689331b70191d197bf1dbd8683ccf1d7c1c6370528f14c010504180f32ef93dc6f9668e134c75bc7e691f9c7cb60942455f9816122c2a26b24607167d3e00a1008182582018b23c2624d8fc6be719abd351bc3a084060a56c279e3da721502ae725aef66f58406b87c12d23bed102949a045549144f8074e0875a6135a305cecaeb216b5ffeb9866df0ba6a36747c8884899b6de1cb9a07a69c34afbf6afcd5bc7cac1900620da10081825820985591203e42ca347945231affddec373beb1f6f6fd6093e4aedea253b6f896b5840a47b27c71bf69e59b1b1139de6b68445fb02cdbb165d5a0598590c002397d7d5b913ff55df0a0b6682caadac2443e0b6ac2baa708d30f2f267c380d39d771c0ba100838258208ca5a11d4764fbaacca7b2df840f649e074aeb83679015b8f776aa01f43ca022584018793afdf95f2aedc0177ecc3cf71ea539e9d38b116666fff8977b6844ac2fdf191dc3a93b267ae6de9e5d78488af81c0c33430092de3cbf4c38f71be9f9f8058258208ca5a11d4764fbaacca7b2df840f649e074aeb83679015b8f776aa01f43ca022584018793afdf95f2aedc0177ecc3cf71ea539e9d38b116666fff8977b6844ac2fdf191dc3a93b267ae6de9e5d78488af81c0c33430092de3cbf4c38f71be9f9f8058258208ca5a11d4764fbaacca7b2df840f649e074aeb83679015b8f776aa01f43ca022584018793afdf95f2aedc0177ecc3cf71ea539e9d38b116666fff8977b6844ac2fdf191dc3a93b267ae6de9e5d78488af81c0c33430092de3cbf4c38f71be9f9f805a100818258203969308d91d75f34750e4f03baeeb70637b0d68fbc9180e81f15972b82826d0858400f963d8f3fa350b5864230fa9f5199b9fb0d04de6127c136ed6e4462c87c1f7fcfbf84065ae8ea4bc9ed0106fbbe06649f13b36862f74db6c407ff559b56ec0ca100818258209fda70d87a57bd46713fc6e799b7f62e94bd9bbaf0b7f7ea4f4fb20e3f5df4a85840f7d66282694662c89587d101a01afa67d39c00a61108d557d5a7b5775fbb3d8867b825e7310d6c2bbfa6798edd3866c520c14df611f99a097d1ef26ca5a7430ba10082825820edbd96e7142ce50ef057bcf572d4549818ec3be81fa08816dc68fc2f12589ee15840f4efeb239ebbc81bc7b1abb45575ef701d9bd26886a2ba18734fe0e2899a672eb2cfa0385e002b159440fa9f863b89674e011ff3eef39c82216269ecd16de9048258200a582bbe971ce655c102b90f38e209b13e237592da975ec052f739402725bf455840709f6f5f6af70c86e95252d66c9298424d483f80e8f2e24a17f755514aee33aa9e7890d13d8684a77674ac0038b142d74406fbcc8123e7a914afe22223f57804a100818258206c0180db688a9e477b10a7792e3c4efd749cddbe0e05068b6cb586dee540569758404957d7a023043bb8de4295ac1d870d8099203786d9fe794f1eacc18aa20e79f1935002b66147254322a1762b5092df9d966b97a884ba9671778e780770552400a100818258206a7a9f121a40f6853736d984b38dc1d8ed91b3f0bbe93dfe0085495bc8ebedf75840b1981a1cfc45b31a49c34a3bd6f3604475c26a00c78aa4a9be0c5f674133926a8e4a146af5c1a26ef27deb53715d683cfbacc39e86f3a50334327bbb8e583400a10081825820ab97c3afba4a4ba3bc8b9172897625ec4967dd854795adb784f80b44735bda29584071266a36829b2fa666625a324a3aef9bb3817f88f483c2d213fec701ce9ed3e8204bc503d259142618f0b49f3a266e47e29c8f5b7f8aafb12474a35e61778803a100818258200e4fbc9759c27588a88a17fc0601b1481ee614f56d36980de24358c559739aa65840c0afca46f9e8ec91b1b3092ae8a5739597f8652b9212c811d9bf407316c1607b62b37f1c4b6baaafe1d1e62394777e44e610421118a49961caa881ef05485e07a1008182582080566178d214a940d3bbff9e54d4acf088759367f7be38b1ee9876a2e5a9cef0584003af95ac56308b5ab70acbf31930c43ee58202284306d29c58ccea8ea4c1c06de19f3db18f61a850e74ed83803a0adfef75d8f9f4dd40b64ef92e029bcbe8e0ea100818258207246eafdb92c343daadcd0dc35e22df74a1a1f844dce044f7f20d653934aa9955840683e871535c765601856504f20a3910a23d41c9c62895cf88b3c4adbd870903831dd0b6bfab45b4a2fd77c22edbd0137cc40947b3c39c41a2ffe3fdb09146a08a100828258209b9add6ab99192f54f4baa23cdfa48b3345c3e3de837be0fd8d5667b75c66f4058404264e453c4bc40d113bd1f9b6224a53c2cb05b94d7632293308953d4e988faa1068b15dfaf6ccf0b771ab56565759fc0198820517fe1f3499322168dc100a70f82582064243ca715ab2f58a51f041ba4aa78b448d2e615718de938f4c7a43805c2ac485840382dc1d868b252662e79d5cdb8d461b7402642dee8105c60513ffd210da85f2030675b0c89b2f9061d709ac6e88ca2449f3c0003d8cb3136afc9d4cde6585009a100818258201c323278af088ca1005e8f5fee545f60b94ba6557cc34141cb012c4164a6f3715840dc9861c66fdafb68de6960e133ad4229d309925e52103c60e886342779a162d0362cfc2ab7d5a939d46cfcf34ca8db92e39c2c05790d46a43167deadad2dbb01a10081825820f36bcb5aad0359d7c14ba250eb1a864a8287abd5e959571a2722b6c0183537cb5840f32e7a9e7c896774b473817389a0b617baebc10fa6979af528a5e6c32f9532e796ba0716b842bb4955b41dbaf9dbbec0f014a62876c199680a65fb861d66ec0da10081825820d7455891b6d522c43f66d765d9cc64ee842e0deae6f61d8f6c7776926f406113584022f50de94c21640663d87d5df66dc0996c5f8ca40899d1190a257d94bc4d457e5e83c0253bc0d093f06db412e74701838ac260a9c8a5c8460f121343f681290fa100828258201aded7e8899dcef082aeb270237697534b86c5c4511e809a11f3b4e079abf4275840bcadf7202437457d4c7c9e2e154b9942252c173b1c86be92ac5edcbb6c63763276cf512bf769f040c59f1e4d9504acf8f4833bcbfaa081a41f1680f48c0e59048258208c2c6189d858a8f620e141dd8e497c2cda8dc1af7c9af8914afc1e87a47f59455840d6c8c89d025b1f096d8e6bb10fea3f84a8c9a5c67e34aa9d51c4966d64cd20f9a4e09e95368511107de3b047a4399eaa07a968ee4d042beb5900df7f7737aa02a100818258209b2a2b96d8d01c1c0a47f06c0ac8afc57e40414a48fe0e966f1846c762e469d75840d2480520448ce1fa269c02f106e8ff9eed4ce29f9f08ed3641deee20e2c420787710cbfd93a71936d2cc34f641c960a04d03961d67859e40bb78bd9d51f6b805a100818258209e672887a100ad91bace21ba7afd22c22513288e2197d0697c77ea96401af5f65840edb53dfd227fcdfb836a2787d4e6ef58b203f3c61deaa44596d75b91d02e9bdf158748a8ec7d662d8285b74a7adee2b7dcaa66d206035c4b817080b8be1acf00a100828258204431744c73d64bd470ba1b76b6ab29b0512426389220054a9efa8c112f58038558409dcad7b4286b55b21161a033795b1c3c2fbafc7b8e7781ff0f8176dc381619ad93047b11b10978eca81d8aaad1a4c32aa1426d55216222778afd425d2eb68a00825820581fb3f225f3d8a72990d30a16ad7892208f178f08833ae6eca1c95f6920c60558401234292da5326abd6afc31998930d2a2570a0736fa7bd696e70d6a8d18ea164ac2f5bdf00f1dacc526fe20445ebcdb328f4db430002087720b34921204ea0a07a100818258202de5c77c6182b31364498532dd182464d2e889c282df124a5f48603f3d882be958404c8e9d0fbb9d53950403e5fca7d8c3937ee705acd180b729f6d387b131e65979bb6974852038b135fc70123c4d313db124991951a5a09f2c7952013faeca6e02a1008182582080b7237a27af4cc4f0d405ada50d852f606f8fbf38bfcc492edd5e765bb1f57658402803ce9ea772ccbe5ec87cb6e44c54c2c19e60e4d666b86d6fdef47fc8d4d185b475fb62ab43e6097093d585cb23ee236adae19cf93c158960f84e01696dfe05a10081825820ba61c67921be3b13ebe880b7e3fe98db7b9fc424934ebcc09b9c72a6cc0229675840541e80169162b9dcc9676411ad2e87a1f317d3fc67188c1aa5c8b00f9968edd268743efa3c5c4e753c0545c6acbb63dadcd338882b8ed646a5bb03f670d4280ca10081825820fd002f36adce2b7a0c4ad11dc92c19c6923825794458b777a5c49858f0c13626584057a8e18c34c6b91faa6e4eeaa0eb169c97964fe4784f729e7e4a4547071a910f4fd13b612ae7423aaa5ab185b6c954b720083e5d19817371330f539a19a17c04a100828258200b9e96518357c523796df8e3cf8ffc5f9ef39569fc9b0783b6bb56e1a00de30158404e4091e832cf46fb4eaf043a6f858d22e33372fb9543ef00200610790be5bc86f0cfd57f15b7e154456f4cfdfae00d11c5cc445a040fd2747e6dbd5cafbbfe00825820b6229594dc7085504a4982261a9a238a74083fc3eefd5e4f28837013d79c357f584010cd613e5096a7c27c75c429ba3d727b85090cf108a5dc282ec89756e30446ad9f93056d7c8f8c22034b529fa6c01865ed42e572d640d69d5e03db1eabbf1d04a10081825820513ca4994cd01c1c9fdf75dab5152a336c5e8bf26158ea2ef2eff1d9a859df80584036278921d624688f563c7ae8081475ac4aaeedfdb39955f23a6ea7182beebb82dec7faf24c1bc492c65e2d871449f9d338bdd9b6a5eae8e6bcc7b1516b292408a102818458208b66d7f3bf0629a3cf791984bf0fa4568b83106c9257818e63edf63142e9bf2a5840eef2da06ff14a26c7686790b2f31166a5a1d9e5faf74bd19e6b4ef656a8deaf83641de849b50a3ae5b37da04cd4e0264bc8f6d2fef6728ede2a0452352641005582022cc1a6495cb1639e562a07c9aa86a4cf54e3dbb6839aa4ee6f0b6a20dc349ad5822a101581e581c1afbc57540db1561adeaccd7f4894065965cd230f6bdc70722aecd8ba100828258206d625247aaa9a60a3e1e4ebcaa335fc54952f6e4972a6218df2626d4adac27375840eb5f558a0537361fb89a755a26ecf08a0b8f04fc20ba54cdf7364c4a2cbac72873b7455e95f748f976bdf1079b298b42bdc80c2de04581036d5ff9fc4b0c520a8258209fe9403fa3c480334ea32007354f1c02d13ffbf0d8d7a5b397a36cafa362a119584035e3cc1b0020fa86a1f1b054bfa738433e1b8e18fc9bf1ada1d7fc7a015d3e663ca12dc8430f6178aafddd93b98db853bc023eba11987630f2d0efe124d6400ca100818258206a76419697979e39aea348c6cf4ea31cc8deba03ee1b919b80a9085a3e5734275840891615581b8bbdf0af21e0be375d8d68ef70ed6aeaee47f7388800b8f6eda89ca7f4cc0959cbce833703e502d3c608d26226a1f6a9ca1a4f3de8a8fa5ca5dc04a10081825820008126e4250f3f9d1ebae937e56d69fb179d45cbb204c6972b2111c16b761bae5840ac9a848cb5c7f4487ef2a55903f1083f07a61c6d67e480dbd526ef74fcd8c32ea11c5a9a6ec125061e5679bab0c40c818fdc61218b35661ee61af8dcebc8950ca100818258202c29deb0ba6e451930ebd479f4c6465e425a59ad43e60973b32a5f3f34df1f995840db3a989a75c9adcad42dd0572e1d646b816016c00171046df5a934db4922457865e87edffba007f0bbeec9b449ab7d341a3f7f694d3fb00967ed0bd84f7dbc06a100818258201344e336b3e0539b4e44f6a559c0133d07db73524121ffd0c9909bdf08d223b85840ee4e369d89ee08a0e3f9418cc4f95b9a1f62c57a8e81ce64f4751a5b2d3711264e859f54511a043f0511c2b62b1cdc82013aedc3e0771222d3e030ff9adffe02a100818258202b414068759c21d88b9e176bd64fa634f1b48bbaf832df37ac2da80165e54ff658407d8df8e512d095a0399b6720404f61bd9e27379ced49c8c799ffe0eeaa47cf0e5db0eca3cc77645a81e0a44eb8cadfcbc68ecc36e3f37ecf2275c1459936890ca10081825820485b90e79c2061f24a5a66a6e2416e67a859b2823d53e68a9e156b09c7cd28e95840c58e38a8c0f9d084bab42f90d0ff8c5b28d9f67ff8c449762ab6acbbed0ff7dcce3566aaf047d05ddd6604e2739988bed6f47bbae2befb62f3daff492165fc05a10281845820a10ae5fbabbe161683c6e8b6728313a0570c9e0bbe5ea053e0815dfb9c3c76b0584075dd79216dfd43015ba424e97c7ec7305dad30fac4f649b031ecbe917cfa9c2bf570ca815db1a0948466ce49bbef95c9c75d7049eb2694fb300a32dcf35e3e085820456035e23bbca71d241f9467bd3f71837339a8aaec027554236704d3122d38cd5822a101581e581c2b0b011ba3683d138fb1022a2a024ccfd5686ca0b8c83024473c6d4fa10081825820d3a0c306edcb90e554b9a2f742eb77f6919912dfa1e9c1c046782326dbae90fd584000e2d206ade28af72bf7290d698ba26934d15cec48874aaaff676d979a21f14400f874ed0f3f3a04bdfdfbdb42f6d782df15d39430b19f717e7abf77c237610aa1008182582003e6f7cf83078454be2cbecce3494bbb1cd79bd82b228f46272eae4feb75ea485840b3653f713f0e6311e2f3555b23f18e1b32030915d1c187ebc662f21d7dc7ce5ad5e6ec76008d701e508cb9ab0a710e739548edc4811a271a2e418ce018d2f00ea100818258203ff3d877bfc60061bc8c7032eb4edb95cdd16dee9c593d2bff2852ad6e94ceb0584018ba65f698126d6c12ed3ac684d4342eb95a52cce1ca0c43f1761ee7834900c168b6e5eba0af3d30c2618680832027ed8913a3fea796a45ba21c1fd496634109a10081825820336bd8df3c779ff740cb844449768addf756894d92d1aeb587ec2ec64638b6d85840c6b9f45257dca4472c758d78dbaba98718ceb040f1be4f12c60368ae8cc5636852ffec4e6581835f1b04f0b7e607a2f9294f69c9547e16392c617576ef14630fa100818258208bf4c9c54b4473f8e9c097dd0a5af648231e1e6b1f63dc1af0508c5dab00618c5840919784feee70739f3a6deb219af22718563974ac8ee829da627b370ef7f91b45d979400a1fc600b7806ecbb08213862335c45a6b901ec05ad26b5d6041ace30ba10081825820c0fedac2cac437b129efdf831da4cfef9e975ca543926579e2f3578cb82779ad5840274c79b64c5200aad7c10c9cb255bde2d46afe90751702f1a08956d78c6c09b21db01a73db15c6c19134e0e6a630c04692049385cb182cc9afab94a869eeac0aa1008182582016e0d2db1398621a2ec8be4a427aa143337a8a0d31a0151016a863c1b6093590584092f89a5b31f9d7cc744401bb54d29372c3c028f5809da79ace5b156a718e835144b114a1fdf6e50d8ba63b9d33ed15fe2a83143638bcd9744e9414d7f0b1dc0aa10081825820c841d34c3227dfeb3111401abc0b4b20f4f52e05273f36ad4ac43d15deaccc345840adf2c6aaa22f71b36eafef681e24254bf1962f2d9f3da67f2e275f29d9cc0cdb6aad5ba4f6e552b31464c6f18b78e4e24fc93482734cc91d9e19031660f1f10aa10081825820ae1d44bb1bb37793034bb8f73840136075f040a772eeb8a9783510b1e3e041a458407f6094fe454ec5d13ffeb2ef2de843d439a422581a7ba3458fda038f796600b37baf8f4e9f04e9f4d85eff81b13cf23899a2f76abe8a6dbb1910b7f4132bde06a10081825820781a9c707144e47a863468cd8b6d1cecfd6847ad847143224649e01b95e128d75840832fe51f225cc551a50623ea8e79fbf82951c82ac4fcfc67d67196460cfcb866110248461496c99ff0c17b73ef83f0e0faf2cc210be4629299eb8867a6eec80da10081825820251592e5157bd0eedc5fa2b35647b3b8f0e2bec88659d33bae43a93d4b25734858404f218fc7ef53fc85dc36e257620b5b3d5bce76a82ae3acbfaa1f1ce9d3d6c301248f9104df9345df7cbaaf7fc46bfade012cd8d9d6fceb94d4d7eb0f4d4b9d0aa10081825820476324238a62e577b9e4da41dbe52dde4e879aad88878697310a8de6960b198f5840411d18fd207b427d9a51b3ac438579d506facb7e6f366c5c5b10784d23d7ae956d5bf967fc301c5f19ad827f382d1184544090c44b03b265a7b3d9dc37550902a1008182582059d8fc0372045e05f64cadbd0fce9191359818ab325c799ff855ffd54866b3a35840ab8622ddc17fdff2876ba38df213b7711fd47edccdc745c04a807fef30b83964c0299b9317987633cefde20853c2c0f16f0a1ca3adc4d18346209224d229d607a10082825820bee45481b23e2b12f2a7dce9475a725c7da28f86b81a5c93fd0d307238607e19584095aef4113bcbd534fcb34fa76316390f5a5aea5cb2f358188057a4e562e3109f39705052e8d090c0d4dc49ecc0d3072770945460eb1b716e4ab409640f9f980d8258201ee1fc8736714c37b7307c640e07307b1efc3c20a992781bde4d83b98f6f007a5840417ad7cbe8e3ee5eb1ae6740b9bff59bd15e6a8716aab603fb9f22ec51b31895ade80b8a588ee2cc2142330333f85074add5a43640b2a7d765ecd409fb5bd50cffa080"; - List> tuples = TransactionBodyExtractor.getTxBodiesFromBlock(HexUtil.decodeHexString(blockCbor)); + List> tuples = TransactionBodyExtractor.getTxBodiesFromBlock(HexUtil.decodeHexString(blockCbor)); List txHashes = tuples.stream().map(t -> TxUtil.calculateTxHash(t._2)).collect(Collectors.toList()); assertThat(tuples.size()).isEqualTo(65); diff --git a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java index 89e3882a..73862332 100644 --- a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java +++ b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java @@ -2,7 +2,6 @@ import com.bloxbean.cardano.client.util.JsonUtil; import com.bloxbean.cardano.yaci.core.common.Constants; -import com.bloxbean.cardano.yaci.core.config.YaciConfig; import com.bloxbean.cardano.yaci.core.model.Amount; import com.bloxbean.cardano.yaci.core.model.Block; import com.bloxbean.cardano.yaci.core.model.PlutusScript; @@ -31,8 +30,6 @@ class BlockFetcherIT extends BaseTest { @Test public void fetchBlock() throws InterruptedException { - // will be removed before merge - YaciConfig.INSTANCE.setReturnTxSize(true); VersionTable versionTable = N2NVersionTableConstant.v4AndAbove(protocolMagic); BlockFetcher blockFetcher = new BlockFetcher(node, nodePort, versionTable); @@ -49,14 +46,14 @@ public void fetchBlock() throws InterruptedException { // Point from = new Point(0, "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f"); // Point to = new Point(5, "365201e928da50760fce4bdad09a7338ba43a43aff1c0e8d3ec458388c932ec8"); - Point from = new Point(86760, "f9fea05ea91d5b413fd138ddc68cc5586f23dcae6019ea7aadc60889000fd240"); - Point to = new Point(2088544, "272193884b33dac2642c0f9d8a37303989a68f868d11227d1204380f14e264a2"); + Point from = new Point(13006114, "86dabb90d316b104af0bb926a999fecd17c59be3fa377302790ad70495c4b509"); + Point to = new Point(13006114, "86dabb90d316b104af0bb926a999fecd17c59be3fa377302790ad70495c4b509"); blockFetcher.fetch(from, to); countDownLatch.await(10, TimeUnit.SECONDS); blockFetcher.shutdown(); -// assertThat(blocks.get(0).getHeader().getHeaderBody().getBlockNumber()).isEqualTo(287622); + assertThat(blocks.get(0).getHeader().getHeaderBody().getBlockNumber()).isEqualTo(287622); }