diff --git a/lib/block/PartialMerkleTree.js b/lib/block/PartialMerkleTree.js index 87498d7b7..3414b288c 100644 --- a/lib/block/PartialMerkleTree.js +++ b/lib/block/PartialMerkleTree.js @@ -6,6 +6,10 @@ const isHexString = require('../util/js').isHexa; const constants = require('../constants'); const MerkleTreeUtils = require('../util/merkletree'); const BitArrayUtils = require('../util/bitarray'); +const readVarintCount = require('../util/readVarintCount'); +const isDenseArray = require('../util/isDenseArray'); +const $ = require('../util/preconditions'); +const { readExactReverse } = require('../transaction/payload/strictpayloadreader'); const { convertBitArrayToUInt8Array } = BitArrayUtils; @@ -63,15 +67,19 @@ PartialMerkleTree.fromBufferReader = function fromBufferReader(bufferReader) { const partialMerkleTree = new PartialMerkleTree(); partialMerkleTree.totalTransactions = bufferReader.readUInt32LE(); - const merkleHashesCount = bufferReader.readVarintNum(); + const merkleHashesCount = readVarintCount( + bufferReader, + constants.SHA256_HASH_SIZE, + 'Merkle hash' + ); partialMerkleTree.merkleHashes = []; for (let i = 0; i < merkleHashesCount; i += 1) { partialMerkleTree.merkleHashes.push( - bufferReader.read(constants.SHA256_HASH_SIZE).reverse().toString('hex') + readExactReverse(bufferReader, constants.SHA256_HASH_SIZE).toString('hex') ); } - const merkleFlagsCount = bufferReader.readVarintNum(); + const merkleFlagsCount = readVarintCount(bufferReader, 1, 'Merkle flag byte'); partialMerkleTree.merkleFlags = []; for (let i = 0; i < merkleFlagsCount; i += 1) { partialMerkleTree.merkleFlags.push(bufferReader.readUInt8()); @@ -106,6 +114,15 @@ PartialMerkleTree.prototype.toBuffer = function toBuffer() { bufferWriter.writeUInt32LE(this.totalTransactions); + // forEach skips empty slots, which would leave the counts wrong + $.checkArgument( + isDenseArray(this.merkleHashes), + 'Expected merkleHashes to be an array without empty slots' + ); + $.checkArgument( + isDenseArray(this.merkleFlags), + 'Expected merkleFlags to be an array without empty slots' + ); bufferWriter.writeVarintNum(this.merkleHashes.length); this.merkleHashes.forEach((hash) => { bufferWriter.write(Buffer.from(hash, 'hex').reverse()); diff --git a/lib/constants/index.js b/lib/constants/index.js index 47397e0f6..54116001f 100644 --- a/lib/constants/index.js +++ b/lib/constants/index.js @@ -65,7 +65,7 @@ const constants = { // 60 members, 45 (75%) threshold, one every 12 hours LLMQ_TYPE_60_75: 5, - // 25 members, 67 (67%) threshold, one per hour + // 25 members, 17 (67%) threshold, one per hour LLMQ_TYPE_25_67: 6, // 3 members, 2 (66%) threshold, one per hour (24 blocks) @@ -80,7 +80,7 @@ const constants = { // Params might differ when -llmqtestparams is used LLMQ_TYPE_TEST_V17: 102, - // 4 members, 2 (66%) threshold, one per hour. + // 4 members, 3 (75%) threshold, one per hour. // Params might differ when -llmqtestparams is used LLMQ_TYPE_TEST_DIP0024: 103, @@ -141,7 +141,7 @@ constants.LLMQ_TYPE_PARAMS= { }, [constants.LLMQ_TYPES.LLMQ_TYPE_25_67]: { size: 25, - threshold: 67, + threshold: 17, maximumActiveQuorumsCount: 24, }, [constants.LLMQ_TYPES.LLMQ_TYPE_LLMQ_TEST]: { @@ -161,7 +161,7 @@ constants.LLMQ_TYPE_PARAMS= { }, [constants.LLMQ_TYPES.LLMQ_TYPE_TEST_DIP0024]: { size: 4, - threshold: 2, + threshold: 3, maximumActiveQuorumsCount: 2, }, [constants.LLMQ_TYPES.LLMQ_TYPE_TEST_INSTANTSEND]: { diff --git a/lib/deterministicmnlist/QuorumEntry.js b/lib/deterministicmnlist/QuorumEntry.js index 90fed29a1..50e95a927 100644 --- a/lib/deterministicmnlist/QuorumEntry.js +++ b/lib/deterministicmnlist/QuorumEntry.js @@ -9,6 +9,11 @@ const Hash = require('../crypto/hash'); const bls = require('../crypto/bls'); const utils = require('../util/js'); const isHashQuorumIndexRequired = require('../util/isHashQuorumIndexRequired'); +const { + readExact, + readExactReverse, + readCompactSize, +} = require('../transaction/payload/strictpayloadreader'); const { isHexStringOfSize, @@ -83,6 +88,36 @@ function QuorumEntry(arg, llmqParamsOverride = {}) { } } +/** + * Reads the fields that precede a commitment's bit sets + * @param {BufferReader} bufferReader + * @return {{version: number, llmqType: number, quorumHash: string, quorumIndex: number}} + */ +function readCommitmentHead(bufferReader) { + const version = bufferReader.readUInt16LE(); + return { + version, + llmqType: bufferReader.readUInt8(), + quorumHash: readExactReverse(bufferReader, SHA256_HASH_SIZE).toString('hex'), + quorumIndex: isHashQuorumIndexRequired(version) ? bufferReader.readInt16LE() : 0, + }; +} + +/** + * Reads the fields that follow a commitment's bit sets + * @param {BufferReader} bufferReader + * @return {{quorumPublicKey: string, quorumVvecHash: string, quorumSig: string, + * membersSig: string}} + */ +function readCommitmentTail(bufferReader) { + return { + quorumPublicKey: readExact(bufferReader, BLS_PUBLIC_KEY_SIZE).toString('hex'), + quorumVvecHash: readExactReverse(bufferReader, SHA256_HASH_SIZE).toString('hex'), + quorumSig: readExact(bufferReader, BLS_SIGNATURE_SIZE).toString('hex'), + membersSig: readExact(bufferReader, BLS_SIGNATURE_SIZE).toString('hex'), + }; +} + /** * Parse buffer and returns QuorumEntry * @param {Buffer} buffer @@ -99,8 +134,7 @@ QuorumEntry.fromBuffer = function fromBuffer(buffer, llmqParamsOverride = {}) { SMLQuorumEntry.version = bufferReader.readUInt16LE(); SMLQuorumEntry.llmqType = bufferReader.readUInt8(); SMLQuorumEntry.quorumHash = bufferReader - .read(constants.SHA256_HASH_SIZE) - .reverse() + .readReverse(constants.SHA256_HASH_SIZE) .toString('hex'); if (isHashQuorumIndexRequired(this.version)) { @@ -118,18 +152,7 @@ QuorumEntry.fromBuffer = function fromBuffer(buffer, llmqParamsOverride = {}) { return SMLQuorumEntry; } SMLQuorumEntry.isOutdatedRPC = false; - SMLQuorumEntry.version = bufferReader.readUInt16LE(); - SMLQuorumEntry.llmqType = bufferReader.readUInt8(); - SMLQuorumEntry.quorumHash = bufferReader - .read(constants.SHA256_HASH_SIZE) - .reverse() - .toString('hex'); - - if (isHashQuorumIndexRequired(SMLQuorumEntry.version)) { - SMLQuorumEntry.quorumIndex = bufferReader.readInt16LE(); - } else { - SMLQuorumEntry.quorumIndex = 0; - } + Object.assign(SMLQuorumEntry, readCommitmentHead(bufferReader)); SMLQuorumEntry.signersCount = bufferReader.readVarintNum(); const signersBytesToRead = @@ -143,23 +166,97 @@ QuorumEntry.fromBuffer = function fromBuffer(buffer, llmqParamsOverride = {}) { SMLQuorumEntry.validMembers = bufferReader .read(validMembersBytesToRead) .toString('hex'); - SMLQuorumEntry.quorumPublicKey = bufferReader - .read(BLS_PUBLIC_KEY_SIZE) - .toString('hex'); - SMLQuorumEntry.quorumVvecHash = bufferReader - .read(SHA256_HASH_SIZE) - .reverse() - .toString('hex'); - SMLQuorumEntry.quorumSig = bufferReader - .read(BLS_SIGNATURE_SIZE) - .toString('hex'); - SMLQuorumEntry.membersSig = bufferReader - .read(BLS_SIGNATURE_SIZE) - .toString('hex'); + Object.assign(SMLQuorumEntry, readCommitmentTail(bufferReader)); return SMLQuorumEntry; }; +/** + * Whether the unused high bits of a bit set's last byte are set, which Core's + * ReadFixedBitSet rejects + * @param {Buffer} bytes + * @param {number} size - number of bits + * @return {boolean} + */ +function hasOutOfRangeBits(bytes, size) { + const unused = bytes.length * 8 - size; + // eslint-disable-next-line no-bitwise + return unused > 0 && (bytes[bytes.length - 1] & ~(0xff >>> unused) & 0xff) !== 0; +} + +/** + * Checks a bit set before it is written after the LLMQ size: a wrong length + * would shift every field that follows, and Core rejects bits past the size + * @param {string} bits - packed bits hex + * @param {number} size - LLMQ size in bits + * @param {string} name + */ +function checkBitSet(bits, size, name) { + $.checkArgument( + bits.length === Math.ceil(size / 8) * 2, + `Expected ${name} to have the LLMQ bit set length` + ); + $.checkArgument( + !hasOutOfRangeBits(Buffer.from(bits, 'hex'), size), + 'Expected no bits set past the LLMQ size' + ); +} + +/** + * Reads Core's dynamic bit set: its bit count, then the bits packed into bytes + * @param {BufferReader} bufferReader + * @param {Object} [params] - LLMQ type params, if known; Core requires the + * bit count to equal their size + * @return {{count: number, bits: string}} number of set bits, packed bits hex + */ +function readDynamicBitSet(bufferReader, params) { + // at most MAX_SIZE bits, and readExact bounds the bytes by the remaining data + const size = readCompactSize(bufferReader); + if (params && size !== params.size) { + throw new Error(`Expected a bit set of ${params.size} bits, got ${size}`); + } + const bytes = readExact(bufferReader, Math.ceil(size / 8)); + if (hasOutOfRangeBits(bytes, size)) { + throw new Error('Out-of-range bits set'); + } + let count = 0; + bytes.forEach((byte) => { + // clear the lowest set bit until none is left: at most 8 steps per byte + for (let bits = byte; bits !== 0; count += 1) { + // eslint-disable-next-line no-bitwise + bits &= bits - 1; + } + }); + return { count, bits: bytes.toString('hex') }; +} + +/** + * Reads a final commitment as Core sends it in the P2P `mnlistdiff` message, + * the layout toBufferForHashing() writes. Like Core's RPC, signersCount and + * validMembersCount are the numbers of set bits. + * @param {BufferReader} bufferReader + * @param {Object} [llmqParamsOverride] + * @return {QuorumEntry} + */ +QuorumEntry.fromP2PBufferReader = function fromP2PBufferReader( + bufferReader, + llmqParamsOverride = {} +) { + const obj = readCommitmentHead(bufferReader); + + // Unknown types are read without the size check, but can't be serialized + const params = _.merge({}, constants.LLMQ_TYPE_PARAMS, llmqParamsOverride)[obj.llmqType]; + const signers = readDynamicBitSet(bufferReader, params); + obj.signersCount = signers.count; + obj.signers = signers.bits; + const validMembers = readDynamicBitSet(bufferReader, params); + obj.validMembersCount = validMembers.count; + obj.validMembers = validMembers.bits; + Object.assign(obj, readCommitmentTail(bufferReader)); + + return QuorumEntry.fromObject(obj, llmqParamsOverride); +}; + /** * @param {string} string * @param {Object} llmqParamsOverride @@ -213,6 +310,8 @@ QuorumEntry.prototype.toBufferForHashing = function toBufferForHashing() { this.validate(); const bufferWriter = new BufferWriter(); const fixedCounterLength = this.getParams().size; + checkBitSet(this.signers, fixedCounterLength, 'signers'); + checkBitSet(this.validMembers, fixedCounterLength, 'validMembers'); bufferWriter.writeUInt16LE(this.version); bufferWriter.writeUInt8(this.llmqType); bufferWriter.write(Buffer.from(this.quorumHash, 'hex').reverse()); @@ -368,10 +467,12 @@ QuorumEntry.prototype.getParams = function getParams() { * @return {Uint8Array} */ QuorumEntry.prototype.getCommitmentHash = function getCommitmentHash() { + const { size } = this.getParams(); + checkBitSet(this.validMembers, size, 'validMembers'); const bufferWriter = new BufferWriter(); bufferWriter.writeUInt8(this.llmqType); bufferWriter.write(Buffer.from(this.quorumHash, 'hex').reverse()); - bufferWriter.writeVarintNum(this.getParams().size); + bufferWriter.writeVarintNum(size); bufferWriter.write(Buffer.from(this.validMembers, 'hex')); bufferWriter.write(Buffer.from(this.quorumPublicKey, 'hex')); bufferWriter.write(Buffer.from(this.quorumVvecHash, 'hex').reverse()); @@ -543,7 +644,8 @@ QuorumEntry.prototype.calculateHash = function calculateHash() { * @return {QuorumEntry} */ QuorumEntry.prototype.copy = function copy() { - return QuorumEntry.fromBuffer(this.toBuffer()); + // fromBuffer needs the params of custom quorum types for the bit set sizes + return QuorumEntry.fromBuffer(this.toBuffer(), this.llmqParamsOverride); }; module.exports = QuorumEntry; diff --git a/lib/deterministicmnlist/SimplifiedMNListDiff.js b/lib/deterministicmnlist/SimplifiedMNListDiff.js index 7f8e46f9c..ad6ab95d3 100644 --- a/lib/deterministicmnlist/SimplifiedMNListDiff.js +++ b/lib/deterministicmnlist/SimplifiedMNListDiff.js @@ -2,7 +2,8 @@ const _ = require('lodash'); const BufferReader = require('../encoding/bufferreader'); const BufferWriter = require('../encoding/bufferwriter'); const BufferUtil = require('../util/buffer'); -const isHexString = require('../util/js').isHexa; +const $ = require('../util/preconditions'); +const { isHexa: isHexString, isHexStringOfSize } = require('../util/js'); const SimplifiedMNListEntry = require('./SimplifiedMNListEntry'); const QuorumEntry = require('./QuorumEntry'); @@ -10,6 +11,35 @@ const PartialMerkleTree = require('../block/PartialMerkleTree'); const Transaction = require('../transaction'); const constants = require('../constants'); const Networks = require('../networks'); +const readVarintCount = require('../util/readVarintCount'); +const isDenseArray = require('../util/isDenseArray'); +const Hash = require('../crypto/hash'); +const { readExact, readExactReverse } = require('../transaction/payload/strictpayloadreader'); + +// nVersion, proRegTxHash, confirmedHash, an empty ExtNetInfo (version and purpose +// count), pubKeyOperator, keyIDVoting and isValid +const MIN_SML_ENTRY_SIZE = 2 + 32 + 32 + 2 + 48 + 20 + 1; +// version, llmqType, quorumHash, two empty bit sets, quorumPublicKey, +// quorumVvecHash, quorumSig and membersSig +const MIN_QUORUM_ENTRY_SIZE = 2 + 1 + 32 + 1 + 1 + 48 + 32 + 96 + 96; + +/** + * Core keeps quorumsCLSigs in a std::map keyed by CBLSSignature, whose + * operator< compares GetHash(): sha256d of the 96 signature bytes, bytewise + * @param {Buffer} signature + * @return {Buffer} + */ +function clSigOrderKey(signature) { + return Hash.sha256sha256(signature); +} + +/** + * @param {number[]} values + * @return {boolean} + */ +function isStrictlyIncreasing(values) { + return values.every((value, i) => i === 0 || values[i - 1] < value); +} /** * @param {Buffer|Object|string} [arg] - A Buffer, JSON string, or Object representing a MnListDiff @@ -27,6 +57,8 @@ const Networks = require('../networks'); * @property {Array} newQuorums * @property {string} merkleRootMNList - merkle root of the whole mn list * @property {string} merkleRootQuorums - merkle root of the quorum list + * @property {Array>} [quorumsCLSigs] - chainlock signatures + * used for quorum rotation, each mapped to indexes into newQuorums */ function SimplifiedMNListDiff(arg, network, llmqParamsOverride = {}) { if (arg) { @@ -51,7 +83,8 @@ function SimplifiedMNListDiff(arg, network, llmqParamsOverride = {}) { } /** - * Creates MnListDiff from a Buffer. + * Creates MnListDiff from a Buffer holding Core's P2P `mnlistdiff` message + * (protocol 70230 and later, unchanged through 70242). * @param {Buffer} buffer * @param {string} [network] * @param {Object} [llmqParamsOverride] @@ -61,75 +94,100 @@ SimplifiedMNListDiff.fromBuffer = function fromBuffer(buffer, network, llmqParam const bufferReader = new BufferReader(Buffer.from(buffer)); const data = {}; - data.baseBlockHash = bufferReader - .read(constants.SHA256_HASH_SIZE) - .reverse() - .toString('hex'); - data.blockHash = bufferReader - .read(constants.SHA256_HASH_SIZE) - .reverse() - .toString('hex'); + data.nVersion = bufferReader.readUInt16LE(); + data.baseBlockHash = readExactReverse(bufferReader, constants.SHA256_HASH_SIZE).toString('hex'); + data.blockHash = readExactReverse(bufferReader, constants.SHA256_HASH_SIZE).toString('hex'); data.cbTxMerkleTree = PartialMerkleTree.fromBufferReader(bufferReader); + const cbTxStart = bufferReader.pos; data.cbTx = new Transaction().fromBufferReader(bufferReader); + // The transaction parser accepts non-canonical CompactSizes, which Core + // rejects; a canonical coinbase serializes back to exactly what was read + if (!data.cbTx.toBuffer().equals(bufferReader.buf.slice(cbTxStart, bufferReader.pos))) { + throw new Error('Non-canonical coinbase transaction encoding'); + } - data.nVersion = bufferReader.readUInt16LE(); - - const deletedMNsCount = bufferReader.readVarintNum(); + const deletedMNsCount = readVarintCount( + bufferReader, + constants.SHA256_HASH_SIZE, + 'Deleted masternode' + ); data.deletedMNs = []; for (let i = 0; i < deletedMNsCount; i += 1) { data.deletedMNs.push( - bufferReader.read(constants.SHA256_HASH_SIZE).reverse().toString('hex') + readExactReverse(bufferReader, constants.SHA256_HASH_SIZE).toString('hex') ); } - const mnListSize = bufferReader.readVarintNum(); + const mnListSize = readVarintCount(bufferReader, MIN_SML_ENTRY_SIZE, 'Masternode list entry'); data.mnList = []; for (let i = 0; i < mnListSize; i += 1) { - const entryBufferParts = [ - bufferReader.read(constants.SML_ENTRY_VERSION_1_SIZE) - ]; - - if (data.nVersion >= 2) { - const type = bufferReader.readUInt16LE(); - - const typeBuffer = Buffer.alloc(2); - typeBuffer.writeUInt16LE(type); - - entryBufferParts.push(typeBuffer); - - if (type === constants.MASTERNODE_TYPE_HP) { - entryBufferParts.push(bufferReader.read(constants.SML_ENTRY_TYPE_2_ADDITION_SIZE)); - } - } - data.mnList.push( - SimplifiedMNListEntry.fromBuffer( - Buffer.concat(entryBufferParts), - network - ) + SimplifiedMNListEntry.fromP2PBufferReader(bufferReader, network) ); } - const deletedQuorumsCount = bufferReader.readVarintNum(); + const deletedQuorumsCount = readVarintCount( + bufferReader, + 1 + constants.SHA256_HASH_SIZE, + 'Deleted quorum' + ); data.deletedQuorums = []; for (let i = 0; i < deletedQuorumsCount; i += 1) { const deletedQuorum = {}; deletedQuorum.llmqType = bufferReader.readUInt8(); - deletedQuorum.quorumHash = bufferReader - .read(constants.SHA256_HASH_SIZE) - .reverse() + deletedQuorum.quorumHash = readExactReverse(bufferReader, constants.SHA256_HASH_SIZE) .toString('hex'); data.deletedQuorums.push(deletedQuorum); } - const newQuorumsSize = bufferReader.readVarintNum(); + const newQuorumsSize = readVarintCount(bufferReader, MIN_QUORUM_ENTRY_SIZE, 'New quorum'); data.newQuorums = []; for (let i = 0; i < newQuorumsSize; i += 1) { - const quorumEntrySize = bufferReader.readVarintNum(); - const entry = bufferReader.read(quorumEntrySize); + data.newQuorums.push( + QuorumEntry.fromP2PBufferReader(bufferReader, llmqParamsOverride) + ); + } - data.newQuorums.push(QuorumEntry.fromBuffer(entry, llmqParamsOverride)); + // Core's RPC form: [{ : [indexes into newQuorums] }, ...] + // a signature and its (possibly empty) index count + const quorumsCLSigsCount = readVarintCount( + bufferReader, + constants.BLS_SIGNATURE_SIZE + 1, + 'Quorum chainlock signature' + ); + data.quorumsCLSigs = []; + let previousOrderKey = null; + for (let i = 0; i < quorumsCLSigsCount; i += 1) { + const signatureBytes = readExact(bufferReader, constants.BLS_SIGNATURE_SIZE); + const signature = signatureBytes.toString('hex'); + // Core holds these in a map of sets, so a repeated signature, signatures out + // of map order or an index out of ascending order can't come from Core and + // wouldn't round-trip + const orderKey = clSigOrderKey(signatureBytes); + if (previousOrderKey) { + const order = Buffer.compare(previousOrderKey, orderKey); + if (order === 0) { + throw new Error('Duplicate quorum chainlock signature'); + } + if (order > 0) { + throw new Error('Quorum chainlock signatures are not in Core\'s map order'); + } + } + previousOrderKey = orderKey; + const indexesCount = readVarintCount(bufferReader, 2, 'Quorum index'); + const indexes = []; + for (let j = 0; j < indexesCount; j += 1) { + indexes.push(bufferReader.readUInt16LE()); + } + if (!isStrictlyIncreasing(indexes)) { + throw new Error('Quorum chainlock indexes are not a sorted set'); + } + data.quorumsCLSigs.push({ [signature]: indexes }); + } + + if (!bufferReader.finished()) { + throw new Error('MnListDiff is followed by unexpected data'); } data.merkleRootMNList = data.cbTx.extraPayload.merkleRootMNList; @@ -158,19 +216,28 @@ SimplifiedMNListDiff.fromHexString = function fromHexString( }; /** - * Serializes mnlist diff to a Buffer + * Serializes mnlist diff as Core's P2P `mnlistdiff` message (protocol 70230 + * and later). Quorums need their LLMQ type params, for the bit set sizes. * @return {Buffer} */ SimplifiedMNListDiff.prototype.toBuffer = function toBuffer() { const bufferWriter = new BufferWriter(); + // Diffs from Core RPC that predate nVersion are version 1 + bufferWriter.writeUInt16LE(typeof this.nVersion === 'number' ? this.nVersion : 1); bufferWriter.write(Buffer.from(this.baseBlockHash, 'hex').reverse()); bufferWriter.write(Buffer.from(this.blockHash, 'hex').reverse()); bufferWriter.write(this.cbTxMerkleTree.toBuffer()); bufferWriter.write(this.cbTx.toBuffer()); - bufferWriter.writeInt16LE(this.nVersion); + ['deletedMNs', 'mnList', 'deletedQuorums', 'newQuorums'].forEach((name) => { + $.checkArgument(isDenseArray(this[name]), `Expected ${name} to be an array without empty slots`); + }); + $.checkArgument( + this.quorumsCLSigs === undefined || isDenseArray(this.quorumsCLSigs), + 'Expected quorumsCLSigs to be an array without empty slots' + ); bufferWriter.writeVarintNum(this.deletedMNs.length); this.deletedMNs.forEach((deleteMNHash) => { @@ -179,7 +246,7 @@ SimplifiedMNListDiff.prototype.toBuffer = function toBuffer() { bufferWriter.writeVarintNum(this.mnList.length); this.mnList.forEach((simplifiedMNListEntry) => { - bufferWriter.write(simplifiedMNListEntry.toBuffer()); + bufferWriter.write(simplifiedMNListEntry.toP2PBuffer()); }); bufferWriter.writeVarintNum(this.deletedQuorums.length); @@ -190,8 +257,48 @@ SimplifiedMNListDiff.prototype.toBuffer = function toBuffer() { bufferWriter.writeVarintNum(this.newQuorums.length); this.newQuorums.forEach((quorumEntry) => { - bufferWriter.writeVarintNum(quorumEntry.toBuffer().length); - bufferWriter.write(quorumEntry.toBuffer()); + // the hashed serialization is the one Core sends + bufferWriter.write(quorumEntry.toBufferForHashing()); + }); + + const quorumsCLSigs = (this.quorumsCLSigs || []).map((quorumsCLSig) => { + const signatures = _.isPlainObject(quorumsCLSig) ? Object.keys(quorumsCLSig) : []; + $.checkArgument( + signatures.length === 1, + 'Expected each quorumsCLSigs entry to hold exactly one signature' + ); + const [signature] = signatures; + const indexes = quorumsCLSig[signature]; + $.checkArgument( + isHexStringOfSize(signature, constants.BLS_SIGNATURE_SIZE * 2), + 'Expected quorumsCLSigs keys to be bls signatures' + ); + $.checkArgument( + isDenseArray(indexes) + && indexes.every((index) => Number.isInteger(index) && index >= 0 && index <= 0xffff), + 'Expected quorumsCLSigs values to be arrays of quorum indexes' + ); + $.checkArgument( + isStrictlyIncreasing(indexes), + 'Expected quorumsCLSigs indexes to be sorted and unique, as Core sends them' + ); + const signatureBytes = Buffer.from(signature, 'hex'); + return { signatureBytes, orderKey: clSigOrderKey(signatureBytes), indexes }; + }); + // write them in Core's map order, whatever order they are given in + quorumsCLSigs.sort((a, b) => Buffer.compare(a.orderKey, b.orderKey)); + quorumsCLSigs.forEach((quorumsCLSig, i) => { + $.checkArgument( + i === 0 || !quorumsCLSig.orderKey.equals(quorumsCLSigs[i - 1].orderKey), + 'Expected each quorumsCLSigs signature to appear once' + ); + }); + + bufferWriter.writeVarintNum(quorumsCLSigs.length); + quorumsCLSigs.forEach(({ signatureBytes, indexes }) => { + bufferWriter.write(signatureBytes); + bufferWriter.writeVarintNum(indexes.length); + indexes.forEach((index) => bufferWriter.writeUInt16LE(index)); }); return bufferWriter.toBuffer(); @@ -235,6 +342,9 @@ SimplifiedMNListDiff.fromObject = function fromObject(obj, network, llmqParamsOv if (obj.merkleRootQuorums) { simplifiedMNListDiff.merkleRootQuorums = obj.merkleRootQuorums; } + if (obj.quorumsCLSigs) { + simplifiedMNListDiff.quorumsCLSigs = _.cloneDeep(obj.quorumsCLSigs); + } if (simplifiedMNListDiff.mnList.length > 0) { if ( @@ -269,11 +379,11 @@ SimplifiedMNListDiff.fromJSON = function fromJSON(diffJSON, llmqParamsOverride= return SimplifiedMNListDiff.fromObject( { - llmqParamsOverride, ...diffJSON, cbTxMerkleTree, }, - Networks.get(diffJSON.network.name) + Networks.get(diffJSON.network.name), + llmqParamsOverride ); }; @@ -299,12 +409,18 @@ SimplifiedMNListDiff.prototype.toObject = function toObject() { if (this.merkleRootQuorums) { obj.merkleRootQuorums = this.merkleRootQuorums; } + if (this.quorumsCLSigs) { + obj.quorumsCLSigs = _.cloneDeep(this.quorumsCLSigs); + } return obj; }; SimplifiedMNListDiff.prototype.copy = function copy() { - return _.cloneDeep(this); + // entries copy themselves to keep the ExtNetInfo bytes they were read from + return _.cloneDeepWith(this, (value) => ( + value instanceof SimplifiedMNListEntry ? value.copy() : undefined + )); }; module.exports = SimplifiedMNListDiff; diff --git a/lib/deterministicmnlist/SimplifiedMNListEntry.js b/lib/deterministicmnlist/SimplifiedMNListEntry.js index 33aa22508..e90889020 100644 --- a/lib/deterministicmnlist/SimplifiedMNListEntry.js +++ b/lib/deterministicmnlist/SimplifiedMNListEntry.js @@ -7,7 +7,8 @@ const Hash = require('../crypto/hash'); const constants = require('../constants'); const utils = require('../util/js'); const ipUtils = require('../util/ip'); -const { extNetInfoToBuffer } = require('../util/netinfo'); +const { extNetInfoToBuffer, readExtNetInfo } = require('../util/netinfo'); +const { readExact, readExactReverse } = require('../transaction/payload/strictpayloadreader'); const Address = require('../address'); const Networks = require('../networks'); @@ -22,6 +23,7 @@ const { BLS_PUBLIC_KEY_SIZE, PUBKEY_ID_SIZE, PLATFORM_NODE_ID_SIZE, + MASTERNODE_TYPE_BASIC, MASTERNODE_TYPE_HP, PROTX_EXT_ADDR_VERSION, } = constants; @@ -85,6 +87,39 @@ function SimplifiedMNListEntry(arg, network) { } } +/** + * Keeps the ExtNetInfo bytes an entry was read from, with the addresses they + * decoded to. A BIP155 IPv6 address in fc00::/8 (valid on regtest) prints the + * same as a CJDNS one and extNetInfoToBuffer writes that string as CJDNS, so + * re-encoding could change the entry's bytes and hash. Core's JSON can't tell + * the two apart either, so entries from JSON keep the string rule. + * Non-enumerable, so toObject() and JSON.stringify() leave it out. + * @param {SimplifiedMNListEntry} entry + * @param {{buffer: Buffer, addresses: Object}} rawExtNetInfo + */ +function setRawExtNetInfo(entry, rawExtNetInfo) { + Object.defineProperty(entry, '_rawExtNetInfo', { + value: rawExtNetInfo, + writable: true, + configurable: true, + enumerable: false, + }); +} + +/** + * The entry's ExtNetInfo bytes: the ones it was read from while its addresses + * are unchanged, otherwise its addresses serialized + * @param {SimplifiedMNListEntry} entry + * @return {Buffer} + */ +function getExtNetInfoBuffer(entry) { + const raw = entry._rawExtNetInfo; + if (raw && _.isEqual(raw.addresses, entry.addresses)) { + return raw.buffer; + } + return extNetInfoToBuffer(entry.addresses); +} + /** * Parse buffer and returns SimplifiedMNListEntry * @param {Buffer} buffer @@ -96,10 +131,9 @@ SimplifiedMNListEntry.fromBuffer = function fromBuffer(buffer, network) { const object = { }; - object.proRegTxHash = bufferReader.read(SHA256_HASH_SIZE).reverse().toString('hex'); + object.proRegTxHash = bufferReader.readReverse(SHA256_HASH_SIZE).toString('hex'); object.confirmedHash = bufferReader - .read(SHA256_HASH_SIZE) - .reverse() + .readReverse(SHA256_HASH_SIZE) .toString('hex'); object.service = parseIp(bufferReader.read(ipUtils.IP_AND_PORT_SIZE)); object.pubKeyOperator = bufferReader.read(BLS_PUBLIC_KEY_SIZE).toString('hex'); @@ -123,12 +157,63 @@ SimplifiedMNListEntry.fromBuffer = function fromBuffer(buffer, network) { if (object.nType === MASTERNODE_TYPE_HP) { object.platformHTTPPort = bufferReader.readUInt16LE(); - object.platformNodeID = bufferReader.read(PLATFORM_NODE_ID_SIZE).reverse().toString('hex'); + object.platformNodeID = bufferReader.readReverse(PLATFORM_NODE_ID_SIZE).toString('hex'); } return SimplifiedMNListEntry.fromObject(object); }; +/** + * Reads an entry as Core sends it in the P2P `mnlistdiff` message: + * nVersion followed by the hashed fields (see toP2PBuffer) + * @param {BufferReader} bufferReader + * @param {string} [network] + * @return {SimplifiedMNListEntry} + */ +SimplifiedMNListEntry.fromP2PBufferReader = function fromP2PBufferReader(bufferReader, network) { + const object = {}; + + object.nVersion = bufferReader.readUInt16LE(); + object.proRegTxHash = readExactReverse(bufferReader, SHA256_HASH_SIZE).toString('hex'); + object.confirmedHash = readExactReverse(bufferReader, SHA256_HASH_SIZE).toString('hex'); + let rawExtNetInfo; + if (object.nVersion >= PROTX_EXT_ADDR_VERSION) { + const start = bufferReader.pos; + object.addresses = readExtNetInfo(bufferReader); + rawExtNetInfo = { + buffer: Buffer.from(bufferReader.buf.slice(start, bufferReader.pos)), + addresses: _.cloneDeep(object.addresses), + }; + } else { + object.service = parseIp(readExact(bufferReader, ipUtils.IP_AND_PORT_SIZE)); + } + object.pubKeyOperator = readExact(bufferReader, BLS_PUBLIC_KEY_SIZE).toString('hex'); + object.votingAddress = Address.fromPublicKeyHash( + readExact(bufferReader, PUBKEY_ID_SIZE), + network + ).toString(); + object.isValid = Boolean(bufferReader.readUInt8()); + + // Version 1 entries carry no type; Core reads them as regular masternodes, + // though its RPC can report type 1 for revoked version 1 evonodes + object.nType = MASTERNODE_TYPE_BASIC; + if (object.nVersion >= 2) { + object.nType = bufferReader.readUInt16LE(); + if (object.nType === MASTERNODE_TYPE_HP) { + if (object.nVersion < PROTX_EXT_ADDR_VERSION) { + object.platformHTTPPort = bufferReader.readUInt16LE(); + } + object.platformNodeID = readExactReverse(bufferReader, PLATFORM_NODE_ID_SIZE).toString('hex'); + } + } + + const entry = SimplifiedMNListEntry.fromObject(object); + if (rawExtNetInfo) { + setRawExtNetInfo(entry, rawExtNetInfo); + } + return entry; +}; + /** * @param {string} string * @param {string} [network] @@ -150,7 +235,7 @@ SimplifiedMNListEntry.prototype.toBuffer = function toBuffer() { bufferWriter.write(Buffer.from(this.proRegTxHash, 'hex').reverse()); bufferWriter.write(Buffer.from(this.confirmedHash, 'hex').reverse()); if (hasExtAddr) { - bufferWriter.write(extNetInfoToBuffer(this.addresses)); + bufferWriter.write(getExtNetInfoBuffer(this)); } else { bufferWriter.write(serializeIp(this.service)); } @@ -177,6 +262,19 @@ SimplifiedMNListEntry.prototype.toBuffer = function toBuffer() { return bufferWriter.toBuffer(); }; +/** + * Serialize SML entry as Core sends it in the P2P `mnlistdiff` message + * (protocol 70228 and later): nVersion, then the fields toBuffer() hashes + * @return {Buffer} + */ +SimplifiedMNListEntry.prototype.toP2PBuffer = function toP2PBuffer() { + const bufferWriter = new BufferWriter(); + // Entries from Core RPC that predate nVersion are version 1 (legacy BLS) + bufferWriter.writeUInt16LE(typeof this.nVersion === 'number' ? this.nVersion : 1); + bufferWriter.write(this.toBuffer()); + return bufferWriter.toBuffer(); +}; + /** * Create SMLEntry from an object * @param {SMLEntry} obj @@ -256,9 +354,10 @@ SimplifiedMNListEntry.prototype.validate = function validate() { typeof this.isValid === 'boolean', 'Expected isValid to be a boolean' ); + // Core's MnType has only Regular (0) and Evo (1) $.checkArgument( - typeof this.nType === 'number', - 'Expected nType to be a number' + this.nType === MASTERNODE_TYPE_BASIC || this.nType === MASTERNODE_TYPE_HP, + 'Expected nType to be 0 (regular) or 1 (evo)' ); if (this.nType === constants.MASTERNODE_TYPE_HP) { @@ -335,6 +434,47 @@ SimplifiedMNListEntry.prototype.getIp = function getIp() { return this.service.split(':')[0]; }; +/** + * Gets the evonode's Platform HTTPS endpoint: the first `platform_https` address + * of a version 3 (ExtAddr) entry, which may be an IPv6 address or a domain, or + * the service ip with platformHTTPPort of an older one. PoSe-banned entries + * have endpoints too; check isValid before using one. + * @return {{host: string, port: number}|null} host without IPv6 brackets, like + * getIp(); null for regular masternodes and evonodes without an address + */ +SimplifiedMNListEntry.prototype.getPlatformHTTPSEndpoint = function getPlatformHTTPSEndpoint() { + if (this.nType !== MASTERNODE_TYPE_HP) { + return null; + } + + // ExtAddr entries: host and port both come from the current platform_https[0] + if (this.nVersion >= PROTX_EXT_ADDR_VERSION) { + const [address] = (this.addresses && this.addresses.platform_https) || []; + if (typeof address !== 'string' || ipUtils.isZeroAddress(address)) { + return null; + } + const separator = address.lastIndexOf(':'); + const port = Number(address.slice(separator + 1)); + // port 0 is reserved, not a reachable HTTPS endpoint + if (port === 0) { + return null; + } + return { + host: address.slice(0, separator).replace(/^\[(.*)\]$/, '$1'), + port, + }; + } + + if ( + typeof this.platformHTTPPort !== 'number' + || this.platformHTTPPort === 0 + || ipUtils.isZeroAddress(this.service) + ) { + return null; + } + return { host: this.getIp(), port: this.platformHTTPPort }; +}; + /** * Serialize confirmed hash with proRegTxHash for MN scores * and quorum member selection @@ -353,7 +493,11 @@ SimplifiedMNListEntry.prototype.confirmedHashWithProRegTxHash = * @return {SimplifiedMNListEntry} */ SimplifiedMNListEntry.prototype.copy = function copy() { - return _.cloneDeep(this); + const entry = _.cloneDeep(this); + if (this._rawExtNetInfo) { + setRawExtNetInfo(entry, this._rawExtNetInfo); + } + return entry; }; module.exports = SimplifiedMNListEntry; diff --git a/lib/transaction/payload/coinbasepayload.js b/lib/transaction/payload/coinbasepayload.js index f242e5bb5..277b65b05 100644 --- a/lib/transaction/payload/coinbasepayload.js +++ b/lib/transaction/payload/coinbasepayload.js @@ -59,13 +59,11 @@ CoinbasePayload.fromBuffer = function (rawPayload) { payload.version = payloadBufferReader.readUInt16LE(); payload.height = payloadBufferReader.readUInt32LE(); payload.merkleRootMNList = payloadBufferReader - .read(HASH_SIZE) - .reverse() + .readReverse(HASH_SIZE) .toString('hex'); if (payload.version >= 2) { payload.merkleRootQuorums = payloadBufferReader - .read(HASH_SIZE) - .reverse() + .readReverse(HASH_SIZE) .toString('hex'); } diff --git a/lib/util/isDenseArray.js b/lib/util/isDenseArray.js new file mode 100644 index 000000000..fd9e48c0e --- /dev/null +++ b/lib/util/isDenseArray.js @@ -0,0 +1,18 @@ +/** + * Whether value is an array whose own enumerable keys are exactly its indexes + * '0'..String(length - 1): no empty slots and no extra properties. Serializers + * check this before writing a count, since forEach skips holes. Object.keys + * lists only present keys, so a huge sparse length is never materialized. + * @param {*} value + * @return {boolean} + */ +function isDenseArray(value) { + if (!Array.isArray(value)) { + return false; + } + // an array's integer keys come first, in ascending order + const keys = Object.keys(value); + return keys.length === value.length && keys.every((key, i) => key === String(i)); +} + +module.exports = isDenseArray; diff --git a/lib/util/netinfo.js b/lib/util/netinfo.js index af7a85842..01a213128 100644 --- a/lib/util/netinfo.js +++ b/lib/util/netinfo.js @@ -3,6 +3,7 @@ const BufferReader = require('../encoding/bufferreader'); const BufferWriter = require('../encoding/bufferwriter'); // Core-canonical CompactSizes and exact reads, as Core's deserializer const { readExact, readCompactSize } = require('../transaction/payload/strictpayloadreader'); +const isDenseArray = require('./isDenseArray'); // Serialization of Dash Core's ExtNetInfo (src/evo/netinfo.h), the address // format of ExtAddr (version 3) masternode entries. @@ -324,8 +325,9 @@ function extNetInfoToBuffer(addresses) { writer.writeVarintNum(purposes.length); purposes.forEach((purpose) => { const entries = addresses[purpose]; - if (!Array.isArray(entries)) { - throw new Error(`Expected ${purpose} addresses to be an array`); + // forEach skips empty slots, which would leave the count wrong + if (!isDenseArray(entries)) { + throw new Error(`Expected ${purpose} addresses to be an array without empty slots`); } if (entries.length > MAX_ENTRIES_PER_PURPOSE) { throw new Error(`Too many ${purpose} addresses`); diff --git a/lib/util/readVarintCount.js b/lib/util/readVarintCount.js new file mode 100644 index 000000000..7ffbc4562 --- /dev/null +++ b/lib/util/readVarintCount.js @@ -0,0 +1,21 @@ +const { readCompactSize } = require('../transaction/payload/strictpayloadreader'); + +/** + * Reads a peer-controlled item count the way Core does (shortest CompactSize + * encoding, at most MAX_SIZE) and checks that the remaining data can hold that + * many items. BufferReader.read() returns empty slices past the end, so without + * this check a huge count would make the caller loop for ages. + * @param {BufferReader} bufferReader + * @param {number} minItemSize - smallest serialized size of one item, in bytes + * @param {string} name - what is counted, for the error message + * @return {number} + */ +function readVarintCount(bufferReader, minItemSize, name) { + const count = readCompactSize(bufferReader); + if (count * minItemSize > bufferReader.buf.length - bufferReader.pos) { + throw new Error(`${name} count ${count} is larger than the remaining data allows`); + } + return count; +} + +module.exports = readVarintCount; diff --git a/test-d/index.test-d.ts b/test-d/index.test-d.ts index bcc277b7b..1049ca3ef 100644 --- a/test-d/index.test-d.ts +++ b/test-d/index.test-d.ts @@ -112,3 +112,31 @@ expectType(proUpServTxPayload.platformHTTPPort); declare const proUpRegTxPayload: ProUpRegTxPayload; expectType(proUpRegTxPayload.payouts); expectType(proUpRegTxPayload.scriptPayout); + +// Core P2P masternode list diffs +declare const bufferReader: BufferReader; +declare const SMLEntryClass: typeof SimplifiedMNListEntry; +declare const QuorumEntryClass: typeof QuorumEntry; +expectType(SMLEntryClass.fromP2PBufferReader(bufferReader, 'testnet')); +expectType(QuorumEntryClass.fromP2PBufferReader(bufferReader)); +declare const smlEntry: SimplifiedMNListEntry; +expectType(smlEntry.toP2PBuffer()); +declare const smlDiff: SimplifiedMNListDiff; +expectType[] | undefined>(smlDiff.quorumsCLSigs); +expectType<{ host: string; port: number } | null>(smlEntry.getPlatformHTTPSEndpoint()); +declare const SMLDiffClass: typeof SimplifiedMNListDiff; +const customLLMQ = { 200: { size: 3 } }; +expectType(SMLDiffClass.fromBuffer(Buffer.alloc(0), 'testnet', customLLMQ)); +expectType(SMLDiffClass.fromHexString('', 'testnet', customLLMQ)); +expectType(SMLDiffClass.fromObject({}, 'testnet', customLLMQ)); +expectType(SMLDiffClass.fromJSON({}, customLLMQ)); +expectType(new SMLDiffClass({}, 'testnet', customLLMQ)); +// ProUpRegTx has no type or platform fields +// @ts-expect-error +proUpRegTxPayload.platformNodeID; +// @ts-expect-error +proUpRegTxPayload.type; +expectType(QuorumEntryClass.fromBuffer(Buffer.alloc(0), customLLMQ)); +expectType(QuorumEntryClass.fromHexString('', customLLMQ)); +expectType(QuorumEntryClass.fromObject({}, customLLMQ)); +expectType(new QuorumEntryClass({}, customLLMQ)); diff --git a/test/block/PartialMerkleTree.js b/test/block/PartialMerkleTree.js index e7b03bff3..b2bf44e72 100644 --- a/test/block/PartialMerkleTree.js +++ b/test/block/PartialMerkleTree.js @@ -77,4 +77,47 @@ describe('PartialMerkleTree', function () { ); }); }); + + describe('#toBuffer', function () { + it('Should reject merkle hashes or flags with empty slots or extra properties', function () { + ['merkleHashes', 'merkleFlags'].forEach(function (name) { + var extra = []; + extra.length = 1; + extra.extra = 0; + [[, 0], extra].forEach(function (value) { + var tree = PartialMerkleTree.fromHexString( + '0100000001' + '11'.repeat(32) + '0101' + ); + tree[name] = value; + expect(function () { + tree.toBuffer(); + }).to.throw('Expected ' + name + ' to be an array without empty slots'); + }); + }); + }); + }); + + describe('.fromBuffer', function () { + // cbTxMerkleTree of Core's testnet mnlistdiff at block 1580498 + var coreTreeHex = + '0800000004a868b2ed8d2301e11fe10af03de99739daabf69fd189ea44fb5aa1c47df56f1d389ffc77b4ade36785589c1161ed70c244ff26dbfda7830e56bad2a9300d165bb9f91e0478b64957632ae2729374a2c7c0f386052c87ac24864baf6bf0f711c70eb605f91fd18b14932cb62fa77678cfffc9ab19d7990e7283e456398d44d5b0010f'; + + it('Should not modify the buffer it parses', function () { + var buffer = Buffer.from(coreTreeHex, 'hex'); + + var tree = PartialMerkleTree.fromBuffer(buffer); + + expect(buffer.toString('hex')).to.be.equal(coreTreeHex); + expect(tree.toString()).to.be.equal(coreTreeHex); + }); + it('Should throw quickly on a hash count larger than the data', function () { + this.timeout(2000); + // the hash count (04) follows the 4-byte transaction count + var hex = coreTreeHex.slice(0, 8) + 'fe00000002' + coreTreeHex.slice(10); + + expect(function () { + PartialMerkleTree.fromHexString(hex); + }).to.throw('Merkle hash count 33554432 is larger than the remaining data allows'); + }); + }); }); diff --git a/test/constants/llmqparams.js b/test/constants/llmqparams.js new file mode 100644 index 000000000..8aae27493 --- /dev/null +++ b/test/constants/llmqparams.js @@ -0,0 +1,43 @@ +/* eslint-disable */ + +var expect = require('chai').expect; +var constants = require('../../lib/constants'); + +var LLMQ_TYPES = constants.LLMQ_TYPES; + +describe('LLMQ_TYPE_PARAMS', function () { + // size, threshold and signingActiveQuorumCount from src/llmq/params.h in Dash Core 24.0.0 + var coreParams = [ + [LLMQ_TYPES.LLMQ_TYPE_50_60, 50, 30, 24], + [LLMQ_TYPES.LLMQ_TYPE_400_60, 400, 240, 4], + [LLMQ_TYPES.LLMQ_TYPE_400_85, 400, 340, 4], + [LLMQ_TYPES.LLMQ_TYPE_100_67, 100, 67, 24], + [LLMQ_TYPES.LLMQ_TYPE_60_75, 60, 45, 32], + [LLMQ_TYPES.LLMQ_TYPE_25_67, 25, 17, 24], + [LLMQ_TYPES.LLMQ_TYPE_LLMQ_TEST, 3, 2, 2], + [LLMQ_TYPES.LLMQ_TYPE_LLMQ_DEVNET, 12, 6, 4], + [LLMQ_TYPES.LLMQ_TYPE_TEST_V17, 3, 2, 2], + [LLMQ_TYPES.LLMQ_TYPE_TEST_DIP0024, 4, 3, 2], + [LLMQ_TYPES.LLMQ_TYPE_TEST_INSTANTSEND, 3, 2, 2], + [LLMQ_TYPES.LLMQ_DEVNET_DIP0024, 8, 4, 2], + [LLMQ_TYPES.LLMQ_TEST_PLATFORM, 3, 2, 2], + [LLMQ_TYPES.LLMQ_DEVNET_PLATFORM, 12, 8, 4], + ]; + + it('Should define the same LLMQ types as Dash Core', function () { + var types = coreParams.map(function (params) { + return String(params[0]); + }); + expect(Object.keys(constants.LLMQ_TYPE_PARAMS)).to.have.members(types); + }); + + coreParams.forEach(function (params) { + it('Should match Dash Core params for LLMQ type ' + params[0], function () { + expect(constants.LLMQ_TYPE_PARAMS[params[0]]).to.be.deep.equal({ + size: params[1], + threshold: params[2], + maximumActiveQuorumsCount: params[3], + }); + }); + }); +}); diff --git a/test/deterministicmnlist/QuorumEntry.js b/test/deterministicmnlist/QuorumEntry.js index ce033ddf5..20fcafbd0 100644 --- a/test/deterministicmnlist/QuorumEntry.js +++ b/test/deterministicmnlist/QuorumEntry.js @@ -4,6 +4,7 @@ var expect = require('chai').expect; var QuorumEntry = require('../../lib/deterministicmnlist/QuorumEntry'); var SimplifiedMNList = require('../../lib/deterministicmnlist/SimplifiedMNList'); +var BufferReader = require('../../lib/encoding/bufferreader'); var SMNListFixture = require('../fixtures/mnList'); const DashcoreLib = require("../../index"); @@ -91,6 +92,48 @@ describe('QuorumEntry', function () { Buffer.from(quorumEntryHash, 'hex') ); }); + it('Should not modify the buffer it parses', function () { + var buffer = Buffer.from(quorumEntryHex, 'hex'); + + QuorumEntry.fromBuffer(buffer); + + expect(buffer.toString('hex')).to.be.equal(quorumEntryHex); + }); + }); + describe('fromP2PBufferReader', function () { + it('Should read the commitment Core sends and count set bits like Core RPC', function () { + // 50-member bit sets with 48 signers and 49 valid members + var hex = quorumEntryHex.replace('0cff0f0cff0f', '32fffffffffffe0232ffffffffffff02'); + var reader = new BufferReader(Buffer.from(hex, 'hex')); + var entry = QuorumEntry.fromP2PBufferReader(reader, { + 107: { size: 50 }, + }); + + expect(reader.finished()).to.be.true; + expect(entry.toObject()).to.be.deep.equal(Object.assign({}, quorumEntryJSON, { + signersCount: 48, + signers: 'fffffffffffe02', + validMembersCount: 49, + validMembers: 'ffffffffffff02', + })); + expect(entry.toBufferForHashing().toString('hex')).to.be.equal(hex); + }); + + it('Should reject bits set past the bit set size like Core', function () { + // a 50-bit set uses the low 2 bits of its last byte; 0x06 sets bit 50 + var hex = quorumEntryHex.replace('0cff0f0cff0f', '32fffffffffffe0632ffffffffffff02'); + var reader = new BufferReader(Buffer.from(hex, 'hex')); + expect(function () { + QuorumEntry.fromP2PBufferReader(reader, { 107: { size: 50 } }); + }).to.throw('Out-of-range bits set'); + }); + + it('Should throw if the commitment is cut off inside a signature', function () { + var hex = quorumEntryHex.slice(0, quorumEntryHex.length - 20); + expect(function () { + QuorumEntry.fromP2PBufferReader(new BufferReader(Buffer.from(hex, 'hex'))); + }).to.throw('expected 96 more bytes, found 86'); + }); }); describe('to buffer', function () { it('Should be able to generate correct buffer', function () { @@ -105,6 +148,33 @@ describe('QuorumEntry', function () { var buffer = entry.toBufferForHashing(); expect(buffer).to.be.deep.equal(Buffer.from(quorumEntryHex, 'hex')); }); + + it('Should reject bit sets that do not match the LLMQ size', function () { + var short = QuorumEntry.fromBuffer(Buffer.from(quorumEntryHex, 'hex')); + short.signers = '00'; + expect(function () { + short.toBufferForHashing(); + }).to.throw('Expected signers to have the LLMQ bit set length'); + + var long = QuorumEntry.fromBuffer(Buffer.from(quorumEntryHex, 'hex')); + long.validMembers += '00'; + expect(function () { + long.toBufferForHashing(); + }).to.throw('Expected validMembers to have the LLMQ bit set length'); + }); + + it('Should reject bits set past the LLMQ size', function () { + var entry = QuorumEntry.fromBuffer(Buffer.from(quorumEntryHex, 'hex')); + var size = entry.getParams().size; + expect(size % 8).to.not.equal(0); + var bytes = Buffer.from(entry.signers, 'hex'); + // eslint-disable-next-line no-bitwise + bytes[bytes.length - 1] |= 0x80; + entry.signers = bytes.toString('hex'); + expect(function () { + entry.toBufferForHashing(); + }).to.throw('Expected no bits set past the LLMQ size'); + }); }); describe('generate commitmentHash', function () { it('Should be able to generate a correct commitmentHash', function () { @@ -114,6 +184,20 @@ describe('QuorumEntry', function () { Buffer.from(commitmentHash, 'hex') ); }); + it('Should reject validMembers that do not fit the LLMQ size', function () { + var long = new QuorumEntry(quorumEntryJSON); + long.validMembers += '00'; + expect(function () { + long.getCommitmentHash(); + }).to.throw('Expected validMembers to have the LLMQ bit set length'); + + // a 12-member bit set uses the low 4 bits of its last byte + var outOfRange = new QuorumEntry(quorumEntryJSON); + outOfRange.validMembers = 'ff1f'; + expect(function () { + outOfRange.getCommitmentHash(); + }).to.throw('Expected no bits set past the LLMQ size'); + }); }); describe('quorum members', function () { it('Should generate the correct selectionModifier', function () { diff --git a/test/deterministicmnlist/SimplifiedMNListDiff.js b/test/deterministicmnlist/SimplifiedMNListDiff.js index 3ea6ae743..f9d370782 100644 --- a/test/deterministicmnlist/SimplifiedMNListDiff.js +++ b/test/deterministicmnlist/SimplifiedMNListDiff.js @@ -6,9 +6,15 @@ var SMNListFixture = require('../fixtures/mnList'); var expect = require('chai').expect; var sinon = require('sinon'); var Networks = require('../../lib/networks'); +var BlockHeader = require('../../lib/block/blockheader'); +var MerkleBlock = require('../../lib/block/merkleblock'); +var SimplifiedMNList = require('../../lib/deterministicmnlist/SimplifiedMNList'); -var mnListDiffHexString = - '256b871b6d78a3659545e11576be763efb97a1343937a7d78ebdc1ff0800000070c7a7deb8dae2650c6c867fcb12771f6d54952cb14fa325296fa60ccc020000030000000399b849a70a77784018b41be5abe6e6baa1041d873f32c1c287f9b70303c6d05e33853149ef48201ddbf71c12c2b889e315837faedb3ec0ce21903e4728547919256e3b70dea340b85a3b33ae0188d20ff7c7016bc493b401a380920e9b608992010703000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502581b0101ffffffff0200694d48010000001976a9144544b54eb482944c315bacfe9b5392ecdc1cd56b88ac801d74ec010000001976a9144544b54eb482944c315bacfe9b5392ecdc1cd56b88ac00000000460200581b000010228246d725173a25631c575a47f33f25973bc749fc61d3fe8f5035f149c6ee545199bcba5e20a8a8d2c8ac39210d6de3e0076e833b2225f15b0625a8738ad9020000000a650e790a074f6e71e270c27d43b9d1cb2513537744d3cd638541c989685e020000650eedb02ef0dd46d5263c72b8e83c932a705a48b7b978b5ae41658c9433010000659f40f8b0272a3ef8bc74f01b3a9f2d8cb90388fba5785e5ef69ceb648601000065f833f4b6f249069ac3f5f9784115b586113af7d6f936a285a574705989010000694228e2454355c39bc167ddfd1c8dae9ef391c4c358d4c053b688d5757700000069f833f4b6f249069ac3f5f9784115b586113af7d6f936a285a5747059890100006b0e790a074f6e71e270c27d43b9d1cb2513537744d3cd638541c989685e0200006b0eedb02ef0dd46d5263c72b8e83c932a705a48b7b978b5ae41658c94330100006b9f40f8b0272a3ef8bc74f01b3a9f2d8cb90388fba5785e5ef69ceb64860100006bf833f4b6f249069ac3f5f9784115b586113af7d6f936a285a5747059890100000afd39010300651a9062a40cc2dde34af8fdccdcb091e957d5804dbc57eb207215e36df60500000cff0f0cff0fb7ce8404327d7e2d51ebcda3ef8b0de5efe3d47d754f345363b4fb3ed1f0f58e0a57e5fdd78b8f540d8e530cc1d9a2950dddc7221af137e9ec65644c01f1c60104ce558e33b2821080691fac2a6034a690a53267f8ce63887ed83924a5b7ff4cc527e9dab4394f794864ea5c940280fe3813f4823a961d2b00bfa14fffa82b7916cebf95a1db362a55430e46cc168a9b3c7f4b956d531b93928044829857058147b96fb3ce4a2ba5101abfe19a56983cab3a6e1deb9e2ecdc2338546d2149809bf8f91639cca400d8dfa73c8f387c11a08000758761486141fe3c7ed5846e6ef15e6a262a6b05030220d5f0b72948d1108b99676a6907db9b7e2e58121a70dd1ea2f6b897d454fe93630fe5825125a13fd39010300652f22c2c0ed659d74d3a3cd234aa2e0ef854c49875514a7559d586d4f1e0400000cff0f0cff0fa56980a7b3cfe15c5f3bbe3fc9ab4f3d73c1cd7da9e8ae537629ebaeb2bac32c90435c5f1258cca230a6c071b5d40897389cbfa08c31efa1155df29fff6c57b2a473f9d146e99ae4fcc097e21421893c96e515a5621917183f8fbbcef6696e01acbf5d4766f067b6809dfb8aaa7a1617102cf1b83acb030fa34ce9b3475c1856057859fd2d76dda81e737d37dcef60ff2f28f0ca586e415b7e62db003fd4ebf32430cb876a3db1f1eda3c1a6599d682e921069ed2cd23e125d984567e7bfd2bd9509c89242eaed8fe832045d789b3b200a9904e5d62ab9c9e6a59067bcf147ed065c8e818abcdf329ef243512de8eebeea0ad234ecca62b1f14b1a2d0022a485d12ea87940af7aac1cddefdec1439999fd3901030065b12d2bd101c3c629859d1990ba2f5a6e1b5d1b01d1805abd71322bf7ca0100000cff0f0cff0f87fc5378cfb956fc0696c091e78fc5113a879fe46818d7f2f690a1cd9b7a6a314ef3958e99f4120d6d6ba1de3d5420ffd7ed6e52266e9f1ecea3657dee4667fdb79ed34fcc71af13baddd13116717602ae6cda610c2d283d61a2a786def53bcf16fc34d6557cb1a8c025a1fe7d5d0a4fac7177054bc00d07860391c7f03e7cae0490af7c663422a95b323161e679a19c35401ae62ddaa0a474ecd95e01125513c0f8361cf68c000459d18e31ebe72ab1838c780da07c11a6076c6459cf020d168191c89e9c5854e23e366f3a884fb5f1c5a6767cd0f5731073dbd920ddefde9614d7dfca0f5dd1cf2d888fee94099b0ef381cc10fb2381c3724837da50e7a3813865f752242c849b4232b391feff5e31fd3901030065f547145bef4d7245728145173abd82530083c426f1ab1b49c427e36d360000000cff0f0cff0f8acfdbcf8deaefb06e03cff275b3807eb81c8c92505b0f48827c6a503471ea75a0a6a37808661d7a4fc028f7a40dda5320684d9670b7129534ce31ebe2923ad4724174f76975aae0f88dca97d8fcb3c1a01f3ec095f8c879b130281f87d3f6f9a2a1691c5c6270c9fad95afa77a2b9bdbfb36683ab4ba406fcdeaff663ade56a0a8373a466c1dc243cfac88c6a89251c7c2602ae5f74c54c5e76eecd63ff1a6a9bee475cbc52a6332775660e43290c48acdcf975a98967eef69cf17ce5f9222f08c50c2e9008c7b4abe99f962c45ccc59ea088f2e2ea66f0236fcbd168d1b3a607a763351a4c60261c6b67bf8b52bcb99a88dff8ae33aa6214773cfc52fa0e8299b051b18d483871599025d92f6a4765fd39010400697fc1936a4507595755d48c39e3daf9146a608e35802416c48a3181d383210000010008ff08ffb0107d065aa3dafcea53b90f7b725e31f948367641c7c6c8b69d7cfad348a3465629975a1c92a3d99fb5a369d3a97d6bba15db82876a91a2a96bcbc75ba61c52dfe33f29885cce4055dd27ae9c55332d9385523156c0acca0b80bdcc01954aa1b98bc748317f29d67b401c19d7a6d4a449e02efce06274cc6c70250008cae8da033d6f63c659d9b2f14dfc5ea97b25a1cca5380ae30d8b6bfa5af5f3511dd0763d2a1d9ab7d7406558be1a37afa2340e91b1c8b0293b63313934374e05e114f7ab7a2ad725dffafca85f0a56ed9f4a1a69af8bd452f5e218b5ec0d1a8094b037184ac54ec66ff6944f3317a09d4c736cefa5685576103e120c0f7c62022af611da3dee4c96e814a90f9bf69255cf2492fd3901040069b12d2bd101c3c629859d1990ba2f5a6e1b5d1b01d1805abd71322bf7ca010000000008ff08ff97b8e0411dd35e02d05675c9d2f996a1675cc9bdeff4ca537fcb8f237d3e5dfad1e9e612ff13c1f7b84e4caaca349fc12d13a726fe7624c8e553c415692861747ba6b779f948c19428997b45115f8f598df857bedab3020c058572e38f9ccd9172c44b3949338ac812eb9167cf9dc349324984f7eb340631cd9fd9564fac7c91100fd679a9977de87998ba814988c3930f043145271c6b6e9b7073b2b81226f30b901cb4f6f720356e54d3bb5960d62db8208c2f150585bbf8f199724afe93253ce9b117a2cf6e838c73303fd8e3034bf779fe1d55e1ae7034e48d97d3c52053153e75d4d4cc72fdfc6829cfa7431849d7e24550b52b15c0ff3efce1eff9deb82c41f59d30707aa07d85f0e3afecd59efd390103006b1a9062a40cc2dde34af8fdccdcb091e957d5804dbc57eb207215e36df60500000cff0f0cff0f80534ec19bee96b84f0dadb28433ee0b44aefd69e6dc8e5124cdd494df15269f62f6fb51b91a5cede7d12d138625776f717f72307471790cb57018bad49cb3557ccf9efd7b233aef0088e08025dec3e9a25223b886c6e4bc7af69c8ec844042216e31ca1d7fbace828f844e53be052165846a75f7e1be96f07e9c4e8db0d165e090c7f39c28da0c4188bd9cf7ff3e777af4647289b24c31288bcb6c56a525845c72cbb31fc0d1e600af89e1f5e8301afa30e5281bfd730db1b36e56efdf2f3b8f18eed1c6f9ac8e1028b99eb1ba20adf1c30046eaedb3b94ba261fcf6166b138150afcd2dd02e48bb7bf756f07a50106f28d0389226e11d749df7871dc0d9f58444e25c5605ccaafcaa671454faa6c9cfd390103006b2f22c2c0ed659d74d3a3cd234aa2e0ef854c49875514a7559d586d4f1e0400000cff0f0cff0f88492096daf2dfe7d48fb346ab406a49833e890dfa60c9025b201848c739f5bbfa75f91d8791e84cddc6479172bc72672641231412080e6cedf10568315514f5f4b0dce002ba4132c67543e73901f31db6f0b8044d99cca1efae8372093029b963905bf467cf6263ce16ca3892c3afd12d90b5029b107ae9c7bc8bae3bf3169003f7e078a096702092b5dafc8bfa9978f23e815d3a9c3daecd2b441ab841111d696e95214d2c1a10885bfe8dfcd0ba9190661336c308e08b239f58eb9e1d5e3152c34adbba0b3eaa5f6ba8ee4f0d35f9631743e12cf08e5751ab91cd8e94723614439a493f5e7abf3344469c9c6e39586d69fab0db4232ceede57d4549484eecdbf4184c9253749a9a7defdea867bf8cfd390103006bb12d2bd101c3c629859d1990ba2f5a6e1b5d1b01d1805abd71322bf7ca0100000cff0f0cff0fa2605b9706e06332f269bc53ea02e7dcb24d47d43c541e079171544012a5eb5dd724dedc32f18b5a8726da4a0c9c22f407d0e311f5c48a7340a80efa581925147bde364f8cb6f25cc437958cdcf8206bb69798eda5c074a0374435567a1c7b70995301c148d4b56a9887edf8cc6b934dcaa89bab01f8b25a382e8f752659e95a034fefd1c5dac4de435e059c2c52ecba21a74107477eab9168e8695e4b56ead98a9748ea3914ae283928b895dddc76dcaf18dbee1ac5c5f5050b050b59e90189cbfcdbf5afcd5d2fe1d20ea04c1710bcf8a4d42c41aa9e2b40d5e3da5998931811e8fe4d97a41f27d146df52deaa589e54d0d386c57fd1e12883a296483a8738bc90e99946b5c76205b0415af805db49fd390103006bf547145bef4d7245728145173abd82530083c426f1ab1b49c427e36d360000000cff0f0cff0fada222a1b00c9653f807ea7b254d225b5f8abf3d0f7e8c84089ae96e74ebae63ab3152622e2cc3b4736d30a51b2d8e8db97821bbbcd2ec08b621b3114a85877aaed1028918cabfb17b90694f5915d9e5a2057983f37f96d94528f68d23f4a137a91ea535b53f0916f83f84a18bdfba8ef5bda7ff6c0e2c5b76c0869ee596f56e0f29da122a3bb2f8c34f8837532148104855cdb3dbbe48274b6f28f3f489e1b5b7c77cdb55e92f40099dad5da7332041900b776c89053defa7c78b8ba8fd1a8111a30cd7a9869e613412c8a748f7e9f6e8ae4a6fe18506013a8cf89f9fa18fb010d96863dcb8b84e23e6cc49d6cc760345bdf3e6dd1bc77c91a9fdca307a3fec57c3fd0d491dd4a74ea6a5a69f50087f'; +var coreDiffs = require('../fixtures/core-p2p-mnlistdiffs.json'); +// Core's RPC JSON for the same testnet diffs as coreDiffs.testnetFull and the +// one block after it +var coreDiffsJSON = require('../fixtures/core24-testnet-mnlistdiffs.json'); +var mnListDiffHexString = coreDiffs.testnetIncremental.hex; var mnListDiffJSON = SMNListFixture.getSecondDiff(); describe('SimplifiedMNListDiff', function () { @@ -92,29 +98,479 @@ describe('SimplifiedMNListDiff', function () { }); }); describe('fromBuffer', function () { - it('Should be able to parse a buffer', function () { + it('Should not modify the buffer it parses', function () { var buf = Buffer.from(mnListDiffHexString, 'hex'); - var smlDiff = new SimplifiedMNListDiff(buf, Networks.testnet); - var parsed = smlDiff.toObject(); - expect(parsed).to.be.deep.equal(mnListDiffJSON); + + SimplifiedMNListDiff.fromBuffer(buf, Networks.testnet); + + expect(buf.toString('hex')).to.be.equal(mnListDiffHexString); + }); + it('Should throw if the buffer has data after the diff', function () { + expect(function () { + SimplifiedMNListDiff.fromHexString(mnListDiffHexString + '00', 'testnet'); + }).to.throw('MnListDiff is followed by unexpected data'); + }); + describe('with a crafted count', function () { + // byte offsets of each single-byte count in mnListDiffHexString + var offsets = {}; + + before(function () { + var diff = SimplifiedMNListDiff.fromHexString(mnListDiffHexString, 'testnet'); + var tree = diff.cbTxMerkleTree; + var offset = 2 + 32 + 32 + 4; // nVersion, base and block hashes, totalTransactions + offsets['Merkle hash'] = offset; + offset += 1 + tree.merkleHashes.length * 32; + offsets['Merkle flag byte'] = offset; + offset += 1 + tree.merkleFlags.length + diff.cbTx.toBuffer().length; + offsets['Deleted masternode'] = offset; + offset += 1 + diff.deletedMNs.length * 32; + offsets['Masternode list entry'] = offset; + offset += 1; + offsets['Deleted quorum'] = offset; + offset += 1 + diff.deletedQuorums.length * 33; + offsets['New quorum'] = offset; + offset += 1; + diff.newQuorums.forEach(function (quorum) { + offset += quorum.toBufferForHashing().length; + }); + offsets['Quorum chainlock signature'] = offset; + offsets['Quorum index'] = offset + 1 + 96; + + // the vector has no masternode changes, so mnList adds no bytes above + expect(diff.mnList).to.be.empty; + Object.keys(offsets).forEach(function (name) { + expect(parseInt(mnListDiffHexString.substr(offsets[name] * 2, 2), 16)).to.be.below(0xfd); + }); + }); + + [ + 'Merkle hash', + 'Merkle flag byte', + 'Deleted masternode', + 'Masternode list entry', + 'Deleted quorum', + 'New quorum', + 'Quorum chainlock signature', + 'Quorum index', + ].forEach(function (name) { + function craftCount(count) { + var position = offsets[name] * 2; + return mnListDiffHexString.slice(0, position) + count + + mnListDiffHexString.slice(position + 2); + } + + it('Should throw quickly on a huge ' + name + ' count', function () { + this.timeout(2000); + // MAX_SIZE, the largest count Core reads + expect(function () { + SimplifiedMNListDiff.fromHexString(craftCount('fe00000002'), 'testnet'); + }).to.throw(name + ' count 33554432 is larger than the remaining data allows'); + }); + it('Should reject a ' + name + ' count above MAX_SIZE or not shortest-encoded', function () { + this.timeout(2000); + expect(function () { + SimplifiedMNListDiff.fromHexString(craftCount('feffffffff'), 'testnet'); + }).to.throw('CompactSize too large'); + expect(function () { + SimplifiedMNListDiff.fromHexString(craftCount('fd0100'), 'testnet'); + }).to.throw('non-canonical CompactSize'); + }); + }); + }); + it('Should throw if the diff is cut off inside a fixed-size field', function () { + // nVersion, baseBlockHash, then half of blockHash + var hex = mnListDiffHexString.slice(0, (2 + 32 + 16) * 2); + + expect(function () { + SimplifiedMNListDiff.fromHexString(hex, 'testnet'); + }).to.throw('expected 32 more bytes, found 16'); + }); + describe('with a crafted quorum bit set size', function () { + // the first new quorum is a version 3 llmq_50_60 commitment: version (2 bytes), + // llmqType (1), quorumHash (32), then the signers bit set size, 0x32 (50) + var quorumStart; + var hugeSize = 'ff0000000000002000'; // 2^53 bits + + before(function () { + var diff = SimplifiedMNListDiff.fromHexString(mnListDiffHexString, 'testnet'); + quorumStart = mnListDiffHexString.indexOf( + diff.newQuorums[0].toBufferForHashing().toString('hex') + ); + expect(mnListDiffHexString.substr(quorumStart + 4, 2)).to.be.equal('01'); + expect(mnListDiffHexString.substr(quorumStart + 70, 2)).to.be.equal('32'); + }); + + function craft(llmqType, size) { + return mnListDiffHexString.slice(0, quorumStart + 4) + llmqType + + mnListDiffHexString.slice(quorumStart + 6, quorumStart + 70) + size + + mnListDiffHexString.slice(quorumStart + 72); + } + + it('Should throw if it differs from the size of a known quorum type', function () { + this.timeout(2000); + ['fe00000002', '31'].forEach(function (size) { + expect(function () { + SimplifiedMNListDiff.fromHexString(craft('01', size), 'testnet'); + }).to.throw('Expected a bit set of 50 bits, got'); + }); + }); + it('Should throw if it is longer than the data of an unknown quorum type', function () { + this.timeout(2000); + // MAX_SIZE bits + expect(function () { + SimplifiedMNListDiff.fromHexString(craft('c8', 'fe00000002'), 'testnet'); + }).to.throw('expected 4194304 more bytes'); + }); + it('Should round-trip a custom quorum type through fromJSON with its params', function () { + var override = { 200: { size: 50, threshold: 30, maximumActiveQuorumsCount: 24 } }; + var hex = craft('c8', '32'); + var diff = SimplifiedMNListDiff.fromHexString(hex, 'testnet', override); + var copy = SimplifiedMNListDiff.fromJSON(JSON.parse(JSON.stringify(diff)), override); + + expect(diff.newQuorums[0].llmqType).to.be.equal(200); + expect(copy.toBuffer().toString('hex')).to.be.equal(hex); + }); + it('Should reject a size above MAX_SIZE or not shortest-encoded', function () { + this.timeout(2000); + ['01', 'c8'].forEach(function (llmqType) { + expect(function () { + SimplifiedMNListDiff.fromHexString(craft(llmqType, hugeSize), 'testnet'); + }).to.throw('CompactSize too large'); + expect(function () { + SimplifiedMNListDiff.fromHexString(craft(llmqType, 'fd3200'), 'testnet'); + }).to.throw('non-canonical CompactSize'); + }); + }); }); }); describe('toBuffer', function () { - it('Should be able to serialize data', function () { + it('Should serialize a diff from Core RPC so that fromBuffer reads it back', function () { var buf = new SimplifiedMNListDiff(mnListDiffJSON).toBuffer(); - expect(buf.toString('hex')).to.be.equal(mnListDiffHexString); + var diff = SimplifiedMNListDiff.fromBuffer(buf, Networks.testnet); + + expect(diff.toBuffer()).to.be.deep.equal(buf); + expect(diff.toObject()).to.be.deep.equal( + Object.assign({}, mnListDiffJSON, { quorumsCLSigs: [] }) + ); }); }); - describe('fromHexString', function () { - it('Should be able to create an instance from a hex string', function () { - var diff = SimplifiedMNListDiff.fromHexString( - mnListDiffHexString, - Networks.testnet - ); - expect(diff.toObject()).to.be.deep.equal(mnListDiffJSON); + describe('toBuffer quorumsCLSigs', function () { + var signature = coreDiffsJSON[0].quorumsCLSigs[0]; + var signatureHex = Object.keys(signature)[0]; + + [ + ['two signatures in one entry', Object.assign({}, signature, coreDiffsJSON[0].quorumsCLSigs[1]), + 'Expected each quorumsCLSigs entry to hold exactly one signature'], + ['no signature', {}, 'Expected each quorumsCLSigs entry to hold exactly one signature'], + ['an array entry', [[1]], 'Expected each quorumsCLSigs entry to hold exactly one signature'], + ['a short signature', { abcd: [1] }, 'Expected quorumsCLSigs keys to be bls signatures'], + ['indexes that are not an array', { [signatureHex]: 1 }, + 'Expected quorumsCLSigs values to be arrays of quorum indexes'], + ['an index above 65535', { [signatureHex]: [1, 65536] }, + 'Expected quorumsCLSigs values to be arrays of quorum indexes'], + ['a negative index', { [signatureHex]: [-1] }, + 'Expected quorumsCLSigs values to be arrays of quorum indexes'], + ['a fractional index', { [signatureHex]: [1.5] }, + 'Expected quorumsCLSigs values to be arrays of quorum indexes'], + ['a string index', { [signatureHex]: ['1'] }, + 'Expected quorumsCLSigs values to be arrays of quorum indexes'], + ['unsorted indexes', { [signatureHex]: [2, 1] }, + 'Expected quorumsCLSigs indexes to be sorted and unique, as Core sends them'], + ['a repeated index', { [signatureHex]: [1, 1] }, + 'Expected quorumsCLSigs indexes to be sorted and unique, as Core sends them'], + // eslint-disable-next-line no-sparse-arrays + ['a sparse index array', { [signatureHex]: [0, , 2] }, + 'Expected quorumsCLSigs values to be arrays of quorum indexes'], + ].forEach(function (testCase) { + it('Should reject ' + testCase[0], function () { + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + diff.quorumsCLSigs = [testCase[1]]; + + expect(function () { + diff.toBuffer(); + }).to.throw(testCase[2]); + }); + }); + }); + it('Should reject a sparse quorumsCLSigs array', function () { + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + var signatureHex = Object.keys(coreDiffsJSON[0].quorumsCLSigs[0])[0]; + // eslint-disable-next-line no-sparse-arrays + diff.quorumsCLSigs = [, { [signatureHex]: [0] }]; + expect(function () { + diff.toBuffer(); + }).to.throw('Expected quorumsCLSigs to be an array without empty slots'); + }); + it('Should reject array-like or huge sparse vectors without materializing them', function () { + ['deletedMNs', 'mnList', 'deletedQuorums', 'newQuorums', 'quorumsCLSigs'].forEach(function (name) { + [{ length: 1e9 }, new Array(1e9)].forEach(function (value) { + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + diff[name] = value; + var started = Date.now(); + expect(function () { + diff.toBuffer(); + }).to.throw('Expected ' + name + ' to be an array without empty slots'); + expect(Date.now() - started).to.be.below(1000); + }); + }); + }); + it('Should reject vectors with extra properties in place of their elements', function () { + ['deletedMNs', 'mnList', 'deletedQuorums', 'newQuorums', 'quorumsCLSigs'].forEach(function (name) { + // as many own keys as its length, but none of them an index + var value = []; + value.length = 1; + value.extra = true; + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + diff[name] = value; + expect(function () { + diff.toBuffer(); + }).to.throw('Expected ' + name + ' to be an array without empty slots'); + }); + }); + it('Should write quorumsCLSigs in Core\'s order whatever order they are given in', function () { + var vector = coreDiffs.testnetFull; + var diff = SimplifiedMNListDiff.fromHexString(vector.hex, 'testnet'); + expect(diff.quorumsCLSigs.length).to.be.above(2); + diff.quorumsCLSigs.reverse(); + + expect(diff.toBuffer().toString('hex')).to.be.equal(vector.hex); + }); + it('Should reject a huge sparse chainlock index array without materializing it', function () { + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + var signatureHex = Object.keys(coreDiffsJSON[0].quorumsCLSigs[0])[0]; + diff.quorumsCLSigs = [{ [signatureHex]: new Array(1e9) }]; + var started = Date.now(); + expect(function () { + diff.toBuffer(); + }).to.throw('Expected quorumsCLSigs values to be arrays of quorum indexes'); + expect(Date.now() - started).to.be.below(1000); + }); + it('Should reject a chainlock signature that appears in two entries', function () { + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + var signatureHex = Object.keys(coreDiffsJSON[0].quorumsCLSigs[0])[0]; + diff.quorumsCLSigs = [{ [signatureHex]: [0] }, { [signatureHex.toUpperCase()]: [1] }]; + expect(function () { + diff.toBuffer(); + }).to.throw('Expected each quorumsCLSigs signature to appear once'); + }); + describe('fromBuffer canonical encodings', function () { + // a diff with one chainlock signature: serialize, then edit the trailing bytes + function withCLSigs(clSigs) { + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + diff.quorumsCLSigs = []; + var head = diff.toBuffer().slice(0, -1); // drop the zero quorumsCLSigs count + return Buffer.concat([head, clSigs]); + } + var signature = Buffer.from(Object.keys(coreDiffsJSON[0].quorumsCLSigs[0])[0], 'hex'); + + it('Should reject chainlock indexes that are not a sorted set', function () { + [[2, 1], [1, 1]].forEach(function (pair) { + var indexes = Buffer.alloc(4); + indexes.writeUInt16LE(pair[0], 0); + indexes.writeUInt16LE(pair[1], 2); + var raw = withCLSigs(Buffer.concat([Buffer.from('01', 'hex'), signature, Buffer.from('02', 'hex'), indexes])); + expect(function () { + SimplifiedMNListDiff.fromBuffer(raw, Networks.testnet); + }).to.throw('Quorum chainlock indexes are not a sorted set'); + }); + }); + + it('Should reject chainlock signatures that are not in Core\'s map order', function () { + var diff = SimplifiedMNListDiff.fromHexString(coreDiffs.testnetFull.hex, 'testnet'); + var entries = diff.quorumsCLSigs.map(function (clSig) { + var signatureHex = Object.keys(clSig)[0]; + var indexes = clSig[signatureHex]; + var indexBytes = Buffer.alloc(indexes.length * 2); + indexes.forEach(function (index, i) { + indexBytes.writeUInt16LE(index, i * 2); + }); + return Buffer.concat([ + Buffer.from(signatureHex, 'hex'), + Buffer.from([indexes.length]), + indexBytes, + ]); + }); + var tail = Buffer.concat(entries); + var coreRaw = Buffer.from(coreDiffs.testnetFull.hex, 'hex'); + var head = coreRaw.slice(0, coreRaw.length - tail.length - 1); + expect(Buffer.concat([head, Buffer.from([entries.length]), tail])) + .to.be.deep.equal(coreRaw); + + var reversed = Buffer.concat([ + head, + Buffer.from([entries.length]), + Buffer.concat(entries.slice().reverse()), + ]); + expect(function () { + SimplifiedMNListDiff.fromBuffer(reversed, Networks.testnet); + }).to.throw('Quorum chainlock signatures are not in Core\'s map order'); + }); + + it('Should reject a repeated chainlock signature', function () { + var entry = Buffer.concat([signature, Buffer.from('00', 'hex')]); + var raw = withCLSigs(Buffer.concat([Buffer.from('02', 'hex'), entry, entry])); + expect(function () { + SimplifiedMNListDiff.fromBuffer(raw, Networks.testnet); + }).to.throw('Duplicate quorum chainlock signature'); + }); + + it('Should reject a coinbase transaction with a non-canonical CompactSize', function () { + var diff = new SimplifiedMNListDiff(mnListDiffJSON); + diff.quorumsCLSigs = []; + var raw = diff.toBuffer(); + var cbTx = diff.cbTx.toBuffer(); + var cbStart = raw.indexOf(cbTx); + expect(cbStart).to.be.above(0); + // the input count follows the 4-byte version and type; re-encode 1 as fd0100 + expect(cbTx[4]).to.equal(1); + var nonCanonicalTx = Buffer.concat([cbTx.slice(0, 4), Buffer.from('fd0100', 'hex'), cbTx.slice(5)]); + var tampered = Buffer.concat([raw.slice(0, cbStart), nonCanonicalTx, raw.slice(cbStart + cbTx.length)]); + expect(function () { + SimplifiedMNListDiff.fromBuffer(tampered, Networks.testnet); + }).to.throw('Non-canonical coinbase transaction encoding'); + }); + }); + describe('Core P2P mnlistdiff messages', function () { + Object.keys(coreDiffs).filter(function (name) { + return name !== 'note'; + }).forEach(function (name) { + var vector = coreDiffs[name]; + + describe(name + ' (' + vector.baseHeight + ' to ' + vector.height + ')', function () { + var diff; + + before(function () { + diff = SimplifiedMNListDiff.fromHexString(vector.hex, vector.network); + }); + + it('Should serialize back to the same bytes', function () { + expect(diff.toBuffer().toString('hex')).to.be.equal(vector.hex); + }); + it('Should round-trip through toObject and fromObject', function () { + var copy = SimplifiedMNListDiff.fromObject(diff.toObject(), vector.network); + expect(copy.toBuffer().toString('hex')).to.be.equal(vector.hex); + }); + it('Should round-trip through JSON.stringify and fromJSON', function () { + var copy = SimplifiedMNListDiff.fromJSON(JSON.parse(JSON.stringify(diff))); + expect(copy.toBuffer().toString('hex')).to.be.equal(vector.hex); + }); + it('Should prove the coinbase is in the diff block', function () { + var header = BlockHeader.fromString(vector.blockHeader); + // a merkleblock message is the header followed by a partial merkle tree + var merkleBlock = new MerkleBlock( + Buffer.concat([header.toBuffer(), diff.cbTxMerkleTree.toBuffer()]) + ); + + expect(header.hash).to.be.equal(diff.blockHash); + expect(merkleBlock.validMerkleTree()).to.be.true; + expect(merkleBlock.hasTransaction(diff.cbTx)).to.be.true; + expect(diff.cbTx.extraPayload.height).to.be.equal(vector.height); + }); + }); + }); + + describe('testnetFull', function () { + var vector = coreDiffs.testnetFull; + var diff; + var rpcDiff; + + before(function () { + diff = SimplifiedMNListDiff.fromHexString(vector.hex, 'testnet'); + rpcDiff = SimplifiedMNListDiff.fromObject(coreDiffsJSON[0], 'testnet'); + }); + + it('Should hold the same entries as Core RPC reports', function () { + expect(diff.mnList.length).to.be.equal(coreDiffsJSON[0].mnList.length); + diff.mnList.forEach(function (entry, i) { + var rpcEntry = coreDiffsJSON[0].mnList[i]; + expect(entry.calculateHash()).to.be.deep.equal(rpcDiff.mnList[i].calculateHash()); + expect(entry.proRegTxHash).to.be.equal(rpcEntry.proRegTxHash); + expect(entry.confirmedHash).to.be.equal(rpcEntry.confirmedHash); + expect(entry.nVersion).to.be.equal(rpcEntry.nVersion); + expect(entry.pubKeyOperator).to.be.equal(rpcEntry.pubKeyOperator); + expect(entry.votingAddress).to.be.equal(rpcEntry.votingAddress); + expect(entry.isValid).to.be.equal(rpcEntry.isValid); + // Version 1 entries carry no type on the wire + if (entry.nVersion >= 2) { + expect(entry.nType).to.be.equal(rpcEntry.nType); + expect(entry.platformNodeID).to.be.equal(rpcEntry.platformNodeID); + expect(entry.platformHTTPPort).to.be.equal(rpcEntry.platformHTTPPort); + } + if (entry.nVersion >= 3) { + expect(entry.addresses).to.be.deep.equal(rpcEntry.addresses); + expect(entry.service).to.be.equal(rpcEntry.service); + } + }); + var v3Types = diff.mnList.filter(function (entry) { + return entry.nVersion === 3; + }).map(function (entry) { + return entry.nType; + }); + expect(v3Types).to.include.members([0, 1]); + }); + it('Should hold the same quorums and chainlock signatures as Core RPC reports', function () { + var quorums = diff.newQuorums.map(function (quorum) { + return quorum.toObject(); + }); + expect(quorums).to.be.deep.equal(coreDiffsJSON[0].newQuorums); + expect(diff.deletedQuorums).to.be.deep.equal(coreDiffsJSON[0].deletedQuorums); + expect(diff.quorumsCLSigs).to.be.deep.equal(coreDiffsJSON[0].quorumsCLSigs); + // signers that are not all set must be counted as Core's RPC does + expect(diff.newQuorums.some(function (quorum) { + return quorum.signersCount < quorum.getParams().size; + })).to.be.true; + }); + it('Should give each evonode the Platform endpoint Core RPC reports', function () { + var checked = 0; + diff.mnList.forEach(function (entry, i) { + var platformHTTPS = coreDiffsJSON[0].mnList[i].addresses.platform_https; + // Version 1 entries carry no type on the wire + if (entry.nVersion < 2 || !platformHTTPS) { + return; + } + var endpoint = entry.getPlatformHTTPSEndpoint(); + expect(endpoint.host + ':' + endpoint.port).to.be.equal(platformHTTPS[0]); + checked += 1; + }); + expect(checked).to.be.above(50); + }); + it('Should serialize Core RPC JSON to the bytes Core sends', function () { + expect(rpcDiff.toBuffer().toString('hex')).to.be.equal(vector.hex); + }); + it('Should match the merkle roots in the coinbase', function () { + var mnList = new SimplifiedMNList(diff); + expect(mnList.calculateMerkleRoot()).to.be.equal(diff.merkleRootMNList); + expect(mnList.calculateMerkleRootQuorums()).to.be.equal(diff.merkleRootQuorums); + }); + it('Should match the merkle roots after applying the incremental diff', function () { + var incremental = SimplifiedMNListDiff.fromHexString( + coreDiffs.testnetIncremental.hex, + 'testnet' + ); + var mnList = new SimplifiedMNList(diff); + + mnList.applyDiff(incremental); + + expect(mnList.calculateMerkleRoot()).to.be.equal(incremental.merkleRootMNList); + expect(mnList.calculateMerkleRootQuorums()).to.be.equal(incremental.merkleRootQuorums); + }); }); }); describe('copy', function () { + it('Should keep the ExtNetInfo bytes entries were read from', function () { + var vector = coreDiffs.testnetFull; + var diff = SimplifiedMNListDiff.fromHexString(vector.hex, 'testnet'); + var copy = diff.copy(); + var extAddrEntries = copy.mnList.filter(function (entry) { + return entry.nVersion >= 3; + }); + + expect(extAddrEntries).to.not.be.empty; + extAddrEntries.forEach(function (entry) { + expect(entry._rawExtNetInfo).to.be.an('object'); + }); + expect(copy.toBuffer().toString('hex')).to.be.equal(vector.hex); + }); it('Should create a detached copy of an instance', function () { var instance = new SimplifiedMNListDiff(mnListDiffJSON); var copy = new SimplifiedMNListDiff(instance); diff --git a/test/deterministicmnlist/SimplifiedMNListEntry.js b/test/deterministicmnlist/SimplifiedMNListEntry.js index f2cbaa308..39e1e4acb 100644 --- a/test/deterministicmnlist/SimplifiedMNListEntry.js +++ b/test/deterministicmnlist/SimplifiedMNListEntry.js @@ -5,6 +5,8 @@ var SimplifiedMNListEntry = require('../../lib/deterministicmnlist/SimplifiedMNL var _ = require('lodash'); var expect = require('chai').expect; var Networks = require('../../lib/networks'); +var BufferReader = require('../../lib/encoding/bufferreader'); +var Hash = require('../../lib/crypto/hash'); var smlEntryJSON = { proRegTxHash: @@ -131,6 +133,13 @@ describe('SimplifiedMNListEntry', function () { var entryJSON = entry.toObject(); expect(entryJSON).to.be.deep.equal(smlEntryJSON); }); + it('Should not modify the buffer it parses', function () { + var buffer = Buffer.from(smlEntryHex, 'hex'); + + SimplifiedMNListEntry.fromBuffer(buffer, Networks.testnet); + + expect(buffer.toString('hex')).to.be.equal(smlEntryHex); + }); }); describe('toBuffer', function () { it('Should serialize data to same buffer', function () { @@ -167,6 +176,108 @@ describe('SimplifiedMNListEntry', function () { ); }); }); + describe('P2P serialization', function () { + // Core sends entries as nVersion followed by the hashed fields + [ + ['version 2 evonode', smlEntryJSON, '0200' + smlEntryHex], + ['version 3 evonode', extAddrEvoEntryJSON, '0300' + extAddrEvoEntryHex], + ['version 3 regular masternode', extAddrRegularEntryJSON, '0300' + extAddrRegularEntryHex], + ].forEach(function (testCase) { + it('Should read and write a ' + testCase[0], function () { + var reader = new BufferReader(Buffer.from(testCase[2], 'hex')); + var entry = SimplifiedMNListEntry.fromP2PBufferReader(reader, Networks.testnet); + + expect(reader.finished()).to.be.true; + expect(entry.toObject()).to.be.deep.equal(testCase[1]); + expect(entry.toP2PBuffer().toString('hex')).to.be.equal(testCase[2]); + expect(new SimplifiedMNListEntry(testCase[1]).toP2PBuffer().toString('hex')) + .to.be.equal(testCase[2]); + }); + }); + it('Should read a version 1 entry, which has no type, as a regular masternode', function () { + // the version 1 layout ends after isValid + var hex = '0100' + smlEntryHex.slice(0, 151 * 2); + var reader = new BufferReader(Buffer.from(hex, 'hex')); + var entry = SimplifiedMNListEntry.fromP2PBufferReader(reader, Networks.testnet); + + expect(reader.finished()).to.be.true; + expect(entry.nVersion).to.be.equal(1); + expect(entry.nType).to.be.equal(0); + expect(entry.platformHTTPPort).to.be.undefined; + expect(entry.toP2PBuffer().toString('hex')).to.be.equal(hex); + }); + it('Should reject a masternode type Core does not have', function () { + // a version 2 entry ends its hashed fields with isValid, then nType (2 here) + var hex = '0200' + smlEntryHex.slice(0, 151 * 2) + '0200'; + expect(function () { + SimplifiedMNListEntry.fromP2PBufferReader(new BufferReader(Buffer.from(hex, 'hex')), Networks.testnet); + }).to.throw('Expected nType to be 0 (regular) or 1 (evo)'); + }); + it('Should not modify the buffer it parses', function () { + var hex = '0300' + extAddrEvoEntryHex; + var buffer = Buffer.from(hex, 'hex'); + + SimplifiedMNListEntry.fromP2PBufferReader(new BufferReader(buffer), Networks.testnet); + + expect(buffer.toString('hex')).to.be.equal(hex); + }); + it('Should throw if the entry is cut off inside a fixed-size field', function () { + // drop the last 10 bytes of the platformNodeID + var hex = '0200' + smlEntryHex.slice(0, smlEntryHex.length - 20); + + expect(function () { + SimplifiedMNListEntry.fromP2PBufferReader( + new BufferReader(Buffer.from(hex, 'hex')), + Networks.testnet + ); + }).to.throw('expected 20 more bytes, found 10'); + }); + describe('a BIP155 IPv6 address in fc00::/8', function () { + // extAddrRegularEntryHex with its IPv4 core_p2p replaced by fc00::1 under + // BIP155 network id 2 (IPv6), which prints like CJDNS (network id 6) + var ipv4NetInfo = '010100010101044e1146af4dbb'; + var ipv6NetInfo = '01010001010210fc0000000000000000000000000000014e1f'; + var hashedHex = extAddrRegularEntryHex.replace(ipv4NetInfo, ipv6NetInfo); + var p2pHex = '0300' + hashedHex; + var entry; + + beforeEach(function () { + expect(hashedHex).to.not.equal(extAddrRegularEntryHex); + entry = SimplifiedMNListEntry.fromP2PBufferReader( + new BufferReader(Buffer.from(p2pHex, 'hex')), + Networks.testnet + ); + }); + + it('Should keep the bytes it was read from', function () { + expect(entry.addresses).to.be.deep.equal({ core_p2p: ['[fc00::1]:19999'] }); + expect(entry.toP2PBuffer().toString('hex')).to.be.equal(p2pHex); + expect(entry.calculateHash()).to.be.deep.equal( + Hash.sha256sha256(Buffer.from(hashedHex, 'hex')) + ); + expect(entry.copy().toP2PBuffer().toString('hex')).to.be.equal(p2pHex); + expect(new SimplifiedMNListEntry(entry).toP2PBuffer().toString('hex')) + .to.be.equal(p2pHex); + }); + it('Should leave the bytes out of toObject and JSON', function () { + expect(entry.toObject()).to.not.have.property('_rawExtNetInfo'); + expect(JSON.stringify(entry)).to.not.include('_rawExtNetInfo'); + }); + it('Should re-encode changed addresses, and JSON input, by the string rule', function () { + var cjdnsHex = '0300' + extAddrRegularEntryHex.replace( + ipv4NetInfo, + '01010001010610fc0000000000000000000000000000014e1f' + ); + expect(new SimplifiedMNListEntry(entry.toObject()).toP2PBuffer().toString('hex')) + .to.be.equal(cjdnsHex); + + entry.addresses.core_p2p = ['[fc00::1]:9999']; + expect(entry.toP2PBuffer().toString('hex')).to.be.equal( + cjdnsHex.replace('fc0000000000000000000000000000014e1f', 'fc000000000000000000000000000001270f') + ); + }); + }); + }); describe('fromObject', function () { it('Should create an instance from a JSONObject', function () { var entry = SimplifiedMNListEntry.fromObject(smlEntryJSON); @@ -308,4 +419,94 @@ describe('SimplifiedMNListEntry', function () { expect(entry.getIp()).to.be.equal('2001:db8::1'); }); }); + describe('getPlatformHTTPSEndpoint', function () { + it('Should get the service ip and platformHTTPPort of a legacy evonode', function () { + var entry = new SimplifiedMNListEntry(smlEntryJSON); + expect(entry.getPlatformHTTPSEndpoint()).to.be.deep.equal({ + host: '95.183.51.146', + port: 3200, + }); + }); + it('Should get the platform_https address of a version 3 evonode', function () { + var entry = new SimplifiedMNListEntry(extAddrEvoEntryJSON); + expect(entry.getPlatformHTTPSEndpoint()).to.be.deep.equal({ + host: '162.35.243.85', + port: 1443, + }); + }); + it('Should get a platform_https host that differs from the service ip', function () { + [ + ['[2001:db8::1]:443', '2001:db8::1'], + ['evo.example.com:443', 'evo.example.com'], + ['203.0.113.7:443', '203.0.113.7'], + ].forEach(function (testCase) { + var json = Object.assign({}, extAddrEvoEntryJSON, { + addresses: Object.assign({}, extAddrEvoEntryJSON.addresses, { + platform_https: [testCase[0]], + }), + }); + var entry = new SimplifiedMNListEntry(json); + + expect(entry.getIp()).to.be.equal('162.35.243.85'); + expect(entry.getPlatformHTTPSEndpoint()).to.be.deep.equal({ + host: testCase[1], + port: 443, + }); + }); + }); + it('Should follow platform_https when it changes after construction', function () { + var entry = new SimplifiedMNListEntry(extAddrEvoEntryJSON); + entry.addresses = Object.assign({}, entry.addresses, { platform_https: ['node.example:443'] }); + expect(entry.getPlatformHTTPSEndpoint()).to.be.deep.equal({ host: 'node.example', port: 443 }); + + var noPlatform = new SimplifiedMNListEntry(Object.assign({}, extAddrEvoEntryJSON, { + addresses: { core_p2p: extAddrEvoEntryJSON.addresses.core_p2p }, + })); + noPlatform.addresses = Object.assign({}, noPlatform.addresses, { platform_https: ['203.0.113.7:1443'] }); + expect(noPlatform.getPlatformHTTPSEndpoint()).to.be.deep.equal({ host: '203.0.113.7', port: 1443 }); + }); + it('Should return null for a Platform port of 0', function () { + var legacy = new SimplifiedMNListEntry(smlEntryJSON); + legacy.platformHTTPPort = 0; + expect(legacy.getPlatformHTTPSEndpoint()).to.be.null; + + var v3 = new SimplifiedMNListEntry(extAddrEvoEntryJSON); + v3.addresses = Object.assign({}, v3.addresses, { platform_https: ['203.0.113.7:0'] }); + expect(v3.getPlatformHTTPSEndpoint()).to.be.null; + }); + it('Should return null for an evonode without a Platform address', function () { + var json = Object.assign({}, extAddrEvoEntryJSON, { + addresses: { core_p2p: extAddrEvoEntryJSON.addresses.core_p2p }, + }); + expect(new SimplifiedMNListEntry(json).getPlatformHTTPSEndpoint()).to.be.null; + expect( + new SimplifiedMNListEntry(smlEntryWithoutAddress).getPlatformHTTPSEndpoint() + ).to.be.null; + }); + it('Should return null for a version 3 evonode with a zero or missing platform_https', function () { + var json = Object.assign({}, extAddrEvoEntryJSON, { + addresses: Object.assign({}, extAddrEvoEntryJSON.addresses, { + platform_https: ['[::]:0'], + }), + }); + expect(new SimplifiedMNListEntry(json).getPlatformHTTPSEndpoint()).to.be.null; + + // platformHTTPPort left set after the addresses change + var entry = new SimplifiedMNListEntry(extAddrEvoEntryJSON); + entry.addresses.platform_https = []; + expect(entry.getPlatformHTTPSEndpoint()).to.be.null; + delete entry.addresses.platform_https; + expect(entry.getPlatformHTTPSEndpoint()).to.be.null; + }); + it('Should return null for a regular masternode', function () { + expect( + new SimplifiedMNListEntry(extAddrRegularEntryJSON).getPlatformHTTPSEndpoint() + ).to.be.null; + expect( + new SimplifiedMNListEntry( + Object.assign({}, smlEntryJSON, { nType: 0, platformHTTPPort: undefined }) + ).getPlatformHTTPSEndpoint() + ).to.be.null; + }); + }); }); diff --git a/test/fixtures/core-p2p-mnlistdiffs.json b/test/fixtures/core-p2p-mnlistdiffs.json new file mode 100644 index 000000000..c851a669e --- /dev/null +++ b/test/fixtures/core-p2p-mnlistdiffs.json @@ -0,0 +1,24 @@ +{ + "note": "Raw `mnlistdiff` P2P message payloads (protocol 70242) from Dash Core 24.0.0 testnet and 23.1.8 mainnet (protocol 70240) peers, with the 80-byte header of each diff's block", + "testnetFull": { + "network": "testnet", + "baseHeight": 0, + "height": 1580498, + "blockHeader": "0000002091506f6644e3da1ec6dd78d648ac13b1817a61c923ec2af6154d292f30000000b9698e99cceb2b4e6fe2f4407a3e9667d3fcb4740da7b61ce3ae65d63fe14ed45fdae06a2bd7001e12db0000", + "hex": "0100000000000000000000000000000000000000000000000000000000000000000083c1bfb8bdac446221e973c3f520626ddd4a81fec93a59aef14830b57d0000000800000004a868b2ed8d2301e11fe10af03de99739daabf69fd189ea44fb5aa1c47df56f1d389ffc77b4ade36785589c1161ed70c244ff26dbfda7830e56bad2a9300d165bb9f91e0478b64957632ae2729374a2c7c0f386052c87ac24864baf6bf0f711c70eb605f91fd18b14932cb62fa77678cfffc9ab19d7990e7283e456398d44d5b0010f03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff016affffffff037c558c03000000001976a914b489115851ca07a26a5ad8bac3cec3c7dbebd83188ac2ed5fd0300000000016a442ba706000000001976a91464f2b2b84f62d68a2cd7f7f5fb2b5aa75ef716d788ac00000000cf0400d21d1800e852020514f1c94ec6ac3c55ce6a2cb8f1199f5391ca8d22be31dd13b12adc04491ba69a17d37dee7ddf37301ced78994fcbef01bacfb2fb14758a6a7ed9587b00859ce2bf11f430492ac6c7c7b00fa1086ec856988e210899b35e280c9e55be4d7aabc69d5b5b15b3078887d62b567ad00c7378b8d6f3f92c02f0dc2e3a7c78decae6e9285354a1ec342895ad04907552c9841d946897c9950e7b1c0d609b3c417af9a1635a220000000000000000000000000000000000000000000000000000000000000000000000fd8f010100a018bc40ee20e2d5f80be412d81e25890e38ddb4e9d2b15d01e2e786852e4185c5caa6fd8dce5f123e00bd92c553c58a961d86751a02d1a02e7e1d050000000000000000000000000000ffffa747dfd44e1e80b7defb6341399f9e9b4ed7c2d627fc828d0eff9c168165b75b24e5fc6c3f5bc8a9eeaee2bc655fdaa58c0d2f3b1b944b682e8847992b8e9a2531cef5a3169ea5b80c58000100807109b23de7d601fe818e7d79b74ee3cd9199d1e07fedbdabb2e1c74a9ef1349bd6efed873f4ab25cfb1a29d4b1ebbe6e909f64bde69e059f48152e0000000000000000000000000000ffff0314461227130fb7164d86058e2b22c4a6f6917714dfa4a2cb4d54bebbf3c9300ebfe1759b33d15b0b68e32999aae19bf0dd92341e401dc640b7e408921b2c99d18ec5bfee69e61d85b6000100c071fae501f5747d2e0259cecda4e40c06ea9add0ddca036de5c69bf77730c52042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a484e1f842d732a03847819b1e2675ac48b9af4a1c92b310ecacc42c428ff902099cc47d08ecd4616da55d185463855aee99f7933fe2817ece26659df352c7abdf87f8cb7dde58b010100001eb4ffa3feaf12495a2206608116feea16b2070915d56f8ba774df7398a9bb0f854345b4041af285193843258dd3b3c170c66401291ecadc9da135d00a000000000000000000000000ffff6b16c7824e1f1059c4fdcdc32d831534403f7e6587555d74dc1624f6e2bcdba10a099e6c4f8d5f31d4c270231180ded264a009387e6cdd3b064ca33829ca65c37e911259da50d3490ef3000200c016291f16bb938f9be4dedf08c461dc67f03346a7740d9ddcd3834878eb118ee026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a174e1f8160877a911d8bb7d1e75e2320e98cc3233c1f6972cb642424bfcec7c182c56d2c0ebb59e45f788f4d5dbfa2ebff3e3a0063eb51fcfd121e6c3eae23b90b26cd1100217b010100a305357cb7f558f264c99460327d48066e197c70576d01004037472321c1aaf3fab2ea768ba4e4f0d6532aba3653361e8e1706518d787ab2042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a464e1f00eb80b32b60db5d7b03559f6e9205beac8d047689904bdda0bc50987d5f208d39b78ed90a34af7e1e9d44495ca1eb422d0ac80af443e477060c785066014d561253d22d010200a047145aafddfb3e068ec4d899ae23c9ad3ccf908446127f05d49f9a9752b9ed47cf4023b16e6b9674087806016cb4c693f3251bb08432d75592daac2000000000000000000000000000ffff57e5167e4e1f82be9e928577bd4f1050a030418b083d625dbb4ad963494ce146ca818c49879dfec6fb746cae1cda4342b9ad442937cb9764876f34204cc0b83bf6543583ecee98d4a511010100a30504f8d328a048126337270c9625819012b06504530100c0a3e8f9461c4fce0d89476083fef0f16648c3be1e838a7ab016984cb050ca6c042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a2a4e1f0ad4f577d067630f6fd15f4d2aefdb9456d648b71cb7253d47511acc81dd5ddb69a03c848322aa11e5242f66afde5a2a94d596c0f06812174b31eb104a44cb1c94563073010100608c43a6a23324860eb2d8e6212798506bebac53120c591d6b32aeb0e35edc63042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a214e1f13f411bb160a34b3d8254e7c537e1300afed010d4a245e376b81d889020854fb999fe9cbb7430ddee0faf2fe5e711ebbb190542b5c7522f4db19e48033d17c34afd71845010100607007ddbe091843236ed8045a058ecf8b12db8d9027235e92d922593f275755ba5d468e5c009dae6c9b2a16d80a22b97b25443c74d50ac600d19fa03d00000000000000000000000000ffff5fde193c4e1d08b66151b81bd6a08bad2e68810ea07014012d6d804859219958a7fbc293689aa902bd0cd6db7a4699c9e88a4ae8c2c08fd1a9502c58ab103792693e951bf39f10ee46a90001008010db04bfaed8961840df7aba51b0ebd897cd222ac58ac8b1c89570e0d62fb4042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a3e4e1f088905cc3f99e76b3a1abf714a55978d9930c2abdc77a21bd809e452e8c47c35d38e318ec3118e1944cf1a4a8df907c1b4fb462bdbb65e1dc1380a1d4723220a09f3b4dd010200803c39eddd391194e9b5b003c533098e8261a6bd6ad88a6577efc4c11070ae991d774733fb0bb8f632ed3223a21c05a704a71e086f73762d5bc4ec820700000000000000000000000000ffff592803814e1f844e036dd08f2a973014023016dfb5fe4d533be55aeb81da956becd312345aeca4a41b168a51b87ef5e42c3c6892fce06bbc79395663389c2864b7e423e111b9d73b33810000000300800b5c82dde5ef1694b17d0a93dfd03ca8a49c1c31512457e78396f79fa23c76592611046b94ce81ef960d2e026aa6deed97bd6c14ec634d2466fc231001000001030001010104a223f3554e1d0101010104a223f3558f300201010104a223f35505a3ad99da4a07542b221fa6f2335ab2a1071cad6f2dbae230fe82a619b07d039b6270632272ea7269dc5c8241eef139f57c6fc8eacf785fce62cb493c3ce335a12ff0596ff1010100c92e0ebc2cf6df05de73b3a9821e9c590420d03302008067842697bc90d881860908be2ceec1f25026a56a67ae18a3a939d730d44e609a4498ad5a1a0fc61ef0c9b3ecfb0fd1a206086c4ee6b4b9db89ecf37300000000000000000000000000ffff55d1f3034e1fa692633b3f7c4b342c44225247f5ce9358bbc4d1eac4d13a971ad6c67c151d6b8c0920d3a1e9cf8143d38a06523850ded50b0033a1380315eae2693bca07a289babeef02010100a3054c2e06cd1cae739d4fc613ced41f8f184ca8e1e2010061d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a139744f95133255a8df4bb58e36cdc6e352c6048633caee29608346a9370c03000000000000000000000000ffffc6c74af14e1f0efda51589f86e30cc2305e7388c01ce0309c19a182cf37bced97c7da72236f660c0a395e765e6e06962ecff5a69d7de359c348a574176c210c37a25d4ffd917866fb0a3000100c1b4efa5fe55cfac7b9841af3161d7b27a208f29e5eecf6cd8059b1236b1fab9042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a424e1f0fa5377eb256323aace31b45c3e48ea110404b053cb80e8043bd1e44de1705130548e4ab28738816251ea57a7fc10324289d0729f182f703f2df4990b51e2b103515a32b01010041e5e9c68f8c61bd44136b148f8a196ec435045b41b55058f8dd5f1a8bce69053664815c5b2f45be780c5caeb2e7d3c840283097e4fdd1d499364801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005a3086c1c6e57c19d3613870421059d308d5b16f000200613da7325ea2dbcf6dcefd15c14459d5271836678ffef57166b79e7308f101ae6157be4e38ea989fab79a7c00f143af7d0f5f2f0b32b9470c041f9974900000000000000000000000000ffff8b3c5f474e1f93dd670bb8e78f365b13163ed86f37e1e794c5238b216d02e8335a07a6a30da5065fdd4219909b37c8349fce149c17ec7170c7f963f11ad0f52e65065c54d7e66ae6b1540000000100c1a555222b83dd986987ffb354f541e7696d0dd6d9f28fa9ea4cab5dad785fc92928c180cef0f3ab90e609c93db391cc46e909fb4772d36243465d100000000000000000000000000000ffff2d2056e74e1f128e20333b8b51fb8c72c2d5acafa049758b53279bf78f10a1f32995bd05d4f6313b2bd67fbc48379455d89ef869fa6e65b4b2bd9e302d32234a9bb205db6e51c08767ed00010061d2a534d50fa0c224d58bcb66aba349a11a2b016e3c08ccdf72d076d339f2236e487a6ed3d93d75da4554375469b3894319255bc718fafb62d4a3003e01000000000000000000000000ffff50f0182c4e1f94b3e11094b8781908d100194fa8b47659c2ee17720200f9fcdc2804c557d219e87eb68fb0e6db97822f1f73ab7f846d1516fc10404fc6199c750577dabb9f26800c8652000100812269339ace97eb8839f491bdd5be89fb9f5f1d455f545a1bc6113cd4f8e29fa5059b5cf433f5b3399486d9b5794d84bd2678e2e1678a762707f2010000000000000000000000000000ffff4e2ea1164e1f80174252e0f66a71b7e53f8b32dea5f97a6b39dfc1479c6e355daa415b1ff7733a4bea6bbe3fdf412fed0fb60e5b71d77ec334a85c5cf331176646c2c357732ec06eba39000100c10a2826f3e775d2d7527b131d5e3620647a6926938061a7ff16b7d0b745efb426cb9af0c2dd8555f03dccbc1f209376c344f2a89d021dad06bbe6aa2c02000000000000000000000000ffffb9bba9c14e1f0450dbbbe82df6808151b83a46f8c531cb240eccfe65f8f0b49f3717056da7268c14e45f0dd14fff8daed28fd353c1b41546085b56803db4366c2059d4755c8a28796ca300020001ffaaa508180ffcd764acd0f2f63409d60562533506a1112da5bf112e507a71dd32b3afa4a2300e5681811768ed8e0c7600bae0f6476328d0b18755bd00000000000000000000000000ffff40b032a74e1fa90fce30ea814b244dc767b5d29bf227842226705a0bd2e8589e776a4dd113ed3dada52c6a07f55be90173d6431d8f34b6e879f9bd59a349caa9d490f0bd51988bb1fc34000000010041d72a6a7c2ea07deb4c70e47dbe2842850b0298fbb6c69cba57373cb74abab4c058d2a5ffc21a38eec0bdc42158792a2b85fda96535619f28cab109000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003db6e109e8d2e2f052216a47568b8881f7df245b000100616f1a973ebca29263147f716fdb4978034079720a88350beac0bf9b9fccf838042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a334e1f897fc76b69f1ff4b06535e7a4bc7396fb66b33194effbb72214dbefc2c7cd3220ab6cc39fe4630a513879f9f8dca27e3ea1e4a53ffa1572af733c63b530860885f9c8ad801010081b39cfc075fe97a75d23fc6fc8b0bf9be906392b04b228148a5145403a229f17f41682e1c0331bb55c8df2555ca522850d657f8b5de650f08e31b030000000000000000000000000000ffffa747dfd44e1f84657bff1dbf81b2aa50e385d01549f9c3683994ab0b16d5b7e3ede8efe95992bb621ec221c5003d2f9f26fa190ffb2ae21d3baa7509a95b0a9d741549047c2e90ed8aa7000100e14bb33180ae655ddddea98116ea3246ee568a5c130e8af39631f10916c258e56bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d413476596005d1f334fabccd08847756effff3116eece973c077e3acd1aa936f4e51293fa8753de661dc7a03edde24714eb7acdcd18fdd71cb9d2d0a4cd75e08191dff37fbdf446500001002129b96ff3d6dfb88b0266a0958f3f7fcaef4c7947e8b54cb26d8ed31fe8eec6906648c5a900b80bc262dd7b0cc54ed958baccc95d135ab293bad9000000000000000000000000000000ffff68f8f27e4e1f050f3a743867bf78d2e9a3906d15d8400d8d58255771d12828922386e8685f8aeccb8d9d81153f9c2d7da0436a71fe5538130b3e02c119f53aba29a12de6be40f0c6596f00010021b1aeb48918d1500c6da4c963b6922a7ad8db83b62fb99fc755c4cea2a577bc042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a1e4e1f174de56654f2bb6417e15ff06361ea0becc00bd72a3eba0f83b60feac860570769fbf28482a706f10906a1e96dae4a8f2878a638db0682d49249ff8ca872f16653a84f9101010021d15df373dec8ce033a6d43a6f0e2369e3995d3592c6c295158e9a99e32b4c4042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a514e1f0196970badc74d068ec1226ffd4a656313decef59d792237a32e6ff56cd4e43030c436025831a4a3d0306a616f03381020b99d09dfcc0f9b69fa97738487f20cc6c57868010100419aa9fed4f35fcb986c50fbaa0c7555c68f8a9876968c63c6f92064ff06f1fe7b34629e1f9f156e4e3720ed76745a9966adb50fb513f1285263d5050000000000000000000000000000ffff2d30a8104e1f842476e8d82327adfb9b617a7ac3f62868946c0c4b6b0e365747cfb8825b8b79ba0eb1fa62e8583ae7102f59bf70c7c7ce2342a602ce6bd150809591377ecf31971558ca0001004172e5a561e36ae49358bc4c6c37ff688f54a05ae8842b496b86feb71f06b886bbaf9ff7a4ffcf3931de9233fa8e151f187bf30235000e3b5fb102b01200000000000000000000000000ffff6deb47384e1f8d1412ff39045ef39c2e19a75cb3ad986afc14c3139ed0a3392b41d471558676029a8137f95b0ba0e7315bf11c497f0fc8270f9d208c75006659cedd927f04ccf829242c00010002d4fa244fbfdb6378f87fb2078363a01305096b272031eaf2af2d1e77ac8fc7dd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102a8370b2b437e8e4750ac05a712c524a6e3c5900010062f0e57a0772b9313555c77c1234aad8f8dbbba68402004950876c2c99bfa1dddd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e0b2a999f6d3d5180bc0a7cbfc57886b3caeace00030082607466905f4091ec6db6df160559605139b246282485a548f4ea38255da1e6c7397e9cc7e7fc3906807f77b36aa3e6389652e4f7771feafd84fbb10e000000010100010101044e1146af4dbbadad41e17d64581f58fcbc8dff2845a9a10c2e57db782dc5fd41a175729e59048dc3fb82d9f0620eeaac3eacadced01a4d29551a50fabceb0c248be984667982c4b7525d0100000100c21c409e1a11dd13d2f81877c63b0c802e3e5b860e3ae43dd929c000374f8b18c66e783d97bee0c61568799866c19bd0772fb1fd7015d3e9552565180000000000000000000000000000ffff761f230d4e21831193814d5cddb0268d276281ff7356b9cbe560bbcb6c9c55f12a53b0dfdb60ed5570c9c4bdd39d8a0728dfb2b0596fa7755e80caba03e54606c89f955d1d4b9b85d91c000100e2f8c6900028cc807e87e3c4d09f23726da5388a83e20f7aa8d23e1b80ca35f725c1addd1ad4c97ac677405576e385ec630d4167f1fdd624fbf2ac030000000000000000000000000000ffff22da81624e1f8cf9b3235f77637f144728584ca13d1d3fd47450ad392a510beb2425e0d88f6a3354f0cbdd26d4e6152d38899c025aa365802f3ece78b624b0739a8002fe35327ab85b7000010022aa9381fd5632124c682f1b498ab52bb1509d7d307e9c53b35daf1dbf9ffff54364b2b68b5020a930796eaa75567538ecd934cb5d371fa8ab9dc301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005bd941a8832f305921d4f2bdf7946d8ac1b6b379000100421684ace5bf21173abee1a68c966880bf93d60f17683e2b94c1ed515d5ae23ff3bfb0d7a45eee0eac355f2c8b58052591d53d96f193c0af1bc3380a0000000000000000000000000000ffff86d15a704e1f91386d3acee0bf9044cce40a07515289589b68fc9b8c8e5d184471ed7982106b1e11587af4c9e983883baec00b67e473d71dc86bda7e222f78c9e45f22deae9437028650000100420fad05dae1e8c22d9e09ed7d02b191b8cb8775317feb4ffcda3c73343c01176bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765ba836747d419d09200e404aa3500fc5d51f044fc01fd1a9f452324c8c14ab90ccd75887b6bae1599cdd458e738a53587f25caf3605e05011b3abef72e467ab5334e74d6863000100821b492fdb5e25efbaf7dbaa82cd8aa9b5227495b3eed3e323cf92c633058dbe365d7a9de501086dbd238c8476092ad180186b88aa6616ea530bf5070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000091b4363730ea1c02de53aaade78508ee41c38f9c000100c2db98a6bff0f6044e212e766ae31421d43ccf06d9831de1d61e3ad18c0a1ffb042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a494e1f02ce863d0843ca66b4a64d94c0d84ec15980ea04e4444ac4d4188f38cc0da4d6d2360b8a2046725b682862255af6a48c083a4e9632bcb5c6490a92b3c3b05979bcc866f5010100024a3ab7c146c32be22d3a69ea33dda2df3d31c636a93ff7f6d87e78afde0a1d042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a364e1f8b7c76ec03f7ae0dc9be41fb9168906ef0d0d4de74f0ad8c5cf0a30483879b5203ae5d7c6aeee5b92998444bc10f68ec68a589741e0d3d06e5aae5ae008f2a8b11426a8c01030002769272e89a947728aebb2b1ce38a35e47ea334ee53fe29e8a6734cf439ee0c042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d8450000000101000101010444437a354e1fa80f24b5e040dcbf86c3f468dd28bf45d9e41fbcd127fad56669d9afe358dcdc26e42f0f8b19997b1741dbb99c553aa6b73068d00c048915c175450c366478f783bc455b010000010022a7834ea2416d54b0fbe309b4eb38a7fc2e5d17e434ba394ad3091ad9600ada042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a394e1f1099dcddc6560d1039b0edb91bd700e5deae0cba43163fa289a80c2bd22335b5b0e7a1fb8f5494c0e6360e73a12fe0a89c14a51ad09e0eedf4d49b11a779aebe5755a4e801010022ff95f3392f9ca21be3601f0cc61989aff67ab0f08d1f8d09ebe4f1b984c1baaa2b5033568b70cec7062265924ed182cb5606dde746099e31a2b7020000000000000000000000000000ffff03dd1d174e1f8bf25d66d63197e3144f6fa17ad92ae38cc11b143027fb91dcf5c20fde6e52bde7f46f2789e6fa84573197d8085389dd5519b11029baf89428cad6fd4e70e3bb09f356f100020023d8aab98a2e8f71a40887665f786aee6caff71abbf4743b9afcbd0a00ac84430c5abfbe03b04d75f843d51269605397a38bde4dcf0a1ba7d10abbfe0400000000000000000000000000ffff55d1f3074e1fa08769d8e414245828d46b3411da93c2c62188f6e5168b0b32e6a04e5efc4af076f6792e10dbb0b6a3896ed77f4314fb8c88081bb1e0e81ed8fc2407330caada78ed3a95010100a3052c77bd9a6313ffa39d5c2b14875eb0fcca0cf66501008384b2330fc26f5ba5ff492e15006e42521cbe351ed42fbc45e4d17320d537b03fb1af5817b316a19cd3aafe04e5c0c499c45eeb613b75ff8865a40b0000000000000000000000000000000000000000000009a5e37e3b9e556a7d7fe7cda1b54682351d4d1f6ecd331d98816db0696a5389c4a09bfc57f66f0a27120302ceadb078bf9ef9d3fee586e471698a3f70eaff9dcc96ba84000100234524f1ecf913cc326592cd1416f67ce1b4c36f58f30a6984e11d8f01e16708de8d01abe32760d948069962e3d81b37acd5933f8bf716a4411169000000000000000000000000000000ffffa516d5954e1f881cec3eab18eaeb916d3f234fae24363b68faa705f51a6efb06f83adef70f73fe28ee70bf4c8300ec4f77f017dbf7f06de6137f220482eab495340caf21f19261b1e84100020043e5f7410ba48e35d57114fcbb98372bc808948c6c25eded4efaec1b8ca69405f37d09c1ebc59238a7456c5b4ce03c408ccb096f19f2378adf9ec97cf400000000000000000000000000ffffbc7424a14e1fa4dc9f6f159977565bbde1257b7bfa52cb0e7762ca2106da425074a1fc821e1c1478f5f1989d4cddc14d0a65eafcfca6916d7ec6890496e40ec4e7903a20183628e63bc30000000200838d0c4f3580ff5602ef3e0bdb8dba2247a56cb1557d43aa1fd1f062e7b598b6af237796d18341710fd6ec7afcf10a1a9ca69e53184059bddfcac81a3100000000000000000000000000ffff12c3324e4e1fa90febb8c2b031ae7ca222debc10f12fa0a71fcef84e2d92f13c1ad192c42e371324bbb66c86e24434754e2864ba14ce2e3252f64967541fc0092b78aac3dfbb7fdfa8d20000000100a3ddf7716c62a29cf5f259446c51122a518b7b3165284ff8636b67ae65def9b469ee9f2add6f18c3496f5dd67566ce6b0f5de80e2f070382e51285bf0d00000000000000000000000000ffff010101014e1f016a16472319f62f71bb60e38038aa8cb93a301ff6c3727f75f4d770428d71d032fdbd27c5d03dc56ef1d658fefe79546960122148b84c5f044a812c35d5380281a0f02e000200c3357bf95fb8fc8628840460745358db1d007bfdd88cb2be27931e07608e3bd109e6f409a21638e9a82ce854c58593bf70c73b515becb820bc4bdf980700000000000000000000000000ffff2ee029ad4e1fb90302b52893e2ff51e468c2ef2e9f42507e71c6c9ec43689070acb15a0caa1952ed0dfa659cbf85516c794111bc3ccd9cbf4a0a2b932575285a85c8475247e5063a551a0000000100e31d716e9009884c5063f5e7c751cd3f03304cb37ee37f43f67382f3d943141f70f83517c7f6c3bdaf58572091885cc2673605cf36b247ef4db70608000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d532ed2827d7bea5c49ac0e1cf57d6dabe11e06000100231e411943c6d4826d78f6b6f8d227f4726eb19de7edf25da1bc88a159caa94b19af5aca73f9f272c83575f05d7f7995e345f9255cacdd8da7938a11f101000000000000000000000000ffff0343bb9b4e1f8b7381340a3e266498248137c146a3c82e36c27c196bac05772dd7b22132912bcfdc1263e2761245b6871b47dba982a61f69134183fab7568ebf7eef6c97f80b3ed603ae000100c31ff5109b565396c664717a5147bd7eea9eb7ffd29541714ecc2c4f26fe51758913680eb0236b652eb1fe84b9535517ac863d4a8a2527d21b3bf4000000000000000000000000000000ffffa1bd43194e1f0becd48c0d44ca6fbff3825f55c35a6f70024f2b8f4f939260d40b5b51c11cdfff85f7d0444a1a9cb8fc45bacd237b31abf73c421d3d73583ebcd9fdf55fe8b3013a82230002000358ffd0b6835dd812312e75fb93156b95ffb4faf29542ca8c958f9220b94c27f0b601148b421813e1631d897f85b9e6436d82289d42ebe60cb2662e3f00000000000000000000000000ffffa7564b874e1f9561a686a3a58768c0792d699480035d5345b95a8030cb3d5fa5d2e7dcfbb6a8d5371c75e166a116c1c66542de2c4a6c164d2cfad6d706d26a6e5166aa600eb92eaaa993000100a305117786967d8423c40aa52b6c68aeec3b2bf2133101000378aaa2e2ec71b47ab267aa9b55ec5422740471ce1651c2aee7cad48d8f0f61042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a254e1f90ea47f22be1644834d8756793f2308f2c5b40afd16ebb98d29a3bd37e437990d4d5930ccfa56c1ea0b4e51d05a49f23bec1b55112fe6dee11593a3ec19e9d712855521801010003aa33e3c8829a5d40df1f3006d4ec8772b0dcbd1a36995364197630bd7901b0caff9aa8378b4e090614186b7f273f20ad3266498c4addeff5c72e1c0000000000000000000000000000ffffc38d8f314e1f878386a8d07cf79e1dc6963d4cdcd7a4af6ef7e350cad3e1373e45fb86fdd9390a77366ccffda6ebe1470c45b0f75910307e6b1e1ebb35dc358870ed74feb6563617e40d000100035a4baea91732fc217a20da6b7b6759e80f26f09383def9794b0cee1ed0803a85bc5faf11681e1eacd93ac4a807fbebc818350b12eea0b49b04300100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e3f6150f12df79d8bf1fa5513edcc000b93d388a000100045480c439ccaf9f38afff4a07e8a212735cdea7e7e8f2511c0883e2583e2b68ef6d852f7e1d547e18f881e4cb053d531b046c0750a8c620553524c81800000000000000000000000000ffff40c13ece4e1f05f2269374676476f00068b7cb168d124b7b780a92e8564e18edf45d77497abd9debf186ee98001a0c9a6dfccbab7a0af4aa6fd6b27d9649267b4ae48e3be5399c89d63a000100a488a1ca6fd40ae8a170928c232ff2109d11338efe218447bb58ffdeeba345163c305a87ab0207b41b271338ce0912d0e27ac116b69774f014b7b0ea1b01000000000000000000000000ffff68f8872c4e1f0a5c47983c44aa99ce5f04b32cfbdff42e7f92b1410558559dcbff3ca8aacc3c2fcaf05db6f021a9f335ba05b9af4c52dec4f1bdf91c23a8ed56738a32f2795855bc9827000100c4eca0c58c7f6be21f57ca22938b405aa7b70d1d7670a30ce00d83c145e6a0d332fb097e425849c5ad93ab25b92b58f57427416f28fac2f9c5d7fa000000000000000000000000000000000000000000000005b7d7aee629c25efc5604104a3a9af1e23663464e0505a057e68cf12317834160597fcf80287a94e98f171a8c79a2a91e88661c273d73b44bbf942d3f1cfc145539969d000100249147dc56394eea19a987b95f6088bd88e9df840e4c7989e2e6a6520bad3e1fb180a8ee02ac30f1609b32b33d9b48b93f838f353eca68fec5ede3164201000000000000000000000000ffff344820cf4e1f1641b24598bd24e49c4c2c59d027567d89f2e7315e53fabafb508e48a93b48f32525fd9dd9266b0ca4bef5d08b9605afe1aea521fd5d9c90c17704c6a7b653c6fdd42ce8000100a4f5cb0858d3831a0a4c38d391a9d9c1db1aca1cce2ce7cb1b6ed87d4929fbed2cf74e8cf4f70cbe8d4169db6e5d941815bd64edf919b434b1286d271206000000000000000000000000ffff0b7a212d4e1f8d052595c653122cccb964230a5404399634e7cc6b3fa9314b54678c28d2f9c4854baa7be02845937bb0de35e43cfbd435d72ae28122e666401fbc5f914da502815aedd3000200c4dd9a97bc37a65224b5289ff8bb875d17c2f1df4cd07b828d05f75229b1e88de026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a144e1f9502bb884b3437d65c0e025e49fb00ff6ea9f55d5bcdc36330b46c8bd18be9126b7a6d7f35f558ef8040f2c2284500a5300904176cc6023370b2462964c12c5979b9f411010100a30502b38580015433ac23013e5c8231c5d2b57f7b33010024ee4b80978891cc74cb110a92d09c6e2013b868bc0145667fc7733677cbb8eeda1a04a5298ec6d6ab7e4a3758875a820516b2ff90440c3825a6d9120000000000000000000000000000ffff030d2293271104cab5bc1d73f5f8299feeecc0bee2d76f27c3b2a56a7e2fc1f927e495ac9b2a0560b7d82fd06fa8fce4af69d0fcb10b87de47ade017f67b717616f8bd4f5fc3005dbe8500010064d23aab4726b9b743c705cf8a9349a438f43ef9cc08f93e0c36f9656b7cbb7cdf54faf1598e2513bf8b0afad7bfc08a361655c15109c02211d6c2dd0600000000000000000000000000ffff68f85c624e1f136de56a265eb21c006bd312a0353c7c3eed46f4f63c301c348fb5d5de8f965c9b60ac6a4ae805d0d241e4942821ed9af46b2001f6dd71cf0fa69aea9f5a71ff6142302400010084ca5074ce4d40b329e07eb1453915553fe207f63d79c5626cdc801746b1eef3042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a4a4e1f90051db915bd86bd938746c14440b11ee3b2801cbc6d6c1c912e8b41ea5eb1d8f852abf220ae91ecdb6da094846c1ba83ecf5e66d9c0373f20f86d3fefdce2165c19c7be010100e486f4ba0e05313dd7066a25389ecf44861451bff33cfc639517aee4b8f7b3b18def264b1ab32efeb22ff1bc8916e07072c3b2e5c26d9043b42d3ae55301000000000000000000000000ffff6ca0877f4e1f0d7a075032c423dfd82adaba63256db7c7a0ab10eecc99544fd628e76840759ce5fc0f27ad83197f464371a1b3530952c61ae923d66d3e7a9a82b85fe5eb375e966b1091000200449b34ec269e1dc2e636b0294a8ad5e0a50d1b6c66ea4b446f6cdc84113b2df8ffab1b00d68805ade7922061be4b91e529316df5ff625cf31bbca47a2301000000000000000000000000ffff037ffd564e1b8458274be8fddb6b8685d753bb151ebe32a9021fab91228611a81c3c70b287b607a81388b46aabb16518494f912777667d354fb6b06f9f41e73aef311024f576db99e6c300000001002438facfc3b253cc62375a88d338848ab45f030e882547619efb302a0211be7aff9bda40e8cf851d13a4102465ca1a74f0c741e2d48dbf290f30ce060000000000000000000000000000ffff128891a54e1e8edd5cbdd7b381c92ac7de638440bb1ad417af0e82fece69432f36930a6defd3faa0d53d79bc3347ef684eb1e470abbcd6f5f660f9cf13ff1ff56a13042be5cdb214c1c100020024e85b1daec424104cbd7808e20c11661ea20f1a2ad7c1a9938283d3315af185e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a0a4e1fac3026b3e3023db1db9ec8e3b7678761820a2a6e96e7a5d9a39b1894170f9cea7765d3d131d60fa9d17492ba560fb1f90f89b9ac5ac8f31255f692a3eb0283d0ef3be436010100a3057c771e4de42b161d0c869c38a8d11638d406c56801004431d5c73ecb095daa699363d0a19e5b6e549414e6608cecd1188dbf551a777f87642a37e81f3d63e25a4426e06087954c872fde1894ea3c937517060000000000000000000000000000ffff2f4b449a4e1f842b8e5b5cc0841de193f440d5fa3e0b4a34df7fffa798fb8c3df46fa31187162cc3b3ecf929689ae35e04cbac6e069ff0a5dd8f973cec456a2423da87b5f1aa97f282e0000100444947144b4830f61467feba8295002de6fb48bcac6eaef828bf708165a412ee66522efacbce81bc3a35ea83e03b7fd2e8ab1b1025c5bec187ae60119200000000000000000000000000ffff34ce62c44e1f121a1cade221b1eadcc0c7a02a03508551dfb97b959ae1511d4cae47b503b39ba0fb37b984e4010164378513edfaf0729a870b5d4ccfe0068e92a6cb4cd35915ef94df9c0002004451b225cc3d73931b2cf7650a72ce22fcf2db4ad465f30ca635332b7d70a30b8dcb2f4f1e6b808472e3fc481e98887117c14b2e476cff4089ebef491a00000000000000000000000000ffff2d5e3a614e1f8005472acfeff13c058fb5dbb0eb641c7e1efbdf590b61e149d5331ce5b12c10e42461b8b70f04c704557348b3b2a3e4d4788322314e747c9aa7a74bf4e08e0cd2326227000100a305d6ca01d5e8bde42836d79d397fb9cdba0b7174360100250dccf685be96396e92687a8ce39e4538751a900a346483aaa9223b778c0cb083bbfdf4db4b783ff62c2c387a1e7ab3736fcceb26fe90019ef1808b2c00000000000000000000000000ffffae22e96e4e1f926eed90600b93cd05453899e96db8dfa36d2c71c3209e1660738c6b3af11473b5169c8f8dfacd89ad1bc92a481978f7ce7f101b7f075273c892063b1b8571311737a576000100a595db826aa16bc6fd6665b7cae78966cb41f8a25f9bcc818ba944d245c1d5b5387fa9c9f93571bcdbf2a480460b3010a45d6d969f6d488a877335000000000000000000000000000000ffff336b04264e1f8f4fed7576bd1e31d45225788c1c96836dbc85b3ece3b77fbe4ede0f5f784f138ef6b32884e3345915a758364d1e58232e1496064dd3a7a8c0a5cda78f3b4a4073ffa2390002004552c7abf240fe5ebd8ecadaa434f4d814ffea5c6865483039389447302d53375874898442ad3b18469da9a1bde26a1f93cb74d06ded25f4792861136700000000000000000000000000ffffc38d008f4e1f90252ab3c93815b60fa87f1cba4726a20edd61102c6b0881170543c01af39c3d00e93c70c83d1d8a98459cbacb1457993ea17b2051a9a9c32149b2732da594398fef9258000100a30571ca4989688a2698395214c4e9fcd689a862ee060100c5ba9d7534d9ba000bf5787804c9ecdf53fd457799dfe7ed72e4bf34e76de7a69906d6e2a830c62dbecf4a41efd17923b5d8e382d0b48be90ba1ff49660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e6bbd756d65c821406c82f5d520c000f2d562e30002008527334f080d51fd2b6b82f562015701eb2d869a26413d2713235c9acc809f95a3e1f8fa60eb5dc2e599432228a114dc64b611edf0615f28439400365800000000000000000000000000ffff052d6c124e1f89faf4977b1390702a58db649bd81cd61e8f8aa0d2e1dc627031d1794da234088c18b0b4e31ff49aecb3d05635bc4943d463adb52e6944043ed8f88956e94903509b24270000000100e5d44bb510f7522960e5f5fbabac98fc5744665cd53e817fd370b3d89769905d042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a2e4e1f155b2a1aa71cbb3b5fc81b00242805a6d573826679a2b6a5c49bec714829322efe8af0c1640ba3b59811c70e17b488a5431c84baf52eb97d5546ed1ee44872c30772a30a010100e5644d115172b5b59c92b110d3aa9c816455b157fbe4edb5c04f133cdf3e6c760dfa4668c81ef4087103d204ae406f37d7a8e591c894bb06ba126f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008fce46d5cc1aec576dbf5ba0ada049a2c2c64fb3000100a60c10dcfab0748f399153c47909dbd55e31c02aaacef2f52ef01a94f6727d61042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a244e1f067ec5f7cb5511ba2bf10aa09eac4107d76fd53edeb2fd94edef4555171dbe3ed7dc6cfe37b087390af61a6aa269182b17acdf555cdcf9dec1b41f2b44511123917d2998010100e6f0f87be3456dac022e662e98593bff81f97f0000a0d5b06d582a0513c965a57760f33a17bac779a5397b8b706c4ef23aecfa0aae9e1272884044000000000000000000000000000000ffff91efeb104e1f01e584b5723fec78495744b68b971fd654f16b016d676ffdbac01b2c64f319675eda577f5eaa5cf5379e95418c61ab10d2a33a64d0e54ed860973f6c22ad36aebba1dc9e000100260db6fd104668297f032b538d026a68ea82a592d6e8f4a11d992002c156f1696bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765e68e72ce4ecb7e37c0ba5376c77ec364606b796eccd05d80583a36da42d57421c21d3ccc3b3105ab18f87901e03ce09a0076a035432f549ce35f39ae28b45f19965340d82a000100c6f98324c39dba83ba0d461f61c3dea1471debb903f83eb4d8db792fa6ce530bd1c310e4699917a51541450b5d2e7b8f56e2267df3bd1e10da2352afbe00000000000000000000000000ffff5fb3fbb64e1f90403255a5c2aef92a899cf01080a78446f07e0a25fe391a81791c37eddba6e82aee9b8b86b7aa4f44129637146221c9eaa8b45af937c2eefe766f3bc3ab753329aa44380001000682c86d9afb864697190081adb9b1c4b0268b079edc428e41368fcb1655eff8042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a204e1f015ae7a4f88fd79e4659c4b24b32f24d1e92106b867a2c23d1d084cfedd0e2766edd3f0a77f274acd4d1d53fb1ff0218baee5acad0f663430ff83c4e9b9fcb0122789520010100e6660e41ee6fab97c87e4a018aaf9b830a064aa2968c5cc597ad689e7566841be4691de8074655741a573ebe01c47f7486f7720d56dee1d291c3826b6d02000000000000000000000000ffff23f704404e1f0b0a6390cd90308c8b7adc4374fee6d4c1d0f467d543dec6e306922e7e78d06ddff70e6adf6b2410cdaaf0d7fbab39ba46d82dfecb82e98abd5b694ffa54558168fa593c0002006609326328e57347b000ec6a97404e0c9af6aaa9057ea744cdef36a05b181b6de026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a0d4e1fb8a2161c64bfdc7d621df51de569911a219f718bad4d6058dcca9bddf6696d43ddc4c1e3cf91640c93f820e5680efac3f3a83d259634fbfc3e3c4ed50cdcab6598fce20b010100a3056fee3e6e0ebaa195bbb879ab6ae761ea16f074c30100667592fd071e8e17bd8c9bff238c14a0d69b9516759083a92a6801b54923691c09ab803afcb97fa1b0dced6b11ceffc2a11a7411f4e71c5d2f9dbf833f00000000000000000000000000ffff2cf298cb4e1f0923d28fcb1fb8b90bf8281f2f50b6a109f2f5b17a4e1653e193ae58980a03b3538ceb82c6b4e1e986ad40d08c63e330a164232c5ce6f1a1448084d628145eedb09f0147000100664a0c663eede7e26fb443b4cbef28f79e5ab6874568557cb79cf75e55753e3a69f94da29881541abe9eb79ceb5ae5fce958a5d880a1250129f8ac46d400000000000000000000000000ffff675076154e1f87a55d353f1c76f34d45486140b3242762e03d9f688b7be28be4389f552b7a057e3a014f7f654cf7da7260b5ec1c15e50aeea2780fdcce25eb1ff50c0edafd09e0397ec100010066f63efc54784de714835f1199821dbbfac1c49bb5d836ad6ac7268861d861719e4f98209ac0950b0726d009a9922e8b7e553876475a191ec0e07b220f0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009676669d0daf4a6ac06239eae91b43b4469cecb4000100472419cad37dfe438ce60cfb70b9156af1d2bf6e1874fe958ba18f229b7af37b5a575b641b2b69ccc7e60e41ef3e26c390c870332f599357a13776de8900000000000000000000000000ffff090807064e1f897af9fdc7920426089efaabbae8aacd61ea4306c0a2c89b140c9d3a69a56084dabcea352d2e1ff8e1f3ae127313e989e6dd610e8d417424f55f90b5c468011307051eca00010087f8bcdc3641a1d3fe3e8776e1c123048cccb4d25615e2e6213fe62fe47f1e96da370a6260cdd4843cf2eaf1c5ce5a4d5de95cc9d6280d7ecb9e97291d01000000000000000000000000ffffa76370174e1f804fadcd7b5dade6f9f577fe663cdf86f1483b71e6fd8e7c5cc4b981c0ee086412b16c796ce8fa3f7b6445fbee866640a656c3104b5aa2a042680aef3494b62eb3ac3e7e00010047f962f47fc21cba4c4256bb383cf366089eedafe639cf909831042679a7be0fc011aefb025f52be8a8cf9c9db5e521fb8164cf7df2755d628da850400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ddba67c250a9c4121d546702cc9d29f65007c8550001008789c5ef265771c98bfe558dccee7987198a1ee0a63acfad19cf50981e4d4d44e61028f0847d8c16f92488990a062e651e50bbce2c1e6ccb7de538120000000000000000000000000000ffffcf9af29d4e1f958adbe9f954ec23983c4be5788e86e0df30fed1d6852136376b49c1a24e0fe1da1178b23dec4ea098b9e355aba8de0b3cc1fe7d3423d6a9a847723ecdee0368320171ad00010007a61deef4c6336a378b05c9af5db085afaf8daffcf97fadd141f1e6d77fabdff8ade1d5f841bd4e0ba3e48bc82ce6bdfe6e68396c8922be00ef7e000000000000000000000000000000ffff91efeb124e1f0b4282cdfe1cd639e60b6c58b2f210bfe6b57f8f247cc5b55673d188ef458270c7314f7128b286a3326b9ab6109bd2ff4c90d0d2b49179ba1311496241623928b4e9bfc6000200a75a0705a546822ec7adc984695d7232d70ad86c9ac7fd93e7a377ba60fa7cf9da8038fb786dd69546ac46fa8b5192928b22d3ebfc57bb00a7db0dea58000000000000000000000000000000000000000000b925506853bc6d953ac37321d0582ff6b2541740955eef79d0e41ea8306514931943f78cf7a5d438c0b0a6978dc87a298ae4703072003e803d0d5226320e2600dab137ea0000000100c73a8b2277538ebebeee5e6e6d8090997130ab6b6922804040bb467b86c6a88b73411e62028ef165eb00b9864ee52f374382715f92b2ab61ada34c160000000000000000000000000000ffff22d2f6b94e1f8b5d53516c0c7134efabc77f5f7d19b6e289b5e8befc35ca5d77626a252e659888fdd09a7c9bb286dd9fc4d73025bcd7b2ea896299dd86bc7c3353a40d354be43ca26372000200c707d1a3b331b8cbfa95fb879085bc7905a7a6c2cc271f52c7d8c243460b1130618d13e51fc41774dab94832b4b49e3cd31f49be1c05ed011ce5f38e7901000000000000000000000000ffffa6469a724e1f8cf6a03c7a0bd8c38c1fc536a013f93afc8224cbfc79d8b0157c7080117ec9c1c60198c3fb3886ec472aeaf80bd5fffc52bfa12608d37422d52423c3a71f9574ffa8267f0001001027b19b0191dd308e8dc1a09d86b34b9fe82239f9c3010028e886392e04e2e498d1e255f6b59d75e411554c7ac053f27180bb15f0bc3fcd64ad295e48132f2af038955df077ff2b8a8a36e1d8e74c613fd218039101000000000000000000000000ffff3ccdda054e1f91da7eb4d0d0f78e4aa5379d45c779105a0d809a04497186d53743876e31874d663f965507339259ce2b99fd3ea4c2757fffc5375fa606d4066bfd9f4fd211b3c5d59a95000200089d7f09afddce572996c8fb89fa5c8d623b07f9e4fa12b0f58f5468182c3e73b6bfd4a56a431cc10c268988b114205650f437450bbdfb955a027695be00000000000000000000000000ffff97f455b24e1fa8284d7f998de96142e7e9e78641b5889062af51fc107a094e76811d632ebf62472b2663468a8d7db497dc11ff41320ae91d119e80a59ade62644150600e5edb9e114f3b010000010068a5d395f0b4cdbaf29a120f485216bf1c012d0f0d79c56a68e6b0d18b382de6dd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884338c3fb33289e04212abd9465b3cb2b7f4a06000100888dbe6df4d824c05bf806a0722965200a32b1c4e2cffe3aededd6066e21fa686761aa4d9496cde9477b1ef872738132462fc74fc06e6a8dbe99e60fa002000000000000000000000000ffff9f45480c4e1f039715a9bc06634ab10b432e3a9d446d436b4584f65c19aef93c69d07802690df0b51d81da6ff9a8de1542c40edb0b1af07b6cfed0116e02792156cfa9f595b52a3d87c8000200a84e24a5ec9c888430c2cd89a781852d3e641fd499aab23324fa44839787b605e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a184e1f80f8efb42f65ed9650078785be5d13e6e90eb9df87a99261d4de34df2b4b79a9c9b8c5e1aec7ac068ebef14636ceac4cec83a5f23ec17737f590153eb7eed16f7b2f1327010100a3055872bc65a323257b585ec95658766f4d4000eb8f01002897d9875cf221b3a813bc7021e962a49338a5e0def2259b99d17e6a0ee2200d740557665772cd580d184520f843e33956db260503e6a3f5d801630e0000000000000000000000000000ffff9f4145f54e1f14bf71456476fa02cfe3de9735eaa10513e943db4576667128051f34692ce042c90cbb9dbb268d3ebf89205e5c8e2afc3e6504110505f16171693fa5f8582c03d7e0450a000100c86353dd241e72ff1703cd03eb947b00ed5b8794db235ae4bd0ed1005fb3eeb938c2d11cbf2bcf39df66686e6bcc7ab47e71d722e593457896ae63010000000000000000000000000000ffff9de628664e1c8f420a082c56b30c9bd8492394d83066a8d03628a7c8e3eac27486377e2648bdb3cebcc4fdd16cb4cab1341e480fa439da5ea047322750daebfc463a87ec042108d63ce9000100e8cbc0cfc994f63fdc8ce785732e2d961e4b21d721610ffec2c2500263a411f3080ca6a2c87d5f5efd0a7a13d46c5c71399b5e60297597ac7cd4822a0000000000000000000000000000ffff6a334e464e1e01841a16adc73f0224e6544d0cc57057ee2508c906706307ef8561908bd476594ff1e825798faac54c8f8a66583a3dba57f73d89afcfbd3c34ff42e19f32af77dfd7a899000100685ce50d7f351dae246ab6d23033b1fb7ad8368467fb348086822fe6ce77d8a4398f99dae01aee02f174c49075e3e92422227fd6a57d4a44d7419c060000000000000000000000000000ffff5350e5d54e1f16415af54406658be9ea44d82b6b502bb90d93e32997484533a8a71a4ed98d12cea3709d84a5835b6ad8ed48d3101633d0669e929f06ac80c05365d9558ad79cbb78f2c0000100685c08be982ca2c27a877e3a552817f0c1d64e53a2ab6633bfac33d11a4ca5c22380e1134e1222e7a10ce0a9cf918ee514d04f0a69e7102ea4ea24100000000000000000000000000000ffff9de66e564e1f85f01c97f8e6d601ed269d4fd1d33b456c5c940aecc45b084c0f8d9ddac26d6fb7c5cc1eb817a41bca401e5c9c4ff85607b48a04d0a73deac14da9df36ca49ffb14233910001000827ccfdc4d571aae5cf0c6a03fa2333274c7127c597330bc3d9eeaf4dc0ea5864a8b81a46fa9ec8109fe6e6a47a7d30522c84646d2277118ec9f51d0d0a000000000000000000000000ffff0b7a212c4e1f046e3d8efdf54e1cf182c8eb894c4bc1a2845fd3e8e20a383a80435dd131e34e69ee562cc7f31628960e1ad57fdc538a44c8afafbb5c35b797e7a8876f8932829f99c6c000010008c793da5bdaf2a08c5ce7312a794e75c36a766be9416a2b381f45e7f9182c9072c143476d845183f9f4afa593ed6a5aeec8286b370e93e0db664d285b00000000000000000000000000ffff53e977834e1f198e877839e3a29d8e1e0f0f8db6d9e533902dac30db36ab1330fc4e1e45427b658fac866dd8bb65b0c68b263ebc695f747d840bce00916fa12947def52684f03a37618b00020048a311aefc2b65fce613d9f995c403cd5a8cdc088c936d34108c977b763e659bcffb90a56a936f35568172c053c3bdae89b1fb114b25664ccb49be7a4700000000000000000000000000ffff44437a044e1f80ab2701950ef7a1e068b97d02a2ed1e64a8d38dd2d0e5a0d9de5432b3e1ad2c318109d89326d48ab6e285d6ac0bb22eda3d9736bf16f34dcea4be38d0eb404335ee21c8010100a305bbe0dd0cf1785a19d29dbbf75dc018a11e888507010048f753e30613104d2eea72327fdb484a33b5a0e928f083a5d10336ff66b1d1156bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff369ad3f265a618e4c1013002f690ead979628cecef981034bf9277813035637c376ce9dd04b1b379c4abd45bc5ea969a894fa0d0ebc26b78d97d6d777cdadb430c72745d53aa169eed8f0001004978e987b1a6bcfb9d486cafc3c8e0529c46386ac96021f3105913bedf6b1d7edae5a8aa5892e3e6c8f119f214d423275c8e50f143bd61616ce296010000000000000000000000000000ffff2d4db0104e1f0d5a850d41302b179b9009a4969537c5cbf7f0145c94de4306a4e09115ec00248cb1aa76cf04249e2a5104b5cfa868792226272b80f5aa9fce78cc60704f1dbcf81715010001008914d3c03997deb1dbaeffa6531a5e185c5f2b07c5cc137cd7283b2c054e6eec38ffb43735af18d84f29f9f027eaabca7616a0df616e3d3e8112de92ce01000000000000000000000000ffffc380664b4e1f1926b68942b544b4e17347c5e0d28ba91453984294c7679965f3a1d3cdcd9f5bf80f2c28a48b503f301bada544798968cebc041331aa21d5fdce3e44b4a2a520889d4ba4000200e934256a123362fdbbd31f9089f39a513196c91820b3a6542f60b2c98f24866f09e6f409a21638e9a82ce854c58593bf70c73b515becb820bc4bdf980700000000000000000000000000ffff05a1d7f24e1fb4bf5f174b66dd12f3648347c5846697247187fe8eb43169f27e1d3247e8ec210cb36547289e2aa7ddc34222714670775db443b2ad2d4f03bd29bd302af26b13068f692f000100a30598c76fafeca892e0b6e4380fbbeb239709bc3d06010009d9ca008cb9263341c196659ea2f751507cc6b6199a9c5b6d40bd136ea3f3f7bf917bdc5b734cddb7bc5822f6846b0c99c9199a70ca6ba0ee3c050f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000954c2b52b2f86d89f81b8eb64b0944a1b1033e38000100494518f9dc4badd3b5615b35236830653f746fb17dd00c266a9014d4d80f963fb11b2330d1a9e7dad84b3e962c0992323ad4b6a2f7a3ee3175fe6c06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001db884e38658de13612ec46d636c67f26d3c43ab000100695dbbea77cd2a5391bdd59b4b328a240c30b5dcc2ff06fea2c78d7d51956344db1ba09da6ccacb1a7d4f4b14f697fdbfd43188f58a14f7736a77b01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006af70254b26589b80f06011b3de26f7fbf715b8e000100c9adca4033fbb3fd459671d4e62585b6cfc8a313b5eec0c8ace665633d5e139b7f1fcd16244270262e3b74927d2e65145a8be0f781c80e1f9a6d2ef4b100000000000000000000000000ffff12ec1e464e1f88ec95047130c4b310db3b6585c2fbe9453c7f5ff43ee5fe844e74c25766488fae8ad62052ec76a6c2b584029e1b1b04d89c5ba79b9553a13f783b50673e15283eff3a3b000100092ed131880f8eaad47a1fb19d31c87f5f821f757eb4125977eb834e746329cd9e66ab48f787b066aedc4f9a668aad7b5c13b9b7f95f328ea03c2a995d00000000000000000000000000ffff2d200cea4e1f04ed653b556ea9d8b8391dc59ce49796b15c118e37daa2eb42326f72571b195cf21e814f93338ac9488000dc3d8b4d55f6cdfba67f9b6228a325c80d5723557d8a2006d8000200c94a4f3fe127472a4592ae88bd5c3bf0961761c5b6aed82a7a90761c2c1d26ffe026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a1b4e1f967796952922dcc5208a3848ab85a787e4592df2d8ce36a29369b0b3a9576073651075039e1377873aa8c67514ad2726b14f661da8227a41acef9f8096df2afd2a192aaf010100a3059cfcce0dd3e16f033500f2ad96c8c7521b5be06701002993a3a976afc9af823b354ed8b60b5315e6aeafb04df534e1a061d14915543a3eb42314547cd77536cd1cccf5db363de1cbc28fafe3538b0f7c9a170e00000000000000000000000000ffff8b3b23144e1f0a97f109f81f15cc0b6af0a57ec93cf9f201789fd28494baf1840594d4bb233cb790f4ba434c49ecb6a1ebba61beec0328c20f725ddcc44c5d6c728b05b886805ca125af000100497324f53dfe04f3892427a246791edaf2ade762f199589b044e4ec42002afea3074a06bd37b893444564b73fb924f6f604540ae1a4292521a1627090000000000000000000000000000ffff9fcb22634e1f0452f32ac367f352d6ef53f984667db9ad658bf940292eae2440024e5af9445f7a7a618d536c4743c9de4c3e07b6a5f916c792ef93229ad52721ad5b08249eac743fb74f000100693bbe97a5f05edd01c77233fe95f8789d02559d37f9a689f7b31267a291b2c693d30745374d676048cbc4580520eca862ca43470d953af9cb2097aeaa00000000000000000000000000ffff2e65f3544e1f83152e6489f210094f5dd558373a1ce9abf36bb8001d4f35b3dea74f7ae74baf859abd22238c682ac94cab82eeb58aab5d068e0fe2b3fd8bf7983a2d806e797c522b75a9000200895f6ad52a27fde4add081c029df4b272a81439ce612d5272eaf9cc1cf90b0d9e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a094e1f86108e551691da2642f37b68bcfbc5bbe9984ca51aca15ee24b6fa9b8690ed62c6ed722d091e04ef617cfc99341fd3587334f54da07d3b9e177f6e57c64f97bb0f982caf010100a305892d1cc512af1d3cfd0f8efb51533c85d3f034700100c9ab04081160c78f70269728c38424f2c4af2263e56529969a2aea3d88a9fe696885e557fb7046fbd7b0fe27142d01ff7a445880b5fdacd67eafc637bc01000000000000000000000000ffff22e180e44e1f18d5da073c85f04213bf2cbc10eaab55f3a2779c0f347e2fb9c869024f30afa57c7054ed6b69f2a03ce928bc9683ecadf573287084ca8cd1ca8ab21ff5436945022466c200020029993fdc71302b0d08af5d3e39ad85ecc43769f83ffdcf75e7829a80fe57e549c51df49169a376435b72aed5cda41832347e46c230899a22de22f7965000000000000000000000000000ffff44437a1d4e1f8f2df81ba65db70eaab625c5fe46f0f5e52a45b25c761686db23b4f18e547cb0d161912dd187302eb6f7c4a9a666a3236cc3fca46aae1c44ea1bab81d506e9f0a3ae1d05010100a3055d92878e4720f0b3c03b82a6419b3edf98472dfe020029b5a218ffe4842991b29dfda254d3a7ea1189d3f3a86a3806121f063bc5b44987cc0d950e8b02155d81693fea6c29a3959e475f4a23a251c1825d135100000000000000000000000000ffff034fb2cf4e1f8e7fdbe2c04139b2d0839c236ddf6640a2bc757791a23edf0eea9526c4da9a8f23f7a94462291c369277605592d756cea4e8d392208cc560a4b1f0f097bc3fba132e95ea000000010049aa692330179f95c1342715102e37777df91cc0f3a4ae7e8f9e214ee97dbb3dcb51fdb1b943d2741f362764fb0722965eb6770b421b4cb8e6d3460d3c04000000000000000000000000ffff342440944e1f139b654f0b1c031e1cf2b934c2d895178875cfe7c6a4f6758f02bc66eea7fc292d0040701acbe31f5e14a911cb061a2f6cc4a7bb877a80c11ae06b988d98305773f93b9800020049be2b744dd8fac4a3968d367ddf71019ac2199086d1c9f7911a9b776c5647f69a4498ad5a1a0fc61ef0c9b3ecfb0fd1a206086c4ee6b4b9db89ecf37300000000000000000000000000ffff55d1f3024e1fb1205eedcd5b3964096f477541d019a0a990f7bf59905eed160622128843b2d277f1c639970e429ba997037667068475d1769a7dd51d17aebb64a38e69042d5aa50a7d31010100a305c1d5bfbc4f6105b6a4e29aaf7911d3f0428b100801000a94e9a705dd8841f52cf30518332578c905573c309365217b9d797f20b441e38a4656a464a2a618e07559761aa3b6ab7cc0e66bf5c972c0533fb5350000000000000000000000000000ffff369d08914e1f029d0298b3ab58f541f566ba5ddd40e8e1e711dca26b1757fd1b707baea16ce77aaf8a836232809a5e1d301a36f204589dbb34ad2ac21a13371e4212a67150bbc0320b2f0001008abc87fbb5018b42be79851aabf0acf62e9459b9c1a64a78f44bd92dc0d1f91b042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a474e1f14e81203caa5c0cc305b5e0f3ae7a388b974a629358e4e83e50a25b2c2a387e3d114c7c82e2b23c25b65585220e63c9983564ef2fc85085dc7f2a155a64fb5d14d89dadc010200ea249c84a35c5c4d767c9effcde8455d607e8b8d9154f5d56829653b53a62f4ca87429402a00af635621bca277c7fa5e63d674140b5fe0928bed71b38900000000000000000000000000ffff97f455304e1f90fd924ac1997c50397612532e96cbe96564436e4cfe587f1cdbf95c977b0138f2183bbfe7f8db3e1fb177b2ec3b69aac80566bf24b2ed33476fc7fc4d40eb1bc7e308fc01000001000a29cc9281c956d30d365c7cab93075a3893d2960a0ed22e2380b77f5b73da3a042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a4b4e1f8c290b31d2e878c2d7235efb0c61f423aa37742a31318e61f8bb0bd6c110a892dc244512fec12a8b0fe7cbb08e12be284c58fc64d6bf36b4cd655f04a3e35ddb1ed7a48b0101002a0daa3b789866a15779d9bb3bf4c1108bbbb82af9414f7a7469b85654fec1ed042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a284e1f06e81b2ac08c3ba6308868ced5075f366013ee8598961bc84150d8dfe9085fdceafc082fd18d3a7bb1338b74584048fc0e42f45a86824089891f17af573e84512f00d8850102004abd3d9b76a34d428159456a2e9722036c2de8da9ef138e7e191d73dd81a7439e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a164e1fb6693296894820bdc3c0ae76f357e544847f10a68f0046f53745370dbe861d57e194ddaf7ff7d5e73cc3f240515c448ebd9857229d4c0509c753afc85d264fe2e4f344a3010100a305b4563e764e296ef17cbb808ca3ed72bcb9f37bec0100cac11525ce0a8c897a57495cedebba859dbae776530759d696213e957bb07d03e15fc250d94081fe2eb9ad7ddb5aab3f92f86f909421936c81e269070000000000000000000000000000ffff80c763bf4e1f8f3afb0dbbfec8610efdb4089f1b163e7f55325f6c0503470e8d49ecf439c848ff9448749e0a383980824994aa5dc50d8fa9e72582f9dc7882e0e40c932d98d302676591000100ea1d9285a4277410ad7f8a478a23e147e75dbf52de4dfa8912476c1d1ae5e5e05b06d81c7f745976b4aa6b10873ac20bc7869461d27ed52000e6f30d8b01000000000000000000000000ffff020304054e1f19224c48bde28c061c99ca2a641ed1f695546fb6f1d103e93b4d8aa5164f5fdced8073ee239baa885cf377c8c173016552b3089828ccd5c5e2929bb45057829bf2cd99000002000af23bc94eb59fae108e08949c3e128eb5a09962d984cc689867955df97856043e3ad8b685c34eb49a8e4edc05d76a10c24349f7b9d16d1ff8a6bb5dec01000000000000000000000000ffff4545444507cf85a8e547858c3772041317eac7dcd53f61fca75a5595870b44081a6bb83e78d28c397222c3c2b3015e651e163f62880b542a58cb756c3edab85a715297c77ff4e02c9bcf00000001006ad616183897963783b37d10d259a5c9c8945f6c44a9bdca811f6348c02f860d2809cdcfc375bc68668f095709bfb241618ed9ccc7d426e652c51428690b000000000000000000000000ffff74cbcc784e1f028f3d9ff027351da67047d78254333d5430c022f800ce5530835d1f048721a82d87bfe959b74aa447908e3c3cacb63aa86191b01824b41f452f494a6d62026fcfc373f7000200aa6248309136c45fc5e13dc128fdbf7c0c69883828bc794edadecc17402403f5e55625d2c966980775dcb8e352b0c9d666b207c822e685af90f3f045d200000000000000000000000000ffffa74708d2752eae85957c684df823b199c7c8f001a70efec163ec830f64787e4a0e020d2b4914434c78b6e9c173b82db346b32d651bb95f68d4c14279d08870f54e337f44afdb72d28a8600010026595b6ddd9532c5863b8bb113ef92d77f77b66f859b0100eaeafb8f8aae2883b2f2e0e023e4e2b8e4c61dbdbd8592922e9ea47a7d40ef3347fda655a6908df732c8979bcf849249abbab19afdb67c9a3d26b04b8d00000000000000000000000000ffff88f471a64e1f0032db28dfb209566356ae396255132f8f8e412d6a59dd5cde82ca347f4fb2a4713d8a49dd2dd9b8019bc2dd3a62745a4f9406e645e1d2a24f68a29124a5ed91782c92080002000a5b7f1478902ecab28b3404f5e79e9020acb9abd31a6e13928c2450d42684b6adf56996e04e36430af272b8c0b1ed1dd57b6146775532c53634ecb5b600000000000000000000000000ffff44437a064e1fb8a150e6472bc281b51537dda3d077264786f207d05c4ec8d314b5883e32b14b15077bf39ccf5d92543a6fd0ee23897802e543987843fbd195e84ff3a28637ab2231d98b010100a30559b537a5cf3ed95e48d8c006d53de9e049dbf9a101004a37c4c6a85c1ba73b84288726c1f43eda96a72f027fcd0910773209b3d0d865d6264ac48c327071e53196e3ff74e03715345b3cf43e95ff0f63ae83e100000000000000000000000000ffff23a155ea4e1f8b98c8e6620c7893f7c26acc50d4e335b74f9cc866019ff0b6af497407fd0ebdb33c2cba0a6f2c4ea9a8aee85f22bf5ffaab53c3c104da464745c096b8c79c91f47b835e000100aad381e4314f1630559db598be088232973e6c9ab61e7da181272675fa70ee72042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a434e1f91f9052f62561db112ddca7df3d914d546866b130124eccb2ae1e8419563e51f239b2efec3d1b3fd388072610939d694889e8cd2ab0cc70e36e566648e265c7536a8fcaf010100ca0bede330fead7733cf68bf2f7edf00bc7dc175b6b56fcf1758969b0402df9a6fcd6b82befa2655c42bf1c7fdff20c341485b0de4208930e822253c6e03000000000000000000000000ffff4e2eb95e4e1f0df074095e5498c6f1b76508c3ae700484d8b0d5cd12a990f3dd54e35b47d6fc943af4223d390ddaf8989d883c84b284a7889dad2c33e1f6b8b68aa5cbb7392d9e96bdee0001006ac187c54507ab1144de2acc9fea9477628be19bba18db3434ee456aa187504dcc6d42235a73f538f345c112ebea18a4f6f19f53455f5ee7d9308941d60a000000000000000000000000ffff411520564e1f8162401aae703d5bb3f53a7b9c5b65b9a94dbd1c207a8da8f1350200a960d1607e89e50b365afb14cf3b700662342a4c336788df1ce1418edfb0b8321db1611264010a7b0001006a59f9b585d75f1b2cb43b7c0fb90a294fa45e0c1f7a432f82139a3ddfbecd3ed2b9c32684bce2de8afb2ba9bc9f7547d3a43fc88bf0a33ae2dddf6e3700000000000000000000000000ffff6c3dc02f4e1f0634f8b926631cb2b14c81720c6130b3f6f5429da1c9dc9c33918b2474b7ffff239caa9b59c7b1a782565052232d052a1bba3b56ededb76c1834f3b3e02c159aba4777a6000100abe4f25c3753453abe3bfde7f0bf6109434b9c7fed47fe972547cbd32784605a255ba1c99b3221641955fdc17d936f0f021e4ed754e209d9d6fc9462b100000000000000000000000000ffffae22e9774e1f8a0ede82d78a0a8f4c2332d431c7be496c3aa09349ed3b2db30f7eb7dcc7b6e580a9d71f7d76bdaca1b3670e0cf4cd3cce7f101b7f075273c892063b1b8571311737a5760001000bd1e4e68fcb354b25185c443da6badc1d61b02f10dade4f68e1ce372d01dd6fdd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004146e5a5841fbed699d9a1e0666340bd1caea1a7000100abd2a494509514d4f548ccf21736f7f65d348c5c297fa91ab7271b63570677df9d63015573ee0a8596c91c15f24f8157227cf08ff013d0a2c6e1f373ca01000000000000000000000000ffffa123e12e4e1f865e0561715f28e996d2c63e1ed462c82d403ad9300369529cc8932f02a8ecd6bf5c5b316130997f2c1ad1df700ea2fb5603402752878f5c76e9f40a3091b748a731beea000100ebde30c65e5635f78ae3a41c82a78d0631b4ec775049e7ab50d41cf7f8950c8e042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a2b4e1f94a0bd3671bf20ef2a75c2e1723eda50a4d7566ceb0f5e018af7c576e11b1320b4b370e703afd4a7220a7c5688414040d28ef7142aa96b23b44a0e7a5294dc2152e5f53f0101000b4ba30ecdaff42079340cfd0be8baa00a30ee8b2d38b1ed12e7875d541beb4849fb66acec91c9305bc81c8eac64a560ac8882ef5b2d2783e5ff09080000000000000000000000000000ffff23a84ebf4e1f051b1a638ba22cba300ba0836304586ba5572a525622c4dd49e7178214985277eebad66a371253163e35e93d3b44081b27ab63981bb3d5f5b0375ebd3c709b95174ec67a000100eb6b18749ab006b0b61b4012a9e2ba65a8684867162b5149db2dd0e9e12297f33795ce7844fa98ddeb3101d348969416f7f89d1dcb7d1a6598be3c090000000000000000000000000000ffff030d2293271796395d8ca159e5ca66eae7685beb6766a6c0ae50b4569809c4ecca3e101a1f210bc35637473b5afd5e71bfbbc976277dbc97efd5f5716cff0af50478f0d383052635c7eb0001000ca0d03ea2379fd59275d0b1a5d512a1e61f17e92849c3a6a78fb3df833d29f80a2121c3c1f41cab0e6bc7ab4db694e1bcbe5120e20d862083e47b010000000000000000000000000000ffff34348bba4e21803d3e3a2593dfd56111203f3f7c562d1df639d57376d1994aff17260cfbfa576bfed870eedf234bec169e2f8e6c44da8e4960713412ea367f0a11c39808c2e77b83048b000100ccc81046e9796064a98dd3bbcfaba09d799ca2584dedb35469a62bad7592c248cf84161ccd4cbbe9497d564562ca6ef8fd7bbb64174857e5dff2f5784b00000000000000000000000000ffff88f459e24e1f90dfa669abbc6504966bf8cc2b4971db18a5052b70475fdd5e6f427349635ccd6b9c7869b52ff3133c5661412ea5ec134188469fff56b20349d06ab783d8e44b7e698a730001002c0582f3b56caa8adb8876762024b5fc5d444629e0304f24a56129263b315e44042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a524e1f12fa57a5676925e8dfe3b340df2132f5844ad9f89594b04efa28fb4fb884fe21f411fa49120ed7a60ce9381a54232a100a2d318258cf9455a414d2fbe30c69f5ce8d0929010100acc5dbb142f64d1b14455d1121c5a118385afacf17624de54b71f4709193bb846413a5c9bc7991b721a4cb96d45227c674edb4a758cb2566b4b537000000000000000000000000000000ffff6d61d62b4e1f0f4002936319c495d9557ac1bd514bc760cb8db72dd99d5d20af93dd5a7570974d75e5761fc494de28127ae02413819cdd52f6e013f0cff9cda0dae34d43317bcb66edfa000100ec99ee5137c789013867f1f3897472c3805f286bcb5abb5f31583519f817cb849e4f98209ac0950b0726d009a9922e8b7e553876475a191ec0e07b220f01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097726f02d3aa3114065784a38c5b0c7dd66811d8000100ec02d43c95be558c4286ac61c9fa5ef23877f66306ed78a1d3c3d3fc6a0cc919dd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003beeb6930d8a27fe88c3af829fc6fbb3198500da0001000ccb06a1735874a9bc1bd9042547aa981b7130c5b8b4090b420b7e5b337585edc6134cd3088e1f7bb40531da5e8710173121224ab3ca5d94a439de060000000000000000000000000000ffff8b3b51aa4e1f8dd3b8d006c8ea260bc6158daf0680c5cc7cf4936458024b51ea2036a800ec6563d75135004055d94743b8341b7013589f9695e95af100e56314bd08680812a0f6256e110002004c3fd5bb504ca179596113aa8e3e27aa1488860edb81269eb18a9ca900b4a91b21a94745d870aa608ab95e20f6d075ad6c7bf73df3174eca6ba33f797500000000000000000000000000ffff58162c0b08aeb006ed30975f322c830001d1b3c2e2f14a4ef35445431356f1c5b93c2eedd9c7ea24c4429d27d45f2fd701733ffd655496375e263814e1d54fc3297fe893dfb5a9c1a9eb0001002659a5ec5f2f26d07486a5344dc4a741f5a1b0d9dbf201008c0b02af5280f5ac3c6d3ffdef14a6feb71c8a277d205a9912f72775d9fe95266bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765a291c5a9a2513c46543acad374e11f69e63df9583e74e511e619355f2cb7c1b9cd7b4c1ddf3f33c28a8c046658958c3a10c33d559a563e9c22c95585c0f556727c6c5195f4000100ac3731e02bb87282b09b7d1ea9f7ca460193592160afa2e194fc2bbf5c9b87afa0d69822744ab3b6d8bc0d9e1ecabaa18371ba75453bea5ee00771818d00000000000000000000000000ffffae22e9744e1f932f6fc90c9dcaacdf9d836a2a7e60d090fe5e55b0b02f5a4f608a4b8235ba5aa7abc4e05f9387d1d942adc57c87f5b7ce7f101b7f075273c892063b1b8571311737a576000200cc533b9eea549b30d93f877a43210e8830d7a85166e995a4cb54a99bf92a516856a0649b7a6e00ce4df33ba0090f654928458509e3cda88ab7ef5f11db00000000000000000000000000ffffb92f84944e1fa28a648ac2b271105426f11a070d3f4854f812c3fd0241860afd511a7ac76e681210505ed41121b141e377a73faaeddeb2f70938b1cfaba9eab8eee718a48f292e1d5180000100a305a95ee5d97531c6e3c0e1f5c075b907e89b1f3ce301002c8411bae19c2926f8a4759da7584b66154a83ea95e78db457109dfacaa46797201ea53c7caf89e4d28761e9b41e0cfce68008ad447f77caa7b36f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025df60b3535787ed34aa614aeeea9f99236ea9940001002c905e309a34616f07e818d1d9ac77e1ffea27f5559bb0bcfe83c5e58d036c306bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d413476598052abb8468545b0593010c326d358cec47a3079a36c6f5002b2c36fb45aeaa6ef00746d0e73697fef01704d51d87049477d7ce66a12ce0c43ee6b3b9116649dba376cbeb0002006c00eafff29a3b2507a6064173c049042d3ee5b78b1718ca0d88810ec57e1020e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a1a4e1fb6e979f20241cbb73de7451779e8e059d9cb75a74b72ea6862d7ac703dc2ac07d86cec39b6e8923b55fd54dbc6177c3a04d65ec5142719f58e31f4510dda3c810a843642010100a305b39ac88b40a8cf7cdad85c3bb4057d3976ebd93501006c209e66aa96cfda6f124e23800e3e6feb830a10d6273483ce16650ac5e0720e83bbfdf4db4b783ff62c2c387a1e7ab3736fcceb26fe90019ef1808b2c00000000000000000000000000ffffae22e9704e1f8d29637a7883deac9d725e72cc5a1c366ea0dea49bb61dc118d2c2afe7f0916ac7b2516a4ad4bdbe7dfe68533867a866ce7f101b7f075273c892063b1b8571311737a576000100aca0ba03c6b25db6766e3080cfb52e7085e5e0ed3d34f63dcf4875af5ce39aad9e4f98209ac0950b0726d009a9922e8b7e553876475a191ec0e07b220f0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a366a03dfaa43d112376f878b171f8df7289600000100acc0073487b3b1e3791a1c330cb80f1fdf4e9bc63024c226491f039dfb1872c8042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a384e1f842c53c3aa11ae4b985a52ae6a3170bdb58f88ec04c62013f9322bd5fda4417939836b6f41741dd864c348103a1155d35c0c201c793d6cb7265b6897058b0b1e75fdf5280101002cca3ea5891fa34136d4c53122d1a4f843ddd8bf062f95e1aea9e169e8e104d3b2919552febeb84511bc568cc875f5d3b4832597ebb7e2dd285059010000000000000000000000000000ffff904c42144e1f09591bf26ea6f179457440d73ebc70c44310ea56dad7539a93be903e28788cac013b646e3ed4198ca74a8325d8a721c26f39ee8ed58b56b0fdecdbf64cc71b75041ac7710001002c5eadbf6056ba14fa85c440881afcc228de1be6f07f90ca0cbefa4c68744871c74d05e7036dd4f8d239c94fac4556d412e7c8fb18f2fd020ea91f010000000000000000000000000000ffffb6327d554e1f11b8a3cdbf872f868b08b211878ef11a0f6f7a7ebd55533864aa98e53e194faa159ae2d13a7625384a3fd1572f68deb43f309e73b02fc83f88a0bf28f87d715a48678ea60001002c7e7a4bc72764adfdd7ae8c1d8296682fddb6434da75a6da930335f013028676bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765f3151436dee05a55afb36dfcc21afcd193ec0852d2f2b27d86f3ce8c05e7e4d4af4e0023d0089b0ef3d41bcdaf4556b1fe0fb0af93f68351ac5e92ff99c982c664da1697020001004c82d4ce98e02fb63af8cda16c50e62e24eb0af87cf5cf6884253fabfdc61fb1dd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a221df490494e647eab2dd93a225a2cc62cb2a740001004c720430ae033bf0a1f8a2685ccdcf5906c2b5b62faeeb18d84b67837738e0f098f6dd6e17b89819d3ed39a8e55ef115846b8fb581a4ecf870f2c1fb9f01000000000000000000000000ffff2be54d2e4e1f88aef910c408df03f396e0f92411de096d08d4ea727fb3abf45541685d0327ee1e8d5b5a5057c92fa2360d9c0abbd11fa6986dc7a8d22b1ec6edc799608163d63eb92ff60001004d6c9b4114b070ff139d44ee9928cc5f659e4e7384effa9866b8177373dbcdcf721e858b5e357f7e3f50a4bf24dd8d18275aa2e70f7f4ea4efcee5040000000000000000000000000000ffff3415087c4e1f009a876325699d6979757ac10d6b37eb7f6690a40447f6473779eb9130975998d3a9fd6e9ada19559730bc017843ce128b7f823b0acb2fce829b7434a720075572ce6dd60002006ddc88c3c0479e2869ef1dec459349532fe6a71c26155264f4346236826319fcc5baf828866946228aaa8193b5d3ea3d5f0e41ed2608d77fca826c7b1401000000000000000000000000ffff258b0cf74e1f8d0651a04f9c243872f613901a1eeb2fa86f3e9a65168bc39941790783d9189a9a7876cedb0c6d7ff7d49fbc88a71911c9a9263ee8c8c606c449959c48978ba70d4a097b000100a305f4bbc801f55f9bf54bc102f66fcf6370f9d8481301004d35ab4dd32e0b2c60354bbc491f13abe6bbbd853994609f802a52462fa8c587de8bf74b378ce943d87ca86bdf897223fa1698fad3732eed2eb6a6070000000000000000000000000000ffff9de628664e1d84fb8f4119d367a2336982fecdcf326c56b7c09c0911994720ebe2a657d5d95252be1871889b13f81cdd16d49e15a7d33305078857e34417e861260119603fc23c67d2210001006d6d5b27f4bdb63658686fd6b2ae2a9a448f94939c33d4bad93258acffb18b29a54ef8809b4010abc2092b9a6fb2a61ec4b378ef734dc632723be4b12c00000000000000000000000000ffffae22e9764e1f925d20af1a6d0ccd3890f0aead4a05a59be22e005b6d732f855311915b351a9153b2c83d84611b2c9958f806c93f7b5fce7f101b7f075273c892063b1b8571311737a576000100ad15e693a28c0055a8610e57edb8e5e11e551da4a98278503076a7e381f9ad7595584af26d1f62a096859ba7f365b5b8064a653c200052a3ca8713000000000000000000000000000000ffffa7636e3b4e1f0fd87b62bf91162008451c1f00a1d7bd65ef581e88c153d105970ab30e451378966b6e4141e68024b3976461605e840282242bff3c046e018b795b7bc3bb8667628dc02b0001004dea1da648b4453ad1529f5fa2df35e99244302b0222503b2f3dbf2e1ec631062f662979b702a945d971fed1d3dae66930ca071b53ae4ce85b9c61010000000000000000000000000000ffff74cbc5074e1f186053ffd90c84db8fb369e1178492b3a0a3941d33c43cd84b839d92668203b6501c786486083eff2b229cec3e0a190d383f1034f0693df2fc8b166d242b00917b7fec070001004d937aac30ca900235ca2a476b48ff19bba8d0682d07bd2d5a75ca270b828a56e3489c49577523cbe901d655f1acaaf06667359bf0de39ffba06584fbd01000000000000000000000000ffff040302014e1f983bd995f3e1280858f37e33edabbd8ca4d90605e26cb6dce824a2c03f83dd498180085db4168f9e6b4df0b46a7f4f5494c7496c719925d805ad2f0a75c837fd9abc52c7000100adcb6dc4aee52790945dcdbbc9283add6c1626d6c715058966c019f3ffc7ae75042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a2f4e1f124aaa5688cb7220be4600211257ae054554583ad9233e8ca0d58abafe317129dcca9e34a1b9bbfa175b88d9fb31b55e36e3ce4c7f353ef721fc1754406cf534bab728b0010100cd576c882db7da6ec00e3f04dec63d60ed858f3b30c35cea5cdbfae973a0c02c2b599dc346b4d3f95b54cf0ddddf3f9535ad01076549d40130e72a020000000000000000000000000000ffff5fd8ae984e1f8dd3ff4dfb358ca5c5c58f5c163d73482caf44427b62751b18255a456b8edb175f887db87b3b214753045f631db58475c5871227ddeef90af4d9e891170c4407a5957ca00003008d88dad3a94111fd2a70061f58de964a745f75fce5499ce2f419e2f0f9ededdee68663ab8ac6b7e137b6b68e6db778fd51f15af506be2359fbef7e2db50000000101000101010455d1f32e4e1f9940ce35274814495ed1855e09650c016fde1dae932ec081c6a4c4e34910a38cfda65cb4eeecfcb0129158a49d1cc081ca0f2acd4e483922b1bd27de9a25cc94b7cd706301000001008da07d39f748b649b6481419678a824322f1cd0f216b4e50d87f21fe4c4fa7318a19568a11cc8fb38243dbdcd5fcae4c877613d6fd1225b2956369adbf00000000000000000000000000ffff8b3bf9414e1f82f48df5b39fac4fe299c0741cdf675eb53aa0b936ea147d4883b650596887142e4fecf91087799de85312aa47c6d601c522817f1747a9922db608af9f16503bff0779800001008dec231b4659e56e4c7a5ea1ef960f0765d7a10755fc7d397504c160aaa0101990df16d78c3bfd377c8cc70ddf3e09e6b25fcd10f119ee6b3f74735f5600000000000000000000000000ffff5350cc764e1f90a2a532b28ceb661521902ada94853f1b90c1a7d13d8ce8a40ebec6c7a62a4e22fe5c1019e278750fc93f6808f410d3a2e7118000386185b278ec3d5304db462f23cc55000100cd9489ebee1dce8c2ff96e8267ed117a1d77d3268713cc8f8afacf25fc21d05d78b0dde07ed0ab2e3f64e550bff2c682a17e4f1271bd15f2dc76a10eff00000000000000000000000000ffffb2077c284e1f0cacf46c91a350240272bcf66d48eafd77c78ab71185ca41769621116c8dae8b29732fb998cb6547f3fb27b556c660ed6d40952f2f57e3d63ad144d4171de75dbad6871c000200cdf814ab540b8e0f6dec0d5a63831cc67a28b4de54a594b73d1f50378ccc567ed8601e2a4b7054c76c6f13349dfd5015496b1ad754361dba7af5ce6ed300000000000000000000000000ffff22df662b4e1f8156eebd6b52f4146a5b3f9686b9712f48980dfa4e8f17b514ba185a31e8d5fa7568c69aa79d9533fa6efd380c27b0c06afebaad117b22669d50cf8bffc7863662b1d7e1000100bb010858d598c3f0e00717fe5155b9b4c31865ef0f390100cd788d4c33d2c9fa714353d0fcff2108ee37467ea7fe74a7e5c9c121dbb71600dd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5df74817c60158e2226856e9d67d86a8e3ae06a000100ed95d56f195611e034617736e54a037b1841918e6fdb048cdcda1fbb1ab009ce042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a344e1f068631f31e13e1c29c739619b2cb58adf73724dd8c6227ab1b8f45c08c5d6934338365ed45c795e4117ddf79f1e50370c826f62669213156ccf082dc70087b8e68ff4c58010100edcd97758fe49da2cb337abf460f1167c1aba09733a286c798dcac6854ef390183bbfdf4db4b783ff62c2c387a1e7ab3736fcceb26fe90019ef1808b2c00000000000000000000000000ffffae22e9714e1f0d4b5e1f48d7a77746676f09e2b995389d0f1c18601a6f909a4b542fccce87d9f5f30695d078a9181e142602d2e93f8fce7f101b7f075273c892063b1b8571311737a5760002004ea41264559f7b224ce1d715debd3817a36ac63400b2ace3350e204509fe02c93f4ef4bab61c4fffaf9b76b49cba2320583b00676388e133abd9ec42b301000000000000000000000000ffffc2a3b3454e1fa2d4df46e1a22a707fb42351f097764dfbee1468f23d662dd11aff6d27fead20f8d2ab1c3a2ac391db6456cc2b7efdc32c2c18488d6ee04f5884ea9a9b1d1fd6c7a2f74500000002008edcebfde23e67353b5d198ec69272f74f062edbca349985e5abf7567b2557b5fa192982c30cca853affa0747097f1d0e57f9d53c3590dba75318a039200000000000000000000000000ffffc2a3a92b4e1fa0de75f40f60ea7255d3bb51ce31ca67121997accb63f75116a5458a8d2bdd45fb827a2cca65e815fe6dbfa04087091cbf0c461435639d704c0145079ae082735a626d3c00000001006eb9d0ec4f9d170dc2885de8770dba4384992de5b326e78f3b0fea38abe64a27f73a62d8e9baf81d17a0de9f753493fe7962078b3d25b3b1ab49a7aecc01000000000000000000000000ffff86d1e74f4e1f0db6da5d8ee9fb8925f0818df7553062bf35ec9d62114144bc395980c29fcd06b738beca63faf265d7480106fc6cceea7f0dddb29235681f498bb64f7bdcee4bee1f4c980001000ee2fbf2614f987d58627e6194b7310059efb793d564d9fb8f94c1a18d81f949042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a3c4e1f8c1cc0f5e0a5aea680a170ec945074f5b83d83db4d208854204f57c0de220ceb63b0121bd2a7bdb214228338c575ff6fec97fc04493eb004db432813f88f3ce6e730baf5010100aed6e6d63c6e8815af71d8c1f5f8517c0e8b2e1d023bb3e27733d79d38180690042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a2c4e1f0dc75e865b89e96560b38fae96f1d0a5438795778e68b705a506046245ca5dbbedb09e2379eea4c9bde0d0fd4fe05080e7b23e56a97aec080890987ca187cc902a4efe21010100ee6e6ce3ad98d6ac037243dcc9e6802c4cd029dab65f9ba9842ba47450ed0268af51ca8170f9e36531d5432a0aa367e2e3a2b6e19951c45f7db17d000000000000000000000000000000ffff22e098644e1f8d6bea256f36d8b92071b66fa64f4023737cbd1b5dac7c1c9bf514cae400c332a1091df0ba8cd007d641a92507d9cbbf0c5f490d930fd36cd578b0f71c053b488b42c4ed0001002e139e5b0c1d87a22539b2711296f67841a4e1f115a35bd2e4f5e6a745bfbf6e52d785d0a97c46da250133e75acb405876c10f3ea34ad0ccda5393560100000000000000000000000000ffff2d3dbaf54e1f0f57b0dd5947df31adba9480eda73a2282aeee0bace77680da718af1b91a05f878e433e8455ad56a38918b5aa262be0975246a92185e0f116ef70707a58be008da5ce1680001004e7358e6330efb9a2a3b6e26c688dffa202c0ed6975cac103e1b45568166c3cc834bf8d0b6f14ef18e63cc9ed226acceeea0e850d6ac0f7de989f4494000000000000000000000000000ffff9b8ae2534e1f057b3b0190261b1ded22b9c58550f7bf17a150de6a755a5478988b58e32bb7a53e7e6f9981bdbf416324e75ddd9853b8d144786581a774e12dacc4aa155eb7d6d73869df0001004e7f4473a1982306372733fca47baa94c2ba3866cb23506988255a90663e7f9a188d92c55ac2b53d71981657895e37833ac9c200976800ad4c9b02c95401000000000000000000000000ffff0de4d2314e1f0517910047a316c354423fedf0402dde38c90fb376dfb680e6b2b430addea140d0b6c795d90ca17f1df658a5d401c9dafcec2b5f013e92232ebfdc4f814ac10eb533a25e000100ef20363087822b4bb46dde06ef3ab677eecb59ae2a42a287e09db3d9da0e2c717976ab294ffc2cc6e8c2466a294f9abbf1cc1873b0827a5124e43cf23602000000000000000000000000ffffc910020507ce0ec57774146e447ac6cf131a40fb37664ed5f9ff45b59d22cc68f2aa9f4659cef42235b63c3f2c3ed36f8b2344399d2f57ac5622d91ea4b76a62b0e4d0a47bfc4a1fc03c0001002f15bb2a39f6e9fcf11d20875134ef8463ab4015cd66e7856d2c0cfbaf0d8500a339dd5b8e5a421220c878bbdebde5c19e890fd3af8c5c633b33e4cbc100000000000000000000000000ffff5fb3bd75752f027b568db397773d473e1e7c5a06af5254ea184be9e0e648f96969c662fb10eb6d559fcfc9ddc7ab8649e360d7db6e59b278c771d9cadb83d08cbd822125e04bf704f547000100efb5ae93b72eff5ddec13c81055e599763534c5098cc259813f2c9490e26efe03226cd27cc1c704aeb3c2ee6c9f99a44b0392413c0295689e7698bd75b03000000000000000000000000ffff8e5da3424e1f0dda9adbc22cdf89c04e8ee714da7d80dd5620c1d14e30780668d3b782b2f0acac9f00a556be1548733c1ad1abdd96ff5d3a8bd28dc7d0ea750f976b89991482da5ef8bf0002002f860df54c72a136fdf53a9c2aa5e5d62517a5d6152711ade90fb6718574b5b3e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a154e1f816ab3f50007333bcb40445130cd0e82139f8c68b592001cd686efc15e303206491fada6cf90af8f24a28b81a9b59ccfd57dc73bca0753cbd47bb33f68362ac13e6daead010100a3056e518ca89831905831557bf6399eca37f708b34101004fffa9f32d7a39239934fb2bf1110b40e1bb4f1ef6ffa1837b1ae7362db79378bc9bcd3d3984dc57ea96b5075a69a7af8ecd12556e0772dd988704240000000000000000000000000000ffff36ecd6084e1f92427cdb8c9694e0c6ba086ed7c00dd9f52ca18e335f65de8a839a378b1e040b279c05e3822e7c2fbd57fa8852d04cf443497f4ae216cc6f837b4d82ca8e6cd348b636b10002006f2b0973a0eee8f50cf181dff475f5c8f654ecbeea7d5387ebef24b1d41b2588e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a134e1faf9cd8567923fea3f6e6bbf5e1b3a76bf772f6a3c72b41be15c257af50533b32cc3923cebdeda9fce7a6bc9659123d530799f054652c1cef10d2994fb83d012d559a1faf010100a305b5d2cd7c46007006b4a9c7912e9be18a54d91f710100aff369a849f52d0a6d172a8f5c312385eb72427ba88766b9e0f6430e4acafc77861efa72d5fdfbd086065bba507377c99076358882fcc9bb777fb2040000000000000000000000000000ffff8b9fce4c4e1f914aa95d1c7d7c39e0a3b213b6497f5c8624d4f476b8043b22c4f30cd05bc037c80d02b42625d743c6a18d0562aeb579f2d2bd8ce0dcf0434c3cf661c2045c6fdd22ba57000100efdf644f2723689338340c980bacc492aaf0da81a5c9a78da28b797183a108407d8c78f497e31b2810d4f948993e8ec268e64b3ef13624250b1433000000000000000000000000000000ffffa5e30a444e1f121af4d4c49a65e1439a27ce0f39a2eca5ff751e9998c0fea7a3c2b13731cfa47fc6a56a313a38b448f3792fb60dc117463b7729308cbc30c9c199ccee5a77921bb0ace50001006f8a813df204873df003d6efc44e1906eaf6180a762513b1c91252826ce0591679d19f6d32541bcc31a3aaef426e8e50c8253c7461b32708853ea90c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000595d9f40d00a7ea5cca2502a1c5bc47706688c420002006f760f3eaea3b3779923eca5f48388b4e4382413e357c02d1297f03e0bd3b32fe37804a2f32f2637a23034f0acc97c523fdc8131b83eb3cd29f9b8916700000000000000000000000000ffff55d1f3044e1faf71f8ffeab47423d514744e6705d6720dbfbeb342c9a0e81343616e3cee9f778320b48f8bf219d83e18d760aece68592555ba22325c711e8d83fa5dba18917057462396010100a3057f236914be948b199f5a4b645453ff99a841d2b602008f8e30b83936a83eb17c86eff0408c401a3c297e11c8db335b432665939999f7997936508516d08819e5375cd95715febee60fe7457e9b4d6186d4944f00000000000000000000000000ffff55d1f3094e1fab052bc57acf5257a1c88bc1240f658bbb5661a83771eca2dd8b09338b12f8003f955790afcaebbe477377fd08338e5f04af4631be1e5a4fe6c8698698d9bf45204ac66c010100a3054bbf5c68a0aef6ae72ed036f6858ad63041d56f601008fc66927da2a1c17c7d6eade1e2455dc9ad034ed7e8cae6a794fab72e955bb53ceeed739c7ea612a9ffbfa2a36d7e17545b370c102352925ed69afb82d02000000000000000000000000ffff2269201d4e1f08af8fcaaef14df7bc8ddfae8fbcb1f239040be0ac89d43ce0f27ea3f7a00d1685ffc1075614144e70f02df385a996f7c291f09365d25227523575b9f07683a28c205a3000010030ac93fe03fcbe94a13fecd0edfbb0aaafae005f269fa989e4a47908b3b320e285db1eb27ee64885870753ee48c9533634095b8b70fbaed815c39bd67600000000000000000000000000ffff951c7f085207127147f0cd5bc3ec7c8c5704924e855d46dac21de72e2de112f7dee7c9c3f9c40d4474c0d9ae36a56b800659af62c16d27ac0bbb97d398cc78587b58ea40d92039f6a275000100d064eed9ffc9d604f1c5a7f3f2be3000732d6aca4ac2a737b3227c9bdb09b32cdb7149840d6e18729a58d1024140f58e7ac14a203367ded2a731e30e4008000000000000000000000000ffff7217368d4e1f903a0f90f0ee9ee05c0d567eaae8ca9aab5bf0d5d1e0240494f40e4bf954201e48585c5a2897ed76697b4fb95dd993f55871467000cb7d7144ece56406a6d0f8e9736039000100f0540e82b35422ce5e2959f5873b47f87ef08fec762e6a054f938cd9b3e4b3a39a0edfe4fa7f5be8ac1b6f397ee967f8ea09021d8569c7367241c8bb7400000000000000000000000000ffffd25ad25a4aff81bc001c31c71b0d4d4f9ecfb205e914fd9cdaab4e7dbaf0b320d40f0bd5b193d1be809ca34eb4979d661f487b21124bd641c8b61a2c1d3a3bd4e4c850daccffc59a4ef400020010f988bebd079ce989cb67a6f6f6564c20c6a3dd0a801e8ebb4a6936ad45d02bd7925f5236bddb172a97824a330da681835ab329b1b667a038285fb20600000000000000000000000000ffffb0df8f944e1fb4788aed0a3f9d7ed8bf4a15469c48eaf14e4a1e96b4f78b1f8c1182533d924e3b12f62fd06a67d930051aeb792b06d86cf1e49d1929342c7acf3afce26178a0c87041d600000002003089853c8d09e23a297b2a20eac6eef20893bb0054bf842749b7a8e83b5136cc0d77b77bfab342c25a21a28b0fc6495d0de7d29141f3cd7e109ad4b19801000000000000000000000000ffffa75672f44e1faaab2ee2cb7aeb52a2da6b3cc6c44536e7c811b33a5e1f7ce2bfa06ce9cf8bb5e74277fa1d61d37dedd167df52cd7b0c8d17b1cefe9a599baa66b3ddfc70fab2bf5814c900000001003076cb339b759d09a3a9e0104ca702843c38d25a652b5cd0bde69c0f074c04945957dfa835f8ddbd004e663f5628cf242d66c624facaf56046df21010000000000000000000000000000ffff6c3dbd904e1f996f5888a81b9668c16a12c87134536e3616c929a7b67b37aa06d3eb7d7e405e3d3148ce7a072128c9063e1a8042eccd4915c7d1e335096ed8afa549bd4b52c6491d0df90002009042bef1bdae84a6f2e56adf60b6c2054acf4a6e0830ad0fd2461e37aded1877e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a194e1fb675a1940be872b6a0d4e1696bb39ea38179933a1bae02ae1eaf4b47f625bd939482f8791eb38925af47f73be027a64cc0e3581e5165c63790a2b8d2670fb4a1917cd4b8010100a305575cda391cae8011920e52a7c24500c8e3ad98040200b022efb5471828c9c19989b00ed17a4ddfcc4b2005677c30334e09cd8be97df3099f4fbfc79d4db0b9d043ce40968437972297007f23312ce018b6e52800000000000000000000000000ffff44437a034e1fa96efd6e93a689473ce1d03d0c00a5d193b6f1e9e64a826f912ce335b0512446e30aff8841ce34155d2a967fe019a292c88818ac93c56d21cb72c3255e3de33d160674d3010100a30527e81486c4ae836aedc0fb9a037bca941d5092510100b0c35e14ae0acd7b69a78f79a023dde9f4fba4c2037de760665e1acbf230471697e55e5ffc3d881154859ad61de09c0555cb84a6e36b4b6e517977804202000000000000000000000000ffff5350e2eb4e1f874a5fe9d0a2d7ec3ce741fa441e2f9a2f1726fbd2d09a000a6dd4828bd4951a82b0bef934716a40d0ff36e4641f9378d593063ca9a226192be390d6841d9fc28f33818c000100f0bfa4aa5a016e0ca66339001522716a8d4282cc3a5ad655e2373028fa8a14d257355edf9513c7e9c3aec6f26475b3e103a1984e5dd65387eef0f2250000000000000000000000000000ffff03140e8f27128de1a5d67b291f75e87e20eb4b9fa7246dff5bcf4030ae26c321b1845609d50d04b240cc51862b4a7b3dc9be4aff050f4e818207ce6a7e9752c006ae4328b26c14af2a9500010090b520c63106b609f67b8127bfd3013e776bd4b4f83a3f2dc5f3323edcc6379a042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a274e1f0aeb5c2757211202b3afd2033ec1b4ef2dfe376ba5c6c07b45e6a7460afa4086423c4a704eb9a781514fbc513e190a62fd47aa98b58734af1453cdbbe5ef9396716775f8010200906d0a50fcc6324ab1f9b06f12b480343f2073cfcef022d1474246e21300b7996bb5051b05c55f903214681a5572d0d358bb6670d332f0ae9babbc093d00000000000000000000000000ffff97f455604e1f8762ab6cda7a38827e39e8dffcea30aea015ac78895cdf9a64c650f5ba71b36c25d29211bfee08619267d71971371d86400d33b0c9735c4ec53457758ed77a21455d4deb010000010090756048c6e7cd1c39b8e6bd88e7ce0b896ddc258c90acc24636da018517b40b7c11d44fbd23d92e7cefa2a5f2dbdd43ae11b3545b75a05c44a4bd000000000000000000000000000000ffff74ca448e4e1f8ef072018a444bc1f30885a199087d2e2200ea31acc4659eb0c05cca30f83e4bd940ed27873458fb605c92556883933b408fea2918afc5688ce5093b789cf08823a57ae1000100d0563e4969ed37631265d56b3a220db7bb7ac2eee8ac0070052117d8f6cf30a4452a37a4298f0adeb55cbeda2c2e7dbe77db073d8dd7d46656f7e808f100000000000000000000000000ffff8a440d6e4e1f8e845bfce2651b80e95f37e231301260449d1d89aad58729398208007ee430fc8137c323857bc75cd9bda7334838181384a12f2ef1a65969f60c6d2e2b2974c36cb309d0000200d06ac37501d8f509618ca7c43dfddda735c3d88e33fd06c8fd815736769e0c9fc1f5e900830f2bdd3ccd56f55bf78dc4d9d8be9e927bc83727015de5be00000000000000000000000000ffff23a96f934e1f8f31cd2f8220f32823bba9d844b2bdc4d5525c8d098d033058916421d6d782d29765ca45b156772f9d0f7b7b1ec40d0bf90f85389e282e08c2abcf50fe7c31b31c1873300000000100318c32ec1598fa8f38dc76e28d15c00c20553c7020161a4c64742524a18465c39b2d789571a3b223a5365fbde69b3fe7143c4f9ebc6293afb84d340f0000000000000000000000000000ffff23a3e2204e1f08b4c1a8b9c1402ea84afe7c47f7e98d657df873b9747a0e4a497120ec62c81f314ad91a6f3384648e7e60f2734554f7f4c0fe75eec22907d6b043edb0df74ccc7b85a45000100516cb4e5f54513bee842f3dfe2ff0589051959934d49b4aaf501161da8562442273b56d826b60e51d09d2227f31ac57cc7876849c1674a6e882c8f117f00000000000000000000000000ffff9b8aefd94e1f14f9192c3986e589f919f428c43770c3eca5c4ff3722d967d8f0d4b69ec3ec02fd876737fb06880a54566f56393899723d88e6f4cd4c285f48dadba815f581acc2e2df7c000200b1d84d597e2985ecd1b87beade5a07f7b584e84131951ce3664be16f7f1428043d9976e17a9b6d09c12ad956d62d86a7292d8cef56f569759dd8b8a11f01000000000000000000000000ffff40b0c6a14e1f8510cbaaeb8d82f2cf1fe1c6e6d3e7fe3d9e6ef58a5cf1c135a0d39194c457fbc8921dbcda07fce72e6aef5b54f9b060a2c6189093918db0942b8ad5c215989cef4d367c0000000100d1348f218e0f26ca214957ab38e6ce4548b38ee7bcbfa473caa087c84ef6420744d8b5d59ff010a5939f343bb8b8480fc2ebf2cc6e4d74ea6c22c8a94a01000000000000000000000000ffff4d1733044e1f12271e7f58e6f7d3e2b7bf724729808642a16369b4ad22de438ed38df62b415a92bdcbe7923a858532eeedb44bf12c129fadaadbe9448ec9a703912e80e3b90f36519de20001009159bd4b4fcaf4a32741056c96d5a1f232afbde78dc34398cc4cce95ac022edc042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a404e1f0db85a27cd589d225beff9977aa0ac32551d15bd906a899bc1ef33458d7c979118f92bf1de4ddb55144acc2f7cf6d85432eb51df8032392d44594907e5032d23df570b2d010100d101339e98725e8dbb87ce803366bb7af996c265f52387ce57fe7ce6067a46d26b037d3e63ba7f02f58d076423ad84ac06337a35f94f16764139d03d0d00000000000000000000000000ffff68ee9c6d4e1f145a212f37b991a6c050cfaaad7e83f1347486984174e8f446e59fa6c225691ea679f70613d829f536bb1a311d812cb85d63ccb848f51351e6623f5841dd00e057e46337000100f1b186e58c0e1247221f1ce97cd273f917e034ff4b66baee366ede691e0590a66348fc52b6a63a7013cde16f282347e5949d88a8f9989a46411409190000000000000000000000000000ffff2d4da7f74e1f8b165f653a3970a17f432f6c3abb8b681c71a3775f998fff322341d2994767c167c8a43b1b4661b9c01ef637763d4d814d31414b558eff2b3b489b6128ac6852e0fdb85c00010071ce9f30984ba8ff47618bdbb362be8315b11d7be903b37fb9ed2a0010090dca042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a224e1f92823797ad456d53ce1e6bde84e8a19164ff88a73ccd242ec48d9c6a479f2a049e214c7e8ec2243b7ea74ca6144ab2c5b7e99fc3ee4cf3d64c4438fc2a61cba12c0e73190101003193e57ee81ccf3c25937a5b585afa86c64d86b79f2b881b88c7302731f51292bed604587f137c806a8a21f6e1c593861deebb6283097b75d228710c0000000000000000000000000000ffff6418ef404e1f1931bdfa94f15b64ed9d09d210db9998dfa068332fee19d8e1ba4872c0acc3efc723e2fd04a64ef2da473caa4471c69e95dc83d7059a8aeca9996d6c5436db3f8c9924ff000100f19071a1fa482d2e511a78634a35fb63b29dd7895ffc76f9183d40fd9886f18b88b7d3b68eb85c3c4f1e621dba7b9372eb0876720287b28a1e694a0a0000000000000000000000000000ffff17f0e8c34e1f8041404bfd1cd4b71416116af92b7a17f42c47bf3dbc2294369fe1691eccb9ba851183a0e85a4fd728c946a582d006a704dbf4531a1c3aa937a63bb4d5a67f13a335d5c3000100f19c027c8aa2192b7bd34090aa59458b47a739e66db4ae1fde3fd5a3d864cd55108c07d959b5e8b4161be3a37591bed81162887053dc0e144fb306b0e500000000000000000000000000ffffc1758ec84e1f0a1442ae1b122649814bd8354b4daee4a289220eea514988d6cc93ec6302e346bdc77236c91967ff86362d81b18b1c7cd2f955d617b4c5687619d9030ff1b9bc39a7fb4a000100f1f0cf17436077ac9fd85ca3c5302f2e5943a55b0b53f6bda09b65d1ba3ca33aa0448ec3bb47abeaf361d19f0b4c76134baccdf2eeb87f292dacb20b0000000000000000000000000000ffff74cbc88b4e1f8130957c5939cc5aa59a9d7ebb88a03c8e60175230f87927f9485d452f6c844454148d8efdeb9e3216600b7f6645ecb8e5abcfb8f638610b4b5ea0b23d82db8e7560b4000001001192bb8a7f71b435686736d079f9c4bb5bbf321fbea8e69efa1b7643c59bd27a7c95722ae41d412c12b2c902075ccb8731e0b2e53de312bb2220b10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000224cc410b74ac6206018520cac60988122bb337a00010011168fc50551f544fc72b230b6b9802337fd6123da2eae9b44806230c8fc341b106254ad9848bab5b440ddf198401771eb5d373cd00097c140da69711300000000000000000000000000ffff36cb869d4e1f1622ef0d7145ce02babddc8762f170f46d5d55c151218ffc12aab3ea9faf90dc97bf158a0fe37a3d8c1fc416bf01bffde922c71d669bf4aa1fe543345d67d0be170b91ea00020012c8d756322b07e0cf2f73ad46f5f25bf91c8b7749419d02a6ccca8da60dc488f5364ad36ce7990bab58abc9f2d480903668268322b7e5099d089ccb3d00000000000000000000000000ffff55d1f3064e1f89d3ff18d870c0b6c9605d986c2de92f44c48032408e183a1d268bad77570e30a6d057a026119d9d044c1ce29b518e91d69372e5829f2cc1c3a9882e466ae9761dfc40e5010100a3053fab9f8566e701ad9bdc75f6f0fe7aaba6008f060100d28018e798ccbd797d0b2fc33513d64d60d55c92f4b35f46db169332dae95f4d042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a454e1f87be789e5b798cf3a40ff5dc22b0384dc690acadd614067c0f7e6a933b8f0c72c67b3f4b3e666e6fc48369a8161b04e64bd7db8a067386d8bda99986fd6661186757bf9501020092a52515eb33fd2e5a41600b34da9833c8eee685f8a02c0262ebe97a846a39ceb455b53e114ebd429d9ff22901fd4491b60c18d9c0e845e370dbf29c1402000000000000000000000000ffffd4382a70752f8a5a3ac96d75c412fa90bef0255204ff5ad428c68476b1cb397e3119d30d88cccc76460f4fc3196199f0abee537bc9e59859eafad31e4785fa131f8ef731f8a6361e6891000100a305041ed8b9be96f156e0fdad65444f01a10d782c170200d239283a62d1749e03339d948d117147e71110b725ea0d4c0f7a2f24bfcefcdbe254f745fb2633a818d888bfd8e0075640788fb0e0553faa003714052f00000000000000000000000000ffff01010101752fadbcd4b1ca70e220279cb084e9fb07f910acaa647297d0953b615418e59cdd4f7d72039538cc4db69bfff684c36e973879cb6f21bc02f8d721ad4aee4fd9dd5dcd92bce60000000200522655e57bf1414ab7668098716f4c474ac22bd7f4a3ded9622258e74de01a7ae026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a024e1f93943908436a934c08582583b08cbcc50b4478bb79b7718789c25eb0ad2f3e5713ad4c152d4b1fd13cfd12bf896072e663c8d3ed082eb5d476a0940ef9c3fadafc4c5d2c010100a30579e3282f6459cf76576409be3b8e7863d1cf78740100b2b69159973619cef08b9add969ba4d1ae18dd33bd62635429d44d93b62d57533d7c98b3942c5550e3bfcaa37e37282fd4b29bb40f677a38906e59725700000000000000000000000000ffffae22e9754e1f07f818e5c2330ac4e7f0ef820f337addf8ab28b07c9d451304d807feda1d764c7074bccbbd941284b0d0276a96cf5e7fce7f101b7f075273c892063b1b8571311737a576000100724358a51f6d02818a40ba46ad2c315c9fc0481de2bc18b36586df2b0f10de47dd1c2dffd75cd9fcbbf25810aa2a3000d4333bc9ac1cf9dce747140bb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8acd3790b09eca3c1f32c7f29e54fa62f4f8e37000100f277e7b83422ba404af92d4fde9d23050a70aa3339f62de35c9f00e2752c38f2501fa5e6a674dc90f03e715648f12c2f243306001340b7c5e59ea70a0000000000000000000000000000ffff342ad5934e1f16d49c42cf506d5687c4035fc8ea37c2bc293761412b8c28a73f674df9d3983581f53a8eeb7f1c7b6382bb0485df3814c3bd25702c4c5cabd642d5498f118ec73134c53400010052f87482f949cc953702e99a586374ec46dd519742c010d09a48d8be6e17900c23ce984b09397592dff901c68604089feb8e05a51fb93121e9fb35a57403000000000000000000000000ffffcebd93f04e1f0128cccd87ee9fa75531c9d8db129ecdec0931b57d3d521d35df8af82f6972bbca75f3861faecd3b701bf4aa678b18e5e1668a2c23b7a257d3e1acfe926e7fb5d64b24ea00010052fc431c2f463e03caf5da0cc4d393c5afa1bc779f766c854005f08ebec102cc042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a314e1f13146b3252f408f1cffc875b12b61f56c1ae02113b24c0b5aaedcda4a9b509332c8c4587450074f3e0906aaf3ceca7541d92da27f8fd00226f85d9e1c2ae33f2d57b843601010013784cc1b2111e1350cffad6499c3d9762776915e7cd851d7e9cf98134c9495d042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a4e4e1f826cbf63d989916d252cfc2cf827f34314c32c64acc0c252f1c3d42019589650c3ecb098655ba153dbc04211a1a73e88079a7d38b11c6a3e040e4e665150768b8a5faec401010053ac76891bfe83b92360aa75c5fe209a30be2de1201da1226f95b2a6421de70a4245e413a2d48a737b6f659b54702b7b7d81ccf1bb63d28737ca862f0000000000000000000000000000ffff0d3be7c527151438959163472114ebd0f4e72e984527894a871063cefbc8cc492593a7afbf4214538c0618ff8477590f40a3b2155aeeb0bd96751e06b5f3f3bb00dbbeab1576fc941f5f0001009364d20cd8691a85992cccfaade15de643fda1c01dd5a634f9fe35f9885f59e66bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff369ad3f265d88f72e69ac2373a62f14b7b1d99fb24eafdc87b74247af42a591aef0989c9a3e152197736dbc266b2535c4b4b53d8ec4aca9f4452a8988619c535d28f0b25aaf2c34b5d13000100b344a77fdaaa7bcb7d53927a255da9bb9e9afb3f2239d8c0dac672cb2357a406042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a264e1f0e9855b2ee991f988e446b60dcd637f33a782baf1e755785ca058f0398133bf3a95e4e77d4168c13c47d7e3fb1e3ecfceee87111a02be65095125160059b010ac7ca3d3001020013f125706542a3146d58cd5e4c24299ade655d0ea1881ee0fd84766b6acd3d14e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a124e1fb928fa4e127214ccb2b5de1660b5e371d2f3c9845077bc3900fc6aabe82ddd2e61530be3765cea15752e30fc761ab7307395853c0be5fb0f0348aff8128979368f52b0ee010100a3054ed83144996c18df0b97d2c2058dcf708f5ff2b5010033914802864685fd11a4d0695e6436a899fff914c78748bc71b66f31916c0ff6fb9b6198e7b7034fd9cbf6dfef92054cf5b915311c6836a89c279c66a100000000000000000000000000ffff2cea7d974e1f06af52990c96c3d5aa6d8d29ffc118f43a74c12f3dc860825818e70bbdc9548a6b62d680af91772ba9231378cb6d29254e28cb55a991fc1d6062edd80b6597f7e72d2a9200020013a65e07cdf3e391d8008c8922f4f8ea307866115f9daf78d54a0b74d10f444e65688e98145afac905339b6c3483019995fee19418be700d436f6b871101000000000000000000000000ffff5fd916b64e1990e7cc61a596c1cae0c431f22e92ad1ea142558e6e2fdfd8cba033040a4bc56e58f44ccb3380d10ce89246fb6f1c52bb21be19b26057cba9acdd22fabf0d3643e8440b650000000100530a0286e02cf5cd463c80d3645f40c0aa3d2f85bfcca5228538e49b219f35fa9906d6e2a830c62dbecf4a41efd17923b5d8e382d0b48be90ba1ff49660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c687a0e88c8907d91c576b0e64a45ad6b504caf000100930fee26405fee758046a6faf808843859ecf67ba3b8e5812a32353f5ff7562349f217d43ece8f23e9cb38449c6bd4f372030d22b5cd57d6831d4c230000000000000000000000000000ffff23ac34584e1f05d35fb7ea96c707d0bb6a9185f575596285d3578af73d43d2940e6ba7a39a60ad5d3a6f5df16449e4f5ba1cbd8306f6e37b26efc667299ef749a8ffb5a2a64bc48c5236000100b3a77b66d092aa07f53c5c176641c56733938b9b777cbc8cfcb39fbab4e495e983bbfdf4db4b783ff62c2c387a1e7ab3736fcceb26fe90019ef1808b2c00000000000000000000000000ffffae22e96f4e1f0620124f5dbe95b93bbcbab48452ba0cc47beaaf554e63db5deef90c10ca79c1e83c08a43d4316105419bccf65958023ce7f101b7f075273c892063b1b8571311737a576000100d3aba1cf7fc23882b84eff807db13920b387ce209463e48689073795d3479cb0eddfaab70cfdd909ecc0c0ab6b9f7ef49c2e188da5cdeb4733df6c090000000000000000000000000000ffff23a971884e1f1848a0024f4a1b85e18552105a7d397714bb9d16a392a29b5d6d18bba91fc880a6b20be09f1400dfe58de3ea87f919badc7ad6d9a56d9e5370352a41c4b9b5560100906400010074e11d7fe492ae20aeb94007f712e302d4f56ceff8f2af866dc143a70262053b042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a3d4e1f0d9ee7b7e66124c4b047e1f93aed5a764ed7384292737ea17f3a7e429ce3f24d602d54b97f72d181b6f093da9b3ad3f542d81ce6e9a394d9a408d1fa828a8bb7bf84d17301010094e54edf33715d857b31c66f23c19e952d80680e673afd03da2352f83471f642042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a374e1f0bde30ce81e7c6396e334eb1bd788b683535eefe9911286bb42662c46e3712ddb5c7c24d7998118ae089e804e14efee5bb34d97e1f0a6406ea00f10eaced5e3ef227a5ca01020034f208f213e6f0035ab464511b314634a746b8c701791a0d41f3186354bb1789e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a0c4e1f87d25769002af2a4f050127c73fff03a24935e48f34fecaacd69410787d0e6384b345c78e81b1cb397b43dcd635568b618d9316618b1f55977af250cc5d8783617084aac010100a305c15c2225333739ae2315c76e0c86ad0d4af3db5b020054dfd3371942cd3d802857286f333ff325c21258ef80f961f7a20f665de81297e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a0e4e1fa8dbccb130522909dc710a65728006732c18441757f12a338cf4a6d8cbd5baf1a484537a6a0542f51bb686e6e546f1a020c7f1991e06d09b1f067b049fe6d0af0a20f79c010100a30588cf6e3d68244d1ec0f88ac73aa2ce3b823111ed0100b497a9cb160b2094fccf8ae90aca774089a89406118cad3f3e36761d03952f26b41595579b1db6bfba5b323d1aa6ffb7ee300335ca0fad24f1c1e5eff90a000000000000000000000000ffff03e253694e1f8a50a9ec0042d293ca23c941142561632df2f182445a96d693b70079a085dc35073092d00761156f88a1f269a2d87e7ff98d462531cb02fdc829890da5d5f248c81f9619000100d4c79312d5f251e40bb72254875cf9dc455f29a4dcb635416b9726692436393955448270f86cbd25c837203a2b73d9c5172a9491d9462243a3310ed87d03000000000000000000000000ffff5fb3a4574e1f847383710ac1786f020769809abad8f93018338eb855c103f5239d75dc2767770eb45709c895c99c0c86d375ddbe478de8ca11e6c7d6a9e15b9676b534663298040b88b700020015081e2dce5d4c68da658ea66c17f6d055e1fe50fbc9e08c532ed2af93118d8be026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a084e1fa1749fecb407bb0e0ab9d6df65ea068dba5dc03e14dcb36abe5cb2b5c6e424683f715ff09ce290d035dbb31add0c0180b802f02882ed36279ba95674e7e190e86bd6458d010100a305c2d2297dd7a669758b34fefe84a482fe7be1a0a001003528eca40c0bd81dbec77ac49a33199c0ea7f1ea2749d783477d80b0cbf62794042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a504e1f959f6f2d48d283390b246a55b19a267f8ada326c8eb67f217839d5d1cc55377d8c1a2962cd5e80b892577454390c36b8de39cdda2efe7dde604c1965fa539df01785bcca0103007584d3a89e5de2a6e0152ca769f786bc7ee915d415ebe822c88fb6e2b98b2351dce9f6cd47402f05d1b12b8ebe1bcc23ad22fee342652e2d5d80290881000000010100010101043e3cf4ae4e1face65ce6933fbafc8686ea9e8ba72bc98f3d07905b4ccfb83162db440308e9255f708ce4f2623b27f9fc04fe11cf60571341a33990289395b5659a4cb30cc07ba002b50501000001007511221bb4a5bc3b4d984739add15f40991b6378e9b408f533af011ef2de73f74c6ed20b7bffece331c67765007c96709bf598d6ea358f2ac50797070e00000000000000000000000000ffff23b9cadb4e1f04f1a3407bf953809815243d539d316d2b055a57ff6c5412f31d98f0d5ea84f54511fa9f02ddd6d7f8751505c560eaec8a108538dca5b073f24fb8db78f06ab6a0aec27e000300952e8f1318a5475efb7dc10e164d47c2f5bcd1fc0ffc1fc2fb66c1bc53aa9ad3e68663ab8ac6b7e137b6b68e6db778fd51f15af506be2359fbef7e2db50000000101000101010455d1f32a4e1f9321b7d8d72b12d10b413673a65b81d375bd0cc1b7f05f027d974d30209452167e577eddad6a76ac737d1563c659d1e1a5f8e9b87827f988a28e63b164c52034b8ceef820100000200b536b4cc225280ef6d25db11cd63ef44a69b7c6ff0d290fbb830ca450ca42e0b48e6c98ccdc3ba7d98cfa062fbf538ecad7f3c5c8e53edcb4dd76a9b3f00000000000000000000000000ffff5a1029be4e1fa6aa4c4a277dfa3d3a42154b286c66f2eac4ae88d6434e1aec898118590f767cbbd0ebdf7362f308a67adc1bcd629511eee4c74a4dd6902ccdea0a17ec1d65fdb03819f8000100a3057ec099b40ed522bbb41032f59b25c13195f6f9040200f56ac75f5801afb0fbf248e8b39db1dd43a55f1d873beb9674989c51b53d73eed24cfd75ef1bd1889ee6999e637e43c333c7f20e4fa3a700b8da0f6fdd00000000000000000000000000ffff0238d45d4e1fb695a4757d60c472f67a7807def81ab7a52dec3bc64aa1c4188d02a4768ac3a862b404f0656ed395f1eb6470dca7bca45b44dc4b3f0ed26a8243afa46bada49c18da6b2a000100a305414289f74d7803fe50acb5188c4391373e9155830100555f6acf211a8abf3c78ab20b2071c9feeb828ecd5b1922cc111cdbaaa7741f9ac8675cc73f106b9b0341300967cbb44f827894b452ad11055c6cfac3e00000000000000000000000000ffff9de6f7db4e1f9993c900fc49b020d4050981a45281cc71274196c57c9405f7ea8d82823b2cb36c04a2aa363111d74e383bdf9fdfe254365a7fa9a52936ee5272729a4011c8a00c0351d3000200f51315cc297f031bf846dd64b7a29f4449334d6d77030501b295a3dc99db48840ae9161f20d9de016ed9fa9fc4251b2b93377a1aa894885a2a67aeb87501000000000000000000000000ffff0b16212c1a0a926c2cdc34322421c41a41ba7b3c088d69e0da3366468438b6081a7ea078bd65fb3eaf75e0b099b44f3335983086e8e0b3054e0a4b97aa63e61acdc2cf8e4481a70a6dbc0000000100f5613cfcc6fc6111d2db7d1372eca86a750a7b196454c9050409748303166f43293777edd2b121decaee02ff1886257a3507011ba587ac97b97332512700000000000000000000000000ffff03d8c6fb4e1f07bab9e0cfd301779007735cfcd14445ef09191df5a8ac39aa177abcd56d20e46f7aff4286ca6ff02f4747c0534dae9ae7cbf5568870f2e301ec84dd720ce2d71d2ef6db000100f56ddedbd69c005b9078ad7268a3b33a64ed3b0d8d1cea619e3aefdc1a3ebd5d6bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765a7119d599048331efb5bd38ea0506ac51765eaddf396114dcf14fbbbab70b7a929c9227f8ec980851ad79f502d2fd1750ad0a7d804064376431fc57385721270a55fd9915400010016500388c354e3e87cb78a87e9c1b763cff8cf96b723adabe91e5957ef5d116393bf68626da222480e1076eb95397f66368bc699aabe83412e1b4c020000000000000000000000000000ffff2d4db0104e2002ede7bba4f6330aa85b22f2d20167cb529ac1334125ec439f873c0cd7d54e7c07b65bca725799f8292564af4296f06079cd5958432147b2cafde22aad47375e690a9a4d00020036f4aebd4b3e42802861a15f33932f475255928a955d7183713815667642655ce026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a104e1f86d0a2ca6f434eaa47ff6919ecafa4fc3b012b89c62a04835a24c00faf62c3d30d3f8755c33a7abc595e96fb5b79594a7ef7c15840f96c603fcd29409a70f77f878d25d8010100a305b8764fc1c19db2b84ffaace89d35576d05332a97010056a400ed0b0ef5c34858db07d9e47173b375e4ce4b461da1aded7923e7eed171c351109755e91b6ee895bac465676004fc8e5abcf8fba6acd3e6f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059b1ebf04d63af3ef87391a0a4a7b47f5f5e7911000100d6f87eba9c62615acae80548de4b605ecbfd92bdd09bc8b5e74ab51aacfb82c56b1de24740f3162b179e380a9f7ae0d559be917d4197cc6fc03ebf502802000000000000000000000000ffff035aa7434e1f96153a0fb857c1da0f6bb1ce2e569bebfa14e6e1f532139f9dcb720d481db17999cbcd8eb66a5ab4c0fd20c5f9695fb1ffc45339be50b5953200438ba8c71ebabe9575e9000100f690ae59eb283f2e390b9b45349dbadafca10f9df3ed955f1b631ccf306670b5ad01d34e972dd43bd5c99a6c48d887f3719128772c34adbe1bff43010000000000000000000000000000ffff9de6137f4e1f874b17058e37c39f770188dfe8e699959654d723e62e28b2760900e5284f63f6b70e077a6ea9803714bdf62d083b1d9ed157ab8fe5436f4c7a475be8a35079b3aca6c5fb00020076354b8d3ef13f96d91747b92a5f20a35170350f44e4a27de1d19977ee0c7a8a152c66931a771406885c603a92177e8ea175f76440015d897f9fab071400000000000000000000000000ffff55d1f3294e1f8770575dae922740f47bf040e13281a74e1424fbf9990edd3fb04d84f51b5edb1ed6535afcbf235d8af573b90b3a16a55345c347fa2df12693cbde46457c3ec1846bc717010000010096b19030565fc88f841184c2c8ac418c8e44e36a4420bea538b1d9b918d1db462f50e5fe5bd461c956c07a8a6280549b6decb2965f787c4e2a70c0070000000000000000000000000000ffffa516e93b4e1f97adf3867ae5155b18345e44e277ab26b9a497c7d0cf9b53bdc42362dff3642c922d1d10e277ce6bd407f48bfabac68be3bbcadcc1f89ed98a2ada3473bde7dc618cf3a7000100165a5b17d27d77e492ba93d65de0ffe941b2875f75bcb3ccbae278f4a386b40c0412db080746bf4cab8c4251d5c1a893d1fe1656ff5fb3fb73a3261a330200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200be0fcbcb2c10e968ea0b5508bc3424c9c68b000300762aeafadd5dcce21e72cb613a99ba9185f07f196a025427d8bb5d32c1b19657e68663ab8ac6b7e137b6b68e6db778fd51f15af506be2359fbef7e2db50000000101000101010455d1f32d4e1fa98f8ca3644fe1055e2604556fcc8edb9d5191116fe2dac57d12a4afa3f2ce23b1aca5bcecd726e271987b86dca91581a0699323fe85451f4e0f6fc1335515a196f4e9230100000100762fcc85ca4b2fab16e27f3756b84a69d0e34ebeac3f58647e51e9d58a4521c383bbfdf4db4b783ff62c2c387a1e7ab3736fcceb26fe90019ef1808b2c00000000000000000000000000ffffae22e9724e1f17a49cea05ca2e18f74af110c5ab52c89a43ced4e056a8af7ca8973401494bdaba26d1c56b46b018091d0dd64f244750ce7f101b7f075273c892063b1b8571311737a576000100b63b44a79eaf7e763434d0510b88dbfe73f9b589bdf08a6b53b0b7b8ebb8dcfb042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a234e1f17f2a4f37de1f78a0d356835de99c7639d8daf824b0d432b2a7c01a921f6a8cd4028272e2962f7d04abf9bfd5a77ab8e982169553d0e618637a1cc6847b185baa50a2ae4010100d6373ec9005b1c601b7c593318c109ee53eca8588543a7b277eccfa4749ac936524b13bac303ddf4f82ef48e21a8a3e59d0ae5a459d2fe9a9a21b5130000000000000000000000000000ffff23af3e6a4e1f8f2caf4cf1e01130aa6bcf27784bb36a2b4daea4ada3be553c9b709afcc752d0f34a0c35e3301e8f6a2fb3ca44656be27509e1003c6e1b4f64a954ba6297e4554f3e735900010036c97361c1b5b664eeb743d04fcff7282e94a37732b99779d027dfafb030c3fc9b69e8417d48280b521fdcfe126c56b1aef3b06128de4905ce73e9ff6d00000000000000000000000000ffff0381198e4e1f89aaf743d70a26ecd18aae71d8e2ff0bfc98a51511f83c8acda856e3f5d7c61c21ff4e19a56f02ecfb6ee014097945e37200be0fcbcb2c10e968ea0b5508bc3424c9c68b0001003675a49a423973487143a5321af768323e0bc79d0772e070a367667efc1b4d8d6d4f0ae98c4142e5ee1c93b8801608426899c0b2af3fa69212da12170000000000000000000000000000ffff2d3f68684e1f05e588704a6f6d703617081d8328c006b1173d60aa26cfe44b954f1279a1ba9a042bddc5b3a00cbc8180676d12060d62571ac9b0061529d85a340440e6fd38a3ad5633c8000200361e3d11a33d09ddf74e0fd62ddff8e87523608dc513f252da58d984b1dd2e7f75c5ec80d4226acd2151e3ba753464a443662f99cd2ab2f3dcd100951f00000000000000000000000000ffff3270e3264e1f8f7f1c9fbef0d00cf365bff2a3405c777c1cb0a89b0238208f599101611159204aec8ff5d1da136acb9cf01696c8d5b6fc512d1f32f0b80ee1b5de33e61039ae51e335dc000100a305f52f517fd9f4371ba168d88b494bf863c3c0a37a010036c6298f595939395ec930f936452726f33a311a79b2abe290ae01aad0200116042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a3b4e1f066d57a6451b7800c1c2a6c6e04fe73ec2e1c95e492bacae760ad2f79ca3c30727ec9bf0daea43c08ff1ad6c2cf07612ae2c14ab7113b1aea921177e509bc59048556b97010100d62a3f54945dda86301e5b2118e6962c79b8d6461c075d97f1b926eda4e7dcae042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a4d4e1f007b1f3f16835ebfba6f505f43c6de757bb22ecf27a89703e90e43aedafea3df353a5bd1915b27e8db397d53f0a23f60996eefc4c3da0c2c59fceec99aac79122c44d632010100d652ae25bbf28109fac407dc21370c72581140d546c8b2fd48815b890816d28769f2a50ec4d41bdf33aab17be8f06527e573f5452fb533305b4a1e000000000000000000000000000000ffff44b7a5362714974b7b4e608007f22ece8fb933fc18d66cf35cc0e5a7977279a092976b501786d4ab9108c7fda681e23978bf54b7709a419483f32fe1e48e4fb5ae37cb31cd39247ed46a000100f682389d5e0baed394a65cbdd6a666f59271943d935f85c9d05b3433978c9b28042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a3a4e1f0e2825781a496023c8be61f2bf352ad1094afd6e4f84c4ef331bc727bc149a6dd7e23d78944b8b047c03da44eef1c7961c6031c212e840f9756b8f08e3801cca20b38a62010200f6aec1912158ae25c3da6b203f7a34c519883eb54542f652753e5393d8b1e7c095c3662ed8812505152aaa218190f92a2ff7fc2da343c74d1ddeaa363000000000000000000000000000ffff55d1f3054e1fb362c9e1c662e038e334bd6c5ab36d31ec161df27439b2662acb55677815bee23929ccdeda8df9a8cb5d97f5c2119555a6b1db0a8e55b28668b0f6ae6278a0141186a417010100a3052d71c0ff1b5b842fafa51f0cf9c24c37466772c90100f7c01894c6a372069b6ef22eec5b383d59760ee487bb252b9e10cf698e8b801dd47eaaf799e52fef1ab499c11421852bfb559826e7656b4bca416f54b001000000000000000000000000ffff22aada884e1f9687b4c357c8faa53e6733e83b77b94e92d2b528047e2e4cad325810b5e4856b7ffabfdf97c825d224992838d7435d7591755d751fafdbf3c4d1e1dc94c2bc198f7b5bb500010017a635a425059e7599576f010e5d36fdfc7faf165d3884cc88b9aa24abe8aac0042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a324e1f892a3de6fe305d39d81ecc9dbc7c85bc6eb57434618903f45ac8996aecfa9a7945e2cc48c40c1540172096229780519eec74aec7dcccccfaec4c5fe65166a2d5ce0e326801010077b25d8809df68a929f68e7f144dbd1ea0a184f3c7abdca58bf7c3af647fce7a042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a4f4e1f826fc7f30c49215b98d5cb47a350f888a306c52fa42c77e765b55288e622f03859273cae7e1cac99e67f7a9a96a6aa2c139d5a4134c30bebba6a178a1c8cf91d8bc47b3301010097ba4d7a90ffc251678e861007072d23c7276026dee3c20860d5964c5e7e800c0076b3d996d069c2d5c2b8918c061ed68ba2b757b196e6d69e87e6040000000000000000000000000000ffff6b9679d94e1f088f0bea4590c29e0a8657faea9d5f2e0f79cbe8f1cae3cf9111e84ecace1443ed8dfe136c539019684d9511d1bba8070df5db4ce6161b24565f0e90a154cb4e38751eeb000100b793ac00290abfe0ae6e8e96dd228c0a209d774ecb90b1e7f384e3e975f2aade042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a444e1f17f78abcee6d2ed68bf2c82afbf56ef9af67313e2eb655ea5178850907cb3057cae0bb5a1d09f161057bf62f9d4890c676c44d67639aaa8fb877f1db08d5e582b570c25d01030057084e10bcf2851de4a8140c15fa2384c93f4e9993f2589f47b8f9a54fa078cce68663ab8ac6b7e137b6b68e6db778fd51f15af506be2359fbef7e2db50000000101000101010455d1f32b4e1fae6dba4a6b6c1fe9fb23ba8b22ff570bde267c0ad0542b1df45f64f931fcc083850c95fae1f7bfd478f6ff3dbc87385cfa7a0d39c7aa1d2f8e7141b1dc37bcd149cde1a9010000010057606f07ab5e314683cd88f7984706da8ad0b9c3c1811f7374b3653cadc0a2aee5f9035ab116e616ff9d6a2cb63a6122d36c2de2ad711f0d31f63fcea508000000000000000000000000ffff74cb570c4e1f95234e6e7d476318b4811f1daaa7e887fad24b1499b3472a3a7decdd88e8bdd14551b7b67b22ab896adb298600aee96fa17349d2777d28f0cc7d39fda9cace46c23036a0000100978c1ae2314759a97b9e36de89cdb8764ba13bd83256e4adfd0145c0619cced1cd9dbf335d3cb88e8659575f6a810a4055ef83fddf0295ad851d7cce1700000000000000000000000000ffff44b7c45d4e1f0c49037992160cb8d7f6ad7e13d778fbbfb5d10230b456bb3aca1c044e79fb15c3b1fcef7efac59899eccbd190bdc40e227ac0c292b7d209dc75272563f6e3e40bcd4b66000100975c1fdcf549fec88dba2714f9536c0fd5ab0bd0cd5149eae9c1d578266a4776bc20451b8a05c6cba10970aac3cdafc549a8b40d7c23b9481a6187100000000000000000000000000000ffff34dc3d584e1f10142d44041c90621d111283fe46fd8b2450d4b9bebad194290fce09ba080679c748b1ba70e3959623f127af0d2bc9c46dddbc8e0c2bf0803d236fcba6cc32a8123ef43e000100d794a4e51ecb0e2c53dd2135af079344bc46a5705da9eea9f119312d91a8134d7b31df0ed546c685b38d9e05e3b3144d351e5f6fc46aa0b917a4f3060000000000000000000000000000ffff36c8c8e44e1f09ca23af93ce00a95bfefe790ffca791e093a8c0e79675b103b2a4d06f930433b3f6b15c83f4e2c4b5118fe0c27ca13ae855484a6e6f72f6f4793b80cafc202a981fe8c1000100d7f0144969037c992fb98d7a5a58f108d98b2683222533321218875cdb3241906bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765e1962c65927aa1616e3783ae7cdf8c3d19b4c26b477686a9f146cd9ae40eb7c0e01a1580d5df8c32d1f4c43a52f62ff5e06efa257a906f0adf9b184b0738a187a5b775df68000200579b91f33ba2a2979bbf1fd235f041739acd7cbcd52c64eec5e6f982d1a87f64130959a181252f08bbb4f7127a7ff34765da22d5f4de66aa4f2121889b00000000000000000000000000ffffa45cd8694e1fa72192989a29df2c4d9c622b642732367bebb975bc1d492ea1f29bc65a471e24c2d4727a021c55c6e2269161d3990dcb9c9af58998bd37f878f460e6190fc94af0d3cdf40001002659116e45f28f4d2cbade36dc87c12a493a5a7f5306020057f35920a1c8a292016ccc7ad12a16cf41e80ea33fbca14923d636ce0d9bff668222bf6dad2d1d743d75d1b3d30e238865b1a0f5553720cd23aaf0ba0100000000000000000000000000ffff9ea012384e1f8c2d2fdcabe70bfa6dc5fecddac62cf29a8864543efe198e21ed99ed09d2b7a023ad675d9b7f4ea2005da649533eb1a13a32d03423002944bf6b79fb9d37194011754760000100a305988e9f546a3c67157ea57d86027771ca773aaab9020057733f716783008f7615d056888ec922d584e5473b7aae240dd11a0de890b86c4ad772704a0499308575f6b94c384c88fdd6aad5a65efaeb4a1901b190000000000000000000000000000000000000000000968e86e1e7810c7064363b9e4fed111fee65852fb79bcd7123e83be67837e5cb9a368761095cfae018b30ee932d087309fd3714128ad774678e9704baad148407bb41a77000000010098ac3707cd76f8ca828c8b7a86acb0f3768ff9d89927ac4ea0b7cb43fcb3e6716bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765f1975c482384c7bd4cfd5930fabac11646121d420e31883673dfb6e6e3bfa273da73a2a91b4b69cc108eff9619fdbb4cf40883903918c1154fb2b589d80caed855a157b1240002001889670d9b727e3d39b2f0018c64d47cb60c3346ce97bb423b3547ac34520787e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a074e1fa7afe7674de986aff5e2e0a173be8c29abed8b5d6f878389ea18be0d43c62ad1ba66a59e9e8d8453aa0ed1a696976758762e689e7bfecbe16bfd0e6138eea642ee0513c5010100a305e3053f65754c630ab036f1370bcc6835fedb9c9b0200b829b67fdad85ff45a551e71b759f56c42ef822abb9597cba60bf83b1edf7160c7c3fac0caa39ba258a547088c1c487ea7031dc00314ca71509ad64b7b00000000000000000000000000ffff2d20db704e1fb4e545a909b1916959139eaea845262b0aadec1c5cf555922bb6e6d1804343e6490428a382a0c01832fc70c50438fea6ff1266f2c8aa7b1f93a15b8882013af424d7d9e8000100bb01a4a889553772ed86b9adec63a244752576d1cd520100381fef4ab9f067b9782bdf19fe0a7d316a9a0ba26ce2b2761b41a1970ec549b1d8b78d6381579c07fdf1c9ed1782262d0a11c2ad1edb94c4c00c2b100000000000000000000000000000ffff342353514e1f8f60f80538f335ba0f9f5452f02d7f5527652671da80c3d1c10e31e040f9b901a53b476a9ce02b507958bc8a65acd7b02adba90fd804d6d89438ea6729608d5276687fbc000100783fd3ab928204573e8a9f1cc91ba620dbd234344c49ac22ff5bbba149e9c26fb2b5f6ad6373581fd4a3a11ee33f81a5896330d5d76cba65efa493c3ad01000000000000000000000000ffffc210020507ce032bfab78f78c968f4a1e7fb87d9b3bd75dd2a49e18b7592e4274322660c27f213b898442eb41f5db42291a2172508b6c8a4cfec45b3e5793f8413968d3bd6aa7c6bfffd000100b80f165f4331e9c2a4b10cf4aac450eac95752679aca3c32cacc20a7dd261cc8fb48c2ed60e33321f5f8e13d0e074d5145891680276d3d9bcae5bf67d701000000000000000000000000ffff36bead174e1f10d9901c0aa8f9b3e789a1413e731ff07b9b58d4f53925f53a1502f00e6ccf056dc86ffa5595a1ca5c02cbdfb38b1cb8e614deca3fce7919013e4e440fd3d4721ad3465d00010018032b1f077f0e7fe6ab2d7c9f96ef2268872bce9d6a6ab737bb2e0c0d84993c042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a4c4e1f121adeaff038746afb470b84fb3a58f1a3bf304cac771d16980845c4902e5da34d366394c8e84a46d8bc0c0a1da23cc6c682e285e7a7bd65e9268c1bc695091eb4ce5a8c01010018732fe66305e3ddaa0f40c3547f1e0a37f5c2621fc716b43ffd1a2eca81d18435972e9a83060119098c4fd59848996682de342c230b52864f8fe2080000000000000000000000000000ffff8e5d284f4e1f05b69b964d581a7659f5fcf2cf4a50a75e9cacccebc4e18d27364225eb3f9886de5472cfffbf9cf029f81b49037e27a2a4e27301bbacc1c42b5e2268bc2b4400599edcf10002001999c60acd2a8a66ca833ef65edcde933436670f70e57d693e00ff0856938df52cdd57d96576c6336413df28b773df1dc6f80b73332630e4e2a252e34900000000000000000000000000ffffcef5a73f4e1f8de9ff4dbb2fab156e5b3d8aca182a4c6807fa924307c8b6830180892cdff466f3b43ee0ef4370d3ac4828e2dd341649acdd4afc43b1275e912db55145e5947bf6735c4e010100a30502d89fd0aa31211b8e8daefc86713f7b625ef69e01007941e0458946e0dc791875aa660f454f898ec81e10c20d9229a63d4200679b86fc07fb8b877bdbdaf7019b035a32cde8c20d8c06c4122f8baab4540200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011b1a772ead943d16af20c36c7255e18ea5102700010059160c6bd0c377545d97efe36e0721538707364df39439b946efa29873de0b503ecbcf63d7038b1ecc838248ccc37e3267844ba7d4cbfcf24d79fd566701000000000000000000000000ffffa763b7374e1f822967c827427a4ea722459a6a5d007c5a14e1da4b6fd52417914cdac7dfbc9233dd046cda0c2980d1936cfa8b22920034e4669b580af3248979acd2db51c30d0794c76e000100f9aa14a66c5edcabbf6857ab16b02e1b725ff23f6fce8e9d174d87b00c1f496742d70ea01c8e792ea8f75df86a9c408511b21172b45a53c1f6c0251f0000000000000000000000000000ffff5bbe7d851a0b91e633b72726091f58e3bd1ede3a21de66abb2456c2f669be8bdcf76f3ab76aa2d75f7d03cf2f7d5761ab15e62e00613de6d2589b9cd1d134c8aee733d9ecf70a21d334700010019ff909c374dbe749d331662dead647a91d571bd0584002e4d49cc98c98cd0bdabea16ba0d55702d510a3d43c78809f37f7c7e7f8f75353c68b2e3cb3000000000000000000000000000ffff951ccbbe4e1f03d325fdc665db2900c24c0736b927b7fa7ba7068c9595991e6cfaaa0f8e1269a31f6d1da2b85fd922186a9979872cff68eb72f005d366e18db38f638716d97bf60d53c7000100597bf0c915434cd9eddcd2360804ef07c4f06ce7cfe930ece85e6da95d3a5b5a7acda8593971d282f279aa9a5bd80d87163cc920353944b2af758e80dc00000000000000000000000000ffff12de6f464e1f80f8dfef131ac329d428504e7cb89974f188f07caef0668df1daa4ca8fc5f50f6c5945f020271292dc220ce313c00f16f0c742302a89db49217e328e18811d0ab1904ac5000100b917f7685d22ec2f95db103b2d18d3bc7c280106d0c07ee21fbef4c07a22e14eb29188c03efc5303b140837097f45c1cee91599efdf9056dece7e525070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000258f7afa5a4ec575f6781fb2d91fd59f8a409d6000100d9ab0e65c1e3de4eb2213dfe0c1a01457f13e6798cd91beb15a714709c874b04042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a1f4e1f002a2e19ac4b986e20f55ddb19cdfa69cbd5f76c5e2d66ac6d9c8418aa1f0836e61b643bf48eeb004ccf3f3f0f03b82acc93037f5cdf14ebd273fc3d4999b9cdf81865d90101003988b5f83343213ac50805d34bd73f1e06c48b873e46ad299aabe92fa3cdb2f281ead6bb4f642ae5995579e3d25feaef564a3ac6314ddb406661f8561000000000000000000000000000ffff22d1d3864e1f195b44e1d553d160abfcf70b8ccfaf24480ad34fa7917fb87675f712f0795a23dd0107f5f3e39c07474697e95b15170dba7c694196934a70e11ed92a56db5766131968cc00020039c4c1a4d6dda0d3eb47b242a5919a81d45f693915953c090b5f797c538eb83931946afc2f225da2a9135e62ab8010ce3a0f6ca0dabd736e980d5bc4a300000000000000000000000000ffff82a2e1724e1f90d98ff5c2f2970197ce80ca8cba9431f7dd7cff5d9fc9c4e99c2ed06e598f273d31dad8d8d05655c8b04599495cee23bf951db34349385992e0303f21fd7240e44e4b02000100bb013c32b44d5fcb611eab1ae4705d74792f7a6c978e010099941c6692c2f238e96fa0df46f978b9013b660cae9f362f9efbebe5ec94332d1578183584f0734134246125dd61453e764fa0a4dc018eb48b470fa36d01000000000000000000000000ffff50f017c74e1f1260b9b40d6a39c14c5f52763ef6e98094a6d41ed35660ba24334c50b60cbf18a524aec8bd4d0203c3257e70e193fd308c5f76e0794a0729c41142e619663b77948748ab00010099ccd9983f3433ae1fe5d5cec96721dff60b527d0f1ee41f4e1f9867bb85fb4143c7372049ecabb23d1ced9b706f49623748f322f31b28b0cef58e000000000000000000000000000000ffff019f8feb4e1f8e53b8ab39fcc259aa22b93d1ab4e333353e6d56b9bd4d194985a59e0de5060c1225588a256569848ea421725223711b07ee5dd8d1e99e4bc8e015d94346580d7cfb1c44000100d9f20fabe28047ea6b5c426ec94868f34f5d98012c5f9549b9d259e67f71d6ef0d40092b542aeff047bb3bca3f9cc454404d8ad8475b7defcd754e18a901000000000000000000000000ffff22e0950a4e1f1560ff0ddb3c1e8bad9f2e237b3ad39c37ba804fc09d4ccd928362ee874308b29e827ea60c21b3c04787d771a16e73219d39d1cb8cd5844cb453cdb1a62b73e0ea6cde7b000100d97629f8a789f38d97356aff702276d6af3597f9c34816220b2e3fe3d2d75e0e042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a294e1f01f24418ec73b09b00514dba8fb18d6d8af1dc2ff93d594bf987911f3b98d659eb43286cd450b7e1ee5978b361660d737d0138cea5135b7e0d5ff96c57483eb5c155f144010100998f9e8a86d04b8c70580a66368bbc4c4edbb8c8cd3beed3ba3c3ba712bb4fae404404120953a8d35895ca9cfb4b61fb0d02872397d746ba3407d998900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ac3feec8ca9b54c378faf16c260b0c4c57876d100020099bf316f8cb867be68dc9bd89bf2b8b984eae2fa9fa6ac19e862459cb7cf7939a2e6fc8cfa816c2dc9869773359c7de54bbda0c587f80b10ebfd89a97900000000000000000000000000ffffadc777f24e1fa73d8c1e640d29e2257042a39bbbac8d867f69ae252e146884816b98ab0d0526ed4992d9cff22ef04878423f66583382212e07f7407be541b585ff0a8225fa1afbcc9ac8000100265901775a0fdf52eb8f479398d971aff60740c0b57101005a94cc400caa13fa0ef41df4497ee1256040467dfd48cb94bbf53460ab958113299b4cb4b026c7a1d33f0cc052f3246c99a1b0083db0290a811bd3e01b00000000000000000000000000ffff23e6538e4e1f0d554f8bc61403a96326c47b95797ff0ca91971a73f50b9c95837b912d8f0c191a85792f7ae6106fbff7e51d50818b18a8f3150f40bb0cabcfae79e98faa3d9d855d9f270001007a555964bcb9ad2701ab5af920a7d98472e489915e247d9471f4fe08a1b76e6d49f66db793dc58cf7506e10bc9f44202a71c8b99fa9686288a5af335d301000000000000000000000000ffff5fd91a8749c8955368e9fb5cce100a0ce6df64bcf624355222e19032cff0c80cbc75140173c2eb47863b189d2423b64af6544226bb50387b6ba2b25346e89278b4fae874858a8c09960f000100baf99a2b89bef2f1225f2676a00d54435fe139e7890d7ead6ab9d64e59d8b92a606c3240b152fd593db7b263fe949ee92b07c20ee42bf0097ceca6430000000000000000000000000000ffff3ada3c2a4e2104eda2a8c31489e17463ea27c0c39473afe2c9153641028de360eee8ce213d36a14ef8f8b4f85fb2cd70815a9c1f56db78a2c01c623373d2761103791484d3ad1164c06d000200da61a86f405f6f062b1bff6c84dfece7a610555f1e0c8c98cc94ae38b8a221e08cf77bb77b7650c4d89b588bb4c3d543feeae6e157e4056bad25a31a6900000000000000000000000000ffff2d8268514e1fb7178e93c29fb6205ae5672a8683ecd55f16ac9a26c3401d8ea99df4c4bfce9436b76295bf013951d5b6659e41a5177e86390b1e047a7e74910f46f107c5e897e7ec746c000100bb010c7fb041c6c2e17110f0495843f02b757628265f0100fa2ea57c1332790ed640cedcbeaa54614f7e70522477a4c04f63d903d35831db6bc82c47d2c643374a47ab74bca42e3f2033f995ebb3ea55807f5fbb3600000000000000000000000000ffff34d4134765b0989f584df6e5a359bc469a4975baccda2bc6a3fc3e89721c639f5567db7abef79f31ddeb4832b95418d49322419d3eb0f312f66e9433c75b461b481f0576d3acd398c2230001009a57659092f2e0943e75e3e5e7a767b9d8bd793c455b069f2de962ffe57ac3d0e25bbecb8cb3a1ffde3031ea275bfa8da4983388efd1a136b3076999e400000000000000000000000000ffff8c520f8f4e1f10ac5b643b6dfa29989103ad74641e5fa27626492f08b8337a8415b598ebf2bc6676c73621905a71a15d7bf9f92d6efd6150352212815041fa5dc70a94c7dfd507f486190001007b88a1cdf40dcdefbd47b1a96bf88be1580bea00597d3b99a3f8e690897f0c40f3e8069958306c30252ce3b2566cbabcee61f8bbd6a0c5d219ea13060000000000000000000000000000ffff5911296a4e1f848bfbe1bf50debe1322e14c9115adb3b96e5b8a3ae96beb7e2161281d9e56c30e43478d6f39835e3533a1c54377258b724befff5e89d92cbad3cb0cc8281020b0243bd80001009b9460f56930a95bd5f8631aa6a1ff1e8d43e4d7ca378bca46d8d0f662dd4653d02464a8dbdd1302ff3745cee38bd3c0ee6ca3be3e19489afc6956080000000000000000000000000000ffff34348bba4e1f847178ed08f0f5728dfe39ba9e3a43555b4c5e8100d825d91bf452bb7dad7bce7e8224fb665abc59cfc74d3bd1e040e1692ac6d2aff4da4de4a815b41b58369de627380f000300fb7dee56a72414f3eff6006d0f31213c842d7c3c3588dee47b793389473fd361e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f80440100000103000101010444437a054e1f010101010444437a058f30020101010444437a0505a3a6a63376eb861bda6afa09e28e39ba40cdfb877ee6f9aace10eaccd4caafe8d9243f2f2c0ef982a0766347073cc199bbb5b414e1a68e36a25df214ac6eba35782a0437da0101006ae924bc352f788d3c83a0949cb30964483eda7a01001bf266fbb2b526bf5ebab51fe983985f579c89eea40ebb2b6963ac0d7474565891a79d300a8d16b09eec921da12c58fb022e34186622a8fc1c88bc120000000000000000000000000000ffff34dc85584e1f93dac0f5d028eeddeaf4257919511991872523675ab24d1d971af3ab1900f27fc617d1d53a846c32abe1ef52a2cb26ee0e03a8899ae0f3a5771fa41ee75752713e920fd8000200fbf60e95f35aa3e6315669598bd20cafa41a0674b87afbf2b40c9c2e1a65482ee026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a114e1f943a88959611417f9e8ce4e664e1d9c6a839daae14f54ae8e78bc5ef6ec1524d116efca49ecd5c57dce31d90015a51ff63f16c50b9cd1dccd8465e1802749bd00dd49a2d010100a30524dc9affa9045729055355f5b8dc4125d3f1fb1f01009b3fc4ec99cbb7d9d47bbc9e2aeedff625d6cdf1a0a8351df569ebe98a029242042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a3f4e1f8f70ff352844250e267b31c0ddb83dffd4cac43532194bd47cbabf410ca29fa7f1ecec08c8fde8c0d13910e903016d5aecd8bcbf77d0dae7f59c750bf68d5d33a45ddfa2010100bb23dd49b698629e519eb1a09600af7b270e2673781c408ef046a7ec7618dcc0c4ea50affac7f7798f95c54ab3ef3dd487c7881e48c605df635c253e1100000000000000000000000000ffffb280576f4e1f03f959fdcb3eefebe409ee7044748f71ec8cba18a7a73df9d55d118e170d7ec2540d5c08a4cadc4bdeff3f7886265ac1febd730bedac1010a2943dff1c796cc544609f7c000100db82e4b93c64f29c1c31ad98326836f404a2143415b588a0b09c3dd2358e3bc41964152511caf8008e3fa873bb8c35afe6593d46619c5dcccad05ad90401000000000000000000000000ffff8d47264f4e1f81ace6ff9442d477f7eb80ebdaa666804cd0c1d6cb131838847f75cd83540eace2c501484adf0333b847ddaa6c087a86c42520f12ca094d0c840ec18eaaf497adda92995000200db6a9006be408fd0e8501a2e66830f891946b4af6c926fad5ce9fb35e44e3feb5def57dddfdbcb46e6322161db6dd38f71cfb2c3803436aa729c665c3e00000000000000000000000000ffff55d1f3084e1faa2442382b30d27ec5057d7dbdcec5de5d1de2c4da6821991ed897f56582818c6a392f862af14d9697b76b0ff4f5c395fd9cfd8235e4be72c7a1a11a70e7da0e6ece7773010100a3056894419cda4dbd7f03af9899f99bd239098c8a7001003ba330d521ffd8d0d7fee89e5b1222a91edd5ee077751bc171cec7b9157bcf9f042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a414e1f0b04bcb5cf6d2d6df5979234611da42854a5e69374a29e0c85128caedb53d9c818042613d2f30f3ef782ed37bd8ce161f1b18d69579c40dc8292842f02ff5f511e61609d0101003b43be0539e3780c85607d76b610a7aa57b27658c410bf852cf9daa6863631a2128cd611d3681883531d9dc3aafa7edc97ccc9026f553bea3ac2760b2200000000000000000000000000ffff68ec34d64e1f915b61f2b726d5409a26a41bb3f350c1e4bbeb5e07e808f9f68361d70ac7fc2fc33178ac9639ae7ef484a427d326f24683c1f4c02bce59c4775cec76c6ca37241429c4e20001001cac95e0a27f4a3de7ec2d4d9b1494e48c6750ea21360e5adbd236ddac7f5d995bcb2758b4ced09c16c0e5d31e27c4b688615f5c121b6595ae5e940a0000000000000000000000000000ffffcff661694e1f8c26812d38faf159f811a09c1462e07e40d6b44881114358cb5390b65778ee437018c5879fbf935fd78955899b67633e373eabed3a46c91e10415d8a7e0b36c2867b92f90003003c7ce42cb2e41faa5980ae306563df345ceb182a4b4726349bb5553749d27027ffa55c0788d7ec18bc37f2a3f9f90543d8922ab62bca18ca085bd499010000000103000101010455d1f30a4e1f010101010455d1f30a8f30020101010455d1f30a05a3b03a69e21639859a0448d9ad97ee33d9c7807adc1aba1791c759b87aa81b99d79c19ba8778cdda6533ce9f0534a8392f0c51904ec5f4eeeb8d54b4f7b43c67a9d7a202ff0101000f4f47471c554804e4ab6ec7bf6fd289c438c6e302005c14f8358952dc50d8d2f989565bc57be769fb3858cc002c1350a01b274fb09ce026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a014e1fb6ee48c7a71a9d8e0813e68ca09846245fa155285f24a62b0ce9cb0102b1994ec58af8ba2a01c09363bdcc395d41f3dfc54ac69739bf492d4328f1bf0d49581ce0b10d41010100a305cf6235366edfccb366a298edfe50b6f6a360e9620100fc24e51446787e48ea9356b09a14ee2b62eb36ea44763ce7431c1b6e6b1ce441fce726f1ae14c80c13fd5e6c275564ef99df407304e144e4cbf012050000000000000000000000000000ffff95f8331e4e1f99567cfb20c6bed5d20638c31e7e512aedda02649e82f2b955ecd3e34f73c2229b350069f6e74a4304acedddd87997fe7efda5f8279fc8c7490aa37ed57903870a1f4ee10001007cea175a485c82eb49046074e08ea41d57a058dfeec2d2fd1473b21e98b59166042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a2d4e1f00a2e66a810493a91b5ed1a8ef8ac4be41543598f5b4765a6f5d6339078ab88030817dc9c9bdb60c7c7a02d7787d6f2ef719327671f37c8f4dca62dba332ba17ba42e7e20101001cafe324bb967bc634d9e5b67ba4bbf5bac35e32605249523624d1acf7be0641c6696889c08766ef56284c63103c37c44e15c9ce0138950745fb79695f07000000000000000000000000ffffc3c913284e1f98e7dca1b8dbcfdc54faff65b94f81f2e3fce6440bb10848d434a96ebe30ccbb33aa586a2d0ddce112e38cb09bbf13c732b8da1c9e22be858fa29f61e92d6d48eb84e270000100bc973f3a17c5fa3dd59493b7d2bc41ce472be2401459ca2d96fac25bedb2b7aa042a425ae2d3289646d71765e97cfe099acdf021aaeabd52393477d84500000000000000000000000000ffff44437a304e1f018a6d23ae53d6231f7dd73a058f125340e92f6e97897f017d9d9d4e6671bbd92241170dfcdd5a4ab8ef47ef12ddcad5853fd590bbfa10868687b7c648238857248adfb70101001d044d90e90fd27861d7340ad564c41deab4311339aee5c394c4e24140eded303a82c9ab3f0d8dd27bfbcfe9d074f221e207e33a77242a538d4e4d070000000000000000000000000000ffffa747dfd407cf0d731903ca090050801af45465c96d1248532819959a5a97eacb1ce518dcd5c5a21f20676d7c893f81ba672fcfb0f805ac1a700d32a6ab3f00aebf68573e26613e62a8250002005d60fa4b85a0f1f444c6ceabeb2514c3c03576cba2c099d0e0bd4ec394cebb91e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a0f4e1f81ad0f9be5a88ae62ff54fe938dfceea71be03bd4c6a7aebf75896e8d495d310acc4146aa4820bc0e5f5b06579dedea5cf83406065cb65ce74100f3c35a0324bfc92dcb0010100a305d96b1b3cefa0553732d9d6cae76666eff869aa0201001d09e6c4660596a0cb7a714a044d41f208b514ee79147f9d73d5bcec839f4f9f83f4a966a456d795192b83bd2c866dbefee9dc9435dc3fdb42882e1c2c00000000000000000000000000ffff2d20d39b4e1f08e37b3fcba972fe0c2c0ea15f8285c8bfb262ad4d8a6741a530154f1abc4edd367a22abd0cb1934647f033913cca58aa2dbf2c2b6149412562f306841959b9cac234b730002003db1bf3af77e15437ecb63597b55627efe44a531f0c788ccfba4e53935ff433f8f0d644077c903f8015a4796855c45911841507d2ca5223a542563f0ce00000000000000000000000000ffffb29d5bbc4e1f980213c5fa8031aff096b8a3eddeefd0584a0e5c2492f631ef67941e082763250e2615975db1f1bfae631f80d41d839a62026629f37f587b1dc70ccbc6732225803db4e3000100a30586c2ddab5b510cecf14f20adca99d2e93204120001001d02c62ec31b37a00a906e0f3d29240a2a8c8f4b5fbbb86edaf8019d18c7e4b90869edbcab8a116cdab8d12560ce2d5a8e67676ed7d329a5f754250f0000000000000000000000000000ffff8b3b56924e1f98bae0f71cbb77fff1560f45680ada9492ec4c9f779df777754b54bfb3474729c269399bd3cdfc736866364d3fb011d1fc8511ca8de4f660ed57620e4537737e55622bd90001009d8df45a92c9e486da531a3dee6e38ce6d32613f0f65c3faaa4a411e31f8bcdc1291fcb624ffa2a24b22ac9f13e47743ff59ccfee42acbc7b83c300b0000000000000000000000000000ffffb23e440a4e1f89277d2620e48dcf8456cc8815aa18ad3587bbf40cf0d1718696bd126e19791bb600b22f1063d4e5e8efe85fab8f90c8d930d39369c43d62ef0fa29c85a349673712f5970001009d15d4d2f03b000755dd3f7fe619d08afe237a6c12a8cdeee78b7570149528720d02a858a529372307d8090f01b548f0ceb58bbe28c9590d4838b8761600000000000000000000000000ffff3feee5ba270e88d719278eef605d9c19037366910b59bc28d437de4a8db4d76fda6d6985dbdf10404fb9bb5cd0e8c22f4a914a6c55665fb5dd519d994f5e5706b4047d65ba1b873d9ffc0002007d46da41bec61f0fc7863700fec88b6f62c002a944926f15601c769a3f4f7840e026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f804401000000000000000000000000ffff44437a0b4e1f82f60dad4b7b498379d1c700da56d4927727eab4387a793b861a96df47bdabe5666c270acf04b5b842ab54045bbf102af37ebd7e83f4185ccc5fb617a7ac9e09c11ffc12010100a30562f9e99a3add57b380c2758dc2d6e96e64108b3e01007dea3af845240b50e269623bb46c68e1dda5146df0a7087f84f91f9df5573b069a8d36aeb1a1ba0710c884c4f6b5846ae0b437d8cf95623d01fe8cd3bc09000000000000000000000000ffff2d4dde3c4e1f0c1c54b5377e920076f2fec26824a5d15ff6144dc106185a614e60fd9722d7577609ae202168a02a50ec45e01f5b7e6b555ac4d7829d25f1fb6c3abb84a180f5cf746f3e000100bda33d9ba20f91152ed5ef65e6876f1cd67cd06d68647e269577bfdfa139472541c3681439c2680bd1213bf72e7cb062bd58a2893ebf596485def7b67101000000000000000000000000ffff47c6dc824e1f90f7f3a97069a5090c885a85aa7e8c970b5d3982718dc4394e2de8ecc2d1c38c8ef51a322e6a5e9660eb794af6c40474eafc007af0360da4457b92cbb8ad08650f20ea4c000100bd3fcd8042b7d7ec522cf00834722ed62c0853f7a634034dd46c75d61539cdac9e4f98209ac0950b0726d009a9922e8b7e553876475a191ec0e07b220f01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054e91ab69c8979d6f2b5f476f666dd09a07f099c0001007e4c78740b1b8e1e2da02fd33b9632b3fe5bf1fea5266b58ffdeaec62d5ce2f562b46ba5e245b806d2522b6bc06a17fc1aaa558c26145aebae0a4bb9af00000000000000000000000000ffff22cb31a34e1f99cec23c58cf89081e39d8862912ecd50a18b44dbe92af0378ef2a3bbbdf4a6a11a76af5b70db205cfd31323391ee64001878fc5b0bde020f35fd7dab0709c0faacba8fe0001005e8193fa6f63a29c9f74fc3546d18d7a37f419244f4d8b4e5a2ff6b8af6e12e958766fc46a8bab5eba49c6c3f625b1bb280d4a609407d672415288000000000000000000000000000000ffff3695cfc14e1f8ad9500ef26ae510e0dd8cf0568b2a89d1234697873db2fcdd11674a73caba91cd416f9ac701f4f7807d8db102bc4a39b2ea896299dd86bc7c3353a40d354be43ca263720001009efe6782f18c780d3691c0d9b5abf48e0b75635e9c8c3a38af9580dbaa334b0b0e2d142b4778d1ddab063a39ad555dccf6324784847247f6016a3b000000000000000000000000000000ffff91efeb114e1f11d05fff5f406fd207bc8984188894b6bbd32098e58244136519a51c183c70db3d713a33c9a55f8d6993f644fb34ff2d9e1a38143b4dffb85126ea5a1d78f957327ddde60001003ebb51759663be0ab997630bdbc93e622179a4dcd112aa180661f032a5461d024bce3f86ed570745882b0515d141c81a198bac0062b12cb561977d0ac601000000000000000000000000ffff47c6dc844e1f1135af6f4c73cd6f45513a9bdbf919fc9dcf76b18c0a3256b4aee0ed48360fa88637cd113502e4f6027232c2ed5de3b857124732477195239cee3a968c4923f5f8adb9610001005e43a6770a39e1b23842a18166efc352bc9c0d07dcc6be4444cfddf2f6dc846e5b8892fd835ba3a45b214442d83cd99c892a206573c67914eb357b150000000000000000000000000000ffff175b61d34e1f0064583f3f5dbb756708aa405572d2eaf3349ec2d9048c93f21a2d1e5a0da7ae1675d27d626035ce0754de1898d5cc308d83de554dcaf4e9c01608497bf4b6bc632101420003001f009f5bceffdaf1a088976509387b07b2bdd3da42fd3a28ab24a1b3cb4bca8ee026d37e13aedcfbccdc13d4d1245422d805850e0ac7adee22dc4f80440100000103000101010444437a1c4e1f010101010444437a1c8f30020101010444437a1c05a3b942e2e50c5cf9d9fe81119cc5379057c05fe15134f85847356b5d1f6a21f29f4a53f61f03338d056edc15a8c63fbbe8983259ad7ed51267fb83a8e41bbaa5ab55c632e1010100fbaae48b92890e7ebb7bbb133af936833a9975c001005f8c48db672ff4fdbb8c73916b3846aff1d93233c063cea706ae64593de4aeb0ef28c0a9d509aef19d2f67ff3167a0966d57d6d15e1e34de0e49cc100000000000000000000000000000ffff22e99bec4e1f90ea99287802a44836309be934ed63933b423580626adeb428026acde6bdb283f370ff19bb37f81b0e4775187ad006a3177f797c6205c1997ff1ffaaf6aac98c13d865dd000100df3cb4f7b5914b8cf121e25f982235b45011ec65b4334c4e8cb82f3b1cd4c34aa42908ebfc04ff050907bb5526b010149e7517fe39813ff2463f0b000000000000000000000000000000ffffb93e96c327118ebffc014c8b97d9da8841464d3c7cd09b9f679471a068666c217ec13524ad7ccafa50eb18126c99edcb43fb74e290b9208d9e4d63aa7667b7b67bebf81d9e58e2f4d464000100ffa884120266337ed03f5fa9b027a34f7900edd40285762c931842677533c2876e7557de965a623c094d402cc39b2c243b225c02f9aca6f8ac1fe2080000000000000000000000000000ffff0353f0dc07ce8074793934715bde7630f4f267a9647955ac45400792369bd3e5f88e2b9d6c809251b79428e3a8ec07bdbb7364e3c299f2b29900381739339a74d57fdbc677a6ef249f1c0001003f6da2cb03f59857012a8bf38003c81dd02430348efaefba70fe49280dd440c9f33dbae25846c3f0626f5178e379e2ac65709f26975e82abbf759ee10c00000000000000000000000000ffffcf9afaaf4e1f069dd3113d6320397e9674ba3595f46dacd562e013e9e80a2e7d1095525f35134d4a8c19f4cd4a19d3886edf603287558fe94ff326b3d3f9986fe97fb2eefa9a36562919000300bff14121e137fc00cabf32e0789977c9e839b9de537b075a72ba8098914e232ce68663ab8ac6b7e137b6b68e6db778fd51f15af506be2359fbef7e2db50000000101000101010455d1f32c4e1fb2b6553f221b16a34b996ae93343f1a0a085a106845f089c2d2258176b218ab6f5fe50acde73fbaf0f28039394bef24787e98c408cd1c718a9c4d0e37f826bf4bc4e90460100000100fff5048726710ee3995c937bf7b3e8c9f9e4fe5fc2a73ca93d0727c25b1b0973353cce03a13a81c74d3a45b3dedd17e9e159e534eedc6035360ef9d18401000000000000000000000000ffff8d47264e4e1f0fa3db9b808db89b49f91f6136cfb966288a56d731c9afd44dc8c4819ae5c286d08dc0572249f41dc919f888d562340190614a6e4cf57a7381bd81839ebce238cca8f9f10001001f0aade9021f071deead3039640d9d357370352f415951669df1c5ea48d2c0b5b64f81113ee8e6e7e704617506dbefbe925f2925693ee86096346e0d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008923f5540c3cde8ff5f57178422303c469510c480001005fb6b5cb76ac928fdd8334ec5af40c467734fd4970d51f796274ce5f650cb92af3bfb0d7a45eee0eac355f2c8b58052591d53d96f193c0af1bc3380a0000000000000000000000000000ffff9fcb15144e1f06b2e598c2a16e7394cff63bb9939e8bec49849f2520f97d8de70ed9336625ea0582889be68007e93663685d03d6996b9406f3c3f3e20632e58e419a5107a5db669cb4200001007f12b79c3583b94dd23207afa39594c528b66e4dd7953588d9faf46743676d5a7a11032d1f4965337f4543150f9d453e65221f1d64b21f00922ca3000000000000000000000000000000ffffb9c313d44e1f8f53fb19c3be85ce00e96d634221f20a06a3a50942998193004264075a70422a3305f57c0c478a70ad69f1112e2f9993a6c4b0ce143aed43b6665e086fdabc05f6797a2a000100ff92ce2d64657cc3d4d9d82547efca2206ec1d9f5dcf2776624bc0a58b12ed3bb004d1b16e8bd67540ea64db533d8b8e5fcc9bcb9d0466b4a327a6269a00000000000000000000000000ffffae22e9734e1f8c9c5c77fe321ff0a115d1ba5bf7462063ef21a82ba796415f4ee538bf9e8a6a49707530c72cbb6b60026c46ff1b9443ce7f101b7f075273c892063b1b8571311737a5760001001fb778f03688d6f966dc6c67bf2c492b04c46090fd3e65e6164ff393573f56be3d804f9b84418bea8e515e321fa7e41ae594415864fe479b4bc6ff24d50e000000000000000000000000ffffc3c925ff4e1f93612d652fabcdc0052e5bde98d276e49ea71d050a323b98c368a06742fd964d21f567e16fbda2c17b00adc470f6f614304c46d5f01a600ba66e3dbbd8bf6dc23e8c52970001001f99c15bd0e1141b1cd28843eb55b8451e2a4c7a668950bab85a9244209018f7401498319af3c883d8727b20c312db37cb5d7f17307d4b85ee6c3d8d4300000000000000000000000000ffff2e65348a4e1f0d2be4cbd0faf7a27695a4f11690ba772a32c9df368f0558998681d697e60888b7127314dfa8495096050638d8507c92a7327f6d42206d3642c4633c4b6b10919202cc8b0001001fbd564371ed64a766e5a7975ef0e7e8d40a0f18a52e201d1e4d5e48bdad3db4bfcf015ddea1a4734d4c1a059310d1125edd3f78d53629878464e9070000000000000000000000000000ffff2f6fb5cf4e2190c0e9ec9dc5f08b1d4d0211920fe5d96a225c555a4ba7dd7f6cb14e271c925f2fc72316a01282973f9ad9cf1e39e03829dcf16f7a66b832d3b84dbab400a1e9eb7f30ac000200df15cf6a2932259767ad2647c67847c7cb7043d90c2ce0866a8ce14decdfb1fc8a92fa8989e5b468e2248b264a7ec3385e8df7038148286f8e6e4cfb1700000000000000000000000000ffff55d1f3014e1f95ab47efb395f60ed95d41b2794c8e9bf58b401ebce334f5ba89fd5df5d072fd4e398e69392f1fdf3451476043285ed82475b535cf5f9cfec26ffd4f53d8ae52f83f252b010100a3053e9e9b3c72b9d3cb7474883d33549418cce8fe140200df99e35429536ad99ab6724e7bbac9a12b83249982a1557eb3f7ba73ab1c4ce090f357fc1d73429a5f45aa226d29c782ea81ad6fd118ed634b0604560700000000000000000000000000ffff22b266904e1fa58642905d5a2d568c8ea25a0a1bcb01b87a1f71be80787784072d1c384679d33c6bd1d851f0eb4ffe04a393c6da82acf5067e909f83ca46f32049d9adc1369bc35e12830100000100dfc98b1ef83c86d8fb556476721104fbc2dbc68ad4f5238c93462dcddfbbb5929cd63f26854111de1c8298f8e0e429c38249ee431b1ffc19354eb40b0000000000000000000000000000ffff6a0c494a4e1f07cff9e4c50da82722bf41fa5da01ca4bdb238d8d53fef085a56c34a432f6994c79e5bf754898499ec4dbe91eec0d00abb9bd2501e88903aa50a1eb8f36a5eb1fd0a29e200006d0300010813b0d6afdd170258ef7b3b91b7563cb99223e80b430d66e5a4d8640500000032ffffffffffff0332ffffffffffff038da0269ad49ae3d6a8bdfdb69599752becb3c0252ef31297e0d074b2b271462ffd481ef3d685437c606d5a960219aa251d8b5afca96eef1b117e7b019f1c7b63ef1f13aa1d01a751634bcb61111b95a9899c44247be8024f85977202cd327e9e238f4dff09e24edb5eccfc990e0390469d16fbbc5a79c435ee7adc0a0c8ae3cc0dd5622a5737ab6799bcc1bb83a925f19e54af804cd464cf65a8cbeabff89c6a8f0222c9b79f92d33049fe2c1063ee90957d1387ab37665dd0f978cb4d6bafb7e18cdd6a920cc72ed922b0f02629dc250a4c157b7e39c750c1914272236230d50c1abdc66a0f1c3d6c2303bf3a456d6f6013edc06456e88bdec5b819eeaa73184fd2661c1877e8e14fe4d7716e88071e030001215d56426d0b48a016626a29ce26fdeff3c96f40fe4667fe6682e35d3500000032feffffffffff0332feffffffffff038e26acaf312859dd5fa452fbf5c072fc069c99a55364c2e989eee465c32bf1d783fa54819ab35fece98b47f951876ddd888b4f258718e259e3e487aaae6bbee620d8e808a2784cc581562ff7f244280bb6efea4449d1a33beb99cf2cd6623f2b88137e92be078adbbebb07c19c5e9e679ffd041c9e5dfa270d6d6420a0393340080548e676c93387290d9b2d476cafaeb705fecb709b3cd2a477849912ec7c858c1188a8ced5c7f1fdb5fdba2a50b673a2c16a6f82a13dd9a3d87cfab79b66faf8dc6a78cab99703ab92ee65dd531f374a3c66b3d7b73fd63d8bd45275730ebf112e50b6ef95b229907bc328d31cf6b7c1b7e7055135443d6830c2c734f9ee8b5412da6ad187498e62920dfd0556d8bc03000121e72893e30f1216cc97b4920e707e4905e9bfed1f84f94809c5b5393100000032ffffffffffff0332ffffffffffff03a1add29d97bd577b65421fe0ae710a20dc76bb600fc3ffe9076e9b0a9cb20e9ed7fd47239498cfa8f0094a2dd90f0ef5c9f04331ad837af2cdcae9c01a6d236928d543d640ac5cb2f85216e308dcbe42b28c2545606086fde3e88d3975d51ae3315e09f17e69e183895c29fc725e3cec309e3abdaebff0c4e42a736c2ad7858a0cb733c1fa9c3cea4a153ba22f4b29cf79f451f361718dd4bec244eff4764bfeaf975ea8d77ca8754f37d1eaf53bd642a0f4db7e08c19b3ce5a2bdc4d18b60c9b3f4da5b67aceb477088e817e79b36282d1d3dcba6837f73ecc6c72c76d85df90c597d662079b786a3a6304f0e44403f292187ab806c0f533b69dfe77e1b41f6ae2f01d9877b63ad8c11bdf6466bfd8c0300012cc16c2804e4db62fa75400be262db71ed3abd19bc737d9f2453a6d18e00000032ffffffffffff0332ffffffffffff039267fe34682325e02ac24c5a896c0d4e013e0712b812cc5f5aa6cd31cc39251db29a9142365d377afe2f37f24540be06a5cf6d2a78fd4ee35033ef97d9f6d556577f97e91167131330d6c640385e840eb02d192c6b919703073fbff366f0d4b17fd5e151ad2cb7ff636d21965532598dee1f16540cf86e5dfbce1f9fd52b4ec6110a969bbc6b630b639c1090e636d2a1f89faf9d60f5e3d6a20ad3172c7b3573f809f36db756681b601687c5d79956e5951e4ac2f56c22a44635581608fee55d68473ff0afd09992bcedc9587464a1f73a28744bdb132f49777a804f0addaa3d030e85410292e6841bf166abf469488509e33d1f9dbd4c17ec6f213d3700f4c9e490a5eb5ec30d2f87b2a9c16a8f58cd03000132aff469aa0f661590ffd786cb8ca4fc0284e575d5ebb112d391ccfe2b01000032ffffffffffff0332ffffffffffff03a75224f0527fb260b488a5a0493faf5ec4889c3c3b3c28247412eee148c970aabac688bd61aaa36c9da8543e3585943e2b965a53ae549f13ac7948e46ee00a29d77857093f59fc948e3cd63f20305033ab30370073706c635dbf40d72af4175dc32d6c6050f97021cbeec3e3e7af960e62bafbc8ef74dd53a91e6ec929432eed180d3836269e0a5926200a1d14eb9a87d3d848e7d05c2ed616b93147166d6d444ea79652c1abacbfbb69c0012a58e3e88db47c06c8190558f10946686cc2a205938e4bfc06940887572c627e089b082eb2a25a9f913518397724983dc69bbeaa036e746e26396ceebf03dcfcc7b018d4580e6a6242b7e971334f1278bfbaa8278b5bf50e77e626a833c0f51ad379c93f03000139397fee7189036f1493b60167be26d83e7d35ea30b2a9597bb1a64f0900000032ffffffffffff0332ffffffffffff03b47c80d44a33768f89138e5ebf990e41a9d7773550d898ef211a26c7cfd0ea5e5f9c4dd3b5056db7cd413414dbd35f4e5afea824ea2d347c1f70bb5359523a6a02fb47a931837c26321eab0c54c20a4bb4d05428f5a42e53c2f07e202e8f1eb8b7cf99034c74cdc596bd4afc1789384865e54d1b99d1800ac6b06a5d8e218e5d0a04d926fcc2a14ffa9eeaba76d10fa114d494ef05389152466b5c79c086cc14da3aed6da4ea4cf0b6fb471074c5cd5e96183ed21698552732fa606b620e76b58446ad647cce903295997bc53f3350d2616f6756feb039a64c3a399eb83a4c3803ad5059d0446ea9b02139050495350672d2ca1d7b61fbf853d8269aa6d7a6e04cf7e54237a7620edb41d7539e1020280300015299ae02324687b8a92df73f82f3f660756227172103ec98fb446f077e00000032ffffffffffff0332ffffffffffff03a78dcc72d52d24124f7c82cea8132d0b6760186b1d1fb032cc2a6390224ea5a1dce6156f1086a6b9f25bfdf5d10a386d710c8a42a2ac9e9adb0467729a7a28cb6fa8b186035c023971661a5ad3e9fccda765ffe0c4f95f43154f3daa649f3316db88a898a24e24125581854eff2ed2a34619e4e375e559e5ddd9eba6724e472714a2d6bb54eadd8dda4a960712a83d0fa0bef9b2ac0b23d145da16f7609d699bf7f26b34367db0c3a8c3ec26cd0f28b2924b2483ac4829793448d52b8f4fe34dc9330a74d9b93d421608c117cc2ce9c133a13e3a6c9c76cc1c843e3eab16d2b013f6c941f142500cac3c830ab9b36f8ef4d541e2e2980bf17a4ce0b47bccf8c22f7b0a52d0c983eb233c8484d313ed160300015ac508113c51d7fd75e527bcf4c0b44a20f41ab9df669af607e603f9a500000032ffffffffffff0332ffffffffffff038d3e948bffaadf77908e28d6d6670c1da00fc2b4aa00f1a6c484984d51edb2f4d0551d1c2a03dc8931fdf7ddb0022ce0d248f0d0347e9aac373441e927402334279fe7ae03ac162750ea5cc7f2f030a481eaeca728ee24098895463d67d9f1d7676515b3a377cab3aaee3b7057814945a34db7cc3c2b0b6570d2306b0ec863b30e4396acdbb3f646aedf668e89b0fd642a2436d854c7a1fac97939bdcd36529581afc2f21c781e6ef3232fa59ae769c1b4e896d28e7217381650ddf4d27bc8ad8d3172daf523cfef122d21c52d706436e5e14102070b34c7adf6747da8a3a72f04a06915c387718b6175ebed1df4493b2baaa566d4724ee0f46bd6e3e8890317e71c303670e1700e33d2cd5a193b1b500300015e0af7ced20920e334926285b580a67a8b8138c56a3b76bfbd4ec84bee00000032ffffffffffff0332ffffffffffff03a45f8365ac32b793d51de542c0d81993ae78f02aee5e50b1914013269ca86558ad7569c347919520b9bc507561aecd7bf08fee68a777d4e143071a2d809d0552a90a33716cefbda4e965123cf3b14e08821e4b0e3a5050ff6923a446498db65d4506aed2898d8a744add10268f9620f0420468da3ac0d01bc699f51229c5d64b06cc0f0971506eacbf4c4889fe8f18a4b117bd96de3eb1cda5fc87c8de2ea45ebb34d01be2a86a470591a0c5e67aaf80a58c2b41a5f5d7f9c827d06f23a4584278e9e943cdee5aa7216a7ea5b4a93ea3305e01b9e080c0f187dd465bb9167a81021b7b6cd921b1b23047d8cb615bbf9dad16f7e23f7abd401c1b98f89c2925a7200f868bdbad53d3bd8600a8e89e982c0300017ff5b4165d7555ad6ccc0ce666026f21ccce5cc6d9f9038a910a78023a00000032ffffffffffff0332ffffffffffff0380c30cb613198ef63e130b10899e9f4d4091c50807ed2e8776c8efb3d955faca59016cf53cfe0c1351aa025d24e558965bd69190e14fc72ccf5e0fe989229b00ffae5e0e7bea12a42e1a04f948e5954d8659d13c05b8afa4084f15470ca0c82413d884a6ffc1b745cc99eb45f6a560c90d52ac4fa95335f39b2eafb360093cb11922864c3fd3c6898f0c71ef0d042f1b87a8dadabdc28593c704dd8d09e008016ef24cd175f682f26f85f90e5466bf9889ee8982aa43735a7007d318e3a75147f8ed908bf62fc346e9940b1e7d62f970e3e2a9f67c1d9a00d976ed44d19e4829126095b9f78dda15d99334bd3ed73cc102e697c6ae996b85c4fd89188a5789e756036ea758484a66db18ed4df97520b303000180d118b1d85d79d68c7920943aa776aa4a857e331d1720d3d77875cd2900000032ffffffffffff0332ffffffffffff039434bf6e190d94f17e3aebbefc82ad2251062ba995637131142c88b1f8b86bc05af92e279c5324bac91cb1dc76bd6b5298c008db18193df99346b6fc77a636ea6293ae0e55714739c175658760e49bd5b20fc363fc2b2f8018fc9f2e04fda2ce7c4761801f6ec35f9b700aa15973af679ed37b59c88c4f65455e38e02350e30d0177ba17776240af759668798e4f3e2528b6edfa35a04d0d7476877a894f10030c5fa24b954f9b50596074a8d0c2318ba0c8d3a434ca3308d5686b17dfb64539b2f7dea56c4101dfa8881a679fa9990bb792486e745e2f3e1756b50ccd1891c80d2f7d7f0d852ca6be1596e2fcd3ba6b97d1efdd1d2ac561ed05f839f9406e816ad388a09f4579d3944ad415647d394e0300018bcc929986c96aa428ac63ce4bd25520244172325ef4e28759aa95351700000032ffffffffffff0332ffffffffffff0391d53a49af51a781573bfa563817cebff72ca951c4c8d8955d5a72fd80b2dfd8f5d98828104da4446adf2f84ab7aa414f6083f6e0cd37cb1f957538cbb5087dd80032dd69002f40e8740bbf7081f4a99a90051e8fb327247feeb4120c9edeefd8cdd33b206072ca577f1d80d17fc1dfe9f0f07583c604766de84d4477c5cac4a14e0e0d49c30d110c7127af43a6f0ec6ad57eea803c5817a99de595444d11237ecaf2fea6bca9925c2bb922c243bea8183080d49465a181fc48c357a78ebfd9c487e8c8f08e7aa67714ea2884854b4f3596e0af66568bdf6bad358ee02b89f3a0caa4c2e9089661d2c2fde0bc4fb3463cee9aa8c704fe4c1e03517b159966fb3efcc48cdb5f4d75202fee4e2bba0f16c0300019c6ca671212d00f182ee9e294d196cb0e82c165bdbe6cd3ccc29aa796100000032ffffffffffff0332ffffffffffff039406d429b6c8884703d6a51f5f665e655c8e6b9c2bd6dd339987e2c2de2c8a2189f783f131a25b6b8d89982950c6e96575e6aac9c52e9fb82fa2b9a2d26b39b7ca890a775312d05cb9127350cebb3b8caa1311bdf058a3bb3da61c0f8799c250ac23b56e6574f803b9e4d704c99d1bee0669ff12ff311ec2c059901aba7389a30ca7831ab4dc273ca2bb86d203f1c88020cbd0df357b54c3905fb95092e21082defb2f04e81477f2debacbff5501691ba124a040bc67ce6874660dd3b64c520b79966f44f07c4fbf3643d657d939e4ee1ac3c833f5b7b5744a855ca749112d0a04227f09ae80769c830f8cc8198bc343dfde53107f0864a91c651b88b61737fff68dc7a774e906646587e0af2b524e9e030001a6162152b073de1d717d209a771a22712a64f3f043aaf3a344f47950b200000032ffffffffffff0332ffffffffffff0383e52d485a8897b577aa4c68cf3eb45845411f2994071cfd967f82a282e52ca0cb9bec324d46e2c7877a2a5c61f14cbff0581a4b4fb369597668d2a9e8ded079f1ca4b9a8c2e4b3b3d7e7652865952f4b98df1734a10d387d744aa50658df034efe0b513e29efd79ae61e1c9a0c7909f6e607f243f562420e6d36ec4500fb83f07d72b464b6109707a471627c9f518f21b82a030992e9d1826a98d616982d349f3bcc4ef2d933879d6e093ea3fd9ab1eb248a66e22668314e0c3c987cc336f67b7d79ef6b2e903dae1eb91552e5b700b4b60235fdefbeaa3d84ba053bfc3f5d10487cd2ee685af37334fdacef1667027a9bf71cfdff3fe068cdd21a4fd31c9dd5e5dd6d77acf2e499a42bb871d2afa5d030001a7240ddbb3b7d50ba386ca5e65e4a3250cd4147dfa43b2c77244e10d9700000032ffffffffffff0332ffffffffffff03ad6142fd9fd8866af0539198ee1a8554493b8769eaefc79b32b746b17ad4144c24b265cd0d9632e452372fa963326f2c97506a19e670e25e76bbd8dab982d5682a23630dfe3aa464c7e1629225d44ee3a131b65cb091f632840211c05b77651b39b45e4d710bf03bd71f408bc42040723ed55181adb5d9bc899051d00cb5c4bc116c9165924bb6e93a56e2eebabc48db770bcd2f3beab165e04c8c97eee66972aca243142c88512eb2a2029361f9c83fa1d0d4b9e2916ab464b9d652ceb0bf8aa45d51e6208b936bf005935c3cec1a1c93a91ccd1ac39493b66212c743869a980a8b4746640f0bf2279c5589fffe822addc2cd7917856e712898c2bf52bd6c35a900ccd70225537abd8fb79d8133a75e030001b1bbec44fcb93a84e4950fbee8d03fcd672174846bd21b1302d65a302300000032ffffffffffff0332ffffffffffff0390e55206090ff5e9cd54d837cc70b16a21858c31accfc85058b0ffb2f6845ad5b3392abf74aba5090c20279db712323f427055de2bf6ce0d63351be4fde9a8b11d5f6bd64d20d4af9c16338f0ddb5865899a0baabd115a335b471e1a399415a2152c6d293dd463c61585c7cbb4c1dae73dfe41de995f703cc5091274ba57a82f163056375762cca08337141db912fd6cf06217d2d0f9925d64f41af788224e2ae352b13f52b38dab7c7b022af9b3d622a586efc2538afaae3c4b681e8802c54406aa659f601ea25a9c7fc0786024eb1f3e50c830bda38a8f9162e119e157645003f65ef55104fc3317c5972627b51ee396364b2a58e06066f2210a8fecd0bb98812c8cb8e60330b12fc9c0147c38c96f030001b34fa2b77c3fc270abcf3bedd235995315c0fae0ef521c9aea9b21ac5900000032ffffffffffff0332ffffffffffff03915f35cf43277e1ff2a8e2ab974e0770af9d1f9fbcceb4371d9713046d3e8d79a2859e40df28cff5009c9341fd19f02f23820a3eaa6b425725c31dd800f9f9db2f774b1e9618860fbde6deab915e8b8b93cf1899aeae9e7c8b4c858ee76f16911334538476b8662dbf8c789ecfb3391e431e531aa0efc4c42e56e0e21c573d6f147ffddbd0441c6b6a265f9939dacb0b6cd5bb68c0a7f6eb1232bc250af810cfb3797f020dd81940b8e92f1bc395ff1b85b2bdff0db143cbc061e1503062f6b1821ea8e9e8872a142ddd710c00cb17af3d6e24ce5e723c5608011b861c0baf9703fce232bf2f4dc6e6ca2aeff83ba6c42237b5b9d4409624e030f9e0333bbf2c1270f0c7147746aa8c5fc4de44e8a789030001dc04d3a653a6b56f66b52aa3541a718b386d0fd43ada47962bbd010b0a01000032ffffffffffff0332ffffffffffff0382fab4032e006f2a5b8fe7aed675ac96a436cd05de3bd27a614fe66fe2438a1022dffd0f5fb28083c091fd6c4aaddca499191a1f67251afd73e3b5b1beafd8229f3691dd0c43fa972de4b56aa1672118aee8f79b08ee1a592b96fbfb6309f478b161c8b53241ce270244dcf16031eb3c42ef553bb4656e160317744d9115303d035e1df52d5597ae40e91537d1e9d07b3dcad591312812055f61b537b328f18053a6327337a913561a22b3e68c7b2f13888cfaeac604516cbb7ece79d113cde1ee46bf71dde2d428e53a21a3ad86b68e93829a79baab8adb4cd2e12e0c79fc1d0bc72cca4be253b811aba4d31aab1c2a2bfe5be9bfb8536bafa0926afd4c5527cf9a4b3683e511ac86f5d5f794ae972c030001dec9605a6e6043753620d476d1919bf586042ae0706f2d902999346a3201000032ffffffffffff0332ffffffffffff03abd7dfe7236612dedb0537ea1a2beb9e71dc4594b549e4c04c01edf3bf693575700d1731873af19006ef3d41ab5b6f05b038d462d429cd02152a3210c60d7ecb235c3934a572e798f955cad862dfb525805dc9a1eb35870bc26b494b0438d7dfd928d7db5bb7f6e7f77fa36a127a510a35a10a73271b9c332f12ab9de950d6580b5d06a8237a3960c4a8219b2c7330f5404ed5664bbc3852a6788d9553685dd312442ad58ec836db6d325e8189e1cc40885d35b84c9fb8d7802cc933219d38c90e86571c659ab847265a250a776c4a93ba1e21b97ac89af80e6b84c63f434164152330671f7751a7e3c4fd6ef8989d0716b30efc94fe56473b7fc0ad8b581a6379cabf3cb4bfb90299b4e7fe86dbf3c1030001df63c7ba777c253c097853ad177f36930345d366a6d745848da4b1b98b00000032ffffffffffff0332ffffffffffff03871fc9ff67e273b8617f4fad594baf4d53645c6f7e52e25b4d5cd025881b192f6b08fa612abaed34b2b413d7ec80f9f47968011b206edea592a9e2d5889867b4f794f4ff08192cee07aa9a78f66fe055b5fa2c62dfd08d48601ce1afaabc179c86f6db1a1792b8a1ff624e04d734239873676e80a76b6caf3f2c075bf08403a80376701149e245437b88cb614ba01ec863d36e8187e777e3d1ddc1045caffbfcbe33ed6e663cc790d5eee5afa0258ca68f89036c0f88cdfa3da8a9cef0d6620bb77c9dd0a4c731973a934b830aaf94bd02a725802c903deaeb069db639795a2601b86151671c603efdbd264d5f9df8162cdc91b2e73fb0346f180c644656cbf61d0f80de3cc7587344c49eaa213f8932030001dfdd407ea3ffae260328deb31a84c8395089c5f3078c7545c0175f224c00000032ffffffffffff0332ffffffffffff03b3cdd3252a98916d7860e182bdadb0d0840fafb9121cf03e95436a4dcb89bda3b1e6d17140244e2e7010d702bca25a5bb33f6ab8d1676150fbec803c041023d5b0e44c3d509e1e1093c629d8f05a78508cda1017f8740381512cd384c0a2c2cdc5dcd22df73a648d52268b0cfa364a271403df938e649e02a1da6a42ccbcef5a0f49efee0fde3e2413600847c807ad0e0445b96cc3255476d15b1af3ee4e97b73b0d215c74f938438152b58ec60d7b2d88fb8b492789177b456cd9088a76cf3f178c2f2500906b40b4f88e5b4fb74dd924bf60bed24af720c396d517fa8322dd035c996f034256aaa1ee3a535dc8494573953b66c54162f45bcfd628b1fb8795e7033544092a0501a438954a59a76ca5030001f1c29d792e05e96a6928cc58d6ca2b1e6525eb361e8984a6877c0968b500000032ffffffffffff0332ffffffffffff038aaa2ecb876d0a19d8ae8a1fda114b0e7a2e40e95d31876faa76599338475f9b4493830c670ac8f0b66f091fcbbb8de806ffe26f2109db9058dd7ab3b94c42d612f1910b32798e99018c7629697248fe9201c13a0dc802191ba8eeddc40cc28b55b16f254d96b4a63d131f4ea4c0e574d2877abf74c0ed68cfc2145f134feab600efda7b7a5c60f3f95837cb944dd3e5416e749f279e1c9790d4ef20153f418a8acfeb038ace0a7896a2367798381d118e1396349b5c395ef8f7f0177fa9bcea7c9e82652cd15930447d0cea672b41c32896f0aed082b0f7e4139c582e3cbc8e06bbbd31af185481bf89e45d1d5ae1c268012029b9da1323dd83851b5822c68b8b11b507d03267f6004edc098cfab969030001fa32ae75020e4ff7dd2626630c49ce57cb6aa4bb53345aa4ff7325685900000032ffffffffffff0332ffffffffffff03a3974823e7ed1916fff308ad165a9285974c7561b3a0b5234a43ac0007378207094d3ee7473a3dbd03d881ed90d4547be1577ecf123746ccfb3b4183c697a448454330f44d553756e778ea1ce019000182418c7d4b549ed241d641614398ae7c9e403a8701aa2a72542f833528e375b19a4786cb9cf41da61a4e129350a6ecbb1804e5826e6ba5a2ab123910252a63f64d48b5bd9b9677d62a6531c677e8171af8406daf830a7b8c8e4044e25376329b98e62747560e8e3a004509f7c436bab682bee3731fc94e347b088d1b0ae7c1fd9117e5de601d015a8c68c63ae69016c40d211e7e802594c57421428a0dd3aba7942dc1798759f46009e99da8c382205692f118a94437f2a2b3f4f60572c650d2030001fb626dfbe19d25e37267c321776a482ded3e172216268ebb87183fb9e200000032ffffffffffff0332ffffffffffff03afc15436226a7478caa97a3ebf355770532b3cc74208b72ab6b39a9c3bd706cff40cd30a8093a36d3c40ab4bf186bf8207b8e29e18de34922aacee8307689335aff4a877bf0dd7b2abdc9a53101dcae6b8512a52c4b7d557ec01a3bed007e596b747c27bfb4891776b7b47660f4dd42623d2b28668397f604934987dbddf249e05adc30e05b42539abdb3bd2e03ddd28211071540c8c4e7768378839a9d3f55a46826994ce5b6e3ccb8aa7c8186f2c4d8df00741a28ae4a4f23a0819d8773bf75f26548ec548dd10aa17228a11a67eafd2f4375b97cb2c602df3daa04a2d3ebc00ff51bb6f6e8e6609e7064e02d39c23399c9177dd0f9a59a35d9de4a2dcdcd9d7410af43f9b08127717fcce13bc0bcc0100021bcfd99fb740059f272122577f51db10ae460a6a45d5a6193900c9aa6f000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000134a941967b2acfd46578804c3ef141c6bebad19a162741d4cc20502bb565f11686d6d1cea4cd6a3cf6f4962b16cab5b8a719639a2fcd6be9d5a694ab1715301449228df0aec5a18ee450d1060d726f39892042a4920141771a2f69b6b8de175f837b3832dc572a88ce962d0d5ed93b1ca20fcc7bbca942b2623285a13c09f0e047401f4b3163748e9748ad19a193ffa3fb7de04e0a4fb78b7e91cfe70e0bae8143414ca0de24ec7458c39d59a1ff8fd09a4d2a4c51fa1a8422f487dcc1a7d6d2ca3459f1e54a12afc515f3946e2656c253a98e62a0a08d84f36e0ed3d3bc9dc14251789c028f139d197ed977172b8758d09d42050a9977e7802ecce8e4cd76aa2f6d761d4b169cc747cc97a4c60aad90100022362a6dd5f1de53b7cb3f54f86d91142335c39c31641998a2322d707dc000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f00000000000000000000000002060e7975897ce70a8921b3dbe99891e0b24835a20bdc1adfef48048ee3598901c403fffe3c9190a009726a73050f993a7b0f34b9d0e05534a58a6fbbd87e9fcb57d9274f368492f3e2363177e6a73d05fe1f69d80e421d0b737dba4dd90e13cb048b3b054c7915d3863e2b0ca13a247a4c3b835d721d6fea1315ab9548cc1f00a1dc31a92becd63894adc234d412c01b1acbbc4fa1ab79ec52f10541cdcaa1bff248b2ad55c0cc8c0c36e0bba0585007abc913dd29b9edad6e453389cc93a0d654005df0126b6a23482ee990bcd2a2c43c2187aff6b93bec543d8e2b8eb18a19f883a3db2f14f46820a91bfc7365806cc33333aeaf0d55144a3de6e4ef8107535bfde0ff77c13f24d1635607a7943801000271d03f52a14e2b7ef5b8b562977ad84303fec35302f6abd81dc23956bd000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000180a133b2c886a53d1d7ec81ea07d6ec20c462dff173bb1a65be0d79a0ef99e4318d7c3e1151d85c414a5c3f3d66908f38ec80832a43aec509f7a782e2beb9059c014611f27a3794b1e488c75855ab618288c3a346b50cdcc4a05d47b890e975176ba1ea8d0195f8a5a21a61d3a966c61a22b98294e0049228722ed0538f4fa015eee01599598541d5cf17c1b9eae18e2401fd6080e675a2e6d4465e9cd5e46135c5b2fb70a4633355dcc299cd49192a89d84f81db0fb04bc959357aa7f97907c02d48f39633efa433bad88bef1849f537ad420cd9a1d7db8817ca714e875380167c03672513db51da413c250ad4971f431f06de7adcad49a8b85d592bf6eff1027ac0eb134d50e293224a11e9fa39fe0100028e14a08cb480e0c248784b83b0b2eb445c994a3c941c212518a1156d0e000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000fd9001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f00000000000000000000000088b7c1ea6ba3f2d9b3028960122b3bb985a6be48b088acfe8e4fdb849024df5da7c0d1b331482f46891ac578c92a9e6dfe1ea48e651973343bbd9799bc22ca77b213529fc5364e996227ab0792d18c5f04d0065fa771279b4da6d1b07d24c20b0e51470ca92dc438c888c832cfca906e324a4993a7d707f4a5ccbdc58ad30463168498bddb3726f2299af07cbd35eeb57249d591e10c1eebbe283cbb454826f184fce72501cd8439557da8a0ff0cdfd7839497aa575cb104193633ea61a8c3c89b3344dff09157dd5a779caa9aea11df5693e65fdd58955ab8a6f68a7322120704d1da5557a583e4dc278bcd847d9894cd6bfce937b511ee0513473d9b2e69a13155c11473ea0aa1ffa5d7984239b646010003df4ff96a37b7e58508c3d327e1649cfb90d50fba0ccd18ee457dd99c33000000fd9001fffffffdfffff7fffffffffffdffdfbffffffffff7fff7fffffffffffffffffffffffffffffffffffffffffffffff7ffff1ffd9001fffffffdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f002a5dfd00f393a181688b0b81cb6dee658939fee0d1163f34fbe50bf3d9b7da721e721dd2c505490730462ec0b7bb05b0536375a7a46573e8b41900d811b2aaefccd10b7a7b8f91807730fa47ce24690a76386291014fe1591564a9dfad552562d68e2291876b4e9032d3a512b98004fbcaa4ebd541a6b9ca0425feb4fc29d90b7fa748d08ab5c2c57596d9a69d61b1015082e8eb6be7e713bd421c96d43e5c0ff570463a3cc2c4b62be7774fddfdac975134cdfffcfcdd4d349b4b5f70bfc06f04de05f70b5bd9c7447342a2ea048bf160b1cc7a54aa12e8a100516ded0c7e06b04211fb1433c153d27d6755974ebdad61329e9345e8530f51a0c80f4246ef35951a05331b10a4204b1157eb8174310300040813b0d6afdd170258ef7b3b91b7563cb99223e80b430d66e5a4d8640500000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fa497ae9f03c5faf99e3836d5039652deab840b18085755526cc8ca1fd81116faabf906c3c35311a21b4219e1f1dd8f491d6bd2e188da85d995b9ead2cf68c05953d368b1033232cab0fc4fba24b779fab2f968063da9541b8fd1ec5081d5f9f1f5303fcfdb5c3373172d00f272f21b0c562330d837932e0758a8ab4f538fa65a0bb15983c2577220d89cee611e27f66cecc4f3a2d89d26dce8ec7972e2e22afb9866e5a961bad7beb6d4a5c72955b314b7fe0858095af5b6a05edae6258fc13669dca94f11ec03165b9f70fe09e2ff26a7899670fc55d6ef36e86f8947013d5316df8ffaa3d85a83391f1008f90ba2617e2d11b701be4c2ba20df3c4e4c688f77d686631a80f41d02177335dbbc88ea8030004215d56426d0b48a016626a29ce26fdeff3c96f40fe4667fe6682e35d3500000064ffffffffffffdfffffffffff0f64ffffffffffffdfffffffffff0fabee0c1f86be4fea395f70126789744e3b2b89cc13b9b51182c0e2eaa4d06071f48e472a213c16f58aa013d19e89fd6de0735c77133d3d07526a621dae095a71bfa3c7146fc937958b6add7a3b2815238abcd614c91b82328ad1db1b956f697fadf048030d0c53acb2801ee575d79efbc4cf67624a5498b776d8f9b5a7ae820515a497aa57f5bb01d2a7a2a8cc84a38345cb8c711373f00ca236b15fd00e61c7b4d9fb07d5a4b9aa2c7d976edc643c1fabc4bdb49fd74f2a3b99dc8be0363141ccbf463cfe5e08c3019ce38e03b64cfd9f804ef6495a1a37c15f4c6d05af78730d05db6a61344038fabc5f2b54801cf1c41f9181176f3141dec97edb8161afc6e01aa3e6b5942f362d4c16ccadec250a03000421e72893e30f1216cc97b4920e707e4905e9bfed1f84f94809c5b5393100000064def8ffffbbffffff5fb6ffff0f64dfffffffffffffffffbfffff0f9843e8d966f038ad379bba8600de762f13f77ae3749493f1af9c1c1f108a5f36001ea820dabb1eeb5774a25b1d6870794a5145fe64cd75720977bfa19c09fa445e5599050198531d4bc42bd26eff6f4e8ea0b0795189e9aa4f7eb0733e4b7e0c6c9e51c55629dc66858de008e73a91ae03d75755ac6a52123250978a24f43a3c1276d9e5d63fd581b18ba08bf72c42279dc6e58c9ac6e3136208d836cd48680b6ba6a2f5ae5b0fb3fb81784ff7fa2ab4a4e90a2bcb801928dd06d4745cebff2aff61bfc35f17f42608f09ef084ad064a080d9dfc245deca19ad2e262f5676f921593dc1750a65d3a44ee8b907121a2e9a706008871779a5933ca443c91a9473e3bdd00a3522f50d335a5dcf2771558c00300042cc16c2804e4db62fa75400be262db71ed3abd19bc737d9f2453a6d18e00000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fb469eb4fd0f9ed1ad1e2b322986a3dd1de51ad5a73690aed86490928110835795ee3d1e3b1624e8972e8dfa5fba6f1666cdec870904e963421ffe531026362a0492fd3d7a1110addabc9ad4fc63cf451b5fe0a04ace8e94b561c1d935977f2b4b657c5b83fee8348d47cee8a03baad0a7ca129848ff8eca49bb5fa093822d0481123d0663a8f41d6e0053ae980ff5af145119afda2a29fa2378fa24ff6721dfdca8ae94e19db01f3c3cc60d7e88e33c1999995aab38edbc216e388f8749d93b600f473d8dd776221446af1a0516a17736881a72cd355072cb7dda18e33949d530e0129416542e5f1c8e89e043c2d7e1a44705615ff3d59ac5f28d6e6d8d530a270e254ef1f8041fd0fccd56aa16fae1103000432aff469aa0f661590ffd786cb8ca4fc0284e575d5ebb112d391ccfe2b01000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0faf3ec93104f5a003433366f2839a77b169aadf341c5a4b0d27a7b18ce0e57a14e98e177437ff68a26617d2cde6c6167a1b2b09c16f85c1b018d9eaf61c666cdfb9928c3b9076b88634611723c5efcbf9ac7d1c1ee5f1d1b8fa31e104dcf9c9e826930559b22922253f972c7a76f16d5483191b31047d89f12c12964f0492b7aa02af0a46fac16f3585b2f1838a08fab77c4e4b4b92379db306695359dd061e2701fa78a6cbba660eedea66745d0af134a93ffe9de779b0ed07e322f4cc570546320be53494c1685936c139dccb2940f24873060d66be1e1cf8958a23f52a4f710fc0cba8c06479fff91c218e5eb9ac1b662fb5543098fdcaa1f690c0bdac2136fcd1b2166490e20a31f1d528d3006d0303000439397fee7189036f1493b60167be26d83e7d35ea30b2a9597bb1a64f0900000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f90ad43503cae47b57ba7fdac1287f41ed8cd3a28b24633026a719d0d857dd53099996ffd02d7b3dd17d71575da711b7a87071a83f6d1b84200636a759c3e8261684a1d7cd60b9d2b79c4a97afb056c4493a44d1f2b261beb2020dc78685da60ea95202896c98a46e20b922b9edb0b66a95dcb1c108cbc9527a59b45e838fe46e1370af7e90e534122efc9f8ed273d0e193c67b167cd290140b943255d794e9a2c9d6bd07785631485f3100fff3bac2d9a3f0c653c89704a3679aa96163c9da7830dd2f53e1cd94a98c551a8c632a7884c417a54eccb47fa465c68e753b54dbee14393f3b4fd8d81b46f0f386cf5be7cfac666861708ff46ad9ed4fb25f80b77581b8ea3905a42355b75782b16b330db30300045299ae02324687b8a92df73f82f3f660756227172103ec98fb446f077e00000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f80ddb726efc12c8440ed465f13a180174b736ba99a9f05ff4beb28e9772f865bd2fb247113f54c7ff9ff59db3d07de48231e3b0ddc62d8087b6175dd7873829884927f48654275241fad17a7c23642ec9730bd2d6950cb07bcd7aa014c4a07c627e71bedbd507ba9c7a3386d8dcc3961dac0630300221203ffd9832d2088054c163bcafb678ea887dc1787cffa3208cb94eacd106bce106ec9ff6a4022eb49c9384581d6bef661f0fe8272a53063a0e996ded6fbaaddc3d227e56567de4e7d20bf29df6a691dd049186221e1cf3b3a846b06b8cb151172be35aee6fd61a4fdeb067e9359c2f9c41bf1b46f2aa6a376cd370477919d89447a0b3d1331c297f4cb4c20fa8940f0837df6ed470141efbb220300045ac508113c51d7fd75e527bcf4c0b44a20f41ab9df669af607e603f9a500000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f88606d95fd847ba14dd44c0a2889b237b0167dc164184d0404f34cb5af33d1d1e4033bcf5635e2895185551bd30653874548392a7ab23a5ccc5fa83483574ceb4e7055d692ff636e4f7fe4e3d3042a61800bc6285e57d6b642aad2ca6819a5e042609b0d235a39c8249c1fb57f3e643ac78a9157661dcef49bdb134ac8c856c3160566f3ac9aa6a545bc025b9d0473f8d6d14e6ff3704a2b1e80e0f74d27d61e36d34b5e340ac07eedcc869138d337a083264a50ef553873a835a5fb7afa201a3b1a0220db242f6439168f2612fa29d8fcdd8ce81660c82090c709f1198e76450c43638e1820ed1d7e81fcb9e4fd6db486860d52d699ea52272155b58fed3c971c3e58b2cd33a24ad4ba5833d18a5f6d0300045e0af7ced20920e334926285b580a67a8b8138c56a3b76bfbd4ec84bee00000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f82155583056b20f582fa4687cde1aec9c6a692fed2f12f4cc6c7d89bb17a788d51a96c660a8973320f151135fc0bd3443adf3771b9ca9a409ac04271ebc2dc1f570b3a62cc7e74d02403c232443ad976b5cef3e78e456ca1d053f9fe9b0576a2b8f437f2f7cc4d7aaef267f88a3732e869cf181f6e68b93e95d9614b0fdf39d20a34c4d28eb1c81fbcc11e706800fc29b458626b90884b018de397539927640dbff3bb9ea35551d003b24301032549c9a9b9ab3d8140261ce3d7b20757cbeba5f8cb37d8554df2958ddafc70f14ab2a3d927433219f88dc45bca855be136644e0d5397ae53f2e642339c76a2580846924ceab54ed83a2f43e7438084ad493f49350f9ae173c1f1df598c72d4cbc32d6d0300047ff5b4165d7555ad6ccc0ce666026f21ccce5cc6d9f9038a910a78023a00000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f99cf7ad02a7d814623c76be3b2313913135e814825c5653a6b91791e4a20683031a872e23f534fe80c6bb8f35dbbd139ac68d3445af815adde76528bd04a8573749f8f813335e91199692a20447bbfb491275d16288ba802a199ee7fef681440e9ae6b6b35849319bf9bcb83e13e16c9d71e0df89dc8a365905db5e120adddd8120227ad9dcf4ca2aa4f9bfad40dd7a6f0615ab2f12c9b6e83e70f3014b7b8ec763f21c997bef2a2831f16070b5917a382826d7d318a7d8c3a000aa817617282cbd7ebc157691be5f0fbcf149a5c7deedbc9b8b113a7680eb250d521cfd2626e05a3949dae548c5579c41966cd87f851eec2a3c3578e461b9789c915875963cf3a7b6c130fdfc65acb4bb42d902441ee03000480d118b1d85d79d68c7920943aa776aa4a857e331d1720d3d77875cd2900000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f8e2ca8434691560e39d318cb7b31adaf098dc8c0f9051cb89e8128e095b7785613de58f1e2e51471e4fd6f8284dcb9dac00cbb03b76dcbbb6332e16b1ca8983151adde3e77fca6cea85716122fd093d19039fe662ef107f2772c8ee335fbaa4d7252b0ead5e5c5bd8043715128eb4a6be8aef3dc77537fb84b1909bdb6f57ce5190fd076cb0efa7dd6cfe30a467c896e39a0bc141054fb2dd5069b62875136da0af9dc658a2201403e211c08eb699641aeecaf05febcd1b3259335bff4846a6a5b644173cc3de2b0cfc1b06b1e4fadfde4f8961c4f440ace493dd7c72e155d9201ea2c10513a77b6dcbf871f6815e2ed5bb285c16d339b6b4b7bf31d0f826369eb259fcb77b5ea015466192a53f825c50300048bcc929986c96aa428ac63ce4bd25520244172325ef4e28759aa95351700000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fb259b22bd1bb62e7025c001cb7d9df81c22f9f7e96378ea9a7c374a90c836923ee31ce3ef5ce05a9232b60ce769d6a74bbd8926b03a6b85eb78219d80f9870f85a42e4402894e173a7caba724eb40631b4889fa23e4260f589a82ec669b1a00e65b350ced8d35b98feac46ddc03d1f5becdd2c4cd3f0424e77284d9badf3cf2d030efe5cedb3f4a951ba81519cdcc1a8cdfaa293ccb6617deeb463dcc829f59807beb9efd97c9b2db54cbf017c62fe50a4abeae9ef59e934665fb349c8baeec44e0d60c8a9e0d9a969b3c2c71183316c28babcf1f901940350ca95ca4e817936038fedf19634717f30d85af45174be698323103c8a38a73b17b660f628994eb1c6020ab8651efddccbf00f22f6fb6ef00300049c6ca671212d00f182ee9e294d196cb0e82c165bdbe6cd3ccc29aa796100000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fb7f2cfaa9ab71095293deccf4b794ed5c1cd868ba5c8595687c3f593ea1f4ba8cf9f66ad799fb399dc8eb0adeedae10dfb1e9fbff1757ffdfa26d1217491b3f2ad50c4b1478baafb6f1fd2cf932b1db0b5f641c65d7ee3fc18e7b639c0f6caf6ab22268d56b7c068db882fb7f479bad4c936eb01c7df7c43049d62f3614e08c009fb9b530bccae838f9cc832a3a20847b065b604cd7a5e074817a56cc1d23a3e608d5901da35bc66fce9f00766d1442d97c5de26430c3e75f9988e684ae5734640445d1cca1a004319c9ff15406ae6a05d4b8ec7d8c7830b841a1c3aab3b909c0db589111573461dc736b3dd558aad5296b3d063159c1654df62bd55cd90bcd296dab5cc4b3a8153c3120b53b455e32d030004a6162152b073de1d717d209a771a22712a64f3f043aaf3a344f47950b200000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f88881a0abb2e4701c4d8f01a6ef512945ea671dee28d4079d44c8f23974f806b60111a6439d3f43821e6498835a8579c830e84f9829affe735da40d9c0d647741b5ffb2cd8eaae3138bac40163c3d2afb5f81614b6f90969599a0db1e9ae6e3016ffa076e41e468f221fd6cc17a526c6b703fdc611f118b4403fc70519ff3d7305b5a8b7bfc24d8bf8713a27e7c68b79511f060d82418297504f2c31ecd6fc7f5ed0f5fc2a4128d0367027eb505a2294a340e7da4e10beef70497dc097716e8873cd177d43cea2a25e841603a45bc41556e305a1e507d23a8cd4dcee791d1a770f671616405dda0046cdfff80c822cafa78ef83d25cb39252af4716a2986af55bccd23edaeb2e4fb205c591254552680030004a7240ddbb3b7d50ba386ca5e65e4a3250cd4147dfa43b2c77244e10d9700000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f8bd313d50dbae9f3783277536ada343edf61ea7e07a217de2013d42668b4431942bd1973f1d6272f5bb517d161224e5c61510d14f0bd2cb66ce8820a4cfc06b04ec2ef800b95b08426c44929ef0944b781a35f517b9e5e9b9f57f558bd94fad333cc818cb308e075c39ea6d8043a4e8490365b9e4677f532cc019a83dcc14a1f15cc069b0534be96e20adc84eacd471930f5d458ec1725d56caf7e0bbdceeeca403f7a1e181b196757b93c1010c66b7589454872cb496a9eddd0a6886f93ae8191c9b5e1cae37094fe3f7890a2d51bb47c3703e5aa31a6bc25052c21939b0a09017fb51f994331678dc84be477b9c46344e06a6bede44e3b4e8a77852c0886144c7c532ede096da18ad2f8bed0162743030004b1bbec44fcb93a84e4950fbee8d03fcd672174846bd21b1302d65a302300000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f896ded748a9744bd2749ce289bba47d1e5eb80d7230ec93284a70f9bb9f9ff3154f1c3f7b9b76b06901bfda5a3ad1bd50f02ecf09cefc0bece37d531b7bc7d6c2605de31e313f20cd91b44ad28417b2daef68c3f3506a0bac76f2ff2534649297c36a2315b2f6f579412074038ed9bb3083e24377254aba37a175276fef624c60d22ae38c6c74960d951e774b4b28ee96f2fd547bf6695bb141398790c293e5a1d6cfdafda1f51188318dc502e7f8ee1ac3e350977a61f315ac7c37810a1e8ce18b0bc7b6a4ba9e4a2dc97e2ef8280884eaac799a5e692677f700ea28f35cc9c14395240a993beddcc43f1d74ad3d7bdba5d44e5465fa987b498acaad0207be20134f306e00a75eca52c53b6a0c6f01f030004b34fa2b77c3fc270abcf3bedd235995315c0fae0ef521c9aea9b21ac5900000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f953f132202c18b488988eb6e5e5de067f1fbb4c9f3d8f440b6904c572e145b09925cecda5013687646063dd795a4189ffe67ece5e3d1887cfbd66416d1219330ee50b8b7dbbb910dc6781e57b621835a97a47cd848212a4634cb66aa06fe287730e7e963efda35e1a4b62036f22cd7129bbd307df5145228618ca2055a8f3aa00b6aa9383c007a654de3c363a9be4e4f0e75a2398b5cdd63ba39dc7e07a321e6ee4fa4df3ce4ad7f5eb839e378e73ab3a41971ae42b092c0f4b55070c2e988526a650a27f2ea105d7c3a272379d5d808980c5f18aabc7cbdf4106a2fd317b4db13867378a8eddde158c4da2ca1984553c9344a6a6c8fbe63f3fdff78cb5d9351b869a2be146d157a6198d023c1e7bc22030004dc04d3a653a6b56f66b52aa3541a718b386d0fd43ada47962bbd010b0a01000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f91fd8243c5f16da96f15c00aa389d1ef6b49ecbfdc48f7153095c1f169277d68401732c498c169b5eea71830a9f0fa717723e492ed85db50e328ef14ca53a0c26f7a7af2ff8faeabbca6ee125ae98d728551ca4b19aed0622480bb402a5dd1ee13c21628215bf7987fa9f831671d90195ea10b69e77c2d20e7bb2ff767557fc7024aabc830c594b3fc08fc4fa81539f8773842f3df27e0ab0a396946a5c3e6ebae6f7a3c1871c0fe5b492dcc8fea65ea99e46d0b05537bf20cce456dc8ebbe346c3dfac1ede556ce92a7b595609643cafd2c3aaa5c6f1ad03299b6673d1ed09901c57946e9733aa97fded2c3b8a0bcbdba2f47fb77dbaa9da91d20e4ec06c80524b8926416e68e6f61cf04969e26e741030004dec9605a6e6043753620d476d1919bf586042ae0706f2d902999346a3201000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fb7b0accd1753b3990399df6adbeb83b7314bbbf25a85b31928c1a6d7b27aa8bddd25d927e22d8def5a59406e66ced1ba8dc84a8cb049009b74d829343a4ee591bc56211faf2ca198c9ce7b43a8fc1ec58213e3d008d7dd87a73672971730e9ab121753a811fbf6a983c1c9a9b84bf8d8113cdaa02352e4a9a4871761aeb12c6802091d3b781e2b8bb1360557c2da1ebbbe47274b528df6916df27b3dd8a861554d762f58cc65eb2f76235a6afa2621c58a3c3145a3e956a1c9c14f5a1233d23a2c56e63ff240a929b59feef760b78079ceb21a5e6b0de1a4b31bbf71ab0b5d2f0211cc8b2e7252c5ac069445337d1d4ce1be0b05a209841ffd431a49ddcecae6a5a52aa00691c29daa0eac53eb9dcda7030004df63c7ba777c253c097853ad177f36930345d366a6d745848da4b1b98b00000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fb707eb9a238e7f4c0695a4d0652624f1c1763b2c09bef5b056773a3ef141e39ea55f831b57474537372e234ec62499a294d7edcaf674bb0ff35a15fcbc6ce3547ea7d1385ded24c1218032e2a38c8e4cb2edae02551be6e716b24135f66da691f25cd1d006e67c0faa74652f7f22c72b9f07c2fc9ee7508cd6b4af5c2518bb130d150baea27ac508039ca65628886e477570a3917e9c9a9888f4b4398429a210893fef5dc7bd65a6f9759275c9b084648343db4fec6b165c3d3cfe3c59d675f27e5410ca9fbeb90f27edf4448d8cceaf6a13e87661747d3ef5483c32f26c7af40a6e659ef346786ab005c4990fcbc5948aa181da8c36b818c9d1987ef615efaef4aeae83e2e3249db268c407a4bc987d030004dfdd407ea3ffae260328deb31a84c8395089c5f3078c7545c0175f224c00000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f80cbcaf5c3b5874f0b2717dda06ccd1f18db8c0bd20202b64f6ef29e5faa19b0e2343b05213d288ce8d2ebd4df2802b849e9c395b7d14519831738fc26523fb37bb139ef42f04d6924ba069941a1a25a88a5433a465a3314655cea204599c7cddefcc8be5ad409e06c0206d9f1a19b99c7478b2be27a1c73087ce2bc1183e83407d8ff2650ac37a9ba0b2dfafd0cfcca1edf2b215a94d3b937a7802f96ed65b8f7da8efbfb089beb2bc7b6110668d2aba43ce0c089087b4ee09e7199ff5dcbdc60f14e55dade2c03b9ed41c3cc9e8b31dca84d6c79c1b444fa88c484cb3a917013970568174d4fb42fabc778d78376a2fee46bed7b54f48dd18f301e6827169439a3ef410370e81b84c56d041f2957b8030004f1c29d792e05e96a6928cc58d6ca2b1e6525eb361e8984a6877c0968b500000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f949e5cf1bbee1f0fada72db264795d650902080cb366654111d5e6cf4ad4c0ab7d8899f76bea432f878666fcf472699527860b9bc50940188c077bd249ba78c83bddef3e7de34fd972791b00c99fd8b2a887eb5e4e8167bb2b0b0233debe22efde549cfe3b138f9ba2b58f663ecf19ca30da33f43db20ec5db827f1975e184281113eb81d4b7a2d27647835842157c72a05d489286a98a9da435c0597fa89469dfb03a032fcbd62fd3047d43bc1016e88ed50480304a1c11e7fa1b63f67f0db4eca15e016c1d850b2dab5490559b2d1d4e1248f6e080e07d378c93ab55101a3d0eaf073fbb202ab4b1e0f68cd8c908cbb8f1a9fd4c707b31e6e299c241b13c6ab2ec6a8f19c7d46e36f4a9fbaa3d4197030004fa32ae75020e4ff7dd2626630c49ce57cb6aa4bb53345aa4ff7325685900000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fb7d76350ae68857cca9b83144f9c2737d3780fd29027c93f453a1ae3e461514620002387047c5ccb3ce7dbff9f495b010a52b3a57c72fe883dd197ad0ee2de1cd165374f67de79cb668195d82b373e5799a2af11f9a7042201615a74a473d345bc1aa0a30e3f21da4bd324fc11690e2a62b9bd31f45903f57a5a77d75e3d77ae16e3ba70e64e5fbd9f906705da14ad563761fba2cb466a733e3991c17bed1e4e3b9c70f6f769506744a09d70d982c95983bb17089e38012c56cc264b6b8eda14e455f9a00593453195610115c36799c632624c8dfba1e8a10c49a6f67d1eddbf175a3ad1fb33457c016394e2ee4d74a271518b04a0e863188df3a31c56b1f44c7ac9dce5ce016494dfdadc1235ca0b20030004fb626dfbe19d25e37267c321776a482ded3e172216268ebb87183fb9e200000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f9580245eb7aa7f2dfba766ce6a2a1ef9d894629b2abb642e43371841fe8a04522610f39e87b8835c6bea849d2ac43866ae421a62e9605a38d5be1de0bfccbe3eecb170d5e85c0be9cb2c95162d5418d08f0c0c2dcc0527a3b37b18e3eed3c863d8abf6d6ab3373290ad5385d80ffcfdab996ad82604168d92bff93d9d3cbd5b51290456c8f2fc7cf6bab6f8c7d284da901a0f287ac1bd330f428c63b2967bd815b17c2540d7e593b63b090984687b4b98c325361a2a18e50e6b2cf986a3d8d01014bf3a532f98ebea39f118889fece039dbd6e7779a6eac9ee1397010fc0cbc201fae7c35c645bcb7aab0b8481b5d87c5cb8a3c93af1679951c2375f860f2881b26d86803e051ec06c35ccc9c16659aa0400051d5a4b418e76e192b77e54db85edbf98aae2a47677e001e807f91dfe8200000016003cffffffffffffff0f3cffffffffffffff0fa0d77e4f829d3a5b9a831dc61119ac0294430780126be1ca0642db092053ee145041be5c7c8678374f6ff77a88e7266ab9f22fc87cf7c9b20ae72db47c4479461531ea7d7e6d31522ba2131dddbd1e13ac0026f0890c29a5f4da2bb6220bdb72da1e8ca68e2488683950a1e429855bbf7647ba5852c2e68a547ad00365085fd810fe246244469832d8ea5d8d23f879d3cbfcd1d2c5cd787d7597f7e0ace865f552e0606368c0b84d2920f22cd82fba40aa555fcdc18c8a23be39063ae391549544b3dc311138ea175ad37c429628a7e8570491fe3b5099bd51a8ef8920d4e1ff151393e57c1a3f16823a25f753a849eb0437f98da75d333ebbedd7143d228a6d152ff77ec0585a29d72986705ce0a08e040005229a3a687c87bb2ecaa9e3890963315f0c08162b9fe01316623f9fcf2b00000019003cffffffffffffff0f3cffffffffffffff0fab652e50bde48076d18d0004d3fb91369bd57ad122dc9ec61d9bdbde02fa77c7f16b7190404f099800e2d00e8c556efd21ae95986174a1e36506ea1a9dc430923aa0922f45d8654f41ebf17a52555f2d89cdbaea0de08730be47b20fd315e437b385ad6463cd6ab0fc9bb6899387907f1b3a5de2808cf238507c6af028ba89780deb1c9c1ea0238c11c02b11485e2be66099d26a6723dbb2158410712dbdf21c897e8ae6cc9f6c5f31a2bf26124a19348b1bf48473d3c90b419bc21bdbc22d599cddf4164c900eda44c7dcd4ddc866112a347a1b99b954fe50f313d699820a530ade6e1c903b3b219a42d07c48aa849b36a3b456b1b5100ff0bdaebf07730e3ca0014d1dcbe7d7c53fed9c76596e5b7d0400052609dd341dbb161ece78f7b63e29385808327a919761bd38242eb6876d0000000f003cffffffffffffff0f3cffffffffffffff0fb55f5d9188ce51d36fce536cc6800432b21c9320ad042e17ac81d499469ec694a358b493683cfeebc9b4630585bb55fe0b33eae947ffb521a38a97c5d139651b8f0367f7be483069a627b33af6ca2ae7b9d73e8868ca50627aa8d910c103477e03bd68a47df4e446bb143309521c9b89a1dbdb187ea00c91e2364c70c23840180dd790c1f229aafee07b2ddf208ced1167dee94d9416879c0261890245d1d24572e1a9fc8b722d82134ef9bb199321f1916cc39b8e73c49b545d80b46b4b83ab4873039db465ae59a82d908df3e85a14869ba8393078e84937865c2021a0a19519ec62f8a40c0d35fd0f44b4ca13f202d37a41abd943438c8ad6bdffcf8e612b4feb101a6c5384c0b44f3e4abed6a9ca04000529881ff840a38774ff5ff68d0974b9b7581e4b83bba2025ca7f3771a840000001c003cffffffffffffff0f3cffffffffffffff0fad4b9cb630e89f3667b81b8ac363a8b4722de2de14c8eb09062ca245ee50f2454ff81461db86b529d16f9a03891f08c07d06b13d0b467c9a7ec357065b2df35cf9b0fe44bf2e4c947cd31d5cf400adc9a899045f16cd5765788f409191d157d0ade15c38467798aa374864005fc25e869bf9387f92d1ff561bba188798b1041e11af18a7752ee17e5e38b1127bf7e27eb7c796add1b8dece9c14b8ae221ab8805d711dc5783537f0e4eab7a019a9caba8dec80dc2b9814c05243ced4a90dd4b77111e561cf28977ec7b60237324a3cc7ab64b55d110a338e4fa1a079d02c3770032ed047a3002e54f4d0ad8c0a0b458193a0e7049618bda1bd99ac3a7d5ffca4d945cce4a394c650d81e1818da25dde604000539397fee7189036f1493b60167be26d83e7d35ea30b2a9597bb1a64f0900000018003cffffffffffffff0f3cffffffffffffff0f967c3473f08b4c57b39d04143cc85df014c4dc74cf7c54c5e2046bf33ca5ab017afa84f3d806e07795a56dce17ff8093c4e600e6d245b9802ec85614fdf79c550f6956c4fd97678f7544bdc9d4a4f1459704a213f35c92dac928d94fd63ff52e117c8dc6dcec3249f83090751ebccffaf78714bda641197d257ebec49384ea41118040557972423e00a6640497dc116fc6fc4c508f02f963eaea6228aaafc4d0c93a1d35a95a1662ddaf6a156c774c8da19f19b4fe0956c5c94d60b11c8250c3316ce60a0c7fb91848f4a3ea6a04e50bdeb65d65a5dc3521843325d397454e3d02e015c49ad6fcde11af6ed7608283ab65a861fab2d0acb4209761dc6221b7b866925c7b8a410a75f3f06d1cd2340a5a04000554f322072b61202bfaedf4011c7aeb704730fbc5e75f4aa6286d791dfa0000001f003cffffffffffffff0f3cffffffffffffff0f882ea9c31ed68bcaa11922a2f2a8890dadf07e54f0caa9e74183ae709dd29e4dc431232e7d943b562f760b948ece39ecf6a442605c9d186b7459341abf38a1b23fcdf440a012eb0b04a7abf4790681e8a0f8e66fb4a15fab329a0a973be49d22039f37b6ced6a943f4829303ceda65ab1219759eecb45a535d5576ae3919b02e0681f1e651821c1da22dbec5c949ad214792121c432828f92287699d757e9595ae24c4e5e3b53d146eca2736eede068aa4af720441f289ea6463d3123eeff374294f8389c867e43efa8e4b99cf79c56cefb14a09a55fdec13f00910c4004476d052e44f8233be24f6ea2bef0db9aeeac04c62a9761a7a53d9b2511dcda49841c363667c4c739b22850496e5bcef215710400055688fc17c0dc6f800c89dafe0d088ac658d26746df09262ea15a3a09ba00000002003cffffffffffffff0f3cffffffffffffff0f9758d0558cbb8cfbb7dee652780732e2ae4493ef054ba80556568213f80137dc07b83d5a3334d52ac33cbfcb5de1d31097b59e3b055570f685eea936fd1ecf7ac6820f1e6e4f1fb4f96218a1ffc8020db0db51cbd96ca682b2f0b855f09c88af8c379f5e9c21b292bfbc80fb3279c7ad569576de1b577a7b125621d5601501e017a8787aca643f785a233285dee220afc92cbfa3af6cca145ec1b0cf5f6d298cf8c6a0fde39505ad6b46ffaba5e7e38ea16690ac93002b7e8127b9b97df334515eb6013f39516f3f84b3b87bd05af9aaad81c6e16ff5f33da7570666683090d00901db5018f097ad3e923ce99dfdc1ccbce1a46c656c6de707d5a27768eb6a2aab8c18829194e37556838e78e253e01704000560a9ad2deaa56320cf681393a1494d57d0b00b142dba980fc35f567bf10000000e003cffffffffffffff0f3cffffffffffffff0faafff6c3c1d7128a14cfe116ae2e8de92d6b6ff7814fadbae028b72c3e991e97bdd92a4ef780c6df051e6e5fe27894e9ecb60ba124e2954efbf301b526d4f56f50dc2ed599e91b949b3ec0a6ca55793bb84fe46d1700cf47cb0523e1612166d0c04f43bdc7509b8678b7880ed0cbe0b63c4866a2f2e0731200a9c8c5ca17c5d10f712910a5e1649bc5462c1529e54cb2bb36b9a58f12e07c53f83722023ea2fd39b3326fdfd780848e32fbd1ec5088dda2ee3c2d3d56c0d55d7c1c81db2fcdc3c56a00f96e19cbe3d536773c5dfee0e3acbb2cb4870286486e0d5d73c483a41218085b2ce8c99c15b469c20c82778c9af922f8608a13c52541d456c7953ac95d2489add14bb919973ca819ed718f1996040005663a8bc77337739edcd8590ff792e2bb689cb44587507b7ee09a42d1df00000014003cffffffffffffff0f3cffffffffffffff0f8a14c0e18df4b528d8f9e35c1e4106a60642918f516c0bd260de6ebe0232e06a5c5dada3fe4baef6f1fff9bd188a271c318167b5f3275933c79881769b45eae9afea05b1f59bc666c4601a7e5a919cf3838bcb831206f69a51985976011a47cef10ddfc4cc68bd6f3ce3d96f3803fc1f098c8484e499b7c939c571850220aca8068e059c8c3a2d30f05a6f773aeb72dd02903ff338b9d998a7bd80613cff39156acdd19b135e54df7efbd59ebfb1313e84ebfc8d8dad93bfc0d497283c8f89416aa5b5a2bcdedaef1e106fa2515a998de5614a9968068f8b667ef0a2b91a6c6317723fa192ec8141ceaaf20715a965a3424c5794f16f27867ad6870221ed2843ec790cd953bb4cbde44a2e978619db450400057c122b0060a6971e3378cd1155b0926484b9fcbe917512d2a7804fdb4100000007003cffffffffffffff0f3cffffffffffffff0f94c61c0c969e5e7b044a94a041d567e92d2de00d420ad03f19eae26a4bc7a0decabdf96ba02912a941049c8916684b253db3cc394d6408413c5d1ab526f53ad5afcfc6574f46ad871915235cd75b457daff1483719a315d9f8fdda65e019b044d54336b8b2e9bba40efd45ec662ea7a35ff6e5bf65906c5347427f1f0773cf410738745b8cb4e42856f59978562cf8708a48502920add54c2221c4ae81edc7344ad7221b1a175cadad4690989bace314b20624e546bed5d0d2c64544f597d81815f61f41a1245bae107d55b82ba89d2af3562ce4038f39b89c387aa40f70611d0fd1eabede842b60bf0d5781ecf69edb3ad11d05211ca2e3b43f1b3874b62604d839411c145bdbecd442844e4312d8f1040005869156fc0969c6055b87aebc0aecbba31d22d9eb8e74d510f475c68d340000001d003cffffffffffffff0f3cffffffffffffff0f81d248c19946b5faeeacfe48f82aa31a1ea08cf4c87fc252d8a6a1f2c2689144b419f399a5f3d491d585cc739500968ed8bb0872153f6e3c49ff90c1a6112b92a701af882b5df70de8ec0ead3b258449875ef421713b60636f5445477fa846ea340622021eaffb526a0f494b3b8310475e4e1bfc06e977c7b79583f4fd1df50003ce79bc05f6d6a0bf48ab93e2130990f422ee79fda822c06f66efe981c285b3509be95834026b22b2223181f1a41b8593e4133b130b174adfbee72ae3faf8331064744932ba4b02e763dc8eb5dcee29cc8bb8b00da2fb25e9f2a4a7013850820cd925b4c104ce9e7d8b309dd37d184e18d02155fde900aa1247cfa8b928a0c5ef4da51272fadc1e6b61b844e4933d3c0400058771a8b9f1f8d2261f123046c5999667200b3d19e5c28e42ce4780f6a700000001003cffffffffffffff0f3cffffffffffffff0f8c7ac677ccb88eb92cbf40a9880120a17cbfe7a167a0f30612fa549dd113236d8501e4184e3e18231aebe9133f7294cb6bf73efb3cd0057986c3581854c127ede3f7e01cc3f223656801e4940a35b90eb24c8a724bbf122c6a2ae7ebfe5549bbe68e6965e7dc811168233673ed177bc0079dcbdbea24d48d9b81a3a950d9046b0cbd05402e573372bd53dea90d55a8fa48e54ef945166bc2231ce44857052fc653aa2bb513175509e16a78cb5edf8d5ab3c28a34c9058e5aeedac0e2133105eaec9b3bc0f5d71a0c0eefbefaa51c302c95a564a2b93b8ef67df8b71168bf9f89172cdc7eb40473bbc49daa30af2b2c85e66ef75fcf39a16a2067fe788abcad7131e45a42d5215759acb85fdff88be204040005877c972de8adf74a3c28e4bd6bf7e5c5fa198e64cdebcd1c7e4949ad1001000005003cffffffffffffff0f3cffffffffffffff0f9693e47c4b3bed080feb3841c70815a5118c47996608acb6537541b60ce29995ea7820173644d56f3ec0bbc31f05b8d5ccd510633fe627cfba37e0114d221f77241325955b0caa89a0d538e6e33ac97c8c4bd39d549622c9e9e8f2e1fa8c59958cb2e51c8e36844181b5d271e96e14eb032beeb7aa67000ffb02acde4f1b6e6310371ff7c8368b91718fecaa20d104357ab2c93790af2b54f053cceeac2905cb59a6d51c00feb05e03a31e76b5bc4669b24d19e2934f7e1b04ce75d941dacdfb0b2d902e68d2c0b2404d4fe7f0b8807fed3a44814f31ef05c581aadcbc3b83ca10fdb03cfbd27f13441ba95f5cb033341a30a9f3f872e4053fa4a6d29df9d1df19329952a9856570806785973b8077f904000594573b8b4aa28c48ff5bafdc3f92f6689f9affe883f4f0d9d11515d25f00000017003cffffffffffffff0f3cffffffffffffff0f869ff3033eaef56409c7e731e124a78fb048988dacf5f5ff2eff5ce2a3849a46d24b482da24ad3e7bbca260c5d099c491bf84d0b8073ab81ec334a94e0e370c76ee70f6d497a1118a9a87c2edc4fdddd95bb74979058059c5791598b20c45676edc5d534469be0c5aa0c52375e2de517754a25d82ec813e0d14893360bb10e5e0d8d6a91584a83b369fcc6f1ed599044dbc3f7a0d87c338b4c1807d69535d63992a507f379801f01f918bcaac9d56f628d886a52681a7c24bcd74649b6825567a42383779d522e9b243b1d0a09c1dd0ebd10add8759a523b0f68f5c2fe6a5545072df729174783a939b9b8a3e9dc5af8c909a15ddf434159d4701c2c157321dae3806aff7cebb9173ada2ee799c2034904000594d7fade46846faf321e32e9ce4a11a41e2d519dfb880399101a4d850e00000006003cffffffffffffff0f3cffffffffffffff0f980ebc2a18ef8a8517524c510cb4962cdcd5c4a879f38c4f6ef23075f381bcc72e3312847685f394899a767e5b8cea330dfb6ac6c503253b31936816987a2028342d9653cb61e1cfa365f7fa19c67325b485a2dcc758430da2feebccdbfa6542cb047d2cd198980051ad9aa19b1a0faccd7e9fab2422d1d6acd441f400fdd17a0b81fce02e1a8f5079a26f5f8707a0025e46702a372e4e4580ac3dcf29941292dd709eb46f67d3bd1d847906c9208e1080801fb777c06bee56737514adc707a3534eb37838dd30f6fc463ed713efd3b6b550fd9bb90c9cf0b24322c94997ea620f3d92f09658dc45491498b89eae183376f96d26672acb740011911dfc67e947fa976505c4cd38e58b1e981f11d0a5ef040005997a477395e247a011c9305392cc1c01c7000a01059a945ef0c07f4a160100000d003cffffffffffffff0f3cffffffffffffff0fa7ae5a6a4d232e123c632813d7414ed89ab0e74e611381bbe603011e184eb71b42ac120ed300fa9685428bcf1f3158f633cdf9de38fba7c1ac456e7dc5ded7a63b1babde00f467d84b8143d82f82a31eae2292dc50e1fc9b5112f014dc88be336a6ec04f538c9feb4c023c39f9606718084c70e3303835df659dc3c3e36eab9f013b5c74214f9c0b94c837cd9cade6e738958f66e99166b001a028c1b72c5ca9d8be523b6be7a6a0ecdec2c73e92e11480cc694a8d0bf7749743eb1aef8ad821bcae8004c0c5e50aad84e67b98d81d3ab602f98deaf4163dcd2c1dc4ba4b42be055586c7d240feeb8272e03d934bf296815283a86ad3cacb3268504d57fd0c86f14fb0a491202aec7acac639f88b21620400059ccdc1214acb4c8640387b6a02b3b0356cf1d16e6425e08d39151a725b0100000a003cffffffffffffff0f3cffffffffffffff0fa4645753366b647ea0da709914ef22b0e5ccef531f31dfa682e8e652dd96f9e8459ff746f55a1a55265ac171be8814f32d8216017a881e9036fcacf3a6caf058f5988b0ec977218c014644ded7cd33c2b7bfb54996dd8d63f0985587dec9676fdcbbce26e06d96c06d9132ad4845a32ec9be4f1537453e3bcab0ec6cef18417a0d704a9cc972d5d734f4f9b21c6858f3ce2553e382210a5e2181ae17e3387f6b6b3e474fe111c5ac45f0fa05987680f4b290dd9350a408b116cb15912c35c2fe43a14ef1c98ce3823fafee6e75ff41c6486dcab82d9a369cbf890fece630122203e5f0c7f9fd26151ab37cf7abcdc4a3a49867694c56f5c99ce022eb2980a612e2bc94c557224cfa95b7bf1e26282fae0400059e2dff47ecf0c89c19fad38f18badd3df0531e4741dfc06dbcb5b20a3100000012003cffffffffffffff0f3cffffffffffffff0fa892dfc1003dd35fbb5c78a25af866ca6b291312c1847f8148bce08c1288cf27f8ba673de9765bda9f7c7066608d517f8caa575398911f48ef8bd3b3fbb4ceef8982bb2348d3f2c31c2d2b1da3245a18a77507d8babacca8fc8cf30e2b15f77d766e0084ddfad7d25a8a7c30d142e82be333cfe3485704a28010fb3a40a0d25d04e228da53afab623227063bc6af90fae6dedde4d4518b94eccbc9687141986a3def89803a5b7d0ecd17ae5cbdc657b3a35af6e9a2f37a957fe7ba2915a0eddabdf98c3686fc746f768251c1059c1b41998d98c4918c3ae19d30410a9c64062b1558daef427cfeea9b4258b91f6048763c23150a594302142d2ffaf2788779e1af467cace4a5b6390910a98bedbb4b02040005b1bbec44fcb93a84e4950fbee8d03fcd672174846bd21b1302d65a302300000000003cffffffffffffff0f3cffffffffffffff0f879e1368069603b46d12226e6b589825f18152c06949d0985c13851fdbeef5b783233e2806a29cab54f164da4360e9aa6a0cb58e490252b105a007b376ce301f7334712b5ae9b9a85d9575ca055284e7af02156d3c8e285143b590f806a660c42c8e16cfb3e047414e03e5699841d6c0ce258002e1f2627c2016e1b4082c1b33183b3a8819f7c04d18da396396c63f471ca287ff7c8e1bee1b4fa35c82c134da8937255a0bc8ab77d08e36d73774741c812569549ada7d117b40d3c8255cc5d7dff81e0d47ff43ebedc1ddc7f32fba40f1553342906f813c11e6d836d6ba968c1772ee78a2fbaca24788a5e212368ba906e80532aa4f4e58cbe529fa244a1af9f08c5fee21af3d918a767464ed529440040005b61d43573e1fae1b2d318b1476b373e66f175ad9b3ea116141be92e27a00000015003cffffffffffffff0f3cffffffffffffff0f95ec0158ed63989e0d4aac9453cce44e3320e428b793dd34cbd94149b1be22a6dee60a7404074d546141aebf68a69c7fb8c851ec4fac0c89bead0ad509275379787726d42eca350da9436a1421590a6584ef96728c2417b65725eb91a51075a336769a7ac58050793594ad135bdcb2d143e387ed9eff9d382015e6a63ffa843113d586dc8bad64b5c9ee23f1e302662e9efbc48d6812ffab6370040bfc37b168420490ec99800e7c0fe7ba688cca3289a8124d3a178e66f42341c62a0dd9531c427352f91cdcdd66c5de42f5ac7d8da048e72fa0811b26f2c64133f75565d941106fac8e521e3bb26d991cbf9a1fbd501046a72168ee3dca981e83bf838880bd96e05f6a76d6b92ff1040ee22e83e508040005b7c4a90870c88c2d035ca55ebcef96430b60127209ab78aeeef4cce3b200000010003cffffffffffffff0f3cffffffffffffff0fa2aa7138059d72e8ad3980cbac1341ef793c6a4e822278f79c20b65a780eac3800d1a1a6e68da1496a550277b85a9a1a3e455bc2880d947aafaa83539e8b77632b6af6c73771ece48a3662af0112f639a8cb46036a8dee87ab772a12522c07210c500fcbdff210726698d9f9df23e577f148247c0c0aa85e3cb12d99496bb6630dd4d00b0514fa913950d6b4beb9382c3c4b1391efd354a306756c1d9e1628ff885c7e21bd1929798298ec3eabc3aa2aa26fb3697b2f597636cfe2a1b9bf4dc348d3f9cab63ee5c9b22c3660c7c2085c9ef79a5048b8a107c8a1fbd2e0aacd18036db9117b479c945422b4ef7463fa59cd44a443f3ade55d61212133aa6fdbe53fceac40237d426d5f160f8b31c4b04c040005bdcc69109eed2c96770ae46f43f76906d9610973eb1433b34727657c8b0700001b003cffffffffffffff0f3cffffffffffffff0fb8ccb307127f4310a059ae2b47701b2bc6d2bee5e31e2b6d92541d010d9d2e77df40b0da6638f341ca3cfeca9bf97de6362e1fc85c48c47cedd411da45f8613f3a74e2973d7b09c22861a74fb314ba82a81da5f011c0b5045f28484d3200d019c4e9382d00ee71010cc476cc3d102cfeee44a077757864f73056112a410e1ea30e0c80a1dfbbb2cd10af57744ed2aa209bcd7fed4ffc00cfb5de8d7f206cd1b81ba131078ee712a876ffb2b78b127bfe87716f20e6018f2e3e19cb8eb09daf39e93554fea2b3d87991cb2793988bc2092e0373c06e2c1a5a5c8067065bd7eac60ed972962699c03ccbc3b61534bafbf1b56d68da61c3b0f20317436d5e4533b47e9cfcff1d9332eb58277ce9f3d5edc1040005c2ac9954e8b50f2c2eeec08de718adc9d7636dc265f97db0245759be4500000013003cffffffffffffff0f3cffffffffffffff0f80376dfd94183f491876fc460294a37a6bf8ef0e88b00540e5a6cf2ab047a05663e1828effd1b22be7e3e0cde9d137243a0939ced3e4df612682d688b54701d00ead7ff759ef5c93c8351619e18e54d99607a39978399232e58ff4a68d695a60b28f10c339c43537c86d8c6504d2ddf997d417450fafe4e6000e76f35d774b3510b02533647d40bb174f2182ea700d10a36fcdb3a23b20c1ced4a3162dfd434bfad109b73d8a2e04c4b859a0b76a7a44b92d232d479ffb0db1401b0331013edda3ef66c1f03b1084757bad56e8b296930746f937c227cabb4dd2056ad6c6f5751292ee87cb409fb1020548baee9f2d1b117d91c4969e90b7cb722ae14bc582c94ad9ee621d98f7966e3fbab18ca8cf57040005ca3cbc59bdfd8771edf8393f544adac6d94006a450623c9fa1d99ee77400000009003cffffffffffffff0f3cffffffffffffff0fa7cd8516553098e52ef65109b1f6a31beb083976ee4e3a76dd685ef978ca1c5f5927a61e9ee0f7a780df33e21add5c3e5c38e9625cc12eb2d2575e6d4a7e56128db6d1b47c71a6b61d26a88148f4a09aa6196dbdb6117ad3c79bb0f7e1a12b13c352d4a3c1dd3c366bd6b424afd7f2a4757d61ce9d5211d10b7779a38f362d6212d9a090b0b81c615a4ab806699f6b50d9dcd221b10afbdb281d7fdf71a2e85bf73c0d7fe11134b65fc2719a1752d2f483b04d6f0c9f723c0bd968adbc21451be3c88d9882a9ffbcfebdfcd2195fae1ce33aa9b5c9aa3a709efb8164e31f76690b92dba16ca9951b326744a984f62b2b4c3da078d736bdb399cd29d9538d0b1dbc6ee6062a97473df88de972ed7e3865040005d0aafe6f3665b0c586996605c0461d36e9c95c655890ee6532aa8c060c01000008003cffffffffffffff0f3cffffffffffffff0f81414b8a527b79e8de424ba60f72653158a2dc44e5a92fecbda0613afd1a657446ce53a45a0cc12d66c7d87a5a96db819805ebac6d48316d08a0ace7dc9381d46915f9eaa3bd2b29736cd86e2d3799beb7a449d7366790ba4d00c38b2e4e046996116bf390df4bd95e9f49cc2f036aae887ba7f927a95f689a56fc62abe3a87c12a2ac06f318e3de653b24ba3e1195fa51bc470446453d964d8573a67205ba67bfb59e7b1ef636f842f4af23dc1dcc7fb647ee53ef1d058cd0070644bb08450ac6d3fdda88b581e3305a52a61f8c6d94fc6c5e3fd9c9d1f62f81ddd41a20551806c9976cad42f0750abcba083b0ba87f6263feb9d905d088325c1874641d42264ac2af0d6e3a13d7574fafaa3a6485ac040005e2581022afc36ddeae07c31b8edbc386646c2c327de8391057f0d8ba790100000b003cffffffffffffff0f3cffffffffffffff0f946dca8d00fe8978b8fa9370e8655c82c1ceabd69b7fbb0c0d1fe3e36d7d73b8ac147b97e5e6a319f06f0e917a21cd25301d8b7849ba03790bda77445992cd61078efe76111247e28bba7b311171bdfcae6452845fe46296e5a40c1d73f0306e10a518a8ba6afc817f88c0faa2dd4134e6f0e7732ae1e6d526e2ff8d971a85c30ec9fc8d8045947df353f39e5c36b195f1ea9eb5fe4329de4612a7189fcd7efd65640c4e6dd41085c569d45a5c3cb7e28828972f14303af42462b5f4ad39c31ac883a8bc96fe623790937db849727070d1af4d2d0ec08b4cefacc61fbc3cddc908f6d05f64a6a1e46a7998774fe2832ee3702c96a3957ea5c9951315563404d10e482b45bfa4c6e73d2610da2e22b357040005e4daeabe9206c1afcd9643b4710f39b1966cef4298145b8e214230684e0000000c003cffffffffffffff0f3cffffffffffffff0fadb7c83d3ca32fe2f124a6650a584deb38fb53f799fb6524f12f4de3aec23bcec2bd9758b1fdae151005dfcedf408e21fb528304cf46ad11d7996f1e923cb30bbe5edbcf0e2ece897622d567488a9ff3b8c835c7e6f61f339cac79018918a02e201f965008e3406724305d57a445a566855dc7bd97ddeb5ee7368f310c42c97211082bb70d074c3aefe84e76eaa2f63dac3a400938ff51707a39c7dd836e925b970b597a808e86be8b5bd357ce21c227a5f3b0ba97541ab57da3e0940fafc203da070942267f2e98df0874347b411b6e73be1e0ce4380263ffab20582380b9fe16e7ebe45c09d112806c14e729d745f07249dabbb1d491e42c8cac658b52b528348b87dd6092c7e4d06313676618a236040005e50444eead63c53a341ef95e81017f04b70ddf3c3e19cf133bc112b07400000003003cffffffffffffff0f3cffffffffffffff0fa9fee4577dd451ed0165ee57b864fbd046479d52493f075424fc500c0dfdaf1c5e92e3c1107c3e858864df9c6721d2d0d6a2e85214ca620c37ba1641a9923f09a3a11040161f5fb33ca555d0c8b91385a860cda14532211a389780f988e8aef34d4bee6bb9fb8876f79a030659f491c8f91d23a474a30d4ec0392e9c3fad7bef037ea613cb3f780d1194c98d9a0c3a5517e2379fbd3d0e9ae38d700c0d361bfaa53dcbb36160c7f0c65e7d328fcd0601a660cfb2eec4771f74f3a493270ee96baf1f6115ba195a810c458d0c2e03cfc3b4fbf229eeb2e14ef0c7790e904aca1a06305031d29923a0436c99d3b841e1c502679395bf36f7832aeab65280a4aa19fb604615a9d14b24c87a0c301fd0c098040005e88fe222d61bdb814636013f9d197e97ebe85505ba2ec59ac29b6a25190100001e003cffffffffffffff0f3cffffffffffffff0fb59fdea4757aaf9cd26bb54c0f6e78e8a5343cebd06514edf0ba4aae1c7195bab2f512632c7520c50d95c0e057e72f9d5f2d8bc401c237fb0b396aaeec79c8c370068e6e269565a1f90b8ee096d61ad7b5da3a964c3c72273970ad1f405d52f4941f2e7d38008c8067ef3dd6a1f16e6e0919ec1acfd842bc78305ef24cfcac8c0abc8e17ce39de8229b9386f393908b44141b67934ce6317e7ef2f5965d3d78b46701f085709780931acd8402fd695beb3a653e2314d284daf4047eeefcbb8b928c7cc564f8ae43b477a5bc145251e71e37ca309848d459960b0bfa26df864f814fd7785a1049608642577ab56611c59215a9c3d60a3ea75668d711202cdb3e3cc41d1e389a64ab285900aa67da4c5b2040005eddcf6728f71e0406f460551446a42d3f8eab9b4237f1671e05ac0961e00000004003cffffffffffffff0f3cffffffffffffff0fa346f81455debd32501ac18f10108316e9623cade3fcae5616d7701b9d3cf4b4cade9e03f9e127945ad27412564a90b522042e59c59a2961fc8feffcf50e1fe46e1dcbb0bae15f13a90b1f9ebd58243085f8d94d67409469cd6fc6ced13f6760c25b6fdb143cb0622f6c714dad83a22f5a6bd94e50630460fd5ad53c73f773a81255df64121e9de4d0be3a5eca980139e6010c7d96897c9881a93b001c883d69c46944ad8fa84863380b9dc65dcd5ea9892e11546d4b6c0581b7863e241c6aec35917801d50021077fd69f820bdf67e078188b972b5694137746220a359e622a18f5fa528a5d3fb8ab6bc10f29e90a4702e17727d257145124076799c5351e0ffebaf3977ff30f02eb478c85db7349d1040005f2804fb954ddfa28728c2d3895bceb1f7866d39746ad3af46e2412314d0000001a003cffffffffffffff0f3cffffffffffffff0f8bacf4fe9f0b3b9e63e574d757384a09b62e0a5488d1c6c1959d07db57827bf9c8bb5772df9e6526cbed82e554df36dd414b1addf7059e6bdd6c23a03dccd3196b25a26661f97d4f2e88e242be54531bab52dfe0bb526785a0952e4e889f16d6ac7690c8a9bca0ea0233000199ae08e88d0d4ed1a254dde6c9a9b961dabd16cf0f80862ce1aaeccd653db2836db9cff24e9dd066ba9991ff1f8fd619bc557ef857e24ce795dbea71af4ec0c038ef108282b346f67ba2b1548d0f4c1c5fbc22e600b7aad1f7e6c810bb3ebd6df1114c2988051add8f8faf17bc089692557712020c0a8f8d357aa5e2c39f9a9e35ff97ed874d35e50f080f6a3edf3c1e670a918f4f9a8ea38214921f27be8ca5aace4781040005fd1d962414d32016c6e93a3d191a21ffea07a1617d7607423bb0b5445300000011003cffffffffffffff0f3cffffffffffffff0fa9ce5e51d96eb1801c0861b923a000f9f0149dfbadbbef210768f8eb63403080256cc41807f848feb8d9f8941c0039a66056557cfba2d64e82dee5adbdfebe68c12a7127ea7885d0c4969b9c37a208d7a89aa70a884f09e3c1626ed651b8499e433ce8c4aedb030b1410da3636462e39946af31264aac397830ad59370e7da130ceb07bb4c518a1ce2ecaf525d86de97e1201d401d451e4d8cd2200a4aa1df1534dcbf103f5eaeaba41c9437db7995ce8a220ea92d9e453daf91eec3eb4f1f8d3166b25b90895ecba565eac03f86ac7a79faeae7fd2749555223ceeed2bf107708e98a35e767fdac4b6116975caf16b59e4637f61567cf72be49ddfdded9e7e0b3f03e73ff08912ca37174caf4f3e7100300060813b0d6afdd170258ef7b3b91b7563cb99223e80b430d66e5a4d8640500000019ffffff0119ffffff01b19a90bf8bd10d5eb046992a133f9688cc7c129cd9820f3ce9fe7b6c1f70ac40d19b9f38d1958792a02c95b77c5cf8b25ab4e6c5cc214cd9c3e07a2c11939fa322ab6ec6ba5696b1a4e401b07a425913af6ce88dcf01f3d97d3c6d3257b44be76bc8a0b28f6ea860fb527b7fadcaaccc6b80d3a13bb52a9041a5af9f0c0e176d0d51466b9f0850e2e0495e8583bac692ec6c3a7da9f59a6089d187a08fdc325137762bc88a7a9ee37ce2208e671ff441a3925e4b8fe0704b1a663da60a28942800ea5476e880c0b9b3999eb40b6cb34ad041a7e1880db54e096b8b4a8eafa54e06faaf763b2a5600f4ab4ac016d3be5456f3c5b0892644103913f290f8f13e908f4a0232c4e5b45d2fe9f5ca8708c5c9030006215d56426d0b48a016626a29ce26fdeff3c96f40fe4667fe6682e35d3500000019ffffff0119ffffff0182520d7629e7a1fabafd164e36b989e201dee02456f170e9a0931847e4e0f2b266d89c5e72642e0bbfd9cc9c997f7876c41678eb9e53f5c3cee854bcab820ed189415441f3f79088640c390a9056c5c2964c7c4e288688bcffbb2e608b68d71ce07f0518e9c47900c32d12d8e9d56e3e99b1d3feb23b5432617da34f353916b50f3871595a92cb7e4ac2552d5f7f323d3e9a52675581e9475bc2042c1d6f06d5afb058f8749c41521be74dd0c45027d0a8727169619caabe68a01d00d9982cf75b156e3aaab6c4bee9d911c5d3acfc2090897e46e1705e866be88d6747a32e5906bc054e93308b614ec827bb29e157ed30fbf3f9a9f496e44524ca997122210dd43be978b53feb7b8b6db9d35ba1036203000621e72893e30f1216cc97b4920e707e4905e9bfed1f84f94809c5b5393100000019ffffff0119ffffff018aa7ece2974f9ee33ac7f6423d0a407d748bc6dbf2e4df798594bfa4ad1c4d72ac0b8be73beb351e976d416590fb3c55a1083359d0ec3a220597f634455dc8e89751d50e93b0a2603928f9c1baf8f331b7b5ccbabc9e9acec23c1408f5baaef3546912f909adbc17f51f946802c37709c3ef34adbd280f221764c39cda4663e701ab8203569f1d26cd06865ab88a3e3258af96fa8d54310d10018abf40ee91ef0000f1e1e0fb7abc8299aac476bb30c2ae87ad2b64926119b9e600406d89bbc9c473c64ef7aad90a6921a62c3b4af527c23f63050f5f87b97b1fe48652aac4bc18ed05c48ae12ee1b4ff4c8db8d819deea20a4bc96796e3b4c93ebe0a7fa9631dd5113eb0aba22682bb63ded41815a6b0300062cc16c2804e4db62fa75400be262db71ed3abd19bc737d9f2453a6d18e00000019ffffff0119ffffff0193ce0728ca389231f4eb3783f21a90593f399b7b2bc104978cdeaa7176174907f87991d805caa0d9982eaf0aced9329e0b34024371bf2b1461916c450508d42f51a324de60270e6da6793be8a20694fc99113256c086b57889b77be6464cccdc715d0926598358d7b6bca6f8c0557cd158868f92a6ea70ea2cd012a3e293539a09e2389cb9bbe2c01c6161a032665a591cb317c405f513ca6c51d6e45c38246a0bd7dbc1d48adb4dce369ffeb6b147b19879aca758caedfbcc9e15318c3f916bade31556b6c22222f50b5cd3c150d9f350dcc8c2330f667be2bd278a7d86557e08d49f0a66ac511db8f7f480a24a113cb0db45cb7e44f757e011c4452986f98fd57963fb4be7b0f488f7a71ca1fa947103000632aff469aa0f661590ffd786cb8ca4fc0284e575d5ebb112d391ccfe2b01000019ffffff0119ffffff01935409e99049b7b23d1c4b3f278e7efeddbc153e081715c3002f141dafae56664c01470fd2bd9f175587e7da7b4d8f5982831d245d1da0ece9a50698b7a160f102ee380fab8ac816ad418f5734dee23bb1de55d65b9129b99f07854583fa87a148fc8d894ec3cb06e571fab177bb541c6a848b3b42c780b1c47d6a2ef0da24b412830ffef2b6ee8f2dccae1891531079d2079300a8037de62206f3889484f8701b9fe7e6a3c7b3e11007e8bcda4744168695d443684139776991074fb326311c59d983577bef4f5165dcff50017bfb128438cd91e9966c625b08e17c2249b72a098f611cada31b058907ec357859bff0e12d963dc9c94b146bd27b4a711a763e349850e0f77b46c31dd183fb56c7589703000639397fee7189036f1493b60167be26d83e7d35ea30b2a9597bb1a64f0900000019ffffff0119ffffff01b41aaef2af4077466f5d3ac3851911e8d4dc9a765e23bbf6be6e398be0fe7e68f253522b9f279b01e39ddc7a8e5c367f3c65599b303f1c2e4790e17582694932bb47e96ea2e570648e5b5919e49add96b69f83df64cce3a1a2f334d344209764b8f263ae73229560c977c367dce11f4d71ff9fd59189d962e7ae729cb1f8863a0acc942b1e8c6bedde9fa944c8f5b4d4849cb7c5feae3426a01e1b43f862a654a075e8f81d3d184d48e93234a007c32592360dd9aa36daa7e22d025604aecbb772510fd1960d6f6993b46add5e2d3e84220a6c856e27f73a50445c331bc263580a43c0fa9153a48f43af5b3a792484457e18f1da917ebd46010a444c8c474b895a778fc4e560aba864cf9da90e11d7c90300065299ae02324687b8a92df73f82f3f660756227172103ec98fb446f077e00000019ffffff0119ffffff01b9f4e6ace00780c27a0cb3cc06005ab1cb27734cb9604973525b489a776ffb86e6347d70a5d0a3a4857ccb1ceb90c6afbfa48c64912bb7535ed59fea5ce8537ba36a0ca6cde2d2c34532fdf30c9f578ca0eb3ac83dbc43b774dada1663d28dce1f5a26e417ebf8869cb5dc43db0e8ceb0eb2f9ab60411f4304a80c37c40eda2b01df20425852eba7102efc6671b7025c6f2f751b5214360caf85dad3e56930bebe0541aaa71b45dfb39d696db380c52085df2356388e8d8347078bdd9359fa61b87b359f5930fd8569ea82d3f8c67c792a5a45e01bd1e8e5ee13936703fb27900a731c66deb0eea376a401b870e6cd32bfff15a5954201545dc093fb7833671bb18398f20bf4e1a4a06fc5bbbad14bfc0300065ac508113c51d7fd75e527bcf4c0b44a20f41ab9df669af607e603f9a500000019ffffff0119ffffff01809fb56cb286108809fe8a45da8ecb8747e103ff40bded471dbf54df732bb2f950d95f2cc9d742fd0836bdda63567509d3294250e4f978e1c7076fea45c1020dedba05577ad2a0081e8b6300f3ae21e7b794d100ca97e741f626cf7da77034153ae5acc40d814a6901cabb1fd0412a8c73a442709750465640fb165f706d8c7914de5df87b3feae96cdfff01f747a07614557fc0907e8dab4b392f3e1f365c99baad1f6907e2936431d7dce6d87340abab56662ee46d27bf24f3cf6d207df40515dd1f62faf14a04bb0b77e44e12e0d3f23f05d9167375d248ba6391e2e6da930f09f8a2b6468d1dc1ffae55de3576fd899dc66f601bed38754713be513805238c27ed6e4532c258852077c8555ef0f50300065e0af7ced20920e334926285b580a67a8b8138c56a3b76bfbd4ec84bee00000019ffffff0119ffffff01b71a656999b7e3c5a9ae886f4ccc0b0ee89c7335303155450f1c0c2305d28ef92c779eefaf495aecfea7f1a7798e4f0e3e317313cf7df4591785e9bed2bc780d51891f5ec5fa2278f4fc11c351b6806789e40ecf3a0af4d68246f45158defa550f22ed89cf3ab06f003deca21f3e219fe954a453e9c6b72ab0f5e336716545b218bd9578f6cab1409ec52e38efd9bb4816e22b2e0ae257f9908711c83ddb5ad951837feb4f76c7150ce29d2eaece144d80015fd327e3a1073f56a215bb3326ffb9abf0be02f220484244c52afe402d0d55e24a1cfdc8a3100fdf2624e964268809bfc9ebe96a6d90d127709973c7ee70f548ab0514160e2281bfbf2dd20dea6f402bb7f6b0a7c3633851c0f13c8d30b60300067ff5b4165d7555ad6ccc0ce666026f21ccce5cc6d9f9038a910a78023a00000019ffffff0119ffffff01abe070ced1f19a7dc991dd112f4c42db8cdf42047fe828d774f52eef4c32efb01bcbb0dbc7eaea9daebacbd96f0ebf6d4a20afb9fae2599ece3ca90e5a854b33d47e154f6e24c95527035407804ebacda7e1c55feee95a90105d4896ccda6d827ae485be54d0abf336e63a830ff114752fb1901f01303e9785072ab66f2106b802bb906197707cf4eac4424883e355bee8b39afeb1140200877942997f2c2bfa038b5cf8987e52008346bdb014a2062a9770c7229405ccbfaee97cd37c801bcceeaf6bd03c8b726d1d820a383be07a8ff860185a4367ca734dd1abfdb893b9401712697f0454c858c65e839172f07f437e5d8d6aa993151aafad92679eb681af0cf94eaed35c2d16f8ec7231ca6c826d03000680d118b1d85d79d68c7920943aa776aa4a857e331d1720d3d77875cd2900000019ffffff0119ffffff0193f38c3c43dffea6714285deee5d7219d9267251ffc6ec7b8c2e436768414cc7ca8bf3e65a593dfd48ff4713bcfdefd9f9a69ddb8f87de3e86c2ada45dc49c158bd56e7716f89b380a0839b398668ba0942f2eb60f027d5763cdf6e3c88126ca23d3a7ef63869f61d88d2f7ded93757cc6308832092c8cbf7be4e5f96e520a6e0de8d87413271d5af53f406e4ae658e224a0f372a2db5e4d0d1b20b719213c1b3892ee59ca27a5415a72c04dde6a5e54b16a8c12ccd282ccde0ac8d2be88abe57677307a2e2bead9c0a226e67a695e413e84604cdff3314db86100afad615b901682b74ec207931e54d39eaf5ff92be212e190e5df93d93757bc835c03a86268e7b3690cd2fe0f0d8485e8f8510f516a0300068bcc929986c96aa428ac63ce4bd25520244172325ef4e28759aa95351700000019ffffff0119ffffff01965611a52cf7e6616fe1dbf615ab690300dc7794c0293c1ab520392fb76f8a887499e7e15c3c703891022c35ffa20a9ec18c4f0db13df25488ecc103fc1c2598e1ed87fb3657f52698f948a9d1c6b5e8ad98f7720e4230da8a5557f3681a5e558b19d9119dddb62786530300c331aeab262789f60324a20f764093a27977fe851987f93cc9edabd877820d359d4d89afa59af00188d174195afe54360fec17c3433f70088c0c4a7bd6e3ea7912400a3894a8423b470f2131bba3d7a5460bd860ceb4edffb56f6a275d58edba53a9b15a95b58995134ed110aba66718ad7c01b716e6aebe1943e1caa610add6353e3a27a157fb92170012104f35af37e91fd568100563d9a8d681a10827441171c94cc20300069c6ca671212d00f182ee9e294d196cb0e82c165bdbe6cd3ccc29aa796100000019ffffff0119ffffff0198bd2f3e17d3093230b2b2e39e1f2ef08504d8737de0016f58d92c9bf4364af0d465cc6efe95b2d29ce17769e8062d65ed81eb6f48ec940fbe85c8a191c8fa76348628451cb98d509abb6bbd7c276c54a96b07623593182dbcb9d00a24cbb1ee811fc6b960dacda93488d80dce1131c4e2d35ae552d0ab14995ffed4778a10f21841fa81b1a5be872c0cc99250748d15474f33f1bb45f7971b53dde12a7b44a1cdd44aacef57520a0c92962264aeca8f8668911f6c20c487803bebdf141d4ee7735094804e88aff1d5a3b281fe404635323bf711279d423c09b27720b22d601b0dc3c6de7f5ba2c3189df118ba5d4696a2ebba667e628a5608852f28086534400cac4aa24cfe90a7364df5c875d84855030006a6162152b073de1d717d209a771a22712a64f3f043aaf3a344f47950b200000019ffffff0119ffffff01a2a9a8ba6cd66d2611f673a1505b0a3a067cae5ce7fc7e3049639c03ea1b7bee458e51586ff210d3bf79fe3d0bf1f644511b2743187ddfd558e66f2e3d208a61699d5ab43df0de92116decbcb1566681b56033bb2e49a4ad70c72f52acccb212f732ef7615624ddb0417b7eaa624360ff1d0a15f8eb4db2d85a606088d56499417be9114f284ba8933bf4b480fb9df9201fb3c20fc014bd21ffe3259a4227e54af1c9c71512fa0d4f187f4aa4ee263b28ffd57e746c8dee3ab23e55ee34fef17db8220fe5682aeff867d4565bc743a5278e4b49bae58cb08ef74b2036cb2c0710d74b27b4368529c8c97a93829257ad1911dd6ea87b8de6666901bb9292162024947832763feda1fc275642a91b422f4030006a7240ddbb3b7d50ba386ca5e65e4a3250cd4147dfa43b2c77244e10d9700000019ffffff0119ffffff01a56297148884dd919e0f2c7b881368e7fb3480194fc1d32db52e5768dd65e6b5087d8546ce0d6934f5464a28f4d772c6d544c35f8c90850d34603c808c171137acfaa6b2db73e5003124ebe16d3ffc35a29f9bd3d5b7f9980183d0c780200cc9ae5b948e08fe2c7c4cdcc9bfc21e173c7f9cf267cc3f07158633ebcd86354b9915745e33e2ef6c24de59c605d7546982964a4614d59d9e1562285547b3a3dd2cd5f02b1e8bf3782603d85522e7ba23f485d34e2e7d3f1b9e36168e2711384539df5e7fdf75556c35cde2050a449881956d1001c0e9759569f7545b537b2b1fb70921600b87594bac7dbd5170ebf7086761be0ae783c0d091018deeab067435415dcc77348dbbeae4624e303924118bf7030006b1bbec44fcb93a84e4950fbee8d03fcd672174846bd21b1302d65a302300000019ffffff0119ffffff0193719ed30073329ab7cc3c46fed6c4913c7caf6a2b7417e5115a2852e05bdfc092794c7da84bd82bab234301ff6f32358c1bbdea7a4eff0091b03069560d235a800351e084dbaae86335173b8b42a42598e20581a651595e701d5c80ac81144732a6b82110e57743a74855a799c2bfbd3cc72f423b305bd94e3fe3bdfebce15d05c042ef92441e1e280c82eb676caac4260d6a92981014e4509e61c46dab09863777614cceec47069328aab3877283bbb353d6660dbba034dd3e0941a2f1812aa0158dee0344617bab92c63925b9263a864eef775773cab086c1e790df01050001e635613f31de12e249e07ed3e2e55f6c485123fa1cf5665bcc7fa78784c785536d29a6131731c1dcf1084f72e4fb63030006b34fa2b77c3fc270abcf3bedd235995315c0fae0ef521c9aea9b21ac5900000019ffffff0119ffffff018795d7beb371df668268543759323f1227f135b9a5ca7b1e3e84f114293a3ee261266a7c0fe353e1fde37129ac7d8efdc11aef282557b97f4d776928f9cfcee1a879691f5afbbb015f10f2152eb3ab84b956347dfdef7995d24fdbb4422e95a5383fe09256b96b167854d498979b06318ccdaacac09231071d4442a7c35b106417946d398efa060adf794a0658d74d172054b60b405379e5d0645bfe7457f42df2dd95d46a9e6f383d6f23f83daaf1ec928d1953ee631c6f098ee37f2e174ab38e574f2ea3f0575b0d348cb46ee76db81b71e4a80c9855bd689ab4d8ecd9ac73189892af92a024da343aafb40a8966eacdaedf659e3cc94176ace322c6fde3479608388b2fff652073b3be40dbb3dce9030006dc04d3a653a6b56f66b52aa3541a718b386d0fd43ada47962bbd010b0a01000019ffffff0119ffffff01931f528252b01c37e4f5b14c0ca4fbff175a2899fe210c17a8041d46f6225aba40cb152c0cd63f058a0be2b373f50178ff25b08121a38dcd6da31d931758522174cbbf63ed997e39d547629e4be2a58eb5e8241d9a329c9941288e2c49df5a62a64b85dd0efde701eedc50478d61d731af403b996b8da8a4a8f308f269cc6b1e07641707d463e0580faf27bd5863d95c73620216d71a426783bcbf10dd9aff77e53f62f75cef88ccf2c8e906c47551f9a46ca51237138f548d80b7b893bd3477274e78310c31b9904869b69674bf7b12335c7d7809f0f67597c139b0d5d2d7b417a82b3ba41e9e568588b9458fcd331bc0ef7eca683d3b37763c3400eaa3dfd18a56cb536b63da7453a0b1f3df5bc6f8030006dec9605a6e6043753620d476d1919bf586042ae0706f2d902999346a3201000019ffffff0119ffffff0185c6ab76a03e128da694c569c9d452f3ddc40e55040e646563a5c2742c41b8950dea90ef496fb7b8e150963d01cbd6ffd4d795e94490624e323f695d9543eaf28b4b8c4229e5845f8f3709e3a1fc90e5a8c14d2b9a24525fc49f22f92c01e06a1e3217af0d94636852ac759d5a06f30c7e6924ef65e2807ea775e9d79b77a95b0328e643755a93a9b0ec6f2b0eb462805326d970fe22d74a954f25b02ec036631f314add70b38b294e0ae30a3761e9a3a6b03d96e193f262d9d2403eaf27278518a82ff891e1ed22a46496747dd173ee83b03f262e1ff7eb87de0eedf6030edb0bd9c37dfad0e1d1d1f956e1065e8369bdefaeefeec4efb6216207dce3a5c936fa95a6b92cb1fdc6d0f6b8f373629e1d030006df63c7ba777c253c097853ad177f36930345d366a6d745848da4b1b98b00000019ffffff0119ffffff018d92ba709cb3a16c7bae070c2734a6d46c1d6128d37dd7ef8f0eac93540d46552a33c3253e0d8e482d3fda33422a5ac89e57b5a2a01ed0b8417705dc76e210ba35b87c9d81ad0e2782e924adee2adaaba004b1531a4d0d294ebc9885000b8a1530a41c7988b0c10afb9b2c3ac2fc63d20d66ea8384847f658915473024ea49a703d03ce1f324764653cd54599b21f088ab0cd2d5714ee55d170c84adbca7381393d12d11f71b1d6d3dc08922b332fb35890819448aea3dbb26cb801e2e10969cca45bea123a197b14c7ebe57cdf8e343f32c215ea1e56f9088c3d366155a8a100d716240fbbcbe6c48b7600736ca1d001fd06e2db283a9e1acbc5aa6586238102eaca89ba6f0b320d34a64f391ba73c9030006dfdd407ea3ffae260328deb31a84c8395089c5f3078c7545c0175f224c00000019ffffff0119ffffff01b91fdf630d9a7cbcf25b09989cccdf94fa2f9103a81a681f9745f031491d233e16377b5fd1090c7b73fa56d7988c23e2bb8bd828af6caf33388c1b60c3d3f194addaa38ae1f757d9ef59c9e6b05866e782d52247f932eb8701f0d5b24c0e3222b3c9c45dff8bffb40a208ce817cd83ca6a9aa0918bead1bee534613a696a51490a4bdfb6f82bb5469e5cf3d5514d8722aab656c8d4f119e743e74c33d9e4a98d282a4fc55f6b69a498c6c5b6a12055ec944bf03824bc274f33ea00c5537d86abeb9b248e50a9de3822ce974added733bd20697137743472021535fa141b835401588876de9a3ffcd79db03fecc8f2bdcff90eb766cac311c6208c2ebf97e24ea23e025a8aa3996e02e095bd64619ac3e030006f1c29d792e05e96a6928cc58d6ca2b1e6525eb361e8984a6877c0968b500000019ffffff0119ffffff01b3b715ef58d33cf815869464852e02718f7cc33d73c2a1d8f4bcb31539a7a2794868d479078949bb1e3aa9ec7a092969f37fa848741e1e1b9f122eed263559c5b78c3c912bc03d3d228950e3eaa7de63af0fef6a58687bf692c33d113813b1c2cca971af14b1c287bebf105da0eeaac8bf3978c35744f40590c204b03a80395409cc1e2c3fd679fc49fda47bfdf04f2ac2bfc69bcd5e53e101b143667f352006705dfa46435c3bac205c041f15b008ea894a19e3320a02caf5f560e9f3a6f616c8fa4b1b967fda4b1f262bd907cf18a9c3771efac5a165e5cc0fe06a48b98d47159c1d1a4b758b27b07ff5449ff3ac160ebd59c7674c5c519109d0d812bcff56476a4e00bca36300033c2e1e348760f8030006fa32ae75020e4ff7dd2626630c49ce57cb6aa4bb53345aa4ff7325685900000019ffffff0119ffffff01b9ce2855f72f5ec46287fa3e14278a0613647a41c099a19e1d178bfb7738d5a0f308024523d2de6b93fd20d6b390e296121d6c8fadbba9ae160b742496cb854d54d61ff9e37abfb16cae49df0e8270a78aadb3e42b1319dd37ea2dcf4118105bf2f5f668f4a82e2da84d20a5a6241c65668bd719d2d9da06e7ec379104b54bc3172be2bd458d494f798fae73f55cca5da85ea46dc1b6f8d8ea079ac62ee5a888171f0a8b893f7343ba349f2a0130e989945073c973ddc631b929e4eb1f1ad91af52281a69b6d360982384718d8d4e3e6e8780334777d58ef9d6a4b2f5db022ae09fffe076523279b5c82bf04cdf92c00bb004032611fb743234b88a2683ed7bec7d6038d27cff5ced9ef19823a22f8be030006fb626dfbe19d25e37267c321776a482ded3e172216268ebb87183fb9e200000019ffffff0119ffffff018daf120606278202197f203241fb81abf2f6cb9ab1d6ef60ae6b6bf5135df80a53f9882dfc82d2409cee3c6d7bedca2a625544298336a8b6f5c0f07a35381a4797f4cd3b5ca4ae24f6fe636b8b208676b335a32f516d4f5b2e608b89bc0b2c955e27641983aaf97c3a96ed87f9d0323933e595033d2def94d124e291ca24fb7d0b097f72d00cb8c071057805ce6a367cba8c91f279c9af305c04345809c9c5c7a097872213ded5c6cc6599c353fcc83bac305ce5f8b783495f05c1b40eae213da84a0f4cff9108730560a67e6849cd3230d8fb4eb8453d5251c65622494686790e248f577605db57aaee238b2c6f822f9677d804e9eb42fd8fdc5c89d4523375e7b024f4166371eab3ff4acb06ca928219afbc9b3fd325d3efb9fac4f8fd7aa4e880ca2dac6e4002c42a1e5dd93c4ad44e85f988aa9e0cb8ddd5c298fc9879491610734a311c59558a0581a4bfc9f0ea249f286db17049119a6288261ce8a67bdc06458a19ffa4236490e487e6ce2350490301001e005600af63eeb08a205edd825bbf1871e8c741347341ab7c4656a01d2b8239474fe184c951dc6452fdd399271299715ef9082e18273aaa2250a84b8f24ddded105f90f07fdf0e84e9cc1d7f8755dfabd503388565942f3fb6947a1fde7b584ae064a2003160033006b008bfa831fd4c999453aa04819c796964f89bcde2701d9737071263652b874b38fc8d7e6210bc60b3f5c71882408ffaf4b015a75ec6c473041d2fb24b07c8dc96836da2ded8a3502cfb2500de48bfc1b9b2844f8afdf1a2a5f861a6d5f6780ceb90302001f005700a0626e00afa70022afcb83714ef4ac686c91a2abaa22b1c83677bf20ab2f08b8a020d803b05066907716847bf1605102006d65076418b524c87ea0f033a80383b3729ed23a103076353cc8007804b382d2bfc5a870c90f9b46e3c951cb9f1716030d002a006200b5cc6b4981591c92e37c50976bb825dd50b0fa594b4e456a8a37e8d8cda81d70dacaeae8e47c1eadffc9ca42b2bccf3a0f328b1707cd125e9aeb04d0ea4fba353a056ce182cd64bd7d410dc89cab39a886e625ee9d372e5c40560bed803ba9920310002d006500adcdd2200c2c6880dc960872060dc5def1ff0871fd639c85d3f6553c739eca667015f5bba3fe279421b779e5126cc921080d01d4c9581d39ca14163364d31a9ed4552064b9f35ee561a5526636423455e6d5b919ddaf853b9dbeac0c628363fe03140031006900864bdf773dbedbc0bd95eb08593d164b6be7cc6b501729fb512a9f91619fec30c65814f888a4aa70810a157438e734030421357c9a13ba27de58d68557c39dc36c1316e4a5aa875d1b3344b3b785462de823878aaf1387dc85388a55373818f90303002000580096d4522a9ca9675804e8536e92f5b3354c7a3886e60be3c7f0550dee0866a89ad863d056d6593aa7cf4e5c627272e90a05b2cb775435392b3ef26fa4abdd31446bf8932df36cdc84e8d2989d63110c81f642fba1b72aaf2c787d36953c9d350403170034006c00aea835e8d60c3075981788526e5ff2d0b01d418c3a743dd454918e00c671bec4755b55e461a6152e472ee7636d0cd3340c7bf264f16dc5c4bb3600c249f41d1a089160bdd41e21b45334414d482f028e72d09c6501e4f560661c570b02b58fed03090026005e00b6745c746cd55c9181837345848e5af6a5b4d2e83878f3cd8edcac3c20e2d4f655085888180894b6de2c2a09e2d65e4215d4256915823629aac46208f78c4fd34043a9bd1d90155e360ac9110415beccd326236d0d88978790ac71b386e10e7d0300001d005500ada499cbeca1b2bdb869e5bedddfe453ae7c67b6285c9ad92e78acea7e2bd905e53410788d7c7434cf5fee48b984126c0e5187388893fba0faba66025f7e946277bde88f18e0d7bf9ef80420c4719ca34423e527d64888f6d6ba354ed1ceab1703040021005900b01172eda03420ab5985fcb60c540a0f5b55536ab3393d40afc20890cebe4bb54480c803d6be955ea70bc271942c85740d5305370023b669b39f9de576123e3a6ec6bfe7186c1715247705e3661cf63ea47c29378f1528b6bdfa6d77fb62b31203060023005b00a84a420431a947c664abd8dbca55b36ee732c5b56676e711e7a57006b104b1d3556f8e04a9ceda0942bdd21d06603b3406435ac9224c87fb718f579896616dbdb75f7d4686e03d5e0f804bc7af1ee1f156bdc211dafc7016476107c501d08722230f002c00350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f00500051005200530054006400b90874d09afd2163909f5964b05d525a30b25e50cf3ee186cc08d1a79bac1876f0a289cac4c0e3d9af978f1d63f6e7f8120178c9efbdc040323c54a067527078eefc175343caf5d5644c9dc7eb899d921113ab622fd277774448b6062059a985030c0029006100b3f9f9542fec225b243ba35407e641301a05981a8378a3e8b6af7e2e6619aa902fa20682f61f64e22f9374d2c724acd805da1ffc25202d233c29ad84543f85869cd53f36461db609ca215601ed10b24fdc6b367706ee9cb91368baaf646c5a4c03050022005a0090c5c77d3e5c73110da4c47993377f81b3da1cb59a3ba9321b66e7b74cf99ca1a2fe986798524559060e13303fada84b1881bba5ea0fa243e75aa9751a7ba1117a1b056086ffb2c22b43971c7484c5f03393585868bd56ea4142451b7ab9039c030a0027005f00940e1c61961954618d9765cf9a5133b7de33376e2ef92d267ea2af6daed06f97e58ab7dcfaef38b3c00c422b2984ac0f04c68a06ac7846328ee1a1bd6227295747bf5d1da93c143dc86258bd0d836dbd3dc46ce103a0f35efe42e5ce60be88db0312002f0067008d418d73787edfc2df52dfbac0b299070db617062ebd36a48eb7e341446794a544b9f6c490ef7b1240993cdf8c98517915f81d2347243c4e8503a43bebe521692349635a8ffc036c1723d38b652d40ec0a58805aa59a011121166d26082538bd030b002800600097df377019a04fe4f902347bab0d3a202ad15a6c7e3de4665f34fa6eb0e025144f8a92f51e7f7d94a14794e1458a32d5192df079c8ecc9dbddd73ff4c29f6914b17bad9a19148e283d9508e672358b7358b1bab043d7673dac4b654f6dd9625e030e002b006300b9c9bfc33e829da7ca59be3e17b7130bc9290c77a77faff0e3ae9343ec2ad966f70f238ce83de39f0bccfedbdac9e6c30d11c56153b9887433c3bd9c9cc5368c10fb5d47779fe27462d5377097dd4671cea5c30c20f4923113fb74f4ce351fa003150032006a0089d6be374e16cf79e13244efc85c429ed49d22471c2d0e839714d71ad37adc8cf6dce58f9d8b76cfeb57e639e650ccb70d2fa887c2592138850bde30502949c1b93f9dcae9cfa4358fc1f307365839358c498eb40aaf28457526dd124293ed8f03070024005c00a2a5c3c3097961ab2511e3dd8675e5a077de09fc35e144a891e0c8a5770efd4c1bbe6ed359a6c4af1c4322858b2f7e771747de86c5f05a7dfe247496b301855938dcd7edcdd3164182faf6bef72a84e6a7de1fc7d31283f9e1469516f349b7fa03080025005d00a7b933c916e2e45620220f872984b0d740e5c59ba2dda83e22ee265043dfc145bdc96188529b9a97f1de3f21a0319b30177645129969fe1412a834785de527451a575a609fbd3e03a7deed383bff32f3c78fef5dcffe72d137fd66a33606f5760311002e00660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005180019001a001b001c0097b1f027beaf26847ad0625b1a8e3d6fc378a10a2ffb2dfb27cb10ea4a263161ac7e918963a90af61ba7744aa4040a2615720c0bfee2abcb161f4d3362f3681b0417e4c1498f51aa75ee8253fc35819b6db2b7a482d951ff9a8ff6e0bcf8f26a03130030006800" + }, + "testnetIncremental": { + "network": "testnet", + "baseHeight": 1580498, + "height": 1580546, + "blockHeader": "00000020ce906c3cf652cca824e1bf48ae6b8787b237e08a7aa10a73f13cf6816100000034dba0e0f3c64a53dd7cd9eac856c060a115e347ea932cfe26988aab145c4c018bf6e06a7440011efd730100", + "hex": "010083c1bfb8bdac446221e973c3f520626ddd4a81fec93a59aef14830b57d00000053138b0dc2d6741f84c987a1d72bb3e1919ba4f53bffed03c52eae492200000010000000052a75349b6ef9c7315e77921c16e9700427c18d23bdd3cc5e3c94625cf848c7ede89d8155f6ba7352def42e555a55345067b7f312c3ebecf26145b3bf00b95d70df947e7c096ad5a62b3fbe48fdb29e34e1223cfb98c075b4b01379cd90eb4008774cdb0e6f3d4d3d2958cf4e5c517648601a978d3fd5db92830ccde46fc3419118e7a90b7802d63e0ee5bb517f7f0cb228a1dbec7ccc059cfd197914b3af256b021f0003000500010000000000000000000000000000000000000000000000000000000000000000ffffffff016affffffff03b4558c03000000001976a914b489115851ca07a26a5ad8bac3cec3c7dbebd83188ac2ed5fd0300000000016aed2ba706000000001976a91464f2b2b84f62d68a2cd7f7f5fb2b5aa75ef716d788ac00000000cf0400021e1800e852020514f1c94ec6ac3c55ce6a2cb8f1199f5391ca8d22be31dd13b12adc04b450dfdb484f142dd2813496d173d9881e3751fdf1c7426aa35d88245120f5f00086bea9f98bc98ad1afade072d3de909a15edf5adf638fdc945763a28d92838f69d3f1bc5f571698df7ec056461ea054e07ad279e13365ff2c12a1d45ce25db738caf3dd80a4d1dac4ccf91383d6023817f70c125a3ab4bfd9c776e9106a1a48e0078e6115b22000000000000000000000000000000000000000000000000000000000000000000000000060132aff469aa0f661590ffd786cb8ca4fc0284e575d5ebb112d391ccfe2b010000015ac508113c51d7fd75e527bcf4c0b44a20f41ab9df669af607e603f9a50000000432aff469aa0f661590ffd786cb8ca4fc0284e575d5ebb112d391ccfe2b010000045ac508113c51d7fd75e527bcf4c0b44a20f41ab9df669af607e603f9a50000000632aff469aa0f661590ffd786cb8ca4fc0284e575d5ebb112d391ccfe2b010000065ac508113c51d7fd75e527bcf4c0b44a20f41ab9df669af607e603f9a5000000060300014ee749ed0627862372727691b516618860ea7f13e6f0051e24ece57c4500000032ffffffffffff0332ffffffffffff03b3a62fdf54f89ea23cfc87d360a05e8899ad4f2214cbfa46cd2984d7ec6306f8b7e35d57f4d6fbafe001b9841b44a4a94a5644625f8034f5f2b86caaf2111b8c262b68cbef4dcd25145d1685047dea0fa6a2683d2ace8bfded54c8b62690a75cbc6c134bd70b29a64119297f3a6e4cef7dba3d30a14890174d4c354e6d9eed520beec35ce54bceba534b9ed709f7a95293353da2b8fa8ca7a22f432866d1cf55dcf61823050be5bc56718b78a62ed260b0b101aac8d37a09cbb128eea79cde5b746baed4a4680a79a4b9255dfb847a68132bd1b4f73db7a1cb3f0314ea82391206245c2dab21f159d8ae82071b64888ebc352c9ee1341a5707b77664322ea15aceb3ba7dd6877a0ace0d5bcc6a84360e03000195f052d987615a3c2ec2313870aa85e619584a22ca37af03e57406721800000032ffffffffffff0332ffffffffffff038ea83d362979c034aeccdcddde5dafb182e7454a868bd81c2a5ff6194d9ba12070ec4c4115e63f22de05fa49a0df304e37d61ba160ccbe91c770b3b04ca057d2c324e29e9626f1482a555c56a7b9a4288ca3cc9519c678a029be5af2e8f99d8974894b88dbcb1225229a376ed5f26c1cc49f37d4eb347a5003bb46dba422388316083f36dcec7ddf47b79591363de8bb53beb07e78de4cb8ae37ec70967e3f0f1fe9124e9dea47a12bc50ca05c3d9bef88709f8b81b66ba1abf0e525ca418bad876b14bf128b559a0220a78bb06973a0311506d8e05bd82d446e4b9a719a36c1192e5df53ae9ba417386923600a65800351445f3caa56d024c67b2eeaa0e452ef7a061cde7d81278ef49488ec3accfbc0300044ee749ed0627862372727691b516618860ea7f13e6f0051e24ece57c4500000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fb7aa306ad4de5f6a3e68908c0732f7208f4405e071f7b85c509fe1d305f79e6e8d3c441123414ee392e93e2f7d7202095810ec15caca1f2d7ee4f6fd3516a756d2405e71be5f6661da8e2ffec0844128a08230c01fe3e97d25e6136066a1739d45c108b03c3cfec33dec510857e6004d439e55e15fadf10d29d6c28cef68e5d817f0be12e43f245016c4689ab3cf8198c69b72d1a3a0e984602578aa44d588180ed88927296b5b7b8ef96e7e88fdd5ee8ea6809477cbddd0576855bedc0329f110df976fc17067d3f42d6d281b97f98eb1cdf03de480fe2e94244495f141547f032164ab19dbeb09aed1691e41ef64b832b398c2dcf40a3677aebf347326d4c1b9e108866d13e9a006b1a0adcb142b4703000495f052d987615a3c2ec2313870aa85e619584a22ca37af03e57406721800000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f8150b7be6c6f3e49579ae0a13234991de8bf081bae7ab6f396a0318c82d4d1d84f0132110e38421b9734a0fa50cd3b2f9dc224cd81810732ab9718cd1e7dc45c6ede9b3a744358e3fa69beae67f40559a608aafb6fb6b372693ae049bd017bae1810729ce9bc61f6e82198469fffb2994c1ce0494c88af057ea08e6f9cd79676174904e8b6c29abdbc8cbc4a715a58e50848e8b9895f670d4d609c653f58d284a0dcc55e63a9f217a61c0160c7ea977ab0eb9ae53de821c79c0ccff94ae6d0991c781e6fddc149e1638ffb3d188c6459c0df36f08bb90a061aa1eb47b4e151c7070cb0def81398529f50272ee00d22a1b3d42af14e455ebd73b554cdf9976449c182ef49f555ba0cfe2439cf64092e140300064ee749ed0627862372727691b516618860ea7f13e6f0051e24ece57c4500000019ffffff0119ffffff018acb7c4dacb67e55e9f9763487d9ef8e9018100241908e6af147fde914a2504c6780c3bcf38fa21ff1a20b7643eec2c3460111b52166e23dd408b12492695df0e0e9caad42b4e9f47b42d34112ef1ba39939912f9395205dbc9c3ad5cf70e2902281286edf5a5e5cd457d0d4a8bfc36194b923bc6c643f1f5800361927bf3aa80540fe6d9295f442ba3e3b1625601c1b220b661975b6e942cd8b1ca37a2580cfa695aefa3c882fd05c32f6144ffb484c99c04cb57e16de50b5223246ba4a73297b2feb52e371f0844e7ec3e0c71a5d9b4429ae2eb0bc9d15841c7a100f853a6c018ad3f63084b6964c6e701bd8756ba6449b697ba8d18f920360348dc9b6e2d3cf9844ee1a5ad772226014e9fc48300903000695f052d987615a3c2ec2313870aa85e619584a22ca37af03e57406721800000019ffffff0119ffffff0185edcac2658181751a7c7dda7f7f20500001dab2d66be222879cc0b276acdd8c0505cc04c91f6d94693ba2cbd892867efd1f9a70f8724697ba034f04de438879b8280e9b3f2ef3a9e39eaab0db5afc81b792349ebc8ed113bf7c385a5339addf8d780c3af845efb9d8c1b106afc4388a533f436a515b3c36e6f1449f5b36cf021265654162dff287edd22407dee0e59dd4c5cde6e8d9afef546a6eb8def59fef64a26b6d864094e95db168dba0554efe8cc8853d4e2d21798fa0288f71d832edca235c6fd90637b1981232c6468d8a8473177bdae62bd7d5fbfa60e7303e53200c1657d12c06da7ed46a45766071b4ea6e3aaab28d5370eca43e4fd6f051d393877608493841bca2f83213417474c11802b0076d8e6873c978eda78da2041f95b1ae4e9db9917daa9b973aeaeaa025d0cba8a707d5df3160792df1277533f0391b14dacc0aac49b3fc1e4b8e5b2060bb5f15cca2de27fcf5bdeb3c4db49d3a81303a7c39e7d3221f6da8ed00e4866ff4bd03010003000500a464e1e1584e30b735fc88872e220e09753969f89de5de0261060bc3a04acb0bca0b23e6d99f6670d7710692eaa148c51172a4e30c23b8f6da23badda303a9860ee20200fe40862b332df2331b284a4b85d36f90bf96e1886d83794f2cd1f2b903000002000400" + }, + "mainnetIncremental": { + "network": "mainnet", + "baseHeight": 2487500, + "height": 2487600, + "blockHeader": "00000020b2b085d903bbd3ce02e891ebe973d68997b9eb40b4071957220000000000000098ed71015ebf7de8d25406091a93bd0fdd3cbf7befe3ab19301ddc9c9ab32a1be8002d6aea9a3d196877bb36", + "hex": "010092b4ba5d0c744c1923f865ae94b4f7d386069e212728e49f110000000000000020d5693eeea5e42c0ce2029d20fcc587f7d0b2fec9fd1c4137000000000000000f0000000529a03bb5518bcdcab201ea2ba4e920426727713e4432bad91b34e73c101c563c1a70571bc4c1772a7ea6ee71e6c24667bb6d66e94946181f26aea727b043adec9cfb32895a9eda013ef37cee73ec2757da5bb12b3c59eb1197fd842a5fa34c189aac126e6c8e63e40a5651b1f32c1ca08326970ca9d72f58305ab41e095046795df2237b2fa16630987021c20439639143942a8bee20ca659c1022be86c67737021f0003000500010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0330f525192f5669614254432f4d696e65642062792079616e666c69702f104a04c900539552e0d22cb4ca52340200ffffffff03706ba302000000001976a914bc89d6071dabc5b4494d303af761a052a5c70d5788acbbb2f70200000000016a928ff204000000001976a9140818d69f7432325c262016eb0a38805c63ef0d1788ac00000000af030030f52500b1269d9ab59a6f8ed4ab58e1bac40574a0f69a847a63ecb099c4f8e4b92de754afd10efa141d712ce45a5844e2f37c6e3157a628b4e0db0f33b9b78cfe875e0f00b25ef25f406262be06799112d5d21625e6c63a225e899e462b6205d1aff14793c12cca831e0db62fd1e2d7502da38a0a0d1e6b45ccb82721d957766151c3329460c8f8ae6f6171dd2e5843bbf5cca9abc285dc38cd0fbc16ddb751dbdabb6b3f4b53ed355502000000070100c387202431f526002c2cb42a867580cf5a762dde21915f9ae8d66442238cf38ba9c4a7b037b73d2704f2ffa0211184535e1cdf944cf975410e0000000000000000000000000000000000ffff93b69280270f0af741440fbb7382b47b31433be23a911b41afe40a1fb8c9ebe22c6946b370d351e5cfa1db891a8a9f749e675c56f61e501bd5182e68eae0700c5a4f4e242399b649363a0101008b52e1a916cb1fcaa88b0802db3b856195ad65aa8f95611f3792a6f92fb5c90d76f738a69bdbd0d34720bc8140edcd1a49099e4805b8f482030000000000000000000000000000000000ffffa5161414270f0685bbd73cee500698c31f79fe287be2b14def46499dd8edede28534b60a1b9b17578b13fca6bd51a76e0520b42b3c0e4f52812becc0c3226f5c2d7a33f86fae7d86b724010100cd10332e29250b009b81be26bf842562f8bd0d3200f477da9e0b3200a332a23dcdc19c042a7ad5a421f5164ca0c4605266dfe87ed645949a120000000000000000000000000000000000ffff8b3bf3c2270f08cb0aa4bd876acd9865ac68224e309b48935f4e942366f1f2b0dff30cf2b03dcb26109996707604542db2cccb68c4f861393d8e7cb0576fc843cb1d3fed90b3789e2f8801020012e4aa37b03d35220c3da9e886ade81736d264aa1cce51025792746a179e6444ad7bcaecfb043c8f9950beb84880c398087bc135f81c97bb2b0000000000000000000000000000000000ffffbc28af46270f870406629d602b0696eb6d6c992e25bfef59fde8883e08ed44e9ada214fee43ce4dd57c4a9ca112c258dbda16e7712dd6995f73efd66ec646cea537bfefcdcb06ca9edc5010000010092d49ab558c9785c5bc1495d56a224dccc514a3be251b8622b8b5b5de492f61a37664cdd13ca52f96c6b7c96f722f7a52107c4463984cb47040000000000000000000000000000000000ffff8a449b17270f9699bbb4ba24b6ad92e8816112baa87ab4638bac785fc234e9de82312fc85dd80bcd91debfb5b43cd42f4e9e53320ed2ce70e0d9a323d99a9210f11340d7c4a242273a7401010055a4116977946b3956febda9c287ada48004d21f9159c65a884e7655fbe5c5ae0154fbb8c74a3b8dff418678e53bb4a5243df4b9fbd1d0e1130000000000000000000000000000000000ffffae8a0e63270f0090b8a58c3f1e3d2b9dc4530c06f3f7f96431d5dbc8acd40f7cdd501e6a520317162fe898f9ff0e44ee2d57643819f759fed6102c73874bbc81a81a0a49a938c7a39372010100d79b39521820e06b784ad7b1fcd43ffd438e77f393036d806a4415f8293315b3c733aebd97dc38f9052e0251dd2c9615b2ced05278692049120000000000000000000000000000000000ffffa5163ef5270f10a65e71faae33d9d167b20d271f9d5a50bea0a711b2601b1e40960e7197c4b3cf4abe760893c16d8e4f0cd6d10571b05d8d06b4534405df32018239781e56f5d57a87d2010404092284e2ba1799aaaddd2a46e2602843fda0d9e1d9a931e60a000000000000000490af0f8bffaedb14c4dc1a17917dce8aa1067d4cc69c49e3050000000000000004cfb5f111070ced4dd1cebf81b7f45e6ed6cfbf030e44412e020000000000000004eab4ac04a0fb4d528ef5b730c9f19101e06762247d3752ee0100000000000000040300041040dd149ed413e12f163986901cb3d5d7bb64e1a1cf9e82130000000000000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fa43b80a7e1e46cdc3c03901f8e3d0877bf0fbc673db27c89f5767a948c30d2ea299e222763ffe32af7c2a00108f2981e2c913f973f5b653fb86f4aba995580dbaaa022ee38eacca5097decf9f57c167bacf6b79cdf8746657fe07e3e9ab94ab90852c4297d9cf0c0b1c4b43cdc7a6276a3ae077fc0f375912156fe42f4102bf612edddc017c0136cce810dc58b38c188fe5c76f66cef99b94b7254e5a36b402c37921000ca3ee478ab6abe4c6992fcab960c92e7a8b491c36789469d24c7fd5de09ae87aa1ea2fb22a65eb503febbc1fa7488f7cfc1bdca75d382ec39897ba8205e16eab74003f00e2ecf7eface6f9806ac5f9d9a1bbcb7cef1ce84551f0b78604e8af1161be207e2c79a581b6f123870300042f5975eb83c020da78288341ce75e1a6c798e4da13fe54d31e0000000000000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f842d68a17c80073ea7335e8a7f5ae20d90354515b6dbb18e4b836d57821c0f7a7375f4402f8aa1ec445da4abe3709a41c3e878f6aa2e710cfe1a2824fd198c5120d7fbf78f1575f0940ed4a55e3e3754afd0359da8378fe2cd856fdc7431592399e02d78fc86782421eab559ae3617ad9fa8cbca2bd4a6eb59888d36e6706b4002b3b085a98f17a53f026a32ede7e569374e624c334814501e8e99da1cf8e1ddf3c3d397500bb07e37529c1f6b290f69b4841d5e97e936a43d26d6d359647b3a67c10ef1faba698a6bae0c16defb04b14a1b98ff83df375a52bbeb3deb6be388059f59d15a4d41ab647ed308118933d029a358697609bd1caa5473b020e62f4072d101ebaac5f8c0afc5c8e78b624dee0300043d8e3c5d32e2f441bed03af3eeaf48e51f762768b96bf0ef2f0000000000000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0fa1f1c8355b322e0697fcc636332b2c5d8fb205ea3fc74fa140d889a0b35ee77b8b8ead623fdf980bb0d3f5ed3bf840931e417568f9539f82fdc2b02f1aac7a1e6046d9785928715d992ca5115edc4d31a2993e9ee723283ac4fc9fa3615da558b99b01c50877f56c2b63af169eb0b0f36788592885435cd06880826c5b351f3a03509b17db5e00861f839afee1fc5f0ac4365b9bc8ef41a0f9497d1215c70ac2d23d0df87c9333a671c849a428dd0e2eb6e735d70d006db17c97ec11407728df1255858b960222dffddd91e0d986edc42221533811744ab0511184a0415ac96d12a533496b524f6e180daa646d86983de44d3d57cdebdd493d9c340c73179fce296d255046149484975280839c096707030004da0e9d7fc30f99c0688e2d101371da0d8534e40bcd2a89a2160000000000000064ffffffffffffffffffffffff0f64ffffffffffffffffffffffff0f971e4a28f2420532039d70f391ca9c19d90170be44bf2e961c0657594b011b77038b160be9ac72bc82a9028751bc331007ee245ed19e01526a843f00e49bedacf82473f017543d67b9fdfcb42a258815b33698e09075be367db3b657b0965d6f165f1aad01e2a185016b0118ece195400e91b5676124e7cfb8fd793ed9c6cfdb023b04f24d1309953cf36b4490290eb5015a8e4ea5ec4ddb99e575d247f9be243ccd67910d15ea395e0dd371e67c5cc5a8d733bbb4d151d299ed6e71c03a6e596544559220624d474ff795e19d88171e7dbd6a382b4d7e8276df32cbb6bb1a2a036f184cc6a4414545cf6c5acf5f287be248a0753ce7811e8b99c16d89128ec143dcc868205d4e4e449eaa35fdb98c7604b0c3c0c6034c234109812b66e240139bf9d20ac10d3b6b9ffbdb2beb8dd29791f19a1832100375a52bbc5887d172d1a6163d594de0bd97ef71fd8b79a75967a01086a4e953eec5fe6e62975705277d67b45e726aacdfcc535900d5a8fec734c8010200a24b1436a250cc5a54ef8b39cbd9e1cd411f5651fbb44a304749f0014e21b10040d2d3ae2e99671b55b8a3b8050f971f06213d09ea6da274412c76e29092287effc6bb6ccf94e315c4de5ab895572466a8f86333b2a450f41b827c7958132d4e010000b232ed20f72bcf1f770bd06292048bc49535543c61bc787a25be6cfe56a2beb694055b82a275f7bc0dc0f10adf28759f05791a173a0dbfc2ee056397caa71cb05d8a3641cb56a7a074158283d4a968ef1b9c80bdde39e28577659720f027ff4901030091cdd042b5c9793886586d5b74e3fd13406400f55c57b207597f6bd3b17fb1b1c89d41704d04ed9cb510374df1dffdd8171f3e09704426307212e6cfa37aa7bbafb026f6cba550db2e3874cdac7fcd22762d1bc3acc556b2b734bbd3a4f6f2d2010100" + } +} diff --git a/test/transaction/payload/coinbasepayload.js b/test/transaction/payload/coinbasepayload.js index a0220f535..f58b18178 100644 --- a/test/transaction/payload/coinbasepayload.js +++ b/test/transaction/payload/coinbasepayload.js @@ -69,6 +69,14 @@ describe('CoinbasePayload', function () { 'Failed to parse payload: raw payload is bigger than expected.' ); }); + + it('Should not modify the buffer it parses', function () { + var buffer = Buffer.from(validCoinbasePayloadBuffer); + + CoinbasePayload.fromBuffer(buffer); + + expect(buffer).to.be.deep.equal(validCoinbasePayloadBuffer); + }); }); describe('.fromJSON', function () { diff --git a/test/util/isDenseArray.js b/test/util/isDenseArray.js new file mode 100644 index 000000000..7c0fc1ee6 --- /dev/null +++ b/test/util/isDenseArray.js @@ -0,0 +1,33 @@ +const { expect } = require('chai'); +const isDenseArray = require('../../lib/util/isDenseArray'); + +describe('isDenseArray', () => { + it('should accept arrays whose keys are exactly their indexes', () => { + expect(isDenseArray([])).to.be.true; + expect(isDenseArray([1, undefined, 'a'])).to.be.true; + }); + + it('should reject non-arrays and arrays with holes', () => { + expect(isDenseArray({ length: 0 })).to.be.false; + expect(isDenseArray('ab')).to.be.false; + // eslint-disable-next-line no-sparse-arrays + expect(isDenseArray([1, , 3])).to.be.false; + }); + + it('should reject extra properties, even when they make up for a hole', () => { + const replaced = []; + replaced.length = 1; + replaced.extra = true; + expect(isDenseArray(replaced)).to.be.false; + + const added = [1]; + added.extra = true; + expect(isDenseArray(added)).to.be.false; + }); + + it('should reject a huge sparse array without materializing it', () => { + const started = Date.now(); + expect(isDenseArray(new Array(2 ** 32 - 1))).to.be.false; + expect(Date.now() - started).to.be.below(1000); + }); +}); diff --git a/test/util/netinfo.js b/test/util/netinfo.js index 13a3761bd..8594f8d1d 100644 --- a/test/util/netinfo.js +++ b/test/util/netinfo.js @@ -69,6 +69,21 @@ describe('netinfo', () => { ); }); + it('should reject address lists with empty slots or extra properties', () => { + // forEach would skip the hole and write a count of 2 with one entry + // eslint-disable-next-line no-sparse-arrays + const sparse = [, '1.2.3.4:9999']; + const extra = []; + extra.length = 1; + extra.extra = '1.2.3.4:9999'; + [sparse, extra].forEach((entries) => { + ['core_p2p', 'platform_p2p', 'platform_https'].forEach((purpose) => { + expect(() => extNetInfoToBuffer({ [purpose]: entries })) + .to.throw(`Expected ${purpose} addresses to be an array without empty slots`); + }); + }); + }); + it('should reject unknown purposes and malformed entries', () => { expect(() => extNetInfoToBuffer({ unknown: [] })).to.throw('Unknown address purpose'); expect(() => extNetInfoToBuffer({ core_p2p: '1.2.3.4:9999' })).to.throw('to be an array'); diff --git a/typings/deterministicmnlist/QuorumEntry.d.ts b/typings/deterministicmnlist/QuorumEntry.d.ts index 7806a3d35..a332b7d6c 100644 --- a/typings/deterministicmnlist/QuorumEntry.d.ts +++ b/typings/deterministicmnlist/QuorumEntry.d.ts @@ -1,5 +1,6 @@ import { SimplifiedMNList } from './SimplifiedMNList'; import { SimplifiedMNListEntry } from './SimplifiedMNListEntry'; +import { BufferReader } from '../buffer/BufferReader'; /** * @class QuorumEntry * @param {string|Object|Buffer} [arg] - A Buffer, JSON string, @@ -19,26 +20,42 @@ import { SimplifiedMNListEntry } from './SimplifiedMNListEntry'; * @property {string} membersSig */ export class QuorumEntry { - constructor(arg?: Buffer | any | string); + constructor(arg?: Buffer | any | string, llmqParamsOverride?: Object); /** * Parse buffer and returns QuorumEntry * @param {Buffer} buffer + * @param {Object} [llmqParamsOverride] * @return {QuorumEntry} */ - static fromBuffer(buffer: Buffer): QuorumEntry; + static fromBuffer(buffer: Buffer, llmqParamsOverride?: Object): QuorumEntry; + + /** + * Reads a final commitment as Core sends it in the P2P `mnlistdiff` message, + * the layout toBufferForHashing() writes. Like Core's RPC, signersCount and + * validMembersCount are the numbers of set bits. + * @param {BufferReader} bufferReader + * @param {Object} [llmqParamsOverride] + * @return {QuorumEntry} + */ + static fromP2PBufferReader( + bufferReader: BufferReader, + llmqParamsOverride?: Object + ): QuorumEntry; /** * @param {string} string + * @param {Object} [llmqParamsOverride] * @return {QuorumEntry} */ - static fromHexString(string: String): QuorumEntry; + static fromHexString(string: String, llmqParamsOverride?: Object): QuorumEntry; /** * Create SMLQuorumEntry from an object * @param {SMLQuorumEntry} obj + * @param {Object} [llmqParamsOverride] * @return {QuorumEntry} */ - static fromObject(obj: Object): QuorumEntry; + static fromObject(obj: Object, llmqParamsOverride?: Object): QuorumEntry; /** * Validate QuorumEntry structure diff --git a/typings/deterministicmnlist/SimplifiedMNListDiff.d.ts b/typings/deterministicmnlist/SimplifiedMNListDiff.d.ts index 23910b1ab..c0fb21fd9 100644 --- a/typings/deterministicmnlist/SimplifiedMNListDiff.d.ts +++ b/typings/deterministicmnlist/SimplifiedMNListDiff.d.ts @@ -19,6 +19,7 @@ export var validMNs: SimplifiedMNListEntry[]; /** * @param {Buffer|Object|string} [arg] - A Buffer, JSON string, or Object representing a MnListDiff * @param {string} [network] + * @param {Object} [llmqParamsOverride] - params for LLMQ types dashcore-lib doesn't know * @class SimplifiedMNListDiff * @property {string} baseBlockHash - sha256 * @property {string} blockHash - sha256 @@ -30,28 +31,37 @@ export var validMNs: SimplifiedMNListEntry[]; * @property {string} merkleRootMNList - merkle root of the whole mn list */ export class SimplifiedMNListDiff { - constructor(arg?: Buffer | any | string, network?: string); + constructor(arg?: Buffer | any | string, network?: string, llmqParamsOverride?: Object); /** - * Creates MnListDiff from a Buffer. + * Creates MnListDiff from a Buffer holding Core's P2P `mnlistdiff` message + * (protocol 70230 and later, unchanged through 70242). * @param {Buffer} buffer * @param {string} [network] + * @param {Object} [llmqParamsOverride] * @return {SimplifiedMNListDiff} */ - static fromBuffer(buffer: Buffer, network?: string): SimplifiedMNListDiff; + static fromBuffer( + buffer: Buffer, + network?: string, + llmqParamsOverride?: Object + ): SimplifiedMNListDiff; /** * @param {string} hexString * @param {string} [network] + * @param {Object} [llmqParamsOverride] * @return {SimplifiedMNListDiff} */ static fromHexString( hexString: string, - network?: string + network?: string, + llmqParamsOverride?: Object ): SimplifiedMNListDiff; /** - * Serializes mnlist diff to a Buffer + * Serializes mnlist diff as Core's P2P `mnlistdiff` message (protocol 70230 + * and later). Quorums need their LLMQ type params, for the bit set sizes. * @return {Buffer} */ toBuffer(): Buffer; @@ -60,9 +70,22 @@ export class SimplifiedMNListDiff { * Creates MNListDiff from object * @param obj * @param {string} [network] + * @param {Object} [llmqParamsOverride] + * @return {SimplifiedMNListDiff} + */ + static fromObject( + obj: any, + network?: string, + llmqParamsOverride?: Object + ): SimplifiedMNListDiff; + + /** + * Creates MNListDiff from the JSON produced by JSON.stringify, not from toObject() + * @param diffJSON + * @param {Object} [llmqParamsOverride] * @return {SimplifiedMNListDiff} */ - static fromObject(obj: any, network?: string): SimplifiedMNListDiff; + static fromJSON(diffJSON: any, llmqParamsOverride?: Object): SimplifiedMNListDiff; /** * sha256 @@ -83,4 +106,9 @@ export class SimplifiedMNListDiff { * merkle root of the whole mn list */ merkleRootMNList: string; + /** + * chainlock signatures used for quorum rotation, each mapped to indexes + * into newQuorums + */ + quorumsCLSigs?: Record[]; } diff --git a/typings/deterministicmnlist/SimplifiedMNListEntry.d.ts b/typings/deterministicmnlist/SimplifiedMNListEntry.d.ts index 864323428..740638864 100644 --- a/typings/deterministicmnlist/SimplifiedMNListEntry.d.ts +++ b/typings/deterministicmnlist/SimplifiedMNListEntry.d.ts @@ -1,3 +1,5 @@ +import { BufferReader } from '../buffer/BufferReader'; + /** * @typedef {Object} SMLEntry * @property {string} proRegTxHash @@ -53,6 +55,18 @@ export class SimplifiedMNListEntry { */ static fromBuffer(buffer: Buffer, network?: string): SimplifiedMNListEntry; + /** + * Reads an entry as Core sends it in the P2P `mnlistdiff` message: + * nVersion followed by the hashed fields (see toP2PBuffer) + * @param {BufferReader} bufferReader + * @param {string} [network] + * @return {SimplifiedMNListEntry} + */ + static fromP2PBufferReader( + bufferReader: BufferReader, + network?: string + ): SimplifiedMNListEntry; + /** * @param {string} string * @param {string} [network] @@ -66,6 +80,13 @@ export class SimplifiedMNListEntry { */ toBuffer(): Buffer; + /** + * Serialize SML entry as Core sends it in the P2P `mnlistdiff` message + * (protocol 70228 and later): nVersion, then the fields toBuffer() hashes + * @return {Buffer} + */ + toP2PBuffer(): Buffer; + /** * Create SMLEntry from an object * @param {SMLEntry} obj @@ -84,6 +105,16 @@ export class SimplifiedMNListEntry { */ getIp(): string; + /** + * Gets the evonode's Platform HTTPS endpoint: the first `platform_https` address + * of a version 3 (ExtAddr) entry, which may be an IPv6 address or a domain, or + * the service ip with platformHTTPPort of an older one. PoSe-banned entries + * have endpoints too; check isValid before using one. + * @return {{host: string, port: number}|null} host without IPv6 brackets, like + * getIp(); null for regular masternodes and evonodes without an address + */ + getPlatformHTTPSEndpoint(): { host: string; port: number } | null; + /** * Creates a copy of SimplifiedMNListEntry * @return {SimplifiedMNListEntry} diff --git a/typings/transaction/payload/ProUpRegTxPayload.d.ts b/typings/transaction/payload/ProUpRegTxPayload.d.ts index 96f51363d..48c6a590b 100644 --- a/typings/transaction/payload/ProUpRegTxPayload.d.ts +++ b/typings/transaction/payload/ProUpRegTxPayload.d.ts @@ -26,7 +26,6 @@ export type ProUpRegTransactionPayloadJSON = { * @class ProUpRegTxPayload * @property {number} version uint_16 2 Upgrade Provider Transaction version number. Currently set to 1. * @property {string} proTxHash uint256 32 The hash of the provider transaction - * @property {number} [type] * @property {number} mode uint_16 2 Masternode mode * @property {string} pubKeyOperator BLSPubKey 48 The public key hash used for operational related signing (network messages, ProTx updates) * @property {string} keyIDVoting CKeyID 20 The public key hash used for voting. @@ -35,9 +34,6 @@ export type ProUpRegTransactionPayloadJSON = { * @property {MasternodePayout[]} [payouts] Owner payees with their reward shares. From version 3 * @property {string} inputsHash uint256 32 Hash of all the outpoints of the transaction inputs * @property {number} payloadSigSize compactSize uint 1-9 Size of the Signature - * @property {string} [platformNodeID] Platform Node ID - * @property {number} [platformP2PPort] Platform P2P port - * @property {number} [platformHTTPPort] Platform HTTP port@ * @property {string} payloadSig vector Variable Signature of the hash of the ProTx fields. Signed by the Owner. * * @param {ProUpRegTransactionPayloadJSON} [payloadJSON] @@ -98,7 +94,6 @@ export class ProUpRegTxPayload { * uint_16 2 Upgrade Provider Transaction version number. Currently set to 1. */ version: number; - type?: number; /** * uint256 32 The hash of the provider transaction */ @@ -135,18 +130,6 @@ export class ProUpRegTxPayload { * compactSize uint 1-9 Size of the Signature */ payloadSigSize: number; - /* - * Platform Node ID - */ - platformNodeID?: string; - /* - * Platform P2P port - */ - platformP2PPort?: number; - /* - * Platform HTTP port - */ - platformHTTPPort?: number; /** * vector Variable Signature of the hash of the ProTx fields. Signed by the Owner. */