From 9a813bad9635b818f4a42357068265d1e21223ee Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 10 Mar 2015 17:47:02 -0400 Subject: [PATCH 01/48] Extensibility - changed options in constructors - define block and transaction constructors for block and tx messages --- lib/bloomfilter.js | 32 +- lib/commands.js | 897 ++++++++++++++++++++++++++++++ lib/index.js | 13 +- lib/inventory.js | 82 +++ lib/message.js | 34 ++ lib/messages.js | 973 ++------------------------------- lib/peer.js | 68 ++- lib/pool.js | 23 +- test/bloomfilter.js | 11 +- test/commands.js | 79 +++ test/{ => data}/connection.log | Bin test/data/messages.json | 106 ++-- test/inventory.js | 96 ++++ test/message.js | 29 + test/messages.js | 175 ++---- test/peer.js | 22 +- test/pool.js | 14 +- 17 files changed, 1462 insertions(+), 1192 deletions(-) create mode 100644 lib/commands.js create mode 100644 lib/inventory.js create mode 100644 lib/message.js create mode 100644 test/commands.js rename test/{ => data}/connection.log (100%) create mode 100644 test/inventory.js create mode 100644 test/message.js diff --git a/lib/bloomfilter.js b/lib/bloomfilter.js index a994774b..10c7adc1 100644 --- a/lib/bloomfilter.js +++ b/lib/bloomfilter.js @@ -6,31 +6,19 @@ var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; var $ = bitcore.util.preconditions; - BloomFilter.fromBuffer = function fromBuffer(payload) { + var obj = {}; var parser = new BufferReader(payload); - var data = parser.readVarLengthBuffer(); - $.checkState(data.length <= BloomFilter.MAX_BLOOM_FILTER_SIZE, - 'Filter data must be <= MAX_BLOOM_FILTER_SIZE bytes'); - var nHashFuncs = parser.readUInt32LE(); - $.checkState(nHashFuncs <= BloomFilter.MAX_HASH_FUNCS, - 'Filter nHashFuncs must be <= MAX_HASH_FUNCS'); - var nTweak = parser.readUInt32LE(); - var nFlags = parser.readUInt8(); - - var vData = []; - var dataParser = new BufferReader(data); - for(var i = 0; i < data.length; i++) { - vData.push(dataParser.readUInt8()); + var length = parser.readUInt8(); + obj.vData = []; + for(var i = 0; i < length; i++) { + obj.vData.push(parser.readUInt8()); } - - return new BloomFilter({ - vData: vData, - nHashFuncs: nHashFuncs, - nTweak: nTweak, - nFlags: nFlags - }); -} + obj.nHashFuncs = parser.readUInt32LE(); + obj.nTweak = parser.readUInt32LE(); + obj.nFlags = parser.readUInt8(); + return new BloomFilter(obj); +}; BloomFilter.prototype.toBuffer = function toBuffer() { var bw = new BufferWriter(); diff --git a/lib/commands.js b/lib/commands.js new file mode 100644 index 00000000..5086e00a --- /dev/null +++ b/lib/commands.js @@ -0,0 +1,897 @@ +'use strict'; + +var Message = require('./message'); +var inherits = require('util').inherits; +var packageInfo = require('../package.json'); +var bitcore = require('bitcore'); +var BN = bitcore.crypto.BN; +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var BufferUtil = bitcore.util.buffer; +var BloomFilter = require('./bloomfilter'); +var Put = require('bufferput'); //todo remove +var $ = bitcore.util.preconditions; +var _ = bitcore.deps._; + +function Commands(options) { + /* jshint maxstatements: 150 */ + /* jshint maxcomplexity: 10 */ + + if (!options) { + options = {}; + } + + var magicNumber = options.magicNumber; + if (!magicNumber) { + magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + } + var Block = options.Block || bitcore.Block; + var BlockHeader = options.BlockHeader || bitcore.BlockHeader; + var Transaction = options.Transaction || bitcore.Transaction; + var MerkleBlock = options.MerkleBlock || bitcore.MerkleBlock; + var protocolVersion = options.protocolVersion || 70000; + + var commands = {}; + + /* shared */ + + function checkFinished(parser) { + if(!parser.finished()) { + throw new Error('Data still available after parsing'); + } + } + + function getNonce() { + return bitcore.crypto.Random.getRandomBuffer(8); + } + + function writeIP(ip, bw) { + var words = ip.v6.split(':').map(function(s) { + return new Buffer(s, 'hex'); + }); + for (var i = 0; i < words.length; i++) { + var word = words[i]; + bw.write(word); + } + } + + function writeAddr(addr, bw) { + if (_.isUndefined(addr)) { + var pad = new Buffer(Array(26)); + bw.write(pad); + return; + } + + bw.writeUInt64LEBN(addr.services); + writeIP(addr.ip, bw); + bw.writeUInt16BE(addr.port); + } + + function writeInventory(inventory, bw) { + bw.writeVarintNum(inventory.length); + inventory.forEach(function(value) { + bw.writeUInt32LE(value.type); + bw.write(value.hash); + }); + } + + function parseIP(parser) { + var ipv6 = []; + var ipv4 = []; + for (var a = 0; a < 8; a++) { + var word = parser.read(2); + ipv6.push(word.toString('hex')); + if (a >= 6) { + ipv4.push(word[0]); + ipv4.push(word[1]); + } + } + ipv6 = ipv6.join(':'); + ipv4 = ipv4.join('.'); + return { + v6: ipv6, + v4: ipv4 + }; + } + + function parseAddr(parser) { + var services = parser.readUInt64LEBN(); + var ip = parseIP(parser); + var port = parser.readUInt16BE(); + return { + services: services, + ip: ip, + port: port + }; + } + + function sanitizeStartStop(obj) { + /* jshint maxcomplexity: 10 */ + $.checkArgument(_.isUndefined(options.starts) || _.isArray(options.starts)); + var starts = obj.starts; + var stop = obj.stop; + if (starts) { + starts = starts.map(function(hash) { + if (_.isString(hash)) { + return BufferUtil.reverse(new Buffer(hash, 'hex')); + } else { + return hash; + } + }); + } else { + starts = []; + } + + for (var i = 0; i < starts.length; i++) { + if (starts[i].length !== 32) { + throw new Error('Invalid hash ' + i + ' length: ' + starts[i].length); + } + } + + stop = obj.stop; + if (_.isString(stop)) { + stop = BufferUtil.reverse(new Buffer(stop, 'hex')); + } + if (!stop) { + stop = BufferUtil.NULL_HASH; + } + obj.starts = starts; + obj.stop = stop; + + return obj; + } + + /** + * The version message is used on connection creation to advertise + * the type of node. The remote node will respond with its version, and no + * communication is possible until both peers have exchanged their versions. + * By default, bitcore advertises itself as named `bitcore:0.8`. + * + * @param{Object} obj - properties for the version + * @param{String} obj.subversion - version of the client + * @param{Buffer} obj.nonce - a random 8 byte buffer + */ + commands.version = function(obj) { + Message.call(this, obj); + this.command = 'version'; + _.assign(this, obj); + this.magicNumber = magicNumber; + this.nonce = this.nonce || getNonce(); + this.services = this.services || new BN(1, 10); + this.timestamp = this.timestamp || new Date(); + this.version = this.version || protocolVersion; + this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/'; + this.startHeight = this.startHeight || 0; + }; + inherits(commands.version, Message); + + commands.version.fromObject = function(obj) { + return new commands.version(obj); + }; + + commands.version.fromBuffer = function(payload) { + var parser = new BufferReader(payload); + var obj = {}; + obj.version = parser.readUInt32LE(); + obj.services = parser.readUInt64LEBN(); + obj.timestamp = new Date(parser.readUInt64LEBN().toNumber() * 1000); + + obj.addrMe = { + services: parser.readUInt64LEBN(), + ip: parseIP(parser), + port: parser.readUInt16BE() + }; + obj.addrYou = { + services: parser.readUInt64LEBN(), + ip: parseIP(parser), + port: parser.readUInt16BE() + }; + obj.nonce = parser.read(8); + obj.subversion = parser.readVarLengthBuffer().toString(); + obj.startHeight = parser.readUInt32LE(); + + if(parser.finished()) { + obj.relay = true; + } else { + obj.relay = !!parser.readUInt8(); + } + checkFinished(parser); + + return commands.version.fromObject(obj); + }; + + commands.version.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeUInt64LEBN(this.services); + + var timestampBuffer = new Buffer(Array(8)); + timestampBuffer.writeUInt32LE(Math.round(this.timestamp.getTime() / 1000), 0); + bw.write(timestampBuffer); + + writeAddr(this.addrMe, bw); + writeAddr(this.addrYou, bw); + bw.write(this.nonce); + bw.writeVarintNum(this.subversion.length); + bw.write(new Buffer(this.subversion, 'ascii')); + bw.writeUInt32LE(this.startHeight); + bw.writeUInt8(this.relay); + + return bw.concat(); + }; + + /* verack */ + + commands.verack = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'verack'; + }; + inherits(commands.verack, Message); + + commands.verack.fromObject = function(obj) { + return new commands.verack(obj); + }; + + commands.verack.fromBuffer = function(payload) { + return commands.verack.fromObject({}); + }; + + commands.verack.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; + }; + + /* ping */ + + commands.ping = function(options) { + Message.call(this, options); + this.command = 'ping'; + this.magicNumber = magicNumber; + this.nonce = options.nonce || getNonce(); + }; + inherits(commands.ping, Message); + + commands.ping.prototype.getPayload = function() { + return this.nonce; + }; + + commands.ping.fromObject = function(obj) { + return new commands.ping(obj); + }; + + commands.ping.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.nonce = parser.read(8); + + checkFinished(parser); + return commands.ping.fromObject(obj); + }; + + /* pong */ + + commands.pong = function(options) { + Message.call(this, options); + this.command = 'pong'; + this.magicNumber = magicNumber; + this.nonce = options.nonce; + }; + inherits(commands.pong, Message); + + commands.pong.fromObject = function(obj) { + return new commands.pong(obj); + }; + + commands.pong.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.nonce = parser.read(8); + + checkFinished(parser); + return commands.pong.fromObject(obj); + }; + + commands.pong.prototype.getPayload = function() { + return this.nonce; + }; + + /* block */ + + commands.block = function(options) { + Message.call(this, options); + this.command = 'block'; + this.magicNumber = magicNumber; + this.block = options.block; + }; + inherits(commands.block, Message); + + commands.block.fromObject = function(options) { + return new commands.block(options); + }; + + commands.block.fromBuffer = function(payload) { + var block = Block.fromBuffer(payload); + return commands.block.fromObject({block: block}); + }; + + commands.block.prototype.getPayload = function() { + return this.block.toBuffer(); + }; + + /* tx */ + + commands.tx = function(options) { + Message.call(this, options); + this.command = 'tx'; + this.magicNumber = magicNumber; + this.transaction = options.transaction; + }; + inherits(commands.tx, Message); + + commands.tx.fromObject = function(options) { + return new commands.tx(options); + }; + + commands.tx.fromBuffer = function(payload) { + var transaction; + if (Transaction.prototype.fromBuffer) { + transaction = Transaction().fromBuffer(payload); + } else { + transaction = Transaction.fromBuffer(payload); + } + return commands.tx.fromObject({transaction: transaction}); + }; + + commands.tx.prototype.getPayload = function() { + return this.transaction.toBuffer(); + }; + + /* getdata */ + + commands.getdata = function(options) { + Message.call(this, options); + this.command = 'getdata'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; + }; + + inherits(commands.getdata, Message); + + commands.getdata.fromObject = function(options) { + return new commands.getdata(options); + }; + + commands.getdata.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + checkFinished(parser); + return commands.getdata.fromObject(obj); + }; + + commands.getdata.prototype.getPayload = function() { + var bw = new BufferWriter(); + writeInventory(this.inventory, bw); + return bw.concat(); + }; + + /** + * Sent in response to a `getheaders` message. It contains information about + * block headers. + * + * @param{Array} blockheaders - array of block headers + */ + commands.headers = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'headers'; + this.headers = options.headers; + }; + inherits(commands.headers, Message); + + commands.headers.fromObject = function(options) { + return new commands.headers(options); + }; + + commands.headers.fromBuffer = function(payload) { + var obj = {}; + + $.checkArgument(payload && payload.length > 0, 'No data found to create Headers message'); + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + + obj.headers = []; + for (var i = 0; i < count; i++) { + var header = BlockHeader.fromBufferReader(parser); + obj.headers.push(header); + var txn_count = parser.readUInt8(); + $.checkState(txn_count === 0, 'txn_count should always be 0'); + + } + checkFinished(parser); + + return commands.headers.fromObject(obj); + }; + + commands.headers.prototype.getPayload = function() { + var put = new Put(); + put.varint(this.headers.length); + + for (var i = 0; i < this.headers.length; i++) { + var buffer = this + .headers[i] + .toBuffer(); + put.put(buffer); + put.varint(0); + } + + return put.buffer(); + }; + + /* notfound */ + + commands.notfound = function(options) { + Message.call(this, options); + this.command = 'notfound'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; + }; + inherits(commands.notfound, Message); + + commands.notfound.fromObject = function(options) { + return new commands.notfound(options); + }; + + commands.notfound.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + checkFinished(parser); + return commands.notfound.fromObject(obj); + + }; + + commands.notfound.prototype.getPayload = function() { + var bw = new BufferWriter(); + writeInventory(this.inventory, bw); + return bw.concat(); + }; + + /* inv */ + + commands.inv = function(options) { + Message.call(this, options); + this.command = 'inv'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; + }; + + inherits(commands.inv, Message); + + commands.inv.fromObject = function(options) { + return new commands.inv(options); + }; + + commands.inv.prototype.getPayload = function() { + var bw = new BufferWriter(); + writeInventory(this.inventory, bw); + return bw.concat(); + }; + + commands.inv.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + checkFinished(parser); + return commands.inv.fromObject(obj); + }; + + /* addr */ + + commands.addr = function(options) { + Message.call(this, options); + this.command = 'addr'; + this.magicNumber = magicNumber; + this.addresses = options.addresses; + }; + inherits(commands.addr, Message); + + commands.addr.fromObject = function(options) { + return new commands.addr(options); + }; + + commands.addr.fromBuffer = function(payload) { + var parser = new BufferReader(payload); + + var addrCount = parser.readVarintNum(); + + var obj = {}; + obj.addresses = []; + for (var i = 0; i < addrCount; i++) { + // todo: time only available on versions >=31402 + var time = new Date(parser.readUInt32LE() * 1000); + + var addr = parseAddr(parser); + addr.time = time; + obj.addresses.push(addr); + } + + checkFinished(parser); + return commands.addr.fromObject(obj); + }; + + commands.addr.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.addresses.length); + + for (var i = 0; i < this.addresses.length; i++) { + var addr = this.addresses[i]; + bw.writeUInt32LE(addr.time.getTime() / 1000); + writeAddr(addr, bw); + } + + return bw.concat(); + }; + + /* alert */ + + commands.alert = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'alert'; + + this.payload = options.payload || new Buffer(32); + this.signature = options.signature || new Buffer(32); + + }; + inherits(commands.alert, Message); + + commands.alert.fromObject = function(options) { + return new commands.alert(options); + }; + + commands.alert.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.payload = parser.readVarLengthBuffer(); + obj.signature = parser.readVarLengthBuffer(); + checkFinished(parser); + return commands.alert.fromObject(obj); + }; + + commands.alert.prototype.getPayload = function() { + var put = new Put(); + put.varint(this.payload.length); + put.put(this.payload); + + put.varint(this.signature.length); + put.put(this.signature); + + return put.buffer(); + }; + + /* reject */ + // todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject + commands.reject = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'reject'; + }; + inherits(commands.reject, Message); + + commands.reject.fromObject = function(options) { + return new commands.reject(options); + }; + + commands.reject.fromBuffer = function(payload) { + var obj = {}; + return commands.reject.fromObject(obj); + }; + + commands.reject.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; + }; + + /** + * Contains information about a MerkleBlock + * + * @name P2P.Message.MerkleBlock + * @param {MerkleBlock} block + */ + commands.merkleblock = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'merkleblock'; + $.checkArgument( + _.isUndefined(options.merkleBlock) || + options.merkleBlock instanceof MerkleBlock + ); + this.merkleBlock = options.merkleBlock; + }; + inherits(commands.merkleblock, Message); + + commands.merkleblock.fromObject = function(options) { + return new commands.merkleblock(options); + }; + + commands.merkleblock.fromBuffer = function(payload) { + var obj = {}; + $.checkArgument(BufferUtil.isBuffer(payload)); + obj.merkleBlock = MerkleBlock.fromBuffer(payload); + return commands.merkleblock.fromObject(obj); + }; + + commands.merkleblock.prototype.getPayload = function() { + return this.merkleBlock ? this.merkleBlock.toBuffer() : BufferUtil.EMPTY_BUFFER; + }; + + /* filterload */ + + commands.filterload = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filterload'; + $.checkArgument(_.isUndefined(options.filter) || options.filter instanceof BloomFilter, + 'BloomFilter object or undefined required for FilterLoad'); + this.filter = options.filter; + }; + inherits(commands.filterload, Message); + + commands.filterload.fromObject = function(options) { + return new commands.filterload(options); + }; + + commands.filterload.fromBuffer = function(payload) { + var obj = {}; + obj.filter = BloomFilter.fromBuffer(payload); + return commands.filterload.fromObject(obj); + }; + + commands.filterload.prototype.getPayload = function() { + if(this.filter) { + return this.filter.toBuffer(); + } else { + return BufferUtil.EMPTY_BUFFER; + } + }; + + /** + * Request peer to add data to a bloom filter already set by 'filterload' + * + * @name P2P.Message.filteradd + * @param{Buffer} data - Array of bytes representing bloom filter data + */ + commands.filteradd = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filteradd'; + this.data = options.data || BufferUtil.EMPTY_BUFFER; + }; + inherits(commands.filteradd, Message); + + commands.filteradd.fromObject = function(options) { + return new commands.filteradd(options); + }; + + commands.filteradd.fromBuffer = function(payload) { + var obj = {}; + $.checkArgument(payload); + var parser = new BufferReader(payload); + obj.data = parser.readVarLengthBuffer(); + checkFinished(parser); + return commands.filteradd.fromObject(obj); + }; + + commands.filteradd.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.data.length); + bw.write(this.data); + return bw.concat(); + }; + + /* filterclear */ + + commands.filterclear = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filterclear'; + }; + inherits(commands.filterclear, Message); + + commands.filterclear.fromObject = function(options) { + return new commands.filterclear(options); + }; + + commands.filterclear.fromBuffer = function(payload) { + return commands.filterclear.fromObject({}); + }; + + commands.filterclear.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; + }; + + /** + * Query another peer about blocks. It can query for multiple block hashes, + * and the response will contain all the chains of blocks starting from those + * hashes. + * + * @param{Array} starts - array of buffers or strings with the starting block hashes + * @param{Buffer} [stop] - hash of the last block + */ + commands.getblocks = function(options) { + Message.call(this, options); + this.command = 'getblocks'; + this.version = protocolVersion; + this.magicNumber = magicNumber; + + options = sanitizeStartStop(options); + this.starts = options.starts; + this.stop = options.stop; + + }; + inherits(commands.getblocks, Message); + + commands.getblocks.fromObject = function(obj) { + return new commands.getblocks(obj); + }; + + commands.getblocks.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + $.checkArgument(!parser.finished(), 'No data received in payload'); + + obj.version = parser.readUInt32LE(); + var startCount = parser.readVarintNum(); + + obj.starts = []; + for (var i = 0; i < startCount; i++) { + obj.starts.push(parser.read(32)); + } + obj.stop = parser.read(32); + checkFinished(parser); + return commands.getblocks.fromObject(obj); + }; + + commands.getblocks.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeVarintNum(this.starts.length); + for (var i = 0; i < this.starts.length; i++) { + bw.write(this.starts[i]); + } + if (this.stop.length !== 32) { + throw new Error('Invalid hash length: ' + this.stop.length); + } + bw.write(this.stop); + return bw.concat(); + }; + + /** + * Request block headers starting from a hash + * + * @param{Array} starts - array of buffers with the starting block hashes + * @param{Buffer} [stop] - hash of the last block + */ + //todo: need test data + commands.getheaders = function(options) { + Message.call(this, options); + this.command = 'getheaders'; + this.version = protocolVersion; + this.magicNumber = magicNumber; + + options = sanitizeStartStop(options); + this.starts = options.starts; + this.stop = options.stop; + + }; + inherits(commands.getheaders, Message); + + commands.getheaders.fromObject = function(obj) { + return new commands.getheaders(obj); + }; + + commands.getheaders.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + $.checkArgument(!parser.finished(), 'No data received in payload'); + + obj.version = parser.readUInt32LE(); + var startCount = Math.min(parser.readVarintNum(), 500); + + obj.starts = []; + for (var i = 0; i < startCount; i++) { + obj.starts.push(parser.read(32)); + } + obj.stop = parser.read(32); + checkFinished(parser); + return commands.getheaders.fromObject(obj); + }; + + commands.getheaders.prototype.getPayload = function() { + var put = new Put(); + put.word32le(this.version); + put.varint(this.starts.length); + for (var i = 0; i < this.starts.length; i++) { + put.put(this.starts[i]); + } + if (this.stop.length !== 32) { + throw new Error('Invalid hash length: ' + this.stop.length); + } + put.put(this.stop); + return put.buffer(); + }; + + /* mempool */ + commands.mempool = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'mempool'; + }; + inherits(commands.mempool, Message); + + commands.mempool.fromObject = function(options) { + return new commands.mempool(options); + }; + + commands.mempool.fromBuffer = function(payload) { + return commands.mempool.fromObject({}); + }; + + commands.mempool.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; + }; + + /* getaddr */ + //todo: need test data + commands.getaddr = function(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'getaddr'; + }; + inherits(commands.getaddr, Message); + + commands.getaddr.fromObject = function(options) { + return new commands.getaddr(options); + }; + + commands.getaddr.fromBuffer = function(payload) { + var obj = {}; + return commands.getaddr.fromObject(obj); + }; + + commands.getaddr.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; + }; + + return commands; + +} + +module.exports = Commands; diff --git a/lib/index.js b/lib/index.js index b65689ae..e7a608b4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,9 +1,16 @@ /** * @namespace P2P */ + +var Messages = require('./messages'); + module.exports = { - Messages: require('./messages'), + Message: require('./message'), + Commands: require('./commands'), + Inventory: require('./inventory'), + BloomFilter: require('./bloomfilter'), + Messages: Messages, + messages: new Messages(), Peer: require('./peer'), - Pool: require('./pool'), - BloomFilter: require('./bloomfilter') + Pool: require('./pool') }; diff --git a/lib/inventory.js b/lib/inventory.js new file mode 100644 index 00000000..111000aa --- /dev/null +++ b/lib/inventory.js @@ -0,0 +1,82 @@ +'use strict'; + +var bitcore = require('bitcore'); +var $ = bitcore.util.preconditions; +var BufferUtil = bitcore.util.buffer; +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var _ = bitcore.deps._; + +function Inventory(obj) { + this.type = obj.type; + if (!BufferUtil.isBuffer(obj.hash)) { + throw new TypeError('Unexpected hash, expected to be a buffer'); + } + this.hash = obj.hash; + +} + +Inventory.forItem = function(type, hash) { + $.checkArgument(hash); + //todo: is reversing expected behavior? + if (_.isString(hash)) { + hash = new Buffer(hash, 'hex'); + hash = BufferUtil.reverse(hash); + } + return new Inventory({type: type, hash: hash}); +}; + +Inventory.forBlock = function(hash) { + return Inventory.forItem(Inventory.TYPE.BLOCK, hash); +}; + +Inventory.forFilteredBlock = function(hash) { + return Inventory.forItem(Inventory.TYPE.FILTERED_BLOCK, hash); +}; + +Inventory.forTransaction = function(hash) { + return Inventory.forItem(Inventory.TYPE.TX, hash); +}; + +Inventory.prototype.toBuffer = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.type); + bw.write(this.hash); + return bw.concat(); +}; + +Inventory.prototype.toBufferWriter = function(bw) { + bw.writeUInt32LE(this.type); + bw.write(this.hash); + return bw; +}; + +Inventory.fromBuffer = function(payload) { + var parser = new BufferReader(payload); + var obj = {}; + obj.type = parser.readUInt32LE(); + obj.hash = parser.read(32); + return new Inventory(obj); +}; + +Inventory.fromBufferWriter = function(bw) { + var obj = {}; + obj.type = bw.readUInt32LE(); + obj.hash = bw.read(32); + return new Inventory(obj); +}; + +// https://en.bitcoin.it/wiki/Protocol_specification#Inventory_Vectors +Inventory.TYPE = {}; +Inventory.TYPE.ERROR = 0; +Inventory.TYPE.TX = 1; +Inventory.TYPE.BLOCK = 2; +Inventory.TYPE.FILTERED_BLOCK = 3; +Inventory.TYPE_NAME = [ + 'ERROR', + 'TX', + 'BLOCK', + 'FILTERED_BLOCK' +]; + +module.exports = Inventory; diff --git a/lib/message.js b/lib/message.js new file mode 100644 index 00000000..9d164779 --- /dev/null +++ b/lib/message.js @@ -0,0 +1,34 @@ +'use strict'; + +var bitcore = require('bitcore'); +var BufferWriter = bitcore.encoding.BufferWriter; +var Hash = bitcore.crypto.Hash; + +/** + * Base message that can be inherited to add an additional + * `getPayload` method to modify the message payload. + */ +function Message(options) { + this.command = options.command; + this.magicNumber = options.magicNumber; +} + +Message.prototype.toBuffer = Message.prototype.serialize = function() { + + var commandBuf = new Buffer(Array(12)); + commandBuf.write(this.command, 'ascii'); + + var payload = this.getPayload(); + var checksum = Hash.sha256sha256(payload).slice(0, 4); + + var bw = new BufferWriter(); + bw.writeUInt32LE(this.magicNumber); + bw.write(commandBuf); + bw.writeUInt32LE(payload.length); + bw.write(checksum); + bw.write(payload); + + return bw.concat(); +}; + +module.exports = Message; diff --git a/lib/messages.js b/lib/messages.js index 16f89aad..754a38b6 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -1,94 +1,42 @@ -'use strict'; -/** - * @namespace P2P.Message - */ -/* jshint curly: false */ - -var Buffers = require('buffers'); -var Put = require('bufferput'); -var util = require('util'); -var BloomFilter = require('./bloomfilter'); + 'use strict'; var bitcore = require('bitcore'); -var _ = bitcore.deps._; - -var BlockHeaderModel = bitcore.BlockHeader; -var BlockModel = bitcore.Block; -var MerkleBlockModel = bitcore.MerkleBlock; -var BufferReader = bitcore.encoding.BufferReader; -var BufferWriter = bitcore.encoding.BufferWriter; var BufferUtil = bitcore.util.buffer; -var $ = bitcore.util.preconditions; var Hash = bitcore.crypto.Hash; -var Random = bitcore.crypto.Random; -var TransactionModel = bitcore.Transaction; - -var CONNECTION_NONCE = Random.getPseudoRandomBuffer(8); -var PROTOCOL_VERSION = 70000; - -/** - * @desc Internal function that discards data until another message is found. - * @name P2P.Message#discardUntilNextMessage - */ -var discardUntilNextMessage = function(network, dataBuffer) { - var magicNumber = network.networkMagic; +var Commands = require('./commands'); - var i = 0; - for (;;) { - // check if it's the beginning of a new message - var packageNumber = dataBuffer.slice(0, 4); - if (BufferUtil.equals(packageNumber, magicNumber)) { - dataBuffer.skip(i); - return true; - } +function Messages(options) { + this.commands = new Commands(options); - // did we reach the end of the buffer? - if (i > (dataBuffer.length - 4)) { - dataBuffer.skip(i); - return false; - } + if (!options) { + options = {}; + } - i++; // continue scanning + // map command messages + for (var key in this.commands) { + this[key] = this.commands[key]; } -}; -/** - * Abstract Message this knows how to parse and serialize itself. - * Concrete subclasses should implement {fromBuffer} and {getPayload} methods. - * @name P2P.Message - */ -function Message() {} + var defaultMagicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + this.magicNumber = options.magicNumber || defaultMagicNumber; +} -/** - * @value - * @name P2P.Message.COMMANDS - */ -Message.COMMANDS = {}; +Messages.MINIMUM_LENGTH = 20; +Messages.PAYLOAD_START = 16; -var PAYLOAD_START = 16; -/** - * Static helper for consuming a data buffer until the next message. - * - * @name P2P.Message#parseMessage - * @param{Network} network - the network object - * @param{Buffer} dataBuffer - the buffer to read from - * @returns{Message|undefined} A message or undefined if there is nothing to read. - */ -var parseMessage = function(network, dataBuffer) { - $.checkArgument(network); - $.checkArgument(dataBuffer); +Messages.prototype.parseMessage = function(dataBuffer) { /* jshint maxstatements: 18 */ - if (dataBuffer.length < 20) { + if (dataBuffer.length < Messages.MINIMUM_LENGTH) { return; } // Search the next magic number - if (!discardUntilNextMessage(network, dataBuffer)) return; + if (!this.discardUntilNextMessage(dataBuffer)) return; - var payloadLen = (dataBuffer.get(PAYLOAD_START)) + - (dataBuffer.get(PAYLOAD_START + 1) << 8) + - (dataBuffer.get(PAYLOAD_START + 2) << 16) + - (dataBuffer.get(PAYLOAD_START + 3) << 24); + var payloadLen = (dataBuffer.get(Messages.PAYLOAD_START)) + + (dataBuffer.get(Messages.PAYLOAD_START + 1) << 8) + + (dataBuffer.get(Messages.PAYLOAD_START + 2) << 16) + + (dataBuffer.get(Messages.PAYLOAD_START + 3) << 24); var messageLength = 24 + payloadLen; if (dataBuffer.length < messageLength) { @@ -106,874 +54,39 @@ var parseMessage = function(network, dataBuffer) { } dataBuffer.skip(messageLength); - return Message.buildMessage(command, payload); -}; - -module.exports.parseMessage = parseMessage; - - -/** - * Look up a message type by command name and instantiate the correct Message - * @name P2P.Message#buildMessage - */ -Message.buildMessage = function(command, payload) { - var CommandClass = Message.COMMANDS[command]; - $.checkState(CommandClass, 'Unsupported message command: ' + command); - return new CommandClass().fromBuffer(payload); -}; - -/** - * Parse instance state from buffer. - * - * @param{Buffer} payload - the buffer to read from - * @returns{Message} The same message instance - */ -Message.prototype.fromBuffer = function(payload) { - /* jshint unused: false */ - return this; -}; - -/** - * Serialize the payload into a buffer. - * - * @returns{Buffer} the serialized payload - */ -Message.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; -}; - -/** - * Serialize the message into a buffer. - * - * @returns{Buffer} the serialized message - */ -Message.prototype.serialize = function(network) { - $.checkArgument(network, 'Must specify network for serialization'); - var commandBuf = new Buffer(this.command, 'ascii'); - $.checkState(commandBuf.length <= 12, 'Command name too long'); - var magic = network.networkMagic; - - var payload = this.getPayload(); - var checksum = Hash.sha256sha256(payload).slice(0, 4); - - // -- HEADER -- - var message = new Put(); - message.put(magic); - message.put(commandBuf); - message.pad(12 - commandBuf.length); // zero-padded - message.word32le(payload.length); - message.put(checksum); - - // -- BODY -- - message.put(payload); - - return message.buffer(); -}; - -/** - * check if parser has no more extra data - */ -Message.prototype._checkFinished = function(parser) { - $.checkState(parser.finished(), 'data still available after parsing ' + this.constructor.name); -}; - -module.exports.Message = Message; - -/** - * The version message(`ver`) is used on connection creation, to advertise - * the type of node.The remote node will respond with its version, and no - * communication is possible until both peers have exchanged their versions. - * By default, bitcore advertises itself as named `bitcore:0.8`. - * - * @name P2P.Message.Version - * @param{string} subversion - version of the client - * @param{Buffer} nonce - a random 8 bytes buffer - */ -function Version(subversion, nonce, relay) { - var packageInfo = require('../package.json'); - this.command = 'version'; - this.version = PROTOCOL_VERSION; - this.subversion = subversion || '/bitcore:' + packageInfo.version + '/'; - this.nonce = nonce || CONNECTION_NONCE; - this.relay = relay === false ? false : true; -} -util.inherits(Version, Message); - -Version.prototype.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - - /** - * @type {number} - * @desc The version of the bitcoin protocol - */ - this.version = parser.readUInt32LE(); - /** - * @type {BN} - * @desc A mapbit with service bits: what features are supported by the peer - */ - this.services = parser.readUInt64LEBN(); - /** - * @type {BN} - * @desc The time this message was sent - */ - this.timestamp = new Date(parser.readUInt64LEBN().toNumber() * 1000); - /** - * @type {object} - * @desc IPv4/6 address of the interface used to connect to this peer - */ - var me_services = parser.readUInt64LEBN(); - var me_ip = Addresses.parseIP(parser); - var me_port = parser.readUInt16BE(); - this.addr_me = { - services: me_services, - ip: me_ip, - port: me_port - }; - /** - * @type {object} - * @desc IPv4/6 address of the peer - */ - var your_services = parser.readUInt64LEBN(); - var your_ip = Addresses.parseIP(parser); - var your_port = parser.readUInt16BE(); - this.addr_you = { - services: your_services, - ip: your_ip, - port: your_port - }; - /** - * @type {Buffer} - * @desc A random number - */ - this.nonce = parser.read(8); - /** - * @desc The node's user agent / subversion - * @type {string} - */ - this.subversion = parser.readVarLengthBuffer().toString(); - /** - * @desc The height of the last block accepted in the blockchain by this peer - * @type {number} - */ - this.start_height = parser.readUInt32LE(); - - /** - * @desc Whether the remote peer should announce relayed transactions or not, see BIP 0037 - * @type {boolean} - */ - // This field is optional, so should not always be read - if(parser.finished()) { - this.relay = true; - } else { - this.relay = !!parser.readUInt8(); - } - - this._checkFinished(parser); - return this; -}; - -Version.prototype.getPayload = function() { - var put = new Put(); - put.word32le(this.version); - put.word64le(1); // services - put.word64le(Math.round(new Date().getTime() / 1000)); // timestamp - Addresses.writeAddr(this.addr_me, put); - Addresses.writeAddr(this.addr_you, put); - put.put(this.nonce); - put.varint(this.subversion.length); - put.put(new Buffer(this.subversion, 'ascii')); - put.word32le(this.start_height); - put.word8(this.relay); - - return put.buffer(); -}; - -module.exports.Version = Message.COMMANDS.version = Version; - -/** - * From the bitcoin protocol spec: "Allows a node to advertise its knowledge of - * one or more objects. It can be received unsolicited, or in reply to - * getblocks.". - * - * @name P2P.Message.Inventory - * @param{Array} inventory - reported elements - */ -function Inventory(inventory) { - $.checkArgument(_.isUndefined(inventory) || - _.isArray(inventory), 'Inventory for ' + - this.constructor.name + ' must be an array of objects'); - $.checkArgument(_.isUndefined(inventory) || - inventory.length === 0 || - (inventory[0] && !_.isUndefined(inventory[0].type) && !_.isUndefined(inventory[0].hash)), - 'Inventory for ' + this.constructor.name + ' must be an array of objects'); - this.command = 'inv'; - /** - * @name P2P.Message.Inventory.inventory - * @desc An array of objects with `{type: int, hash: Buffer}` signature - * @type {Array.Buffer} - */ - this.inventory = inventory || []; -} -util.inherits(Inventory, Message); - -// https://en.bitcoin.it/wiki/Protocol_specification#Inventory_Vectors -Inventory.TYPE = {}; -Inventory.TYPE.ERROR = 0; -Inventory.TYPE.TX = 1; -Inventory.TYPE.BLOCK = 2; -Inventory.TYPE.FILTERED_BLOCK = 3; -Inventory.TYPE_NAME = [ - 'ERROR', - 'TX', - 'BLOCK', - 'FILTERED_BLOCK' -]; - -Inventory.forItem = function(type, hash) { - $.checkArgument(hash); - if (_.isString(hash)) { - hash = new Buffer(hash, 'hex'); - hash = BufferUtil.reverse(hash); - } - return { - type: type, - typeName: Inventory.TYPE_NAME[type], - hash: hash - }; -}; - -Inventory.prototype.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - for (var i = 0; i < count; i++) { - var type = parser.readUInt32LE(); - var hash = parser.read(32); - this.inventory.push(Inventory.forItem(type, hash)); - } - - this._checkFinished(parser); - return this; -}; - -Inventory.prototype.getPayload = function() { - var put = new Put(); - - put.varint(this.inventory.length); - this.inventory.forEach(function(value) { - put.word32le(value.type); - put.put(value.hash); - }); - - return put.buffer(); -}; - -var creatorForItem = function(clazz, type) { - return function(hash) { - return new clazz([Inventory.forItem(type, hash)]); - }; -}; - -module.exports.Inventory = Message.COMMANDS.inv = Inventory; - -/** - * notfound is a response to a getdata, sent if any requested data - * items could not be relayed, for example, because the requested - * transaction was not in the memory pool or relay set. - * - * (from bitcoin's protocol spec) - * - * @name P2P.Message.NotFound - * @param{Array} inventory - not found elements - */ -function NotFound(inventory) { - Inventory.call(this, inventory); - this.command = 'notfound'; -} - -util.inherits(NotFound, Inventory); -module.exports.NotFound = Message.COMMANDS.notfound = NotFound; - -/** - * getdata is used in response to inv, to retrieve the content of a specific - * object, and is usually sent after receiving an inv packet, after filtering - * known elements. It can be used to retrieve transactions, but only if they - * are in the memory pool or relay set - arbitrary access to transactions in the - * chain is not allowed to avoid having clients start to depend on nodes having - * full transaction indexes (which modern nodes do not). - * - * (from bitcoin's protocol spec) - * - * @name P2P.Message.GetData - * @param{Array} inventory - requested elements - */ -function GetData(inventory) { - Inventory.call(this, inventory); - this.command = 'getdata'; - this.inventory = inventory || []; -} - - -util.inherits(GetData, Inventory); -module.exports.GetData = Message.COMMANDS.getdata = GetData; - -/** - * Sent to another peer mainly to check the connection is still alive. - * - * @name P2P.Message.Ping - * @param{Buffer} nonce - a random 8 bytes buffer - */ -function Ping(nonce) { - this.command = 'ping'; - /** - * @desc A random number that should be returned by the peer in a pong message - * @type {number} - */ - this.nonce = nonce || CONNECTION_NONCE; -} -util.inherits(Ping, Message); - -Ping.prototype.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - this.nonce = parser.read(8); - - this._checkFinished(parser); - return this; -}; - -Ping.prototype.getPayload = function() { - return this.nonce; -}; - -module.exports.Ping = Message.COMMANDS.ping = Ping; -/** - * Sent in response to a Ping message - * - * @name P2P.Message.Pong - * @param{Buffer} nonce - a random 8 bytes buffer - */ -function Pong(nonce) { - this.command = 'pong'; - /** - * @desc A random number that must match the one sent in the corresponding `ping` message - * @type {number} - */ - this.nonce = nonce || CONNECTION_NONCE; -} - -util.inherits(Pong, Ping); -module.exports.Pong = Message.COMMANDS.pong = Pong; - -/** - * Message used to notify about known addresses. - * - * @name P2P.Message.Addressess - * @param{Array} addresses - array of know addresses - */ -function Addresses(addresses) { - this.command = 'addr'; - /** - * @type {Array.Buffer} - * @desc An array of ipv4/6 addresses - */ - this.addresses = addresses || []; -} -util.inherits(Addresses, Message); - -Addresses.writeAddr = function(addr, put) { - if (_.isUndefined(addr)) { - put.pad(26); - return; - } - put.word64le(addr.services); - Addresses.writeIP(addr.ip, put); - put.word16be(addr.port); + return this.buildFromBuffer(command, payload); }; -Addresses.writeIP = function(ip, put) { - $.checkArgument(ip.v6, 'Need ipv6 to write IP'); - var words = ip.v6.split(':').map(function(s) { - return new Buffer(s, 'hex'); - }); - for (var i = 0; i < words.length; i++) { - var word = words[i]; - put.put(word); - } -}; - -// http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses -Addresses.parseIP = function(parser) { - var ipv6 = []; - var ipv4 = []; - for (var a = 0; a < 8; a++) { - var word = parser.read(2); - ipv6.push(word.toString('hex')); - if (a >= 6) { - ipv4.push(word[0]); - ipv4.push(word[1]); +Messages.prototype.discardUntilNextMessage = function(dataBuffer) { + var i = 0; + for (;;) { + // check if it's the beginning of a new message + var packageNumber = dataBuffer.slice(0, 4).readUInt32LE(0); + if (packageNumber === this.magicNumber) { + dataBuffer.skip(i); + return true; } - } - ipv6 = ipv6.join(':'); - ipv4 = ipv4.join('.'); - - return { - v6: ipv6, - v4: ipv4 - }; -}; - -Addresses.parseAddr = function(parser) { - var services = parser.readUInt64LEBN(); - var ip = Addresses.parseIP(parser); - var port = parser.readUInt16BE(); - return { - services: services, - ip: ip, - port: port - }; -}; - -Addresses.prototype.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - var addrCount = Math.min(parser.readVarintNum(), 1000); - - this.addresses = []; - for (var i = 0; i < addrCount; i++) { - // TODO: Time actually depends on the version of the other peer (>=31402) - var time = new Date(parser.readUInt32LE() * 1000); - - var addr = Addresses.parseAddr(parser); - addr.time = time; - - this.addresses.push(addr); - } - - this._checkFinished(parser); - return this; -}; -Addresses.prototype.getPayload = function() { - var put = new Put(); - put.varint(this.addresses.length); - - for (var i = 0; i < this.addresses.length; i++) { - var addr = this.addresses[i]; - put.word32le(addr.time); - Addresses.writeAddr(addr, put); - break; - } - - return put.buffer(); -}; - -module.exports.Addresses = Message.COMMANDS.addr = Addresses; - -/** - * Query another node for known IPV4/6 addresses. - * - * @name P2P.Message.GetAddresses - */ -function GetAddresses() { - this.command = 'getaddr'; -} - -util.inherits(GetAddresses, Message); -module.exports.GetAddresses = Message.COMMANDS.getaddr = GetAddresses; - -/** - * Finishes the connection handshake started by the `ver` message. - * - * @name P2P.Message.VerAck - */ -function VerAck() { - this.command = 'verack'; -} - -util.inherits(VerAck, Message); -module.exports.VerAck = Message.COMMANDS.verack = VerAck; - -/** - * A reject message should be sent when a message is not supported or - * interpreted as invalid. - * - * @name P2P.Message.Reject - */ -function Reject() { - this.command = 'reject'; -} -util.inherits(Reject, Message); - -// TODO: Parse REJECT message - -module.exports.Reject = Message.COMMANDS.reject = Reject; - -/** - * Used to send a message signed by a developer of the bitcoin project. - * - * @name P2P.Message.Alert - */ -function Alert(payload, signature) { - this.command = 'alert'; - this.payload = payload || new Buffer(32); - this.signature = signature || new Buffer(32); -} -util.inherits(Alert, Message); - -Alert.prototype.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - this.payload = parser.readVarLengthBuffer(); - this.signature = parser.readVarLengthBuffer(); - this._checkFinished(parser); - return this; -}; - -Alert.prototype.getPayload = function() { - var put = new Put(); - put.varint(this.payload.length); - put.put(this.payload); - - put.varint(this.signature.length); - put.put(this.signature); - - return put.buffer(); -}; - -module.exports.Alert = Message.COMMANDS.alert = Alert; - -/** - * Sent in response to a `getheaders` message. It contains information about - * block headers. - * - * @name P2P.Message.Headers - * @param{Array} blockheaders - array of block headers - */ -function Headers(blockheaders) { - this.command = 'headers'; - /** - * @type {Array.BlockHeader} - * @desc An array of `BlockHeader` - */ - this.headers = blockheaders || []; -} -util.inherits(Headers, Message); - -Headers.prototype.fromBuffer = function(payload) { - $.checkArgument(payload && payload.length > 0, 'No data found to create Headers message'); - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - - this.headers = []; - for (var i = 0; i < count; i++) { - var header = BlockHeaderModel.fromBufferReader(parser); - this.headers.push(header); - var txn_count = parser.readUInt8(); - $.checkState(txn_count === 0, 'txn_count should always be 0'); - - } - this._checkFinished(parser); - return this; -}; - -Headers.prototype.getPayload = function() { - var put = new Put(); - put.varint(this.headers.length); - - for (var i = 0; i < this.headers.length; i++) { - var buffer = this - .headers[i] - .toBuffer(); - put.put(buffer); - put.varint(0); - } - - return put.buffer(); -}; - -module.exports.Headers = Message.COMMANDS.headers = Headers; - -/** - * Contains information about a Block - * - * @name P2P.Message.Block - * @param {Block} block - */ -function Block(block) { - $.checkArgument(_.isUndefined(block) || block instanceof BlockModel); - this.command = 'block'; - - /** - * @type {Block} - * @desc The block received - */ - this.block = block; -} -util.inherits(Block, Message); - -Block.prototype.fromBuffer = function(payload) { - $.checkArgument(BufferUtil.isBuffer(payload)); - var block = BlockModel(payload); - return new Block(block); -}; - -Block.prototype.getPayload = function() { - return this.block ? this.block.toBuffer() : new Buffer(0); -}; - -module.exports.Block = Message.COMMANDS.block = Block; - -/** - * Contains information about a MerkleBlock - * - * @name P2P.Message.MerkleBlock - * @param {MerkleBlock} block - */ -function MerkleBlock(block) { - $.checkArgument(_.isUndefined(block) || block instanceof MerkleBlockModel); - this.command = 'merkleblock'; - - /** - * @type {Block} - * @desc The block received - */ - this.merkleBlock = block; -} -util.inherits(MerkleBlock, Message); - -MerkleBlock.prototype.fromBuffer = function(payload) { - $.checkArgument(BufferUtil.isBuffer(payload)); - var block = MerkleBlockModel(payload); - return new MerkleBlock(block); -}; - -MerkleBlock.prototype.getPayload = function() { - return this.merkleBlock ? this.merkleBlock.toBuffer() : new Buffer(0); -}; - -module.exports.MerkleBlock = Message.COMMANDS.merkleblock = MerkleBlock; - - -/** - * Contains information about a transaction - * - * @name P2P.Message.Transaction - * @param{Transaction} transaction - */ -function Transaction(transaction) { - $.checkArgument(_.isUndefined(transaction) || transaction instanceof TransactionModel); - this.command = 'tx'; - /** - * @type {Transaction} - */ - this.transaction = transaction; -} -util.inherits(Transaction, Message); - -Transaction.prototype.fromBuffer = function(payload) { - this.transaction = TransactionModel(payload); - return this; -}; - -Transaction.prototype.getPayload = function() { - return this.transaction ? this.transaction.toBuffer() : new Buffer(0); -}; - -module.exports.Transaction = Message.COMMANDS.tx = Transaction; - -/** - * Query another peer about blocks. It can query for multiple block hashes, - * and the response will contain all the chains of blocks starting from those - * hashes. - * - * @name P2P.Message.GetBlocks - * @param{Array} starts - array of buffers or strings with the starting block hashes - * @param{Buffer} [stop] - hash of the last block - */ -function GetBlocks(starts, stop) { - $.checkArgument(_.isUndefined(starts) || _.isArray(starts)); - this.command = 'getblocks'; - /** - * @type {number} - */ - this.version = PROTOCOL_VERSION; - - starts = starts ? starts.map(function(hash) { - return _.isString(hash) ? BufferUtil.reverse(new Buffer(hash, 'hex')) : hash; - }) : undefined; - /** - * @type {Array.Buffer} - */ - this.starts = starts || []; - - for (var i = 0; i < this.starts.length; i++) { - if (this.starts[i].length !== 32) { - throw new Error('Invalid hash ' + i + ' length: ' + this.starts[i].length); + // did we reach the end of the buffer? + if (i > (dataBuffer.length - 4)) { + dataBuffer.skip(i); + return false; } - } - /** - * @type {Array.Buffer} - * @desc Hashes to limit the amount of blocks to be sent - */ - this.stop = (_.isString(stop) ? BufferUtil.reverse(new Buffer(stop, 'hex')) : stop) || BufferUtil.NULL_HASH; -} -util.inherits(GetBlocks, Message); - -GetBlocks.prototype.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - $.checkArgument(!parser.finished(), 'No data received in payload'); - this.version = parser.readUInt32LE(); - - var startCount = Math.min(parser.readVarintNum(), 500); - this.starts = []; - for (var i = 0; i < startCount; i++) { - this.starts.push(parser.read(32)); - } - this.stop = parser.read(32); - this._checkFinished(parser); - - return this; -}; -GetBlocks.prototype.getPayload = function() { - var put = new Put(); - put.word32le(this.version); - put.varint(this.starts.length); - - for (var i = 0; i < this.starts.length; i++) { - put.put(this.starts[i]); - } - - if (this.stop.length !== 32) { - throw new Error('Invalid hash length: ' + this.stop.length); + i++; // continue scanning } - put.put(this.stop); - - return put.buffer(); -}; - -module.exports.GetBlocks = Message.COMMANDS.getblocks = GetBlocks; - -/** - * Request block headers starting from a hash - * - * @name P2P.Message.GetHeaders - * @param{Array} starts - array of buffers with the starting block hashes - * @param{Buffer} [stop] - hash of the last block - */ -function GetHeaders(starts, stop) { - GetBlocks.call(this, starts, stop); - this.command = 'getheaders'; -} - -util.inherits(GetHeaders, GetBlocks); -module.exports.GetHeaders = Message.COMMANDS.getheaders = GetHeaders; - -/** - * Request peer to apply a bloom filter to 'inv' messages sent back - * - * @name P2P.Message.filterload - * @param{BloomFilter} filter - a BloomFilter object - */ -function FilterLoad(filter) { - this.command = 'filterload'; - $.checkArgument(_.isUndefined(filter) || filter instanceof BloomFilter, - 'BloomFilter object or undefined required for FilterLoad'); - this.filter = filter; - return this; -} -util.inherits(FilterLoad, Message); - -FilterLoad.prototype.fromBuffer = function(payload) { - this.filter = BloomFilter.fromBuffer(payload); - return this; }; -FilterLoad.prototype.getPayload = function() { - if(this.filter) { - return this.filter.toBuffer() - } else { - return BufferUtil.EMPTY_BUFFER; +Messages.prototype.buildFromBuffer = function(command, payload) { + if (!this.commands[command]) { + throw new Error('Unsupported message command: ' + command); } + return this.commands[command].fromBuffer(payload); }; -module.exports.FilterLoad = Message.COMMANDS.filterload = FilterLoad; - -/** - * Request peer to add data to a bloom filter already set by 'filterload' - * - * @name P2P.Message.filteradd - * @param{Buffer} data - Array of bytes representing bloom filter data - */ -function FilterAdd(data) { - this.command = 'filteradd'; - this.data = data || new Buffer(0,'hex'); - return this; -} -util.inherits(FilterAdd, Message); - -FilterAdd.prototype.fromBuffer = function(payload) { - $.checkArgument(payload); - var parser = new BufferReader(payload); - this.data = parser.readVarLengthBuffer(); - $.checkState(this.data.length <= BloomFilter.MAX_BLOOM_FILTER_SIZE, - 'FilterAdd data must be <= 520 bytes'); - this._checkFinished(parser); - return this; +Messages.prototype.build = Messages.prototype.buildFromObject = function(command, object) { + return this.commands[command].fromObject(object); }; -FilterAdd.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.data.length); - bw.write(this.data); - return bw.concat(); -}; - -module.exports.FilterAdd = Message.COMMANDS.filteradd = FilterAdd; - -/** - * Request peer to apply a bloom filter to 'inv' messages sent back - * - * @name P2P.Message.filterclear - */ -function FilterClear() { - this.command = 'filterclear'; -} -util.inherits(FilterClear, Message); - -module.exports.FilterClear = Message.COMMANDS.filterclear = FilterClear; - -/** - * Request for transactions on the mempool - * - * @name P2P.Message.GetMempool - */ -function GetMempool() { - this.command = 'mempool'; -} - -util.inherits(GetMempool, Message); -module.exports.GetMempool = Message.COMMANDS.mempool = GetMempool; - -// TODO: Remove this PATCH (yemel) -Buffers.prototype.skip = function(i) { - if (i === 0) return; - - if (i === this.length) { - this.buffers = []; - this.length = 0; - return; - } - - var pos = this.pos(i); - this.buffers = this.buffers.slice(pos.buf); - this.buffers[0] = new Buffer(this.buffers[0].slice(pos.offset)); - this.length -= i; -}; - - - -[Inventory, GetData, NotFound].forEach(function(clazz) { - clazz.forBlock = creatorForItem(clazz, Inventory.TYPE.BLOCK); - clazz.forFilteredBlock = clazz.forMerkleBlock = - creatorForItem(clazz, Inventory.TYPE.FILTERED_BLOCK); - clazz.forTransaction = creatorForItem(clazz, Inventory.TYPE.TX); -}); +module.exports = Messages; diff --git a/lib/peer.js b/lib/peer.js index 435fbbb9..583e0c68 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -4,14 +4,10 @@ var Buffers = require('buffers'); var EventEmitter = require('events').EventEmitter; var Net = require('net'); var Socks5Client = require('socks5-client'); -var util = require('util'); - var bitcore = require('bitcore'); -var $ = bitcore.util.preconditions; var Networks = bitcore.Networks; var Messages = require('./messages'); - -var MAX_RECEIVE_BUFFER = 10000000; +var util = require('util'); /** * A Peer instance represents a remote bitcoin node and allows to communicate @@ -33,28 +29,33 @@ var MAX_RECEIVE_BUFFER = 10000000; * @returns {Peer} A new instance of Peer. * @constructor */ -function Peer(host, port, network, relay) { +function Peer(options) { if (!(this instanceof Peer)) { - return new Peer(host, port, network); + return new Peer(options); } - // overloading stuff - if (port instanceof Object && !network) { - network = port; - port = undefined; + this.host = options.host || 'localhost'; + this.status = Peer.STATUS.DISCONNECTED; + this.port = options.port; + + + this.network = Networks.get(options.network) || Networks.defaultNetwork; + if (!this.port) { + this.port = this.network.port; } - this.host = host || 'localhost'; - this.status = Peer.STATUS.DISCONNECTED; - this.network = network || Networks.defaultNetwork; - this.port = port || this.network.port; - this.relay = relay === false ? false : true; + this.messages = options.messages || new Messages({ + magicNumber: this.network.networkMagic.readUInt32LE(0), + Block: bitcore.Block, + Transaction: bitcore.Transaction + }); this.dataBuffer = new Buffers(); this.version = 0; this.bestHeight = 0; this.subversion = null; + this.relay = options.relay === false ? false : true; // set message handlers var self = this; @@ -76,6 +77,7 @@ function Peer(host, port, network, relay) { } util.inherits(Peer, EventEmitter); +Peer.MAX_RECEIVE_BUFFER = 10000000; Peer.STATUS = { DISCONNECTED: 'disconnected', CONNECTING: 'connecting', @@ -91,8 +93,6 @@ Peer.STATUS = { * @returns {Peer} The same Peer instance. */ Peer.prototype.setProxy = function(host, port) { - $.checkState(this.status === Peer.STATUS.DISCONNECTED); - this.proxy = { host: host, port: port @@ -122,7 +122,7 @@ Peer.prototype.connect = function() { this.socket.on('data', function(data) { self.dataBuffer.push(data); - if (self.dataBuffer.length > MAX_RECEIVE_BUFFER) { + if (self.dataBuffer.length > Peer.MAX_RECEIVE_BUFFER) { // TODO: handle this case better return self.disconnect(); } @@ -155,14 +155,17 @@ Peer.prototype.disconnect = function() { * @param {Message} message - A message instance */ Peer.prototype.sendMessage = function(message) { - this.socket.write(message.serialize(this.network)); + this.socket.write(message.toBuffer()); }; /** * Internal function that sends VERSION message to the remote peer. */ Peer.prototype._sendVersion = function() { - var message = new Messages.Version(null, null, this.relay); + // todo: include sending ip address + var message = this.messages.buildFromObject('version', { + relay: this.relay + }); this.sendMessage(message); }; @@ -170,7 +173,9 @@ Peer.prototype._sendVersion = function() { * Send a PONG message to the remote peer. */ Peer.prototype._sendPong = function(nonce) { - var message = new Messages.Pong(nonce); + var message = this.messages.buildFromObject('pong', { + nonce: nonce + }); this.sendMessage(message); }; @@ -178,8 +183,7 @@ Peer.prototype._sendPong = function(nonce) { * Internal function that tries to read a message from the data buffer */ Peer.prototype._readMessage = function() { - var message = Messages.parseMessage(this.network, this.dataBuffer); - + var message = this.messages.parseMessage(this.dataBuffer); if (message) { this.emit(message.command, message); this._readMessage(); @@ -199,4 +203,20 @@ Peer.prototype._getSocket = function() { return new Net.Socket(); }; +// TODO: Remove this PATCH (yemel) +Buffers.prototype.skip = function(i) { + if (i === 0) return; + + if (i === this.length) { + this.buffers = []; + this.length = 0; + return; + } + + var pos = this.pos(i); + this.buffers = this.buffers.slice(pos.buf); + this.buffers[0] = new Buffer(this.buffers[0].slice(pos.offset)); + this.length -= i; +}; + module.exports = Peer; diff --git a/lib/pool.js b/lib/pool.js index 47d65969..9871f7bd 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -2,11 +2,10 @@ var dns = require('dns'); var EventEmitter = require('events').EventEmitter; - var bitcore = require('bitcore'); -var Networks = bitcore.Networks; var sha256 = bitcore.crypto.Hash.sha256; var Peer = require('./peer'); +var Networks = bitcore.Networks; var util = require('util'); function now() { @@ -43,7 +42,6 @@ function Pool(network, options) { var self = this; options = options || {}; - this.network = Networks.get(network) || Networks.defaultNetwork; this.keepalive = false; this._connectedPeers = {}; @@ -51,8 +49,10 @@ function Pool(network, options) { this.listenAddr = options.listenAddr !== false; this.dnsSeed = options.dnsSeed !== false; - this.relay = options.relay !== false; this.maxSize = options.maxSize || Pool.MaxConnectedPeers; + this.messages = options.messages; + this.network = Networks.get(network) || Networks.defaultNetwork; + this.relay = options.relay === false ? false : true; if (options.addrs) { for(var i = 0; i < options.addrs.length; i++) { @@ -186,7 +186,12 @@ Pool.prototype._connectPeer = function _connectPeer(addr) { function addConnectedPeer(addr) { var port = addr.port || self.network.port; var ip = addr.ip.v4 || addr.ip.v6; - var peer = new Peer(ip, port, self.network, self.relay); + var peer = new Peer({ + ip: ip, + port: port, + messages: self.messages, + relay: self.relay + }); peer.on('disconnect', function peerDisconnect() { self.emit('peerdisconnect', peer, addr); }); @@ -298,4 +303,12 @@ Pool.prototype.inspect = function inspect() { this._addrs.length + '>'; }; +Pool.prototype.sendMessage = function(message) { + // broadcast to peers + for(var key in this._connectedPeers) { + var peer = this._connectedPeers[key]; + peer.sendMessage(message); + } +}; + module.exports = Pool; diff --git a/test/bloomfilter.js b/test/bloomfilter.js index e7859f09..45c7ac53 100644 --- a/test/bloomfilter.js +++ b/test/bloomfilter.js @@ -1,6 +1,7 @@ 'use strict'; var chai = require('chai'); +var should = chai.should(); var assert = require('assert'); var bitcore = require('bitcore'); @@ -8,6 +9,10 @@ var Data = require('./data/messages'); var P2P = require('../'); var BloomFilter = P2P.BloomFilter; +function getPayloadBuffer(messageBuffer) { + return new Buffer(messageBuffer.slice(48), 'hex'); +} + // convert a hex string to a bytes buffer function ParseHex(str) { var result = []; @@ -22,9 +27,9 @@ function ParseHex(str) { describe('BloomFilter', function() { it('BloomFilter#fromBuffer and toBuffer methods work', function() { - var testPayload = Data.FILTERLOAD.payload; - var filter = new BloomFilter.fromBuffer(new Buffer(testPayload, 'hex')); - filter.toBuffer().toString('hex').should.equal(testPayload); + var testPayloadBuffer = getPayloadBuffer(Data.filterload.message); + var filter = new BloomFilter.fromBuffer(testPayloadBuffer); + filter.toBuffer().should.deep.equal(testPayloadBuffer); }); // test data from: https://github.com/bitcoin/bitcoin/blob/master/src/test/bloom_tests.cpp diff --git a/test/commands.js b/test/commands.js new file mode 100644 index 00000000..5e02ff18 --- /dev/null +++ b/test/commands.js @@ -0,0 +1,79 @@ +'use strict'; + +var should = require('chai').should(); +var P2P = require('../'); +var Commands = P2P.Commands; +var commandData = require('./data/messages.json'); +var Data = require('./data/messages');//todo merge with commandData +var bitcore = require('bitcore'); + +function getPayloadBuffer(messageBuffer) { + return new Buffer(messageBuffer.slice(48), 'hex'); +} + +describe('P2P Command Builder', function() { + + describe('@constructor', function() { + + it('should return commands based on default', function() { + // instantiate + var commands = new Commands(); + should.exist(commands); + }); + + it('should return commands with customizations', function() { + // instantiate + var commands = new Commands({ + magicNumber: 0xd9b4bef9, + Block: bitcore.Block, + Transaction: bitcore.Transaction + }); + should.exist(commands); + }); + + }); + + describe('Commands', function() { + + var commands = new Commands(); + + describe('#fromBuffer/#toBuffer round trip for all commands', function() { + Object.keys(commands).forEach(function(command) { + + it('should round trip buffers for command: ' + command, function(done) { + var payloadBuffer = getPayloadBuffer(commandData[command].message); + should.exist(commands[command]); + var message = commands[command].fromBuffer(payloadBuffer); + var outputBuffer = message.getPayload(); + outputBuffer.toString('hex').should.equal(payloadBuffer.toString('hex')); + outputBuffer.should.deep.equal(payloadBuffer); + var expectedBuffer = new Buffer(commandData[command].message, 'hex'); + message.toBuffer().should.deep.equal(expectedBuffer); + done(); + }); + + }); + }); + + describe('version', function() { + it('#fromBuffer works w/o fRelay arg', function() { + var payloadBuffer = getPayloadBuffer(Data.version.messagenofrelay); + var message = commands.version.fromBuffer(payloadBuffer); + message.relay.should.equal(true); + }); + + it('#relay setting works', function() { + [true,false].forEach(function(relay) { + var message = commands.version.fromObject({relay: relay}); + message.relay.should.equal(relay); + var messageBuf = message.getPayload(); + var newMessage = commands.version.fromBuffer(messageBuf); + newMessage.relay.should.equal(relay); + }); + }); + + }); + + }); + +}); diff --git a/test/connection.log b/test/data/connection.log similarity index 100% rename from test/connection.log rename to test/data/connection.log diff --git a/test/data/messages.json b/test/data/messages.json index 8cd3f61c..d7260b53 100644 --- a/test/data/messages.json +++ b/test/data/messages.json @@ -1,85 +1,69 @@ { - "VERSION": { + "version": { "message": "f9beb4d976657273696f6e000000000065000000fc970f17721101000100000000000000ba62885400000000010000000000000000000000000000000000ffffba8886dceab0010000000000000000000000000000000000ffff05095522208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001", - "payload": "721101000100000000000000ba62885400000000010000000000000000000000000000000000ffffba8886dceab0010000000000000000000000000000000000ffff05095522208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001" + "messagenofrelay": "f9beb4d976657273696f6e000000000065000000fc970f17721101000100000000000000ba62885400000000010000000000000000000000000000000000ffffba8886dceab0010000000000000000000000000000000000ffff05095522208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa3170500" }, - "VERSION_NO_FRELAY": { - "payload": "701101000100000000000000d78be25400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008f7e557f1b892912102f626974636f72653a302e31302e302f00000000" + "verack": { + "message": "f9beb4d976657261636b000000000000000000005df6e0e2" }, - "ALERT": { - "message": "", - "payload": "73010000003766404f00000000b305434f00000000f2030000f1030000001027000048ee00000064000000004653656520626974636f696e2e6f72672f666562323020696620796f7520686176652074726f75626c6520636f6e6e656374696e67206166746572203230204665627275617279004730450221008389df45f0703f39ec8c1cc42c13810ffcae14995bb648340219e353b63b53eb022009ec65e1c1aaeec1fd334c6b684bde2b3f573060d5b70c3a46723326e4e8a4f1" + "inv": { + "message": "f9beb4d9696e76000000000000000000890200006e4f18431201000000f97347795bf7490ddeba98c129086f54e06633936a49a2a398defb49e5edbb00010000009cb17394db9280b2d5e7d9f01456358384e7453300d48138ca1590e8cd86632f010000000f810b6071b9808117c4b82619b4ae0a7994ed5a0f2c050e50fd5742c3b4e90901000000962798bce9cbfcee9a8c6b5df4cf8bed944eb31ec975e8c1be79b7ab010a1cb501000000baa905bcfc9d2641ecd759724f796a7614e91c7492507b418ba8e077a6e57054010000009a5ac6dae146392edf8b6e96c30951ed7752f9095fab61728207db0f135b18a30100000050e5f153e3f0d89ea432d9a26f088a89b3ca38dbf3f0247ddf15f80779915da801000000c255841510a284b88ba696ed9edfeb4cdb34966271c30bc3bce9fd881106850f0100000000e45af8455894f72cccc4053e1f5e6076a673982b1f4ca5b2abc7b6496823ab01000000c7e6dc049e5b0fdc0f8fac8934e84e9b2f0c3036c7c02638cc05ee2575d6be1701000000aef6435c03c99ee83702aaaf106dc853dee5bcb6025f3af67d1ec72202c437ad010000005369fc3dc81403fe52f766ef585245af908404be1b46e2ef67c93b748ef467e3010000002400f16a63c411ae5e336175afc26515e548c7267c0debdb6aa46830399ba35f01000000ceb4fa6a8ca2713baee2214422b73da47a20934c83d25017bd80053e65ef3091010000000d899968e591703ddd6fd6ce073837588209f8b3b245ecf1bf93e77b9806a6c501000000bddeb56581ad7882d8f2abc78e5a48b3de02d923cd1cfc7525d2dfe80470248a01000000f286188c0947b023f0ba5dd1ea596751d50f67fbe31e64ead39dd711d3585b5801000000e17ad100ebffd2d5a630d37fd2496b2f5ab6ae5c8812da3c642fb6b8dd37f5fd" }, - "REJECT": { - "message": "", - "payload": "" + "addr": { + "message": "f9beb4d9616464720000000000000000b93a0000480bab8afdf5016816fa53010000000000000000000000000000000000ffff51403eea208ddb2a8854010000000000000000000000000000000000ffff707c60d9208d31413a54010000000000000000000000000000000000ffff5ed5fd13208d182c8854010000000000000000000000000000000000ffff505f3f81208d65ecb853010000000000000000000000000000000000ffff54d72104208dcca94054010000000000000000000000000000000000ffff40bbe1f2208d73238854010000000000000000000000000000000000ffff55d683cd208d5d258854010000000000000000000000000000000000ffffd523a66d208dcbea87540100000000000000200100005ef579fd2c9d47be4b56d671208db3e58754010000000000000000000000000000000000ffffae373156208d11e58754810000000000000000000000000000000000ffff51071276208d38f48754010000000000000000000000000000000000ffff6d4372e0208d4f448854010000000000000000000000000000000000ffffc556cbea208deb208854010000000000000000000000000000000000ffff450eb059208d97336054010000000000000000000000000000000000ffff4d60444a208dad608754010000000000000000000000000000000000ffff01abd3c3208d2cbe8754010000000000000000000000000000000000ffff7963d8b8208d9dfa8754010000000000000000000000000000000000ffffcebe862c208da7f66554010000000000000000000000000000000000ffff62f2a82e208d430d8854010000000000000000000000000000000000ffff6deb6956208d08148854010000000000000000000000000000000000ffff4b9804ef208d950e8854010000000000000000000000000000000000ffff6b8dd2e1208d1cfc8754010000000000000000000000000000000000ffffb2fe1dab208de7458754010000000000000000000000000000000000ffff32cccb82208d12625654010000000000000000000000000000000000ffff55e6f35a208dd7908354010000000000000000000000000000000000ffff4c642e6f208d17398854010000000000000000000000000000000000ffff45ea14df208dc72d8854010000000000000000000000000000000000ffffdaa48b96208d57fb8654010000000000000000000000000000000000ffff5f0080a9208d4e9a6d54010000000000000000000000000000000000ffff6d94e741208d7a418854010000000000000000000000000000000000ffff6d43b0c1208d5c098854010000000000000000000000000000000000ffff9c11e7ea208d442d8854010000000000000000000000000000000000ffffd0424482208db142fb53010000000000000000000000000000000000ffff54344a4a208d70158854010000000000000000000000000000000000ffff538f8201208d39288854010000000000000000000000000000000000ffff57daab71208d262f8854010000000000000000000000000000000000ffff57e045b1208d91430654010000000000000000000000000000000000ffff4212def5208d7c468854010000000000000000000000000000000000ffff62a4f53b208d9c3e8854010000000000000000000000000000000000ffff5b736bcc208d4d074f54010000000000000000000000000000000000ffff43af9d9b208deaca8754010000000000000000000000000000000000ffff4a8084a0208d74f68754010000000000000000000000000000000000ffff46304541208d5d098854010000000000000000000000000000000000ffff6eabb626208d08228854010000000000000000000000000000000000ffff1f07b030208d959a8254010000000000000000000000000000000000ffff4fa996b3208d4bac8754010000000000000000000000000000000000ffff43a0605b208de4038854010000000000000000000000000000000000ffff4cb41a5b208ddc308854010000000000000000000000000000000000ffffb72e6072208deac86f54010000000000000000000000000000000000ffffd5b815e2208d1b118854010000000000000000000000000000000000ffff3ff8d0a4208db1258854010000000000000000000000000000000000ffff5edd3a6a208d20268754010000000000000000000000000000000000ffff59ed3cfc208d83ed8754010000000000000000000000000000000000ffff4460baea208d71278854010000000000000000000000000000000000ffff4583b547208da7968754010000000000000000000000000000000000ffffd1c34463208d9d428854010000000000000000000000000000000000ffff43c15bc5208d33448854010000000000000000000000000000000000ffff2e0d8a5c208dfa2e7154010000000000000000000000000000000000ffffceff2bca208d47288854010000000000000000000000000000000000ffff5b8d0190208d0e847954010000000000000000000000000000000000ffff5501b8c2208d552f8854010000000000000000000000000000000000ffff5cf3b604208d8c2e8854010000000000000000000000000000000000ffff58666b0d208dcbdd8754010000000000000000000000000000000000ffff59a85cf9208d04208854010000000000000000000000000000000000ffff5f60abe0208dba238854010000000000000000000000000000000000ffff2e77ce67208d62c68754010000000000000000000000000000000000ffffcbae5658208d41108854010000000000000000000000000000000000ffff49329ec8208ddf1d8854010000000000000000000000000000000000ffff603ada12208df90c8854010000000000000000000000000000000000ffff51e0c21c208d5dff8754010000000000000000000000000000000000ffff904ca539208de0118854010000000000000000000000000000000000ffff5c0cecf7208d1b2d88540100000000000000200100005ef579fb18f317b2aba4edfc208dfaa27f54010000000000000000000000000000000000ffff54c65a7c208dc2db8754010000000000000000000000000000000000ffffad0b7981208d88a58754010000000000000000000000000000000000ffff188a1995208de0d78754010000000000000000000000000000000000ffff62a7a6bd208d18f66b54010000000000000000000000000000000000ffffdb4fbe80208dfcf78754010000000000000000000000000000000000ffff67145102208d3d3a8854010000000000000000000000000000000000ffff64259bd6208dcaa98654010000000000000000000000000000000000ffff32c737e1208d3a6a5c54010000000000000000000000000000000000ffff6d793f75208d22d38754010000000000000000000000000000000000ffff0595cc0d208da9ea8754010000000000000000000000000000000000ffff62ca37d3208d440e8854010000000000000000000000000000000000ffff123e1c11208dab428854010000000000000000000000000000000000ffff461df51f208d0f0b8854010000000000000000000000000000000000ffff59d36752208d631f8854010000000000000000000000000000000000ffff5ef8c640208dbc1d8854010000000000000000000000000000000000ffff5f5dc582208d04bf8754010000000000000000000000000000000000ffff0e6797e3208db8028854010000000000000000000000000000000000ffff3cf0c4a1208d6a0c8854010000000000000000000000000000000000ffff58bb5c55208d4f438854010000000000000000000000000000000000ffff5d326005208df6068854010000000000000000000000000000000000ffff5d5a5850208d40eb8754010000000000000000000000000000000000ffff5751ed86208dbad18754010000000000000000000000000000000000ffff63e51608208d99108854010000000000000000000000000000000000ffff36e299cd208d9a1e88540100000000000000200100005ef579fd38a43785a5d441b7208d13258854010000000000000000000000000000000000ffffb90de279208db3778754010000000000000000000000000000000000ffff67fcc80c208d3d358854010000000000000000000000000000000000ffffd00c40fc208dfd0d8854010000000000000000000000000000000000ffff5e89c2f3208dd6428854010000000000000000000000000000000000ffff1863e14a208d89e78754010000000000000000000000000000000000ffff43a11d76208d89ce8754010000000000000000000000000000000000ffff3edb6281208d03298854010000000000000000000000000000000000ffff6d5b26dd208d16df6e54010000000000000000000000000000000000ffffd9c3d176208d7c028854010000000000000000000000000000000000ffffbcc2db1f208d67fc8754010000000000000000000000000000000000ffff59910667208d86b18754010000000000000000000000000000000000ffff6c1482b9208d790b8854010000000000000000000000000000000000ffff0263150d208d89c44554010000000000000000000000000000000000ffff72fcd6b8208d331c8854010000000000000000000000000000000000ffff4c7dcfa8208d9fe98754010000000000000000000000000000000000ffffad08fd05208d0ecd8754010000000000000000000000000000000000ffff7c0fec89208d4af98754010000000000000000000000000000000000ffff2ea6a167208d270b8854010000000000000000000000000000000000ffff59d40fca208d11cf4054010000000000000000000000000000000000ffffb52ac058208d63358254010000000000000000000000000000000000ffff6bd9a130208dbde48754010000000000000000000000000000000000ffff4f915dc7208dece78754010000000000000000000000000000000000ffff5f8423ef208dba308854010000000000000000000000000000000000ffff904cba8c208d953d8854010000000000000000000000000000000000ffff8d1421e5208d5f188854010000000000000000000000000000000000ffff4e1bbfb6208d36fc8754010000000000000000000000000000000000ffff3205cfb6208d5e458854010000000000000000000000000000000000ffff493092c2208dd0878754010000000000000000000000000000000000ffff97caacde208dd99d8754010000000000000000000000000000000000ffffc05f3c38208da7018854010000000000000000000000000000000000ffffdcf5f82d208df0f42254010000000000000000000000000000000000ffff58bf997f208d94228854010000000000000000000000000000000000ffffbc8ee72d208de3448854010000000000000000000000000000000000ffff6d780f99208d32448854010000000000000000000000000000000000ffffbc64c76e208d0b3d8854010000000000000000000000000000000000ffff6c414312208d49138854010000000000000000000000000000000000ffff407e52c6208d2d128854010000000000000000000000000000000000ffff6dc987d8208d4acf8754010000000000000000000000000000000000ffffae151a25208de9dd8754010000000000000000000000000000000000ffff522d2d10208d17df8754010000000000000000000000000000000000ffff4d294737208ddbd48754010000000000000000000000000000000000ffff443703fd208d79108854010000000000000000000000000000000000ffff4bb5a154208d1df28753010000000000000000000000000000000000ffff4c44fc51208d5d496a53010000000000000000000000000000000000ffff530924b0208d180b8854010000000000000000000000000000000000ffffc15fced3208d53128854010000000000000000000000000000000000ffff5bc5b9e4208de21f8854010000000000000000000000000000000000ffff2e05fe1f208d31ab6554010000000000000000000000000000000000ffffae865915208d09248854010000000000000000000000000000000000ffff7d71a4b0208d120b8854010000000000000000000000000000000000ffff93e40150208d25d98754010000000000000000000000000000000000ffffc19f7a86208d99e78754010000000000000000000000000000000000ffff4d675993208d2b218854010000000000000000000000000000000000ffff5144776c208d5a208854010000000000000000000000000000000000ffff2e1cce5820f69e8f8154010000000000000000000000000000000000ffff58718848208d9edd6554010000000000000000000000000000000000ffff2ea758c5208d28c98754010000000000000000000000000000000000ffffc60bd694208d181d8854010000000000000000000000000000000000ffffba6b689e208d77868754010000000000000000000000000000000000ffff182016aa208de1698054010000000000000000000000000000000000ffff5cf5daa9208d823d8854010000000000000000000000000000000000ffffbe4d61c8208d2c3b8854010000000000000000000000000000000000ffff71647df2208da2248854010000000000000000000000000000000000ffff4ff359ca208da2dd8754010000000000000000000000000000000000ffff59f1e52b208da6e60d54010000000000000000000000000000000000ffff579596231159ad218854010000000000000000000000000000000000ffff5895f42d208d5ba78754010000000000000000000000000000000000ffff458ae8b0208ddc9b8754010000000000000000000000000000000000ffff18b507a5208d20eb5754010000000000000000000000000000000000ffff5181a537208daf1d8854010000000000000000000000000000000000ffffd5f51eba208d07f58754010000000000000000000000000000000000ffff6dec54bc208d4b3b8854010000000000000000000000000000000000ffff5c3e1991208d13b54054010000000000000000000000000000000000ffffb273845a208d4f338854010000000000000000000000000000000000ffffcfac797b208d4d898754010000000000000000000000000000000000ffffadaf880d208de72f8854010000000000000000000000000000000000ffffd956ef58208dc1e98754010000000000000000000000000000000000ffff980711cf208dfd8f7154010000000000000000000000000000000000ffff50aba98f208d59fe8754010000000000000000000000000000000000ffff5f2ae582208d3ffa8754010000000000000000000000000000000000ffffbca836db208d389b0054010000000000000000000000000000000000ffffded10925208deb154354010000000000000000000000000000000000ffff717532da208dadde7654010000000000000000000000000000000000ffff93afd0e5208d40458854010000000000000000000000000000000000ffff54ee8cb0208d08fa8754010000000000000000000000000000000000ffff3658eba9208d0df58754010000000000000000000000000000000000ffff72c6876e208d5d408854010000000000000000000000000000000000ffffa7582d7c208de8338854010000000000000000000000000000000000ffffad40d7ad208d6c2a8854010000000000000000000000000000000000ffffbc1833a3208d0fc28754010000000000000000000000000000000000ffffcbceb676208d98448854010000000000000000000000000000000000ffff0121ce6e208d77b58754010000000000000000000000000000000000ffff4b40d938208d853b8854010000000000000000000000000000000000ffff5648d643208d1c9c7854010000000000000000000000000000000000ffff6c3230db208d4e028754010000000000000000000000000000000000ffffd8e33c5d208daf738754010000000000000000000000000000000000ffffad4e9d56208dca438854010000000000000000000000000000000000ffff4f8a0353208d7b408854010000000000000000000000000000000000ffff0599e92a208dc62f8854010000000000000000000000000000000000ffff46236049208dc4d58754010000000000000000000000000000000000ffff4400384d208d0ddb4c54010000000000000000000000000000000000ffff71653b5a208d53388854010000000000000000000000000000000000ffff43bddf66208d7c388854010000000000000000000000000000000000ffff47edca96208d8c068854010000000000000000000000000000000000ffffdded3c2c208d89f60554010000000000000000000000000000000000ffff4e60d8d6208da5ff1154010000000000000000000000000000000000ffff55983c68208d72f58754010000000000000000000000000000000000ffffce47f56a208d7e2d8854010000000000000000000000000000000000ffff17e25c12208da7ca8754010000000000000000000000000000000000ffff051d0baa208d19af8754010000000000000000000000000000000000ffffad42d883208dffd68754010000000000000000000000000000000000ffff5418ff88208d87bf8754010000000000000000000000000000000000ffff4c1fe0dc208deef18754010000000000000000000000000000000000ffff4c71d406208d25868754010000000000000000000000000000000000ffffda67a475208d4d348854010000000000000000000000000000000000ffff5d561269208da5918754010000000000000000000000000000000000ffff42b163b3208dbfac3754010000000000000000000000000000000000ffffad4e01eb208d5c408854010000000000000000000000000000000000ffffcc0bb91e208d4a928754010000000000000000000000000000000000ffff531dba30208db9fd8754010000000000000000000000000000000000ffffa3f72b33208dade58754010000000000000000000000000000000000ffffd8b93aeb208db75d4d54010000000000000000000000000000000000ffffae1d5595208de8d18754010000000000000000000000000000000000ffff4405665c208d88e78754010000000000000000000000000000000000ffff6b96180a208dae667354010000000000000000000000000000000000ffff531f490a208d92f98754010000000000000000000000000000000000ffffd447e857208db1428854010000000000000000000000000000000000ffff36c6b4bb208dd42c8854010000000000000000000000000000000000ffff4b4aff14208df71acd53010000000000000000000000000000000000ffff62d17930208da0b88754010000000000000000000000000000000000ffff974bf913208d5faa7953010000000000000000000000000000000000ffff7419d269208d6c1e8854010000000000000000000000000000000000ffff48a72343208d9c2b8854010000000000000000000000000000000000ffff64431e87208de8908754010000000000000000000000000000000000ffff44660d45208d410b7d54010000000000000000000000000000000000ffff43a3340d208d5b298854010000000000000000000000000000000000ffff9ffda678208de7238854010000000000000000000000000000000000ffff189a3759208da5745354010000000000000000000000000000000000ffff57026e31208df23c885401000000000000002a010488006710000523fbe100000001208dddf48754010000000000000000000000000000000000ffffc1531c5b208d61888754010000000000000000000000000000000000ffff42cd8bc1208df92e8854010000000000000000000000000000000000ffff36e1e970208d0cf75754010000000000000000000000000000000000ffffad4e2420208d0f028854010000000000000000000000000000000000ffff6ec61b02208de6228854010000000000000000000000000000000000ffffdcaa807c208d1cac8754010000000000000000000000000000000000ffff6179416c208db6068854010000000000000000000000000000000000ffffd46121e4208d7ea58754010000000000000000000000000000000000ffff80a41679208d91216254010000000000000000000000000000000000ffff69ece091208d0bf78754010000000000000000000000000000000000ffff45320c0b208dd03c8854010000000000000000000000000000000000ffff4859a2a5208d8e308854010000000000000000000000000000000000ffff2e1ce15a208d30f36354010000000000000000000000000000000000ffff4b61ee15208d7aa05553010000000000000000000000000000000000ffffb129186b208dbb238854010000000000000000000000000000000000ffff581a7f6f208d41248754010000000000000000000000000000000000ffffb009055b208dee218854010000000000000000000000000000000000ffff2e7771f9208db4e78754010000000000000000000000000000000000ffff411bf6ee208db5178854010000000000000000000000000000000000ffffacff001b208dbcf487540100000000000000200100009d386ab83061cfabfe3513a3208d76048854010000000000000000000000000000000000ffff2e35dbc2208d83d28754010000000000000000000000000000000000ffffd91bb1d5208d5c3d8854010000000000000000000000000000000000ffff32a47969208df4bd875401000000000000002a0104f8012032250000000000000002208d1bfa8754010000000000000000000000000000000000ffff53a2f4b6208d141c4554010000000000000000000000000000000000ffffb27b8281208d580c7e54010000000000000000000000000000000000ffffc9ea1340208df7228854010000000000000000000000000000000000ffffd90a268d208d41e18754010000000000000000000000000000000000ffff555b8809208d66358854010000000000000000000000000000000000ffff5280ff23208deecd8754010000000000000000000000000000000000ffff84c6a102208df9bc8754010000000000000000000000000000000000ffffbbc96dae208d9d0a8854010000000000000000000000000000000000ffffb2a2d18a208da9458854010000000000000000000000000000000000ffffca16c30e208d2ca88754010000000000000000000000000000000000ffff6baab63e208d923b8854010000000000000000000000000000000000ffffd5b87bee208d12258854010000000000000000000000000000000000ffff59ee408b208d980e8854010000000000000000000000000000000000ffff2edfb0c4208d33378854010000000000000000000000000000000000ffff6caa7b42208d41176d53010000000000000000000000000000000000ffff1fa2df68208dd1c57f54010000000000000000000000000000000000ffffbeab679a208d17c78754010000000000000000000000000000000000ffff3f983f51208dbb278854010000000000000000000000000000000000ffff568dbc28208d5d5ad953010000000000000000000000000000000000ffff0ec112b7208da1b78754010000000000000000000000000000000000ffffbc287698208d99348854010000000000000000000000000000000000ffff5ff1a302208d23ef8454010000000000000000000000000000000000ffff7ba55d22208d90e98754010000000000000000000000000000000000ffff5ae14503208d58f48754010000000000000000000000000000000000ffffd45a3cae208d530c8854010000000000000000000000000000000000ffff18fb80c4208dc6138854010000000000000000000000000000000000ffff4e2fd6eb208d97208854010000000000000000000000000000000000ffff55ddd5b8208d74f88654010000000000000000000000000000000000ffff1809ad86208dc0278854010000000000000000000000000000000000ffff6ef2dfb6208d84338854010000000000000000000000000000000000ffff58d8114e208dcae78754010000000000000000000000000000000000ffffc21c474c208d61e78754010000000000000000000000000000000000ffffd5b3fc7a208d7a078854010000000000000000000000000000000000ffff32740193208de7547b54010000000000000000000000000000000000ffff8046aac3208db5328854010000000000000000000000000000000000ffff5b0c5366208d5f208854010000000000000000000000000000000000ffff6caa054a208d1d682854010000000000000000000000000000000000ffff69e3eb9d208d179e6b54010000000000000000000000000000000000ffff54aac628208ded2e8854010000000000000000000000000000000000ffff4166d226208d8e148854010000000000000000000000000000000000ffff54305af6208d1ed88754010000000000000000000000000000000000ffff3ed2b2a4208d78ec8554010000000000000000000000000000000000ffff6dd2e575208ddd3e7854010000000000000000000000000000000000ffff1b83a102208d421e8854010000000000000000000000000000000000ffff2eb51bf1208dc01f8854010000000000000000000000000000000000ffff58c66033208d08ce8754010000000000000000000000000000000000ffff5d57b84c208df22e8854010000000000000000000000000000000000ffffbf213e3d208d89cb8754010000000000000000000000000000000000ffff53d7ece1208da9d68754010000000000000000000000000000000000ffff71bef412208d38268854010000000000000000000000000000000000ffff84fc8a6f208d4f107254010000000000000000000000000000000000ffff57a072d9208d990f88540100000000000000200100009d3890d71029db21863020f9208df3c36854010000000000000000000000000000000000ffffbba6a6c4208d3ea71f54010000000000000000000000000000000000ffff3e61237b208d60a18754010000000000000000000000000000000000ffff53a56b4b208db2328854010000000000000000000000000000000000ffff44eef265208d2c0f8854010000000000000000000000000000000000ffff566553c0208dec268854010000000000000000000000000000000000ffff57a69ecd208d93148854010000000000000000000000000000000000ffff9b8f4433208d741b8854010000000000000000000000000000000000ffffae17c74c208ddab94f54010000000000000000000000000000000000ffffab19c63a208d5e886f54010000000000000000000000000000000000ffffbc67a037208d5c3ccc53010000000000000000000000000000000000ffff0e77c86e208d26fb8754010000000000000000000000000000000000ffffc1eae172208d61278854010000000000000000000000000000000000ffff5ce15439208d60df8754010000000000000000000000000000000000ffff2599fb3b208d52418854010000000000000000000000000000000000ffff028710e6208dae448854010000000000000000000000000000000000ffff18152eee208d6ff18754010000000000000000000000000000000000ffff542e396d208d8c248854010000000000000000000000000000000000ffff5519d6d8208d01e33e54010000000000000000000000000000000000ffff5027d563208d05cd8754010000000000000000000000000000000000ffff45a5a9c5208d7c1e8854010000000000000000000000000000000000ffff68c813c8208d38a48754010000000000000000000000000000000000ffff3a60a934208d73ec8754010000000000000000000000000000000000ffffae3db8f3208d096c8754010000000000000000000000000000000000ffff4cb88802208d55d38754010000000000000000000000000000000000ffffd31f082e208d0d8a8754010000000000000000000000000000000000ffff6c22c9bb208d81ba7754010000000000000000000000000000000000ffffca5f88da208d5df88754010000000000000000000000000000000000ffffc31abc05208db1d58754010000000000000000000000000000000000ffff5cf614c8208d2b257c54010000000000000000000000000000000000ffff45f9baa0208d38d78754010000000000000000000000000000000000ffff490e0608208d27e88754010000000000000000000000000000000000ffff17f1ccd0208d501c8854010000000000000000000000000000000000ffff5be834a7208d860b8854010000000000000000000000000000000000ffff640173bc208deada8754010000000000000000000000000000000000ffffdc98f8db208d983d3c54010000000000000000000000000000000000ffff021ab398208d99f28754010000000000000000000000000000000000ffff59a9e417208d19468854010000000000000000000000000000000000ffff91ff01a1208ddbf68754010000000000000000000000000000000000ffff50048949208d89118854010000000000000000000000000000000000ffffc8366923208dea3b8854010000000000000000000000000000000000ffffbcbbb5a9208d364a8854010000000000000000000000000000000000ffffc654bd58208dd52ef953010000000000000000000000000000000000ffff7cabb7bc208db086cc53010000000000000000000000000000000000ffff6c4004ea208d0ff48754010000000000000000000000000000000000ffff0536a3cb208d1c0b8854010000000000000000000000000000000000ffffd90bfef3208dbf188854010000000000000000000000000000000000ffff0252431f208dd1c78754010000000000000000000000000000000000ffff25732b19208dd6fa3954010000000000000000000000000000000000ffff6c1131dc208da6108854010000000000000000000000000000000000ffff1fb9b748208daf368854010000000000000000000000000000000000ffff1f2a2981208d9e2f8854010000000000000000000000000000000000ffffbc658482208d6a458854010000000000000000000000000000000000ffff568b843a208dcd428854010000000000000000000000000000000000ffff5d820794208d38258854010000000000000000000000000000000000ffff6440c630208d1cda8754010000000000000000000000000000000000ffff6e9f7be5208ddd388854010000000000000000000000000000000000ffff9f080244208d2e2d8854010000000000000000000000000000000000ffffbb70cf3d208d3e0c8854010000000000000000000000000000000000ffff3ec27a16208d89098854010000000000000000000000000000000000ffffb55fb2fb208d13468854010000000000000000000000000000000000ffff180ab1e5208dfc2a8854010000000000000000000000000000000000ffffd58a5c0e208db9188854010000000000000000000000000000000000ffff81ce808d208d03d06b54010000000000000000000000000000000000ffff4b496858208d319e8454010000000000000000000000000000000000ffff531fac9f208dc8d98754010000000000000000000000000000000000ffffc113e4ea208d06395754010000000000000000000000000000000000ffff7cabf0d6208d722b8854010000000000000000000000000000000000ffff545db4cd208dd91f8854010000000000000000000000000000000000ffff5f12a597208dfbd57054010000000000000000000000000000000000ffff3cf654e6208dd7358854010000000000000000000000000000000000ffffb4998e79208dd7018854010000000000000000000000000000000000ffffb45cc220208d86df5554010000000000000000000000000000000000ffff7660d462208decd28754010000000000000000000000000000000000ffff5b7bdfe2208dafe28754010000000000000000000000000000000000ffff2e76899e208d0c318854010000000000000000000000000000000000ffffc7bcb183208d35258854010000000000000000000000000000000000ffff3ab2d076208db5c38754010000000000000000000000000000000000ffff48b24d30208ddfd18754010000000000000000000000000000000000ffff904cb00c208d40478854010000000000000000000000000000000000ffff02a3b787208deb318854010000000000000000000000000000000000ffffd2564026208de63f8854010000000000000000000000000000000000ffff615d16c0208dbf1d8854010000000000000000000000000000000000ffffc6fff6f0208d4aee8754010000000000000000000000000000000000ffff56c74bc8208d68bc8754010000000000000000000000000000000000ffff31b581f9208ddcca8754010000000000000000000000000000000000ffff0e97397a208db2318854010000000000000000000000000000000000ffffc766348d208de1bd8754010000000000000000000000000000000000ffffd5927a3d208d2aff8754010000000000000000000000000000000000ffff5152d0a5208da7feb753010000000000000000000000000000000000ffff53fe1694208de7418854010000000000000000000000000000000000ffff189f3d99208de7b38754010000000000000000000000000000000000ffff64004655208dacabb653010000000000000000000000000000000000ffff55413231208d43b88754010000000000000000000000000000000000ffff796e1029208d03788754010000000000000000000000000000000000ffff49b90ff6208d04be8754010000000000000000000000000000000000ffffc327ce1c208d33648254010000000000000000000000000000000000ffff2e896478208d9e308854010000000000000000000000000000000000ffffd395d9bf208df13e8854010000000000000000000000000000000000ffff51bb88ed208dec8c5653010000000000000000000000000000000000ffff557f146e208df4538854010000000000000000000000000000000000ffff48d05928208d41bd8154010000000000000000000000000000000000ffff47384162208df6428854010000000000000000000000000000000000ffffc1566314208d753e8854010000000000000000000000000000000000ffff440c9b4f208d2f078854010000000000000000000000000000000000ffff56021a19208d7b0f4754010000000000000000000000000000000000ffff71653a84208dcee88754010000000000000000000000000000000000ffff5401a332208df4db8754010000000000000000000000000000000000ffff5a9db0e3208df8e38754010000000000000000000000000000000000ffff5d5208fa208d2e298854010000000000000000000000000000000000ffff4c61c0bc208d42ea8754010000000000000000000000000000000000ffff4c405ae7208deade8754010000000000000000000000000000000000ffffc009c823208d99358854010000000000000000000000000000000000ffff6cc2b493208df3fd8754010000000000000000000000000000000000ffff904c6007208d1fde8754010000000000000000000000000000000000ffff92732ae8208d666e8354010000000000000000000000000000000000ffff6daa9d16208d18f78754010000000000000000000000000000000000ffff4d6d8d8a208ddea9ce53010000000000000000000000000000000000ffff17e26f7d208d1ae48754010000000000000000000000000000000000ffffc32ebb89208dee258854010000000000000000000000000000000000ffff4a0f10e1208d290a8854010000000000000000000000000000000000ffff42731593208d7cb33354010000000000000000000000000000000000ffff532e8b97208dfd1e8854010000000000000000000000000000000000ffff6ee91680208d480a1354010000000000000000000000000000000000ffff60f220f6208d55838754010000000000000000000000000000000000ffff1f36d48f208d247e8754010000000000000000000000000000000000ffff44b5a49a208d0efa4954010000000000000000000000000000000000ffff63890597208d862e8854010000000000000000000000000000000000ffffc14d8751208d443c8854010000000000000000000000000000000000ffff70d16dba208d72fe8754010000000000000000000000000000000000ffff55983dd3208d236b6854010000000000000000000000000000000000ffff7aea27f9208d67e08754010000000000000000000000000000000000ffff1b20750d208df10a7154010000000000000000000000000000000000ffff7b9fe56e208db32d8854010000000000000000000000000000000000ffff7ba42d54208db3f58754010000000000000000000000000000000000ffff027b4eba208d70d68754010000000000000000000000000000000000ffffbc1b62b9208d97951454010000000000000000000000000000000000ffffb4b6af10208d251288540100000000000000200100009d386ab800773149c4d7e673208d7b168854010000000000000000000000000000000000ffff58969392208d91d18754010000000000000000000000000000000000ffff9ffd6d4e208d19008854010000000000000000000000000000000000ffff4e926cb8208dd1c98754010000000000000000000000000000000000ffff546cdb77208d6a0a8854010000000000000000000000000000000000ffff6caa8c15208d3a3a8854010000000000000000000000000000000000ffff62e28d6a208d3fe48754010000000000000000000000000000000000ffffd9850be7208dcb438854010000000000000000000000000000000000ffff6c2444b3208d78338854010000000000000000000000000000000000ffffb2df574a208d3b2d3054010000000000000000000000000000000000ffff4b529e67208d5b988754010000000000000000000000000000000000ffff36e152e6208d01ab8754010000000000000000000000000000000000ffff58b531bf208d7b038854010000000000000000000000000000000000ffff54717a6f208db0388854010000000000000000000000000000000000ffff4a694e98208d65418854010000000000000000000000000000000000ffffcef84b25208dcf218854010000000000000000000000000000000000ffff5364fed9208db6c68754010000000000000000000000000000000000ffff4d03decd208dc6158854010000000000000000000000000000000000ffffb9044c79208d65418854010000000000000000000000000000000000ffff56b2192f208d23308754010000000000000000000000000000000000ffffd973f7a4208d05158854010000000000000000000000000000000000ffff1b6d9975208d52cd8754010000000000000000000000000000000000ffffdf5b92ad208d3a128854010000000000000000000000000000000000ffff6d93787e208d351e8854010000000000000000000000000000000000ffffb6a4336f208d9bd58754010000000000000000000000000000000000ffff3ba7f521208dc3288854010000000000000000000000000000000000ffff6bbf20cc208d68998754010000000000000000000000000000000000ffff63b30032208d86848754010000000000000000000000000000000000ffffb52ef15c208dff108854010000000000000000000000000000000000ffff4d06718b208d226f0754010000000000000000000000000000000000ffff44248ba1208da3af7354010000000000000000000000000000000000ffff86f9d72c208d9fd68754010000000000000000000000000000000000ffff521f2444208dae428854010000000000000000000000000000000000ffffd445b025208dfafc8754010000000000000000000000000000000000ffffd447fa84208dbb358854010000000000000000000000000000000000ffff3ee2d196208da01a8854010000000000000000000000000000000000ffff45a615b2208d32edb053010000000000000000000000000000000000ffff6d6494da208d77ed8754010000000000000000000000000000000000ffff555dcc44208d09608754010000000000000000000000000000000000ffff45a5d222208d84558754010000000000000000000000000000000000ffffb01c30ab208d71fe8754010000000000000000000000000000000000ffffae5f68c2208de0acdf53010000000000000000000000000000000000ffffae737936208d29e18754010000000000000000000000000000000000ffffb00a63cb208d220b8854010000000000000000000000000000000000ffff9ec4d12f208db4408854010000000000000000000000000000000000ffff4c6d9bcc208d65a58754010000000000000000000000000000000000ffff5962c453208d2d628854010000000000000000000000000000000000ffff257191d50000" }, - "NOTFOUND": { - "message": "f9beb4d96e6f74666f756e6400000000250000001d33d53201010000003a4af715be220eae7b2657582869daddf79ac4afb4a0e1cafa5b57e1afb8dfe2", - "payload": "01010000003a4af715be220eae7b2657582869daddf79ac4afb4a0e1cafa5b57e1afb8dfe2" + "ping": { + "message": "f9beb4d970696e67000000000000000008000000c6466f1e6b86480ae969867c" }, - "GETBLOCKS": { - "message": "", - "payload": "" + "pong": { + "message": "f9beb4d9706f6e67000000000000000008000000c6466f1e6b86480ae969867c" }, - "GETDATA": { - "message": "f9beb4d967657464617461000000000025000000253fc50c01020000003c2a5e4dcb5a44daf5ebff14a89d26a99b8aa1c1d757694b03d7e6ca7eeda6ca", - "payload": "01020000003c2a5e4dcb5a44daf5ebff14a89d26a99b8aa1c1d757694b03d7e6ca7eeda6ca" + "block": { + "message": "f9beb4d9626c6f636b000000000000004792020036d2881a02000000e74122c23a90d7bb207fad2cfd07fbdc33de36352b5561120000000000000000d6097b7aded2327c8ca979ff85367f664879a7a7f42e1914ab880e63276b8dd1b12eff54c02e171831fb8d1ea201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5b03984b05e4b883e5bda9e7a59ee4bb99e9b1bcfabe6d6d5cb348c1c7d580627835202f5ad93c2f3db10bb850a1a513979f8328d9f35aff1000000000000000006189dd01cf00004d696e6564206279207975313333353131373131ffffffff0111622195000000001976a914c825a1ecf2a6830c4401620c3a16f1995057c2ab88ac000000000100000001a9f062bdd6bf76f3059169ce8f30905e23b08b25e30570b2baca08ff2878b0e3000000008a473044022037da4b94dfbfe08e425b4b72476047bb45850fe9af109926dabebb8c7f0b93da022014de78058989b54d1a7ae19644c83ca4e26efd4000a4fe8e45659209468cd4cf014104861822906143d90a413c28c5aec29985ac36783d2c157fd0f87c55a5663aef51e74b0181740f3247aa1e7ab78b07379e1b1a94384c9442cfa5eeca8898769537ffffffff02e8030000000000001976a914b28ae06f3050160e775c806a42e7d1d127ab5dd388acb8820100000000001976a914017c96cc0f3a81c2604d9820dbb1b490127d2bca88ac000000000100000001b36da34608b43f5af519bff8f4ed4a30969963de3a7b7cd27ef897d14a0732f5000000006a47304402206ab21b1f1aa900eedfc4dbba741f972644647eb19627d1a664d1756d65e029820220070caebd37d1393e4e4af176f64d718bd860d6af08355a42cedeba6f509c240501210386b0396cf4aece2bf608adfd0e8d95414f6bdcd61b217241ec980d2f5f76eee6ffffffff01e882d000000000001976a914539c3e80f41382631d2d03c4778ed0fdf81b7d2588ac00000000010000000133f61c96b620149bdd45808dc8f2795bd8d9874a1d91fae44dad627445adc09a010000006a473044022035f04cdb8048e0a4995ae9bc8e59373f99b2751d5a920f7847d572090ea1265d022052e5eac0fd3e556171b4fe4bc15e4d5fd2a5f801d1707ce84dd9d74b1975abb0012102b9913536286170a0f6adcc2988cc2552bf1f97ec30d75d1ca6bd20b6b5394e62ffffffff0118fd6700000000001976a914539c3e80f41382631d2d03c4778ed0fdf81b7d2588ac00000000010000000151148eeeb3612d11bdd073f98f964c9431bd5ca67aa0ede8004e98f6d627f5dd000000006a473044022016838fbd4c9684407e771d2a99fc2a230ab655f315f1b5086949f286f71bff3102204ab02bf9e188619bc9b79bc9b01788f98900373a639b0888463fae7b3a4c66b20121029ac24d1abfa92582156ab553cc1562d56f48bcebadeadf1904fdff2dab74da62ffffffff0110ca4d00000000001976a914539c3e80f41382631d2d03c4778ed0fdf81b7d2588ac0000000001000000019c05873dc500ba9ae3616d92282c6a8723a8703be8c85fb880a4330b2995a56e000000006a473044022038ae1c068b089a73df1d369de616ce3bac0110fdb824523922934a6fb0e3f01102201fdb5edd8d77f2e97c4f898ac022be1e0c41d90550aa410d12471af305e520000121030c07ddac3638f0f9309d9e344fc9653aaf6430b4b38d703570763acb8246132fffffffff01d0aa4d00000000001976a914539c3e80f41382631d2d03c4778ed0fdf81b7d2588ac000000000100000001649072082c6150572663065f26554692502cbb8006f6ecac39e8cb421f5de070000000006a473044022004428233b5b46e36e244669d1370663c594d487b525cefb8d693e38286ba861202200f54176b57e8ec2a8e6ae4df41cf0cdc556fd7d6a58adef23e32bf6e744e1cf5012102820ebfafe39f5f4bf0b7e25ae9915cb7de8ceff88c0d01cac19bdb50a4b1e051ffffffff02c61b2107000000001976a914c225ab6924b62a12e85aa53f366eaee38609ed6d88ac3a986013000000001976a914c0fbecd8b2f85c31c5ac86630de53c7ae7ce464c88ac0000000001000000015a5c351916611a4b995dc75c482dd8afc6a813a5ce678cc7c5fb9e95b6e69bd3000000008b48304502201fe6c1b80f37c5e452c2ca4d1bee0fbc87e8fc17a13967d1cc9f685a29cc3e77022100f1a1fdbf297f50c46a2fe179170233181075260134fe3a8d8cf14490d72d10e9014104ec8286c3e7d962cd687cb9f3860c3e73866d4f79e57fe4df692680b6488c37b7009c4a963a5649323721ae3f20d6f393d7865f85b829a27f0b541bbdd7f15838ffffffff024e38c404000000001976a91484e16c064a30f69c78f9b3b42002fd89d8c900f988ac808d5b00000000001976a914a6e86762140b6d7f661b251cb7c3cf1afb2d870588ac000000000100000002507c35d72b8ebb55a43b5cf450f17f5f13cd3da4b263609cfc6e4508132a4ed1000000006a4730440220772619770d94488234ea4f47430f05cd5c78697a2e7791b12c6819602ad9004902204b2bc4aa967d0709a4c99d5f36f2317c44794489084cb593726c4062ad911cc3012102094d1fc9488b1ce68d021c6623b6d12ce2dfc0d6f527e6f2096765c245158624feffffffa216eab2a659f25ca6bc7bee9cc4a5e54772118f0a50d98f0656edfecdd849ad000000006a473044022009bb9bec4494389220e3188b37551b1dcabd035a7114a309c2ea62d1b7228d95022034beac693b005a9b5312b5c48c41f31da0665e1359be81dce8930c4c47de67f7012103fd5c201fed02e586282d48d353db05b58140e7a94e02d886204c35076eb70bf0feffffff029fbe0b00000000001976a91412ccec1db43e3a4cfde88e25129302f2a2246a4e88ac309b2400000000001976a9141e1800460fbaa6eda2ff7b52833555255af3926388ac8c4b050001000000010cc8087f431d229fa6daccad29eaa779c17312325e2cf651fa849b763fd85953010000006a473044022069b16749d22001b0a21744683d2d75ce9ad9f2492824912ab52b8830ea825dfc0220450abe230ceebfd021d262508ad447b249866e248c80d4e5c7827308671426c6012103fbef068b7a0dfdbda3f53acb71cbe12c39f76550177b975d2038c2e32dc430bbffffffff0211d58700000000001976a9148341537bc729cd32e1b6304ad7800357052c848988ac002d3101000000001976a914469dad04041ecfdd7e98079cda2dfde6015db7fe88ac000000000100000001eb4f434c6dfb2ea5e453afcc3b19c7834283196056e5e55222196a24898ec396020000006a47304402201a89a95ca205d07c70b327cd13d5cd17e02e79b0f1de9bc4e69bb50562cbab3702202970e1e064e68a9f2223c4a61e8065f4df299e87c62ca99598b1e375141ffe6401210358f1c439eb89915cb7f33e1c87373d6664684c0cb5095ea60ed585b78230a8f5ffffffff01d873070c000000001976a9149586513770ac55d28829279275f2932062d5816a88ac000000000100000001d0203c594d3a05ec4e01a649927a8ddd3ec45052bfb78e62e3da4bafb801160d000000006a47304402204255fb29cf9c7c8f451d714d9a076af5fe3f073a261194eb9f49ac5fc3f0ece802203d6e6d9f858da72b77edd739461702eee90facb55b4e682e009c9b40511391d50121027c386ac59a9a1dfbe52f11151388664d6ac544a9957ffc3c389b63a05404f479ffffffff01708a4f01000000001976a9149586513770ac55d28829279275f2932062d5816a88ac000000000100000001f0c4498ee70b44e5ef0fc7c9ae9d43d5dcc31f7498e161968eafe9935e2a57c5000000006a47304402205af8a8a26addfd6b631c8c4af22eb5f45f5c42bd2e2a060041c860f634e240fd02201510e5417cf1f9cc1ecb55980f13538a16a53952ff4ae8e1265557e68f4e8f95012102a7ac4f4831ea24367dcc21e2d586e56d06c78ce6859325686de71e479622dd62ffffffff0176200000000000001976a9144827ff0289efaf9374ce8db5d40a1e7e56fc36a188ac000000000100000001d77155da4a5b0435ea9369150cf44ec7556d3a716c79a4159c509cf1ac947ffc000000006b483045022100db0fe1cbeb3dd70f97633019424ab83046a01e4fc5c3d241d03226092fff7f58022050a7ec815892734a5ed85791433f79a1c436ab19d3dcb9706a378b2dacf86078012103f9ce7b512cbe79da51ea4bb25f24dc435ac87a55ea8cb5fa99411177255cc89fffffffff0110577500000000001976a9145cbb4918a6e7ac4c2de529afd3f0ead258fe129088ac0000000001000000015d011bd73d1c66ef56eff8bc3ca0ab99c500512119dd07028bfabf400dd22b22000000006b483045022100c9c30e7dac291752b08514d59ae9e034f8697ca1457cf55dba90d6d33e21f05b02207f802094dd1d6e427f45be7f8ce4ac4687eebd754a4a9ad148c2506ff5623d3e0121027544301ab1cdea80f629d7bd7a61eeff0f7c3eaed35679b4422b5e017189790fffffffff01f9744400000000001976a9148c692ee5b907848285c29f3dfeaa6ff9c79f156388ac000000000100000001602d404fe5970e4da1a5454baa50808a53df3d97bcacaf1ec798d5db3312e57c000000006b483045022100a9fc8ed2b99ae012b76d5cabcba58ba2c901c153f9572a6e359b18b4b0577fe602201b2f701e30400962b9441bcec0bd7e78baaf04e80480d874f6a156789a044195012102cba6951d129436ad8256685668bca8ce044a2e81fdceadeabfd39ac6b362ee00ffffffff018cfcd000000000001976a9149586513770ac55d28829279275f2932062d5816a88ac0000000001000000017cd5b2e220f41041f3b707945caf720be28057a269c6d4cc9945d984c34aa4f9010000008a47304402204a567d5bfda4edd2bf7a6e239307eb8b2ea84c3caea8542278409d09f6fe762e0220655fdbd14dfdfed1371d362a510d84e9afdd76083cac8087f83ccc084b451dbd01410486b1655a520d98dce4e50fd573a5579f69bf3fd73cbb3911d296ce47b0d27082bd9217421c00c4c52bdc227498a4ff061370be924913192efbc5b28a245119edffffffff0170c9fa02000000001976a9149090c8dae016b9fd3a9fdad72ba4df54b1834a3f88ac000000000100000001716ff994b731a312d2c4eec8915b9552465d4b21dd47f685a99e8e814315bde1010000006a473044022071d095a786bf87ba8ce3fb07dc75f9fd77ad74dbb296c12b10b3efbffad0c561022032c2e1ecec570e9a031a31a0ce41c099152ab179a543379b85c5ad10aee4a1770121025e06cd8d479b3808dcaf919b1289ab837316ce962732ef272b4fdac5777fee56ffffffff02a0f703000000000017a91461981717dc4d24394220e11fd09d42bf2ab5bcb5874f600000000000001976a914d4696a9c84387c12240ad5485fb9b4d6b7336b6288ac000000000100000001ee5a78d068854fcbfaa23774b9ce121f83bf809862266e97600883c3f108ecfc000000008a47304402201a01877972a71d7cf48e6213d5e79544aeb4c7b2852463f82b21a2c6264c1bea02205d622c4caf8f461dc92f2d1e9cba92d5b522a7e0029f22225466611ccd8817d3014104da6bc6a6139bb008454bfc8371141a5fb8ba6de87e9ab1578ab4c31e1b25513d6d1b1b0e66b0e39a29f6baf19f9f0faaf51d22bac02b1c07eb08058498763784ffffffff0120f40e00000000001976a914ca7eaccd4ef037929be65c7706992b055a41362e88ac00000000010000000121c6ce4a254c4d3138f1a7b61986720bd14d2903e7fbcc6aec3ffc88f8bb62dd000000008a473044022070ea08ad064f8b8709ada1680a6d153f1d52f3d1033c4c34fb45e6fb15ab552a0220070eca82b87d2bc4a44c1f6495456853b29dabdc092cbd18a9b498d6108c4f290141044a3460b7e035b73998236aca3312d8a694f7b1fceeac872e4ce65d012a6f7d8698fde52a204260e8ea1eb210597409b8ef2ad8ece83daafc40377071ffde1c6bffffffff0120bf0200000000001976a9142178ad5ea2623ae333116185b1540b29f09808f088ac0000000001000000016c2708faa270ae79b3c5c7e754d40609fd31cf4af60fbd56299c9cad52a5fc19010000006a47304402204faf65d5779f2c109ab8a9481c3f655846d2ce2e6af8c72334fc853daabb5bc602205aaf7bb1e112ae29fe9f01f8e4efb410612359c1cdb96511d8acf1ad3b32300c01210381ec871716e98a71777fecb4e18f05c56c3e44a079234f0c1cce75fc68e0d00dffffffff0225c91620000000001976a91460ff4529c00d5730ed8c63f6c2e0894e7b86e58488ac00497f0f000000001976a9143f2a486ea9689c05455033bb588917767c8ab10d88ac000000000100000001649072082c6150572663065f26554692502cbb8006f6ecac39e8cb421f5de070010000006a4730440220085b4b898bf767e25972b24cc21c4b43aec7f6dd501c8a4865be496ffd6a4edd02205da61f3c71fae81530f51d055a44e45186cf20c38128b92f34375d4ca0a1743e012102d5db1bacfc9a4c375a9223db95e33ae1f591af8dbdc600865e5053f225da24b9ffffffff0204bb5809000000001976a91495951cc4260ac3a378cda2e3395f811f88c4c7dc88acfe226800000000001976a9144a86401e50d73d8aa53a073b7c0c06157bda1ce488ac000000000100000001d33571f1fd3b59be9ad3d3f715dccbae79bb2e59b9cc933bf408855495f30cb9000000006a4730440220754a9a82cb29213bd687eb304c122ba75587fe91877ecd749ddc11c1582ce0cd02202fd6dbae3242b7095e82a105d0abcb63d28a4e3473af2bf4a5f2eecd58e0c5f9012103a3191ea65afb1b5974e502a53981381b5c5f6d34035c66d67cd2072f26d0a62effffffff02e082ce05000000001976a914ab5309ce0f75a383a80ec11e65750b3d1ac24a2a88ac204e0000000000001976a914c8b0325d344252d7df7a61ea11b9c1570a31680288ac0000000001000000016ac3a07914ec216826fd443655dec92110b86da10426ca8a5b447ba74ebd3841000000006a473044022072ac351fd4a03bb6a0e965bcfc3472d64a2e3afb0a5a65d41e59fe6faf7202b6022003cbcc236ed4178c3057b6b3e3c0f1bef577503631c20ccad7cdf9cbbe0cb047012103d24e158108ce0fa7322e1f95515e201b0d4f78c78b21652b3a276c109884eae2ffffffff024054fa02000000001976a914e6b42818894cf54e996aab26e94d7328f179357488ac005a6202000000001976a914b6dca96067a94ccded28ef8ddc9e6338aab7979188ac0000000001000000012bcbfdbb246f6795352e15ef30ffc30a823266d410b13940de1c6bebe2e950ec010000006a47304402200bf6a4e0d050186cad9ec0520faf230ae95069a23f1f1d7a94160e2671da26e102206e519151d5c82f41794df47ba57e2a747ad3b9351666c7b36d168134400e9de3012102af03be02630ba0a38a2f9d38832fedb4e8b5bf979422f234085fa44a38dfeb11ffffffff0210270000000000001976a9142d5e62586a3d244c5e3c4ecc32eaf7630639a1fe88ac8dd90f00000000001976a914420da0b7a488010afd9cc41cf96ff14a05ce970188ac0000000001000000016e726655a6ceef71c0629ee997aea74fbf3ae906699e069d6c9b93bd62d6f666010000006a4730440220527961b5de466f8eb082579be880d422866ce91255f3a2101f3f7bce6f4cf56002204e3e4909c9dd1b4ff736070d64590902ae3044118defe23a22f9e30c813c8d01012103f4aa7eaf4813e36dce5b07feb06be26c40d8650d6b87ff71e0748be9899ab50effffffff0210270000000000001976a91449254ac0c3f62eba4312a85dec152d34be8b95ba88ac60900f00000000001976a91497cc736c7499b6dc1016f728073d04783fad514988ac000000000100000001273aca4518c4bf276bd9ea75a9e2c1dedc724599104ecbde61f85c4c23a53e6e000000006a47304402203d9d92a705c310dc331da23c9c33133b238a326e0d137bbafa3fa573395e5595022050c5076a0ef48e2af4ae38d25e715dda448dfbce281b7571ceb85f643f1bdd7e012102f548a1debbe3727f8d01a8df06d03e2748c158570318c3e256cdc675db0fdd59ffffffff02020c8300000000001976a9146d860b78f982c94e590d4986f0ece2121202d94088ac4a0e1200000000001976a9140e51e95ce63f343d4268bd071a03c567ff80bb3588ac0000000001000000012db825d3ca7b56f1c9c60ccd4e4760222a3fb3c41a3271ea228350d27b4ded04010000006a473044022028d12c789842ae14d29cfbc6739c166414f9c508e45f1c1542925e72d8512de102204c8fd0be2c28f1acabfb34eaba2cd4930cd3d27478264c740f78145d43e3b2a90121022c3e626b9df73142c40993d79b995ea820b5c5237859f0e4c405a5a7d8c37e58ffffffff02b0633700000000001976a914e098b0eab9e76331604b9a9dc7446c62f20b6f1a88ac4015d200000000001976a914ccf3eb1c37695a7c5eedb040325b535bb4430b0588ac0000000001000000019c975c4bfb36f1faa640ac2b179aca4cfbf745dece1dfbd5acc26820fcb634c2140000006a47304402206ec42f99865e495ac51e1383876f4bda098f1406e7743378fb214612c40b09a102202d6ce2dcb64d57bb391590ca626ba19fdbc45396591f4b32e6271e1d3327a8cd0121022a023d7d15923fd2d3351323fe743dff984a87507dab7694adc75a80e22b6fc0ffffffff0222689001000000001976a914757662cfa0463ea90634d203c71ce57c5e092e9c88ac9d73c900000000001976a914e8d84dfd711bb9ec2b74e8bc2bfc95506f482aec88ac0000000001000000012812f85fdc7b6abbfe8a0b88a9a896273b986ac81b4323f8367676f4bb745ec2010000006a473044022045dc6d8e30bac58d090383a6f64c4856a03f09520c019ee3e8d8a47096293f3d02203a772ae961d0dce63c1ce6191235ad6306ba75d9a51146f0b6ff68bcfe70e90601210367c1a91e6ba1125d451539058ba452cd02a97b45381d3b3a70726cb46938491fffffffff0260900f00000000001976a914ca2eeeee7e725d58a2dc104ad5fed4cda700b46088ac66990301000000001976a9142bc3de6fe26eaf73f7e69ec00c6a295a6cf5c39488ac00000000010000000151b46528b920fc823690945fd7ea4629fb960bf408383bed8550cc1542e2b96f010000006a47304402202643a944d6d552e44957b4c94e6cf519c922613fe05369f11cc7e7bc2b7d2eea02205fbd2d5c3551304e91b5a8ea58097e104f99293f1f709adc4fa65492778a1a9e01210330b757708b1578b69440e07c9a13160e90e838fe05cd1e8c88eb6b70da950501ffffffff02d74e0100000000001976a91493eb43b95cc78327c0b962273bc12934b81be4e288ac3fe10300000000001976a9149093dba908264eec7eeabf40969842e3a4f31de088ac0000000001000000011cd13e76ea53b8862eed3b6c5b57e90c1ea82491f49f58aef2935113b05b00b4010000006a4730440220085d68cf406921d17752a717d8f744965c31d49c751231c10f7fe84f7d3eafe1022068f69330824a929f95bbc0b9ad6e3865c2479398e1023bce9295eb2364947d39012102afdc01cfd4f30ac98301015dbb4d95db2423effb73eb5562712834b293348132ffffffff026c2b1e01000000001976a9143bb03138f0525466232e9a5b3ef8d067740ab1a388ac4d538300000000001976a9148b95334f9e312aec176095274f4ba94e22b204ee88ac000000000100000001a3d41ae91943253125f5daf3557d225a0e902663916d61f532092d4622f4c798010000006a4730440220572b6675f8bdb97bb01e1ec1b3f4e5ff15ca23689f4ba8331f4ce7cbde672d16022042f483bd519cf8aa82b3dc9eddaef8673b244405bb94e0f04aea46ac4e68a288012103bfd8dcf3a70403a1ed4d1e519d9c4acb2eb922ca98b8588a8e50c0596b869e82ffffffff02442a0000000000001976a914e43126376d70c8ede82b626fd27f016e7bf74b0088ac3e810f00000000001976a914b02faa6a8a3776e9e8730edcf0a10474b06198fe88ac000000000100000001c323f4fd89d4cc5dbf5eef2cb3b708c5eb52ec6ab8b91e7bcc194482c90539f7010000006a4730440220423272b85b1758045a77f3e2f57a9293c59112c48d85c90410bc7964b97afc3b022022dcdb4e7f8c9254ae286fd6818b0c452f26ff6d85318ca97acf1f60bac370a3012103ee28fd365326672cc05f8e798e3c5a0ecfa00123c26b88e4f91cb81f58715c73ffffffff02e0673500000000001976a914cec8ad83c825d2f78c4f8a24f15253892f67618788ac28b90000000000001976a914b27940450993cdb1cfe6330958e82acb98d4b84488ac000000000100000001f02f961b41c006d8f72eeff8b3b57116217325e3d2db188720fb19b2a1fe8141000000006b48304502202631b16684b0ab481fe5189871507168360def8c43d893bc9bb9c36fccb61d140221009b1f2f853f113a3eb3f12543adb1f8b0e67de9d5a7d369ade56dc03d94535a04012102b87670dd656e3e542eaa7f0d12528dad3222d5eabad3dbe0866d0765b170967affffffff0278e00100000000001976a9142b84df243d010fdac32c714338fd811783d3d2fa88acb8f8c0c9000000001976a9143cc16c4522a396ea37a1d4ac57f700779601d5c088ac000000000100000001ca72c6efed43e4b8a86a6b4c7b3c988a239372a7ac6e84bac3961287309fa132010000006b483045022100d7d936b0f0c12ea909010e45fff284ead7d08569e3ffe716a2ce2c87c9245fd50220621f444a4a151d22cdb3ad5f586110db7b4f454b7eb0069685fa69260a0ef71c0121028f81e539260205ae7bbc22cf9c388b57cc5f9e2a180302db5a878f124107e4efffffffff02d3328702000000001976a9141f541dfebbeb51cd77f68ee3e879877f981658d388acabf9bd08140000001976a91485301528e5ad68e21925f1f38e60d9e11dc1eb7c88ac0000000001000000015eb75b01e18ca08845933b30a397ae03b6230a854c58836efd8ccf831666661d010000006b483045022100c3be50a3b07c6c4508df4cf3935816e8d5a12f44cc852b5f0e297c668c4c231f02200d20115aa2e66e9c79aabdc0c684b941b0e2e238e12329bca2f4f2f49c86860d012102da535e96fc5a1b3c3a77c07cf254e47bdac7220ce188892ce1e54511b49dc477ffffffff0280c3c901000000001976a914e1ed9eccb3653a240fcdbac8c76d52f17fc964f388ace0533546000000001976a9147da7a23cf7445bd6be3811dfe3af49fb7f0a2cc088ac000000000100000001ba183a01ead18e32468681b9cfae7d7dadebf3099381807b89df6b1f5351de2e000000006b483045022100bb685f3cc05680c798fdb0a8d66110079e0a19ad627ec75f4f5f677fcf4e82ce02207648e482d70beff1c7f783b12a99dd43f40ea57d63367e7fcf4c1cfb7cd04304012102105268f9eaa0f694ba4104a2fa34a2fe9ed0a6fa234febc64a7969c8e952bc64ffffffff02905a6a00000000001976a914d3e5c3a1f16fdf0008e07168506482d7482711ce88ac40420f00000000001976a9149913be3ba628eba6811bc0cc252eafbef73ee40288ac0000000001000000015efa958a8905fcdc2c5e0b222ebcbdf45a05356432a32a934cdceb3563019f5f010000006b483045022100c21f70b9addee155acc29ff3fe05d6c5a6246373c43e7ccfdb6d5fbee037b2bf022007d6bee18e535b45daaf6808dbdbe686d1b8fcbd92fe46aa31dbe4f7e377a616012102d46da59ba6dc1eb24c88295e14e4733696fa12bb04d68f2ea184727065f5bf66ffffffff02d2ba4a05000000001976a914174c26508d593f90ed1a55a6f092d381b3dd736788ac2b25544e000000001976a914c0a135c4b05a187289ed8d25a31cfa04bd1ca4aa88ac000000000100000001320aa3852a23214f3f444e2683f3b545a555cd46544d3aa97383f840839ea1f8000000006b483045022100b55e45c19fd6a810d045a77ca2fa33827e85bd88bf6147729d37de3aaad6b2fd022068d4b78993b5a0bdd364b002aeafde175f33592b1f7c51e5f0f95355cb54a3ae0121028d47279ea007706e624271a369bdae02d5658b6b5deadc1b0221518ed096352dffffffff0240420f00000000001976a914da5dde86d69a5d9dad88763f2df4b048953c7d0488ac80380100000000001976a914f5710c7a405f1aeca11b79ed8203ae5328853ba388ac000000000100000001777e61de7f2cea472a3a851bd5403599ecccb8ffb382582861e0f5172b1867d9010000006b483045022100fff70cc33bd128648a9dfe6c5b2e2bfad4f1c3eb5a8cbf97f0038409c8598aaf022064c89f5a37ca3980a171c0004333aa35a305b5ac1b11532218ad73401be3f26901210266ee2a40edde81f8acd7b19c219fc07ab476a2b4f8a3d7cfe81c5f511435f1a0ffffffff0230d6b601000000001976a9145621f431fa99c7d87e40573fa2bc31e781abba5c88ac00255602000000001976a9145ed051ff5a28a48678b34d635e7f203b9ef4e09f88ac000000000100000001cf8357bf24882d3a03544c4657de9f7cfd9191eb1f7bcc17ee2ec22e6a84e694030000006b483045022100a0af8623feedb5e3921487422de1347844340a5471c220042d1586aac492d930022029cea3c5dea4408d58e5fc366fe9cb1f992e73a1c3052a18a8dfcc4e1fe80c190121027bdf5ea599028ced662ed71b4f9c36b803ae07c70144c434dc872fe58176a8edffffffff02bed38101000000001976a91438675862d52a02d8ebb5b0c7746436521c9e5bd988ac25133200000000001976a914e5711ed1d4b0751bff7c8266f613c5b1fc6df31388ac000000000100000001e8d02977a8c854749336aa21237a74c1449a1432c97b9984f7ef73663f4d9add000000006b4830450220549ebac060a94f875f70893baef873558e5ffe9bef6662e4a74d3d3d41596f66022100dada2ca77d646fd9c24c1215abe80c3998e5521cd408fd3924073729e50231390121035fdd68c73f464c0364ad77084edd6566a2e94cd6ba60227fc2bb52f7dae31d8effffffff0270791205000000001976a9143341879a5abd625fb07c88d5681869438d39ec6688ac904cc800000000001976a9149307b74ca38170f6c4617efe73bb842aafe763ac88ac00000000010000000192c4bc2ee78b5faf50ab4b79658d38089283dcd2c9044fb7229505618d0a253b010000006b483045022100a23f73354759e9f97f814e82fd63079ce3ce37312c1679619298795115ab7501022001110cafbfffb01a2793bba530caf994c3344ae29d0b594ac2ac8b781be27dea012103be139acb8b1eb484385939a6cec7297ba3d65158407bcb62153b4af94a099581ffffffff0293b80000000000001976a914d5a99e28eaf7a5337a6f3a56a1d2436dea59fea888ac22479700000000001976a914ca4f1c265b75374e4102ac2559a5694fc8799f4f88ac000000000100000001c4c7b5e9622430320c9202d5af797a04283818ebc53aa4e450962d6c0c5b9d04010000006b483045022100889fdb81acc0348fe140e73cef1219dc2fe8af67e813eaf009d60ba59e39c5cf02200396f4ab692a5c4c17aedf5b129a39a49314917f4ba68d74d553eac2fe13c9e6012103881abad2f5b53ce1b1d1fed517344a0823a8bd2d6aeaa61776d5011e2376eff4ffffffff027c330000000000001976a91491373ef109cc861eb6e948b046753cd004aa11f288ac66559700000000001976a91439bcf6db3c66a9e032083b405c3b4dda0267f38388ac00000000010000000144a7f3a00f9bb763b2d5dcc4f54d3387cedfc47e19fffa6d83060956b0f1d197000000006b483045022100be57d9b2fb8578dcae72ec784e11d72f0956b56670deeff316746f438854f28902203530051affd587a800fa5a52904bc00fd3dd249501f0aa08898da73fc9e88cbc0121024bc3b58780d69d43cd8fcf9c850289567014f5d5963c1245b3fc8bef93498927ffffffff0268334100000000001976a914b0a72052ae4630c850c68b23885202b1596d956e88ac38a01b07000000001976a9147241ec9ac7c29efe9985fbc69927f70029b40af988ac000000000100000001579d2101d3b10dce5716e2a7c7b5727bb30086cfee4b6da1823f66f5b0baf213010000006b483045022100f1975cf86198a316ecda478fda199f134a9783e615f1d3df16816838d7d9306402200c2ca1dc6c200627624be248221aab8de11c86a483744aed29276c0a4b7bcab4012102b2d192c1740e213ef0804b579535234f45c69674473f5a691199c9ffd72f2bdbffffffff0279294e00000000001976a91408843fcfcce6c092f1ba9d17acbf644c803e422088ac87bf2901000000001976a914ecbc2f775d741490d4cfe4d3a61d6b57b7d2ab3788ac0000000001000000015edc6c135fb3b7b95c945c3a64a60ab38f42572dbca58b5dc22d678a7af1b45c000000006b483045022100e3e60dc61028174ba8198627b29242faa12232d57570db169c97b9cfa8acbb4502207fdfdb729a9a04afbb1f73e8765de137957d9be5d39f1ce442dfcbd00921e4fd012102b3c3b2d51c86500f1e754198e38474a6ceab39759d233e9299e514a4f789d3b1ffffffff02ec261a00000000001976a914eb29476acf4ad065d90af94e2662201a06f7ae3e88ac1a6e3400000000001976a91412082207ee0f399539b547ebff49727374bc2ae888ac000000000100000001d3addb2ceb3cfe904b0552c8467efa6306a42cf45b56426e19d6b6176d9e22e4010000006b483045022100ed5922e3053025c7527d5704def6ddb56f8a02ce45f56c1198d84fde89c0b214022020a980d86a056f378d4942c8ba6fbe3f18b55fee3406b15a300026311a151074012103ad9b97eecf95726db5713be1700376d33a5ff83aee6a15197075ef59285551b9ffffffff02b02a3400000000001976a9140d40bf9e554dd37839394a15c84ad2a3794b89a888ac52df2200000000001976a9140b71aeee3833b17c82ee31051df0668fc37d463088ac0000000001000000016fdeb0188cdae06b9e83d5aac5ec68c09136199b8165adb6cf75f5efa22e67af010000006b483045022100c92be15f1296a8a865ab2d6d41e781b7cb7b8d0dbaf5dae34fdb9ded70eef93b0220479b34b7f370d1f9cbbfab757b5e1880d8e35d4ab5c1fb1ae3aece33cacb4a20012102a106d4af749bdf605f5eac7ad9d46a72f7f0598fafb82a03c2e635ea68b8f4ddffffffff02cc880f00000000001976a9146c5184afd13a71dfde0dcb681813c3861866af3f88ac0b300000000000001976a914f425a393ebbe9a3fc3c34957d8bc691863c895c488ac000000000100000001d8de133e6f5c5411ed92c39e18cfa9aa5e71a50e7e0f3cb29db9e351341159e2000000006c493046022100c85b8d6ef594ea9a69f1fec82c117243b196a3485eadc66bbebcef98027b4850022100f3b83cc70610e77c9430f17f75d2d7e055f3733778145b4781734c1aab2ad02d0121028d47279ea007706e624271a369bdae02d5658b6b5deadc1b0221518ed096352dffffffff0240420f00000000001976a914da5dde86d69a5d9dad88763f2df4b048953c7d0488ac80380100000000001976a914f5710c7a405f1aeca11b79ed8203ae5328853ba388ac000000000100000001853160f3b6e2785caca300bac5430d2b3c7c0de1ab18b63bb96167f3da618f17000000008a47304402204b8babc6c254750f2008dbcd2e4b7ad04191228df503eda79ef225665ea09e3102206dca042aca915cf1490f63f859c81e7fe80dcf90ffb97344358dbd4f0bc2236a014104409e655d836f67c7c095da0184c65515431b296401602a3d68262869f3f992c6cc7f913a43be3ad0cc1df3d37512c80a1e37405b733a203243f0152512d4bb04ffffffff02c02a6410000000001976a9141a99c14fe5b2abad38bca2c355db786c7a82d6ad88ac30b57e05000000001976a914185c9e53b6e4171f1aa6b35390b2b960b71741d688ac000000000100000001f7c5a3cb32097876d35001d0ec50f28d7f0fbaede580a918ea4287c3f8e116c8010000008a4730440220665bf3b4cc7b07f2b53f4036b3b3bf74c85bab72efed6802c200c4eeb7deed9502206257341efe59353402f24e616634aa4c6e90388072a33dc5ffd0f205b768dcdf01410450dc028d6bfdc291021090ac8d42a568d69fa2638fc89dc76c4bc82cc2ce38186034560319fe0665f316c8dc66e31f779ab9665eb632e49067bc64c43568eb98ffffffff02201efc01000000001976a914e5665fa435239a42dec015c04ab3f83e80fa045a88ac0b74f1d8010000001976a9146187ad1cb7f4093a7ebd89dce4ef2354009388ba88ac0000000001000000015f73519677abddd79f854d9a93b4d6cecd211c6cd78ec302f60475fc6f9fa08b000000008a47304402201a454001be9d3404269c127a255d807a3536a92e4fbf449682288b03da44e6cc02203146198c92074a632bc37e92ef1c712c8615c825a33c14597ff77c587876826b014104b8789f0453d982a2f2b51a8bc5a3bc71e2ab9562cf2e7bd85dcd1ac21e8d3b77cdbbb2ac8c7f75eaa94fe23008370b3d0ca8e19c95b3071e89a6916c7e3c4d8effffffff0200879303000000001976a91433f4812fdd403247e86d7a1cd7e8cda3a09b9e2b88acf0135808000000001976a914a89469416e1cec6311eb04a798615429640cda4d88ac000000000100000001c9274bb3273d026c99e00cfdc35f0a70b01c35b08604c8d2243b0c8bbf476d6f010000008a47304402203e3e7237d3680c114d804843d6fbcb893b01ad31aa171843dbf089bdb01d8cfc02201470d19947d78f9c9299c57d6c3cb47b6c4fb0bc7d082fd6996984e0395740a80141040ef0c03173ad8917cd86938950401f95039526feaa187115c8a237504920cd0619883e1cc408ddc4808dc6ac7649ab6c08831f261aeff4631bdabbd263984a0effffffff0260e31600000000001976a9141030ab0117a2bcd090a0e044d87ecc80d6bab66188ac2a528c05000000001976a914ceeae18e590c48de17b3f978465c89894ed6f95788ac0000000001000000016a411d2a3de51595a80f22f2e1ba84576371d971472f9ac24b6132a1fb1fe808000000008a4730440220111aa46e1633d7be21c6dd89a26ea8174410b9db5545f663216a2161866749130220539f94c9ed985b977c7ef40f23e63eb9d899e4bd905ac449f388ead7b83b0bbf014104f8b6b50f288bf5805a54e71d76e0ed2483218fe9aa8c12a8bbbbd4c4e3999aa446a87edbc5722d01bd256259fd33cb24513c2514a684780ab490cbd93ca36092ffffffff0261464d00000000001976a9149e14f554d90fc1f8e80392953a9b4afc8ec2827a88acfb246800000000001976a91457d9454d85d5cdba9a6a1f0b54450012f3a1d03388ac0000000001000000013babeb83449c990c4fede2465099db7f217a1649e1207319d0907f04f5c43191000000008a473044022078bacdd5e66a1a12e33cebaf303c07cce0a43b757b9ea0f85ae33cfe66cdd2ee022050826d3c4427ef0bf5357fdb95db33d207c8fc9ad9c53459027c31046d549a8e014104293397b707b5a23a9ffee09940b3e16d9dd585dbb80e0b7ef083148c492d2b32e65f878c0d3926fbeaedbe0792f8a2bd04da33f8dbf507126af5078c044a1db0ffffffff0230e60200000000001976a914f5710c7a405f1aeca11b79ed8203ae5328853ba388ac00350c00000000001976a914f0dd368cc5ce378301947691548fb9b2c8a0b69088ac000000000100000001124d837c44bd2b345237f27bf759245ce2f7da168c67f13fc206e83bece9d4d5010000008b48304502210087b09589c4b5feb5e4663fb4d22e7068af25c6d4a4fec8edf6455ea5a4388578022059acaa0056cbf6c3a9b334b1f2018cfebcea18b4e7f84f6fe1aafe6798aee2e20141041e11ba348505b1e8be9a4e1723377ed31d03f1640cc68782e8a9d24e550181bf8a3e09d19edaafb0df0698882f4585ab5bcfc5e6c4169d58e7bf1068e55dc9feffffffff023a986013000000001976a9149a0f7f1e60867d16cb60a4dafa61dc32cd2e3a0d88acc8f8ff33000000001976a9146e971364ae0bede923b7cb28d367413510760b4788ac0000000001000000017966a2d0eb7fce33ace82bc4c0d1d4f8a96b4fd7d13f759fb60b90b56f37d38c010000008b483045022100ca95c5064dc2ee585c15fc1ee3fedcfdf7cceff3346faa18ebe8bce5842a30ba02203527488c6b4a5718bfce41a2de6fce65aa7138f1a2f6c72f3e55705a75bd7f2f0141047a69bd825ab06eb8d8b46fe4f86e2b806d4d536d4694b84c668675b0fe00b4d4b52eaa3655080863de126d28f393e9c77621d07d8c3bd0d1056a3d00a7171db4ffffffff02009d1c00000000001976a91475fe37e63a11f3dbc9dcef07825483b8105bcfab88acb9317802000000001976a914e80d9f396d5da926d25d8c7fcfbacca2c8c9fc6988ac000000000100000001deee959fee56f2612657baf6129364b6730ce35f2f2dab657ce22af82ee1b4a3010000008b483045022100f82e75a0a04a8cb14395ba3de6a47b68050796777c23c287012e3161a3bd0f6902203899b588ed685ca8896a012614d2c42115598ea08d55f5fc5cbf7f84b207816f0141048d6a16767516cfa38e2e9fe2af31e27dfb15cca7cd8c926d64a5a2c33480a895b3c83664b50714116d9c6e48cf3d87903bc54577bad0bb24d99e10010c3109c9ffffffff02d4f21b00000000001976a9144f51332849fb0546182081b805b5fc528b2120ab88ac8bad1000000000001976a914c6b2a32b468cef28a9d4a2304ff83f80314fe64c88ac00000000010000000111bbb5438301722fd575accaf2a839070aa7050fdb48e0da00835f14a49ba3fd030000008b483045022100c88c8f183041c7bb7998ec27cffe2fc02bf3e5a7f2b250423d70a72c0d90a43d02205fcca7bc718117b4e6f1e3ea5670b89682512e49734ed42900f7722a2c36e394014104094de7f3d8b58ebecaa329f2d9ee41c7040225415fe910753f491d2a9c044f721f7568760ca14e5e8c9fc70045e33f714cf83a152dce27d5027990a72324e0eeffffffff0242f33707000000001976a914b6b2df18fb4fa92b4e0f5f96ac3d5331c5cc36ed88ac0e21b204000000001976a914de82acab0f37d0a26ee5a51e58524d5eba30eba488ac000000000100000001f3aa5055dbb96f18313808c7c1305b3bbb5dbcee1c2398528c891fcd493faf19010000008b4830450221009efc33106f82dbdba82fd6319a6b53fe9baec4342d66d7b68518bb651199f0a302202cb1b73072a7a8cc290f10d9fa8ae41b749eb168d71f17280872b200474938e2014104417443fa374c00cca0ea30604c7aefd6f21b374a67b5ab3050075f9d62c12d47e43aad055ba447e2e4ec7676f683dd33d3e3646709dac153446ffde34ee2e34affffffff02bc750a00000000001976a91443262f596e7a65672ca56334a795cea898121dcc88ac4dc91500000000001976a914b1eeede25eb666a34bb33acdc5ae7850abbe81e188ac00000000010000000110c7918c6e46668bbc05114864e3f8fef4e96fa0f6cbed9faa1c753f6536d3b8010000008b483045022100e368c9ef534ec91c1cdbcd5f0e5f95ec670645e8a5a05a226dc3fb89c2d3878e02204f2b0d49bfa0e5f947adb2adb5f5da55b69dc016df97072003368fb79469c5390141048f60ccb14e412221aabf01f713eaf25d12e764505ab9f54d5faafa75482006f9891cd4a57ca98edea9719dd1f4874a5574f3062c585ead24c104f912d88f76c2ffffffff02a61b3400000000001976a9141fe1c54e815d5e667f310ca8f4fc610a06ce154988ac9ea55401000000001976a9140d4a24d35fe37112cc1be874ac266960fb666fdd88ac0000000001000000013e4feb6ed0f5b761429cbd095483bb9325d049e394b88b8f82d4af888a999479010000008b483045022100e6ea99c65c417aef733d0f6feae1a2e43c73461520d50e2bf7626665459b0a8f02200572553dba49b258332a3c2ce461b648ece6649c6cd206be04a6b7c5bdf082e601410420d2e23be09a121d8290649441d4d6a00a69df7f61281e0746c529c7429ab475371435829b91965f719894da5439e69cf04aaadb5a91a4a4d3b5a4eb396f6037ffffffff021c980501000000001976a9142b2a422e2ba5e050d1812a9d4f0bdb3c5c0a791e88ac71073102000000001976a914b8e02b9cd3b7a25f31e4fcfbf4f828ab9a177f9788ac000000000100000001b935034809ae629d9f29f0ded3440f4e2d8baf9f9cf89879b3610f51b165f254010000008b483045022100e63c9ff9aeaa8845a7feeddcda844988163b2c5ed529184121026d2553cb07ef022061528663de154ec2d3392a43a62b2ecea1d5d8fd7c859e1d89786ee2730ca28801410429d01739dace5cfac8447d8551d06718a609deaf1e826a7fc25c6648b88bd5da1cda9105f845aabb8c216a3857826d0aaeb32f0c71c45e574b5634b1820c8192ffffffff02978b6701000000001976a91486df3b2d24ae69ef12b1f0eb2fe4b2907047a5aa88acf43d5100000000001976a9148ab2ae9d4eefdf5c13e88b297b853b8461a7ac6988ac000000000100000001cb21cafaf3b7b016f86233c6364f6cbc16e314ccb4aa11b1ba260d47e1e67925010000008b4830450221008e893cbcbde1824f54b782da2ec8a075c29486273680b336f145aee0c11859e9022039ba1e22d90ab46600e4ea4c97a2f23def73b55a06f694e8569194eead821bdb014104c716b5b08e411785cb2712c12e58ce18c7a3c6aa31f707fd83fe40fa251edb1a9422a1beaab829623b55991f1222baa611c8f6ecc6f5d0837b6903bb49883134ffffffff02d31c5e00000000001976a914cbe6ca3c6ee0d502f6df956f3b9ddb80275d958288acd6072402000000001976a91428c553835cb95c12f1f9dc7b7ba9eac3eb7c3ed888ac000000000100000001f021ac29a2d0b05eb6e2b471c0af9113efcc7c8f6dc85d5513f56ee549cb91c8010000008b48304502210097b2e5921231da06c6c4e5d343104cc70b7e7b197eda82447a90b09f77b7074302200f23ae89a2e4c12fd309c27afedf5055a68461631107ff161baa3617938f66c901410423a38d2d3aa597789d39903b7eb80e989ecc66ccf10f5ca8bd274dbbb703c4a4596ffba4fe62de1aefe4580c761e72c2e1b4c6fce017e239e18888f71a41cd64ffffffff0220e88d00000000001976a914bf0b795fd8b79b7f62796070779727226577843388acf81f6a00000000001976a91438d8af7bc118d40dedf94b738f8fac011f05b64288ac0000000001000000011569a87b45e016068b575b73d60ee9ef7d35a8319a5255e2a547b4ff00794415000000008b483045022100aabf3e1277a077d5247d2a9d532b48a3e8e7b585281f69d8692ab1a988604e1a0220191cceb0cf29cd10aefef6444f11aa2b433d4c9d95d7fa354e61681c2af30e9a01410470d0259b8f0d5034c235c829a8b13915e04c747159f6bb26e89fee3bd9a0c50f60ae594e54243fc7639e1e2cf67c6594533a993b02a85293d87a66f598540571ffffffff02a0bb0d00000000001976a914da5dde883cc084fad0d72ab4cdeb11205fc63bf888ac70110100000000001976a914ca7eaccd4ef037929be65c7706992b055a41362e88ac000000000100000001181fed550dc092404ea20275e94359f3e5d96161353811f48bdff313de0c125b000000008b483045022100fda418ec9a3147fdefda1309bf58137e7921e57d30d67dcced102b5d78e9905702201733f69f5754a8867923aafc9e5c4a96e315be2aac11021ed38b37dd4bb92ce2014104da6bc6a6139bb008454bfc8371141a5fb8ba6de87e9ab1578ab4c31e1b25513d6d1b1b0e66b0e39a29f6baf19f9f0faaf51d22bac02b1c07eb08058498763784ffffffff0190940d00000000001976a914ca7eaccd4ef037929be65c7706992b055a41362e88ac00000000010000000111bbb5438301722fd575accaf2a839070aa7050fdb48e0da00835f14a49ba3fd020000006a47304402200b77bf39c7a72b581b8fd7bccb0e032350f7abb7b4254cf8e0f5248c1d9ae9f70220526af0fb6a3b2cc92a82cb06bce250a06877f0fde151e67415e6a2f28405a865012103d799787474d743ed1b8df4d417783c1b5e2e78a102e33f277a64971390ea294dffffffff03945912e5010000001976a9149bc9957b09c245d39c3917510bd9e05736c67cbf88acf420cf00000000001976a91407981dcf86e5e13ba4c065d9f9476c34eabcfb4988ac80fb651e000000001976a914e65218b9a7683303f6dc805d402d25950257092c88ac0000000001000000018fefa67b4502fc780064b4d10b77cdddc2d0bcd2d69f799e51c0df2552ea939e000000006b4830450221008e50d557ef26f173463a86adba49966dabeccc7ffef92b4e4ce785b88a0ccc5c022052afcedc884a2f603e8a5b12a746795d6aea8226430db51f4bbb12e40b57f120012103674f89df91621af00629f97f566767f688355ebc97fc01d14c51b70fbda24525ffffffff03db3f2000000000001976a91427cdde93ddfa070233c68b694cb85fb9997aece888ac599f0200000000001976a914ff5c109fce9d655d33680d55e9c7a74c455883db88ac7b9e0e00000000001976a914c0192c748170eb5490b9163e0f5f7c3c9c50c34288ac000000000100000001740120c1e446bb1a839c9b9a1bcbdb8fbad2d68055c8d847810088d7a1468320000000006b483045022100d1bc0a611e92abbf8c4e8a44b6347ce1a366b2aba335ae345c8be11b144c638b0220662ae07f35733a14b0fa48eb377fe7318dee6e635d59e3a063234d7226c73fd9012103ce4498a8412968f705d75b1ba7b5d79299e20bb9cc19f4acd8eb59dd0916ddc2ffffffff04c059d100000000001976a914813c4d05243cb64d32b21460360112bfd3b499b388ac1495a000000000001976a9142e143d46f8fc8e6dc94dcdde9ab3c73cbe6d9cd888ac800e8500000000001976a914cba04cb546c9eccab7c6f835d60e0189843ee5b488ac20a10700000000001976a914b31a32c32834bde2651e51e36e22f37cca431e4a88ac0000000001000000025eae9c64a6e9a9a896b4b8f833844a96e6806b0e021307f4730dbbb097fe6645060000006a4730440220680d2fae8d08cf2ded9401c070efa27f98353740402e8b28a5943ba240049f2202205cc09fca3247aaac8c86db9337826e94b6f98f10817539105d1d1ba7b277f49401210224394819d214109a51c2abac3bceafd404a6a0591c1e9100db89f22e23c2ef66ffffffffda990246ee3a8368cc2e827ff0ba41172c9fd3a1089fc0ba62c1b0f95532a998010000006b48304502210080681ac2afc3f59503bd79ae605c7aabc4ac88b66115ed9994ea2db59218e42402205b61511542311259a37d93f9ca5e48933c05cd9c82ab35f4365b631a3c400a4901210224394819d214109a51c2abac3bceafd404a6a0591c1e9100db89f22e23c2ef66ffffffff01b0360000000000001976a9143b5b06bf745bd18b66e3dd4bba1824a16672eb7888ac0000000001000000025725fc135209a978b44c8d242d4e0365dab7ef21590bf6c6b9c6ba33e135331c000000006a473044022074df443fea96a9d6cbbff9a34931134045b3063c203dce34371178014e3cf1930220215dbda357261975ffffbd9039a947bc71acda571e231f12d05b23f91f68eb840121034dd36bfbfdd7e845479ee48682c4f782fe66e85d135ebb826d7ee119719bd9c4ffffffffc07afbf00251f537e8fedcc834f43246f8a97db021818e834a433898721b4be9000000006b48304502210085e2773bd046141d81d01d5b59e0d5986646ff2f2eb186d7028ccbd04199c981022006e4de78ece565e3c81849403f9190bd131acf1d86fc1a6e7cc43c059a95d77d0121024dac20befa60963a0dba41a7625a632d7398e49afb92111d0d4a2c7a8c688d95ffffffff0170cee4090000000017a914c3c8e5cc4486a617748d946eeeacbd99039d04518700000000010000000113564c11a0fb53e31b4c382e25cfe3dfe59e266759fd2895808def70f24bf7af00000000fc00473044022026e6f4ae9c5033efdb48aed1190f98e4177190846a4a66a7336d4dc21d6cc55902205bd1cf2fcfd1c194ce10f204117be485c21c94130f4b8d177f225779ab9a74c501473044022005afa168749712cdbe76694e51dcb060e37d33caad49d806afa3e6ad4189e552022011eec2bd514609fa9c390647561f03400cd67c9f047ecad32224b36f38dfb1f5014c6952210372f70a3f4ea93f6775c0e87486b1308115479ccfb219ac6aa62df34246b32ef02102748c8d6bea89f79627402514daabcd28814e4a543812ae334e101a40212303262103e981b390020a5465c4c3504e2e7f63838642812ead315580a5c3cc8848dc427e53aeffffffff01905f0100000000001976a914f9187f5c76e90d574746bd5f61883e753c0e1de188ac000000000100000002bd1452be9adfb2aaee1c51c312b44fc8adc35fd4c2fc4fcab5b2111c9a51a02d540000006a47304402202b7d09a5b90651e4e77a220000fa134feef96c570d182556f597b7bb8253043302207d0829f620d8bdc86dda3b141cc37e78393cb6e6c3ed88801bc17378ec74f0dd01210259f00dfb22a5f496d6575945175813e211c855fcb39f0a94f5c431fb512bfd40ffffffff39b53904843a12da55a44806f77f0d352ace33565ce0bda2bb21ba3d241353ea000000006a4730440220316c78a229aa870d5c3ee4c897f8924fd7fdeec5ada8a00a9f25445f2601781902206385395c271339a2dd6e285efbcab98d6112d3fc11edb810e860aad34b21f11101210299c4795dac95e81964bea178d62b007fa8fe7f047fd4a21161924783f0d880cdffffffff0140bf0f00000000001976a914eb84dbf2edd461b3e983f7de911239e150cc6f8d88ac000000000100000002d115fa1ae8fd0b6d5878dc787fe79712b546b53316a1f1cd5450abed7ef89206610300006a47304402202e4772a3f45f40da54d54d2d68b5d6f3f0b7f8737697362a0b7f4374873678c0022068d25a1dfe8faccd349a53e3c116bfb7af1a1051956da3f8ce19e74b35314849012102eb8e086efb575a031c5a01d9e10bb3984bb4bd88b2430cc60a3ffe3bd9df5026ffffffff07ddd6f71e0da29004d4649bc51729457c81ada42aadd970cb13b5766f3c82f46f0800006a473044022046c6e3d999914176aec9ad2bf3af460165812b4b1acbc5881ca8fbdf7445bb9d0220687c03a8fb427f305fc5daaf705694537bc8995becd735c72d9b8bb43b0b05a7012102eb8e086efb575a031c5a01d9e10bb3984bb4bd88b2430cc60a3ffe3bd9df5026ffffffff019c160000000000001976a91415a2c078b214cc95d7ad87c7eb45c264f2c9063c88ac0000000001000000027caa31452e709a5aea64e201b8d712751aaa63828dc1a466fc5eb5ca86d6fc79000000008b4830450221008089d52ee9a0ed71ad25441df44a8bc4c2ed82879fa430e73e3686030f5f080d0220099c02362fc29844694aa133f00207f71d7e8ff1650d1ef974f36cf32950b360014104d84611fa78824128dcacf21ecccce744620a8610e906072ae6cb573d01f9c333f02f9fb799afda52ff5116fdef053460f01e7c7a372ec4f94e0bee8af7125d93ffffffff68656e1123c69cf73337643417eebca345d41d5ca847393d3db0e451b8e40172010000008a473044022076bed872ddf06522b16804382c2c96a7828dbecd8e59547d6f46503e4349f89e0220437ae2fcf9046e306eef258a75f10332d0b775e93c0c5bd2920d654f40cd754b0141040d756049f1ec92e3d3c3cf01ae02ffea271c4b4aeecc71f7a278de5c09b9112c2e61db1aa4b430722931a60c4a89479a62f25694d8861bddff5941258f36a1ffffffffff024b630a00000000001976a914a0c5b38f7c2368e96dfc4526bf2ed463ca0d4b9288acb7620100000000001976a914a12136aca8620119ea6aa100ac3da9b99809b69688ac0000000001000000012596aa4b5997ecdd15a1d400697d8e52828a01cd65f7e35651164e4f96c49c63000000006a4730440220233648a5d8773ab004c2cc9d8982498a20c456653399115e38b8e6751a678fd90220479eabf4540a653d4c57950061fb8970a4063873b48b0e5ac63ebf2ecffdf16f0121026f4f7b5176851570e91b518d7d301d557a7404dffbc8a88fcd66bd525c0f3480ffffffff06e4884700000000001976a9149b7687c561eed2073b263a35f1a47753caf5b32d88ac70288302000000001976a914e63fbe349c4a83009a1eb1a365051fec31b4451688ace0775c00000000001976a9143b6aa25bc62434c30a4a798a9e3f3996bef7137888acac19ec02000000001976a9145ecf0cd918f472fb4a2a72a45b46dd95b13530f088ac603c1701000000001976a914de6e34bd2f7907743107141a500c599cb9ed1aca88ac100b3b09000000001976a91431d7bb7e733709288eb30d0f19811203bc7f4f7d88ac0000000001000000021fafffdb425cad075e85adc266dc22005480cf93fbace93fbf9023921c79477e010000006a473044022038edf155b171df595a8f38cc9ef2b9c517134301a7c607a28a68caf809e4cc190220782572761ceb03104779f6a0442e678df799edc73f25eb2c3497bfd1c5e686d001210245c19602bcfeae6f09481e45a847fa2d5391fd13712e9a9a12bbf61aa8e80527ffffffff9f8620f6c9f531ed3b5d1c5f55e7a511c24d1d855aac206c90276e35c2fe0684010000006a473044022056825d30a6ac44bb833ba62f1150fc8a4c2b31a5fd43909c2354065a9746ca1a0220408199839300a0c9137b3963f174a75700c119e88825ad840f061c8eab0a300a012102e20ee178f2eb8c71d6c19c82a1c27a43c90616e0fc8d065d181d530b455457aeffffffff02e2f94100000000001976a914dfba28dc856c9a949fb67a55b234adb957416dfe88ac34c0f201000000001976a9144c64073ba9d839d4cb578f88cf3f72690eddb5a388ac0000000001000000028eea3e838f3a9d04730a6c2c78373437160b36cce20c140ffe56f15c92a003dd010000006a4730440220590cb1f0005d74882aa09c87def0ec1762b7af74ab3ad267a5c09fafab9bfb9702207b708cfa0e4b66a7af158d0d7d4553c1a190887d4cd1c34e04502d366c64cef2012102aa621b1cac7e835b5ec78e6324136d3d70c9ac213dec4aa97b32d691eb081d8bffffffff7a38994b5605c572e160e7b7b9e9de1eb4c9889a8f300d8b7045168e4e03e93f010000006a47304402203cb6f7dfdedc77f35ab86df573b6b853dafc1a28d9154dfe93f8195ba0a06dd602203c3f05e8a5cff0f9510d27260d1c9df01181ccfcdaeddca804c462c44af52e3f012103d3e562c5114b26191c8bd2af9f195c5eb5c8f6b757a7e72016570169c7679374ffffffff02d75e1c00000000001976a914dfba28dc856c9a949fb67a55b234adb957416dfe88ac4943f001000000001976a9143b29af8b165554c52b710dda5285dec00038238c88ac000000000100000002b791f6682e071fe6b384ce2fb0c8f1fd58c867f9ae076e98be8190c454bc5211000000006a4730440220206e8872d69b18b7dc0b16fba168a81685c33b8bfe10da79c0730023bff7a787022076afd2b9e6f296f1fdfdc73d5cbd5fe510c75c647af8ded2b3670ce242d67dc50121023cb02d3bbd8bc227b20b6f8affa80464e1f9f2aa684f2e6175ad18f6f1ed3ac7ffffffff1b39183f6c881581dedce68a6a708602c9f897e86ffc8363beda770c957fe955010000006a47304402207dfd5c4ce70c05ee332904eea35315934125b7cb48e765ac0b9ab6d9600c516a02205377ab5b89a53398781e3e3a262aa45628f999308f3cbdd591d2a62c896351e001210338c12cce00a01ee5675f76a685a9bc57af8b3285a8b074e465880adf6794b8c7ffffffff0264ae0700000000001976a91454157b374cbe22e6d57577b5ea31ba7696f46c0c88ac18196f00000000001976a914ca359613589c6df6e4646d962effd2a6e6b7556088ac000000000100000002b5b715cd300ea36492e8fe95a1001a00c5120f9a3969a47cc9c7191978e87c80040000006a473044022007bd254e07ed53ecf3a100f4ee2539109404f8ce589c70c7e497cb116301705202201d268081223073aef6ac12a3798b367023e20c0ba5a40b93ee331b057ba06a10012103e8e47440766a0d1f4696f5520b2b0c86aaecb8b2a7a33de489c477d753a34677ffffffff0f9a358cb817afba91e264ff1b41364d98628b52bf1a7f0c916a9f08fbaa7850000000006a47304402200dc3cacdecdea6d5c81fe97534afc4510c42f94a6770fe515570c378e10a60db02203bd3b614870c72c0639df43af4441b34379ded3f85d9e5a0bc521fc0746d02b00121022552d66996d64cb87f4827433bb345a0b7a1d3b02f394c7e889fa9e1791f6c00ffffffff026f420f00000000001976a914ec4d996b5d9f22dd1e4ebcde6e06dce850d6d8f588aca0860100000000001976a91474ef9b69d30a2b2af63adc749ac3169d40abd67688ac000000000100000002cc129ac4ba3782defdbdabe80c93569371935a14805aba8f403a2587a36efde0000000006b48304502210092464f2d4cb41ec0d39a450fa9186752dea45fd92714739d5796ae8eb78321ce02206a26d3e3a3313d09ed604d2c4a2b54c640b8a001b0bc0f8f245d0e723d8c0d970121038795e76aea87acfdf50f3ea0a3fd7886dad42d6eea24b31827512298fddc7f92ffffffff85c6b1af317eb00c4996f718d3e4ba1a16f6bada87f2b088ed38c785cc7a4545010000006a47304402204e82c7d2e67150dacebf7d61c1dedd53f42ecd602d47a22d454af6b970d3ed6902205d7f516fef463fa02798a7c298df281d81071612d05a3bdabb9a1129ee8baf23012103e2405329b76406222ac182eb477bebb6bbdffc3ef2e7c593463ce80fde269dfeffffffff029cbd1d01000000001976a9141c6959c775b5052cf11a0f88a2e2974e31a8a3b788acdc7d5a00000000001976a9147c6cc52376541171ff54df4698ba81bdf5157f9288ac0000000001000000029692fd40f4e29b1f0cf49eff3e02e5c929483730421e5a9bbd7a4e73fdeed5bd010000006a47304402202bf7daebd603aaf7f4b9d582f140c2a0fdee2f25fbbb1624c7321640a7fcc97e0220010bf367935d627a51f40e4b127586e8a8b7a0e51e8bf110b013f4c8f8687624012103536b9d1c215dd201bc407510bc6412e95ab0fcae3bcd5cd652cf4a82069f2c07ffffffff2133ecf456f347a9ad36f2746167d2f92fd12b3ffe564f0168e8499f64b0b6cb000000006b483045022100e994ceaa431fcea10c2d694796ce0f4d2ab1a6787bc28fa38501bc78d3877b620220587efccb520c8fbfb31b73475bcb06e680cdf84b98e1e4fbb185dbb66ec81d55012103536b9d1c215dd201bc407510bc6412e95ab0fcae3bcd5cd652cf4a82069f2c07ffffffff0229303d02000000001976a914a79436ae0070b735d13053ab80ff3bc8d3847fef88ac29310200000000001976a914d4851ddc7a7a212119e5966bffdd773a4a79507d88ac000000000100000002a17278f78dc2f7f232559f7648bda45b5ac0de1bf24f394f6fd290f8d053776b000000006b4830450221008d1e8c702eb08fc8f29c2f1e7a92ec0e4feb37ab698327b84d11fd0e449f1f35022029c629969663a4e74bd267d1e8e4a8aa79f9f1c2cded78818ab42bce1c8311a5012103080030c8f06254fa1675a99e8d43bdd3b6cd7c54c1eed27dbfd6fb9f5239a0bdffffffff5ee938e6039b2265bfc85adad30e637d565319385cb6d62fd1a90f710dff89b8010000006a473044022062614fe509ab2087ad3dd28a613ae2d2bf806e445770a6268455807a781fa95902203d732e0185abd6baa5bccff351d076a8e2dcedda6929fa18843457d03aa71f9b01210359d7dd8e411ebcf6979752e44e1bff9d89b40d4108704cb7ada6c1c36d2c71eeffffffff0280fb1f00000000001976a9142a447615d9816e83fc4763a579965e1bdbd8070e88ac2f480f00000000001976a91478e5fb84b45767d0e16f8056b0b3c54bb1afe7fc88ac0000000001000000022dfccd5d3548f6977d59c7b50de23ac55053b5b7266dc7ff6277089e40c69bc3010000006b4830450221009b6ab9be70cc5ee818b2815d7daaaf1b987bf553aebb6e657a4abc6674edc7e40220305be626ae66f2f9e5cf0a9c95b586300f1b75f43b4d3ce8fe982621dc9103ae0121037a1f7216c20906ced708b56f4699dd4e7616a504ceb355af4021049737f8f3a6ffffffffd27117a6d9bedea429a9afe15a58e669a462b009cb712c705df1ccd2f47811c6010000006a47304402203a6933c7c2cd22ae0eae63e8ea3c708110a1ec495dc0e9471f445847885d871d02204b4de74b13180b1181d278c52e830cdd876595a86a0120b2cddbcd429cdd42a7012102618c263d7426a22aac903249263f42eb9781f7a1cf5c4ca1b191d3207ee9a2bcffffffff02f09b8219000000001976a914f7a6bae5731af376f690551e17a6f272e43bb25e88aca040dd00000000001976a914f00b08101bd21c669f58e9eb669743a31954c29988ac000000000100000002e9ff749dda3d1cc920635d8add3ee2959f4eb8e6de08892ea447b813d3911333010000006b483045022100b2524879f4df70682b0193b44f524a959da0c1c06076249a4bd659dc4b7af43702207d1014a1c1d18ef27bca1a9448acde908b9d778a6fe70af278a38d124587485d0121038e6dba49984aa152ef0235b279495490a48d3af44b1708c10d29968c5582b68effffffff8b68f63f412ef83d4d2043adb7cb199b515f790ca2b5e0936e167e228fa8d6ba010000006a473044022031d49e83a109d9b5a1c642c1c7680c9b59f37f808645fa48d63c6ba0dbeb1d6002200cd3bd2f7ebb25e5ba829dbb070a876c843669e516fda59e6dd08c00652bc95a0121029f5f4a4d9cd48691cfb8d8662193193271affd0d29b359cdfca0e1b5ec4233c9ffffffff0243adae01000000001976a914b526df90f2bb0c5830b469b8b8f96d25e127de5d88acb1f66502000000001976a9144e47227e4f9265c307be2d9c116ce3bb5984fc2288ac000000000100000002fa8bb8e245285095b5b6cc54938a1b7ca962b0ec0253df633cd8a73f30a754f3000000006b4830450221008e2081de5ee39db3d5cae1823aa28dbefdcc45103604ebe41ebfa40d629999b402204c6b9a411ac007c91bb45a89f08fa8d039dd1f77cd4ccb0aee62c7a3930f913f012103acd7c49fa50c2d6d0ebda873cf8f2b853e55fd688972e2e07aea867abefc4744ffffffffae2e01136a8e2ecf92696aefe327f9a5d1d245759f8cf9b3fbcfafb1ab58432e010000006a47304402202627cf535e4a1d7acaac7cc1d35e1e7de4a25338755754d8fe63032c35a8aad202201ea21b0f1a766f919a5c83d5ee75043a18821daa04578cd709cae4c8c00fde58012103327a03c8df819cd44719936978102598f80e0f3e2a755f6810f224d3ef1e09b8ffffffff02cf0a0600000000001976a914b526df90f2bb0c5830b469b8b8f96d25e127de5d88ac9a154f03000000001976a914a1196b3eab885462bb15b22a25f84a14811e6bb088ac000000000100000002e886cdfd7c9c1744798d6dbadeff47e1c49be1dd52646f568f9acb08d7fa277e000000006a47304402203f4c3a66f0746b942e36ffecad93d74112824136bdf9c598d839a92719549b3c022028f6d45d304e26747d48c3bec3045ae3b239ad516007b3d38badb1ea50205bae012103f0d831fce8615a857d39e695f45f247338afc87e85abc903598db38a5d628895ffffffff0fe0cca889ad4349942ede802af2053e63c51087afaf540186416a8041e2556b9a0000006b4830450221009355f5d5e0473ff99116f25735db998516771546c8a62eb5797a107bb924a19802203b80649b9fb59855601d19bd4ea34cd838077af0b1548c6773bed05230cba3a1012102d8da4b76f21aacc52fcf0ff6197c34856a18978fe1e7337b3f433bb5ef0f548fffffffff02219a0100000000001976a914bdb11212ce17c19563c95fee7c92149366b33c4d88aca7491000000000001976a914fc0abc5bdb6536b8ee53491f79a307b7e41efafa88ac000000000100000002114ae6e237a7608e1ed77f8eb4418b9733201944f547962e1bb8a7e917e47b0d000000006b483045022100b4bfa5600eb69ae778c5161e92b2ac264d51fb9d8290a1ce7c9f6385711883eb02203849342ead32d95c83b0732aea56d037e01bed1092e71fb4c5552a2fa591c6fb012102f9740daa1cc88c48e8b78e3b889af2f21aec7e3cad7d3d8ed6574572f1c31c5dffffffffa56f6719de1ae0f491e334f780a7204a833cde3a950fd150935d2fece7a83805000000006a473044022016761e99b60ba1c9599d9cfaec54c14f20582c8906a3336b1eeab77a899ceec0022054987514ba0626219ae5ba4eb0a0183830572def6a5073229853f4b96ba20cae012102a46391bd7ba047f50f67ebe58ea05b631df90cb096d0db736357bef6216ad0f1ffffffff02445b0000000000001976a9146ede94d77318a624286101afc4f12f5d66eea8ce88ac5a4f1000000000001976a9141e22b511b0ba3a65a794f58d8dfcfc58b35b671588ac00000000010000000268fb85cf40333c9d6c68099d574c189578a5a0e22b0975080ea340d99c17541c000000006b483045022100c03f78e69d3473b143d318b6b26782427f26865cb4d55011f020cf8a6b804d76022029da988e434b878ad34db92a6cb030715f9d20d2e3723ccd1718475fdfd76eeb012103fe9fa8f0045869889558c255ebba869b0f5bc2931ed2412af8dd05841471e681ffffffff1af0ca6a06cb4d3ff0708dbcc9b070b93ea843a86f67d6ef796d8b929432cb99000000006a47304402201843eff603cf74c309c6775cdd03e1089c4d47bb93719efa011be83a0a78f25d02205a6fb730c271cd701e0e7ab21bb0eae77a335a57b28063be7dd13a7c78cfc286012102f128af487ba7bd9a2c5363f0d858c796528c8ceb232eefd75c881aa43f4574adffffffff02400d0300000000001976a914a9385529fdad30064bda677835467c1059118e6a88acd6500f00000000001976a914b1f1b70012d186c87cae45b978ccf17241d53e4188ac0000000001000000025fa2f80bbfa0a59e95d5d21c7b3978ecfde71462e363c20b0f6874a732f69ee5010000006b483045022100f2f214d1eb1a9ac723799145d3f889ac5b21bce51ef9b48687d4af3293b1113e02203d891b3db82702852681a5bbb1b5ee1b0addefed37098c2cd3ece76a4ae0740c012103b5e87580b49bb682210712a2134e733b5335c039d01b827eff509abe7d0f2cb9ffffffffc5348ea29760b1ed1a0ffac627a58314c860cdbfa07864d88523f9ea89dc5420010000006b48304502206b9d8323493158adda276247574802ba9ab71a045c449045cfa6430d783d5c6e022100ed9c44471fd75fc0beb2690e67d052a0160692dd1ce5dfbecfe8da5f2a45f97d012103b5e87580b49bb682210712a2134e733b5335c039d01b827eff509abe7d0f2cb9ffffffff0240634800000000001976a9143cc8164e264fe4ae736c4083410d4c2cd30f452d88acc04f1300000000001976a914b6c945bc0f1ff79d1fce2b5864f5cdd62e0a7ae688ac00000000010000000269705dc38bcaac091786dbd082e377e071ef1c7b0f34b3e860ab97157bc9e090000000006b483045022100bca2d06dc1d7744ed538ac733eb6b107898e9395c458ae6f87678c0d4c62c06802207c0a068f528f67f046f63ca0df539e82b18441a6019add90d7fe6079db7729530121035e67c94c80626b9d35a4eb2d08281ff89d736a6b531f22c6731ad51d41355745ffffffffe0057a257aeebe7b2cdef5890ed9f7c549f7724716abb4e4e88cdacd08e061e8010000006b483045022100f1575f5258feaa5df6fb9cdb96875284e36773c26c8f689b56b82f5875049168022052ffc8bb404e8210ac9895d79569759b02bb83d72d239f67a840492ff578de4d0121036542ce5ef42cf0db962d58a183291deb34be7cf5f9a2c3fa404b43c5bb5e37b8ffffffff027000094f000000001976a914fb19253681ef5bf4ea16c708643698dd3846ecb388ac00cf7b05000000001976a914828d5dcb5c89dc352289e7d1d201b3a6ab3ee9b388ac000000000100000002c2211586b41a4bc97d01bbc2bafc4074a46aea31058c34911a7ae9e54087cb7f020000006b483045022100935217d81b8a302c4c8d706dca706a6f0192a531d0b0fd2b5aec33956061d069022036c58a8d87409387e0fa3db953b7dbd678f9bbd6ee058233b6a3d197548a59aa012102cd1f1602f26dde53fee26e989a16785fb5a4de6f2cec27ef09a8eff8f1306bd1ffffffff6c7e953c87dd495747226fa97c8888a99d778363aeadca95a1c93bde4ceb88db000000006b483045022100a7b9b1afa8bf629f2768269dedb1d6c5601fa7b8c71f2943d663f04cd861d89d022057d8481172aa5faa69c408aecf6ac67d46b24314abd1275b8bc0f91b934002900121038cdb5ab9f97b9e5c7a485d6c3b1577a2da3785e86479450b27a02e634cc642a8ffffffff02a0860100000000001976a9142d5e62586a3d244c5e3c4ecc32eaf7630639a1fe88ac6b470f00000000001976a914c6aaaede9f9d43193b7749d5c57c9ad1c4c327ab88ac000000000100000002c481e49d3c43af9803e5c2ab43cd65f7e683a9c4a582d192df4565bcd3439be5010000006b483045022100808ce8f7f0ac830207b9bc54dff1b11d5c0ffe3381e8b3de7854f1cf4fc2e90b022016b5a54a012d494438c7c04f37e75d8c7cb6fa6ead1c90323d05fd17625e915c0121030ae096c2324003b60aff4614385a5d0c78fd0202fcbd6ee74021b50fcc394dfbffffffffab402046c0ce06d4bb83206bc558cc075a3774ecf64a9d3ad1c3236143e46f2b010000006b483045022100a3952b2e1ce07249e655071d301fb4a3f60012fb38a81404cbacac9340aeecbd022029e2916ffe02943932c789635abc91dc213bc886422209f26ee6e83b513c5cc30121035897535e4918dcedbf540e47b1f44a42c2abf2918af9f83db348be90df4d6690ffffffff02599f0200000000001976a914dfba28dc856c9a949fb67a55b234adb957416dfe88ac6168190f000000001976a914e95b7436132f3a0a7a4f58b11708a73b2d597d4f88ac0000000001000000022f924c484cacc1c8008cdd196f03c7aa91a9a7b688fc0d7d16b3eb9f7a085f3c010000006b48304502210081f30eaaa2f75c6aa26dd1b6a1a4cf3a06003cf9f38e8d903752dc8bad382246022021ea28f2ae4350e8ca479288fcd07e93cde3727b087c923d8b979b2160c06904012103db1f92c07a99df028296c4dfa2f8eacc0980611d43613e279f1d71da0cebe2bfffffffff736b38a753465f81031cd2dca539625c0e785904a363c92ef57854357bc8f09b010000006b4830450221009602d7fba6edb9e06a708e6bf4d7a5d0fd4afdc82214510841c4225023c098070220037446eb63435bed4552df905f1d6a44b1252815adf3f1bcb4c5b12485b33891012103f0dfafbc9070eb84f4df5338e0a4d661e5add8e4f9d6f41000f14408d4204088ffffffff0200255602000000001976a914fdded20c0ea9bfbd6277fd8e662a6814319cedf588ace95ac402000000001976a91460cbca5891b04d6db63338d9e59048e77d4d90b588ac0000000001000000020e2a262144de4c498d3509463bcd50b6b02bafeed4adb4c7bb0d18f52cabff12000000006b483045022100fa08411f7cb42bb615b713fbe21e0257b72cb4f5f61c5a668c545cdbda8eb785022059e5a689c2c9d5260efcb0df8437d0033618cca583e0a35d97e31599156d8398012103c30c6695c1cd0570dc692a8223a0c72259377224721c0bd215cfc181cd7fdbedffffffffb01ec2d5d2476756358a2195dab0c77c4216484cc43965b95e01bd237438626f000000006b483045022100d0479500671b90b83de9eaa4fc369337b3118070c97226311cdfed89b0866b2f02201743fcf337a267c1ba5dcc1bfe186c98e51d8bc7788c1c329007bd8027b6cf4a012103a1be5261bc6e5885d1f881d016282faa9bf08205967be417085993db989169eaffffffff02394e0f00000000001976a914ef8994da5f26394a1f3b772fbf1b07a3bbcbfdde88ac20aa4400000000001976a9145c5463f5273c54c6faaff36f12b3e3d2fdd58af388ac000000000100000003b5dc3f08854409bf610c1369338329ecbcabbd30f86813ac44eaef68b2d398aa010000006b483045022100932a007751ad135aab3918808c4a262c728d1f0bdea0f8fb68dc98ed17a5b18c0220716ceff39e163cac1882f3a8abd9d7240fe426067281417f84f08129e28e73210121038ca47fb100bf5ecaccd7378c7d0d6320169d6192158f82c6940967c35ac227e2ffffffff7946a2ac77792419d935ec7b08a666d5305e6db00f767a10ac9745b67eb62478010000006b483045022100d5e5653a8f4f90f29686b27b0e3520ca157bb6f1f19c05dc2555a3b42e83ee7002205b4da1a5559e18fdfc12d9ec79cb566630bb56f80fba222c28ac4a651e0b37410121038ca47fb100bf5ecaccd7378c7d0d6320169d6192158f82c6940967c35ac227e2ffffffff8ce02cc3f66713b94f7dd4a9af382f3321c49a925b7dc33d3c45f236bfa5817f010000006b483045022100ce81963ff8916fbfe939d17efcc683b7eb2fedc3f7d9ad3e7d8427a14fac021102200534fadf789ac9c605650f749e398e40792e0939764a6b2974364d6a69461d1c0121038ca47fb100bf5ecaccd7378c7d0d6320169d6192158f82c6940967c35ac227e2ffffffff015ec05900000000001976a914a78b8d26957613f8d16d647c70213eb7e705266388ac000000000100000002c5ab219f6881a8fed6c8fb224630b44c51e9c75f6847633533d5515c5a9a1724000000008a473044022071bcf7933a1a325667060e2f689ed4543433b5457418b12e4e5aec320133966402202b21b656d4f9f099e82b8f013578d81abd35a06fa6d3fba0fa48e26ee992238d014104293397b707b5a23a9ffee09940b3e16d9dd585dbb80e0b7ef083148c492d2b32e65f878c0d3926fbeaedbe0792f8a2bd04da33f8dbf507126af5078c044a1db0ffffffffe296e946ad2212a4b4b4ecc1468a21576bcef94c965cf6e0c4a5a18ec96d2a37010000006a4730440220656fd430b4bf4a79b211bf6e82a74fbaf13c23f44ff3d4f7ffded6d70a2a28a8022048c458a111246e53f4aa47983b1ffdcdbb15c9bf519eddf34c0cee5f4c0f45610121028d15246e451c1b64b013ad3e83b10cca9805aa0f1aec81149b2e02d71b77bf4bffffffff02d0a11000000000001976a914f5710c7a405f1aeca11b79ed8203ae5328853ba388ac20a10700000000001976a914f0dd368cc5ce378301947691548fb9b2c8a0b69088ac000000000100000002aea1c43db415e4e30a620d9de6557d1ec730b52ef7af744742856f00d122f579000000008a47304402207cc4bf147281ad9a57757d7ad9f7fc1164ec781fc1b1edf6bcc7a5b3f344bfc4022052754cc113d8b741f65be90efd1bc476581860132b5281cc58aca1f7ca3a3319014104a6c93aed872a7a83556c454db39973449982278c9d38f2328de2798646909f58552c6dc6d59c6c1da81c6bb180aa08ab13ec4bf5c7f39a995bb9758d19d60c7dffffffff99e274fce738828e563324729caa0767adbff6872e728ac5d4f9d494ad3cd848000000008a4730440220527f4adb1580aca6885fe0766750c91b4763c87b6509e81f0e54b33c9cf021ee02201998d3745caa90f7d56b175c370ba6a75975a80d60276ae120f9b6dad3d13f59014104eaf51b074e8c8b59badfe96dfd9458fd07e6368ca94116d5eea4fc7976c063e89463ab797da2d51c538eebc0974a95a929d13e98edb6563287474af7d1397702ffffffff02e69b1000000000001976a914f12aa4feb7d9803b624d85d72538ceed5aa2898b88ac8a8c0100000000001976a9146d116a93760198d2f043893ed5d8eb9088f38eec88ac00000000010000000205d4d2c1e92ca4e05ae626a32e283170afc735fc60904312833ffe31c1f7c8db010000008a47304402200eb58e6e83d58cbc8f99e0eee1fc3cebb722e423715f552335095117643fe28a02202e75a991ce1acc9c9b31cca94a558bc1a9c63b822cd66b46f634882824a7c02f0141042ec075981515fc9020e95c8d474a04d520100e17c48e0c1304ec35d6e33fcc8f7b7ceb2dd047a9d92a3c739089faf2ab3aab7311ab11cd882e48de02d4da9997ffffffff5ac4ee047e29eb7050e4f2e9a43195a1f875f624ef9d46daab96cbd156725c76000000008b4830450221008c39cb069d681752b00751ad5dccd2b00fb8bffd08c9ad43ca59bf782792fcd40220520ab6e4db72270afb6195a02d11049aa5410b5b4f9c69a1e13b9729fa960f890141042ec075981515fc9020e95c8d474a04d520100e17c48e0c1304ec35d6e33fcc8f7b7ceb2dd047a9d92a3c739089faf2ab3aab7311ab11cd882e48de02d4da9997ffffffff0247631b12000000001976a914f30ea2308c4c059b40111232999ac8ebf3c5c60c88acbff40300000000001976a914af50323ec6b3b12a5d6ac9ad81799b2f3e37ff8c88ac000000000100000002dfd757919106b76174a92af02561da7d4f804d389b06fc0131c107814fb3ecba000000008a47304402203834dd07a1effce5c9e474e3ed3992d3d7a0bc531a907fffe631ab61844147540220744e183eb5329ec16c0b2e65acdbb3477655585ff1805cb170813117f49f298001410456a9625593897575b54771d32d7037612c5d8ced907eae645d43aec3994dd010d9b43db7c50e134c6bd4644477192f5ec11bd838e943a384d5410b35f6e11357fffffffff7c3183ae2619c6d6aac6691339658391e47e2cb71e9eee8f3f88fea1a1af899010000008b483045022100ff84610e9cabbcaa98bb83ed73645436a31792e993bac9a766ce976aa3a12fc60220147bb35a35c2b426625e6b828c779bc1240e26f582ee4dfd1d51f1849f82652e014104c735efaedd169c5545c2eb27bd21fb50282798090c410faac286f3658f778f1c8286062f749d1703102d19446a53c92981a64a56de3dd13c2ab60ff18646e7a8ffffffff0200a3e111000000001976a914b104f5f94cfb336281c25d23a94d58b4d6ff310188ac94ed0000000000001976a914394c27ad741f345520157abccf2097a01de4d9ef88ac000000000100000002bdf33f8fcce5ebc3a436b9c28a61fdf8002760ee67a4bdef070ccc6ebdbc4844010000008b483045022100e5b09c224aab23028920f3d053cbba686e933cb92865a4177a62b6d787bddc250220183da984c55b8f08d59d851fad2a9496bf0de4f1e921a8249e452b6777d602ad014104060aa2bb92a0ab8a996038e393b3b0a9c89d77fd7e0f79fe56d2425ea3d249bce201565386a82781d1a045a8277fb69c982f5f7f5056a79c21c8da8cbf9bf2edffffffff31718e811b1f655b2d1495a8f80338781d27826a3091a04a8c33247496812653010000008a4730440220360566ef60561ed82612096495f0b070e67daac7def829d2e2b7c23ed81397f602201a156960cf0f946d736bac5d2d76976deb3f0721f43438926f7df2e8dc16443a014104060aa2bb92a0ab8a996038e393b3b0a9c89d77fd7e0f79fe56d2425ea3d249bce201565386a82781d1a045a8277fb69c982f5f7f5056a79c21c8da8cbf9bf2edffffffff024def3600000000001976a91410ccd94064fa2e1dfe3c3a1877d296d14600636488ac3fc60000000000001976a9142cb1dd92fa9aeb9420d35bdab9fd68293a2e6c8788ac000000000100000002fc1609b18ca3fcf95d806e06b3dc6782522838e7a3dee132bc43a44b1cd74290000000008a473044022024b4ffeee484e590c6dcb573630d25fd02d7948881083d0ac1997a54837ad77702200d02963fda604beedf1f254b0d37d2d95fbbc9f062c884b1156fb9b51b67a33b01410464be64caf9fce8219048e43f1679893f36a7817a727305b5873e9796c3fca526f94b46cab4901267a739606972af59619455d380a53d821ebd6e657f0391addbffffffffe840455f404eac395dc37fbf4e068213bfddaa38f1041407b7f17ec05df3e6df010000008b483045022100fca8e796b514fcef86ede7b2bf30375356ac18e31dd73e03fec9c04c42181c7f02204fc9595d11e61fc7bc6364325c89ec502daee9c2b2ecc6ecfcbf92a1a31dc368014104c39fd3d1cb89d5be371aa0837d0e93feb5ece3bf0f6d61690ab6c1b126730b3b90bed98c19b1a4818b2edfbd927dc9d450f3c9ca2c01565c23f89e0e806e6579ffffffff0290051000000000001976a914bc8d74df37f65c832ceb7e58464326ec9ba6998388ac9e3b0100000000001976a91492251b417cdbcc4eb991b4cb848fec85bd9f306888ac000000000100000002ace568f57549ad4d9d164c87709b5ca1ba3e29395cf70f1eda3bdac0e3a5b7ad000000008a47304402203e9c28e46d3027ce061046414f9be2dff479ebe93db3f7cc911eb39102b13af10220018fd9ade10af3b712ee00484e0ac296608e8c8aeae89ead469385dbcdd03f380141048eb9167d6ac5e47a6e53f4cce771be822a615a6f919661705528e8155d8d173d67ee9f0ca409a6697f2727a6d524692a335fe6766deeaa46209f3fa30404f01fffffffff6b5bf4e8492b9ea3eb50ee8b5823da3cd75f31f14bdaabff945d235093d16808010000008b4830450221009b5251ef4ac5fb22dd22d4bf4a3751a9f344a271080f6c6d449115d76083d74802206a70be002af0c7c4f3058f74acaf39d96f9b770234404934ea81fc2cb3b5aa3a0141042f53c8e430be2cd02d415f166ca79da8fa9ad64b3c691aa1c81543b2e299b12f0007c3051e0be2ce92c41295767dacad7959c448eb660fd8a806949a06b2658affffffff0200a3e111000000001976a91488bf392c6adea62ff4dc9d6d55a7826f345d739b88ac6eed0000000000001976a9144e03e6ed32f4d0f3585dc4a5c771fc589a0b921e88ac00000000010000000288c10545d1b34990a39ec293f61211dc7a6871a4d37e0c7cfb8df792c835b913010000008a4730440220760083dd6480a37ec194e2477714ed60dda6519e555e85060c08a4fcfa8616e00220565999c736c4f7a695532d5ecb25dabc68c89b33190980c861adaa524f182cae0141049b6c72b3831db502ed62fcdf85534de12f3d56a222d993e26e38fe139e59b0d650782f9650d7607196dce66374904fbcf1baebd6b5d7dbb8096de648ee6d540cffffffff49af6fd65d2ccf5265bf11594c5c5cbb9c10301bba8f837bb19c25ceebc0e1f6010000008b483045022100d13eaae86ecad48669bf83fc6207f3a47a105e5f4b1313ca46807fd80271684902207ea29df51897b053edc4419c1a68eca6420eea7f185e19b7a5db223b5142a7630141049b6c72b3831db502ed62fcdf85534de12f3d56a222d993e26e38fe139e59b0d650782f9650d7607196dce66374904fbcf1baebd6b5d7dbb8096de648ee6d540cffffffff0280a21900000000001976a914c3de9416f7f92a40dee37a2d1a90e4733622e02a88ac15240000000000001976a914efd63a6eab86c082f65232607f41df7b3e22fb5b88ac000000000100000002e6d394ae5bd33ad5b3b8fa0541ef2adabef79e061fe512088c551edf1ce2a201000000008a47304402206c1e3387f59add5d7783ab4853eeb08b4d26e3f334bdc2706eea31a49de3e24502204166271c44c75d24a978bcf5d04ab149d033d6a16799c8869d9c9ddc42572a9101410480df6adca7edf7b6ac450a470b0a00b67eb82f7739e7b829fe3985ca2d1696f161c7a794af1da6d8e8cccd1fc24de7025e37a83989bdb484274e4f1b531a5c47ffffffff8a1b54c0a4d3afc521de01f3d9040a7b7507d73247abba60f719a967bb14433a010000008b483045022100e97dbf54aa2905cb6a48ae02177affff2542ea62b579d1415c1efc07935f9d76022062dcd24e07f2cd043bec6fcf2b6e7f5d83ee87fc33da7fe75391d4e993f3494801410480df6adca7edf7b6ac450a470b0a00b67eb82f7739e7b829fe3985ca2d1696f161c7a794af1da6d8e8cccd1fc24de7025e37a83989bdb484274e4f1b531a5c47ffffffff02bf450600000000001976a914da5dde86d69a5d9dad88763f2df4b048953c7d0488ac9ade0000000000001976a9145f2839e8075dfe25989942ad2778a6cb1f28367a88ac00000000010000000219cb00628209dd4814a28f716e83460ad240420743e46c5f925db2cfd718238b000000008a47304402200bb788cf1c128107de1a93e1e21cabadb15824beeb41d232932ac13699cd11dc02207849c4b091e3672e6f176bd5b447fa3634e83b103926ad8e856adb7e41c4d9dd014104293397b707b5a23a9ffee09940b3e16d9dd585dbb80e0b7ef083148c492d2b32e65f878c0d3926fbeaedbe0792f8a2bd04da33f8dbf507126af5078c044a1db0ffffffffcfd51f86e568b026d11be583c597a125845de6065142758e266c6dbe9db8a27d010000006b483045022100ee430a5bca97fc7ac70245c9f893896eea6cee0deae2919c8000c3e7432356f902202302d77ebdd4a1eb3ef1ff738162ea2c368216e2ccb5415cd455c9a12bd36a080121028d15246e451c1b64b013ad3e83b10cca9805aa0f1aec81149b2e02d71b77bf4bffffffff0242bf0600000000001976a9145f2839e8075dfe25989942ad2778a6cb1f28367a88ac88b40800000000001976a914f0dd368cc5ce378301947691548fb9b2c8a0b69088ac00000000010000000219cb00628209dd4814a28f716e83460ad240420743e46c5f925db2cfd718238b010000008b483045022100a73c958e920a94dc5aa2b031d9fafcccb2bbc6b426d15f56be19eaf95630b44602207f84e5367645cb86e51eda70f67b05532b62f7500785d9028e7b68eb08484bc701410480df6adca7edf7b6ac450a470b0a00b67eb82f7739e7b829fe3985ca2d1696f161c7a794af1da6d8e8cccd1fc24de7025e37a83989bdb484274e4f1b531a5c47ffffffff36412ad0920aa5c363524e445f8600f6301158d727288f56c04ebc120f7a5140000000008b483045022100b6898b1f3d301f4fbb81bb2a92de1ff3c10cc5c07cd2a91974aeffeadb2b1bdb02202d68f25865c4e11922e60be8cd4fdaa887d4275a611069646898d3e2eacbeeaa01410480df6adca7edf7b6ac450a470b0a00b67eb82f7739e7b829fe3985ca2d1696f161c7a794af1da6d8e8cccd1fc24de7025e37a83989bdb484274e4f1b531a5c47ffffffff01cc760700000000001976a914da5dde883cc084fad0d72ab4cdeb11205fc63bf888ac000000000100000002181fed550dc092404ea20275e94359f3e5d96161353811f48bdff313de0c125b010000008a473044022033dbeb610009361bcbf5f8984b2abe7fe00e3e48d93f4222d2e82a34208cf91e022056bc9291adc93cc9e3dac7edb71ec1a4e34fc97ab428ba900832c478ce89bfe101410470d0259b8f0d5034c235c829a8b13915e04c747159f6bb26e89fee3bd9a0c50f60ae594e54243fc7639e1e2cf67c6594533a993b02a85293d87a66f598540571ffffffff3d8538714482ff493bc11d89d7554d1bd34fcbec692a97ab310ef0657c257bd8000000008b483045022100aa0e9191f91f98a11ac3bc148bc63ea268973e1f479e2e0f576a90a87c90a8400220345923cee26bfb4cef2474f59a6bcdc9db96302d47dff5b2ddba3fddb306f91601410470d0259b8f0d5034c235c829a8b13915e04c747159f6bb26e89fee3bd9a0c50f60ae594e54243fc7639e1e2cf67c6594533a993b02a85293d87a66f598540571ffffffff02a0bb0d00000000001976a914da5dde883cc084fad0d72ab4cdeb11205fc63bf888ac50c30000000000001976a914ca7eaccd4ef037929be65c7706992b055a41362e88ac0000000001000000026f8ce9f97b17ef4a29d33e7f0f19a5c0d379048594311c460e15783d89e514ba000000008a473044022011420ece4381ebf2880bbb3f472a832796532fdec42fee694d546292b2ddd56002205dcbde0dfabab8fe79416d3df0a663deff9f45b7e37ffdae76e334a357cc8be2014104da6bc6a6139bb008454bfc8371141a5fb8ba6de87e9ab1578ab4c31e1b25513d6d1b1b0e66b0e39a29f6baf19f9f0faaf51d22bac02b1c07eb08058498763784ffffffff3cc8543ae0f38541ad69ac9c9d4af6acf968575aee9eafab07017b61277e7b43010000006b483045022100b95b0de07d7b4ee5d89db1411b4a455a81daa9bd1b7fe45a6a83a451be94d26e0220026b410960a98edc0521a8fd51a80354f8594eeb2cdad94f300063554a92f1ad0121028d15246e451c1b64b013ad3e83b10cca9805aa0f1aec81149b2e02d71b77bf4bffffffff0220f40e00000000001976a914ca7eaccd4ef037929be65c7706992b055a41362e88aca2200900000000001976a914f0dd368cc5ce378301947691548fb9b2c8a0b69088ac0000000001000000028773f9efa141d42f501f5cc1c8694ba847a09e1113966559a1cf96d3198687a3010000008b483045022100b12995e84ab5b91d68a45420090bbd2a4d4fdbc91e48cd0e2401d83551670d48022051b21cf5d1909f81e6fe0f5e1edf2283903e8092a1d1b24726e4823442714d4d0141045004624f44b8d7172fc04c126c2629536f533c0174aacc87070167a4b6ab65981ffd6a8e220a53198fd14d8f1bc82ec0ce8db600c7390cf2a74808cfd896f002ffffffffa38749f84b00ffd22e22de1fcf6a5bdc3070557f8bd800e7f4fde67b6719bc89010000008b483045022100feb99393b1e4d80eda329f3146208da1f13204151955f8297d7bcfd6a67e438102205220dd1c50dccdb482c3427c768da37bb71a2d191b21f4150e36a06ac273cb130141045004624f44b8d7172fc04c126c2629536f533c0174aacc87070167a4b6ab65981ffd6a8e220a53198fd14d8f1bc82ec0ce8db600c7390cf2a74808cfd896f002ffffffff0280841e00000000001976a914270349d5f1a856c2317702cb4b8c9a3e31f76b4588ac50802000000000001976a914543027ebd4cc39877b31631a8b616e5229bf043e88ac000000000100000002bb6e625354fd3f403717fb5fff24b4f00cf5ef52d4f61ebc53e56b26cfb5f164000000008b48304502206e7fd08c23d34af8abe9abba51b9015ae59828e60d60ca4b332749c652f515f4022100a149fc9b3cbd694fa6b4ee658c97767c52405e4142cf018b428107fa2bea823701410426f96e96c52076004e0bdbe21485f69512f82d80980511d76e3b67ade5a58b2a3aa0754c78c2dbfedf89b7439268c02b58bbfc40600db582c2030bfa907408b6ffffffff3e8e253918d27342899a518d083278646a388a348fd94dac0281662f5266f952000000008c493046022100f348ea07d739eecbebc93656b4185f4dbafdb3f61e777ed463d36733ae1c5ed4022100e84b7d09d6bd5b192fde407a27b7f8114b4ec6012d3bea28d760d64204e2acb801410426f96e96c52076004e0bdbe21485f69512f82d80980511d76e3b67ade5a58b2a3aa0754c78c2dbfedf89b7439268c02b58bbfc40600db582c2030bfa907408b6ffffffff02610d2900000000001976a91454ab536bd2c85df8ed88c3942427c415fadbc07488acda351400000000001976a9140df75ad11969c404099bbcec9446ce766a41221388ac000000000100000002f6d5f482decf065e881e3b8dd18c3d53ebbeb82e24947be82d838f06d4d9938c030000008a47304402202718a4644f526346da9f44c820607f162921a6e6d741e2195d0885d238dcf2b302207811bfdc67534ffaf9bbdaf4da3f47a88cac37c11cff6848ac04436abab640e6014104689dd09a2134c5c6ada1541fc715c292395a622fb59096b942e2dc13b8318cec2f3cf1def51074ca8cfe6f2ba654e37430b01797bbd68d529a49ec3cb23c80a9ffffffffa803e6094b44ee1049d88bac8910f4010fca1b62fbc845760d5cb63a0d80bb16010000008b483045022100a602dc66832a3ec78fd258e0251a106f8467eb0b83f37bc4d9997b840b8dc1c1022000f04d410a6736330da64abdb7da91f7dd21fe57ae31f292a3b43e7e9f22b6cc01410452fac25ea480b3483e56df06f8f34ea12ad9f6efd071c792d8d64355fe4da6dba9750cdb9003dc9744042be98cfbefd42429c1b2a2e46f4e28345bb493ee1e08ffffffff03b1210808000000001976a914472e53c0a19483ab7d32a9debc65721789f00af888ac138e6208000000001976a914f562781e972686456514455c29b0d0c21f3abb7c88ac94ed0000000000001976a9144a36d029c3bfe8bbbeb4206478b1552b612415a088ac000000000100000002b85900e96279f8be262c776ccabce64f557b4021e8f671324a3e92ff7adc60c2000000008b483045022100befee20276bfe9a3a9e59965b99039180d3fdd75afc6d571b1d7d346a75b04cf0220778c9c5d793aaf2284c2ccdd52cb2714978b7a13351a73ca4be693525bfffc7a01410479ecc33109084e334c338e31ad4282c39440de359d946d4e66671c6478f80ba84ce6949681ff2e52f8e3f2e05080cc25592a662079633c1d75a6be3dae1b890cffffffff33ede880a22775417667dac13d94f2ca8aab606841b155b6d8a8c9d85ee2283c010000008a4730440220726f0140a3408d1a276a74a31711c7648f611c794db34bfb378c799c0553624102206900604a95f4b24ad32970354d793db0ce535b485beb9f47256b9453f27edfa00141049f1d8a77c4227b75aef9748e02e3c81767f4b551b0bf4d6469df1c0c87e4955a1c036495bd5017143f3b7489dadf5ee564f1d8627ec01e831228d2b557a70e01ffffffff03a0860100000000001976a914bd314932e2643a7fc53ea6e9db8f8954903c687688ace5540100000000001976a91405d99d15e7ce574767642428057bcb8815878f6f88ac94ed0000000000001976a914488402e337a27c5e1990f8158d5621d007ac996d88ac000000000100000002aade3463dfee6d1b8ab52537cf7400fa4768d624bd48528c81d2bbd46308139c230000008a473044022029a590e9af3b0eec2f7e7ab08c110fa0bae0cb8272709ca2e56583c9f40dd3b202202812625f2808d84df406babdb26bddda1687f1bb0440585513efa6cc501d34f701410479ecc33109084e334c338e31ad4282c39440de359d946d4e66671c6478f80ba84ce6949681ff2e52f8e3f2e05080cc25592a662079633c1d75a6be3dae1b890cffffffff550af905cefb299a098f5d9989587c19a7653753fe8232d7e003582b562deb1e010000008b483045022100b730d3dc5c39cba3fdc3831012680e2f6165894e44a8809ac1a06d90bc571a5002206f5007f1eeb08c73031a4f6147e4a57224109017777f8c926b0c3045206559e00141040333fecf212d7876c9e823dd139d3fdcc6433f63065bb6df4e6a2370009bb56970519ae201827a5aba6b4f7f47d0b72c9910763c7aed7419728140cf5001ffccffffffff03a0860100000000001976a9145ca37b62f843a168b4c304dffa176e888971ea4a88ac44480000000000001976a9148ce57da128e76afb72f35d6a1770178af5fb8fb588ac54510000000000001976a914a63fe52b9a7d54c8ef07a79bff9acea31d1fabbf88ac000000000100000002add9ab6b125165b9a08d8f709ef5018d995f90a4bc2baf661bb9baf60c561bab000000008a473044022004dea9857ab3543ef685efdedea2f516a553714edfa7299094924abd986860390220192cfd3c805d2838216eb2cf7460068158b27e5b12b605a7a4afebf497e4eb69014104e42aa0dc9c3e95e824fc43f361af768ac9fb3958d85dfc8f2d9c3642b41cd3a387c388dd6728b75784fb00907181021b01f75307cd1f55c71e30e56cb875282effffffff1de496d83543d397d5f7a85df548a2f2543ff5bbe35d39823d4873d1d6fb7550010000008b483045022100d12d74c4469a76b6d5a68e4dadadce2f04ffa09638d9e073f42c24eaf9e374ac02202099f90bc3026684aee486fcc5b399afc41092d8fce72f4805642d4a74ea08470141047e25cfe8a868e82a054641ccdd75c7643ba009432958b185a709d2a48129cd13d98f8d77f72ddff26c0c51b643e43066bf9a331ee2b11da6e5c751481e24f6faffffffff0392b62c03000000001976a914f33a3446bde3bb98efc4676014c2792d6d09bcb988acedb61000000000001976a9144a1f68109dca42d44ab4df8d4bcd58d32ae132b488acc6e30000000000001976a9144c9864fe2946035139773bf7c107247e9929627188ac0000000001000000032db47904bc4900f297e8d75c48bb5be7d21611ee57cc391ee1ee6de3db15476c010000008a473044022054e1ed8ef08c7bb0af9b3d9e9dc57d88bb0d158c66228c61f286c5263362215c02203a17fee2f7eeaa8e16b50a3f265e9f883a407b664d08210b85661f1fec53ccb101410464d46bb645d6f9eca350d806709e00f9f53d3357e403d5bca1c71230d58c474e931f896191c98b2028eafe3e3127d3cfbd600b78442fab715fc0192acfbabc36ffffffff60f73776facb40af231e413586187fb90110b5ca86e95ecdf325f7d955512c44000000008a473044022070de91c247070ec7f5618abdac25b0f0c609d685fdf53c745e86dceca6076d9e02202cee8d97f481484072016579b4c2beb9ab9a166cc31bd936883ef4a0a574a853014104abcb40d71a3a7cc16f9633a8a5d27861d53198f0f06d5e3260d6a2211fc25e2adc0dcbad7ed6e022ca7fc2bc53dd375eecba2655955cd37c5c2ed273f3d38a31fffffffffc0d2d353aa040a2408cd43c467a7bf23a7edc37c0bd6a69529bc72411203c5c010000008b483045022100b37e88bec14e072e5854dee2d655828e4ebdec7a7a3c62411e20b4814a01ac6702203318715db84c93dbc65f0a20909b36000fc48fa8d057c6608fa692d720b6fcff014104af3d0ffecb4cf642d1b73e8096bf73d5d5edde746f7b2f20ff3e684ce8162d75eb97844b0c1fda1932fd16619981f365864b5a84156506cf7bf33ebed0c8d7f9ffffffff026ee72d03000000001976a914b15131337b9b3d290e6e4429e1534771d55e86a688ac2b740200000000001976a914bbb737e6444189401d4d8a98c08afb399cfb205688ac0000000001000000021b06fa54ea348b0375d6afd257c3bbbac50ecafd5e0e585daf3e8d87d89ac7ef000000008b483045022100ebd9cde62571625523ad4c0a4d57b8e58c5d6a41e761cf2e396d392218df098102202dd41ced1104974fbba8535d81afa6d5595eb7a49a05fab3ca8e580056b0a8b20141049a7894a2b9ae3451f0f13cce15315fe69f31bc3857a6efab818b6e1617d962f6899a07602bf35a32e954ed2904596739d6904401fbd9dbec59b665dcbfa643daffffffff30d77fd44adb739c8049987a5c641103b4561e320f9c1a2d3900dca7e670e81e010000008b48304502210088b75ff97f3bf4c35e1eee5374c36cda60d801858cf9ac24fbe668b1b5453dcb02206b92d708003ddd84fca36896f334afc9ac2c3317745a28f60ac7c807053c3e0501410426f74a0bb07f232eb875de4f3f511bdf52ec2f92f9b029b70a3a7537f600e5baa0fa515fa5482708d169537b87972d7b63df73c23cee564022546f26772aee6fffffffff03a1191c03000000001976a91436cb7c1d5b9c52d37b24fbe8ef7b36dff93e656288ac96641c00000000001976a914c87b9699a7f45b81fc8ea5ca1040445be3e4ed7188ac94ed0000000000001976a91431a5070f13c596d6d70d5653be2ef861f8b0963488ac000000000100000002e265ca1488f1e4f826e33f996228eac9214516dc28eb771d54a61b6b4a1240b2000000008a47304402203a89f205597cd212db981dbde2143778ad87fd35a9f06d1ea35a09e3488cad9702207af35319ab5b6e32a703c0e2dbab32bce8b4a8337eafca60b6b3f163b5a4e167014104c050050a33240b943652854625020a92a6e4bb2d8bd9ae9ae5f1c092d99a10cca228c2fda9fb87b5209a3b8a0eaefde464a558e6201c9abbf1079233e43ffda9ffffffff49608af7672697979b0115fd9963e54400b246513c3ce59fce75add70181f8e8020000008b483045022100da744899eeb5161a9d982f1bf5d330e6119271ba497fd7a3555c72ace127233a0220744a1b9bf3a0d19e9c4f5d2496e76c21cddc9b032ddad9604cdbb4be0bbfe165014104f765397683b5af62d01ab2708aadb90c775e6a2cfdbb386f03f65703097f961118821487aec80af67e3b9707c1011b5ecf83bdb6f9e2f93f1a5f283fdf9e0bf4ffffffff02100d1c03000000001976a9143f8eeb5da54790785452145a94c4f2eaff78cd5088ac84c60000000000001976a9149c99dcaef83ac9ff5e81ba6c4620f924877d489a88ac00000000010000000392498c1329832ddf168765dd5325278966343c2f788e0fdbc375ca17a424ba59330000006b483045022100cba4cbe9b5bb2d6f312a1658552ea71bd6e78cafae9f748e870e0e6c79bd7e43022038963de40612d078b637e2a801d7d8a3d94e9b052d614bf202b6c9ab82b20a3b012103c054590554fc059039a0519ef407c7336178652930d09c620dc4fefec621bd1bffffffffe0d33964c2b8eb83ecf76c9b25b79595d6f9f44897c69b1f522d8b5c7cf6c86a000000006a473044022016174b530395460e770593f6b913aaec4d1abda9a8c4f37173f7148f5758b3e50220506753eda9bcf96a606b6d6b1801d888593d65bd925aa9fa21bba845ff27978b0121034163ba3439c948b2762906c11ed0885074c9d45479a830228899537357089086ffffffff0322c9836888efa7f8a54a743ac34780621a95fd6d76c52a7f9998be9c64b48d000000006b483045022100972d2f15136ed4cf6a806618b20099af99cee15d599d4a6980d82427916de31d02203d0b6e5967a1c8a6dea2ae2b516d9619a91a796fe172892fd38338f8ef5bed8a01210391dabd268cccd7c13a5f527bda8d976514a298a59ae7e093c862dcefd3b6dd05ffffffff027ea1c30b0000000017a91464a0363d19b5913ecd7fbfd3d7d0fb0b1227a39487a201ca15010000001976a9145bd0ce93ae8d518f715ba2892c3bb433b2554c1688ac000000000100000003bab657ebdaaec260f39698e6f3a5901b4820fa1b1472f0721fc7750cea59e94a010000006a473044022072f0461305904a2017480c048e8fc1c16af970e06fb18a4f882d71a22f73cf270220162404ecb3309b9797c349b3a0a6c7e5cf717a756b23c69d26877ba9d8975093012103f5af354bc11893a5d142a7dec5d31223fe180f3efb07219b28ac8bb78d2432b1ffffffff8487c8384bfbfb052c400d4202340608d4f8a429541a4b2b2269939057da9042000000006b4830450221008806be3e28ed4971bce05cef39916aeb7c8c37cb294177152076bcbc95d28aa6022057e7465cff8ba1e35b5e4af87cc5d97e8c35ac5ed53eba67485499a01d1f5190012102a5f1c6c743128c2bcb507ba68738e8116f8e0c2421e7f39955b677f956cf0e18ffffffff15f622e8919049707ee68ff4fc8cc70908e2833f772ba2e3dbf5548f90b3e250000000006a47304402203e3e04e7b273951d7346d41a9c4e9132092da7f80326dcd4c57f29fbbecb88240220017c93e80c36c6c99e205149649d61f737f7610b089a76adc7352613994dc231012102959683ad6f667636d6ac0583760d785a83a538f4de67e610c5adf01004eb5fb9ffffffff02c0e1e400000000001976a9148667a0b47a63a67ef38b833a589e333acfb360f088ac10090500000000001976a914b648c6f166a4c321d665b62eb5595bee38b6bd0288ac000000000100000003d662818c9116036316d5b1ef415557c50d5a49d1d9abb1f002472d18ea7649e8010000006a4730440220204cf8379d64fa04839ee58f8cf1db5a7d13d8a5289c29cb0916248b708b50ef02205d7e1ea13f1e65bacf68ef59cc750cc569f95431f639a1551dce62d2e56025ea012102bff66d64e5fde31244ecfb168b8f68542bc85bf323bac9e07e733af2dfa1736bfffffffff5443c88535d23f62d80abd7d7aa009c5c3705559285481badb71d73cb6f4ead000000006b4830450221009d82b6cb6dbe43dd1a03412432862b382d8628adbe738370dbbbdf72f4586c7002201fc44f13d788f8aba7de4cc3e178727c407620c9b658d82549e4b545b27e7ec8012102bff66d64e5fde31244ecfb168b8f68542bc85bf323bac9e07e733af2dfa1736bffffffff6d5c97e4ad9e977ae1f6c42d887a5a670f474cdfd1451d5fa6c13962f81e420f000000006a47304402207228d53e4c89b083b2747865aed8971a76924641426e88fac89329d59910190002204311d07f3c6cb13f9b32dbebaef23227f3a55c2b20446a2b3b3286c3f8327a82012102bff66d64e5fde31244ecfb168b8f68542bc85bf323bac9e07e733af2dfa1736bffffffff029f513400000000001976a91479ac58c0ec17d19ef2cb7853f4dd6eb4293a00b988ac7c782300000000001976a914c387c3ba41c54daa3e01eafefd36838b5fe8a27888ac000000000100000002e4fe9a7b03bd1abdcfdd587402827bf5f02fd4bafe1758049e1557d24adf023d000000006b48304502210095825a2e491b737395c2c5cade58c7a161ac8fe729858de307f9cb916f79647d022020b8469f31210e54917c52c0aa0bed7d04ca1bf5e10955edafe363ef99039691012103d56be50a08ec7d8ddabfa78590188d24f98ad437e2cfae7b7520fa062021fb5dffffffffe01f36cdaf5cece96e40b6af47b7a1bbf83525589227a3666473b49969927c2c010000006b483045022100f8549405d53d58bf2afbb4520bbdc071a31727c4749ca2c31b537599324b5669022021af2323ecb749a121c37576a5531cb65b32da75af5faf75942058849ec586e1012103c42c432bb89d4f541ffff5772871cd897fc149adc5a811d86985a5adc89aa4ebffffffff029f513400000000001976a914da53e43b441ea706fe63fdcf5e360463e83dce9b88ac0c1a2000000000001976a914ae806ee5fcceac6e7958c0af6905c37ad0ff1b1f88ac000000000100000003ff8278a55a71e2b4b25804f8c4e87bfb7286ebd077129e76effec957f048738d000000006a47304402206b28431597cf59d64418152d67b6ff14b5e89708a5d14cbfe505bed76c3a7a53022064baa4217cdcd76ede50f78b15ee9b63639bd2f63a7797e1f29ef31b34fed1930121024222dfc7ceb3a0755306a68b1c77c3147a48e6158943c38e9b900c379091441cffffffffa0037fdabd5442aab4b95ceacb22526b27b77844e93aaf6ce040ccaf9c64dacc010000006b483045022100fc111908aed79a663202225768e82083aef3f2e9eea707edcb331fb1781084e702205eef6812715e4cb597897c7192b66b4637645fd6ea2dcef6688da1640e3f806b012102fe622835f43bd4f78cc465aa379f76a21fca153bd421b4ec0e80fd802e439131ffffffffe72eb6138c6f8a05a1699797f6df1aedd954881f29ca4c978bc66cde321d2664010000006b483045022100859ececf2aa0b8e43995bc3abf94aab5ef90269cb8434048bbb97c7ce8fe31a3022076d92871bec8840c802ec245b2d028706cb04e0c60373f1e49158c516e9b08470121031136babf6e01a0ab7a1788e83e9792068bf2a4b1bc92a42601186063f6ff5e75ffffffff02c6a84e00000000001976a91407a5820f962325b4fe9d894c9f721cfd481618f488ac005a6202000000001976a9141e13f46b4fe4ca9020caada60df641c80ad82ae788ac00000000010000000370a7045990f6d00bfbca584703493bcbaae577fb62287dfd98367937d152a6db000000006b483045022100c4f1da6fe9ce03e908190886cd6627e80a59c4804cfae36c765438e99d33592d0220633eaf175453ed61d07be3c4937d35e835d27c1e2d8b1c36e60e6ba07f2b706e012103eebe1f21a4bbf4a026c9668a9dffa6285b09915a6da4abe6ec437004da8e3491ffffffff9c4864a6a744696a75563dfc9399019af8a6345d9c2cd7171ef281c04ac275d3010000006a47304402202e09d36241a4dbf105aa65f5e56f5473fea8198d001416a96fe2f7be5bec00b5022056af0f20f48eefc8d2d3dd131323c7cdb0488f30cf7dba0721ba0b8c3e06ce61012103f7f2917018fae7de83f0a04d2f73dba3ae39ec2c3d5c6b8ffc2f0024f5b33cf6ffffffff76c1281fefe8c53f2546ab3da5f40060d12cbc6d5e14253c63c50628c4d121aa000000006b483045022100dd9598b600b1e014a785a70a624522ea6672114f2c74653f4ed65c30c91161d602207e9e10918fd01b9f71d9d3698767c3880b9a33b8843c9c80a6a4386455a94d8201210212e631428341b5b408fb8e1001878c2495f1404722d5b2bd4fb06569eb58c706ffffffff02005a6202000000001976a914a94e5cf642c1e100b7777b789c07c614f353b62f88acc4450f00000000001976a91450c6cb882f47c64329ba865b9055ea95bb317bbc88ac0000000001000000034e5e7887a22d54d87d7e75c2d297d644e813cd5f54d8be892c2f389c86dcb7bf0c0000006b483045022100df137de8f526bf3cf57e8ed9f6c762cee6ac8f752d1e50d71b9bab98d1b8d0f202200633505ccb7698ed9bd1854fed686464a89624786f9cb52a50cb64cf81fe9cb70121036def04e46ecdaf13ecd6ad3a5db9647bebe79150179b867d0c0a2c760aa8d9b5ffffffff9123cfbfe058749491bee5bbdeef7dc5810cac18e41b1eee672c94c3f7a0b989010000006a47304402206b6d2b5450a98b534519abf7770652e02d927c4501da7cdf8b1afdf9a98e65de0220195edc0dbfe63fc809c00ed46e95dd0cb8766eda8043f8e696efca5a52f468bf0121036def04e46ecdaf13ecd6ad3a5db9647bebe79150179b867d0c0a2c760aa8d9b5ffffffff78f434073747d95b0e7528db9fdf01fbc1f7a7cdc41b83db2a5e74bc13926307070000006b483045022100997b921015b253701c072d3b4afde5fe4e529cebb9e2a1d7f37736456184574c02204924afdd74d8267d3f5731ab6eceffab4ae03f0d5cc60b1464837064bb68d77801210312346d4ae2c915936986296c2c42c96f1fbfa931832af9b475f911855680f08bffffffff02e02bb002000000001976a91457162eb62138da24afee34646d9ee37de68546c288acc918a604000000001976a9148ebcf60d674bf740de4c61b39cb0a39c57120a0688ac0000000001000000032395e5d3ae093f701d604b23375ac44bd31aba63f61811e1a0d832b80847835e010000006b483045022100a1b23c4dc659eb71a34bb6626627c48d0594231d668730947c08c8f7d0f083ec022025e43b87ad01921c526d6d0c681420c33c098b43db86d88c3f9266205099636801210201676d6db697046ff852d66265d1343d8e97173ecf81940238d21a61763b5e53ffffffff4be714871033b0b7bff214d2ecd3841d20107f65e8095b29f54268e27030447a070000006b483045022100d66d8be96a3439bf4d63ca8bf49ed9cfd54268ce93db4d45e4f95bce6a2c924102203e3482c196206fd65a210f6d6221a967881dc92b01c2b7db5469dc1ad56aae2e012102a42d0ee4a818fbf92351ab0148580c159366fd90b95ebe6beead2db22fdcb8dfffffffff86d4f00e5528d34c382d32b55387b5f70f48758351ef67288d7d729c2b370e06120000006a47304402202b0d08309b5d56bc78926f93e9a4fc3dcd74a32499c76b7a83646279ff40462d022047d2868337d08775ebd31fc0ae62b3318910fc598b9e0b19570b5b2ce83971b0012102a42d0ee4a818fbf92351ab0148580c159366fd90b95ebe6beead2db22fdcb8dfffffffff02b8480f00000000001976a9143a93f10fbc5d799710e31e5edf01ec444f0ca51b88ac40420f00000000001976a914459cef560e3a369916ea9ea3ea9b707a053bbff888ac0000000001000000039d91aaa66da8edcddf1ae6b6260329b814489e6b0aff46e271003cdde6cdb4ab250000006b483045022100d25724b6514d817372d10dbd2e71a016aff7ddbcd8a4e6b3e59794912578c64202204b2e62af5892801804f14f804da633f61da891c71ab2b305cdffd3c4c2ec8d060121039c8804ab7b479fdce96dc4afe21fa77cdf31b4d15e8312f11adf5c0d02a3ceeeffffffff3237743d3b1999927d4190e1c452d9ef98ed8d821d03f635b4b15e71e1dd5cd6520000006a47304402203e6efedd80b5ff47dd02d70b67f0396b6316c1b2fa0f18e0e7104982471653120220280c3a8a0082e324fde1ba0ccfe50890417301c7e61299f7bde88507ed12137f0121035466ddf546ac8b55de30f9185a5c29ca4cb653b123c6aa20aa310cf23ab7114fffffffff294c939ae46cd1d247a2328c4179ad519de6766b5de6f1f5441397943925b1de010000006b483045022100894ce5326ad352ca174efa4029b91bc61a683fb0537310e8a86f83c764d3c0de02203ca7cfe5987ef468cbcbd3a904d1b980aa57036e5f3021377b7b5376cbdc380f0121022052d131c1afec8603217ba8677caa9323ff8d36bd729449ed69156b9f7aa674ffffffff02c91a0000000000001976a914761f7cd356e3395f23932d7bb337ea89866fc58d88ac7d500000000000001976a91428046bc33c89989db5c81a62a66c811f4c20b69688ac000000000100000006edd7610a7bc56727561ffab2ab4753925d6bf651ea27075ee85a1cd9f41d0608000000008b483045022100d48ef657d06fe457e5906e4db897c33e71398d2475ffe4d7d6725f469532add60220422686a3f0879d63df302e89af19801adb5bda0b711592097bfb5bb0e15a8d3501410423b5f7b2332ef81a045e01c67af6bed905ac5eee3a1712abcdb0ec6d84a8173c75b39c962b636b1028f707697c55a5a6e58ff8b43d28f9bc63a70949a85d18d2ffffffffa37bae54b5e8cf1a31f0549e0466e06c45548ff903da468a5b31904efbb255ad010000008b483045022100c961d69903a18b5870179debeba05dcffc172123dc9750601997d2f5082ae4e002207f009395775c2578d4bd7fbb774e344f38c05d3462bf2b6faac4756f7ac77630014104d4a6240fcf510dbf8f4bdcbaea74dfbe4b6d7344db67c30c2fbbc2e9c24d204ce024c00cf0722686e4fafc10f153b1dac6f260ea7307bff9b8ba4fb0b2c23a88ffffffff22b5f57dea707b4bcffa44f628bc9cddb82797aa2f0fe054bba6e01013e39aee000000008b483045022100e18b753345f6816b4035a361ee20f5ce370b0aab886b464000ba3e83fde250b902201152a1f130692561b78576861ec1ee93f9ea67b388c145ee29c88cc6d99f98db014104b4a4c2d644bbfe5545fdf00c34342387710aa4a60cd1194e168305f775e7e74641fc7529c2fbae5bd5cc3733f6866302a748d9b84c1a1a16e949215a5670fc6fffffffff51b06c532c42c185b91f04a2a55657bf6eeeaba35ac48bf5df977f7e0e46d69e010000008a47304402202ddad561b2a022b6cfcac80d9a17732858007eb46b082ded9736510c9c3d907a0220035c63609d9e6b6acdf2d3440c6fac76409c5621d6666011f1df1c0a59dc41f20141045c4906d48d260ed65be33c99e5cf6d381d9400aa027f1b6a2cde78679247cbc49f4ea9eca853668702797732f612dfa785b984185b8362a0662bf6de299fb98afffffffffcb9b2f6ad263eeace1ec1eb653ac873223e49f0e377a896bcf6040774251cf0010000008b483045022100d2123b325f58f80820c2ce6ee793a577de3d484afd7db8fe6e10487ada1d094c0220532eb8d3be9df32f7365bec27cc708187966ea9b7812c58d09eb94a7ebbe311e0141045c4906d48d260ed65be33c99e5cf6d381d9400aa027f1b6a2cde78679247cbc49f4ea9eca853668702797732f612dfa785b984185b8362a0662bf6de299fb98affffffffb95b40bcc0e2bc28e20a3cd237642f52c1165abc9727ce97cb8494a00a0720cf010000008a47304402203ac8593e88c20e9d3fa6e6a7797c5b6fcfbd0a837db3b59d3823631beff397d502206c01e6fca945d61b501e0d54f895981db4f560e35a8e90038203ac88eba8e28e0141045c4906d48d260ed65be33c99e5cf6d381d9400aa027f1b6a2cde78679247cbc49f4ea9eca853668702797732f612dfa785b984185b8362a0662bf6de299fb98affffffff02c667be04000000001976a914a73e49d9cf3d61329cf15929366761fcb3c0efe088ac345fa800000000001976a91406c06f6d92846223d090c353a01c873eabb2f70988ac000000000100000001888ba4f9bb01bf9d18bbab26c25f4ce989f799650e84c299bfad14c199a44583040000006b483045022100c189a9933b6e6fd4a717bcf9161aff88c0cddd698cfcb08848ad751faed45217022079d4ad9ea936c867f0009c26be35ab04c59cb10cd0b3129da83a92e8cc58feb201210223082d95d956cef3053996ab5eafb1dec04798401a3f444fc56a49bbb8787f49ffffffff0d2b270000000000001976a91463a19dd83408c8bba9b11b1bf3fa549f57694c6a88ac16270000000000001976a9146ec41bd2140dbeebf4a0dcc1e156d03382a7d68388ac1c270000000000001976a91490d809037623cb98d394e49b44e993b541366db688ac11270000000000001976a9147e80f64281739d87817f3e213bb90046d8fdb31c88ac17270000000000001976a9141cd0cb02580147e8d9d7f88e1bce63d723f9bb9788ace0270000000000001976a914556fe723c062476a9495ebbfe5c85d7ed55de11d88acc0270000000000001976a914e590dfa70eeb62a34590966e4f1fa55341918f4688ac5344cd01000000001976a914e03f6429b25bdccf13feb78c2eabf725919c1cfc88ac1f270000000000001976a91473923aef34dd69f94f2a2660e24e51bccd58ad3088ac11270000000000001976a9145daf72c8ec988397d3dd088799d61bfaf8c8ae2188ac17270000000000001976a91455e4a39188e3bfca34e162190d5da797299f913688ac0c8b0100000000001976a914a738f17c9422af56422e02099a6f7737ce637fef88ace4a50000000000001976a914e43bc2ae3e263afba96fca24f991b3e8ae6177e488ac000000000100000003869d5ea0650440be0334589d98a58f82101d76534e6a19eb5f22c84e9aae63ca010000006b483045022100d8e793670eff5f3a41dd083a5190ac0b5bfe10f67e0a18531e29f4ebd2f447cc022076289432c5dafb84a1ad3a922ab1e91a8d6f836d320dd31fab1095a618c495c901210329802bf3d593bcaa9ec40d1cd4568782e289f3d4cc28e622ada5517efda9e728ffffffff5c335e6084c8f739a9ec0975810e409f2f14627f622923753452c0a522911228000000006b483045022100d105cbb1f6fa5af93f719619d6446423c4b4a96f236a70a84a39fdad371ed8e60220239117bd6c601b58162cf45a3d151d5df645adccab95f57c2912442110001be001210329802bf3d593bcaa9ec40d1cd4568782e289f3d4cc28e622ada5517efda9e728ffffffffe12ace827af1168b1e055149893a5cf40ea91568384236b7e3a52d9381ddbf2b000000006b4830450221008377dc51c6f1a6810dbb38973314ff289868a70d03ae34b1b6e403cb01a74b0502205ae5b9512d8f199b2be4033f672e934107cfa29a887648e07670e43d4e3a729001210329802bf3d593bcaa9ec40d1cd4568782e289f3d4cc28e622ada5517efda9e728ffffffff04ac020000000000004751210329802bf3d593bcaa9ec40d1cd4568782e289f3d4cc28e622ada5517efda9e7282102e970a940f8bcdfb9e9165fe6de05c94b9b65f5bad1e17cea44b4099640874d3452ae22020000000000001976a9144bfe5fab8a0d3680187266ed77260cbca5ffc12c88acd2810100000000001976a9142d5e62586a3d244c5e3c4ecc32eaf7630639a1fe88ac22020000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac0000000001000000043fb289a48460716e4ed08da7f40d4ba66b16a28c77a1fbcd0cecb226ba9b95ef010000006b483045022100eed851fb6a7ec6c72588cc3e0d3ece38766d60dee4ac4ce55ecb865ce6d1868102201497abfb12e1fdf35e9eca23aea46f022d3a0feb0ca3267a8b2dd2fee003ce11012103c0a948938917a04fcee738927de1e769ac3c296ef3671832bce487f1689029bcffffffff022ba6eb9681568b49f3772656caf2dccecdb3fa0843eb6e77f2d0def34704f7000000006a473044022028b12fedad9a4d809b6bdf8897aa8cec209286dcc6f18db3f0948e59c3aadf4902206d142cbd57d008d016bd2fd9a7338fbeebecd3efed679329775134ab0ed880da012103c0a948938917a04fcee738927de1e769ac3c296ef3671832bce487f1689029bcffffffff841a2e91611714993a47566632e29faa1a1411b92bb3d3a35c4997b3f6de8c97010000006a473044022023c3b45dd6274055e46310f623ed00a3b9f3268fc7f31eb79c3d9ea266c2e54b02203b37dd29938acfdbcec421dc0bdf884d34100e9c04e066ade4a01e93dda40938012103c0a948938917a04fcee738927de1e769ac3c296ef3671832bce487f1689029bcffffffff65df3232638cff6bff9525ae6b0d7c5b563d06ab657a2b046d7e2dc00054f520010000006b483045022100dbaae1667bf3278f7c7ccdcec687393b0859c3f742b4f87f8a4070ed739feba30220620066ae3d31ad0eaf061168a61f6e103ba1096b3b88f3eb5e26ec7453e656e1012103c0a948938917a04fcee738927de1e769ac3c296ef3671832bce487f1689029bcffffffff02b0b33200000000001976a9147a14361a09d777e17437d38cf7f3e5e46f65cd6088ac21510d00000000001976a9141d319c9e95533e797a0f7886ba66bad810b30f0d88ac000000000100000004539796a9f1331b2171f0766ecb43d4db97d748e592499019b2dc90edc049cd84010000006b483045022100d97d5b8455320e04ffa9ba8e66bf3d226685581dc224812f0696e7e1820d931d022033dfac7674efcf93db65b365fcbe6cbb541b325b940ccad89f8ac81fc7626deb012103fcf4acccd7c34e8182d47bec14f77f8992a77bae1b19753581ee16ede9dbb713ffffffff0bdb924289d30abf1ca1ff5be9dda72b272116fcdb1b8324e10c50873bb3f59c010000006b483045022100e065599f095ac58d07f844c7b3df86f668207d9b6da35d8936fbffa9a2e1573002203c8c957712169d91239082098d29f1c9726a600ee398e43faa094ed3d27d09d0012103fcf4acccd7c34e8182d47bec14f77f8992a77bae1b19753581ee16ede9dbb713ffffffff76cd3b4d3c823924f56483f8d3657e0884b91766d533881c082e93312ab6075a7e0100006b483045022100b2cadb8030f826f50f10c3ee26bc5af3537d41b07fcc7264bb2b7388aa9c8923022017ed24099f967db3b0d61dd8c8cf15e0ff3447b7c55d34f32a854113462228c5012103fcf4acccd7c34e8182d47bec14f77f8992a77bae1b19753581ee16ede9dbb713ffffffffae740291fc71add2329a65c2503de2275ebb4caa3f44be304d2d3ca35ee79a1d010000006a47304402207442f403d042a7fd464bceac50eae27a6cd6e76d82b6452b1a2de78e55345e2202204fc6cf74c93ff0bffc9cd996fc914a7912a17a756cd4cf88f65af648344b027e012103fcf4acccd7c34e8182d47bec14f77f8992a77bae1b19753581ee16ede9dbb713ffffffff0220a10700000000001976a91467dc78286bb047d1fb34b61fb9596aef0377d60288ac5dc21300000000001976a9147d5aaa79d6cef73bdfa2c6d29ae0bc163a029b4788ac000000000100000008c7493c5a2b1cf714ffeef7345d4fdc844b5ae78caa497a0b5015adcca84e913f330000008a4730440220379d25f5499a8ba349ce13218aa7a4ca9fe8c7408ec0bda865a7823827b39c8f022068941ad4894175d405078385dfd86e8ca031f6a7fa6812b98a4be0ff923a1b8d0141047a824fc5a97280f66e50f7a44b21896cafa17b6996567eccb467bd583a83963a96221214664efe412d91578cc74c38c512aa4e9dae1ae0d0063d1a219c04684affffffffaba24afc9598fe456c67ba4563584bb63ecdded496dd253d3dd09c211ad14c34010000008b483045022100875741dec657aba23a90894e0e55312d7992108198288a5246ba1d649faf9f7e02204a1855b1ab58aca2fb903dabbf0bef465308038976b306e321c8e6b73c7be1a70141047a824fc5a97280f66e50f7a44b21896cafa17b6996567eccb467bd583a83963a96221214664efe412d91578cc74c38c512aa4e9dae1ae0d0063d1a219c04684affffffff708950e3f327dab9673004db5a5d8d6bb419437e9813511a682b57052929d9792f0000008a473044022027e4cff1c53e71b17d1c50e6b29d9291d406b81f51b458b58cc7b6a42ec4249f022023bf054acd5ecb18c22047759d54f45f05ea216f9baa92d069f94bbb2f23f76c0141047a824fc5a97280f66e50f7a44b21896cafa17b6996567eccb467bd583a83963a96221214664efe412d91578cc74c38c512aa4e9dae1ae0d0063d1a219c04684affffffffda42171bd2a4415c3bf6b90bf9732488a104418c7ce6d586f85b6cdc3d9880784a0100008b483045022100b559a877422b974111f75be12ea4681afe86a26d1a611f4748a682daf1e37fa3022014cd671a74399948b88a4d6bdb494ca360fe462ed6f025d5fb48b1ef9858fbff0141047a824fc5a97280f66e50f7a44b21896cafa17b6996567eccb467bd583a83963a96221214664efe412d91578cc74c38c512aa4e9dae1ae0d0063d1a219c04684affffffffbb58ad675838fd456317fa8cb07f2a83cbd35fc854da4ffed1119b114d6f4186000000008b483045022100bcd24dbcec2024a42bef06f6435a631edaeb6c52b54c669cac8590f6b0f83e89022073c928e1a421e58c1d4ef478b00ff67bc2268ec11632101474bb910bd723b76d0141047a824fc5a97280f66e50f7a44b21896cafa17b6996567eccb467bd583a83963a96221214664efe412d91578cc74c38c512aa4e9dae1ae0d0063d1a219c04684affffffffd5f0f20daea7d482155b4a24840145740de7a5eb62e041e13d9f9730cdba9899010000008a47304402200c7993991e94760ee0894e1f1534ba82db588a82f6a7c51f8815bb370938d0740220108e87d53c6bd30e08cd1d1582726cb663042cc09975289e9bac0d8eb483a3d7014104cec552e67ac80c9237132a133ad6a7ff8b577fb53988b4d6251924eda0658859d2e767e83dbf895ee1581c3c635ede319a4f684a39689d27af90e381d234cf7dffffffff5a8730c0cc6ee731d5d3ddb52d3515a1e577a83d83a6c15bf31c29db8be16942280300008b483045022100cb97f7c2f4353baf99d17e849ebb925ef20ad044b78e64da602ac2eb58043769022070e0686bb1eae1c29049ae3ac26845d4c5b7e45ddb64df7fd24aeef1fa6787af0141047a824fc5a97280f66e50f7a44b21896cafa17b6996567eccb467bd583a83963a96221214664efe412d91578cc74c38c512aa4e9dae1ae0d0063d1a219c04684affffffff770bc7f7c00d62beb0e59ddbfa130eca87e2aa3ddd7daa8de34018e7b462150c010000008a473044022035a8dc844d0af45a1f128f1b0f604a5a6d9cb3cac088e57672c97eb216a53dc702201e3ac62d6a59b0dba34542ee24ca3d3369fb789d04877af147297a8f0a112cca014104e6666dd418424d6b35af9d0f0c0285f596d517ae89e3ca1de3f884d8c00fae46f8de683e16fd7462e8e8c1b3ab63e1a921a3b2126c8e9d6c22f1a5e1c7a1c871ffffffff021ac00200000000001976a9148629714b466d5b8656661fc8c85e8997f190caa188ac803b0100000000001976a9143893a14ac05ebf6a52a9ed2838fcaebd39ab608688ac00000000010000000f55ad86faa09f030aee8f58e27b80bc7b849d8e7a0ba6b378302fbbae56e7df23100000006b483045022100f438e4794713e6e85861b5e821906186c97f3782021d52c13fcbc0c826c402770220344ebdcfb405ac946fb3c39c81c92c0925bb6536917c606e09771dabb9139b170121028dcf43555920b975673fa8d65de59e84c06574f9bd94b294d15e3697f590f612ffffffff47cea2934ce2500c3b5325e908393a56e1ff13c3df8b4deff73f7af45b3c2bc40f0000006b483045022100f72c5fb60b9d64509ea4a43508390897b30905bed8a96c77fff4d2311554a34c02204f898845dd3de6f0c81aee9c54e2f57f205464617c4f5e7fe4fdcacee518fdc50121020124b4a9dd247b5b1fe3dea733be14ca6ce3f56a6fe23bed4d4fad30b7f14b89ffffffff47cea2934ce2500c3b5325e908393a56e1ff13c3df8b4deff73f7af45b3c2bc4000000006b483045022100cf7a33ae908a62fe9221fd768a5f6ff58ef2359f46fc7cacbb7d3f8515e19f2502200bdc893f10a4fdc6e9a802c7a8102bccc1e9d5e33ac35a44bb7cb95b572aee070121029c82295543dd378c7d13073b439e16bc2f354e0566234d2c2f9904e53cf692e7ffffffffab719c3ddd5d7945263f26143912927b79974e558d68df3a1236cb79be9c5cde030000006b483045022100dea9ab110eafe1cfc1dfde9fe730ef5bba8a96c6234cef33b3ce4126f622698202202e1d7ba8ff9bcf7156bac0c7923a5aab255b6931e3bffa3f906df27cea616271012102bb2833f994636ef7de7761ffeb80e77e8292f07611a2ca8ea254422dd0ad7dffffffffff78f434073747d95b0e7528db9fdf01fbc1f7a7cdc41b83db2a5e74bc13926307060000006b483045022100f393863e5676490548f4dfe15a93b3e428717d871e124bdad6b7bd3d9093deb3022045b6a3641c5ff4065fde59e896723c883b4225a2d0bed59ac75b7498dac4c8120121029dcf8c7cd53a2b4b18ef4a1fd1fc4bde9cd1b2d22a7e40360e2b9943ee702740fffffffffcf1922514dc48c1bb631110c8ba6d92737b7bb991fff301438828f75d0ffc73000000008b483045022100d4895f52db026b7bd61e5d0c238705c4ae21730c58af92b8b481ebaaa30a7a16022045b04842653518ae9c11a0c334c87db3db8f7f31c8aad92e0fd52672ce523a0b0141043a1bdc3563e134448c92472f1389ec158fba49ddb4d4f257fbaac26b7cb9816bae29cc317bbdc96693165f1c8c300e7dcfd9b3f6de3734a1ee242c33d2ce1e1affffffff47cea2934ce2500c3b5325e908393a56e1ff13c3df8b4deff73f7af45b3c2bc4080000006a47304402203b20cf23c0958d659af0a085c18e3746ff5a80df5ab9653afc47c664a5f3f75502205e46ecd9da959da670e8346cbdd26c34acedcccb997f6f58b1265ae1d8e3cd750121037cdbced5fdc351a6607d68abe8949b25b3ccb1e95fea4d85a15c5fbf29db8ff1ffffffffb68e141800bb7d0cbbbbe5c842aaaa53fb27505f42c8040502b03ce547ca408b090000006a473044022067ea18daba0705333686aef070b728a25c9958680ac552a824153190e74137cf022046ff40f2cfd935c67897574684a0fef181548ff261563bbf69f1e8abbee42de001210368722eaa8e19a979040733f048e28708ca2a2baef5fedbe6612d7b0d595a5026ffffffff55ad86faa09f030aee8f58e27b80bc7b849d8e7a0ba6b378302fbbae56e7df23070000006a47304402204938ad3a90d183fb80189621e0a2c4a7b87ac0662777d1a69b9365aa640bf76b022049e86a8d64c087aa1a5bd945e3f685a3787fc974f96b54a652b798f53cd2e0a2012102cd34684a88a2165a1037b245ea8d917dcf49eae7f1d1d0ce1e830cb1cc73f984ffffffffab719c3ddd5d7945263f26143912927b79974e558d68df3a1236cb79be9c5cde010000006a473044022053ea3754de7317ff847ee557a1125b7b396e74f1314e2765c9b45f283b300428022043b8925e4bb74de99ea12a8b20121d02c666a947122c0ae87c49e5f2a1b2e4ae012102960fda75ed6cede9ca4536a00fcbb99a9edfd510049341524dc9aa01528cd6a2ffffffffb68e141800bb7d0cbbbbe5c842aaaa53fb27505f42c8040502b03ce547ca408b0f0000006a47304402203b590d969e14b6786e2560ad027ca978709d0a69994d7f2ca0d7e2b40e560707022015ef73b332dbd7e88b3f51563d949d993759df371a783b3f213ae6175735bf9b012103d70c3c2407445bf30e1877562c686d487987205e6b39724aa60b85f6666697cfffffffff2fb6d5ef691bd78529c644197a5f5466acb759567ba50b43e3e68104cd4bdb77010000006a4730440220443e2f99436629352b710f8e46b439ee059295135724ceb4ff111022f042f3fd02206dee1bf9fcad1d736c2bf1cada1d223515c1144bebad1d333c45d06f9721cc40012103fdbd81b8921b932c64b40f0ee28e77aa15dc15dfd672e58b43a57549b3a316f3ffffffffab719c3ddd5d7945263f26143912927b79974e558d68df3a1236cb79be9c5cde080000006a4730440220278710cd5b6300c62c1ccc92c5be0912de36be7e30a50661db0119988387a9f802207056df06530d756429bf6effa80cad5e37fe910ef29259d356de4b67932b63ed01210224f9be7f81dc4357a2f18ae0b698fc5c60134918514f25aef52c5da73c52bfeaffffffffab719c3ddd5d7945263f26143912927b79974e558d68df3a1236cb79be9c5cde020000006a47304402205550ae898228e245332db632e1fc2ef85be0da85d809c419b834abdc0c370401022045a136d4aff7ef8cc3b6097143c2691cd843c4170f0f1e5423396b1e0a44c6c801210344de1a320b1f05a57b98e9687384a1cb232e1d76eae8a9a270900166bdef196effffffffab719c3ddd5d7945263f26143912927b79974e558d68df3a1236cb79be9c5cde040000006b483045022100a63a3e8038634cfe47b95607287451f007051c0680711f8c93490ae49b65a66502201b560e18e42c961733d29bca24cec4887e79cc9617674245166ad1298be5d0b3012102b4a6d812b3354b47c0c41561fd1667adffbeb48eb296f10a9db252ab0c6938b7ffffffff15c80d0903000000001976a91489214f459783ab99ab411d8f8338d8b366b126fe88ac60fbf56c000000001976a914ac52c193aeaca17ff5f8511272b06171c4639b6188acb6a40e00000000001976a9142a5817a0d0c1c24da4ea7b375600ecfe42fe7c9488ace0af015a000000001976a91469b1ebcb5d58d44ea9d92c34a6c853c063f0047188ac0cdc964d010000001976a914d4249ef620cf5b96db358135e4d392f130385fe588ac1035a830000000001976a9149292005022ba766c61496b85f7680c0375a040a588ac6603ae29000000001976a9141455543b92f177ee2d5046ae72275438afd8a7bf88ac06f42d00000000001976a914b85daec89f67060ec37c54e5eb417b44934a0f5b88aca6d8e823010000001976a914a8c4cbed19d6cd0406004e34254deb1657a79de688acba3cfa04000000001976a914b879050a15b00dbb29d88f14d079eefed99eed7c88ac92e14261000000001976a9145d1643253120d1799defdb730bb1a94be873c69988ac583daf29000000001976a914f507d70fda905feb6d3cf00e7ec49b5f4880fe6688ac9f5a0200000000001976a91447ced88f66c87207baaed3c98ba232aa69de537388ac00e110bd000000001976a914028e1f3dc45530fbfaab1a232c5d9777c1554a0388ac1ca8bc29000000001976a914ecf09155e97a9b3ef4926228536c1ac59539776388acb0770401000000001976a914ff933d6753f14b4aa554bac93d0c5229fd98028788ac802c05d0000000001976a914fd52a6126e4709da83504678931dfe37db457a6e88aca2a6f502000000001976a9142ae6b68848a2b43c78dd9ea34a0d68a066a185dd88acc46a0d00000000001976a914fe13b17ed6971a532e4464728e97601644ad462188ac50a75ef9000000001976a9148fc2714b3e339a16be73cd5bd2082dd63e69e4f588ac67992b00000000001976a9145123b7783a07eac14e4ff9905704a66cbc398aeb88ac000000000100000013d7fb3e99ec6ad179955cefea5e1940eed8d61c0f8f9674592c845a6fd8ca70ec000000006a47304402203e66268320b52ff95eb3970a945a39a8e72ff0e17924b940c0504ae506a827f1022054fb9c19d1446a52b7ec971a4217ccf5c6615844862bfdeffe64e1e82f68e68101210284fbc92cc6d4433cf6d02aeda5d19d4d67fd2f284e1de9168f0286d8b247e511ffffffffef43ef6705e4293b06868032b86f1b6d9bd092f54fa98dffe666b69f1d4500bb0c0000006a473044022057fcad6492f86370b903aa58d65fdcb1eaac10d5bfedee3b72a6bec433c6d26f0220748f6e3fe3ff107c1d7d8e0d5258a06e841e8d0de4f537d6a59e658f560d1de2012102feff76de7a04d338094cd290e82b2ab027e4f54983f966b7bb6943bcd1bace4cffffffffef43ef6705e4293b06868032b86f1b6d9bd092f54fa98dffe666b69f1d4500bb070000006a4730440220594130000dc237b189d2acbf7963a076a1243e436eba5ddabbe9482afa990da502203615011cbf4e8686d61fc27b49a5c0223a008b1c4c60fe5488b88af0ce89930b012103494ef6a3fdf2832bf96837eafdeb528d47b027a7477f0a466cab51d0ea5f3cb1ffffffffad6dd2e138f49e6c05ebd631fabc1d7795f73988eee66364b7e398022350b295070000006a4730440220631116baf74db92c0e8ad3c8dd84c23350c21ae2412faa9a5f78f410ef27bd1602205dd4fa83596799573b88fec9299d130497ef9adb6ba23b26fa711ebccf93074a012102decce366a6a724e883c1832f8b93fc33b32bf61fa0c757a5e9561c2cd58b6b94ffffffffab719c3ddd5d7945263f26143912927b79974e558d68df3a1236cb79be9c5cde050000006b483045022100b62e9a1adee18146bdaa758826436d5c48967e11919c0d1c466d5271ecc78b2e02200264535c086f85f29fa2bd6cf4d73e50149a565e02bcf1463ce5511cf7e8588c01210376f2f130d1d27bde88f0a467f2c6ef28c97d516c4891e5d3112f197982a53185ffffffffb68e141800bb7d0cbbbbe5c842aaaa53fb27505f42c8040502b03ce547ca408b070000006b483045022100ac9e630853b2f4eab1d04f4f9c760ab06a8654539b973e8d0cd229444a06730302201c437d09dad554473f92b5116f2c954b1f8433eef58b8de5010b2ee37de9be640121024a2d0fb36c556f2e0f365ec91ad252c3bc57fd0d2542974d893a325da107ffb0ffffffff78f434073747d95b0e7528db9fdf01fbc1f7a7cdc41b83db2a5e74bc13926307130000006a4730440220568609afdef0dc4a1e143ad11d2abbad05be7d553b0e251acc6defb1a7da155902202cfff19622227fdec91da6984ece10dd7810018686c4561d540634702045f8990121022221ffc3a562cf28c24193f0861790de03f0062934b7ce00d49a4c27dcf8ec00ffffffff78f434073747d95b0e7528db9fdf01fbc1f7a7cdc41b83db2a5e74bc13926307050000006b483045022100a8e3ce7e600c4859f9f6f6b1609f2b83361d9ddaaf41aa57a04ab9d7e813307602201cabf9f783057560d4f44557361157cf0e830c0af6833eaed123853dcf311b830121038f21f9f3d38ee280e72d9135941663195a7fb0abe3e28f4d9b3bbb4b48583838ffffffffef43ef6705e4293b06868032b86f1b6d9bd092f54fa98dffe666b69f1d4500bb010000006b483045022100dac425607bfdb4658eb2feed11bbfa3e45e2a2888fa3e3e7e576c5eeaa0b84be022029b7ede1cd2ec4c72cbf03728a1eeb136e4eefc9e25a62fe2a6f00784be15b92012103be8103b917c25d5b250d74608d6fa4f8776463ba20b9f7e4e131b58ac29e7962ffffffff0d2f6a1abeb76bf038bf0de8a4946cecfdfca99caf6035a071684e1dc200d9f8030000006b48304502210093d02720b7f6bbd69afde4aac2b67ca828b5f41a7e765ce8da810f33fabcbf79022003a2f25d249e45959da9a3522307d8363a966de6ac58a782dbd1e9dbc1c59886012103adc3502860959d8731c39bfcc502d836899e9a4c432ed3e113469f2a86e3ded5ffffffff0b80f043e007e4a021dcd2b5cf448cd1a457bb14b5810da16ab5161ede0f2d43070000006b48304502210088106d9057ab0d0b7c0a0053ed7ced94249dd120fe7a7ef3c95820af1b2a42c40220152f79003d2c57a056401b9153981bc2ae3e9ee7e9d512852ae7b12afba6d51d012103570a61bd857d9945e74fd550aebedddeac46b8a15c1bea6a730101a00202902effffffff78f434073747d95b0e7528db9fdf01fbc1f7a7cdc41b83db2a5e74bc139263070f0000006b483045022100ed50345435ee34b9ad5ca3a25f7bb5767e3c1bee109fb6c54cdf7863994611f7022055c9baad30e6056519bd21e11d7cccb7759b6c4c910bb927d7ee0545a648355301210219848d7910dd1d9e5d20236577ea519fc6a59a6b84a44ecb8d475d121a5d3dc6ffffffff78f434073747d95b0e7528db9fdf01fbc1f7a7cdc41b83db2a5e74bc139263070c0000006a47304402207f7027c0fd99046f6585089c33f1676932ba73a6738eb82dc6539b68c381c616022040b070d15820c7f4b99ee1f413047d4d014e549b80383693eccbc3127738a45b012103448381cbd4cc253e878dce1c51593b309e58f97574e918b70da1d348ff842609ffffffffb68e141800bb7d0cbbbbe5c842aaaa53fb27505f42c8040502b03ce547ca408b140000006a47304402203cdeec90e5b730533604c859327219cd975b3575bac8fd033e904f31c001b15e0220146f4b58a1db758e75c0dfcd6e1bbcf50736bb94b1f00680d3b4ebd01187fa890121038506951c80bef6103099d3c0c090eb75d6c5319856c6c17b8b222393e5917b27ffffffffd0af5d9e4198d457576e0f6f0660b2268c6c9c794dcf35b6fcb97f09b77962c5110000006b4830450221009ec10a2516bd3a0cbfd30612981964d5e1285912948e8e46f802b14e3eaf5e1002202d699385b4f94c11206669e1bea585aa4354a18a5ed6f3632ce85f30a06861dc0121031dc422f14ca460f8eda4b47481a417c9ea5285421a1e7403779beb05d77f6ae9ffffffff78f434073747d95b0e7528db9fdf01fbc1f7a7cdc41b83db2a5e74bc13926307040000006b483045022100aae2e85df6382393aa2584ebf3691ccdde0f8f8dde5a8fb4b899e6ae74087e5b022034c18eaf3164e88d67b97268ee041050ffeee8e8baae873fa0a414ea3d57690a012102015c9a906bb14f64e3fbe6373a7311b92cb8076e5239d33cce60c40bd94f6353ffffffff84d20e975f1b268e95a866a2ac1d91e48d6bec5f9a0df34269b996303b0c7162010000006a47304402203f4eeee326950e6c91ba8e2838744a874cfa864914b516346d89b2b11b01da14022020c69aaa0920bf592028280da9c89f997cbf840968d283ab13f825a59243d023012102207579fac69a3f8c702fa779fb4ffb3ce72084716abca9a014697ad91aa1069bffffffffd0af5d9e4198d457576e0f6f0660b2268c6c9c794dcf35b6fcb97f09b77962c5000000006a47304402204f848a906c1f955ffb82f606490a77313aa8a6d7bbd629b8274c1f3cb2ca6db0022039766c5dbad62568369608bbf30b60c5deee64fdf7c6cd5d29e61710b8067aeb012103991eb4f1a47dd1f634d1075142486ae74ab7abb0e68202a928cfb8cb2e76c894ffffffffab719c3ddd5d7945263f26143912927b79974e558d68df3a1236cb79be9c5cde070000006b483045022100931da94c1f8788283e064933a759178f8dcb6481744dd74d1d01982b20aa3c210220186f595a53ac7477bc9b2c847fa246cadc91cfd2bb91201e92dfd003e9fdf69d01210374865af212f1e5a1246107a8fe4ea81d1362d1a6bb63c91ed2f5bcca5ed3120cffffffff1566525609000000001976a9140fcb0e28c1cb5f16b7fa5169ed20865ee6e9e0f488ac00c2eb0b000000001976a914827742a437edbd9457730797678838cc770e4b3e88ac001c4e0e000000001976a9143f215fa3fcb48276dc8319223459383003e236af88acd0206f13000000001976a9149414c2506ce1d3f557a4b14c59206e81cb189e6188ac23e20700000000001976a9147f7d3fd076faad73a6700be65901e0bd27191deb88ac3ae0a023000000001976a914b8f95fe58db996aa2eb88bf5acef88fa266e0fda88ac76828d1a000000001976a914a77cba1d132c6f336e73afbfe656a2fd756620c888ac1265f501000000001976a9142eca8491b59821a95b4decb10d33de11a87c1af988ac80bbee02000000001976a914ac01ff5dd3b4c6d4f8499537163672ce1f7387aa88ac48280904000000001976a9148535af0a130683b1dc3c7667ae2cb6d77410169188acdad10f03000000001976a91483b63006fc1ae9483a0b9d443a6606fae2bf793988acd0f33d12000000001976a91463699a81cf865eb1b09cfc82148e8953d97d68b988ac7a8fe105000000001976a914a6a89f46fff859b8a0303aebd82da16c0ef0faab88ac53a0851a000000001976a914835e9f30a7c6b5341a4bd152d355987f9519213c88ace0fd1c00000000001976a91405ee1086e645298320abaad9bac166549429c41888acc45d1309000000001976a914104239a9d15fba7252a5620feab2ce33cdc4f91888ac00ef1c0d000000001976a914694b0ebbae242491eb7bada96b08d1630926ef9a88acdcd4e323000000001976a9146dd0ec807e8705d3cd6f14a965540ef1ca1e368788ac1bac1700000000001976a914e05b7c3a1c5dcee2cb962d4de5abe93bab16ae4588acd04da014000000001976a914dd0d7f52c52b5f8484f46a6951a74a95c294b0cf88aca16f9500000000001976a914969bbc4bba55af8b16aa169fc545698294954a1888ac0000000001000000283eed827ef96f1b7075210c7f049093cc760f0dc61d2d474a6b700734045c1a62010000008b483045022100d1e0bde370391361865b48a6d103b79573d8dcf24305000f5ed4c190fa8115d0022001b9a5ca7a2a36422c11eabd7f592d9e9916dd39b2932cc76e14753d88f6ce1c0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff4db464587f85685e87ae5c5ba62eec2d1a8f19952dc2584ace49ee9b7fc2af001f0100008a4730440220286cd99d44561316f447c5450d0396a6a0c4dae803e4f3cdcc91d5df7eb3134e022067076f79112da30cd65ddc59d0d5843e9f0f38e4ae1c558362b730dd1b1d17380141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff830fd53ee0997ec32ac64e171568ccdd3ad8033980e41429d0dc69914f51be83ef0700008a4730440220685e7ab1c7da0bd69fbd650b407a08989567c8894c085dbc6d252f89c2e900f302206bad211388fee8a3b96323e6c3a242c57bbd2dcb13e9c971b3b7139bf2b712d90141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff8c9db2a3fc07fd6004e6c2a71ef51ebaec7a6c1dca4f2874a5e61b4f8bb793ca1d0100008a473044022071385d36cb9efe530fc24929f001d4ddffd3d057716c78b174fb516358d0afdc0220350eb07e6dbe09802b4dfb41ae300082c952acb1636b85e5681ce209fa038c3c0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffcf215866352f9dd5ad7394d701e507f36c67b64428521bfe7b103f1130193d00240700008a47304402204d3eb9f948aac5b695cd99eb435551cd72327833229cc9e5b1d385333a44a6ab02203b161195dca449dd0c0c2c904716530344c70bd79411e5299a35cd9ff228ba160141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffa3467a857b4747d17c31d9701ee8f4ab337cfb71c2b7f71bde9a7a76ef414320bb0100008b483045022100ef05e86165d535c10ce5c7127dd96525f49afd5269dab4e9601f6729f74f8a7102206bfdd93df11f87c8fd0e03b92e57c0104617d85098a98e08e48204212106251a0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffc2e5c82ae5a7831c6e19f54394dc0959fe2e2ae889b2c6ee353af72cff8d0acb1e0100008b483045022100b8f6ac0a0fbe7a51912298a3aea6db020cb59b145551ae47ab2bc56c6f5c1746022007d79692e77679c0d9d9d04e63f8aa17d6c1bf4363e6728cc2c491fc6ff14a6b0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff1e3b385edfa983193c713af8ecaa6c6693f4c5ba4d2a07846790f0116dcb6c35530700008b483045022100942c7f577573d6f0b18b3b10857895a9cee2146db83fa2759bd0cb33e4156b9c02205a128e121a3def39d37446eff4a3a1b664a1fc372044fac8d5ee6fbbdbd1ed250141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1fffffffffda89832950b955e6865ac76c5bbd4a7c76b41ada24da7f21e1b48b7dc8272561f0100008a473044022071d5f7d2b4d3d73c736c0069007259118c8704f47bef9938ccfe7c6eb82c99b8022053d6fcad0a74a5c059c307dd6e702452880aeddc12046135eb786ad2a776f93b0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff3ed10d0764cb1816cf93160551da026692696726e9aebeae2bd1aada0c3528cad50100008b483045022100db8b85469e3b788491ffc218e76fd4a216df384f28db6a8b9e7bae87b3c9f9bb02203efa7aabd83d5c11ab1091e42d20b629a32e871e336a46723fffe39538c1bafb0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff27b5ab68d9ca206ca5a4470dd580b76feb370270ca0903803020b3d371f350aedb0100008a473044022066c3a71679ea23cdb2fef21ed2dd12f086255b44bfaff06828c64e274e736a010220414701c93a206e34624038374c74a154e67945f46de8b415f4b200009976d6c00141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff10aa5e5dd5de1713efeaa5a6198878e092cbedddf5b6abbcbd9335f660661c531f0100008a47304402207953f275f9dd7d68a901016d24ed6a080e4b34635f8bf4e54d5846ee811e92bf022064644bf657def7ea6c3be981686fce501baa7186513242c36aa77f9f304168110141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffba5f43bfef7cb6a50ceeeb163a61723e11410614c92de99012a061c40804cfaec20100008a473044022055f9c6cd9ff4b060081648265b198864bc4a727efe5f6e87b9254fabb84fd9cf02206175594222860690d32e1dd2efd51d67bbc35e1635016ab8d148d02c201554790141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff8d78152d1709bf625de8a6706d047e737a9b96bf5b661f25746e46f2ed22219e010000008a473044022067c29b5d471e24cee3b5757aeb9d712fc188c5f745d398ba991dc8ef7f71e1e1022067a1a350ede3095a6c67d280ba1f01f0d2fe0f5e6eb4dc2db57756ee9ac2f9990141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffaf938d296a140d1e5c33a8413b9809363bc9fd3db15613b8b2ed1436b95b63731f0100008a47304402200a6f18ab2dcf7d91f02575223b1821a40b168e18fb1ca0781599afb969fc3864022039c97aeb5d2427aece295e3bfa619db071000a0ad5660d596f31a2c4b56d717d0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff26d8bc4dc318d3483744f51f4d4a521e876c9d505b3b9a33562a6741a01a699fdb0100008b483045022100f62d7e2d683b6517195ceb39b41d5893abb2f437d38c8a468e5cf028b4b93e22022056d0e83d53df4785545a1431a82d953513e2759513639958ade8c1e4b8fb08bc0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffd2c71d0dc40fb8a428412023ac3cb072d902036d9d76383a52a4e0991dab3f57000000008b483045022100d504306e66407dded2769e8c9fd4fa26d08b25e1308e505dfd00b6fca157d5c6022012a62af4cca34b03e8ab3096d8fe9e1714ec96367dd299ecd2faa62f5f2f05060141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff626539cd2bb704ea8f58ef265a399ac8eb6ab9ee8f4b56303796392de6d71147000000008b483045022100fd553c743bf97d7aa461ffa84f0c22126e9ad10a393ee1cff8d2b14cf0b38e4f0220343d61595754fc7b6dfdd7461b9fb6315fa7a426f45c7ae33d2fa127f3eb5dfa0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff5e900e591609ef3853fb9d6c653a85a4419b0fe0a4bef72b40db6868cb8f939f000000008a4730440220457a09638551fe1fa85e10e3896ad610e410049e664ead147d9f9779f40a73a102200158696da4c13d004f63b755175a1a7141bb0e8e33c86b3ea42c70b9da060ef80141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffea26be75210fb968647fe384d9f07821d38f01ae174ae76bbe47a4f113693d18000000008b483045022100fbf10c5ec91e28d325e1553795eb880f894b695f494c8ccb47f30ab6aa4006f1022051461c846c1a138e8db8878c15b90b012daf4a2bb3bbacd883a3bf643c449cce0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff8b3714f7e1618f11937e6d24ab02b8d3c95e279adb9684d57d2efea263739687010000008b483045022100d1b55814828527fc47b2ad8571cb6bc6ca3a21e144e782e758a0c1b3e2c0bbca022024065a6ee8a2cf322031c3235ce90160dc4014717d09486b4940a4c4a88471e60141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff144d3477ab4a57dc3f78c9c1ab230861e9ab80aa9e04ac2f5ec3c07544f0230f000000008b483045022100ad3376b0d12ab79bf8976dab3cfea3c200b13e9770dc2ca4cf28b03205b2582702207dae7dea1cb35c9c3fafc2ccd09f7fba2fc82f6ea866ca42bcde69167503bb8e0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff3ce4ca74d951c5f45e02f9463a6a54b26c352d2aa83dfdf05cbabd760b0b5407010000008b483045022100c640f6b7f6f124593b1334db6fc74d80d87b550906c54f98c5ab73b99853f5bc02207ee54b250a462442647a828a06b6a1458b0c9017078fe533534f58cb6f1f6f310141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff9f559f0e4a8c65e5be2b18538329eb88ad8b4fd4ef9f4632d32ae0999e16e4ad010000008a47304402200300189f60ba8056981b6738091957b32c45ba5ebf8e2ab1ee15655d7764aa7902200cfb1d395fe1f585f1552c0ad23b54ea03eb25e6333b6fa446eda66cbdfc20a60141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff4fb8c745bc34284cf212ea6106cdeb8ce37fa14c549cc3765d9acf475ad09ee0000000008a47304402200634e0d7928bb8f85bd5178495fabfca9d27cfd9b808bd44980f8d473885adb7022003638f04d5bd20cc5201fd8a07fa042599edc831b6cc010dc7b25bba3e568fff0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffb937d2e2ff26accf1088a35825330ccd7f0d1d78b69b4f54111a37536a332504010000008a47304402206f38e86043e29102f01d449489c8a5a7a88358b1a08851fdfcdcc23d4de2e785022024b8ebd5e7ab3330c78a8c6e1b4b0cf07704b8b05b1cd9fececc5a8d86aa257b0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffd2f0ae14e2bc19114b22bd57630a484bf21bb5fadd218c5abaa2fe723830cf4f0c0000008a4730440220170aa2247f2108d525c55ade9d8dab7510ceb0d16e270f3153901018e2638dc702201666b69bd9466705fe02b059e769a9943db53d81c5c39655111a185ddec2cfea0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffc1754d047ecefb750fa1927be6de9fabf96e2becdd3617e26dbc45be68cad2d3010000008b483045022100c1cf10efacbbaa5a0e4debabf4748b9a90d182eb09fe76b43ecda6928aed1257022004507bb709efd770e975559849a0c95dcf950a6fd5c4a53c9469d492d45b3aeb0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff9d64f5c22f9d9290284bdde5fb286fcf684b236fd6bdece6e02d07776757a4c2000000008a473044022056ef3635e03a494f00108969d4b82a5857f3fa6373be1f022740158bcfcf771c02204fdfbd12fe204e4e467ccff61ca4f8962bf51fbbafea655e3f809a53a44177df0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1fffffffff465d892dafd2aadfd914ff8af877ae6e4d72d58ea10c4a438a708f0f873edd9010000008a473044022064840358fb7b292b624f14a08c110bbda0b174177bc305303d5ad4a78cb5009502202242bbbbc075012cfaea52e06846961436ab88535a3d6be34bf1c131866025d50141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff9748db5a541909357aa325359c6f0c797bb157156de96e5a9f5f20ca06882b64000000008b483045022100d35b0e57e370466553e88e86cbf54704cb3d2d826e9a1774055dd8382e8f155602204a1ee8659f9a760133e0832171c20e07907400668d3169daed4482aa62ead27b0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff6fea9d0b3b2b24cc6b352b27e4c3bc038752b143d3620d085e17ccd805661b83010000008b483045022100981712dca424adf195780c75384118271f41934022e89ebf47289ae78b43d2dc02205ff030bbb218cd7b4e15b035f962cc708f07929208cb172afb39e2f379f4c0450141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffb2f914cf1fe445e76933254e00ef9a4f8a3142377fa7592e2407292334d30baa000000008b483045022100c8565b696194a5e248b60cac7e82a567dae65a928c3bab1cff0c744df969907a02201bdead056a33689421fcdcac23186dfa11613782af793387c6805a04583223090141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1fffffffffafdeed2ef13e5a0ed961697460e2ee5c36536b91f5a4661dfc13d6a5200ad83010000008a473044022013c3b669571bc35a83293065275dc0e196332ce5703a00443dc6066ede813a3e02203a04f6e3617729763acb9e661747e1564a93b200799210268a2a0003c62205ad0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffe696cd39802f7e9bf2963eae114c9deb00bb86a03f6df452c43c53ee689f1ab0010000008b483045022100a4b17e6107a3b5b2a3a62cadfe018a725a250cecda572a23d006e16e4781195002202563cb9c19c605cf7a490023f6164627119a241e658ff58896ef1a6b592790b20141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff7662abb9e1a9a1b8ba8ce1222075cec7eaec1aba80aedaa090af514876c23dc7000000008b483045022100fb85e713601fa08b4da4682627224121437dd46f85fc0f9dd1305a36e6c351eb02207bbdeeb67050a83e2d919dced90fb930d6c1d6743379b56695485bc0652586bf0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff6a6be5256d050bda7f91301c1696841b9aa9d4610386d40369bc3e0c01c3aab8010000008b48304502210090cecf9de11738c28b534592e0139371c1f12f18590a1f39fdfef19dcc76ea79022075fba124cf04255677d433fe34864886a4d75cc66088e38e465f756a9e6a81980141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff5a51dfd9d76f6164401f31d30a1971a6e7467d19fb1a319b0d546ef3e87b777a000000008a47304402202c7345033ebf73b7fbd69a7ac26a62ae7293d1fbe4717880737c2b83e06f96b702202e1a34ccabb36fb3088c63f37e87eb4b744e1376afe8f25686a6c5483b7014670141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff4be714871033b0b7bff214d2ecd3841d20107f65e8095b29f54268e27030447ac60100008b483045022100c6f38ac86ff34b89f6c07512313d59c36ad958c12f9b4d2daaa6b92d108e2f8702202bf655afafde6df24343bdcb82d98c9dca52edbd9b5574405d41f3be017216450141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffffb4dbd3b8da503bfa60726dbb7eb38d39641236c692b699209496a44555c9968c010000008b483045022100e75a40386e6ef94ff84434e3c3e3ca02da3f6a718b62203b024f7aa5ee7028db02202f0e3726d743e74a8bbf9e89fb86ad35603e3a984960dcdb5b4fd7ddb509ef7a0141042a1f4f9afd99e458f577cba0afeaa3e7e1fa7cf3c8854cdef57f84ef18e87334f9abf072baf9368382424c83f89d208e2837cd7fb906dbb91502075db7366ba1ffffffff0200e1f505000000001976a914dd44b69a14f0ad169d628451f3793e8591639bcf88ac9e083001000000001976a914596d3055016c76fe13b46e299e458c03d0e102f288ac00000000010000000552bb594c351c7b26b6c29b84ba47688468bae4093fae25049abdd41911bb5ffb000000006b4830450221009078968631250c4690a3dc3562e598f186a8986b4b19ab2cdd9b85c2d9a44d9302205cf040c6418677d94caec9e37f59ccd681ca131a298ac54de8cbc9fdf5736f7201210383fe170364b867c94cecc3ee0f0c8ff5c19c18c44a42b2b3df36d59bf9b1fe19ffffffff534af251e89d80a5b0fda190c880d6ab909a37bf4ca44c1b3bae3154847c6e8d000000006a47304402203076c781e832a56dba20b51d807e6ae772918000cf0bc8105a74ffcbda158aee02201770b0049639dcb92e395d6422c2b1719fad48a1ed3c5065bba82d236e65a157012103b66c014184f07f571ab6fdac8864134038373e5abe905e3afdecf0f98189d15fffffffff92498c1329832ddf168765dd5325278966343c2f788e0fdbc375ca17a424ba590d0000006a47304402201860dc8a6203376581dbf581fa56745d43cc22f23976c712a456cc41599db1a0022032e02cbdb43e70d86fe741d3eefe990d1f3c05e316ccccd54986948e519a452e01210227997757925471709e647321fe566c74ab4487fea0831e405313349ea78ad5f6ffffffff3231f0e7ece48eece297979f6d67efd91015c8309256fe26a46b2431284d9518010000006b48304502210095b82ab7237217b26db7c7d226222388ec2d4daab988f7f980f8abbe49dac3fa02207a4c9ca49e021c79e0581fb6ae165f846957bd6343ab1a7e92f5c352a3f3fecf012103d7972106d09adcbb0f0fa60321eba7fc51ac06eda3c8f44963eb4c1cdeae8b5efffffffff7a7ad6e2267a5811506f4b4e0e84d01ecae49f4fa490847e69e2c31bc241da7010000006a4730440220040ab56b2dd8a818835d1d802957224157712e5e60bbc936fbd92f8e30f379720220188a1aaacf1cf4295cf42c8d096da5973939b32d47748137ca9ab279474f8ea501210390a8456aa253d30b208973ea44b074acdd0e4cb15e25d5f2de295290cb6bcca2ffffffff026fda0f00000000001976a914abcc5501c2bdab5c7ddfb8af54d12c46c418972588ac0008af2f000000001976a914c082abbfb3ec413ba489209b11aecaac5aacc4ff88ac000000000100000005638d152911ad71b0ae3128a7f552462e722f103bc34cfc4ee940454ac2af8e283f0000006a47304402205891102c12f372653a2ad7edcaf16b6344359b723ef301e63512c1b69fead374022042d4c7d9f55266f450e6285e0ae3d15a74a1213eebfd028847039927f6bf928b0121028c8bf6dcd49baf18e6ef57228ea54c9e4039b7f7d99946626434fc17c9e425faffffffff638d152911ad71b0ae3128a7f552462e722f103bc34cfc4ee940454ac2af8e28400000006a473044022012906e7d0c21d45416f386fa5e8b70e44ac657845d6276918ab6217c0811056102201adfafb846be7cfd7b8251d1567cc0b01b82e8db28ba3d5d9804a45809b897bf0121026baa49ed6bfe52f16f0d328b8bfeee05f99388b3b4641dce900c3684a95a28d4ffffffff638d152911ad71b0ae3128a7f552462e722f103bc34cfc4ee940454ac2af8e285b0000006b483045022100c6811fc1edf9421d07645f7f792056479f31016fb5d65eed08453047e8a9e3dd02206517429e0cbee9b1751e2479419d0cf93e5d1e0959bced6ed623bef870a6253c012103802fcdb8d6635484fd21c0320daf6b9b2e0b811db96442d9be99a822676a0fb3ffffffff638d152911ad71b0ae3128a7f552462e722f103bc34cfc4ee940454ac2af8e285d0000006b483045022100c07527eb9f0efe6a5fb8b23f35d1a47445baffdd40bffdf994768ddf19f97594022062dce10bfeaf75137fef51b619b049f3ab7456b5ad55eadcc4ad26a6403d37d7012103bdfc3fe7c752f21f3101b331c7ca0a8bd760b083260a02b000fea68c04684dbfffffffff638d152911ad71b0ae3128a7f552462e722f103bc34cfc4ee940454ac2af8e28650000006b483045022100d5c6a7d346c936496c2f00418bb649ccc15425726219013c14b7d6607a6bf03602201bca73549878a6179e5421dd89d280dbadaca281e3b1919a61e0b467a1b7e19601210362b1ebc2050133818f3ddb2bd1afaa712bd43da9e3baf8d91a905acc451af5e4ffffffff02409c0000000000001976a914ecb15a858fa33b5f121edd856b6110982e01f46088acd0dd0600000000001976a914ce3f9c438767b93be5503b14adc8b56c4d5df67588ac00000000010000000567d7a52aaf377a247714679712258769ca57afbf4cd0d7984f8580dca3021603000000008a47304402206ebd94b22d7b2ad26ddbdfe4002a34bf08e108d95ace0e4c828c103d112606d402203117f01fe96d349c13d117740f148132aa987e242b993f0be3e864a2168e5f9e01410443d0c474d738e11f29da45995202320b05c2a847f94a59ff6ab083857261d8e35213914d59cc963d83dfcb15d37120b9308b0db96f09cb49ee3956f128a9beb0ffffffffdd2f6ca977a4aaf843cf489cf0fb0ad61ad51ce11f34cfc482a76b7b838d4fca000000008a473044022056284030b1bc05ffb9d2d9ad9231d43e62ad2fd60b1e819d4747ddc4b01e435702200bcd2b125fb6e6733ab2824a5d077c16406c5cd079f0e2d7f0592c316f3763a601410443d0c474d738e11f29da45995202320b05c2a847f94a59ff6ab083857261d8e35213914d59cc963d83dfcb15d37120b9308b0db96f09cb49ee3956f128a9beb0ffffffffa6f00f891a86d3a630e09b43aa24b91ab5c7a9fb5904d7f4e82cdd7b52fe7330310000006a473044022040e7701c13339a22ecc7edb3f1a9d2f15989d27da10f825f84912abb5aa468b3022046984c24232b5295750535afe70204acceb604814aeb85d9da0f7f2f49d4faa3012103f7901b70bd57e655f3fa3842519b4aedcf2a7d1bb25937854b386a8bbe0900acffffffff612e526f4a0df2cd9c20b1aac8548577e6935ab0d26d0f4280ed7ebac6bd12bc000000006b483045022100e691b5777435676d61fd56d139234a64e6f3dc7ff5ee5f489f18de8088ea33280220597dcac0822fafd9fce5dc0a843ff806c8c31e797fd008565c0be1fc8973e2300121020febfe84f518d971c1c3158c878b61c3fb7250600c532cc0df8f0a02016cec26ffffffff61665659744ea3566b3795258247cc65bd597c4fdce1d47fb54dc8a1f1960543b40000008a4730440220569889e4ee6f12df5b249389ac33ed066e7a07b8939068ab92959d5305d8bec702206cb4a5a1f7c67b151b36c0f2278395242e4750bba43e02dc5af2aed710b79cce01410443d0c474d738e11f29da45995202320b05c2a847f94a59ff6ab083857261d8e35213914d59cc963d83dfcb15d37120b9308b0db96f09cb49ee3956f128a9beb0ffffffff01404b4c00000000001976a9148bc8d05aa085575014d2515cc23e7865b1728fed88ac0000000001000000013bf82ab6ec03fb21a12fc8434a2f409c6e05b6a59ef22e7dd27ca76754650ae8fb0000006b483045022100c173e802f6af265f13b3b90f4236e4d9c712c725b7c1df10c4c6db25cdc433c702206900e08269c384ed113caf8d4bf4cf12c0100e96c7c1c8fdb449465510dd068b0121038c5c3b57f3d238befbb3d2d30722c84a258e7a7c1c6104517c34bbc5628fad3affffffff01c6ab0000000000001976a914d702201d250b4286c770ec453efe24fb6cd685ae88ac000000000100000001f6155abd104103c8bf63c7c71373dc09dda4c2d274bd4daca3a3f9fbcbecaa0a000000006b48304502210089d02527090fd13eacc5db55b4b82de45f12fed4e2e024de611dd7c479ce2c7b02206ac4e5b9d68516d088e335678bc1055a9b412623aa0fef10d16426f340f423c30121023380650476b0473c1af2a2beb4a861e37fbc6d131626f60defd2660adcd7ac47ffffffff02a6558600000000001976a91405acb15c954eb47058ce41875e79fd573ab3569b88ac514b2d00000000001976a914c21ba1a47928f8bef6be74b75d0c20e095efa8d388ac0000000001000000015108c49440d32f496d75e4df926b9d44028574f26f3415d79d7b69851a9b37e4000000006a47304402206e6ff17253c0742b63090795267d5bc940b1b490cc7a22ad6289f77888f1d6510220041ffa2cf43ac960b94beed09012571490fe2b55ade666ab3cd22f47d2184af101210328e1bca9b6d122c93fabb896f88f278a2a2452999a0e503c44566cfa005302d5ffffffff02b0838500000000001976a9146b4744b1caa854a95c829bb750e9f1d20496e5c588ace6aa0000000000001976a91405b535c47c2c3833621182b5be2acfec19ed265a88ac0000000001000000012cc5d2942e896f62113305f7a171983a8fc8b7d0243975055d02fcc70d368a4b000000006b483045022100dac592e83757daebff93fcf84b43e5d6b8272a334fb65b9db1bc4f40fc8571d70220340fc78e510071e9be79a583de253920105c158f77bafa9f64ff7919067a2e7d012103c6d94cf9b34ddbf57655ae016139681e1d544480589da64ec7fa52cfc3b86d26ffffffff02f007d500000000001976a9140eed60663e5a3661056131458d1fa6dfa957209f88ac48aa67000000000017a914a97a15771369c7747cb3fedcfbeb8f6fbed3c96d87000000000100000001ebc0408c8fa08a7111298be3a425045354e89a2cbe0d8a72a0bf9fbaaae5ed3b010000006a473044022015489c2aebd9f29902801c9c1c4e0edfaf00a687a61c14447a98c7aa2a26294b02207dd3fe965c7fe87cb5fa349d7e28712d3dfeab7b1f3953f79dd28e755750dc840121034a74dbc431735a515e44a69e12d33717119159f9820a542e8f086bdac5da5e68ffffffff026d786300000000001976a91410b274988d65f957fb3fd7e8950e34d847a9495b88ac9632b600000000001976a914df29a6dfcc9407be57f07c7116bfc0bcae3b34eb88ac0000000001000000019ac686ad93e3190110b70c40a6db567fb6dc7b1cc70aa9b591390f5b80a70ee1030000006c493046022100bf2afe55acf5edf17f00476dabc468cce1aa87048f6a3aa58ee34828d801d513022100a629446e50c72d0c9eb78aa89d2cb95e4d63a3c9eea4bdbc12634072725c727f0121030749fcca4a7dd8e6a27fa6189ce4cad476a4ed5b4a2a40fda660852a54b22107ffffffff0218156400000000001976a9146737d81de609e9af1f8759a39b1fce6629d1357a88ac50160800000000001976a914f13f79a8b06c3f32b8aa0fdcaabfb17df1b2f30788ac00000000010000000108433a84a70189ae539cae0d92f87babf71ac70ac01eab375252730cba4e36d6aa010000da004830450221008a98e2763c78dc6ed251a430bcfcce4e435bbad9e1b82f36d1a6e172b01b14ea02202b4fbdcc6c9d53bf65279aaf2000518973be9d616c24c72cc50a9c04169a43d6014730440220379f36b1fa865cc7dd7105c95676fc86309e400b709aadc300a5c2d7e910a529022020df8f63955815031c4bc063bc9f46323d038f6ed5b154f4c6df2d02f820da37014752210204ee8c004fe69866d3b8f3a7c0189c9c057a04cc313005edc639f9f50be504602102d4767c371c863dbeff2b79b0907d25b3d6cec8c7b24a5a08ae565853698e27bf52aeffffffff0188130000000000001976a91436b5253fd9868b203ce55ad5cb0098f6b8a1aa6f88ac0000000001000000b87c4198e55d1e4ceef534b613c02910a5b575630d525050e0a1fe113dbff28aa3000000008b483045022100eeda446aeea3481faaa212da516c602432b0914969c7db1bc4dc762ce791f8840220302d9db3df494c5e69fe1ea5572ab5d13643689a609e749b4e25b1c2de12f46a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3277512f83aa03c92e1684f99b078b312e31b4512b85c2b1803de35906dd2064010000008a473044022000ade40c68e440f3cc3228770ceea71e32b730b302eca0faa5285c1424eb84660220260b07b9c9b8584225cd8f1f494fc2b847c67220a7f931bdf4195fabad232bf6014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa0119a50b72d800af4da404c23f6a0f9f2d5dc70b237b8a42437083e3f5ea591000000008b4830450221008e26d9f386a1dbe1bec9d64992fff72267d58c820f95777339a7beb33074274302202490d80c8e493b3c2aad40e8d46b3e5b422843c69dbe1ea54dcde9befb302058014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3e9719e662788884c92bcac44b49dc598beacce1b86c7cf354813251963970dc000000008a473044022070633190d0a813e19eb534246bf78e50750f7efa502968da05145b740b32dbab022013df01422c6e9579fdd7c702599dfa5d1e4a20ffe6549ddb77651ccd1c835d41014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff4f1dc75a10e37f7c07f25b42bfbdb8687b366efc4f35722fc66996437dade1ec000000008a473044022040664463aa5027baa60345d6085d3320fc923fa9e4b891ac2c63d8257c2843450220119695d223bd88abedb993dc5d7f163324b927a2c38200a7cb9e53e9e74ab7e7014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff2a60b2351dffb2858ad98d9bf14c8f2cbf843ca4fef386b6380ffd7dd030e430010000008a47304402201b99e239f503030ebe369f04047343ed2dd17600fec55819dc343a28304bb096022053debd56b2cf5d60a661ab6c217e1545be5de2ea329e2d02c275567867c58df1014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6e16cf94944e72e495e516c09595cbd1d80209917f8fbecfa3b8256ce822fccc000000008a47304402207e6ae3d939a29f5aa7c71b15b58ab64bf52e5fae57f1096e029d5e371d0c446602204aab472539cdde0526deaf4595d93c5eeefcadef136ac2b7416e09b181945f9e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff254a1807b6d5e9403375c54dcbd520555f3e6266dd4ca144d8bbc16f3870ce50000000008b4830450221008fc243d30ecaad533fe51664ed759f0ac389b1e9e265c7956856e16d25356d9d022033dd3c3f84be5a6d344f734a4146722a3a58820bad630c5b82bb1ea50e932856014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff47c4b165a7ce79f892ea3515670726db487f354984b3cb5894e9c414ba6483ef000000008b483045022100be633ae66d39450a26f732abf591b635f476e6736ee9dfeea05d6ef779e61a4302207d4dce0bb91815ed5f24bdc0b6a3214b0c661b62975ac351bab0fc736f8c87c9014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff9de2082c61edb091a37250493c017922742c956803ddf7148a84cf59e5c72404000000008a47304402201e298e468241d16d1e15c6ebfd8174a15331755574e1ab5923fa47eb851cd208022078545a5af7a96537f5e6cf91f167f2eb84874e350a96805ca7bd900000d59266014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31fffffffffd64001351aacdc62258c3b6adc122a1db38491f4a8dfaca57bff5d005ce5ff3000000008a473044022011ee32514619892c0206b22ee435cae58ffb701d3e72f02d84f4fbefb2fe07be022048b2849b9ccb51959083e34195af970b96fadf32451b4f07500e1b567ea1de56014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff03cf3296b916bf32c037cd73e6fd5a25e6ad692fe0068842962ea6e436a4b321000000008b483045022100e16691b53d93ec9db9290b331967a970d3790ce63e4f8b77f86d4fa0887f6cf5022050f3b26be49efd4bc2e8050ce1edb098f00ed68736c926e17490ace907a6b022014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffc485d158f7314bc0b2c9a66518e7acae4d396b8d573b50c552ca00108b4b4aba000000008b483045022100f9c2a5b8aa10dc498498e68ef463873b64adc8e3a46ea67f690180c7461a32fb02207709d8753ad5a56ce2b7abe36d3cc4ee66e15e949fe4afb0004256d8523069d5014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffaf18b493a975a6ec7721fc23a8cc8e0c700780310dd587bb2b9f23a629e68a62000000008b48304502210090ece15ef481a340c19d65f6d100f426a604eb9a73296a8022352a1dcdf2f7ed0220158956495c8b0891d8905b24f50db1f1029a95130f9ae5b021ef3b6e9dd86bfa014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff407810ae14f48ffb8cbffdba13e00dc412d5965ed29730b2e37fb2558302906f000000008a473044022051844ab15f6ad2f31c443720a7944c940a48d309ef13fec718c9c77ede6a6f250220226faa94f75f1eff928f2e28b33615509189976ed6040c55002bfddc68350070014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffac0f6e6d5dea966c7ec24cc88c93067a7875e0f9d36e0f3cbd210b5715b6edd2000000008a47304402206aebb839d7f647e4cf973779dc38e636de9bac10fd998fec10d78e4afe01ed3402202557554620d5c7914c63b55c8219bfa0b3096794f347cca99200ddeb49e20153014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff957dedf22d0a86b4c020e8715dd8065bf8699d1a63bc7c1ddd74112f06cc81de000000008b483045022100a6b59866f2049ebfc358529f0d551ae8b2baace5c25776b238270109072f62fe0220108697a0fd6e455de2a601660efa6b00cd4f26722436f613a6736b933fac8d33014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd467f7377a086ce0704916327ceae6c1963d2ee38729f0cc8c66b4b6d7c4baca010000008a473044022026cd4956d3455e90b3f8c1c5a50ace75411e04b8f68a6a2027950b83c4ded28f0220321bdcbef2ad151cd16dc61320b32b5d295624f3348b70c2b7e90744cea4ed15014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff503880629d78f396845186291c3ce00fe99c8b62df583d5f3987d8ad69273de0000000008a47304402201a4147043ed4ae14d820288f5b746d6b0ab7b2a2a237251ab9acb5e73864493602203054cae22af872328f220c0b3b4ddef7fda256f34491591ff8c9e0e4f5baae6b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff19814da5f966f8ad13350f6a639d37c7ea7f66769cd3c3e8d08fb65e263a121a010000008b483045022100fa192e15b372b949f84e30bb31ddc236bd8edb48fec5254d35c22b250cff875d0220645ac2488f5341dfd75a8dd16832742e2ab141df84030254b09b67f277d9938a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff29742ee87db55f67e77c3acdbc812042d37e803c1cc1e1a451d8ca84ded54a02000000008b483045022100ee6ba558c1f6e302b201bc1e07824469490cb967dea4d5b6168e6727b1f870f402205ad60c9fc9dc75b3ed341b32d8c6f3b69e68658036bd4cf4f6a4ccbb298fab30014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffed776aac195f445a7f76b41416804b4182f96b617d0e2be718436b18333f1642000000008b483045022100956f4c4692cb71cbd568a8e84b9959de21293ce8d2605fb27def79b4444553fb02203e5d65820636ed4cc233bb670cb573e574da0651b05a5eb9f56fc682a45d7585014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5df8698fb02381bb28768dc0c4049dc377b0fb376ec3b7761a2b57a662e02bc3000000008a4730440220353f2709edd69a12b03238f6f343d487a17b33b247604fe032ccdbc5f13dfefe022067f0a3296881303aaebacb12b2dd859e4c123c338cd9f65eb8e7d9010a7ecd29014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff61fbdcfd7048345a445e00a48386285056f8d7a7ed9b52805a5e400117b87d1d000000008a47304402206b9ead4a53cafcf84fa44faba826205b9b2e340614efb10c4a670480d956b3b202202e47059725ef945f042369569297448c4e5928472a6881aa57d580a83c94600f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5061a471874e56a7591c26bc1802abd5bd2d0cd62f9138d90303f2b1875ed031000000008b483045022100c57982480c741738418e284d03c296d5a8680502c262de9336d334f7b9424ae502207fdbfea795314e434605a29fe15d2c9321db7dcae4c780dbf688c8da7299f1fb014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff53698b5ccc19c0f781f1429d418360f63cd2579ea8696d876af97a5717c3e5db010000008b483045022100e9a73aadbf54f29a239a65e6c842b772950b0a18ef26e4421e9a162d666ecdce0220439a00b842cf60195042db8a98aba17239f79089a31fe71161fd9350690b2f15014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff2641fc9b8e682eac377dd41360754ac38100f9065d237649f9fcb12acd902c8a000000008a473044022004e04b483d1b884161414df372a61df26466bff4ce271e39c16cbbecc84eedd302203672dc6d0f58d10cf0bdea1f647df855d66e206b854af4138f51c0757f20a094014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff52a934404054bdbb9ea752b99187b46745c28fea81025eb26ab667bdc8757040000000008a4730440220044022550bd2c3e57fff2e47d01170f57199ebc64081936e7f493588d227f01402207e6233f2d50023f83afa9e6f9174b3881c5e60f2f367b9850e0057fd3eeb1b9c014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff627b10f07a1ca1017923798fdbd0b447bdb3cea80b399044dbbaac4827a6da66000000008a4730440220129e328bf95b2253bd7f510cdbe1280c8140ffad110d00008c3ac1e6c1636c2602201cc89204ad03315271c7306b7f6ba0f3fd9795e868597f767091f98bd689b645014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5eb612fad48ca64c6e9da2fc9bb0bb42cacb4f1cfedf960b664d408e61a8ce2f000000008a473044022078d986296e71bf978095d31d7404e5f7f49ac936b17bd401682cbcae354a23a4022071bdc4f2e7cad5fb521399d737fe10809a3f03e410372c60101c0ef633e53fed014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb3b1dea271be16cffb2723caca96800805133119df59a5413c4c18b2a73dced7000000008b483045022100c9e25c79d1fa49ecab1345b2989c63a87383c3b6b7d8161708741bcbda62c7d902200ca94940c6056f1960f5d19a0852e11e29f430afc8c12fe176bbb0d9245de2ff014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff8eda448ed420c500b4751224a3c1bc829c82ddb2571c1604033499eefa415f44010000008b483045022100ac9a41dcf327bb5a9927a2b43ede6067427e24dc737b4331996b1fe326bb901f02202974a02008b35f66d87e2b8bbb6554c3cf26eead36baa4ffaade682c6fc8d6e0014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffea50912337935deeb270d8ec170ed43468f0f4ab8935513544e514e8cc82785f000000008a47304402205e668cac8f8200fc964b950a4e57668cba72f09915b2b5088d984a571eeb2f87022038295f8b5d84b1b784ca4059c3bb6a4c06098f27bde87d394bd896061ce95b32014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffc3860da9b15f6097f6948d6dbdcef23d0599d6b8d8678e44a2ab4584fd5759d0000000008a4730440220086f115a6a3cc10160eb7e84bfdc0dedd27b2e53f7a9d4c353ad5cab37a49be702202030d5668c906219319a25e37c933616f05a0cbcd7588c62d73db831ffd09818014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffad775652988c4b5414d96411f13c557f5ab822adfce99be9b1168966329cdd2d010000008b483045022100cd8fd9d3d845b267e8069e906ced74999587703de5692d2192d0a52db62f20a0022049c2798b6a7b776e2e37b0f707d7846a7d04b91d01173a31dddc19c8230e1755014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff37cd20486aae1e6b1564221a8e51635db9380dcc3379d60922248ab16c687b38010000008a47304402203f16aa07ca09e56ff1dfc5d80cc9be3716d9b1cdeb27c7ababeafc5a8af0861f02202e9296d02d55d6fbbd801e86c62598fb519e38a60e980d4ed023dec2d5c8cdfd014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff8e6bd97bd3b51402e59e71e99d131e3a1049a84ba6ff62c3b6fb41727d6e8d46010000008b483045022100fb0f62ed272d2be75dfc8a8ac33b6d82e251834f42eb1f604594efebd4b5ecf302200cc28c19730ed6eba6218826868c1c69b65cc706cf9357678c5ce8f0b8a20ad3014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff4e867af2aabb9bd83bd9100739079ea285883a30650f7e2e1481b52b7af97b2c000000008a4730440220539667b0654549b9b15816dcdeb6498ec687f6c67fd723096d1adf0fe22afead0220418b18da629e89af14787fdf547e41047d4c9a5bc71669ccd890d9021a33605a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7158df8dc7240f4cee203e70373fc27ac1fa11710b0483e606145d6b15011908000000008b483045022100c2c84d90e345c94614e34fe7c46f2375c49b84bb67c6ee11161439bdc1935f5f0220798e8cd6aa9da32ae8b84afd20aa4695970c0b5139f77bc4213c27b82dc8a03e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffde33795b1eac1a780dc22f07c04df835de23b71417f82fe77c395e212bfe33bf010000008a47304402204128e0aa2a2505477844653dfafd8323130c3e1308476049c7c479ad6972498e022002e8c192849b7e03211ad6756b0c14a4d80f4fa0a67418d6f1efc8a99ad79b7e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffe84954e619677efd1aeea6b00bc4452878d5918dbfb2a8e186fb6ee2fec4663d000000008b483045022100f273c82287b4fc9355546c2c1d4b2fa55127e554d26727d7b6fc231513fe31fb0220453a6f0103757d048cb574d7af5c48f3cba459ed1ec4a1c4c78e525fa1fe2f29014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff8580a0ed63d7bb72b3173dfd709f5878b1dc3d00d97f799fae88290fb20fb7c8010000008b483045022100990eca1c490380afe9433aad3cb733b6159d2663d508a66a022e7ed8a51bb5240220170eceda6d93055cb505b19fcbe942841dee55f9a6148ef1c999f0d8f675e1f3014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff24589ca66e842e9eda1119cdcf750913bf57704a8db23f07aab13e419e9a3d3f000000008b4830450221009a3550d9b7c8289a19a9d5c0515965a0dc8eb836fca04f1958b322b756777b4602203a710b314c1fd1172d86f9235b3c8eaaeb6854a053a05d6f44c0c8c01f93e613014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffe510ab6abdc2f0eceac3bb9c719a8c7506d824737e9ef0bcba0f4c118fa2ecbf010000008a473044022023a97e79a14144efad96ef147eb6ba6e372492b2b6fd6fa2f0e0e7d5ff846f5e02207179786786eb72f4666aab36d0b5e766a984f9167cbaae8d53c37de339ee8e34014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffe5536289772ae083c041c11bf0a963ed22c608d1b5bd4115158d11bdf031430d010000008b483045022100813886331fd81eefa7bca4c9d67b159ef58267dda0d2821bbb99646b52fcb3d80220701b7aac212609cc13a344c41135ec3028fbc00294f0fcf4a8cf9a36ee5d6b9e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7c8f010bc0a031404fa7823393641d152200c555b47651b2e0825c197553236f000000008b483045022100c2809cdca3471f5196e5fee32487965c8ff919910aa58459c90c622d5544e5eb022025b9a4b474b021269be086d8b90b766c17a3a1bac4bdd336cd423f8afb2ade2a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffcdbe97681bb0119b19317af8ccb326e992fdd232327c00a76237cebbfa536ba7000000008a47304402202ed67072685202d2ad437ffb1e35ddc6652e7aa35010541ed9e69eac811b379e022035d8224cbfa385eda5d79a776fb33b97a6aaee526d46f45bfbe1bf34f9914b8b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa2f8d98625a03e292702649f7c3f9118663b8bee54a731698c84bedd814d0917000000008b483045022100a3f56fdfe39c9312164d646298bf9b91eadf6c803d7a8206af0a3e948455c56402204451edf99e7c5ce263f9479260cce2c1596f6a23dbe580e71b98b44087524ae6014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff219db259b8f0779017c8b183713041ce18312152443ba1ab61a22e3017f76d61010000008b483045022100bc08e13b2f94a8100830ce531b2b2a06e70a21cdfdca10eac7f889b54e31338602205ebd473a861350671e9f08991d4512e3ded92c8a091e6f98194f3b08fb4b8e4a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd8760ba4d949f9002282bf254a4b414e2f1ce4d3e0b874c21c4e5f6fd3974589000000008b48304502210083d38ba80768150228d781cbeaa44fce32e99fc44558266de28bd92ea4d61f6b02204911bdff074b0b7541599070385f6c7cfacdd0d64a018ba9502f50539e4f8b0f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa2aca55af7f47273c1efd1930d43b10dfcdb4dd9675c95acc11b8ce085d02b92010000008b483045022100a1a98d816e3d3ef94cf774e166dfd3853b8b44fd019f06254018bcdf9cffa3ab02203ca23adf7ee90210136f103a03857ae8f021f406e290c6f6ec47f9ce11b02ff0014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd796e114a60b46521652d943ec69f89b708c44034e1403b7e906e505ac5de134010000008b483045022100f84173dd064f7c9e978599df7eb8ec4e689e294fab7da9f681a8bcd64449b85a022034c80a3e6c40ec4e1ef3f86b21ab2b138851863aad5c9c1711f784e080431875014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff51149c823fa3c91390b6fac44db7b3e67a25682364486d09727f5fe357cc247d000000008a4730440220080d35dd7f0112a1b4e327249aa5526d3ce79e388f4786ceb0b171759611ef0e02204f7211fb127fd188f9d88f3a65d351dd4465f4dae7323db11d2b1c6ec0097693014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa4be8955ffd66dfd7e06c220abc009a36607b6c7002e95094cf48b5f8b8c738b000000008b483045022100d3919ea8cdd491a699c68b1a2eb23849110cdf07c7e5d5a59a528ad1d1ba80a6022033944f0def54dea79cb28ae27a40e0e2c45d417ad4e2a044f99d5a9eb8d463bc014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3a310aba7c3e3dfdd24bae393d7b3f9ab0d4ebe7a9b2ae7b9e0543d7737bd3f5000000008a47304402202ce7b6b9b16cf6ed83ea30c395fd277e1d1edc48fa8202e206d6ba17d6f92059022028ba664e1618965e6f586b84989cb0df0f23d7222374653cd95fb8eec206820b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb93c390e1b4b837fa28e8a91628b8f08c3134bd754c2bd2b3906d198db9be11a010000008a47304402205bdee36f23809b9ed67d9b40d41eaeb02a6f7c314800c61fdced84e6e6d4afd3022028d03be15d43bd22f799f86a5a1fbf8744a8633b50d36cb1eb14115c4a332394014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb60d5082bcbf074956e986273799c5f9cf561b1440aac7bd2921eb35150f305d000000008a473044022025ef764daff376fa6ddf9641d4c2e6682ffd764d14b3af80895d2cd29d9b0e5602206cadfcc1bf24fe780fe9690c26419c166540c03a3917e597c7bd24b00d54c649014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffae3f8629958ea057d99081e94e956aae9f0fed7631bfe2055d86d823f7300df8000000008b4830450221008e5698e1ad05c297cae346fab32e6e0e4d8ccd497ac99d85b3058db3aca5024b02202153ef5dfd5218229bfb61bec530bfb87cf6d62d923f5e2e57b20617f99b3e2e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5a645dbcfe1d7f8c84362491d30be34147e29f5c5fe89ce41071cf82cd4fc090000000008a4730440220237d6e715aeafe591a4bbce13ca2205d4742e67ba7126cbe2e0548ea84f240f002202781feac551bc415f954c68412e75dd8161152a1123ceb09a5158ef02493931a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffbbeb3c7158a5a0c1c013c7d716f889936dc7317510caa3e00c1bdb2b82d9cc09000000008a4730440220186ccadbc99b4a2a05aa2a8cf30c4e7b2941452c5b2932defb7e3a7ff22c51d40220257aca08118cae27a827856365b541420dd568bd42e64e342874bdf35fe28d12014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffe34c3dcd2193f88130bbe0662b898a005d0c95554752aa99b7a5bb8868b45e0d000000008b483045022100e791d6f955bdf42a7deec8968e677592e666fcd11dde697d4d3f8e53d13f68b3022021bf1cb5084685d614ce6d876da9cf353bb8bd6a85bf176bffce8f509a458c97014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb19eec286c735a140c6fa491c9ab42f997e0f7d7822074526af4281c1b3f3643000000008b483045022100e5dade3f507443886b3d6ae840999e9ab2196b2e2895e7fdc835eff9ce93cd96022059f81d7217ef38f296db35bff0ba928baf7edb86bc543e810bb37af621e73dfa014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3247d44c025670b907831a6611b123322eb8028ec5ff393b68b20315fd16d28b000000008a47304402203a6fee558c5d4c52bf67ff18a8361cb7af1f850dbb0f930da270f7ae80d93363022021b8c162a1fa670af36e206affcef61b9f8bdbbc3448ccedaa9819d5c3f2d8d8014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff8e972204e06c5792a1ebaaec8426654ab5e5a7b1f292c4f94fe5590c0f7d85d2000000008b483045022100c3b507fe529100d38feb60c5f6d09b685468094bf060e8b7f41836088e1ecc6d0220370a76e3ca0252710351e2af8b88a50580265e0a7f96c70d4bbf751a04c5614a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6dda38a03bdfa0c362bb6f2b2cab2c6f69f5711f297555af29382841a69e99e0000000008b483045022100829071fdb1c8b1e77ced838aa68b5b21885219b0658d252346cf8130fa3acf0202207996837b080bd26087ea3572ef33617851b41c7a10c905fa34d1f4cbe6258e01014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff75571451fdc37d95afab561755d8a7852356bce9d4ba3148786e0b055be9824b000000008b483045022100920ccbe0ac458f1933f038752c170bc0cc479356f64d5d1b32117a9518c1859402202a4cb804b739f3bacd1b9d4b6bba0ef7cc44bf581619d2ecfef9ade76a3c5f7e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7b8b65702b722659bb26abc601d21bea053f503af730879e73b44b2df07d2005000000008a47304402204a663f4a80cc5602e325e096645497216e4a799686e8f3b5f1463dbd7112dea0022031a9fa3d5dd682dedb0b8dbdfd53cdd318a43ad448f434e932e7db3c50e21c71014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5483de99c58fe38bb9f18e39c5bee7e293feab4e5a1cbf33d809a9009a778e9e000000008a47304402202c3e0974aa34f852c20cc355374a6bfed1d519bd76593454ab1ae355b7aea20f0220180f297e3c2a2b83bf369bffddcd162d9bbb491ce4cb74c18822fec9bec36e26014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb6920943981c338874eb4b81bdbb238f7f9f7c364ddc2a4101589859cddc29bd000000008b483045022100e3e4521e165b08889b8add001a7f2e6d2e4cf784e175659c023367551335bb3702204672996b313569c15d6c4b6e5a7582325264878af7dea45234b53b4a1e94d736014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb979117ecba981a558c7fdaeb120dfd674ff65deff370c3fc3d033a36e7595be010000008b483045022100a8eae5b8478ecba8abffe1314e280386f78009b6c388f6a3cca25b61e8e1976002205d957f4e19cba9b09b630b53d4f9effcf3f2e9b8e6c548d7c67d90b1dcd0ebdc014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3bde6a3172c412b733a84b7aabc70cf261ae4d0f1b7f435e6ee21c8a547b1a9d010000008b483045022100f5014f528e70fa7a109a77b6a920290fb412fafbb2b20a52bc4ad398b670e00302203c53316f83aa14db4fb3de7556545e54424752809e08aef42cc4fcc1ac4a4fe3014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffad564f40dba7d0351204717637ffdf446c2a838eab98508b3563b64e4738d14f000000008a473044022019a80059a77d926ea71a8fadb9c1e8a2f0538fe736be5bbe49411f8917f32ffc022073ac901cb186196c5f70faf346039ed9d2a534f3bf38d05db2be4aa1f6f7cd3b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff9a3335f1bd1029838236285fffa275d042c5c55edeb86f8e9f166f23b38b1407000000008b483045022100b4c615707e0084697d8bf79d0764cb02d6fe7c7a1325fcace111b177b02db1d90220658caecc16db36b4321d3bb85e348a74ef18ffbcb0247cc97fce27bcef8db6f5014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff84e05905b9f91889ad2bbf1581de3b2cef77726987ddddfb45cb67407a28d4c6010000008a47304402203e6d59a681e2edda2718b013f7f2c88972b8385b9ed62a5349da1e9b9580279c0220454262933f9f008c0bec68c81c3188f29f124a3e2bf24dea8b875dd842196cb2014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffcf5c10bf6a3639a715e876dccbd829d0735b8072e793868dfb3ef72e0342aacc000000008a4730440220056b1c4c548224de2adb5b3019fb95b867767a84f5dd9bb9cc091392f607c7ed0220272772c57ef153d4523b82572a976f8974f79054d64754177db0e2579227888c014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff1353fd4879d2278fa903ae59f4822c219e87110b8ead3d31b2c3a6410bdef4fe010000008b483045022100ef2baf818f7f44bfd339aee5ab8609b1d344eb31a94184f8ad3ba1f19767dfb202201371689e1adb59af5c8ebc6e35f23eeabb612c43bf26403cec751b31fdd96bc8014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5399d5a7d6530ad48628e7a038972745a1769c0c299898a9c9136f6ac5533a35010000008a473044022045534e0591902f508ff50fe55845729e88d44ff7d9bcb93a0afdb00576fb87d102205f79b79a3795b2857eee7aa819dd794012e73a4d10e58a33d8131e8337bf5e22014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffefca3bafc2d9d0b40da3bf3152ca227b5e380fe6c28829b00512007a5aeb4946000000008b483045022100f5fe50463e05d2a3a3750e3556ca046a768f67349a7ed109072f0e66bbd23f8802202fc58b737a8095d1bf0b0d22d836408581bf323d174afe9b293c4eba8e3ae44c014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff19c71b3562d72d49250f6532ef8fc0e6e1d5e5a01dd36abd72be7d2bf011ee5d000000008a4730440220697b858e69242298680abfdb451fb9e82f9f6cb04c791a1c824b7c804cc16827022059d229da3b946f4f451e4e697e4485ea91a74207d4f5929602271335f7f3a8dd014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5bb75916582eee737a89e43d804d7c95a3ce0076efae967ab58d322497413a2c000000008b483045022100c55a8673ac09b50d1f55bcd8436834759b5d8b76525288234856c18aeb4447fe02204c512676c1411d8f7ba3bbf1668bba888e3732bee4284c3160102619c91e36cf014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff341eadc37fe707beb9cb10820d3dafed594c2db879bc86073590f0e425789b31000000008b483045022100e74da24be0cc25f466a4f32bf607ba08643b8f38fdc645995a5175ec064b174902207afdcb708992f7269c8e8abf22840fc77d8e5993192f96b502a66296e63e3430014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd7be31e7640389705769614fb7e7b1a406105f4c77f3430321288241eace429a010000008b483045022100dbe14444bd0d76a13deb03a407e007513fab9d8fedf00449dfa67c7c9b909986022051a761687d81fd381501b61a6c115c7e2996680a9985f4cf44a8fabb92d6647d014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff9921eb3073740ec243a76c2fb58f2ceffaf63abce4dfedb8128f51f1a17223f3000000008a47304402205a54eb9d87b9ddb8145e3e4c04e8f2b930f5b003d678980f823e13a205ba771902206169c29b8314c19f8bb7a26af21d9d63221321b9cdb87ebfb44ff742f0e9d0f7014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb8c300713e7999b0913f7dcf9615339400a7bd9f6d1158de1d291c15d96d7f4e000000008a4730440220799e39435b2080b98336c844d5a997ed783986d07a69567005548a5e5107464c022003d741fdcedd1fa7c4a82625b6f984bc6f16bac6d68db8a3ae4ed7b68e38a49c014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff739960f57711539f31ab19c617003fd874fd35c224e00a7bdb754ef7572da76d000000008a47304402202f29fd429fa832762307779eb15d4fa0b4abfcfd33d00ac94d31e10669ac4b5b0220126c3399e52f5035ca9cbb1b21edc21757778196541461ff1384a25a1699f6c1014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffcb1cab67a095611e20621cc2fb878095609eff0fc0747b06bfe93111dbfa5838000000008a473044022073e8ca9be3260b524207a8fac634b1490e1ceaadf41119b4c10a07fc59f8ee2002200840eb9c98bfd9c3ee72297ca5d2f9b72ce7d3416a0998b62cd9116a960edaa7014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffffa386af16e1c935e1cf0f82b6c7029da3ff28136423dd647d8d6522269487ca000000008a47304402203cd12209d80f2d93fc31c3e4aefd8e807710efec83aae3fd34ddade3dc3ac2a3022030729e72abd3e3ba61a376152a97ef6bfbf02ba23c0dcd848ac1e208e5884420014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb6b803d129190d7560678decff3967de9813e32e191884a4406e3e4b8a38364e000000008b483045022100f32857b4044ba6701765a5ad531c16f3c129a681c30a156440f3d3a4e4ec8c56022002daa8432d861214fcfa164108768b74b7ff97886d3bad9885a655e0a6d61d98014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffedff6fb28d5cae891c26c8786af0447643fec708759e24cb16f4863052d5ed9f000000008b4830450221008a5e583a76082dd398b9b1a5bf97572895d434e5d1fb5e783e273e18b8fd75d002202c6f6a296645f5ab2522a69b7f0be28dfa36daec6b353f4c1caf8ae5ce7efaf5014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3f06bdf732290cefa08becffadb08ac9c85aff5bcb320eb84344a0312dbb19f1000000008a473044022067475d8413b0ab578daac369522b046c4cc9d067e628edc50a9249365cbdc696022022717187cecf56df7e7478cfd5c1a41c0d2106a88560cac26e8ab496b5858b47014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff76d7eeebfe78df9b2345b901a3cf1b811d785d33bfc9ef69e0eee8ab2bac87e4000000008b48304502210089ab8911c742bb4190a24969ea3906d495f5ecf196ac962125b375e32080160302205b857923b24e44bc6818707e630f223c590773f7539952d188b6b439addf8d7f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff1b67c1337d493431ba6555403bb29074f8594b9bd89bdc171268b6cb8d5fabbc000000008b48304502210086dc86d6ade4211304b0fd399c655c4d5b27431296dfb1f037996232452feac902204cfb1430b507445582343967e5f5def131cb786321fefc53d8c07176e2262a6c014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff8a5e0517eab85e25654c717a25c72505ff56570b395d1e6493f22d87085f3d1c000000008a47304402205df97747e1ebf980b5a7ce5c1eba4bad652307d1d36c230dcffa02fa91781d4c022010ceb03ce1a0ce334f92799e6cb58711e677ea3fcd2a5b23f8b415cdfc7703de014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffc41c319801e90eadc57b4f08c09c204b0c87a8939d421eba2e8f46a05a814643000000008a47304402202ea29bdbd504a29e67206f618090c1ba496668561a93f0b26643bb132a944b2002201e789b05303dd5cc68aed8d9c5ff78d4a129bfb5536ffd6214ece0219ee5bd3f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff87411b1e6a12e903d3c2ddf919ff303feefe09afb13f8bb853ff9d0f987cacd3010000008b48304502210091f26b68caff41a6e423d36ff91102c24ad6ae4858ec791afad4367f2eae7897022076bf9bf94b8343a7bc630b607fa273ab0390f7cd2af9fa9b4d9de4b7a7112e80014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd4547281b9edc214d147cba1d7408b5ee26c6c2389348b7d3e7859b54714d98d000000008b483045022100bc0ef15413d09d82ef92d065210741e9241aebbe2c3bba552422c775861d894e0220174058888e772f68206baa57696941b0a538cb1bfcafe7713e5c33cb9b5e3383014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff00b9358cbb2deef7cf26a8e7dc8f8d2c31d1ee56d6c7042ca031f4c30564c9c9000000008a47304402206de0742f744af7968b90c7e1eef0735af93cdd12496760917f9e0dc5a17bf6dc0220788c2f233444bc8a8155121c53c68e2c10463f94e8f409d49914b05c1ef62ecb014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff005d8b79412ff14f0ada5292feaff0ffa9138e1276aeeea6f66c994ab57b47f2000000008b483045022100fbc626da5f5c298532fdb3aff395ca38f7a36fb9591c06ea6ecdfadbd72d8a3302201f9a078c57189ca9dc7f7f488b80598b75e25d2fb0eaf2fbe12fcd88124c161a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff920f59399168b74eb11785fa6701f41e692b2ffac1fa29b446de85fa0cde86c1000000008a473044022063ef51b31d42c8001ce1a15d57ac90d5bbc36ca39a091eac4e7f1373a0ac8a2c022022285e9e83fa333780162268473decaa3be30d62ac6f4e67d4e0353cf7eea38a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff0d4ed410fa70a65455632fc4e4cc609228151a21b5b9b0dfb292afda40a10c4a000000008a473044022067ed12233108143cbcd70f21e4ecdc6a89aa648be55d9b3597d2494eb5d664480220268784e125afc8e96d7b1a28b1f46af59701d4bbaa47529577cad8dc4bf27032014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffac8bead47aac599802cdf05d30f62b1404248fcc9f2e1d76b768384bde25796b000000008a4730440220250cdb1f8c7e33a6887554c8645aeffbad4434ea33703b51b032223ef9a8b00502205a95738f10bf77717776ab15173038cd68e7276b4b3ff9aa732553ef579c74db014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffbbf3cbe2bf5a4fcb0d10224b5e1dca14036a806de3bf46654ea18a6190054f8c000000008a47304402202fc4d3538bf1601ae9871f3e27cab9fc2eb427e05101fcbe0ea411d8a8238a480220076a72ab277f468eeeb1615c478071b6fff12300caadcfaba5bf4eedd5c8838f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb87e4c78efbb64f9f92b27c694b5b7c784f80606028c1d487994e47c2a274914000000008b48304502210097dcd6cdaa57786c862dc115db567e4cb386df0869908cf89c19bd17233d488d02207feb6656c0f769d06a9ca9b75f1ef13fa25822eccbc0fafaff044c6d908885c2014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd5773bfe1952cfc90cc46490fa0a126f52d72847a0ce0ab919f6bacc51850d3a000000008b483045022100a441d259d25af870d39eccc0685a6c8aee1ce27b400abdb07f9565f414bb5bda022032277fe7eb2a08390e50c89dcfdc937c3fc892ee9a946976c91202cfe7c427f8014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffbc594876b467b2b0ecaf21d327681a3be9989778a5804ad49e8f817fa1f27997000000008a47304402205f001fa929d3092c150c243b31abcbb15a7dcddd7162abfe08b4dc33961c25bb022072ae3b951b7a660a8e1a91b7265fdad94182b3692b74cb0407e8247cbf56e96b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffe32b4cab0e68dc9ff28e822dec3745f0c103ecb2f337a29d22c0dacb4cb6e4a6010000008a47304402202daa1fa6db42805ea5c841eafe1c0dcab99f2e269d96c725ea176fe2116ed6c402201650154cb20b6a5f535558ab67f3dd16c621c1e745b7c142b4d8514a2cb59d6f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd1b5374661c8cae0e214e8309d8c04ed3cc9c7689f2b5a98354ec15d6d0be2a6000000008a473044022046ee82dedddc26ca99eed8499b12a695a9c7a2ef1381b17e50552ca61cd228d902206fdf2a5d54e55015105dcbdb19b33702dd1a01f2f1ca8e621c45cf759cbd282b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff2cd9d8b0c6e15541f1b8590154e02cc8950ee5e9b694907368db04134192325e000000008a47304402203f251d4d7843f0f9e8c2b679df7f4ff693df1b31cacc1470e5f7ceb8c6be0c7a02205706ab52b580d5be3385fbacb34593fa1f49d910e6411aff9938f8261ddb6fba014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd8d5f536f8cfb7e90a37fdaaea180db7bf30568e6c286bfc86fd92a36d00fcd5000000008a47304402201cf7e79bd55e39a9daad974e903caa4347a4fa79ae380caf41b5254bf74bb64402204ac03a15b33f5cf4132a67b307edbc814a37bf1c781581420d95d8526a94ac23014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff613c450cc4ada24ac4637e521fc758349f02f8b82183b0bdfa071e3d8fbda581000000008b483045022100cfe5bbf4116cc4a70b204abfceb2791581a072634364d27cd6638c8ecd7301360220708cfdcf732825f742679a83cc2922985f5aaac6458aeaae51f9262b35331165014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6d3c2512f4e9d6e1f992264b0f8d3876783f08bfe9dd96fe92c5fb21c971b075010000008b483045022100f051f41838cf40a4aa1faa774026b125ee8986d3b785694a56299e753484c587022049c5ebe12a1b44d198abe3b4ffadc266abd9741dfd1ab9533a03a5620527f6a8014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff1cdcd99d5fece0543ac159abe33fc1189fcd55988a133ccb74380e05833dff33000000008a47304402207c2e321bec96b02445d522bdcee1a17c2f693623afddfb4088c82ea5071ea56b02203b49d9b7d7118794ea6849e7d63387d894ffe8c881cd0edaeb6bca2db7476767014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5bb6206b44dbb0a762caab0ddddf0c6a7d2ec3a4eaaebfc1ce8611cc0438a626000000008b483045022100a43ce3cd6788b2ec10617d4540959da885d62fe90875c2c6dc6627a5d156d5f80220789fd46d7324dfd50e346f3a9568c36f05bfbd2c793ce19cff31c71dd9b0354f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff410a2272cbbdf4d1c3d85e487932959c1c66fa09467fbc32bd6bd478182c1698000000008b483045022100d02ea2ca50f8f4ad625f7f738e487fb523c75d104b06b9704870ebe4f457375702203eee932f0fc09aa5c19b54b5b51ba030b32e677c212438b36207560f340b912d014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff90f0ebc055fdabc61ec6fa5c2e1fabe664b66c8e95dd25abffa7116baf9eb787000000008b483045022100fdcc51cc1e937b35463fee6f1a49874980bbb8f5143ee587adda7a1c35ee8fbf0220762707a80fcc93e94c55fdf2b7d4daf640691810405f7b64bc6e353dac8471ac014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff8242d17e7b3e7a61334eadc44357d499a397b9453570831735753b50768d3f69000000008a47304402206d684282153be872c4e11772cf9d915407a09f2ad49ad8b2ca76a44c79c9aa0202206f035aef0350317f6644280981d43975607ef5b157d74d73bff94b4216807662014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6e9d70f0655a4b92a677f7646b820b349cbb227aeb6f9d6cf0d1f72bbe891e25000000008b483045022100801dca6e34108199bb96d955eb37556ad3e8751b649e9991a082feda63cf756d02207aad67f56de4ae23028fc0eff5a905a26e5455cbbd45a6ac96c4ef356626e323014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7b470a939f1dafbef8d3dfb097800a0a678ce4c76ac700923849b1fb4afb7216000000008a47304402202758edc5a3f8b1e3433b8f094d8deafb6cea8b4ad77fc9548c67a54298b2846402204f1fc190cb5b4388f1f500640a99752e2cf481a07f67bfed4ea8ec56565455ec014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3fc6443e9c98af6c888f420a7b130d93fc5bb43f6d0dbe4cec07390ee72813aa000000008b48304502210087a84272b59b56835ac01806589a9a2b902327edb4f9449c710a0e09e0c2518b022062a1114886705a19d448d7e22c2bee2c13f64860eeb4ef2879d60b0226661e08014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff04d0bcba3b007db4a9c06564d40afbff8183f9ff23bbbdebf7ff995051d2fc94000000008a47304402203b031f0b0b884383d498e78a937854ca1d0316d4d17e4fc2d5c047ee7b98db2602206789a7890ef8a41f9877e6885337bdfe6b564d1b9f7f29ebe19e5cbf330e3f87014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff77e7c34ae68833d9dc46c3c81dceaf4bbafcb64546b021557dd5949d2918f60f000000008a47304402204fb919701f43f138d2be8cc68dbc8dda72e6e76c47e472e89589f8fe67ae211702202cc2b296ae6052d22da1e16abf57588579c89e41e60c16f8423fa675e4337076014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb9b5dfb3aad4209ebf9800b7cd1682b7c230aec93ad443385873bdcd22fd0df0010000008b483045022100e5a755a5d58cbcc719d782f0f26efc0cfea38a8eab375bf3267cc4327dc67d090220262a6641c87c0a5c465faadc8b9bc4558975b990633d8c7ceff85e4eed7e4813014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff97bc3f5ba4abc5b8868d26262a126dca3703b1ec087530408cf11356e89b03db000000008b483045022100d9e3088b40a6179018b0778f8dbdf2f4475c7e3c10d3dcda1334812333a47ff202207ec5631f210a76fc5c440c2d21bc05d5b9fe8f8b0ec84b0c3da0b93e509b5a5a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff504ffaba0bb537c161043856362ef433ad59008a179c5fd160b2b2752ebb3681000000008b483045022100a9549a4a6ad21118c583e75999aecee0c5150361e8ed244f08e3808b997ad7ed02201827c923c7c6001aa4b9fbb5ea514dffc5b5047393273df76ab9ec9a8a8b5895014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5822c751db3aec1f9c186ad04c647833fb306d65971aac91b93d28c9212c35f4000000008a4730440220707b3659923b8dfd76c7d903dbfbe0aadea217e6a275cd427d160149bf24a30102203f3cad64eb867d1e81f3eb5025bdeb94ec15df254367a6da85decb7f923cc6c5014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7c8176d62010dceefdcaaf2041c93751f4921bcc66e11af6bd7407cd023b1c31010000008a47304402207500f7ae56f554fa5da181796d8a2c3789ef83a0c19a3195e512261669a52aca022004cf4984ac55396daacb6f0fdc158dafcf879916feec9b5293bfa295d7da6919014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffca192f3548af8c5e4527acb43698086b7d8554a6186f45d0f0677d4341497058000000008b483045022100d17b1db7013bea0ffc9682ae183367030034ab17951636bcf18196e818d4f96202207d284c19394d64d0db8485ff68086e140cd9c0c4750fdf23f98478fcbce2c945014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff302d83d4861b1805001d50261f5a57c16accfe008c71c7c2b3bc315a045650c0000000008b483045022100be2858873d48f5509da2f0d24f4c2e0ffa170242021e30cad8519884474cb13302206680758cb2cc534b2ba1d5a76cc50e25438ced3741410d0249dfae6c96b1b3e5014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3a55da4edf4d70fa0365e33978ccb01e4cec1580f88151bd4afe464f13bb0bab000000008b483045022100867bc26135f26fc394d4fbb7543769cab8d396b441e8663fc98d90130469a7c50220048fb394a87e67e4aee54374a6838d96a04305c315a07f428b2a59acef6fdd38014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffca848424a7316a36a62d61706881a46ccdf9b674233ab36c9df770faea8027d5000000008b4830450221008b379fc7c944ae0c5aea0d999bfe255a4b246a83b7672010f95bbbefe19c92cc0220662010cc05e771ea21032c6f4322f9c519a51945eeeca629f5d2c2f3dc56a76d014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffab95bd888d54daaf79c9ffa61919094a7984e018ff7e96677c551672ac197120000000008b483045022100ed4e1245c7335d86da3d1344ab431a217added435df3f9e29b5f75951383381302200905a3663b79abde7b1908403a120baf9a86fc58f88858aa2b09f0c0aa4c929b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff005d5bf27713fcfbe9eb5241413b4b878d9ced694085b87b0c6b78862ed58133000000008a473044022040ef397be77a634b06a169811c7683c1d057c8e382b2e2a70818370acee89e0b022036d4d6132f815e5fb459c92b720f5af12b5ad492731d7dbc09c761693b4ea17e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff320d3d03fd2dc18439b9548d7a087b8e1e050e33903cd2fab7277ed219d605d8000000008b483045022100bdf2df098ed75d0a9a703cfd452a74ff2a11bc5e302086fa97e36866dd091184022033cbe0d4cecc337274d5b2995d403d4117180c41e68c7c1142800ef853f15773014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff24ec65c908b33ed3bfc086cf10e85df742308afbad37cd5af41c07ff85a2cf95010000008a4730440220650a1491229ba89784106d55904d0e19df732e583c0ee6324456bbf9186ffc120220331de16013fce3d496057903a7f68a53a1ff29e733523711c5afefbb64627a5a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff42a2a3b811d8fb56b7a4223ff019967eb5041ac5bf659adc358ba067ca0259bb010000008b483045022100e1909855740e31c8405cd39ca6777ed2e2d0b0416d3488d3ddd0463164724e5f0220623010050534f4f6c26d51cdc421b27fc657048380c1241d5e756ab11e121d04014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa1034a6a2d9d4c65ea7648a31226024d39153076ba09b9d45b393725915cfb30000000008b483045022100806de8e5fadd6eb2c5ecb78af6ea63382ca80e03984edcb888ccf894d912676c0220526be08d98dce7b22a87cf32c09d9f0f12502d29a03e0604330e0c8a87346e3a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff0698cb56a5908c6192e0c8e6d041af203127f9354211806fa07465725cafbfb7000000008b483045022100d376b3a52f06dd22289bdd9b40840a16bb3feeea3fa5af0ef0e327cd59bb51a102201c475286124ad3b3d21fc7ce1fae1a4682f5a3bdae3ca108f84aa71e4a5faff9014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd2810d3708794a0ca2f443aa48fbe57059c8522aae88d7626aa003fca715027b000000008a47304402206ce297a3fff1f387e8b51e2ee0a3989188a6704d2c8aaecd6456c5149420486f022052905a95e2df32b446c965c28090ed6f6905fdd8b37576cf81894999a4836b18014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff07a5c3cde1aee46dd25ffeb5d2a38c389c0935e104dfe5fe80358da0c9ff1fad000000008a47304402205d25eacc41ac1542219bf37e4987cb43320e2bf9ed728f2bda380a687e64969b02203aaccc2840818b2e0b071eea44d4839d77f02ddbbd1e3d723dcde4a449a69e69014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd328309a12f9a353b568c8942f646f4d4872556b244975ee2c4f32b58d8532e1010000008a4730440220691de8e44b34e8f8d2258d5615367ad8411598cf558dd39d7e3a3263fc214cee0220375a35915efc51ac011244e40eccc9c9287767601a5c130e64944a8c6182ebc2014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffdf261cdd4022f6a492796611e95e22f39361baf7fffd3c2f869836b191326aa6010000008b483045022100c755e0ebaeec6c5876d9bae03b7c371c23cc88b742754a3566f034bb3382941302200a159f45ece620a9c7264483c5a35dda1c52f98d1e7c1664d0051ae265655cb5014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31fffffffff1cddd934535d9494cd76b4fd950cc1f18a9bb4cd4f5bc8053d7f6595cb9a277010000008a473044022008d270eed1bf8eda19798a8587eed7dacc943b5ccdef759756861bd86d6979e80220123c49dca5b8c9f46e37e727b55fb6400a72d37bf0b869f98ca51a3068d9e24a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31fffffffff2121cbafc846910733618448595e5227285a4bbbb222ef751e79c276b8a5a5b010000008b483045022100ee978bb7a709e99b3de5bf0f4d213df1fff26145a9ba8b7eb1be7f4c62810b5402200f26d59fcc72489834d6b77659e065ba324c83b323350274d41628324f98e99e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd588a900826ff889224a12bc60ec4ddb1aedafe345c4dcef01b8f8ff53952c8c010000008b4830450221009bd2b206b18db4f2a79dc0468c778b5434cf032328a7388f5ce6ac1e1d930f46022049de0061a046f8c6a372f75f1f1ae33baa998085c5a67e93cc73bc8381cb5850014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff63c1431b6515b603e3ecee029845246a4cc30aa445fb16a654c7f3aaed307c8d010000008a47304402205fe9e3253c5b7062344b9446b6c110ff30979f120623839377800120229ff2a202204b3f996326348c13804c01e061de002e6a9f0eabff0122ea786f167ef9937906014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff39c83f0172a417fe3afa61601d11cc64db00491d0ea5f409c50782b7f185ef33010000008b483045022100dbf8faf92cb26d2cb7e849fd6779bd471cde07dd5e3af43fc5f91e16b9a58abc0220246d519d7f42eb64a5c36964863289dbad4a2a24597d48ae6215cc75c1bdb1b5014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffb38a77cc124413448d78400e54534142da5374de08023f1326915822b441f1f4010000008a47304402206c6acfb1a35cd54e5d6a37da42d382db6b2ca45b5e398b1f3af05905ebd1e1b1022001cf35c338d098a8a59735f504f5c79d4a02e3a929ab0d5e7c1aaec1b2c8a8bf014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff1d32ac194c07011c38e81f7ea7dfddbfb63e947e47ebddf7d2f918de49d261f0000000008b48304502210099d9a8cd8d28b4b604fb600882695d6a134468dfaa32b560ff7b1199cd54850d02204e69288a83597f174823bb61228390061942013141194d70a828137036c329da014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6e6a9f6d2fc82e551a32366407056e8c3cbd83154553b1b03152df76ea7aff83000000008b483045022100e89ce7cbb9f6971f5de1445df82ce70763623f797b2466c2ff72759ff05d4b9d022028ed2d146dc025d6df3276baaa5ac4d7d58f44a61c58f05b755aa29de943c18d014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff132aad09768c7203015b216bccb0cb0ef8ad2f1a88c852b38ed2b47e54d84852000000008b483045022100fe99f91e45dc47fd23397588edbd85644daed0af86b8df537ba3e40ae8661c510220658080555caa11751066ad65a13227ec08dd22a090470f55cb8eee8d5c9d4aa3014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff74699364152cf9503099025f2633e64040215009f88c48f5a53a1846c76774d2000000008b483045022100bf3582ea3e3abd9e2e85e3f3d2c113b9d9744583b5a775260e5473932366a8c502207f3d6395d48bc3a2a9550ec8ba04988f12a846174f668a43bd7999d4cc289931014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffd3d918664de66eb38492b1a560dbabfd3aad8bc417230302c85de69084c5eb6c000000008a47304402204af19318445c1bac157f30c18f83cab8ab01a60aed6fe3dbea3c4262ee1e047a022079b0aee834f9c150ee80c227a08d04c1459be273e05b7a956bf0daf76c9faaa9014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff8a78c1c76af02f23da8fa9972a64c4a2ea937fb9e74ded960ff6d9865c290361000000008a47304402201f1ff75678b2cde661d9924508f05680c655d0295254555ec1f0e0434523ecbe0220700157e0f1f855adf5896d1e859d1c528f519f583bbe9198b517b01962ace788014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7259a0d989c12b84cb6c8287ea3a14d991cb43eb82c9414561e11e433693e789000000008b483045022100c1723a623ac964b47f9eda1fa0d677b654509f2aafdf31a9ccec9b780322594e0220451a0afdde1853efdf6fa04255aa9145b2f32cec01dd3cf8eea533b553c6e9bd014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff1a670f8e3a391c3c1825b5e240b4386d07e06c85999037e73350c2d8faf7fba5000000008a47304402202c038661489357f534f70cd4bc62f2de54cfc17ce3bdaf906f2597e024ddb70202203b4f23660e011052a1b6455f4e3a8475ccdf9aec711451fd36050e9e4ace6ecd014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7ef67493e1ca6e65e4689c761fa9ad0ea4db3a8c466754cc0e5274a87c88ba3c000000008a47304402204452da0cddc8ce6e0028d01784d158cbe16e53bb4d6b9bb47498885ceaa18c3b02206aafa51747d4df8aaab35ab3200446734e496cbb1a602434c4b64154bf4febeb014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff483c8cc18b5b1436540955c219dad2f7462def2c4f4b516dde21db381595ab64000000008a47304402204febc808cf2533b62b3fd11f7031bc63494b54bb5bb24b6ce9df1991d8361562022023d3a81bc83a769cf9177d38b728998dfff09983b0b91246f70f8b6d201d3be3014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff215bdac20fe63e8335f9ea0c43f0d8a25d33a98aaac3d9cce2f582e9963f1b60000000008a473044022073294aa66121a59af5a437731c9e5cc43551f4881c1b204f0eebd2ced278df4a02206006c675103253b36c272c2656522c816b0bd1507001d578d9141762cf8669c7014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa91d00e346908b7dbfb1b99d50cf97174326c96baf2c80f46b1fc2a3c59f21a3000000008a47304402200d4f7c879f014360685964a6bdd2c893166399ce0b58cf2dc4b0d41711e769b302204af0b15fcb364f1244e94d282d34de8d4e01cae46ea48122649c3a8344fc237e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff1ac78c4e3f938507b6221ab9c9f3a626f75401d0cc9cd52610f81f442c26d295000000008b483045022100d19a1aba79b29b94dd3e3cb9eea7e6fed13af4a92f12321e2a062fd6f7b7fc1f0220360a0bc78872046f29dd7cb5bc038f0150c9ea255e2e0f6c774c6c36c994221a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff2d91dce60581601295b0f7ff77b30b40fe9f78dcf2fad0fa9ba6fe314dda731c010000008a47304402204b331c66e6c6c57f1a62727e3b826a9a41a44d76d28e971e3b6840e12907bd8e02202cc7007afd33b189cd9bd8a942b39888e32d4add4d7986398d2b4332626b3517014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff935aaeb916d3407de9642165789893f1c9e7fd9efe32c9bc28d7aa47a3bc074c010000008b483045022100c0337f80e2292fde2a35c355caa3db54d007b37ca154ffcc1f6133beba00664e02205ec8fae0b65b7aeb3e22adfe233d9829b7c773c00a695b5b90bfb948f38e931f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6a25951027174bf71897b0053f6b7802c806aab3365985bda5f6bdb0d5471023010000008b483045022100cd020ee32cfc152bfb7bd9c1e6ca0b7338a7f06fe896d5eb98581147c8dd9c1f022034e74292c5371c6639d6feb59050fd30e6c520f7b68c44c2908e05643fb8b040014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff57ad14afc7991423c5334bcd87d1a13d16c0c8cd1be9c64b6353b0b35f902d3b000000008b483045022100b0b8801dd2230d1f0c6e4c077ae47917900902ff446472f257ff61c95eaf61a402207398f2dc2a16042acfeffa0196761f96c451b0a06db971f9180e17165438fed9014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa4235ea316fd8e8acb55c1c21f1bd3301f60bb1abf38ccb8888e9c7abd0d8c55000000008a4730440220698bf4e31c91ae559f3f754c06740c98b74accaca0d2ff5d6e1c7536c44f169a02205e4dc61282e66f9e53bbb63e87c75d98d954478e1545beca45f498b12babf840014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff5e92da56e2fabe175184be5c050b88d35d5b8333642ddabb2e13738e856ca200000000008b483045022100c9345da7f30cc6828487a8283e1c64209ef0e29761c83297e61428224dddbde4022042085694970b494cec4a19a624ca03870ce6eeb57acda5aa8e71685dd20e6f9a014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff89b0db9276f277e311df97deee08b19339236b25ee841d663b1957ad953d1e0e000000008b483045022100ec0ea2ed79bf747637c932c567e958eef7efa699104e950fc358a5ca8f9d7b52022038e78b215d0e1decf4ca0ec3183548bc58806aabdb0ea6bf13c2f6762bc2e8a8014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff092fa51d98dd7c8aaaa8b00a4887d80d4f74047e678a61ec5e5c73e32d61ffbe000000008b483045022100d3da2824110c0c6b7afea911a6abc92f600dcf8a011cc1c535ec00c520cd8a6b0220320c14ce6d4b7d3e4afe1ce756d9b53d8276c053856a5db720ed299ef2f2a83e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6af4728adb84d71999f175f0857930699508950727fbf3e99d44d8faa11391b0010000008a47304402203be009029197b002a5299979235fc2e87c014fd1cd05ee8437f5e07f1d959ce40220516915baa772c83c1db33dad66071223f90346ad4a80946cb3fec9de28ec180e014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff025d770801f54a073e14f1577ab3e392ce4cc606fed40dd10ce502ef2fd6319a000000008b483045022100e40f3c18270265710d1c13eff2973504e6e5495c6669d5be8e76ef37d467624802207cbc8d70d8d67d883865b5d830e88663230b501a71682bc24a85eeb4125bff49014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff6aaab7a7deeb45df42475cbb94dfe7ebfeb933a0701007d3598e570d52057eee000000008a473044022062eb92b45fde372be23a4303d6db8f18fcd5df0ebec8c4ac09e92671bc5c8ddc02205d619dda7697f12d0919c5e279fda221a7cdb34fc803c68397f7ad1a9113ebb8014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3f3f10bca4e619d545eec4c8de8d39f2f00d2e11496de84592bf0d359c882072000000008b4830450221008cfad63762e582869626df188e02d1c0ba60d007a8db4187fc24871712fa26b502200b4a6f3ddd6a111e50c677e26add7a86c3b7e3c6d4d0f1f323f12e5d74ce31e4014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffa331749f551ee590a5f2eb0908ddcf1bc11510ade17341b499718e1721c0c62b010000008a47304402207e8d20740a72b1c0f408a245a3205c56eaa719c7e8e582877103bdf29e123d5c0220503be1571dca8c2e9e7a33b886efe8950a2630f10e3346a84cacc5fa2228e3bf014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffbccbc5790606effb348cf836286c780c6924a187fa2a5e7d8b6a1e09d8a1bee0010000008b4830450221008b26d9f2a9c8fb48e4bb58936bffdf827e01021630dbb046bcbffe32b2a25e3d02207e9a0f8d87e894d79d26e7b1ae7c31fff681cf8f750a4cc53908323abaa6607c014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff091205b3a39937f559baa29509fd05f5a4ee8c99bcb2ea9dae01a711b09d5eea000000008a47304402200e5e51efcb65fe808bbd651899bd0066676423dfac8632741fdabdd6f871ad3c0220067c775af3a930d87517d9d47f5270ee842151c5f7b386a69813d34df6f56b94014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff3bd2e5f5963812c511990473f571df69b7c771ff39837f5cf78763f2d0ae720b010000008a47304402205c2112bc85801508d99068b15be0eb7e6659294b9d2fa5178590c213d220cd8c02207f5eca0f05a6e222a162fe315e37635ad0cb5c9eac15db1b4f034a43908e8bd6014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff2640720e73ca59450543e5d7e31df9d7627b3b813f39a4f9f1c5c0aa33eab6f1000000008a47304402204ecda19976af6e6ab59dbb8c2fd96d5f8b69cad6605517c2c0d5d83427b230a202202e575fa7b7702fc9600ed471c794903ea2b6654624e711b4e98fb9d68dee6ce9014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffc781bf6d0c5a01c9a664a9ecdbc430244a9959f1a1e6f411fcfef32a0effa4bf000000008a47304402206dbc53812699cdd117d98a2f40fecb45eef0a3cdb3421e1835f279bf71a8250e02205c7c5014c0274a4bf2354a69db5e1fb0619114e484fd7697218cbb1a4f1bc01b014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff73a878aa68bf12ed0f027367f0ca1b75ef3c685f93019f9a0fa2369428e99f84000000008a47304402201d9f838135c01a453e2f57b5e20b896510c4c6b3ff9c682fc52bf94a1bc726dd02201975732143c12b706a0389a002dd8eb8cf02cc35fed12eb4cb169b1c4ead5b80014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffc6c0b577374e63c8be2f3673ee6877a15cb1957c2459d668e8c16791c18fd385010000008a47304402207a921c13c5b80154379a7e530d557e3f8f36438f87f9083638912e201f95a3d4022056a253d5f2fa5be5573fdb0e63762b44f34c2ca2837c506845ed1ff084f4de9f014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31fffffffff7178c2ad8a788cd9b8414c316a90e7a2b9aa4d0e3500a078bb78f78adaf2f27010000008b4830450221009d5983a820386a665e3abe92949aaba92ee9efd4dfce0f6cdc64ba1d5fa6952c0220564297e5930c4895c0c244be47377e1be4f207f96aa111219b1fa1e8200506c0014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff7e9eb93b7347214c7a4fe15bcbb753124a84ebd259013a148dd90cfe83f12a62000000008a47304402201178601ace2551471f7da7fb398c1f9e2d2f789e5d8a662fc32b73a983c7f36c022074affc15eeb6c499dc98bd7bf1de94f1bb60af71b6c0e2ac6b67f7e63623eec6014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff41c542485a6dd3e2acd62f57c34a6ff33dd73d875991c39b69f0a47cafae4c98000000008b483045022100fb311ce78f3bdadd9c80effea6446257f95429963414ce669ad9b50b1a59588d02201f39e30778ce8d91aa8732db5901cb9e4052d0551ec178853e736b1baab7d4b0014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffffc791001af9897690ee3b3aab38893b9c04295a2ea6caf83017dcdddf190b7c20010000008b483045022100d2ccbeff28c4019ccbeb67cbd0d669af90ec2bdd4d2301fd93ea0db8f3db9a220220116e9cd2ed250cf417d0f45a6361a47bd1f26d92a49a933a8042490627b68fb9014104a5ed97469860bbe8f05b9964dbc83bb17e5d14383a54fc4395e1e698d01011f6827632c50871df697d46faab766a267cd3b12a17244db5af311133953b440e31ffffffff0261236e23010000001976a914510e4273e56cb363926a149dd7b86e8838e98f9f88acdbed4901000000001976a914b526df90f2bb0c5830b469b8b8f96d25e127de5d88ac000000000100000001d27b447bd3730b2c58e05dbd5a6aec7473150f8f7e69482cd7e6eb3ab26d9f7001000000da00483045022100a698a2d989bd249ba70571230b354b8e54845663ff7d32afac2b3591154061e7022061d18cc45f7e6080799c2d18aa10d4e7b55f5b50fa3db99babd19c460c34c5f301473044022065da4ed1421c0d0c3e157c2cd736e4c22d6af912a84cf1fc0e6f418e51a7bf7402207bcd88362d13127897881bc9ed3120f114b6a5a027b6f2979e65810c1676882f0147522103eac01e7ba42cfdd382d140754497ca33bc561750077e8adf73f963e288df6ba0210396945cc1bba0e1920753033878225a9db6773e25ee54b34b9998da8d7264e73152aeffffffff0288130000000000001976a914ba5dbe9f26ce3e4019bedcc8d820f50cfe7f482088ac802429000000000017a914d9ac0ca54a0c406d20b84fdf9b3cda1809521c4b87000000000100000002ba215a16780773a6492d9a9faa379e245852ce64b772e9554437895f14686f91000000006b4830450221009ad06b19d68f485d5616de7d64a373cd959d4fcb8b263d78e77194a780fe40030220659a86324793846cd91dab815fe86f5965c574711f08b0e8bec67ca60f3efb1801210282d8a0845f70288da1c9733a34ac3d53f8af2d3af472f39b66a3c7d9a4d9f8fbffffffff4617c6d64467436a2ae1299d5ced9b686be607f0bd5614a717c81c718d362b79010000006b483045022100d34ddc1164b0a11ce669c9a46b6d1fd0c1cd7d1680d5e724670718f7ab529fad0220204f57df044d968962c62c8ab7f6b9df4a86c40d5b5d2ffb384dfcc6723e32ab012103093e395799cf1d2047d2bb108b849754bc5f9f8fe3072f40e72e56ed049512b7ffffffff02dc0a1400000000001976a914f44903978916e08286ca171309579ed21639218a88ac1c300000000000001976a914b803863ac070da84ae68b20626c5d6f47542495588ac000000000100000002b9d39f88260df204f6a0bf620b4fde95003d095cd6422a01946dc939ea8ee30801000000da0047304402203edfcd14022dc4624ef6197cd8549512273e5a38519a922b1584e07fe98aa45e022044745d2ca6eb1f2ddcc6d954029a0a5ebd263cc97a5f0c7d65af2610b798bc6701483045022100886f7f4c740f7fae0e76d9dd2be028c86c01950b930b7eb7a9057f612092e0ed02203134d8d0f080e4dd6dc85b60c51c03beef2e12138404aaaa53be0ace160c20110147522103cb5e916302a280d56688daf22122e67665cd9208ff4273dd2fa1fa39625950f3210369552a76669aed6e02979aa08d743409f55f3a668db8aa4aa50735a3cc231ac452aeffffffff2a4bf2c8f4ce17ef610b9c9afdd1dff65752d7b1ca0963b0ad5141350b5cefca01000000da00473044022002427e1469cbe60062b157454f42eb90780f2a86c78cbaff072b7908240604f3022042a6abd842e2e3d7921fe186f5c0089f22a0f5bd688d182f45bab62c6772f00401483045022100ad9be23e59184041b3cd082611ac83be295f7dcb5d23cab7bba38a2ddaa231e402202d47f2853f772f90baab685ec38f1121dde0279cca4c0e0b6de69ecb4ec1eaec0147522103cb5e916302a280d56688daf22122e67665cd9208ff4273dd2fa1fa39625950f3210369552a76669aed6e02979aa08d743409f55f3a668db8aa4aa50735a3cc231ac452aeffffffff02da534800000000001976a9145232cc109276d3307a91a54ce6f2c23c050082e588ac0806e62a0000000017a914df7131367555744a89e69ba16b645862dd753b8987000000000100000002a46329f00f5ce42835c20480cb8b7e9a5694a21653e194b7f1aa2bd79c98ed0700000000da00483045022100b1bd78e127df911775ce386e6004c556a31517a8c05bb03fb994b0551e4c728f022050e995dc70d5d933c6ca0baf8932235aeb8086fb45b6b2ef0ad55f26632c2bb301473044022071a0801adb35bf1ff897d6fed026f565508bdd8a7e675594523292fb66159808022004f5e611bd2a21307ae5f524b622d01162efd6fe5ca0f1a0b4e3a90525481ac00147522103729b6873394c29d1153f3ca9f4aafc84d825d6a68d793d373ce3cfb9cfbae53d21035734ae241e88a345de09a9b6810951e41781f610d2c8716aa25d634bd0c5401552aeffffffff320d8349e448c80a3638544e6a2b38730571812fe9ba0bfaf73898c9f08e1dce00000000da004830450221008c2de186e21b9cdf6a1c20fd3b29b79f12da360bcdca773beff8d030a870206602202ca28215f1f2c17c5c2b560af7f31fc84fefd61055edef25e07f4f911edbeb7801473044022043877b82b7fefc3b6f839eec53f056403a7923b1f61fc3bc02ad9ed6c44730430220640566554e16cc457bbf5317b2d83db8129d941328b022c89c1abe06dd3227330147522103729b6873394c29d1153f3ca9f4aafc84d825d6a68d793d373ce3cfb9cfbae53d21035734ae241e88a345de09a9b6810951e41781f610d2c8716aa25d634bd0c5401552aeffffffff02b0360000000000001976a9141d9357551b00179418f6a1c9fcd16938ca7a1bc388ac586209000000000017a914d16a297f5eb2d9a3f149d1e76885c07e55b331158700000000010000000490e92033022183502fc32e5a9920eb38944c0c5a7c3028d08683b14e369aebdb000000006a473044022076e178d40c102a5724dfe890d7a2d8870a31f6b20f42114637ffe922006ef62f022036e69d20e6a0e564b36495623f50c9fd5500cb5ad57563a6e98b6a04019d7b23012103569ac034acb343be34cce17996e90e5367227b473cefc946f6bfa19da60dec40ffffffff7620fbecf73fc14ddfe74e266f8e884cc645e38e99f64f2bcc5e8fb7c0c369d2000000006a473044022057c8251a4a12262eb30d92cd79ed091476f8d1c278274cb9a9c933eb615f748b0220610d008d558389cd737cf87cbbb4525ef602e2b9b4ab155da4af1b41d545abf001210310f74ae1830d58ec6a42284826c030475e3b8c58bcfe32ac3a9ed9f72600b413ffffffffc08e3516a48c9754d4ffde13d725a900943555c0ffbd91b745c198487201e505000000006a47304402202b4c1b05e480ef74cd8ddc9c72419212238a27b462c1c64c63d15739af0e7cef02200ffa0373a964f8b02bc7759ec411b65d20319af7babaf3dd6041d50f2b13f546012103057f484429b599b0b6b6fb6f2c6f2dc1892ca320b57d2f7e2d191c2a0eeff31dffffffff0eb66cecbd0c506f31bd65a96ec0933780e7b0d15f2fa9dfbcd962d444d0a628000000006b483045022100d39695bd98dc06360284a8f5e40094dda9be0901f86c3d01021630953693e10502200751956dc08e823737ff191d834b752e23e23db83a53b14abe99c9b23cf038c9012102a5f0e7ec7005fd18303f813fec8ba1846d6f8e401ce4356f27a644bb0c06618dffffffff029ffa0100000000001976a9147d690a03eb998edeacd67c2714a486087e1b698c88ac394c0f00000000001976a9147d6cd03388492a093456cf21149fc1962a17ebb688ac0000000001000000099f455fc2ab8ccddf2b1ee25c9bbbd118606364aec7f237ac75e8c8406d91bcbe1c0000006a47304402200a53dcca5564c250ba89c67a9b5daeb82a8a79cfeb08481cda4d98822c53e66c02206d953ecf27189cfd84b74f00e455734cd841e038372cdf91f12edd3a40286df8012103ebdcdeffc072cfa522ba305fe1fe0c6431be0abefef55f826ecd49807a53fcf8ffffffffbd7dd753cbf7d1edb51a170d82d2be5b29d4dc4d6944958eed2d485772d015c4140000006a47304402204a0c05c663005e865a6aadf41d9b2d7228aea34725eb8919a2d76a746e964b71022012c54eca0abdb155f588c79da79c794807fee244163fb049a31cf9ae78708758012103ebdcdeffc072cfa522ba305fe1fe0c6431be0abefef55f826ecd49807a53fcf8ffffffffbd7dd753cbf7d1edb51a170d82d2be5b29d4dc4d6944958eed2d485772d015c4d90000006a47304402201337ac7f3aa09842185a561c6bad4f5fbeb1a200071ab6ffb25495e4a9de3dd802201db68a253107f61ae76acff775609d899ae1ff52cbfa5b044bdfbbb778da416801210249abf28e6f462d56a0078ba3a45eb467a47701310838a2b515ea4f3f469ab0d8ffffffff894083ac923a4260dbb13f1bd3993ca10d3152fe2973297a94cde3de721db134010000008b483045022100e4f50cc4ff4b39106ab3183a3220a90ee5175ad9b933f28cccff4d060cae2e6602207cc1b9c082d786aeba7f89a92605a75e2096f74964b7961dfb1851d9b7fa64da0141047336b05430fee858729a60f791cbbe2e88d8aa90256ccc20558d80d7d3e3047b557ed522450279514207a5fa2af76c6d8df9e4c7b0fe04190631cb9f57358e4effffffffea113aa0c02d7b5515a60ff173cef170c13442e2163ec4ebb5427834cd056cd1000000008b483045022100d4b5f97c9b86b6dfd8f1bfbcb5ed2eac556fa254a93134087f476c155fb638d50220126dd77c277a80b858732ee366f4675d84840d5380867f8787ecacaf98455ffa014104192b7f06039a557345ae4b50b1290dc2af54e935387c6867c76ed4f67d7ab1192fc501dc94452fd3aebe9da5919d640ab3326dc9bcc04e12abae5298b01c24c5ffffffff0835921a048823a94d7600cb1a362578b4a0427d695fdacd4a4326dfd4a93e73020000006b48304502210099717705c7b33be308251f2de07895f5fa85e4680460ec8998a9e2d51405057a02203b84ce6e3461dea109f12258639c127858fd5a14e4bda13b26a24e4493be79fa012102b3613ca1742d38bf89511aa98217a11b7fe2f6c829b30b02f07744aa2acdd9edffffffff0835921a048823a94d7600cb1a362578b4a0427d695fdacd4a4326dfd4a93e73030000006b483045022100fac990201a2fd67e0d77f0495cb927641db216ce5b59be2a57eaa8ac25bed280022034c75345243656403945acd14317aafa805faff6b07f3ef2807401cb33012c40012103f4c2744ee15671eae703c6a5d74818e3dad735c043b8628851656a1c85d8cd3affffffff5159105b6d33699103a6e712b196b9cff22375e462ed13b2d491982efec2112e010000006a47304402201b8bee4547adcebdc29e2f9ed76e735df2449b79a4f4000418d48ef10ef2507b02201e97a396c2c5c926f8db831a4218d3c8fc70dfc6446f8da6e428a60c437009ef012103d8b4b62ba86bb31fad7d6297bab00f05b4a4346b119415b7922001a230c5f731ffffffff3e476b1db06c88385084ca98fa3b5a23e46b0fe4a2c0e4e5e6b0961d5c3decf8000000006a47304402205ccee8ae656ae9a79f215ac37f0266661eb42673ed134f3d2330ef767cbfdddf022024508069bd9bf158f79a8906fdfddbcc32f036f7bb09aa0029190c4aa76411f8012103d9289fc2e8662c1e7d79f3c140f069d7f5d12d83b260d764564e2ce9db068d0fffffffff0280f0fa02000000001976a914e221989cfd4b54c17999b9cc1de35904aeb2bbcf88ac5f490f00000000001976a9148584f2972d7d4fb42cbb978e35f4ada50e9e369a88ac00000000010000000416acccf6c13002c544c5d315f7a2af99ff4409c4be0a1c6b42de665b435b970b540000008b483045022100c34e3e676277cd728d4a1ca4cf86f764cb372a92f11fef56f64912de1b350460022050dbe5beb5b2e5b307e23030d1a198bfebea968c5a950efea57b2157c650ad6e014104475a197830331fe7fd1a89d49d119d1743074ca8ab85f5c6cf8127e834f4a0204a6a4a4a941d87f2017c18a0db424c36b0981bb1d974205d5f84afe9ea10d712ffffffff212751f66e30fe4342619c70a803a1e30f3a90ea0783b8ccfeb739294cecde5c0d0000008b483045022100a0b5e2456232c617806eb0f74f3509a7595dca7f281a40073dca1ef870ed782a022003f285302ab824e652d30124207db2bb14778a0ee69c0f6dd457c4c25a08517b014104475a197830331fe7fd1a89d49d119d1743074ca8ab85f5c6cf8127e834f4a0204a6a4a4a941d87f2017c18a0db424c36b0981bb1d974205d5f84afe9ea10d712ffffffff15968d47dbc33da80a24ca16201045c79992e41f36ee787b2b5608f8bd3023efb60000008a4730440220062e634821d2a7612a74b8652e36bc05aa5a9849e4b7a338b0de4d99e85c7fb602200a239bef9fbebbfc0793d5b34cefbf1627ab691ac230adf6c3170fe6224f4e8d014104475a197830331fe7fd1a89d49d119d1743074ca8ab85f5c6cf8127e834f4a0204a6a4a4a941d87f2017c18a0db424c36b0981bb1d974205d5f84afe9ea10d712ffffffffe3bae31e9608ab0b50f3b79db06877add30cd23f6b7c9c1854b7a1a4f54fda52b60000008a473044022054d643139f7d22c29d2e532f10c134ae6ab5380af6490e94ffa93ee70b35ad67022074a78160bd73de58d08721ca8849d943d987c2f683545c35ec905e1408bae7ae014104475a197830331fe7fd1a89d49d119d1743074ca8ab85f5c6cf8127e834f4a0204a6a4a4a941d87f2017c18a0db424c36b0981bb1d974205d5f84afe9ea10d712ffffffff01db8f0200000000001976a914f867fe60e2548a7143c254190a6d254c6dad7c3788ac0000000001000000fda5012a0476d36095a99756f5ebaa9cd184f9ba4d4b52a515aef452cc90b61972d8d8070000006b483045022100c95e641e2007df0eab2ac26a805be3d72808cf84aad375706cab0f93e464b471022078a0db3467bb370304d97fea2a30d23876cfbf365dab4afaa0b12c9b9006c0250121027e52e8b25aa97fed2d2db4616604d7e3f400fbac239eb079790bf6a6b34b2610ffffffff2a0476d36095a99756f5ebaa9cd184f9ba4d4b52a515aef452cc90b61972d8d8210000006a47304402200652f84ebb685da926ca8ff3ff87e2188e8c231d3e98a55d68cc1368c9b7617c02204871ee9906f44e8d91d644c73639acd5dfddcb66400fd77f313d3ca1000ad2ac012102ff192b32ceb6ca7c147da931b3886549e18ec1d47a482ce5375235458d479a5effffffff2a0476d36095a99756f5ebaa9cd184f9ba4d4b52a515aef452cc90b61972d8d8270000006a473044022015108d406303a4e342f712d5548b6644e8d60ef67fc010934c8f254d239a0c4502207f2b24205d52f17d9a05e37ad58e24c494774469c31c8552ac35c7ce503d6621012102a5c4276fb5e2b3218fc67f5c3fd87bfc9fc75f12bc0e8fb6ad1c50a6df0009eaffffffff2a0476d36095a99756f5ebaa9cd184f9ba4d4b52a515aef452cc90b61972d8d8420000006a473044022016d794d41b6dad8b62a6a51ed29153c69a4b97a1d686a804d4953984535af2f10220770f6483e72f3442c309f912fb6df3fae88ffa2679f90c839bccd39989bb32e401210221c7ab81dbf119a938f75adab81d255c60bde3836b7c7707ee9a7bdd301e80b5ffffffff2a0476d36095a99756f5ebaa9cd184f9ba4d4b52a515aef452cc90b61972d8d8430000006a473044022045cbdc549be2e27a5ec8058b57f6dc10df757a16661eae6ce4a934e2facecad502207cd0662a5844a38a614ab8b2d171f80bb403f7ace9f3ee31978847b95b0dfc4601210360e66e446a61398bc9232016e6a2f34e5c26f362479398702acd988ce66807f1ffffffff2a0476d36095a99756f5ebaa9cd184f9ba4d4b52a515aef452cc90b61972d8d85d0000006a47304402200755b40438bd36aad288bb02270dcac3c9ff1a66af1ceaace41f18d53482992b02203e622ba7dd901918b7b60e012c17160b8818769cbea764785e207e07d7fdd5eb0121026c9c8daf61faedc31b1adc045f6db1d90d8a46c7f457017d8f5ea01873d57789ffffffff2a0476d36095a99756f5ebaa9cd184f9ba4d4b52a515aef452cc90b61972d8d85e0000006b483045022100afd20a33bda3e233b956a0487c68dc8b63a8cea6560349527ddcc23628b5c51c0220713c89412c92651b35d50135bab2e5bb36060838105df48fcc9b121ed832cd83012103c798853d7f5fcc815ad4fde1183d3d80a40003b818fa86a21403aabfbd0d9959ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d000000006b483045022100893c5648ab3c75fbbb5d68d439407b7fc4803f1f1415c744f66af0ce8670885c0220735a7ea1f2967fd7aeab6ff2615d25eb3cf16c3dad0ccf5454aa6bb666e5622b01210252e8d828e4939310e60577062aa5414d03bda9248b3b1b284ac0ec0d77dc39ffffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d0d0000006a4730440220025185c440839506e1e56f75c0af7ad44deaa2867204d123c49a93d5a2df286202200dbaea862c2aa32c703fb724b4240b7450dc32c1c2ddd91fa0afd14ad4e18787012102f2b84f3bf02c118d60b1cd602d6126594993c70af1b6895127f3a7333d397011ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d190000006a4730440220229e610175b723981e63727a1140298737e63d79bed5dd1c27e46e40ce86893402200e38354cee3317a502bc7973242e87ff2fd2f5eaab9e331c22091c44544a1426012103d23bc2304b5382f53bc573bb7d3d32ce2fdd81eb1cd1910611c8f54dbb794771ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1b0000006a473044022066b171da2d12b9ec89351042cad56f0cbae009407d41ab80749eb4df35c7d8c1022070b874f9f4536c085c1b0924ffce4a6fc29d51060ecac9ccf88bbada2e6f81db0121022bccd2c3fe9a40ab0c9e6ae7cd4555920281bce457120b58a62f237a770f35a0ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1c0000006b48304502210082a58252112cd43f1d497df929795d5563754c6bca3b16ea4a47df84f927885802207e78f65a172111e10cf2daeb75136eb7c54a45abd55163dee42eba0c44285d47012103cec48e102fe0a049ebc494ddd3d14d3792b6735516ad9b50297c4733a17b90d5ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1f0000006a473044022010a136afcaa7177272947872dc34100aa39377d2e89e5bd9b2339062255a80e2022010a047d4e8921ea6045f14e83c905e2ebb99dd28df7c3c2f230e6cb980db377f0121021461bf989e3d749eef8073ecb337427f17042db80720c0e362f01793d6e42610ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d230000006a4730440220785f635d4fb6faffad8d4afd8a362c1cc1e2309814d20df0bdd30c90a1a3bae502203b0223b27fb4190eae8cf7b08796c4e8d6c66ba07fb97084475e5e89d41621f9012103cc1cb2bfb5a8610d0b253b93f9bd07c2bde18e387b149f2947b753b36566c511ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d460000006b483045022100bbf58acc4085bab384da8fbeef8d4fcc3eb6b4012708cf80e7a8c490ed866f4902203c125c27b5570a281e899029da2d44a4b23d8c6fd2758afe1f174a7a02ea0e210121025ea8074d10f85c04eef3b63930923fcbb122a5c9dfdbf41a56b47e91b3473c62ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d480000006a4730440220486cc50c3a9e70d5e6ac3447c0ad95a7614d14d524315bf21244476d41295a6f0220160a55e0608eea70362729a3b02affe32ce7616f3fa0640d9fd54222901659c3012102e4562907fccfcf4b9a743e4715d5a9cc1ba203054c63a5f4d0ac660870d35ebfffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d4c0000006b483045022100c4f759b47f2ec1d3c71f47d699be64fef5d91a85d2354fe902bba96e88f19e9b02202dc2f1fe29df71904873e3903d55b43f317b734a9d2bbff1f424c502f5d59ce901210201a71c6842cbee9ff2e7c0aecf1042ce5d30103f4f73d8ef35447ba56fa78430ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d530000006b483045022100e03965fd123dd8dd86ff71f4065e4140250bb15024287cf5a3360a5adcb49a1702205f491a0ab1c61ee5ea2081998bbf04cd15b16b6a0bb142f3e55af2b706be13c1012102ed801ac57a9715ea9d33bae10cf77e955e97d3eb8ec3e0610b2e1618aea49743ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d5a0000006a473044022064e7d380461dfd83426363c29426b53ccf537419b84dd67e9e8503e46c7cc32102201ddc0f4f953d82d63cbbf259487108a10100c04954879c502203f4d22b89ecd1012102b944e9570267305e26e166d0e782432bc5145c60d9b399eacff1b1b05497ace5ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d5c0000006a47304402207473f92e3e6a4f10b5a3f4ff1e4840e83b15b8078b2af3c86d71cad85e38a98202207602514d7405e834653e88c1e0daf07fd3c70db66228292296c2359c5c8ac1e30121026c9c8daf61faedc31b1adc045f6db1d90d8a46c7f457017d8f5ea01873d57789ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d5f0000006a47304402202ffb79456780f95c5aca97fec0e5ad0f107241a5dc045aa3a486dfee8739eec5022079fcd30c14439d1dac99b1c9f95bedf7b233a0bb24aad295a9cb0a33343e9ae301210375d0cd09e670b856e4d4d78aecfeae7ab5a7512c545fbeeec5e886eba5058191ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d610000006a47304402201c9fac88dcc73ec3f87ec5b6f155e1e81ccd9cdd7067eff5569ef7a7e6fb7af202201bf4a539f6ba10e224b3e9c21568f251aab2d3ff2756bc0138e4f7b48dd37f08012102d091a45a2368c4fa118ad23fe1759efd5b69aac7748ebdc04a3c7956c772bd42ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d630000006a47304402206b093609e4d69e2b01affac61f690085d61c06491189fa3452aa26a36c517a610220662249f96f5ba0048cbf0efed79318759a8d5bf002b9ce479bed7866d8e4d5410121020476e85e348a8447b7c73a039a774715c51466d82c55a24935264f22de8e5a6cffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d640000006b483045022100d2dd95ab01f5896bfc6cb54e7b0b1913df9213a875677e81d69be50b60d3d18402204e8e4fdf7e9c7444223a0d089b00e711a0456a74f18ee292b69c655cc22b9f04012103640004c90bbe3d4afc532736742932f32ac2e2742845ffe1f0e11fde8b0737e5ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d650000006b483045022100b19fcfef7d9cdddae7f99f9d2a11716dd78bd3701d993ff799cf68e157c02479022000f9ed5c27432a938257b8cb23240768f85f7afbba600b88f492d0b8c8ebf77401210287fc31cc44bb9a7bfd7f487f5042c5ef237fdf4586f38696f62f7332aa526872ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d670000006b483045022100f88a016e2968e7b70a0f01ff5a5b121bad8fa6d2da58d4a53626228e5f492c5702201523c08ac69bfbbbc44ae6f287abac7d107df595de5a89c0a5ef1f87a215dfa90121020537f5d907e4ff313a83e488234fbb52aff34028d8d04106861ff4ef71abdb5dffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d680000006b48304502210099577e9abd6e030ec1a30c5424d35a5487ef75aba44b74b176bfc70e02bca30d0220128f31ac116af9e0f58f26b7b2ea8b96ec5c0eb1d2a0108fc9ea8995f8652a42012102ef521a7a55cbf3fe9950765a737d88289aa2bfcc2aa3c9ec8772b1c8d43a3ba6ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d690000006b483045022100d367f14810a9a9ff272a8920126288d185e2eb6419c61a2e9daaed6c057882df02201537548794e192b0e0dc9e0348ee37a2b6f9e96d42cbaea6854ff2ea232f1ef0012102220cbeb0304f1fc487b145533d5a59d270235b550775adbbe064133789d5a374ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d6c0000006b483045022100853a1f1bdde73e98671dded1446ca76ed4a3866603729e9b4f22aab56b85178c02203b7d4c7496f70e23a806a8aa6f149fc01d9d0f1ed92e83eb3989fb8d2f388cec012102b1530fe1f946149b84d63d2366668fa931ec5860b8f7f3b0ecad46e0bff32260ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d6e0000006b483045022100ec303a6cb61e91dc97cb48105de39620a2e0add2db15020832b7479b134f6152022062c3f8d863db9ec05e8f7f46c8119b66d645df5327a5abe0641fef4ac0ce7b8b012102f51dba1a63b3fcc2d5d57598461b9d8c63a159b6509d3d0b269ddaf3c0ea255cffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d810000006a473044022043a339ce4571e5592faf6d59713ed8ec1419f6189750d65662b1650151fe359d02204267412810803c7ed0eceaf83813210df62b708e432f8240680edd99bad18665012102bdca2c5703625f8a3a800d14cabb4b622dfe766e29cbc4e03f3e2faa227e4ceeffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d830000006b483045022100f975b9bc745fef2aa6f0dd7af5f0d9b17b803d9bdaf73422f797996fac3f0fbe0220072095d5085ba7df3fac613addea427f2ce2bb9d1d168a5e0439e4187b9fa9870121024f20cc2f4c33c2d1161a439be2a7695eb3703ef7a203966d3f32b201089af9b7ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d840000006a47304402205275b466e547db4b7780968623b4229feb946b480a8fef793b6983d45a07670202204a9316d7edde843648368aeb70e8c86f8f8539c165962b626fc165edb89eb27a0121033a84598a27229755e6c2ace0c5fa4fffc51fcc46a4357a904409879a1218397bffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d890000006a47304402202c06c9bb2815d4a271f6addfe592dde181d22dc47b3f8c7c8f188d5043305118022070675d3d59076900a0bca00a86886e8d08dccd8641649b1fe0627e29c9ed67b1012102092c130bafd3f042a14faf0791e49dee1c4e4c45fa8151f8c29e28a1eb156065ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d8b0000006b48304502210096ba3427c920a108c973adca05ae1d5d30bb6f232e6493d36ab00ba0b0d7c77f0220164120670bfa1eef85ce6b538afe7b9baa7179f00d03052bf97268c2f34f56c3012102e28092d1ada820e1aca104ffb1c7c48f3a2a5e8c5f90838939235dbf01a144c4ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d920000006a473044022039c06148010d4f7852bf61dbb5838ecc0aa1e2a26cb2b84b9bb9f6cbfc0e39e4022018985f0505f88b5a27ea1ff3a817706e50183dc46c21fe76fc64e71c9d2fd982012102e79e860ef42ad0f30a2f347b4c06dbe0d82a36ce559fa65044a7fc033372105dffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d930000006b48304502210096479f7298a3ecb791a14f2d919aaebc65c84ccb91535ea69272e1e8db30820a02207a99010921e49c95adae92c2f3274a9bce475fa95fc9db839abd352f0c2db82e012103daafedba4fd924b5c08c6c19f407d9a8dd8796c6db74f9a3473827dcb48f40c1ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d980000006b483045022100f716c60b8a68c37d9134092a89da0f534a3949c528e8356f35346cc6d0c3245d022071d89d381f0cfb13a3e6d759380166e6b87035e0613e34ebbed758ac6d057e4901210381f6523b99eda5a898dfb4f47e14d15bb956ed10e759b81f3e102747d2197dcaffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d9c0000006b483045022100a817618f0995dbcb6a3dd14f28f8539c9e8bc0fd34ffdae1aca4ad7601f117ec02202b704b4ac8d9d509442f87c31c2f7f878a3129235f3dc036b68a4bc27ec383340121021813cdd05350838ff535db25d1605c183c7b4141d0ee2ef2bad24671945f35ceffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dab0000006a47304402207c68f2e388bd29780acef2a0d16352609edf43af8b358274d8ebf7fcdc663b98022034b180c7a304fb6433a8aa70816d7f846c41cb35cbe17ddf566b68b3bacf742e012102ab574812cea5df5f4ccb2bc7cb6a2ee3ff9188213cb664db475b749cb0991283ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8db70000006a473044022074e687a2c3b2f62436088a709ab64dcd91b55fb228580ae564d54645ffa82b6602207e1aa74c7c87f7c8bf8e8feff0d9aa6709533697db1a220c701b4bf1a6ab8e47012103d5ef5cb73c67cde8a2ceccf5d41dcb665955ba4d7ae511e049295ac0c796a4aaffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dbb0000006b483045022100cf1b92ed2a335f4146964606de11dc787047444a52030825ec3a6496c37df65502203b72f38996a8cb7d4f40733a66ea0cf3e326b38b555dd47e565eb40f536df14e0121031f6390da5e2c34d9747e450778e62217b57d4bb4dd7b4093a6720675bbfab1aaffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dc90000006b483045022100d7d20e06bb88f79785db6720af4e0dcae92976dabf6dbe407d885df621ba2eb502200c014a36b6ca2916f28ca41bdeb0750bf3b70e73fd364ea4d4a281919b19a6fc012103c0eb8b1c197366c5aca991758cb9ece316d77cbdabc880279b28ef0417fa96d8ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dd00000006a47304402206a52107134e27f4a2b5b50af9ebc41259b7de083e5fbb274074dd444f29ddca402206380a2a499c8d4db5eb956c57dc66a4a55c9075403dce289a199a2f99e476c97012103c017e13a66a2fbb5773eb8c158fd91d87d51398ee06058d52874555e24d90439ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dd10000006b483045022100a0249312c747a7c42fc5dede0624c940e4764256084fa5f4fc29a5bc29654973022068a2c9b827abcb7c9686ccc83231b9b7082449c8bddb0a3c01b7e6e6d28279bf012102c6bac6f6a26107f1b6ac7731d0eb1f68be1a960fcb936138d5ec1003e3009b9fffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dde0000006b483045022100c258e3c1990bdbba3f7d6db50f314ca6f78eb85c95cf99cc102b88b6baa7b3db022008c2f2c2ede6697abc4b42b268c228be25e8937ff904075709a335fb4276eb6f0121030504f2b3f5ef6f01d3300ebfc4d477b76aad88f96b8e17015c032f17ab4b7941ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8de90000006a473044022024787ddca037f88f5b1e264d57c904114bddf0deb99b9530f8f61cf48f21f9d6022030b3773a96d75274ceb3cef1696f92816f32f004b0e7d310c6dfb19a510bf5e7012102085bb3c49dcc6335149e8e7bb4bbe22f815814183ed53b7ee65de391462235dcffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dec0000006b483045022100cd48773c658caa98ab6a0b984b9112eed0f452783de35de5a37a7231b6c8c0d702200e5d8c4a6e625256895cd1bd296f35b50d176f184d4869d76e3626dff7e6bd9501210215fa4ab10ea2dc19364ca008396577725ebca25bdc5108617cc2915c344c575affffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dee0000006b483045022100b06b7a904c7df67eab6a9435bf0a6de07a20d5817fb0a50eb4abdb871765de4e022059ccd1fa0e0c12e37c243189ec2f8c5781a55ba6f0d276f255e91b1e8d7a0cad01210224d78b34605ccebe94d1ccf7428094f852dd5da910125aeba8b87cf1c12d9be7ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8df10000006a47304402207b033df5dce1e78551a20a3fe083096a2eb79f50843e706a80083c09d41ea2c8022075dd7b28eab6cd0434e201922fb634765142e07440a0bb5c6c71b8697d2e5945012103fdd7c5ed26ba61578966971b16eba7a3864044610a345b54bde3987184fb2ca8ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8df90000006b483045022100bfd439a666cb7c28d16b0529150f2f2596d633e80c2fed9638ea0efbdf8ca5000220242a25953596d500eaa364aa5c476b603736b59db0ff4d268dd99d4bf276af2401210253d4804dd1539f083fe2fff11ce66fba62baff916bf0c0272bf22c45a09772ceffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dfb0000006b48304502210088c9e18476c6a5826a80eec2f9b391ddcc5e9d280eb67225c26922b91da6b4040220755b38be69cc7aca30668127eadc1f51fca3b6e61d7dbde34cfc59c5b4ef34ca01210289e2a3024ce7a0451144fbf6017e31fd10998413361057f43e85b6a5d82f19f2ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dfc0000006a47304402200748712d5844e76a3f63870458dfa5eef7f3b04b821ca32d7db98954b6b5a688022039816fcbdca22667ddd285a0f89bd0d7649ed52e24d956fab67566412eec3cac012102838ffc97652219dcb7e571b0a88639c2a6bbc111e0b0c287aed28e49badebbf7ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dfe0000006b4830450221008b66cf0126f57bcabb0868476d8e27bd33bcb54ab0207a5c0e55ba6dde4f0ee902200ba2dde14385b206106744e55452b8451bc5a25feba480aad6c65646b0a07c48012102c47390ada367119450bf53473cb9a1bfcecc563d51ace4d258482fcc86e341dfffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dff0000006a473044022067b1df43975f5848b11db8f4ea0c79338fac49711fd89b3bf9da5005ee6bd88802205adb39dc244fd3442271b155ec8f56a7412889a051e51d62e3e16e11c3a3bab50121037171c297ba15cc70dd4d9c040944b085d555c97798758591eb95b42263f23b5cffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d020100006a47304402204625d6b400ba3990c5ad379b90e970fe6d64307cc5bdbe71f9006c4ad8d0595a0220164c8deba9762dfb909a9bcb1626c943333f06461e2c58b51b41ad6340005afa0121021849eb6126a81231d59d590e73325f2eae6f8dd6758cedf5ce7cb04da2053956ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d040100006b4830450221008fbbe55a9d67fef6a04fc975f851ac54967fa0800270c04010341ce4228271840220549b04c3c7a8d5520b6c9f05f3f1cdc54ec209efcb28aad110b53b470a06b42e012102659d3c8aa3b8ed1f7ccaf816e04d6753fc0511959fc4092bf3402efdfd1ddaf5ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d050100006a47304402206f653ee2be1e3226eb4a725db388030f0fdc46b5ec6820f84fd34236bf2820cf022005a212288b956c70aacfdd4a4ea2b8a6d50b573cd7ffdc0f5769eb15bf31e611012103ad75d6dc4bb9b4b1ccf7c6859244167b3b46b218d5d7c10001d3096ffd0aac01ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d080100006a47304402204e1723e2d61967726cf3a2b0a162d7a4371d4d4b3f0fb207809f5ce1c55f547c0220018e66868f2d47e89738d4161682512248d58f3ed1e1a94afd6b173ab7911e1e01210275d67da7cbc7c0bc0e30740d659c8465c2e2af74fd0e2d29abdeddb82963c59bffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d0b0100006a4730440220036f42f9ec22b5b7273358397b51b8fa134080a900ccdfc49db8f62aa5df8343022019000511752b4d1e9f9f7c452f098d65c116b25e12c10568e940f7254c4f6f1f012103628f06885c53e23474d28cd2610288270d191923617b0856e9e0bff0a807aeb8ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d0d0100006a473044022039b64c02fd04e0cab94a175c55ba7326cb051d43507500c57119beb86ca3b204022061aff32ba90e68522831abad438e34070f37b41e26f23cb7921f03f851357a2c0121020878a547339dbc2a9375ea4b3197d792daffa18461b2e33bac0edb76fac50f63ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d130100006a47304402203a72392081261036c93eabd9c6cdcddf15bfcae6de19785b939711dd093820a702207693129a5a6c141eb57366585e553b875b0a6480e77b9b6ec2de822b13a80ca70121038861dc0fdd8bdefa9747c60b91be7f88648e1c0db70341c7ca393cd1d9145a53ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1b0100006b483045022100a46259b42b8c8caef414dad6b9c1c39338848cc204df8e9e0467213037f563e40220676ce2f9215bf1ec1558bec6bcd259f245fb4b8b3e283cb523812a2262f92e120121030df38f26078d2072ae8c53866fcfce2398fbb3d1b39ae3ad0f79eb77de9facadffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d2e0100006a473044022000954c6bf95490186b020b7d456657cb19c71a851996448b0dbb7a69fbce5ed8022069de6c4eb1d106eaf56f728dc41adf33238b435c7c1caeb29ed9bcdcdaa82b920121026596df661f5d6ce02be34806c12efbd77636e7c0e9e8685eec7e9162adf6ce7effffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d310100006b483045022100a9f01e13961c4d024c59a169e4ff959735b29bcaa109c865dec40e54ecdbf925022042494176bffb8a64cdd934e62c1314ac32a0da057bd5c5494bbf64ab52226582012103483a33dfb5497521fe2133beaae584e2672327663d930cd866f24886ea51002affffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d3e0100006b483045022100834f8522b6e99a850694255e864c56efd473f5ba7f0bbbcdb5697872ea95823f022035ab23ba7be772044cbb77cc8eed0fcc22d10c67d444b6a57559d2b93293cb7b012103c357a607dfbed1b44707ac9abbc0b67b515a3369a10748dc3c5164336a4072ceffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d430100006b483045022100ef7c3b7682046528f18972baeb85a84b83bffc165dc26aa4fe2e188d350ab2860220068be06ddb4c5706c570921f960bdfcb4142a5c38e543669cf25adf1616b2ac8012103cc7aa2a2c6d662f66dd8e880611b4d9df0019f0d436cbae665a2e3994f3661f8ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d4b0100006a473044022034849a5b0860f2f6dae657f02aff9e558543ef689c052b4066931619a1d1fd5e02205d843fb322134487f975c718a9aac313517f9a58a91e5ccb42ac5903acad60bf012103435b160c6b7532d7a6df4b1b32a67f02a42451fc8f2c2535a3350dbe0c889e5cffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d4c0100006a4730440220653b76c650c8ccb86f8880761ace38192a55848f4ee992b13d845b61edd227ef022031ace2a0fb499bc3e66300820f8fa277f2b45df3488f66c8a9b92a5120b03fb00121027965cba44c23b05468d010b09a66c88f11f837fc82d93e65735270d1c0e2af77ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d4f0100006b483045022100d6dc421210cb807fbd8237ce505035c3434b8607c6d8eded004fadb0d1d5bb720220681a22e1903957027f574b16f3eac1b51285365f216ca55f2916233844efe36e012102eb8582dc08ea8270bd164d684176a105fd70dd5165766194f1613ea501b7385bffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d520100006a473044022054acdc886ef6db020e1a2bbfb7416608133796ea0f97734f60a53d38cad59bbf02204364f26aa2555905ffd368546b136cb1e44b83ef3ee3f8d20c6b252585e6a289012103feee761b7175fcd481dde6568aecc1dd9cf04df3d65b70f005541dfa9403763dffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d570100006b4830450221008118b31c53fbf7840f17729835a3ad7ff09d2b8ce8b2f4a1ef861759194da10502203b63de6d057dc7793a41e0e7c397e5993580863777c17f00456c976d104d395701210239e8726d971e4500e1988e528f061ab92d730e4b9a93155ffb540d41c05b11feffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d580100006a47304402206b8b0c86e8bf85d2d4ba4c3348a26dc4601ffa6c21258314cf76efdd2dda725402206a26aed98b41635581f887299376e70ffc9c17080e34783290969adb457e4fe701210261191a870744f836b3571a815a1b1ea13a7aa0ffacbf3d45943986ebe3768b8dffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d5f0100006a473044022068e6fe616d8d273bcb1e1f07746145ea7441a3b3fe73b0579f48a28abc22f43b022072097a0511e2af032bf7ef0cb89b08a5c636745489bc5faf6dac703eda0ca06b01210200385356b4ddde07e2d9bdd89f457ba05c5e9da33ad7b50a92051bd2a91a90a6ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d6a0100006a47304402200c241b59ae006b8105e3b334020ac5f13381fad06dadf25b0698e60b2233e3e90220478a91dbdc52c1ec8f1af7e400d8d1332c4aeb38dcd9944b551d99a3e0586488012102a48b5b01a33b59a2a3eadf23d108ce1ca951ab4d5bc81c4e87c7284f70e28e48ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d6b0100006a473044022054190595b148c32d55eab3bc9a38ee35d3aa6cd6814b64bdaa2403bfef15ec4902202563745d7d4815ec9d232cd8b38a4bdb463dde55d51203498e381fc1b60e6ad0012102d36d24cb8d752b99f3c771d502b762ace0991b697c4e4a23bfbb9740a9f48a99ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d6c0100006b483045022100eef3daa0a589d1baeb18a95e511624676e3e2320030d00e4b0a160560b4a296a02203d02195e9883de19490407fa99d972a858a8a94264bd28e909d908d7172ffc33012102b99681d93faf7d71411527c099d59def01beb5aca0bbb37f48df055a24627828ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d6d0100006b483045022100e8ef1e3d015f8b83d4950cd754fd5fb9ffaa2afbbcb7ab38e457242388c0736d0220760d4f9b3bf5e7498c8a15ab486b4bf174bec6a18385c37382e5dbf45ee0a70c01210281b87d0e4b299a674d62ec6d25454bfe20337f8d7ea3e3b1e689615cb5526cbaffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d6e0100006b483045022100b8daa336abf403bcd90bc233616adcaa17c2e055725dbae1fbaefb95e8151da30220193f9022846b60b2cc43a633266413ca0b04f83be5f329bb006bced45d10fd7c012103048a2bcd8bf1d2964705c847c4d767a531342071436cc1703562fd5ca7937f3bffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d820100006a4730440220386741223d2f6aab0307d1374bbf4840363a89344daec60ef1e05d253028b14b02204c33aaf3bf08d89ca6921ff2334f77f6939ed26fc4aef08a3a587c4d719a26fa0121033c07e5f15c4d4e4fa886adabdcff18f5160a0229be7f97ea925b10096c867f5cffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d850100006b483045022100d6fc51f59b76711a70e77cd2b0a07cbe9f318a8b3eae0f593b9b1c23cef315dc02200e3d77a869930b29c12e7e487ef4b39fcb087de0f56e99d479b741adc85b0ca20121036fdcf58eb2d612438d6117303f666df1bf0f29d09b3fa886b351b80c81480acaffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d890100006b483045022100a1d17f403f7e3bcca15116e50daa4d1aa96b6f7b53fcd6b6c180541e53e9aaa5022073f4c37082067d60b1fc6d78d432928f8d950ce558b20de68def7aa2c139695b012103bba57eaa6d0f43f79a382db7fa868b7917293e2a663512ee0b546117164bb91cffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d900100006a47304402206b3f60e339b0754eb37c1abf84d52c3678acb419a27ebe69d8640a7916a0605a022062840aef8c126e2fef8c9f26c574399f9ff42bb0c7e43ccd2989bc0c5c58558a012103b922bff588b20500c8565f39e16fa7389954d354e813e9d044b7cae10f7e5eeeffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d940100006a473044022011cb2c0fd65e4f040a4af0e800a9d0da4d87c67aadfcbe8f943d6e5fe82a7c9f022055e92b0c643b6343da526f628b7e0decfd39064de9ed7a4680ecf4425fa1707a01210364f61b677581f5c898e5360e126cacf49c54422502b47ae3bc28f2df238af0daffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d9b0100006a473044022005836c5a53db3cf1383b361003663d5c91b43227ae0ef4e77aa9301a4be2a7a602205a298ea75a3463064108d5ff5ddd7f5d948e1e7587ab74f451cf367aedec3cd0012103045fc5c01ebea94b876878e8cb1f81644720b16ae3b43875921c5feabef1cbeeffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d9c0100006a473044022059c867737de096574360feb30b967cae2d1c7b40071babaae5407212202e74db0220295fa86daf9570e5b0206c42b45c91377d50d56fcd38bf3d6e0f62c0e2b3b6ee0121028955e012d5ffff6d63758ed9793352cae5ef426481be6c3e5fc71171d1b40a40ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d9d0100006b4830450221008203c9ad7210a05efd7c149e3248214b6a10ef2d54dd83e3f4c3d7ac94a482e702204fab424b741654ff5f409197485a507a2e900a5b4c097d315bf9cb4b276165530121030673a0b89d175ff09c2edd0b310b9582c2eec64036a02d536882189a8a6801dfffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8da10100006a47304402205fb0f84e129e1c53ad675452226509329f40c378f1e13e9c1ea3ed25c084a11702201625ee2a6b758153600dc122c127bfad5f30000df090a2b8b3bdeb61580c152b012102db821d5fec8b536e019e96f82161686fbbb0b3b15246db02173c6e93f02d0a81ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8da30100006b483045022100e88d325a434642d4c60c892d4c7c926b4df87dddf25155b289bcc161571f0fc802204dc4a084205abe098a957c0a970d8438e5330aa59edea71e222b8d44f60806b40121029a75eed8d11d13c7b36d336dec0c37a9ac360d0f7c43355a1cb8763dc4845e98ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8da70100006b483045022100c838056c473164edb49850a3165a1410d47758d7b1b82e53eb2288714e5f844002206e8fdc6b5a9b0f74082ed640ea7bf0df85ec48f60ce9e0f94caf849e8793974f0121039b4dd60a8065305889b5f07fe5f4de59c8ab144dc851ba405b5cf9282cbf8637ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8da80100006a473044022054bd2e818703bd381152b2a9837295a22a7e5364a3715196bc0026c26284ed9f02207b6549d92c8cee6981bb84b85b566e3daa40c0bb5514574e1df8f58c1d8764b90121027d510e7ef2d251d74ab897b4bfae915cfb3979a043552c0d8ede26a95ad91fb5ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dac0100006a47304402203f421411be23aaa66d1db5ac9228060f97acdd62f5966ee7a497187b0794d6cd022069fd055bcc7b9f25f4135ae382a3fcdeedd6e57716325c5cc23e6d1f94f38172012103b1d2977a5cef19d66d6d12e74e069993023e4cd6cd3373e845be0e4c79290ee1ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dae0100006a4730440220027ff809af616652e7c323497c7b21547e900f000fd0a83e11b87acbaa64f87e022014cd4203e604e9c99a4674bb54d3670dbba24402d49798500dab0d1dfe6554f601210330f147db0faee2e800e1bf2eed4841793d0bd0d5fe692847fd7065af8b0e01fbffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8daf0100006b483045022100b665474cf5b8553756bc64d8414b3f6cd25fa2580a143362a28293bd96f684af02206e3814dd31c78adb0fe855137f92f5f7eb2215ca68f88959d113ca61f66cf7c601210271eed898412d9d8d04b21b2484ec39d9308edd10651373b44e2c6972e486f302ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8db00100006a47304402205cea8138fb7e544c8f54d2afd9b4528c1ea43f09f6f51123edf629718440bf2502202abca4f1bd41da14d6da97831db5327126cebcea7425d0917a05aebcc0b4bc80012102399b853c2fdbf93a7e0d320bfe8a64c4125f58d485aba150baabeeff5bb8b646ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8db10100006a47304402200b81ad3776e89920bfa72699531edde8e3e836ec54b49c7922b4a66fc7bd1cc202201b41cd2817a419eaf667a071478ad1a424081437db7b7db0951ea30040fbcbdc0121034d3e3498a4f125bfc0cf3eeabe8ae604910a22c27a825c5f345ae7186023ae82ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8db60100006b483045022100d029c7090bb9b64d96920515b158a9c79bf6666be7c755e00fd85c48c8fb03de022037881d78f67dca093cddf2cba9d6f93e108afff9d8db2f5b46507bc3b2e92a84012102c3d8316da0c1250d9d9f22f225d72021344ba9ea3aef22dc9cb89d8d326baab4ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dba0100006946304302201f7935ab4459cd8b8ad9b3afee270c213218bdbc2f1afdd1c05517225901ea54021f3e95f71becd2a8ce281427810ab6494a2e213fad920dba6f960767282bacf3012102dd11e61aed947dfeb87f4898e5383715d9c4064a18f92dec576f484eacb2770dffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dbb0100006a47304402201a05238c25ee15f4d19e763e99463dc4d8514ddcedd5fbf52a7a60dda56df3d4022015ea1ddfafa6d335347b0797877718ae31b35d1470593db2d3315eaab301a0c2012102383c6cf9f6f16dcb47445d984395f04b1a41ddd42cc66d0aa1b40b2a685d43b2ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dc00100006a4730440220743442be0b13d5022b8cf31206d25592e6efc1ebf11c22e06e73ae664fc50456022014882eeb158f755f2d7443983729c5b81cfbd30965d948606fc475eee1ccd3ef01210280010e2da50bf224d2ad15cd764acf0aaae9bfc9efc45a8d88258cdcb673e95fffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dc80100006b48304502210094b490aac0d953e517725d7b8244c6f2f059b960405ddce907dc005e100a9fe6022022a5740dd9a927fc26146f25288bb9a387e9ad099568412fbe76d5e11e4768af012102b8e37b15ed2962547879341e41c3b55659929ee5c259df0b740320d0d627968bffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dcb0100006a473044022004f18fe83b4490b4eca0383d3b56c9055b29efe6ca94c3acda476a428becd04f022021035ed356c63e0647e4cc8cbeaa083ddb6702728da9202058a4612a16f69b9a012103661abe9ca452dd39e739dcffe5a57fa01b09cbb16f0c77efb7e83203c60b0357ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8de80100006b483045022100c60c5ff1453a041d06b26b81759f846cf69fd75fa9360fa1c15e679bff52cd650220325d1dd7ec3d6a2300ce070fb7e72d76dcbf822eef29b766a897e09efea4a0a3012102a86b88cd9ba8ae114080ac297beb46a1e9d94175018f0593fecf69fbdf0de37cffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8def0100006a47304402202e4a26ec62104dd8984c9a870f84a9a499b5ebee1f300b3f18065101e7f5b096022012f1ff7449c51fd7ff622c285b39b720cee505c78d4eb64569340b6dc6977538012102727f658519de2b214fc282a4ce9f6deaf09ff34d20b95f45f97199916cae95aeffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8dfb0100006a47304402202748342a9a3e51f4bd4169d0f4839d147aea77db107c90eb2b664f665a1f730902201577f054d76941945c3dc672ce30d0775fff5251375c63370e8e05069c5bfc230121039a467d42aa858bb84b7115483e4aaa32a033b7bbc04969b1b283fe1eeda525afffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d030200006b4830450221009b58e8bb89bd25151988bdec1619da76f99cea0d5f1b1e36535b53fbe69630bc02204457ffa90d82212f9a3d3065adfdaddde9cd6940cdfa9b56a2410ddf31cca1090121035a3e4b6d57a68da2264a66e8dd4f95f08773b6bb45dcb13c0c4c0c02a1370bfbffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d050200006a47304402206e0afcd5808327fe657e0e5d9ad420d145a186e2dc7c2cce3b2ca8934e037b800220398f2ee374791529915a7311a11d0eab48e246bba355d73c6c0f7dcdcc9c575d01210398b42c0bdb3ee0a36db5bfaa54df1af62c8e618c45d2f607d50b41981021f5e1ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d090200006a47304402201a7b5700f0231c8b1b1b985cca610e1ba8398dd5e2ccb00afaaa021685c760d302200c1d98f9cd633fbc53576f4f35c47fe5f92bba44da9d63462045d061bef85142012102365bb4d19f31fb2ebb9e0dc2a3de342ca54c9851e11e395cb90fc00372ff0032ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d0a0200006a4730440220570faa67153cdb600537c95cabcc27a6f3be50adb1cb34415ddf28716e28fd30022033024b95a07e46808d4448c7672cf7e55b62c4996dcc49d9728db8d8b2171ea5012102869610d6807e1b8fa4ee9fc9ec77a5c65c815c459ff6c0c43ca58a385fb21ff9ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d120200006a47304402200691a6bd9a218b55b22d7c8fe7e30cb4b5fc8d9f0f467105a12840c4242e774902202ec960bb27f11ea233edd7e8846061744ff4ef43d190a1410e3f5b77960b754f01210310d3e7e2dd65d14ea328df383f942ce917d764a74b4211f1cd885c3a15f07072ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d150200006a47304402204d5eb7479a87d8f7c5e4b10fa730c2c8f7f249bb98f33ac3f33b653c83fe6d5a022052ff9a19d2cc5d405efe6c487414d62b5c7d37d63b7e7ecccbf247abb1303fa101210395fda29e25b649eb29f8ff2c5553a494ef95093cf746a8b229826a1de3361daaffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d180200006b483045022100aaf10af8a32cdfbae477c509516dbc21d22e9d849a2dc98f8385a7e52170eb6002203b774d805c6c76e876c98cc2809f50aad53a18e677991b3fcb05d672a3b751230121031afc3db826d4afc2b8ce67eb4cc00f1ee10f42618dfff7147d9a9cc70e09c3a8ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d190200006b483045022100d060760d508f9938cb5da7164203a7ccdf7c953c637e2b1e8fd6491e28c3232202205ae876673c4ca0350446b86e8bc4e1b06aec36ab7d1aaa8e426f4101758c04240121022abaf62caece617e1e21f254ea8f039d15b3e1d3114c873bc2b13a74b3284a85ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1a0200006b483045022100ca3e9d9e40d47a35652e0d2cf1492b6d6f99ec73e52c4326c2e217849822521002203cf8780eac3153cf7e6c278edaf55f7b23c124e7258f25a0948dc9e77eb008d1012103c974162d97e336a85a0ab0524dff7642361aa223e828b28043cbe72914b70b62ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1b0200006b483045022100fd1061fd38f51e8404eee8de6bd32532b963c85b0c60b6b12b2762b58d2645b702205d16be604e7503a4adb0b89ab83efe0a71f0a3805788ab21cd2191f2030b0291012102ce403cb7fb712e1c9843fade390efed93527e69b909bc4080590108b0f62185bffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1e0200006a47304402201b2c7422c861f1afeb4a10537250a58674319b40eaed8ab917bf4c01a6e3ab98022018f5a40ab9c659ebb24e8366d997d401959e2b1c5db310ed3360ddfc3c0b55940121038787f749b550633c0ce68c87a5978c93724f21deaca1e27bb75f18b48cec83ccffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d1f0200006a473044022049cb99756a94f213c4a117667726ecd2c30b66cd3a73dea718378bee7aaf006302200d64a25a5489927ec6d64dbfb6ff1c4fcb2c02d9177b70586eb5dbb90b48ffb10121032c416ccb3b041baef859872db424aa5364116abae839402eb16d2e66d5aca646ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d200200006a4730440220730f2ba94915cf66bce625f231d4b0f9205334b53feb95426c6293519163eca00220707e1a06c4b574a91fbb9b0b882ceacee3838ac5910f39c4078b9bc2a5442cad0121026ce0c77f80afd4c0034136698a4a96cdcf0075258293d883bf9f4370a1d1d25effffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d220200006b4830450221008fe9b998a72c096aeb74b8ef285beaa82d9c22446fb19f5850defa23acff457d0220223cc831bcbad7ff1119059054d1c867fe98cd14f90f9f45e39a4ac8d695bf20012102a0074a676f3348c8ef8e8f9d06f8c619f92f21a816a9b5be96c01079133f9153ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d260200006a47304402202d08bd80d982ba29d7222e51326737fbff9ad94ede724c59243e650f49afcbe402202275771bc11143e9f336119144226f52ba7d57976447b7501878f4cee6639882012103dc6a39ca42438d74f372f2e6c39b78cae0a6a0497ba9b324b37323a429c25693ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d430200006a4730440220772a000a10c3f2087d88503030079805ab985b3d923fb89fb5716f11dac5afe60220555cef4e3c4dc02c5f8fe6698ae4e48410eef130b328eec11f7f42b66b638f1801210221806710404c53649237bef9592c17097c4d173029ac24cba99e2e2654da33ffffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d4c0200006b483045022100aef1b74b7a4dc7d34369a87b59de0649e67b1c6a2569870eceaaaa1bbe772209022031f6cb38abe93ae0beb2e70a72dd291d83137675c8a65af97c647bdaccdb3a43012102fef18983aeb87b4361a6d0df281dd301db5976bb29cdfa0f6eed403be5921941ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d500200006a47304402203ee99c495c372e89ab7a116a584c0233105543463aa9677c63b3c11f8e59de85022048b4be2b999e1c80870e2ca9829b6a13144c3d530a6cbf7353fbdd5472ce8a97012103912a48f56f2dcc6877cfa5a5b34d6d1fb545c2dafe35b3bb3224f7df2c744b62ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d520200006b48304502210093b6a50d5d21ba73d2f4eaa931dad23fb37d9e14092bf5e6a998ced21a5129d502205f678ffdce4feda9a0cb651c677700a08643eb2af259bf7f7e7e05075ea5c2c70121035f91879f6236f544660bc6ef4db7b54a75d81877188804b624b2451a22d9e603ffffffffcce475baddbbe9b5f28e614a5077882d98fb5636ac0a8ea882a8158750a5af8d560200006b4830450221008ef854b9f8cfff81ba5cf3840a7e30757e8a04607902585d2d28ceb29838d4e2022070f0a093cff8838bfe9a7183025068bcf14d5c3a034082ad778915612a775354012102fa32f17b49b73beecba0c2f8227b08683b56eb78c8a40edca596a9010c91c51affffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1030000006b483045022100a796aae8ee86bc1b60cff71107f85b32a564b2fdce68a3325bea0e803462da06022036ffa4e3cce3e6f61c961904589e0382da12e356c5118ae975da0f5cb4ddf06c01210265d1581eb36fb426a10567e42a77c021f054af9baca4bc20cca40c4d7e7a6b72ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1090000006b483045022100e112752ad37ffefe392a93492df41fd20b3cc00a3dcd8bc58d1fcca5438148bb02205edb6517da39e5142d3bc08125aa71d400417138caf22f402a57131ac73ae442012102d09c0636e4ef75d55f30e3ce8bfd0e15b8dad9f4125fc896269d8d94ccdcccbaffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce10c0000006b483045022100ee1558240424404caf77056eb5bfc77476744b556b9a9fb53fe46598de76cb2d02200539461619399d4c70fbe092fec1a79486089c253ebee07ce0dcdfd87131f90e01210398b42c0bdb3ee0a36db5bfaa54df1af62c8e618c45d2f607d50b41981021f5e1ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce10d0000006a473044022009da225ff9f6ec4917fa24a10f2a25e42cce512c4a7877da14cb289588bda61e0220315b19e7880821a941d11541baa233a2a43a2807c2cd0192def20a52ece188ad012102e177e59930592cf7f25ed9e65932249f4395e9088234a938aa9e8dd8fea737bbffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce10e0000006a473044022026a21b23f0c94b2e4fddfe0623e95ccf58733375ef3a9aa56c5ee85e2f2b02ee022068682ec97d429d5dac141e98d47ba6974428e3436280e3eec253da257d5bf5ee012103feee761b7175fcd481dde6568aecc1dd9cf04df3d65b70f005541dfa9403763dffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1100000006b483045022100e7d9d061b253fff6fbc7f5b533661e2b08ce7d4496b80d988b69b389887b6a6b022036c72c7d0f1b1f349fc8307dfe71d30edd93a3dffd73b613fa834f3daca076fc012102bdca2c5703625f8a3a800d14cabb4b622dfe766e29cbc4e03f3e2faa227e4ceeffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1210000006a47304402201f94668fedf809b34c1950e29bf98e749077eebe7b555ac6f182d1cbe95a4cfa02205d051a3b51213779e0f550b584674d33cdeb43a169cd2ebb63021172f63022520121031afc3db826d4afc2b8ce67eb4cc00f1ee10f42618dfff7147d9a9cc70e09c3a8ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1240000006a47304402204111c3e5248ff15a7b212cf8bdbce95d4318a4267310a0b488fd8f3385d723c802207ce823aa9d90642244d48b22cd62de54c2eea000df629d062cd2444725b7b2d401210310d3e7e2dd65d14ea328df383f942ce917d764a74b4211f1cd885c3a15f07072ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce12b0000006a473044022018dc25150db8380232511b5cb9302ea3d903cc7d666906716b4e30f0a739ff0c02206d772aaadf196d0147c85ca297ee737d41291df5f4541ec54d36c9b755b9ac8701210200385356b4ddde07e2d9bdd89f457ba05c5e9da33ad7b50a92051bd2a91a90a6ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1320000006a473044022059f1028839ab4af6940f16a1e516283d8f0e5a82634c05677fbd3cc35f77e0c202206cda1e5ea5f7fbe92eaa3762848e504c0d7f40374fe3176d81ed366e07076582012103bda66744af00c2926e8a9fe7d4e4b10b44978e31b6c1c621a2dd2192ea1cdc89ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1360000006a4730440220019aa11c92b978eca86cd3cbb88ad40f9035174559bf802195dbb8ac2d7099cd02205196846deb50c0cb171f46f247de8f98b3eee3eeeca79dcc8db095fee0372ca4012103b85351a7b2041a50095248f463f53e82bf504063491099fdc3b0c9a83190bf8fffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1380000006a47304402206d2295d8bf8dfa5b596ea218b3badbb648371beac8550de84899dc33affbfd6202204c882dce7c35e7fd922c2e603fb3eea9a9b1f4a955bfd50ee2ca9946127c1cf6012103c974162d97e336a85a0ab0524dff7642361aa223e828b28043cbe72914b70b62ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1390000006a473044022020cf5cfb7369179e400e3025a2f78d5be4f975ac26287e634a159dfe1c857b9b022038736df5d7fb7db76ded1f590123f73858a358c85e4688de3b6f73000f2d7c13012102365bb4d19f31fb2ebb9e0dc2a3de342ca54c9851e11e395cb90fc00372ff0032ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce13c0000006b483045022100e7bca4ae4c32b339a2359f07b7ec2ec369a19e874f06fb4e7a7a45a6aa5a0d2d02204b2dec6c29ab65723761f9a1fa1fd71959714a956fe67792fa67cb2521a2517a012102869610d6807e1b8fa4ee9fc9ec77a5c65c815c459ff6c0c43ca58a385fb21ff9ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce13d0000006b4830450221008251823c9ca39a29ee0f4e2bec048584bb6ae7be78a758372111f0a38515bad802200ac17d99279c9725233d40ca3a76b0d4ea888e5d459b9f8953257303f7dfefec01210375d0cd09e670b856e4d4d78aecfeae7ab5a7512c545fbeeec5e886eba5058191ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1460000006a47304402206a45ed38986493559e31b146a54279f6d3db81b6d458a6b492bc5d6da1846e9602206d2361598a468eb52b6ebda3a79e0c592a803059f9108926e2960a0696f9430c0121021d6d6d74787f56298827ba2de6b362ed728437b43636605185bd2c7d38b7a2c6ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1480000006b483045022100a759eac0127618b9b67b3eafb4f542e5ba700edc002460a72448622f770bf5ac0220444cd313d27bb968e215fbdd5ef938d2bac2269f62b19f75831d2c8a6c1bf55e0121035a3e4b6d57a68da2264a66e8dd4f95f08773b6bb45dcb13c0c4c0c02a1370bfbffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce14b0000006b483045022100ac393f5efb0faff6e47e460d94705b5c28e0d4660954c86bcb8d2dfd2024bc2802200f9758bb634bdae1e15deaf25dd18a014d642e3b372383d569ef1a051cbdf006012103d44ec737656e1fd7b52f1dd5d6e223eacfac855527cc86e5c89ea8230222bb65ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce14c0000006a4730440220643219e1e5397c263a571c823f6e2b69c35ce651c4da3c9ca4fd89160a481c9202206878f8d2dc692303c853868ac5083c094e8da9c2f1bb871ef700e2cb980b8a7d012102207ef56f0c83b6d2fdf865cfc9c8398a0e91d5ed6fbfdf5fb8607f1050fadc44ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce14e0000006a473044022059f45814196e8b57b0f82de0ab3703f78692b0048620c123d6f649563ec6b5bf0220057d85bdc145d2be54bd1b00151bc99f23ede82366d3113078d31aefd154faa7012103c08c0d0d0e4715e37f3f434e06fc3634a820e9dbb7db953efa1fa2b707ddd61bffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1500000006b483045022100a0da2f47b157feea9366a90a779be3850997faae8554b99e12e3724f8d153f29022077957501320a4648124d3b358f0c6bc3c075dfaf6443b410d4e833f1a2b35a33012103048a2bcd8bf1d2964705c847c4d767a531342071436cc1703562fd5ca7937f3bffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1550000006b483045022100d8c72f6baec07631b9bfe35474e29a8f4a730d84ded906892f05dcc5aa3526c10220663ae767e4a776030eb98f529b96567f6e73913c9a2b519d0d4d2ff6a491e46101210281b87d0e4b299a674d62ec6d25454bfe20337f8d7ea3e3b1e689615cb5526cbaffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1560000006a47304402207b418b4cccecdc3109842012a197fb955d9838ffe22390343cd4c6ec001c6430022001bc698f4b73de6406d16f6217a6c992f47e78b0dcc993248b12a0eb4088868c0121038787f749b550633c0ce68c87a5978c93724f21deaca1e27bb75f18b48cec83ccffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce15e0000006b483045022100a19e82cd51a2dfb1ace2e1cf2c4c33452467412d55e7fe175e563e71f3b2cdbe0220757786bb52a4c3cfd0e6f2d56d67dbdf2bffe4bf0f124d9cb7c5e2a95fc364070121022abaf62caece617e1e21f254ea8f039d15b3e1d3114c873bc2b13a74b3284a85ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1610000006a47304402202044ffc93608403fff54930e308033adc5359ebf9b65435fd1a29204d42e0837022044776b7fc9b011bcf390df52dc0b88f7573da88bfe53edf1f59c2feb210df3b8012102da7da607a3d5689f3b548b84158cd7d01b6a3d06ce46e21d30a89f2ae3b38d3bffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1620000006b483045022100b6e413a1f591c6c41b215ad6637d209faa8d5efa2820c6755496c3f3d0095e6a02206749a0b8a3b985b1866d2b75538dac1b4b6c1250fc97f20501c1404670089158012103ad415589158489b4849f08a35d5232ab97b8af419f54a51293eacaa207f1255fffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce16a0000006b483045022100acbd746359056e3d2c511c76b3ef543a4baf5db97011383f281795a30e6f1555022006bd970c51340437ce3e148dea839bbc96f5adaaa03fcd20cb72f7e4c8416696012103c808a9293397918ae34b7c6c754554ed70f7b092899f39f40c84efe6b44b3cb9ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1750000006b4830450221009c29bd4b22b5dbcbc8b723fc00808e5dde928458df472f7f1ef508f8b4915acd0220344d78cca5a0c0cfb03a8d5b4217944233529173f5bfb8777a301654b49315cc012102a0074a676f3348c8ef8e8f9d06f8c619f92f21a816a9b5be96c01079133f9153ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1760000006a473044022046d183986cc6462ad04761a493816a31700ad9bc60c4be0589354279973ca55d02200f653217d2f08fb2bf2f779f2839c577a8a4688f5d345c5c6e6a8e0f5436a621012102001c8add5f438e5bb88384cb5a0636eef778de49c72ebebb820d60ec450c72f3ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce17c0000006b483045022100b5ff9dd5493ace5193217c279274a800200c1cae77e90b66da01eed7c9eb47690220383c858d65d93c097ae532c236f337163d07a98aa0e623ed1395aa586108752f01210254e690cb1831c658615c1eb7619d37baa29261662d643a0346019186edd5e8e8ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1820000006a47304402201413569e40213407ec132964bf8a5f5c53f787abcf7f8cd8a211f2b626f009b9022040dfeb7ed9c53cca8a1825bbc327f9a42ee4254b610e2de491d7436fcfec11ba012102467d9c0ba7fdcb7c5bcfb88c409da4abd7bb7f0f1392ea537375d308beb726caffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1850000006b483045022100ef1ebf71f7b7b33b29cedb92462ee186247dab4ddf5977b5f385ff90033c6164022052c042fb195caf30b8fab1376d5703abe1565c49b4dd69d8ea5fa483e6e415d3012103dc6a39ca42438d74f372f2e6c39b78cae0a6a0497ba9b324b37323a429c25693ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1890000006a47304402206f171f092aa1c83b385293e6c8651d332b0a26bae1e4a1b936f579b64c85b51002207e1178443936f66cd0b64265378ff25bc74422fdb57e3561323ac04e688c76e601210201a71c6842cbee9ff2e7c0aecf1042ce5d30103f4f73d8ef35447ba56fa78430ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce18f0000006a4730440220403520ed55944828f742a4b8c72feac418271e14b516d2331f6de2173248c972022046784ac0666c9f8e9495e9ea027530c8254d382677ecc3730b1e07dba775fdd001210290297b638a0a815a09ee946947567946ce8d40ad4ca4b814e42d4b86d64e5161ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1950000006b483045022100bd08c38e9b00a560692034bbd25013f50d130a335c87a7e727fa97b6c228a3fc0220287d22c6826d9fa683a3914afb5a9c04959de7355fd6beb25ace68db19d3239e012102c6bac6f6a26107f1b6ac7731d0eb1f68be1a960fcb936138d5ec1003e3009b9fffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1a30000006b483045022100848a82938d6268129d76e94c7b777f8c0a1d599ce007fa34d1eef5a711cc815e0220240cd29affd1c5daba89606afae3e6d98583c18731f2346cdb72be6eff28384d0121021a5795b6607ad6e840c9e286bd0483015f198c14579a07b09abe352f3adc21a8ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1a90000006a47304402205acabe46f8f7371a38e439a909e0cdfe69ecb7b9f8d1af57057f6291e3e5544902206e16fd082122e56e3165afac33b873dd781610cb32b324f5ba2cb28a1ea8e08b012102aa40eec24fd5aa30cf2ba80b1dfcce8b3cea6f81b63c4798c40c52b0641d7d6bffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1ad0000006b483045022100c7d466f3db1f924d4c43b80a717c9dbf42bad03e305e3dad837d987417567f120220474d5655aef0d0510388194185ae579bb5b3a66bb15dab578840a645c2e24100012102dcc2a5abc3a39b4840455b057c912c3685b6801e7df26d3674ef6d2ac12a8d9cffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1c70000006b483045022100e2a7032b935736a85de81c73b23872c3c9bcb6122eb18a40962ecd02049f3bb702205b510375ddae6c39ccff655ee40d8044d2af612b595e2b19e0dcaafc6e22ff380121029d46352f694824dfcc800fb6781602a527d7ed003be2d6b1d64c8e6eef50df7affffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1d50000006b4830450221008ffc2ab065cb3a260ad98968f6acbf193018b0062e8ea6a92e4f687377accd6b022012826fd27976709a57c0148d4840964f4a3c3e18b7fc1c070845ce13589fd32d0121039a467d42aa858bb84b7115483e4aaa32a033b7bbc04969b1b283fe1eeda525afffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1da0000006b483045022100df98058d7a7cd1e6157cdb1a5a2b1bd889cd20a55ddfbccf8035c31b7357e4df02206ab0498e35c1e28cda73de832e7266534fa4c07ab361e2775f3f4eaf1d71b14701210253261bb6c86f395e68f7480f90e0e7a47b32ce0d56b75be775345a5fd009ea25ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1e70000006a47304402206ed58549646a47e9bc7dfe95fe060fc5eab35bdbcfad49b3533bb35d2954287d02203ff50dff4929b869c1ac746749f66bddd6a6945e228d11286e40078b143b2087012103cec48e102fe0a049ebc494ddd3d14d3792b6735516ad9b50297c4733a17b90d5ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1e90000006b483045022100d748aaa58a76351cfa2adf3fbed17cac0188ec7c822430358bb67d4c5c0493b40220358651be876f294274b82e4ab27cbe614223cdeeba7f18ed4de9eea029430f98012102b99681d93faf7d71411527c099d59def01beb5aca0bbb37f48df055a24627828ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1ea0000006a4730440220208d5ababeded05d060511ee084d4bfb8445a6bce9f0d3344ed18d387175315b02201a060f0adf15f82bae7fc1ce52d60d1ff80dde87bce1701fec63829f8869cd110121030df38f26078d2072ae8c53866fcfce2398fbb3d1b39ae3ad0f79eb77de9facadffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1f10000006b483045022100e64c57c363d4d317d4c63b2543af5d18d5c80a8c19ce959f1b3c64544debcf62022054414f5c39ecd2e4b7c3598bca35fb8f44b8c443c6369806f47275c4d69124ce01210381e1b57ebcdb3b56c0da96dd3f40c4809533f3949d00740175af286551fd39e5ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1f40000006a473044022000af69b4690dda54e96bcc22ef1ccc6e597ec9643bd791693b87a7541281dc4d022014b7c8bda024a5bc12db33999a3561e97831b0ba05297547e5975dbd793045fb01210291cded3a99d54bfbba91178c3ac811e0545b9ae98594cd88165bd8d8a6c5adcfffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1fb0000006a47304402205b306ce1132daac159bb1616dbfebeaf141abb669a6e8c0cfe6083ce5d2acc390220688d5478cf187590de77896b1edd9a2a203e95262edb5f269eef7b8ea83ada19012102ed801ac57a9715ea9d33bae10cf77e955e97d3eb8ec3e0610b2e1618aea49743ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1fd0000006a47304402202493af2acfd48e7ef306a2ca586cf1ce37b0b0df15dd529978d04d0c126b190402204cf7fa00378a962f5a02c13af86da3ebb83eb41757f47b4dde7364e207a73679012103661abe9ca452dd39e739dcffe5a57fa01b09cbb16f0c77efb7e83203c60b0357ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1100100006b483045022100976aacbc4b3226fde372eb72a011f8343e0996675b5ab5dca8caf4c9411498b102204ab6c4955d76ec1500c7c918c58ffc1baaa0cafe7d37f3fb3a3369dd924de9990121027d510e7ef2d251d74ab897b4bfae915cfb3979a043552c0d8ede26a95ad91fb5ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1190100006a473044022100dd2cb049a9f33e199f8cc28e3e7679eff17ef0aedfda59399558c0c49db19d90021f562725a736cf9b9c12b5f1db508915bb520a83340271fb8c94e205099d33260121025ea8074d10f85c04eef3b63930923fcbb122a5c9dfdbf41a56b47e91b3473c62ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce11a0100006b483045022100a5b1934e7acdf80039d21c94784cc4a0fcb3df42a349e03788b91de12f5ccc7202207961a88464ddf26ecb8c46902b55478d4224255d5be4d3e2b8715c4f5c0b64a4012102c059b935facfa01366100691c91328659bbdb885e3c802467cacd44e92bea4cdffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce11b0100006b483045022100961ea22b02b1cc46a09275dd082e1f59172ad5db2db9a092c179bac9910399c702204172b2a043a152abe48acd87505cfdf1d4c7bc61a60e4106d156286e15cc39cb012102cd0581d13ebd3d022a63c8d86140e52bccf1b454f824ab7fefdfd6a338c0a4a7ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce11d0100006a47304402202f5ec2b5364ee715fc43b743f73062f04c4bda8e3f287b840f16d32756f58efe02204aee179ba38fcfb3b077ede655d099d9cbaf546bc4159d30f27669e360d4ede5012103a1cdaec2cb6677a444b56fb6f4c7dfdad2f16e0e83da9d08bc0ab837d44ca694ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1230100006b483045022100ff8373e26ca26ca23c5e0f999e5ee448ecc8086c972b64619653a31da0a624e00220361762c6aac55ed25f420152a24133dc731b0a36803b496602a5ab5c5d220538012103966efefa3922def76bcb9c9da9ca320ff071df2253a95e554e88f0a43dbd1af5ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1260100006b483045022100f153ca090f3011070d662bc4b07e99ca4b134eb18f54226a923b090ffe79bfc80220447feb3a9d45e98a9707ec5bb9b7373cea53207529823c0db76c8655e173c6cb012103c0eb8b1c197366c5aca991758cb9ece316d77cbdabc880279b28ef0417fa96d8ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1280100006a473044022027f3f6b2eaf4d288835b317f989ad1188e49613b44c068eef0ec1a9f44a7c98102203e27fa6e447b42ab288937e4c2c2bac355db38c2681dc293ce88d3b9791c09350121026596df661f5d6ce02be34806c12efbd77636e7c0e9e8685eec7e9162adf6ce7effffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce12d0100006a47304402203b55c002ee815b3bdd84fa67e7ce831b70e8e3e7b1b65ff1ec8f504093845ed902200ea8bdf7d242d4fc592ac9d64a03e72c98a38fb73cf06d80e783badd944e3982012102407e47a6ad48d7be1d7769a0825e1c80da8c6e138d4a3244d02b82c86b134b73ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1440100006b483045022100fd421e1001a1a80d0f84072d648d66bac47a69fcc376596cee7f18e17cb5e41f02200b38c605e725a14f744f72dd62405aee66f6679d104dc0ab5870447990ab1f730121035619b45c3b340326aef6829d231c44907d48f1e9a9c79f2ad67bfbac67391755ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1460100006b483045022100c46d879804431bd6213fcd9154be70249545951b25a52f753b7dcb821c280f42022007f8b7abfa8419862c4a37142b476baf5e05a67b094828d06779a4bda7899e4d0121028e1b5a54a6a54245bf21f0848d672d3f688d11e9328f944365b146e38efb4bffffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce15f0100006a4730440220102fe149d1bdd0efdd007a7cc71c3b83b0386a848551748e7654e5e9dda4178602202db140ecb33fdafb218274777c946f42c28ee486a298488763a7a3781f0733a3012102b944e9570267305e26e166d0e782432bc5145c60d9b399eacff1b1b05497ace5ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1600100006b4830450221009224cd25356cfda7872374fccfc4113a6d050b6810ea69b6086722b30e653e3b02200eb98f74952c550e924daea631810f0931d8b4ff9327244fd38e8ff2accee5c301210394e8f35ed26bf683033a5275478aa2fef20dd21fbf5fc5baeb9ad4c4d4226698ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1630100006a473044022009eaf02543c90f2e987a81e1eef361c5315dbb17bb30475a4b2b0d1dd207d1bd02201210a494912a07390746a10294d9f9a371d06b9083625b66aaf43b17d41e82a5012103628f06885c53e23474d28cd2610288270d191923617b0856e9e0bff0a807aeb8ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1670100006a473044022044b02743085cca9ec5a2d5a81b6b99cfcb4339be69ba8b65fd9fb74815310bb902203d97807b80072d56ef4aff0395de843fe965323a5484d7edc2781af29654c7b201210360e66e446a61398bc9232016e6a2f34e5c26f362479398702acd988ce66807f1ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce16d0100006b483045022100a3e4fa2e154c224a621ddf06b086f79e62f183b89834a9dabc3d2dc9f468636a02202f0bfd705f6398d94d8970b305f1dba71eeda8f601ffbecd9f60ccc87b83008a0121039c596113a41cfd570dcd458350e7370af3986daa6e3953a1b5f6c4d9bd3114abffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce16e0100006a473044022013e0955cf80da8f0662294963cb6d3fd4c0eb694b7a7dc7d8ae1f615205a5a3002203e1790833a50d3d662582987ac0ecefe24db18987ec8bb3b46a6f81c2ae9ed31012103c357a607dfbed1b44707ac9abbc0b67b515a3369a10748dc3c5164336a4072ceffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1a10100006a4730440220460db28bc3addb3370375346727656c1a8c55bb482b54583e4faf9466ce69bf802201ca90204d80bc78b716b84e2dce5f456c61c9de267181493149ad3d2c81388920121030f7d9bd8007d826c505a99dcbc9c155d1fee0bc95c4cf820501cbbd0566b2d0dffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1a80100006a47304402205a9859cc2f2518dd90e063c9cabad05a73657d133367687ca269a05558b4577902205aea5452e9feb3f57737b33c7d278d429acf81d66ca3550579d4cb4f3f098467012103f96fd1a1f4db52fc0457a3d13be145d0a6f332d4e70ad776a08522d749dba056ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1b30100006a47304402200d6c40bd724d9143caa8a19f9b690eaf19d40971af193b37986477432e7cb7a202207f1ecf7adefb6f34847e200d3236d4754213a2540eb639c3ea719a81ff7ab4dc012102727f658519de2b214fc282a4ce9f6deaf09ff34d20b95f45f97199916cae95aeffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1b70100006a47304402204b2577ee7c4e548440eb876a2ce5b2108f5ef2b50cd29e4af9ba76c9fe0a6ece022076de26cd97bada67601db24f5c1ee7585aa441256b955ea1a4100e05cab5f4c4012103531725f6d0eba124ee177084ce0be3312fc88d45ad517ff08791b95810f94670ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1b90100006b483045022100d93a9613b27c7d264e5169757cef11ead4c89928df6671df78adb3916d86727d022070358d8e0837fe188eb1f5ae61f8ad2e0bdbbaf4653dad1fa7d2a4534177c4cd012103771ab78fab9c924f56d9bed1f4567ca8eaa7b84958575f7724d1b8949ceb2d36ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1bd0100006a473044022036caa3da4ad597835733b9988a0c2c93a2ce84ab2cfba3e881ae8b3d78caa63c022056a559471c90dde6e92ec8c6e043e5f231ef229ac6d8938db3b07bf4c302252301210395fda29e25b649eb29f8ff2c5553a494ef95093cf746a8b229826a1de3361daaffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1d50100006a473044022053ed4be0141efa9996949751e9c0ab4f1a1ee2b9598836f9e93282a2bfce86db02207133eb7956820da32dad04c275735a9cd973839d71f0b4f8177d45b027dfd846012103483a33dfb5497521fe2133beaae584e2672327663d930cd866f24886ea51002affffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1ea0100006a473044022035e53c2f8502874c854ae8d9d7ab6aaee793d9d2d69d814f82520422a8685f8102207e387d04d3c6fdf5b3e767afb6ba5988b43a10fec28dc798e3eebe9f048fbfaa0121035d9ce97541161d79ba3346e6c755cef41cc110b2fa78a658c349b3ba8e7ddbe1ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1ee0100006b483045022100c0f43856a459c691b1065b11fb80c687618f339a13326257bcdec0baac9539fe022055e2273109decc4eafdbfdc0456b0dedf53d484fadc7ce5cb8756d5a8c159c3a0121023ede75e5bc13b2103eb0d3c8c7dfb60b6c7b810f49ce005a55ae4b7d0eb1f658ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1ef0100006b483045022100eef38a4bb2747e5bb1800c3f4085ce43e56fc868aff98f3307a0358c158e33dd022079862e3b9f1abd528cb9a41b842ea5ca7e04212e810d65ad4fb899a9b0c6a2c8012102e79e860ef42ad0f30a2f347b4c06dbe0d82a36ce559fa65044a7fc033372105dffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1f20100006b483045022100866fc8dfc71d4a3e6098b27e1f837dcb18e48cc3aa7ca3d5f223bdb9a7ccf8fd02200ccb4860b6375794e17eaf20ce7cf01cbe2e392c989857c0c1d055937be3b845012102765b7ef38aa3489505eb3927828dd8923aa0525d117a7ae20b1f59f96df0c7b1ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce11b0200006b483045022100cbd1c355903d6363fc5cf087016ddd2d18609fc669eced315e8ba9bdf6abcd1a0220027940c9598180501779ec8d1588367e24775d1982edbf54d601e00545e895ab012102812793e164e39f5631e622575caaa9cac7bf43737f7f95302280566b8c7f31aeffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1230200006b483045022100c6941f9c43f6257ca934f984adb1bf248f78098f40f72bbf2dd6aaade13f06d50220099e7a882d56cf1f075a2ed8106e477e3c4be7eac3e2de113fb7440fbfd1ab6d0121024f467b75f6c16df2f8239406df5646651b6be9e9f50b504b60ae9a1209a1e993ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1280200006b483045022100d3d4821b1600e7fc7c7ce3a66377b6085f5a02e9e67c755bbd182d34767e7fa8022017a414ba662470886ef289a686f761e27c6fc2280f5cfa5664b1e143f431a3790121020d934c98a189f626654c773dcb53de3de5690b48f0fc64ad92b25f8d08e9015fffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce12c0200006a47304402207f9e201874f3ffe97fed726620eb1b3dfbbed6f3ef2782caf09af8cf8a6623030220021479bd9ba60b76ffc4f081b5817c74b103d99869982d3b408eb678798d7e5d0121021813cdd05350838ff535db25d1605c183c7b4141d0ee2ef2bad24671945f35ceffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce12e0200006a473044022017c26f22dba6fcb856906553a64b4500749316af603077d318358fcd2c77ad970220230bca1d5f797d53a470dfad6278d92b2b94a505e6e3e8c33a11946c71963c1e012102a48b5b01a33b59a2a3eadf23d108ce1ca951ab4d5bc81c4e87c7284f70e28e48ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1310200006b483045022100dbb4430985bb573d8b8a577ca5347ab0d195468a9de0f23b69f5c9fe584f5dc90220293bae1aa0d291dcdecbe147c767361b90c43be5ba8a01bea34f3ed34caf4a96012103a52b0909c010f6df76c29f4e53ce078884d7a0edb027d3a680cf1ff2106ec18affffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1320200006b483045022100931b902c13c655ab99db6143feffd7c48b52491bb41491def8cb1c53f34f75ed02207a08369ec4a934c7136bddbd8570eb557b0c865c2d7e6d56c757ab7f7fa4ff6b0121023a186f3c944d8349cd144d419f2f043eebcf260f5cf06557d87a11b731c0e76cffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1360200006b483045022100f9d4917400ec6fdc41cdf2274f1760b54d1bcbecab9deb00efc294380d1ffc880220631d22cb4f9f2e3a7e0450023e6f089b6d00eaf9b37925d4bbf46ceee71b81a4012102c686a2956d0d18adeb6af37438ef2392a7e1b0c070d253212d333013500c408affffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1370200006b483045022100d8b04e92ba289360d3589e77ad35126c5cd759787b4bfbe2ba8163f3a9406f6b0220478f3638c63af6d2c6e5a2ef890523cbe4381a5b8e174673d599bc453cd20ce2012102d091a45a2368c4fa118ad23fe1759efd5b69aac7748ebdc04a3c7956c772bd42ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1390200006b4830450221009a5d8dbf8561fab292d34cba14d1b0bc76c475fa7229f1dfdc63d392349c7f770220343b743c1db463b91b443ef3bee68eacc92f01d6eda9c27a655bb47ce1f4ef700121030d170f347bc5ed046ce73ccf1a60d017ae7cee5f7254104bac52dd0de19ad91dffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce13e0200006a47304402204446051fee410f77a847137e376f2f123575ae8352c4f11c4271f9c42c8210040220560aa920725767de3930e3b35473723a7ae39fc543ef8d10972e3508a142e7a601210367285ed00b1ea1c032beb9515c6ccde95b57965c483871b83cccdb58c20857fdffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1400200006a473044022004bff4c7ce6489cdb962c1900274a2879a9e7388c00267c4833f0742aaf54c5302202ec1183039fcd4c48afac3a97a214776b4d85b8cebff1216deb4c74d73fb6a420121036b80696a61486e2fe3bdfea251522ff9e66eb66a4c5162fdf60ac6c833ea90d4ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1660200006a4730440220679fb3854cf2634447696468f9367640b61737c23883bd45235c87d734759f0f02206981cccb33b6348a2f32c4abcc2b34e2cc127c97a9328319a464a3bc7ff7bef60121028035704b48f9cb31a48e577bee6c22f6c259f9b6b23d6c2cd325148e8112ab12ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1750200006a47304402204f79a45812f628cf1d11b79adfe77cd1d7a0ade3f64e983d5e5feb1e6dac132d022021ba37477841b0370cbeecc3c9ed844c1ea1d0569f4bccb735b772510023d45201210261191a870744f836b3571a815a1b1ea13a7aa0ffacbf3d45943986ebe3768b8dffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1760200006a47304402203f35d4957abeae071b8f0cd2bd8acc52f364aeec88913fa3c75c5328da0b400a02206e6a4a1209bd4c2bdc6435aef7c9b2439ff0c093f1fc524dc4216d21277933440121022981500e07f5e6bac5b5187a33fe6ece5962db6f98defb2b5db580398ace645cffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1800200006a47304402203ed2fb6c30ebe4ebefaefb23e37e4ee7e636e46574350ca8da134a4c0176894e02202b105e20c5178946c576f2f530b70c532a9ec83f0d23b0769a8a5cc62be494e40121028ac506e0e8d1a686e602f6ab99fcf9db7d91f10d9f677f67fd5e3f2f273b167fffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1810200006a473044022053a357744855f26e6c45a8624588ddcc5fb697b08e75697b61105110f444930702201e2e5de286be705bf72fc6a619812c522b80f5aedc917b9e041e23cc52225ddd012102f9b763a3b03e3a2efd7861ce9863c32e863e919e96573c923581e8f64fcd495cffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1840200006b4830450221009a4cb0b438cc08110b96116ffe7a5f990feade8fa6bfe64e9cb53d71f0d2fd7f022028c58666bf6868e277af9f1f2f086b4e2be80ade355d66ed967b7f1c8aa444b3012102fef18983aeb87b4361a6d0df281dd301db5976bb29cdfa0f6eed403be5921941ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1850200006b483045022100efbe5a98380d87a33a1b32415542ff5804a3eb9e2849eb35a7869b812512610b022019f08404a2e2a990c4ec76d431d0b54fd990c7d364f56b0c9cd0a6e3f72f0d54012102092c130bafd3f042a14faf0791e49dee1c4e4c45fa8151f8c29e28a1eb156065ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1920200006a473044022035250088d01cada30ade05f3c3b36d70d861bd1cdf06043be51bbf8a1490275d022076c597cf590d29983e7e4a1e86b27d56fb7082927dbfbcce011b7493da642b230121026ce0c77f80afd4c0034136698a4a96cdcf0075258293d883bf9f4370a1d1d25effffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce19e0200006a4730440220776d3446fbcc8939e09783c04ea919190dd817947d9700df4f70f095c1777d2002205164b3ad67db0578bda2da7ad6b43ca6164d8cfdca77a080b4200fb1bb503bd00121036fdcf58eb2d612438d6117303f666df1bf0f29d09b3fa886b351b80c81480acaffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1a40200006a4730440220093839389b62068ed30bc99a49d738d482022f88c6cd5176b109d44483a091cb0220684c0077fe2ac4bf51208dbdfba52b79d031afbc3e2629c21f7d1630383214aa0121033f10a61831dcba9c5a55a79d7e4889a24f71e2bd65f34ce770ec07654fe9c5c6ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1a80200006a473044022054e1cbc2ddacfb9d34d95a304bb7f78d2dac06b168fbfbcca1d16871b1faffa002200794a401df56c7dd0dba4f8f2bde962345e2e1ecc89974056c79401f96f7fa330121029460ffd6c8b809d3cf267772a31a9ad2e94a0fad6677446fc81d03e5da48b73dffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1a90200006a473044022065a97b7d338e2476fd512d4895a7b8359cb7fb096b9401206109735be41ebab302200d556b237b96831f0b558c8c2bc2bf4614c237f893ea8c500a7656e518d3039e012103ff89a25c084b465f1c5d854078ceddd639920e4a06b1ac84dc033fee257752c3ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1ba0200006b48304502210095d50f2221faa05a2f24f7abc226f3c580141969d72e26d0b7d706c1f95d69b702204929bbb23f7712b19c9cd84ee774a7c845e5dad1873713945904666acb0f5b52012102fcd18fe4278faafebc53b6da5660745d2f018def26e4ac1c23f055893256eb61ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1c80200006a4730440220154b98a27af29babd05fbdbddcd83c2945c12e193111bb01a81ff7c6d8074b7a0220025d9ee492e11b4f35ad0fc65c817f1371828a4961c3702680c0c7d5b2ba0548012103421a402b70c808024d2b6e353822aa266272106b814b4c84ccba22211d2288dcffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1d00200006b483045022100fe1af213c6c257329c6b91478180f36b9ab517134359e2710335e02c1479d302022050a2063bdcf822ed723e48d10ba43dd04a3eb8ed5ac4638c9be5b2f935a331a8012103cc1cb2bfb5a8610d0b253b93f9bd07c2bde18e387b149f2947b753b36566c511ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1d30200006a47304402201d6935d87c82a1e0d6c2fa606d0bc8701d047db1f57edc75ab6032a48c18d60a022037b3c5c7262125313c39886615ca74893bb863688203a7143f34b0c74746c7c301210257ef3ad11cdfdd5206252442bcb4a3c8bb6aa5c333e9292d28a6b08acd627c68ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1e40200006b483045022100ba49330e9f406e4c86e9967c64131bd0700b141f3833c75dd0044ea4479ce8e602206ff81e4d19a934f76348973aac37c345fb105371f288c7e217ac5fdef3809c1c0121027186a3f30963c7d158c4fca8c002f8565d9d19e010800f352ff841d101068eccffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1ec0200006b483045022100c52c36c0eb07d44c8f16179629353c3ea91a50f2b8c29ca2389c87a96f5edb780220087db3618d5258513284139dce469eef56b20c0388b3b508aa08f060cc73988b012102368b25c7524d1dcc32833d493cadd47cb44078f321fbbf0ce3ec4695c1f60f88ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1f10200006a473044022027fa07b35fcaa0720e069251fa2c0f89fbd80aa24688deeb3ce6684d1e1a787002203aea48c806ac23d7ccd9cccacd8993e0258ef48f27010247b0e3ab3b6e269bfa012103a841b267fbd3eaa91bd3cfafb1e825bc3ce407aaec3dcf141c3a7f3ba73b85a0ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1f40200006a4730440220670673d5e0db628e220c73e17aa0111e24f6796090fd66605e48825beaadae7b02201e5554bf875da04100c60f310f6ff44ee37cedf6457efbb8e35eb459088832580121031127a9b77b881ab4ff268cd9845cf781f63aff8f7882e0a7f3e207a720072751ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1fd0200006a47304402206f2314f3361b7a931a18598d5dc3a05eaeb6fd032b88c41efbfc7bd0fd5707e2022046b6866073a3badc6615fc1e67897a945a9b4b097cfba7c679efec8906cfc18d012102dbd4392d0aa21a4ed840662efba31af122eb1469ae9a02c4c2f83a5c8095a7f3ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1fe0200006a473044022048eebdbb1590bb51df31a0e61a2fe84e28380fb9fc2f599abb0dd97c59014bc702204f809646714ea082bf56d0c70a3d9e0a4659610f27056415d9a2f523ef5da413012103c017e13a66a2fbb5773eb8c158fd91d87d51398ee06058d52874555e24d90439ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce12a0300006b48304502210094178aa187fb866720628d18efe0273d3d23f6d81478a73f600c647fdb2054d802204b18dc266e41da1626f763df02d7287384a41451b8cf87421ef765bb5dda37cb012103978b1b884a950a5c50b0ef7a9c3274d03469f06cd1c4b6a9efa35572442099f4ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1410300006b483045022100c7431c64b989d55da3bd961e9476f32fc15ea62ca7b564f3da0920ccfc2f219b02205b3826c561c8b19aaad27b16fd275c083eaf5e4db56f332e4fd21469b680185f0121022bccd2c3fe9a40ab0c9e6ae7cd4555920281bce457120b58a62f237a770f35a0ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1020000006a47304402207e2ceb28d1d5ec8a3a2bae661d8dd900da5c1efaf2c62d0479d550c4fe5e05d8022011ae069c65ae1f7471d41835b7b7f18513bb316fdb0662b280e892083c5e6032012103cec48e102fe0a049ebc494ddd3d14d3792b6735516ad9b50297c4733a17b90d5ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c10c0000006a47304402205e30558218a9e2de55a0c2c60fc64f63f79d6cd9f3755d04b7b2059cea909cb5022045ae6c4d510ab2bd2b36c0fb1b26849ce4aee47c68ded194adb2713752cf46d1012103ad415589158489b4849f08a35d5232ab97b8af419f54a51293eacaa207f1255fffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1140000006a47304402207d0e11f83485cb2e11872e112dd9ebe3bf915e80dbf7fd32dae074b40746046d022068fda22c86fdd9221ae00ee47da62511dc4c4c1aa06b3fca3800f088b90756fe01210319587887e9c21c023db40d7fd0bcfc1acbaed6796d02c1d6e66a31d0adebaa16ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1160000006b483045022100c25b07d519298d806e2144113a123ee190d6500223226363dfc5a4f194157c2d02207515d1538d54f66a197dbdec6868d035d41080944cd83c654250ad97efa6a2aa012102cbc5aba426b7bc5ffb99f5c3dceb94a6aec3e538b745cf2ade901614831f660cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c11a0000006a47304402204b583ab90021b1114c79234a0c2f808724ba758e7eecc264ca6267c5544a60a002206584514632f12f4d2ce46683acdacbfd9d80242f522a2d037441352c29bbe1ca012103dbe7ba93d1eb0dc93928d2f6ce4e8a58786c5a713db103041f08001bdf6fb10cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c11b0000006b483045022100843a6ebeec93c84e1a7024818a549fd8a81fe84f508c6f8db826969f6831d431022057c163146db80b195ace0ca7db23281f80ad82255e3277bf17c60a501321f6d3012103f96fd1a1f4db52fc0457a3d13be145d0a6f332d4e70ad776a08522d749dba056ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c11e0000006a47304402206033b437f9eaed1d69b525249b99c1fde9b08a230d877f08d9ed1bb0937ac53e022006583b02855dfd2f0758263f3e15cc8db2950267b1140132c49f4b2f063482d901210355de154c8c1798416b3a9ad0a253c3b6ac8cf3f86744f3116a5a024f8776a0b3ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1260000006b483045022100b7c8006e9ef1a5388a03f4515559c7be9d4a3523a872beb859bd04f3d1c094a902204a1a77c44d4dd31406c1b5a4a0c47ec7d6a8229f9dceee04ffadd5c6cf682f2a012102134f3dd1d9aa169e979cfc942b4acf53f98f9eebd6035274e63a26488ee18481ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c12a0000006b48304502210080bd1b73bc7d89b162e759832cf34e4ff19f859338d245fdc0be2b082f39c69e02207b93a46e8ee07f28e0617b6376c5657119cdfad9bc1c6365493adc7ac91398a5012103e5c650c07667218e554028e18c0ffea102484ad04ddb1840e5a8b1330dcdf97affffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c12b0000006b483045022100f85e9ed8178cc1197ebb4b35f391e698ed19a1ced00737f4e4069888dc3e2f8d02202eec34d757281cca45433ec7f3ccf1477ec52fa601e6196f074e76b61e9c64150121029d73cd3df300cc294c5e08f354ab262e75f33e2cafd0f0c5fe9d640b5aeef2b1ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1310000006a473044022072b09fafaeebfe781e3a15524dac07146624d7677a162d4bb506077ae2c349a402203bd79dce9df9bdb2331bb084f3170ef43427bffabbd78a40bf9a0daeb60f4fce0121031381cb42f1d3adcf86e68d1f293189b11390543b91ca52e8dceaa03f9b30fb0effffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1350000006a47304402203a693560347b2ffbda244b509b03f1e8c8cdd9a3a24ce49fe8d5a340a6263ca802205d6db80a6d2569e6d3a611f056fc2c6ee557f5cb21aac34a0dd28ff12d7c18a40121032aa3ecfcfa7a4240529f51d0ef4101164b4986c156f82f74426e7aedc9196beeffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1380000006a4730440220780114e1a5cfb1d0d438e6b749da50a71f72448d96f7db63f637a55d4e7d323c02203a7c9735335711a506902067493be57b64b5d94b1ed2a6c97253c70701acbf82012102f9b763a3b03e3a2efd7861ce9863c32e863e919e96573c923581e8f64fcd495cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1460000006a473044022048dfdca6c8d4388167141b9e66952ab7aa9bc84975ca9871d2439d0cbf32a2cd02207777703ccd32aee6c7fcea7fe737ceffd3ebe1a2c5f8c5e240da6436d89d3a0c01210243a7aca40e7cdecfca37b1166207c581a3fce3d19374a4d3b8476ad6d1c70681ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1480000006a473044022051306324185a84f72dc97c72505739d9397781134c16bee0bd677779a3d42038022051b351286993c783accac0ec07a983e7f4a54c5299e9505d05e4228bc9aa6ce30121025ea8074d10f85c04eef3b63930923fcbb122a5c9dfdbf41a56b47e91b3473c62ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c14d0000006a47304402207045360fdd9c9dd44bd4c2bd82bd845fc50c561dcde952fc9b671909ee180d93022063fa229ea02da39b564876efd4475e2b90f1241d096abf92a03979f75055177501210294a8388e3dae50e5270bbcc5d216e2436640ae94e79f72e11369581dac0ee8ebffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c14e0000006a473044022076ba9aeec5c747ca6d4e110d2874100bb09b6fa79dc2d1a7477186971358bb62022005a30b40fb4dde5c0c32b8009c01ee8381cd709fdc6966a62cc0b5fed44d54de0121026cb924cbcc18026ad8a1863a0ba8e138421ee71d94b1209b3fbe80f3ade72095ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1510000006b483045022100a5df0ab8703d3ee32fa5d9ff2e5bf6532a2c88069cd49e1f8104f7371f93403e02203bc72f5e763406e3d5a2431f46c73c24b7b90cae435186e6118dfe07f6a4b0a501210240f55a69fe7db3396c0527e192be91e194ab10fd66540e77edca56cdfba8b1dbffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1520000006b483045022100b47b3f57485805b68b588a71e926150f781934eb03fa03e820d5d6352edfc75b02200460e42c19d41056a5fd609c5be852cd24cfc2e1bbffde82d3ab7074ce32a336012103c3ad46bdb26dc757802c7fcf7ec3ad7c2d80c798371314a0b18cd7116ed8e79cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1540000006b483045022100d43332fddb8939cdc4bcc18f2aaa708a49617b8dee4ac192b02f268731c4028f02207e7136b3611dd6fabdc23da7df847b00427bb4de75b5d7fd78a24b3b0fd42490012103154ae5898aa507a8b85734bc65e55a05f8ef7403b5eb930d2b10c6baaeae5bfaffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1560000006a47304402205cb847fea5f862fbaa3eb029fb66a7c2169f034a7609b43b85d963f8a42c053c02205c5b15ee1ea0a04dc7967a155501701cc4491ccbfab121589b03e3d74ab42da70121033f10a61831dcba9c5a55a79d7e4889a24f71e2bd65f34ce770ec07654fe9c5c6ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16b0000006a4730440220085045b614c0a88a793a23d1896f1cb4ecbe0f99a690a9a5f5265c4d39effaf8022055c98ce76470351a17848cd8c7eddd588b17ac79fff41214de315bde5fa8cab501210254e690cb1831c658615c1eb7619d37baa29261662d643a0346019186edd5e8e8ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16e000000694630430220449129beeb6eb697147309bda4503bcd54cfdf40c16adacf362c0a3982005851021f759ad3a2062961de9039021fe544ae6dfd26fd823558b8aff3c03982cad8bb01210381e1b57ebcdb3b56c0da96dd3f40c4809533f3949d00740175af286551fd39e5ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16f0000006a4730440220791daab85a89c37bdfc66800cc0aa8f301d1d0e074efeb22d953db7ea4d6e051022064a53999ddcf17140eaeca5b6281e871d35158c51634b5705d551ae128669d6f0121037229e6a626c5643a8775db71891525f555ef31dd5e17faa2f1f359875c25dfdcffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1700000006a47304402206f903ddde3fd2229d0bd46da2e93d081c92496530bc797934a89365f716940fa02205ee7f7789a5756f15382f0ab6ad5930c429205e81d27be3662dd4d478f6f97ab01210257ef3ad11cdfdd5206252442bcb4a3c8bb6aa5c333e9292d28a6b08acd627c68ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c17e0000006b4830450221008e99d744660ae4ee80d61f3e289b6152717ae18c186741fbb5871d6e8c602ba102200a49efaf0264d612cb280d3228005ca4226c693c8651dd82ccc99353c6f56ca101210275f3d5d97e27c20ec966c8b3915802dab7d9fcf9aac1054de04eb4349ee23800ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1840000006b483045022100d0424db7363d25d79d94b39ad46932e85c59db92bc7d69bccc54bce100270ad302200a56e8aadba19c1d826cbe04f5ab6ba9936b276fb1940f934bf6d0123d4c1142012103bdfbdfea12ccbf3f46026490bf9ae6d6f179e08523edef0c65a2842366f83b9fffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1870000006a473044022057a783d20b70cdb2cbfbb567da26ffa725bc127994b964bcc7882a454c5f222002201834c10a2b8673457dba323f96fc489acf754ae9e7870ba4b3a968fabc617cc7012102de203808d960a70c9c03fb19b8b454f5b39bf2db7e539b329aee1a224dc10c30ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1a20000006b483045022100eeb03c49d146f9562a3e1b71ddb5a3e67a623ee426f9388ca3c48f9af64edaef022078c5670fcdb07157ccfcbe96a9c3cb8a5716bd6041ad2d882b4e2c8d994199f201210244b5dc8420f26953076388f782a00cd4a5e7f8459fbe010e07e46209e2e16d46ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1a80000006a473044022052b259d0a727b435292de04e0e5e3934e220ff0795e6431223eaf2bb4a029e8102203bd8f0965fcdeaf61d1eb0cb5dd8b915baa564aaf8f6123c0872e87d024f1ce7012103ad0ea873e25c5df4784476028a1b9712b488fd43f3d1328a78b6087251b148d8ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1ac0000006b483045022100a39e46811b1e3155a7591191ffef7a1f71119767564d3da0bc86fd2bdfd89d9d02202704758039b9b8b44882cda103d04d81fcda07166844bab9c155609351c1209a012102383c6cf9f6f16dcb47445d984395f04b1a41ddd42cc66d0aa1b40b2a685d43b2ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1b30000006b483045022100c445164a1e32975beba08880f10f3579b3f43a1f4a994a183a2ba517de03d2e502206480f1c5a629ed1172d3f830f1dd2e99e501b6bc422bc0405b45d6f7b812a73d012102da7da607a3d5689f3b548b84158cd7d01b6a3d06ce46e21d30a89f2ae3b38d3bffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1bc0000006a47304402205584beb0b3396b2e500d9b1b36a4a4a70d870a047629eeef9155d4701423a94f0220252ebec6fedab9c85a52b26bdab1d555fcbca9cb964366ae03cd717013f3abe2012103a841b267fbd3eaa91bd3cfafb1e825bc3ce407aaec3dcf141c3a7f3ba73b85a0ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1c10000006b483045022100d28f4547be7d4bf9a542f02215689ecf6ccc92819416828f15ddbdff6c8bd44302205a3c58569507c10eeeb772411d5ae70235b94e2a969ca3e44aaf395d7d8740c8012102f8c0685818898b45bab049f8a425553ca599649058bcf93f4bd9be88e2f70d68ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1d60000006a47304402200814800f4e931a46e5992ffd6b8bfef5c801862287d1c41c4ad4dc2723fccf3a022005744606cb083b5af3b10f85367bb5014ccf9d9054eaca427c8c26bfb4afe90f012103be185088cee0178a35d7d9341fc759408cf97ac2735bb05fcfbf6884b1548e68ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1da0000006b483045022100ab5de2d01a7e345ff79eb6000b25e017a690b98a5b5267514c594fcaeeeeeb9c02203532baac3b2a93cb6aae13c9d2da6434a442d21d4b61f003b7fee0f2aec7a17b012102ab574812cea5df5f4ccb2bc7cb6a2ee3ff9188213cb664db475b749cb0991283ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1e60000006b483045022100f260eedda49b27f03f7e49f255936eadaa8207554f9c461adeffabffaf71b146022072a0cf027cd2466e63475dff2f454d7181656c1710cde87b4f713164b1902f1b012103c98ba4248f4a629155630c476814dbe4d51eb5670e728c8e4325a114951b9feaffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1ef0000006a4730440220713b7a56314d8528f41d7d1b2587335f5fea69b8713eb3748de7fa4763d1333c022072c3974b293ea92f9a95a5d8eeeea5053c40fb54d681398a4b4dd924d5a75f4201210290297b638a0a815a09ee946947567946ce8d40ad4ca4b814e42d4b86d64e5161ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1f30000006a473044022023848ce5ec8185a7399c8c1790dfa15adf617af23c207091498faad353516b3202200d0bba502ce4253f7bca5fce44ff1791c068da0fffaf756b907b3be50acef16c01210201a71c6842cbee9ff2e7c0aecf1042ce5d30103f4f73d8ef35447ba56fa78430ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1f60000006a47304402204f70057f1b142bb52097127782cbdde144c00045650e7280d906ab93d881a41c02200fa1f37648d9df168d28872618908a7fa4dc39de7745d80beb5e9386357d8c670121034e7dc9404e4663f804876b10ac0a7c67d5e5bf69042c155fe64c65ee255b0c37ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1fc0000006a47304402204d849d3f2a32b257f606b713fd655c409d7d504a2d91c724ebc30e55c564d7560220292d49489ccc115c1109e450930e0e9d98219ad631796a81fa6eec6dea8048c30121025830212d902083c9426c82faf3d87a2795420890c03e98190b091ea2f86cad59ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1fd0000006a47304402204c46627c53cf4ce553e27fd12b569a991a6358eb202ff9b13a8e12bbc5959d8002203e5b608b05451f9131de70dbac159f516c7510eadbcce56a57a2bc23d1d7065d012103b86eec845d655828ce9dc6238aa912c4c66567cb1ff8e301fbcf5c5cac889ae6ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1fe0000006b4830450221008d10e50561f19f1b2bc7aafaac9111a6dd707efd8a092a1dae9118e45b743a0a02206ab8a588881cd446df171c8ec2c0d4bd14c0d670750928a0c2a64164260bcc350121027375441fa3ede1ac7a84778934aa509bd445fefb207609420c34af415c016ff6ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1010100006b483045022100d4235e80da9c8c59f0ed10c80c5dda053cbd230f99a663b5ef845ad26399feef02200e11adee8d75ed0e9263eb234ee8ad4ae3258e1b4c0cd3c42e5b109ab2dd6507012102df66553dd06d7331d250d82c7516d0462bc7f6c0a0a5e96351eb7d3d5decab21ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c10a0100006a47304402200f1e1e7afd45f2332be4846d7324ff84781ad833946eab93f79c0cb79d5c59c8022046308a18e592ec3b42d72df8fe352e697e2aa22c3b90300f5b6c174144d89f61012102f3018b097fb866dae7a8c5489421ac92bb977be9c0b3a5f165c423859b0984bbffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c10c0100006b483045022100d7e544f3805719ffcfad7e4a7a4a1b54fee6795bf1d26b0b65e8d0972e53fdd5022005cd9336bfe305cdf92dd5a2eae29b61b7779e6ec100073d08c67691d938b85c012102a48b5b01a33b59a2a3eadf23d108ce1ca951ab4d5bc81c4e87c7284f70e28e48ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c10e0100006b483045022100861da85671b0c175dfd9ca8597c08462647053db7d43066c26693d87ff63f36c02202ceef07bd6e8b24ede21d7b9323716599fc96061d898dbc8cebae698296f2ca8012102b944e9570267305e26e166d0e782432bc5145c60d9b399eacff1b1b05497ace5ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c10f0100006b483045022100921cca5c7093879de45e06394e868176eeb82f82d50ba9ec1e0033fd27b5fd3802204720d7118edb84192f86fadde7812f371ca65d655a265ce575d27d2dbb7c9861012102178c8a3273c867bd874609bfbe96839bee20545eaaf07e9ff89777d2817f1cebffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1110100006b483045022100e7e7b5053768a8db37c2866c77f41eaca267ea91a35d1d8d8c9fab9fb80d5ff90220319f65acc061f2cbb4e7c003af9c635dfbc5031d91972768a5ae8f2027e8cafd01210395fda29e25b649eb29f8ff2c5553a494ef95093cf746a8b229826a1de3361daaffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1120100006b483045022100a56ed9e4f0c3851ab1070bed261761126800179d8360b555225fa38fb900842902204bd095ebe85b2d12ca4dea2e94394d1064b251b884adb2d0c6858333b64146500121028e1b5a54a6a54245bf21f0848d672d3f688d11e9328f944365b146e38efb4bffffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1130100006b483045022100eb5e4152e99af6dd5f42b7f6ac22882748f024263d4f391ec4ff6ad577c3443002207a4a57b7eb8537aa75e342fc2f5e43c2ed943e188188b299132aa50026a4e06a0121023ede75e5bc13b2103eb0d3c8c7dfb60b6c7b810f49ce005a55ae4b7d0eb1f658ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1140100006b483045022100c36c26937afcfae4f40a0ad928712715d43582799bf6b8bcd6594e20b34802f7022077b83f2ce8440dd7df61632615d66af4c8dcd422e2f122ab98a01878124fa5f6012102c059b935facfa01366100691c91328659bbdb885e3c802467cacd44e92bea4cdffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1190100006a47304402200bb4881eee4fc55e254fad94c67442dc8fc2f421acabb80d0e86ab6c3a74309c022060c1e4e4a141ee3133736165fcffb50413866713a1d4bf01fbabf3f0a5f67cbb0121036b80696a61486e2fe3bdfea251522ff9e66eb66a4c5162fdf60ac6c833ea90d4ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c11a0100006a47304402205e54387f30877b358fbfcea2d0eec5af1d4e573d81a072795c0a085ef72930d1022078717ec3adb219af592d17d981ac0f4d2c055f8bd4b32c22ad951a2e19c2e8b40121035619b45c3b340326aef6829d231c44907d48f1e9a9c79f2ad67bfbac67391755ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c11b0100006b483045022100e5f0dd15f2da589e52c26cb96455269d4d3d7317e0f4ac5106b2f2f9965ad54e02205336d9686d2f1ad47c7e712e24b064e365bb977eed6d68ed8d87e79fcc288aa4012103a1cdaec2cb6677a444b56fb6f4c7dfdad2f16e0e83da9d08bc0ab837d44ca694ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c11c0100006a473044022014669f8b19daabe1e9c6e7c97dd3f9e8e1260b45e543110e229c4ac2df7d0a3202206b44929e72e8097388c38a154fbbf47f213838c446b3cefb6d1ad9990744f7940121030d170f347bc5ed046ce73ccf1a60d017ae7cee5f7254104bac52dd0de19ad91dffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1240100006a473044022011cf1d95fce0cae6d692cbe96527e73e78295edc21ac05a80d1140cb6a8bef2602204385a2586735d35bcca6438afff35e48ceb9489be857e342452097e098928754012102cd0581d13ebd3d022a63c8d86140e52bccf1b454f824ab7fefdfd6a338c0a4a7ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1250100006b483045022100a0429b5e1577cd8e80a7aeea28317e348175723bc32440e8e104eb4adcae9c4702200edc7c39f7747dbe44ed29838bf25bcdc7e8e2826346cb55d56db8e90c54594e0121020d934c98a189f626654c773dcb53de3de5690b48f0fc64ad92b25f8d08e9015fffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1260100006a47304402204023db73553e1cb230d304ce893f408c2997b08767bfc5b831fd0138c499c4e0022078522c39530867bc8d7d53927b0a37107d36664f023781be79ad28880e8e95f2012102812793e164e39f5631e622575caaa9cac7bf43737f7f95302280566b8c7f31aeffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1270100006b4830450221008d55f32d80e593826162f59cc290e38d98268879b5eafeeefd78cc177b1a89a002203ed550ba71a006389458fd12a7f440aff31df0b388ee00ca27c04ed436ecb6af012103a52b0909c010f6df76c29f4e53ce078884d7a0edb027d3a680cf1ff2106ec18affffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1280100006a473044022068adbbcbc81e989b5684ae88f758d2fd17a2b0c58781fe956b2aea3b232284660220561d1725741ba4ace25906d21e203111aeb31c79dcdcacddc7460d2f588f71bf012102dbd4392d0aa21a4ed840662efba31af122eb1469ae9a02c4c2f83a5c8095a7f3ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1290100006a47304402207a0c11df27f422bfed97dd1d245057402708f6155697702296ff55ea31a2d757022069172e61307badaa12e341d224a867e3319e4aa6d9760a1f588df7a5241a68ec01210360e66e446a61398bc9232016e6a2f34e5c26f362479398702acd988ce66807f1ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1320100006a473044022040dcde34453812617c17211b377be1487a67ab5bbe791187e76ac55784c26aea02204c9b2fdd516436285524e7c1e5e36913ada279d16bc3d22496fbf16ae37e4e4c012103771ab78fab9c924f56d9bed1f4567ca8eaa7b84958575f7724d1b8949ceb2d36ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1330100006a473044022016d9389cfac10f2fa72ea449609909ce7c094f89e72c96dfdf0d71ece091e5f8022039bcb2c5231034c3feebf78b79297ee505a484722ebbb1293a8c1a41fb5689680121029c3a25dc611d682d85bd00a5104a920092865e1ff4ad3bd2de36b4832ac374cfffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1340100006a473044022045f4e7457cce177cdf5b6113647c1c9eb16ec6a5a7ef947b44f71695d5c510ff02206ca62fe434a06e469ec392ccaf68ca333fafbf1f0c9de14269e76487eae80f5a012102c686a2956d0d18adeb6af37438ef2392a7e1b0c070d253212d333013500c408affffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1360100006a4730440220793d34fff42b616bf53f9ff8bb8d27bb69598a8964b2312d5c6f878be539e33302201baf5b3ed160ef3879825e9ad2bbe39cd2620c363a685a2ccbe4b213368819520121039c596113a41cfd570dcd458350e7370af3986daa6e3953a1b5f6c4d9bd3114abffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c15c0100006a47304402207e52360bfeaae3173266e7a808c5e7ca770a5cf3f2b25b549f652a061344a10202201d44b1a75a19743a51fe49ac0f5ff822b7b97f05e5940d129e41b04d9eb12ddb0121021d6d6d74787f56298827ba2de6b362ed728437b43636605185bd2c7d38b7a2c6ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c15d0100006a47304402207c0b6d0f79c00ae00ca763c5b3bcfb8f0f4dada2f28f12c19a23b56459f6987d022044d97882c3af733e68a17575c617850c1cc6ec93ad703140f84dabc99b477c25012103b85351a7b2041a50095248f463f53e82bf504063491099fdc3b0c9a83190bf8fffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c15f0100006a47304402200c238b48948ae5254c72bbc5f9980d6f78c6411c63a952f783f871070c4ebdb502204590781da41c8207df5689360644253908edb9169cb052692df4ccd9cc1cf46501210209af668e09809496c4acee142dece9a37ad00839805a6769376a18f60a3a8bb3ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1640100006b483045022100c698c263e1b40b5bbbed6f3a619af92ccc9bd3af5a39a1b8cdd148f2429ceef202203a37aa84af3a5c0dc0e0b583f111ef78ad3c37ab80371ba27edfd4a848bf57c40121030df38f26078d2072ae8c53866fcfce2398fbb3d1b39ae3ad0f79eb77de9facadffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1660100006a47304402202fb63395b7e996c618015cae5a1971cef6614b73e9f3a321f9daa727c6a1a95102203c1eb0ab003436e3b4d7c9a959d6d6affa5dc6440ed7871607a2e14e7e5bcdd30121022bccd2c3fe9a40ab0c9e6ae7cd4555920281bce457120b58a62f237a770f35a0ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1680100006a4730440220326370ca79173884eaab15f5cc9254b673c6c598260668aee5cc34cc1f5f4c9d02205a2abf73b8f401be110719a34c4f4d8244896f1919e8b030cd7aa11f23debfbf012103563639e27c2df8e945bfac8bed17d2f0320ead623aa358c251ac00f83e833dc7ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16b0100006b483045022100d68f87f24772d287d9daa82728a092c0ba0f3eacc7e4e5a839a242e619136abf02203434f2aabe1b6335448342662bc4a4ad8575bba36e6dcbd54edc0d01b13d7297012103e9ae7c902f748221a9bc6dc13dd3894c4160c7d821e6c7acce0560e92cff231affffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16d0100006b483045022100d186b892201252aae37849971a29980d1f7d91bccac00ffe561ed836272738ea022070629d0257acb9d21d44f109bea479188f22ece0f3f2da617f41145cf411b556012103c357a607dfbed1b44707ac9abbc0b67b515a3369a10748dc3c5164336a4072ceffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16e0100006b483045022100a301dd8f780545ba68d7615a447210482dc64eb920d88c02a534e2bf267d12a00220166f0418ede111363814c147cece1681ab21973b7e4bb6511f7ec3d4f7c87b540121030b8d0dce74c5e9ef271e8e1882729ee0a272d0daeca7fade9c47413a30b6861fffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16f0100006a473044022076e277afdf7cbd3af104116935edfcac34ccb56057f83bfbe9fd053f57f4ea4902200810a644d1f0f06eebf67723b62f0bea2e80469057497e5b68153acdcf5c0779012102207ef56f0c83b6d2fdf865cfc9c8398a0e91d5ed6fbfdf5fb8607f1050fadc44ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1710100006b483045022100f9321e8653cdb00782ce4786c743f1bd77fcc3565d4776c10baccd49dfaca93f022072a1f854351fa4e4cfdb77f655172b1205a862228d39c00b027c9a7e93c7a48e012103561b995e7c0bef58f05c78d6debf651b47596fa8642791cb87256d46bdd15a55ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1720100006a47304402200feee05210cfc4dbc0f3bd7f7dd7b1c2c635c2fdbc372d2f36ae9ea266bf216302201e35c025dc26f0e112214921bfc82696767f7d97e82067ca288c41ffe4cbdb40012102e79e860ef42ad0f30a2f347b4c06dbe0d82a36ce559fa65044a7fc033372105dffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1740100006a4730440220232103647230a1e5dcae5fe4ba1abf81b2fdd4b50cf078c8468e2df6f4d24a1202207db933d6441b0e2f4db35af53bc59e1215b35b7c14706d8c7d2f4b4627ff8db5012103cc1cb2bfb5a8610d0b253b93f9bd07c2bde18e387b149f2947b753b36566c511ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1750100006b483045022100c8dcaa349c76ad8e9ae0a80851938dc5e866d1a8bce653661e8aaf6fddc83df9022032e73f147b77fe6b9f901809f8090c37be4cfab701eb911ce20dec54864b5030012102001c8add5f438e5bb88384cb5a0636eef778de49c72ebebb820d60ec450c72f3ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1760100006a473044022024d5ce69fcd58219ab80ae61331d10339a69b45176c1fd077b9455b6532997920220146cbfb6b989f754b52aa6b3b6863d290dd455aab7d594b9e85d2e33e44735da012103ff89a25c084b465f1c5d854078ceddd639920e4a06b1ac84dc033fee257752c3ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c17c0100006a47304402207487ace7f49476c32429c0819c95eecb2856313240a1fc409146d297899fef4e0220746cb7ce7d8c5773dd3afbb9d8d36b5225b1f3e6a720eab6467cc5069ea5541101210265d1581eb36fb426a10567e42a77c021f054af9baca4bc20cca40c4d7e7a6b72ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1800100006b483045022100860ad1fc2b92937080c9615405b8a11bd3ae2b37b2f5d8c14588e43730c77a1202203c1184bb3a112c107f1f1decb5ff7c2e0ae31c3b591039be61e028932a621b0b012103ec9213e95d4f572091411c9e7451deb5df05c3f2400015c69336d453446edf8cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1950100006a473044022015458b01c46adff34da2313a67069c9c1533608e83a68fb11dc094a413f3bda402205740331af5fae271efd7be64ae0c24e69b6c864900a6c373409b721c598a4eae01210360c328e5bf3421491d2bbcd68819e4646163a526916f74ebd0675673fe82454cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1a00100006b483045022100c3c6e55c4025f8338c30ad6581250e41ed82b4ed1124e26802daf36e280fa7f602201e999e26e3dd3ac67227dc2a26f6c2ed90188b84039e282ce649d366e5b61a36012102de3bb7d8edcc2e5cba3d9850c5691d91a322e2888fb6f111351420320501c7b2ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1ad0100006a47304402204b720ed603fbb7fb0a8dc371f9170356fef1161bdbe5dc6ae889edc00ea65b5a02207b2423b8c0f226449abbaabb86bfccef1d28cfb2074e17e876cfa063997ff1130121035983a0bb2f97c118c75867afa075c44cbc81f8b448c00243a6297724e75dd793ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1b80100006b483045022100db246ef1d851733fe1f567357304164f1014724f2c8e485da0e10086774ef09702205e5866f0b3e5a82e7d32dc5358f29934cd0ac7a07e7353f3cfe0d1a8db45ed54012103783e97757e436c200c24647d6dfc414bdd4d6f859562c5fbf3d57fea7e24dcf8ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1ba0100006a473044022021389dbd672f0f893d0b7bc4559357fd3d11e46ed0db9b38dab8d005e82b994d02202fe086ac26d12e6842982f9653553187b9af48d6b1db3f4efb38d9c331613a86012103ba0be2c463e5b5d894f771f899811862b765d09c735fc16eadd9c2ef97030851ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1bc0100006b483045022100d55f97b555c5064e6e8902e8e1802b918f6755ac0909dde8ec2ebb81992cd13702204aada200f3bacdcc800609e33d4b4164951da0f463e6ba0c11797612ba83eef50121039a467d42aa858bb84b7115483e4aaa32a033b7bbc04969b1b283fe1eeda525afffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1bf0100006b483045022100da28401d08c132142b96a330b96aa9dc2b4d994b3276d4acea2d5ce645d2618502200561adf94be34b23fe59363858752e0dd0548118f1b0e20bcc6509b0f9482cbc012103bda66744af00c2926e8a9fe7d4e4b10b44978e31b6c1c621a2dd2192ea1cdc89ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1d00100006a473044022057ea948e707dea3d86eaf12eefe16bad4923448478389b44fae09ef0988bd1ad022037a2a9d83f6cdf079fe31291f13085d5a209aceff48146f29c277600a63ed076012102ed801ac57a9715ea9d33bae10cf77e955e97d3eb8ec3e0610b2e1618aea49743ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1d20100006b483045022100c8d05c1ec13543217056e91f658d2d69ef2261e1a96346bdddc618dfa9bf634b022020022c382c6f2dbf51aa3249d03b406742b115c31a833ea7822977cfaca0e7f20121035c7cc3e6bfce86040d9133c267210955e6b85537eacd0634e5f10aabc1bdd017ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1dc0100006b4830450221008c42775f4e2ce3c3394c33030f3a25a64888776bd28a56bab8773e283da24b0e0220272b6ae63f1010080142c9282a617ef00276194859444d2db717f8bf0c8e2ff501210367285ed00b1ea1c032beb9515c6ccde95b57965c483871b83cccdb58c20857fdffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1dd0100006b483045022100849109c514040b23c75d81bed0585d316c0a2dde3d8926621792a3f6a6a59f3f022074cfb8eb92a2517d026c8253351c28d29e95350a886361ab3a29fe7e96139bd1012103966efefa3922def76bcb9c9da9ca320ff071df2253a95e554e88f0a43dbd1af5ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1df0100006b483045022100f5ed0d3c85aa5ffd649a8a8087982289e7010357789e945428c6c4b269e4654e02201260a7eb8fc1b02c6de2b144463927afb4e3c87fc0c65a6d43d1625e40895a2c01210281d386c40c59fb1ef1881f1b84bcc92427b8536ab222c52235bae910c9285dacffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1e40100006a473044022056b14b482e057b70b89003edadc9fc9a9ccb4e89017fc42d2e4ed1926902a84002203b1be778de305d62f03bf5b22bf80ca30fe66bd2073f15998ee3cd3ceab4cceb012102ff192b32ceb6ca7c147da931b3886549e18ec1d47a482ce5375235458d479a5effffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1e50100006a4730440220243de4b91d94bebac601f72fb64a89b5e541d628a5944817624ec04905d3098e02207ff99fe167804fbcaab1d56628b72faaa2c7e3e1fe0dd05f9a01c1d368a066f9012102a5c4276fb5e2b3218fc67f5c3fd87bfc9fc75f12bc0e8fb6ad1c50a6df0009eaffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1eb0100006a4730440220502bbae45580b9a5166cb4a1be1a6d7c02af0579d8ae74d46894c6a13d191aa202200b3ac854054a1f17414eac57a710d19d5a93d41d0e9bd12584c7374d077fc2b70121024f467b75f6c16df2f8239406df5646651b6be9e9f50b504b60ae9a1209a1e993ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1ee0100006b483045022100a55afab22a2d9931970e0cc320e3ab9cee62f3ca47a8adc4923b0ee9b144f01802204182cf9c8272e6a9fe3e58c4632f75a37fc3e82bea741003fa096aa3183182080121023a186f3c944d8349cd144d419f2f043eebcf260f5cf06557d87a11b731c0e76cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1f20100006b483045022100f9e3d0bbc090c85f2510f346b6dbd76af2f9be724768c3854b7f0ee9c1fa4eb102202c9a6abe8581e308dcaf1a714c93984fa94e48f85ba2149a17f13087abd725c8012102fda3f6006fffb7975d967c5b473fa64b712ae2b469095b3cfec51eb70d665204ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1f30100006b4830450221008a53b245e639f2d6aef246709156a30f546d9e3a91e9359c5d4cb9f3eb80b3c202205f2db7f30bb163243825281a0f57340f0b768c029a44787c8ca83a9b26bd01200121035d9ce97541161d79ba3346e6c755cef41cc110b2fa78a658c349b3ba8e7ddbe1ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1f80100006b483045022100c53db2e6181df4b401c79e3ccb97358121dfaea7fbde135db1e1c15f7649ba6b0220753f38bb138baaa5ae2d8bba4a5d36fd55120b17a2471694969b1dded9e61b730121028ac506e0e8d1a686e602f6ab99fcf9db7d91f10d9f677f67fd5e3f2f273b167fffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1030200006b483045022100be20c7168865b2b8c53f452b112643415963ec2628b3115c26e94310d6ffdd2d02204165e6f64532a35a77a8b712541fbe4e32b240d815a9782806a994c5dc195d8f012103fdd7c5ed26ba61578966971b16eba7a3864044610a345b54bde3987184fb2ca8ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1190200006b483045022100e4a31fd6b7622848ff706a3c02a6f1e06433e917887638e904b6dc55f04e617102206e513e2af1634ff2c959df22cf622b403b9379f055452ef8a36070523370515f012103628f06885c53e23474d28cd2610288270d191923617b0856e9e0bff0a807aeb8ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c11b0200006a47304402201db8da5f70a05b7daf6bcfbd742cce7b6bedc1bfbf9c467d4ee220f2ba3fd07502207b1926a925c0f5d60e7a0d1bab7a0cd8f3dab5108bd50e7114740b8079203dfc012102e55506f1a590c2ab666ac1ee6a4867d29d5d5e51df33272e78d5e8cc0c805373ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1270200006a47304402207231aa4d05e65edf095e4c5f4ff545e9a083a549b98d217b40a552ee90f445e302205a6a26ecb8934140f5068074b21965616203e0791f2c784924c1ddef19a9da6201210221c7ab81dbf119a938f75adab81d255c60bde3836b7c7707ee9a7bdd301e80b5ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1280200006a4730440220789b372f1ad5a63e97ffc30cff76d758c9a05961bc4e4260d85c66642b6ba1bb02205c66855819f78af7257b4772e9c7d27f344dfda87982877bfbee590b35c3aa84012103c08c0d0d0e4715e37f3f434e06fc3634a820e9dbb7db953efa1fa2b707ddd61bffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1290200006b483045022100c0be199a0cdb2dbc6f9e52f1a52068adb7565332af7447f9fc22417a4662a76b022019ce6043d0b72e429e3d7ab48416d1e3e1505c83cb44f8fde10bf21621ac43e2012102407e47a6ad48d7be1d7769a0825e1c80da8c6e138d4a3244d02b82c86b134b73ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c12b0200006b4830450221009d74913022239d6f4b700d43a3c2098075991168de7c69240fc5db51f82b550802200223e44184e664e6258a3b25e81897a4c71da2cedf85c12e4c4c6b554407c911012102acbb90364ef941aa87dd5364e8bb3f15a88203c1ab377566825fd747e0aa6cacffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c12d0200006b483045022100ab0103e735f39f93dd8d1cd844ee98b4eaa17b29dcac2d968904074c3df5ce2902200b8290665fa178e26f057582c17032b1aee584f2f4288bf41fabfd6b78d37208012103235173889e3df92f2bf3cf88b52fbadd58211cb3df76b9cb5f5f7046f2500301ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c12e0200006b4830450221009fd5ae1aa33964e5f9cb4179373a3937b712f2645c8cc4ecc6e9c867c5898754022058ea415c52cb550abf515d05a8edeb09ddc6e5593093d35e8748f19db4f11f4f012103c017e13a66a2fbb5773eb8c158fd91d87d51398ee06058d52874555e24d90439ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1340200006a47304402205215e412351ae943b95161e9a9b2c6f471abf7b1002604d4bc6455f64500b4d002202629325596d78f14279277392e3d02f8ea195f5efe7aece2c4d1c728aec765dc0121029d46352f694824dfcc800fb6781602a527d7ed003be2d6b1d64c8e6eef50df7affffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1360200006a47304402200256fcb3d766d4b74ae66b46710e321b4778f9532823b778863201f396e6688a0220049967c904dde6af5f38d659741f524680a89d164e1fd5a3beab580dcbf4507f012102765b7ef38aa3489505eb3927828dd8923aa0525d117a7ae20b1f59f96df0c7b1ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c15f0200006b483045022100866edb83dcff43cbe923d2d13c3dc4d320e0fd02c2277d44c0b3a99f8fd07527022031620880000c14c9450f92165f9a472c722afc97b4a2f248a2ef6abffdfc762301210261191a870744f836b3571a815a1b1ea13a7aa0ffacbf3d45943986ebe3768b8dffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1610200006a473044022051caaa02be3eaec5c4ddcf798a87925382d30f419b78fe988b6fb2993872e51802203ec0755e03ae821074c1b3297a0bdb8b5b1de6169e165b1c1d11714127e107a6012102e4562907fccfcf4b9a743e4715d5a9cc1ba203054c63a5f4d0ac660870d35ebfffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16d0200006b483045022100c67adb798d345237fc08b938c1a1ccc489846dfad538d0ad39981eed8504a3cb02202ea640592068c08ef55fe09b4f1cfd5764104b7cfa5bcf690cf5e1acd6b1d36f01210362ed038dbba6b24df044dd51557462b845547273183bbe3aa6c67c0cabf0ea4cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c16f0200006a4730440220767d431ce4646cb5e6ba4c29b3bd4a8d07f878272bd48b0600ec40c9a08e9d9f022041d543294659ed9e5e5a74c9e7745dacbe956aff0cfc0de9fdffa13e36dffc8a012103745777d16ff9d42b2d09eec3d5132c7a719764f1ffc215769ab6c7beab05181cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1700200006a47304402202e9b385df79d95668e1517024507f5967ce07dfddf420f63b7c5fa2960435e3302204898f0c107da783bedd2b3a2cd9212beac7ccbd14a5453156309481516154c3b012102b642f0057b3d76784e58d22088b8845f67e27eea8d9ca92b8f4fcfb23a0b16a1ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c17a0200006b483045022100f6d23136a92adc72a106b6524e80665b09af34d7d1d850bc4accd8be5963cea6022033bec0a03be7c42cea86bef491b3ae3466228e9cddb8e8567d2fa5de4306e7ee0121030f3468386c873a408e11fcc45534d6f8151b883f036f5f377eb79ad8b51bce56ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c17f0200006b48304502210083d075cde413642af7280886b056445615f1936da2e38b42abd9e2186710683802205981df2dad0940540f773c5e51b66a77cf740cb33d33f9356e12832769c3781301210229b48fcff732b7873b310126a69d54fae60ce316874d6c762ccc9e085f68841cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1810200006a47304402201bdf3f5ea0b4b7e6be3c6bc38f2e7ebe431426a1bdc1da4c0100dcfffe57c09f02204c585cb0d2588985ec1e82b7a79c93cbf1c86a8f28c054d841fc16c2c05efd75012102424ba81a01cb0b447a1458c5dbe7fc113adbe6ca5c53052ebd28752249493d8cffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1840200006a47304402206d6746a56ceb9d8cc5b36ab610cf7d58db9529da6f5f3eeec2665cbfd40b6d280220702182d024b8514c6c3a618d10be2bca52a4d637194eecaa42a9843d81552a3e012102d09c0636e4ef75d55f30e3ce8bfd0e15b8dad9f4125fc896269d8d94ccdcccbaffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c18e0200006a473044022006d85918f570198f86ed13374e5332e4816e183676eaf31d0a46d945b301f1b1022064d13f7021af415b835b7a70ab24af05fe1f1e4e168327edd245cfd154ea1c5e012102368b25c7524d1dcc32833d493cadd47cb44078f321fbbf0ce3ec4695c1f60f88ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1950200006a4730440220267fbaf726513478d1f5146da71c7c345a0aa15aa0350c06ddcfa8d68069cf490220453f6dbc8a0ffc78073a40e8db5fa1df6203824742c26ed0b8c5a8f18ce34b1c012102e177e59930592cf7f25ed9e65932249f4395e9088234a938aa9e8dd8fea737bbffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1b20200006a473044022067e3a5759cb8b59d734d79858039b62ac12b62965f3aff64a54852e43e17b0900220248436c0008e12193254a62a3108842f95972243dfce14bd4d95e5c602034889012102fef18983aeb87b4361a6d0df281dd301db5976bb29cdfa0f6eed403be5921941ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1bb0200006a47304402200dcafb80d9ffffefd5f73db3184d4d7fe09f29e89e04b081fe1d5eacfaf98d3002206868357ee66de526ae9572fbbfd564fba9b276da79b1011d5bf6ba5c082d16ae01210329030722d978a8f6da9f3f3d1e27ac87fd71ae0526ce122193dd184987e9c843ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1bd0200006b483045022100e2b32957e6c07bdbb52999069e58b939e734d0e3f44515366a3175249c015b57022014efe65a78004edb3782490336ec554085b555af814572df03adac484649e331012102373e53ce9f2bf9d2bf03a673096e5d5765d0f3ba47dbe2c6c47e89b053945b7dffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1bf0200006b483045022100fd07cede26bcc15221644053fbe40325ab099366c531d582eda989084bbaeea40220688f6776208625084293987941d68271bdfbe207aa4dba732733326b08a93c50012102bdca2c5703625f8a3a800d14cabb4b622dfe766e29cbc4e03f3e2faa227e4ceeffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1d00200006b483045022100be9b2ec070fe903959a0cbfd1c1ef0de5768b01989c7aaa9821a6ef5e1e79eea022026ec2fc6b51b0b189434f619223d07e3b2ffc6eaa4fec751fefafa7d5a85e4cc01210217585e9d7a441b1a5cf6227bdb4249ab19d692920b3c400defbaf04ba3971106ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1d20200006b483045022100a2038e751e69fa057e5ec349af69363bc0070c6ddd6eae299a29f8f64cd3c0670220475bb60545bd230940b2345b073695ea814f83d740a4f071260537fc31532075012102fcd18fe4278faafebc53b6da5660745d2f018def26e4ac1c23f055893256eb61ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1d40200006b483045022100d7c7a4703f594d4bb4ef266cf109b61078e58c5f52e33866b21175d4e389ab3102201aa1d2c08f5e71e299368f18240088a8a1af242bc1ae2551bad79e856aa35a7b0121038347dc7409a829d9b7583c54132795c2fa4450bb21f109a14f523dcd54f316ceffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1d90200006a473044022022b9efad0130159aeeb0aaaf6be556bbca5c3b9e953add448207cd344856743c02207ab80178349832cb98a5776ceeb85f2556ebffd95ab98abba379901e247926e80121026596df661f5d6ce02be34806c12efbd77636e7c0e9e8685eec7e9162adf6ce7effffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1da0200006a47304402205dc46a72acd6f4f72332b005cb859215b5ab31bc70f9b2273032db774321904a02202a6c9424fe921a84f76d89ccbc16103c5ca624058f9592e4ac89894d3b3eb20b012102c3d8316da0c1250d9d9f22f225d72021344ba9ea3aef22dc9cb89d8d326baab4ffffffffe8205279b473e7aa25b6e1af1d08ff7d4e374edb9b51523bb3f2de8c6a0909c1de0200006a4730440220544c2abfb1f99e93284482bebfd3896b433636853f3ed8268f0f150f6efa287f0220026e69e3111ae67bb6fe6c7ae47670e1c0c7a065a57e13bf48ef8646ec999d310121021813cdd05350838ff535db25d1605c183c7b4141d0ee2ef2bad24671945f35ceffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d340000006b483045022100c651f4093a0619d99e15655d93a8680c552fba5d06a9241aee29398c2d1c3b01022036d1f06847034cd02646e4656c48980e4c1b324a22807ffa1dd874bf2975bfa9012103fab9fe19ad44ff70410f6ae2819cbf9191d026eb2aaa6dabbadbba01a11da13dffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d3a0000006a473044022011b6be5a307d93a09f666cf934bb3a92e1bca61c45814302be50300a576d6247022051d2a7c1bba62b8a79f63b53f32497ab1dea583290e928899869908963a290b30121037229e6a626c5643a8775db71891525f555ef31dd5e17faa2f1f359875c25dfdcffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d400000006a47304402204e3d105dff49e720553a1bf76ccee35012481f65e41b1eff98a48c3b01f0aa6602201055db4eed7d203640ceab2589994188eb8dc5bdfdaa3a26d938ade6624c30f1012102b82efa454258adfd0e5ba4b328b2bc5330983f705b793cb181482e79f80e9930ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d4b0000006b483045022100a11e6dc3fc67eccdf62858e8f5106cd281d2f70d592d04ad2078c972d08efb0c022042e0ab45565cfa5a11c3ff4d62f8b6dbc6f4a526d79dcda0fd7bc6f983c41eb10121032c416ccb3b041baef859872db424aa5364116abae839402eb16d2e66d5aca646ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d540000006a473044022020a0b9610bdcbed7c2b0e0dcb59a2de6273493ccd33ac080dee77b7abee881bf022045de7dcfcbccf289835e7b690f75b6285352227cac888ea673c99216db7e8a52012102467d9c0ba7fdcb7c5bcfb88c409da4abd7bb7f0f1392ea537375d308beb726caffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d5a0000006a4730440220214f53a525785f646828a726ab51041b3a19eaca305e27828d6e2b022bbcc3bd022069aba6a28acbb70c6abd5755fc0757e319049c99eaa62e464a423c33e9a9ea17012103125358e37ef869b7d243e2cdde883514386a2ed00e185a976c22b51a377cf1a9ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d5b0000006b48304502210088e40beac5270955ee2ab00d6eff4e06bbb03873eb8b2f3db4c73af0e9b56ca2022063c22e296096c54fe4563df30cbf7024d3bb3af0ca305ca62a45d78a34b5696d012102c51cc9443ac01d5afa5a175d610649191434827fe0275828993a80468c417529ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d600000006b4830450221008c76c753bff610210392a3ece465a6cfd6be7204aac61f5c74541beec233c850022067c4cc522d4bf20de28f176cfb3e058fa0464d4e35ceffb404b52358cfd060e90121028a152ff0467a7e0003e2df129e0f4afe783b8b80b590680550768282d091864dffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d630000006b483045022100f598201a78ae3776c193609007ac99f082450a213b0b4532dda08ba61c8ef73e02204ba4c2fd15fc4e96cb82cfc7d777e9d7962b56a02ddc52b449d50ae7c05c45410121030504f2b3f5ef6f01d3300ebfc4d477b76aad88f96b8e17015c032f17ab4b7941ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d650000006a4730440220508e44f9c0972c721e0e02f8faa6c03598189d4321b8d95e59a08bc396d9ceb202201150e0cdd633ab30c4561c4bb15381a37ea0d1cba72f576d26c000f42be618ac0121031acd4aa6584cc492552970924773f327f01a195a7eaa11ce7403e8bf33deca9dffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d660000006b483045022100c8399cab0f1fba3aa80e1073168fc8625a5eddbe0a6c55fd95617bb7187c3616022014c487e3fc2a34cd239cbaae3cb9a84948a2c429b57c22657b40ce183074dae601210204c81ba289892783af47a530bb90b1ed2a40b5b075ba78c571f4ad7a23ce8789ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d9c0000006a47304402200cef576689285a1002b9ab7708a892f822f42b41cbdf56faef4c7a798e787cfe0220406e0e6d705044b1a8286d47d9d498b7a0f189899dfea712063b22d6b65331eb012102de203808d960a70c9c03fb19b8b454f5b39bf2db7e539b329aee1a224dc10c30ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d9d0000006b483045022100897cbe00d0d33e17f7286e2675dfd331e97558e38973b2d8de294241b28d694b0220516caee45cfa6d4c52cdcd70f8d33369ecffa9dc3bfc591ff41226119fc29c51012103dc6a39ca42438d74f372f2e6c39b78cae0a6a0497ba9b324b37323a429c25693ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04da10000006a473044022050e956aa0b79ef3acf680428387385202191016fe91f8da2a26e8c12c58534de02206451fcfb7a2772e55520314fecf74a4dedb980d2a1a9acff91e3bcc1781ade2e0121022f7408dd4da48b2e180101b9a68bd33fa5f4b50641138539b41910ae7f06ecdfffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04daa0000006b483045022100acfcdb0296ccc93611637ac70d2f901f8f2a7e6bf8e7beafea7b7b3edb5e3c7e02202291b9068ea1ffe02f03165ee164f4644719f93605c14e3793af5dda30403db90121027d238c0007250e50e13826c5ba631692699cbefc723f1af81bf4fdd7c1567c40ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dab0000006a47304402204fc6836bbc8ebf419e42eff8d3e83839b36d5c828653f08ad7cdc44bb26c286b02207e51ba252e7fa8b39d32f82a6d334c05227ebea45277f26893d96d0f76d430ef012103a980c6a7e06e582eb8e3f9e2f74800ca9253a7a019e0ea3c1235e471268fc253ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04db20000006b4830450221009aa80d095b6157670e141401e1e9f66b739f28ac53a559bb47e231e257edfd4502204b77c18f1da1bff0208cac1c31b3c2cd4d36240a702a8cb192f40b63935c298d012102da7ce91a1f9f94c4e4d87e474512777e0b7d702961152176968ea999dbfc3ec3ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04db80000006b483045022100dfed5ff1d938f1c11b82669de0a05813aa618496e2640ae6a298794b347a24fd022042f1e30b9796768af4e8e7a55ebfbc340c9564dfe2d12ecfc3a64215aceb722a012102fda3f6006fffb7975d967c5b473fa64b712ae2b469095b3cfec51eb70d665204ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dba0000006b4830450221009bde25cc62278c354ecb751b0a60da5f6b384af9a26a2d3ccae35f9fe5c875a70220440973ed436bfe8fbc4454c5718d0ad7a8238a1e802c56d065d6c15bd7fb30c9012103bdfbdfea12ccbf3f46026490bf9ae6d6f179e08523edef0c65a2842366f83b9fffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dbe0000006a473044022070fe68a82fd2be9294aa2be9554474dd434491373933037ac84b6c404a4ade7302201c1ed802e861efeaedaa53ebeb6a4303325dd5c096a2aa7e94671740c0f6a080012103bc3019acc5d6c21e5a1f3fe7ffd43a66cbb19bb9522addaf13eef3a229dcbf04ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dbf0000006a47304402205375add2afa177cc967def4c9145187595cbb691737df1ea22b5ddbb1e38428002204bcb401bef7a6a08c59e2fe9de725b7f8fdf11b0e8477551c36ed84a886961bf0121029c3a25dc611d682d85bd00a5104a920092865e1ff4ad3bd2de36b4832ac374cfffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dc00000006a47304402201cbf699e95c22ca00d541b298c30be2908f86947eedaacf3a1bbc2823a0ae275022051c995b5b499c3481812308aa38301825a65309839bd7016803ef79a8484c1f60121033c07e5f15c4d4e4fa886adabdcff18f5160a0229be7f97ea925b10096c867f5cffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dc10000006a4730440220248fca32d208085984d21faeb86a6d8591aee8efdc01f7fbe7c0f38aab8f4ded022011adac25a8a4aadb1cb6ff0d897996f19e3886051a529577dac27473dc25f25b01210275f3d5d97e27c20ec966c8b3915802dab7d9fcf9aac1054de04eb4349ee23800ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dc50000006a473044022001354d0f1440d1b246c9689a8adaa6daadd7d0aa7ab93b99974d344206e706e00220096f649c9de140e47f1ae1825491ed97e7905d8f3cf4683080be95924a12f179012103d402e991d2209db833cc442d2f237c4597c1ec046a1ce786759ae5a25cb540a9ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04deb0000006b483045022100bb822ed9466b3f2e10b5bc5eed6ac5adab802b54e2cd9228c00f24697c92de40022075b2d8e65ef1562b0ee2653706728a0ae977b3171c7ad35f94e89d690fe6e092012103435b160c6b7532d7a6df4b1b32a67f02a42451fc8f2c2535a3350dbe0c889e5cffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04df50000006b483045022100aab73969efd8dcf674232af198dea3c6a7f08321d67e7e4d0d753cd892f7aa4702206821823c64e00720df6efdde856f46091e88d405c3bd8cd56d0593f4de0c09fc01210218cdd60d110982bf9e30a2880577a203d6bce3ac23f49c97db76b14756a8b13effffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dfc0000006a473044022039f7a7216086e4c7b8dc1f812240cf87245c3b3687595ba3ae448cfe9063497c02203a55bb226dbeed02039841f74f393945e1a297b576a1c0183f33b930583c3a6d0121030f5851d0ecf4f3f67f7a7766457647b9a5f576f2f277020707b8b8d7c0f29fabffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04dfe0000006b483045022100e5e44d7bbbafd43ef8cee9d0b93173b847b38061f714866bc70838bffa498d1402205c76cc970c91cbb23437b0c66f34005d8ec7f781dde92bd3c233ac18f0bccabd012103aaebf34aedec0cd42deb2937c160fdcb97bd2a6eb6e6e7b39172e87c8eedfdf6ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d030100006b483045022100fd49010e2bbd1556647ed0c2ea093640625a66e7c735734d1e9c03e86f45e52b0220113b6503de9e447d9a5d49d16c9ef6fbd1e33bca1334a499ebb51d1d3cddefdf0121033324938acd374e47c423e07ce826a7a5bffc6daf37626544b2d06647ec9e757effffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d070100006a473044022022f9d9ced8975da0c22a1d52df023f018f1c38c0f998be2d91059a5d4ec3765e022071572117af11cc8b5ca9e1aea9a3e2b9b4d490055db52f37ed54fdbd0acdf54e01210217f2ab02d668df006ccee4e2d7f1775886f9bc0e05988fa6ada6bec60b76cfdbffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d1a0100006b483045022100d453aaaadad61755a3f489db54da02c61f399a75f18f16c58d9c3154ec7c7e57022028e757bdad5aa8fafb435d3496dffb03e6774ee713a9c879194ba97b98d1f259012103235173889e3df92f2bf3cf88b52fbadd58211cb3df76b9cb5f5f7046f2500301ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d240100006a4730440220064ee21337cf7f595e06a9387ba3817a6b8f01819d83aed4e8200e57b7e469ce02207f91702bcbeec7c438d67f44dd5ff94f7de9a4f7f2298f540795d1f1349b50cc012103661abe9ca452dd39e739dcffe5a57fa01b09cbb16f0c77efb7e83203c60b0357ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d260100006a47304402205bfaa9df8db1f052c1d21b3167be884fb2f800260d0a02cb5cc8363b3e719ac202206a396d3c916cb21efffbb216bc790f12e8c21244bb1e460aec5c4400ab33103c01210265d1581eb36fb426a10567e42a77c021f054af9baca4bc20cca40c4d7e7a6b72ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d340100006b4830450221008f2f454314cd66d6a2e387cbb571dd0772441cf92e94584084684feb897d13cb0220072df0d79cbcc8d28f0f061400b6a00157a0b9fb1d45d1d0fdce2438e15e5913012102207ef56f0c83b6d2fdf865cfc9c8398a0e91d5ed6fbfdf5fb8607f1050fadc44ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d380100006a47304402203e68893a11b612efaf965fcd5ccaff70bb988d5a07cb0ceb307094c5bc914b7702204888ca05a7a3bbde208ba0819c3503cae767ecdde2fe9c4b8930069a179e55db01210290297b638a0a815a09ee946947567946ce8d40ad4ca4b814e42d4b86d64e5161ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d390100006b483045022100ddf9e0b0abe6e8fc21f838018111b4fa9749a5ad3e0261798b3d55730672433702207d95da6999e8ba05a496a9374bada56bd97af55a8f78ebf6e2388a79fc2aa5340121039a467d42aa858bb84b7115483e4aaa32a033b7bbc04969b1b283fe1eeda525afffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d3c0100006a47304402205f3f21bb10e840235baf505e18847d8df04a1c916022d1689ea763be8fd6652e02200f3a1295695a119ce2827f63f1a968218b4312d4e6eb371793dda3f66dc9371a012103bda66744af00c2926e8a9fe7d4e4b10b44978e31b6c1c621a2dd2192ea1cdc89ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d3e0100006b483045022100a72ad4071f5adc2eab9b47dfc6d74f3fcb3e2c7011b7f9b2c1eda0dc50ef28a0022005bfb847bfa5a052300fdda82ca941101534e5c2d8cc2d5ab8cf52a53c5ae34001210362ed038dbba6b24df044dd51557462b845547273183bbe3aa6c67c0cabf0ea4cffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d450100006b483045022100a53fdf11cdab6b2d4bc27b339b64f83d1827327eac22379a1f03aae5c4bb6cb202206fb1826649a8debc88e6281d6ccb3b5204ae1d5ceb24ae52716d5aa6858c46a7012102a48b5b01a33b59a2a3eadf23d108ce1ca951ab4d5bc81c4e87c7284f70e28e48ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d470100006b483045022100ecfc64cf2fcab3e5bb5c98110844795dcf75370193679af93fe05eb5ace40141022027cce8d27fb4a9453dfe4340a45e271281b137e19a7b3b0943e7b12588a3669501210395fda29e25b649eb29f8ff2c5553a494ef95093cf746a8b229826a1de3361daaffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d490100006a4730440220098c7979cbaeb1ca9a6816db7339d0f1a31c34fa1164e92adf388a8c7a4d18a802202670fef7b6c49785e889cd3e5e9043b8e854f876ac3d034b7f00044b793a04b8012102406168425a18fced174a6c8f6b7fc4afd5a145ab318c4b5217578d904fecc423ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d560100006b483045022100a4d52a574aadad59af87ee34f29f0afaa903b86bdd87202cc254fe8f30368ca60220178bc9f0527cf301c665dd6c363627e085f8c28b359c1f57b33b39c5694a205c012102f2a31ffb3afc4e87b31ceab7ea9e6ebb8224fd3d8513a612467493b0ca9f16e5ffffffff011c68ff8b2108ae7807e39103235fe872654d9cab4dc4b4c40248394ad3e04d570100006a4730440220762ddae95bb59e460e1e028debe18f0eacc07c3490631507786f59f28475b7d8022064ca70bab2ebd8554e387092dbcba1b4af516025c73ab6d6690e63a7a3275d4d012102ab2e84067502ae4213ded3ee19b1f8c12f70fa8422079a7899bdee45911e5601ffffffffd22123dbb9e97d97bda1ae9dd0aada7170e584067c2645a1e3470c81dd16bedb110000006a47304402207f6ed6a18010b4372cf9c108128201887c2aa8430d78dc127a5e207b7983c532022037fc6c49baf10acc7d13d244549dc5ef4f0ea147b8103dc427bd81859b04def9012103b1d2977a5cef19d66d6d12e74e069993023e4cd6cd3373e845be0e4c79290ee1ffffffffd22123dbb9e97d97bda1ae9dd0aada7170e584067c2645a1e3470c81dd16bedb2e0000006a47304402205b81e12831deca0232a712a53f69ad51c1a38ce4628df041579e369a5becb1ea022072df22adc97f93c2fdf917e86783b6ffb825b963673c364ece198381fcd3a1ed012103d5ef5cb73c67cde8a2ceccf5d41dcb665955ba4d7ae511e049295ac0c796a4aaffffffffd22123dbb9e97d97bda1ae9dd0aada7170e584067c2645a1e3470c81dd16bedb300000006b48304502210084b5f49374556b9e2182a298e5b49665aca25c49bdfa9f9bb06e40ac424c6e53022034842bef214565d8a5a8867a85b0967981261326a86a58bb0c509ad3d54d65d401210330f147db0faee2e800e1bf2eed4841793d0bd0d5fe692847fd7065af8b0e01fbffffffffd22123dbb9e97d97bda1ae9dd0aada7170e584067c2645a1e3470c81dd16bedb430000006b483045022100c60469f511d1e4666c3d43c010db849fef48977b2e7e72603a394709811d8f8802200ecc8b6cf9fa117040f7408fd60daa3faab6fb751e8a2990b34bf84307d6340201210229b48fcff732b7873b310126a69d54fae60ce316874d6c762ccc9e085f68841cffffffffd22123dbb9e97d97bda1ae9dd0aada7170e584067c2645a1e3470c81dd16bedb470000006a473044022008440936d645d817009e6a745d6071a0636676643891a6a7fd7f661226fef995022070cd3eb6618f212e40829eb071c7aac15b6c3df486e8d62a54aa9d89e81f8f68012103235173889e3df92f2bf3cf88b52fbadd58211cb3df76b9cb5f5f7046f2500301ffffffffd22123dbb9e97d97bda1ae9dd0aada7170e584067c2645a1e3470c81dd16bedb760000006b483045022100fe13e74bbe60522286dc6ada0755874e0bada1edec8d04c2dc6c479a8cd322b902204c5de0e514ec352897408f575d320e38f33cb356ce5dfd055fd0f72c023fb68b01210362ed038dbba6b24df044dd51557462b845547273183bbe3aa6c67c0cabf0ea4cffffffff723233eaee72e43c545516c55435bd2c81a1b508f1c906714551ca5ab07ba82d020000006b483045022100f4527f6bf203b2c964694706971520b527bac35ec78720283998f4f97b5959db02206f01a395c2149b27fe13253f6cad37e27c3237ba19c71db17621340fa62e6e7b0121025f6b53f4faa269f8fd0cb9c21a84782627e9d7dc495a37aef947e0f19e5b490effffffff017f47e200000000001976a914b5f78d55fcc7580519f10cbc467c111f80ff743e88ac0000000001000000019683987f834045a0e96549f563eced5d2e2a4cc20c4adba16f92b97521a7666d000000006a473044022010c169383471633e7346c31ab98dd19887ec2d5f683368012f7f1088f44ff114022050cce93b0eeeed2df872a3fe3dd31fa73c6d30dfe4ab8aa0e3ee4f8c29e8ca88012102392568f053515eaa3fabe6d8cd1f0771fb4a9ee076dd0d8638b714d6603d2040ffffffff02dad11000000000001976a914b250a838f20ebe174ebdf6176bbd40b0eb10d58b88ac40420f00000000001976a914f98cf91ab614dbbf94c591227ca604423125e7a288ac00000000010000000295fc49fa80593f458ca163a6c284040cf780044524b920fdd3002484fbfdf68a010000006a473044022056ced18a1ce4b3b90aa97f270c5684321017b777d5aa543f27ac2fd7ead5275a022028906086a7f2a10259229392590c7d09f6712795b3b80cd5972277f00493a7850121030cd3a60e9d5bbe20759835497421404f2b3e7651231bbde6c49886dcddabc738ffffffff4be714871033b0b7bff214d2ecd3841d20107f65e8095b29f54268e27030447a410000006b483045022100ee0d50095e08db2eba2ee7fe0531e0309886282a957e73edd04b7747f31172b902205926eff21df9a3c67674e221652bc084133c30b997c448b80e2599341a947ec90121033ff747cbbb4c9c450d9355c58ebe034611ac5dfeb6593a764e86df584be9427dffffffff0200f91500000000001976a914b0ae71a83c999f387a433f3a87fe77cb6b6c768a88ac64040000000000001976a91409f37a533d5432e1a60d9059a3a70655844d0c6b88ac000000000100000011d2d484d34717203f37a6fc47c38ee78b86d983ad542c55b28977bf2451d6c084000000006b483045022100ad32aa03fd4243cda4cf67f8d6181169cd3cfb19e3f417b1aef623f8fd44de0002204ee1de1955af1862da265fb1cf7b446f58f157fec62d0ce27d81622414c051b10121035e34ca1654e4563a705184aa8fb851f001d140c4ef66ce255ab02f6e3280763cffffffff68602e161b5af094ff4ecce669954978277155de6a0629f6a95ae2619c251263330000006b483045022100b98e67cb2c7e02f755d580fac80a247390526b5f22da2ce2c4479f29c1714a7b0220340996366d4ef1fd58b946b6d8fc2fd331b27dfdec97e8353f3acc59cd25e378012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff7d3bb57753bb999d054605ed53b82043f8f9f3c5f2c5dedc27c883df8b05351e810400006a473044022075aaba9c3a7fab43faf8565e10c1aa01e88f3b1f56923ac6e1ae093216cb51bc0220792f30636726898ee70332220bd2f2a31957fa7a68eb5a0009bf0c593affffba012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffffdcf6d5a683d8432a0c2bbaff8263be61902a8b1e5f6187338d4b45687ceffe1a5b0000006b483045022100daa29798d4ee06152746331703887974c3242541c9ec3d1941b02fa6c51e6ed2022064c68e93d453adfcbac582e749392c3763470a9143cd13577cde3a1a47998e8d012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff26ab92f05388883725239c6621241455546e50f83b483a60a58f747889fe5e4fbc0800006a4730440220202baa87fd0fe9aade6f3e3a946edbfa446fba9ddfe7c9bc681a3f90a7824bc202202cefa396b41a51e163c95fc5d0bc1c0620fd6807a8a24ef6a119f0cfe911da7e012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff281ca586e067a9cfb126b6851b1af50b7f3b722aa7f4607d7a3cd872acf844645d0500006b483045022100d76665ec77e0217d1ce8bdbab918d38add30e55bebca850a114f645c6424522e0220524ae9f86bf0a30c290c1a5880b38f5e94fccf12a7ddc043173d23ef4e68a22e012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff84b66e3431cea1a4727e9fc6ff3714ecd50896fa5d49448926ad21bcf6aec25e550000006a47304402201c7063faff2bdc59db866aca354a613b8235284e9a3fdf33448f0f9abaf4046e0220413d78169cb4a300e0832087829833390c4ec4c47d7ea6b28c5d48ce8e5bd2d6012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff103aea34301370c042607edc89ff90f192b6d79e99e290633764bbe4b5cb4bfc2d0000006b483045022100a904af251cf29fad607a6ce56e100e4b073184ad68abc45abe391c3991d5841402206e2c9ba87522192c3a73b56e33a2a5c2b231535307f03e4ff6f0c40f3f0d85fc012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffffd4cffe9a2f50bb1a08eb4ddfb1a31ba86de783b35eb68d7b982e8aecc22df625350000006a47304402200c44ee6f856f3d89cfb335fb608592bb6d31a30b14d500c059b6d1e35304f4a502203a59c6aca5e258d1565ed0619fd7fba1b1a61e30744ff654d3a9434fd05934da012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff72de34fd4f59dcef28fd6a60f2fcbf276fdb9ae494473bb0265b64bc5001f7104b0700006a47304402205797eb9a000e9cb19b258e4dfe927ef647013122aacbd017469b92243a58ff5902203595440f7095c434574f298a86e279c158aa5d5a3c7052388ebb18851a5db78a012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff893cd3ef8130644b74d8f936e82595ca18ec095ca47a23233f4fffeda7e58c52430000006b483045022100d3e0662d420df1d2bd8c3ae3690adc005a5e2ac1ce82a7ffdb2c7f04e2483929022067196650e670abfc141a22f6cdc2e3be1cbaa8f3d7e58180caa868fbe7c29c06012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff7741337671b30cf29649d5f375332bfc3af8fefc3fb4704c7be4f5618843fda3360000006a473044022061db9a1f59eae734e414e7b5d3a3fbafd7b2d6876958641f98ece8a89756c8f80220739f2a3faaeeda4fcf855d81cf46be3d4eb065599ddb6e3d236016c401358dc7012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffffbd756bb55d6c80e929071c9171bbfa929fd434dfb1c11d2abb07b9a399958838390000006b483045022100f68a3b94ce1618b614eaecd52381c90ac0690f96dcc6b73896a15e34ec18576302205c72b3bd30b028e80597bf9230ab8a17f9029b8780d9802decef90db8ca70225012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffffdd18874296cf22078d7e2763b6c65451a6046c2050f86b0e1dcea83c5e450966230500006a473044022013b0623171e46dff5650499955e3339ebae67ad08070d21dda98515660115878022005c5e1717ba46a4992c15b4f9c683443a56227b5477b60cd96526033a043e461012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff30d17fa1464f3ee104ac73d36415ee929bd00aa0771110230739f3766c0572d3ee0100006b483045022100bc7f78de139843473cefb5e359828d74a49f58e94eb500741abcb9a346fdcb04022018fd4c13bf96206546b7d61e5fb15bc0176774fde2a7e3ffa7a55d095b1c34eb012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff7535ad709eee6d0c61f5670afb1613303a85195c7e81f9717e3d06316900634e290200006b4830450221008209ada22fd6f580d385fcdd03769295a28eab3ef39870665ad39a47464560af0220153642331e1cfbfe8111db19c15aa09fb5e5d336bbde270d0ec0e9eb52300da6012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff0ac876832debadeaf7b7fdcbdd09cda529b7ea962e34a433c89addf557e09e97390000006b483045022100e3df4777911c982e60a97873af56ef90fbac0408807bada0cbbbf9c7eeb7387f022010e3113809d9d3091937d0465a5d1a612728d1393e34313a1c589144a6bb6c15012102d25ecbeb1898e8b050e508da1c1655594869512a6fb172000f690e612b0676daffffffff02d8030000000000001976a9145978eb0c5724b0724478fc0ec651cc603c27258288ac08c90100000000001976a9141651fef006ab0ad0f4014d3b0a127a945b947b0e88ac000000000100000006161bbf26e83f566300cc382d948a94252750b8cbcec13e9a669817a11fb61f49450000006b483045022100f6a96333a865bd4d1adf14080fe18c76c1db5a493131100847616ed44a5b74ec0220680ec2afdda4a31c413d73665d388ce542f6d5f9a430bfaa588e48f19ed7fe15012103fd3d2c65beef2b22b8ffd69f9abc4c737dbd71104fdc8c8effab6db1aff46343ffffffff47be55af3052320ebf5257de244e1b6ad33b6fa3e14e5822049ca50cd5f82c17850000006b483045022100e432633fb1f043a59632e1eb4911db10536eadf9cdc19f4e09e70ddf4e274d810220131c9b14a5acce5815ad4bf92921bd377bb685ff3b7f947583fe2dec84efacb7012103fd3d2c65beef2b22b8ffd69f9abc4c737dbd71104fdc8c8effab6db1aff46343ffffffff10bff1960f98239427ee5beca495269f38b03d08fd3a2d87b94e9d3020f9edb6bf0500006b483045022100b33699cc861f5f5eca33161f1777f208be0108bf4079ce52d14fd0d68c40319d02207e97fdd1f7a69da9c0dd5c098962ef642d7b81e1faa2580eaf38f207a852d671012103fd3d2c65beef2b22b8ffd69f9abc4c737dbd71104fdc8c8effab6db1aff46343ffffffff90decc43b6d900d53c0c9fd1609c50b6b97b2853be27046b534864bf9eb3002e190000006a473044022066f974a70cc86edc2b8675853f7a0c024cf36a9fff804872a95967f54070515d022006520defc864b59fac6eaf8a3747096389bd9f907cd02153f280af6159920f39012103fd3d2c65beef2b22b8ffd69f9abc4c737dbd71104fdc8c8effab6db1aff46343ffffffff8b41bfadb911a722fc423944953b4e2d5952b364b53f56e45af1ea3f8d007ce1210100006a47304402204ccde391a43155fa75165ea9f609f82637548283d047197ac64e40c854630b300220444e75376a8cfb1852edb39021b078e4ed29674c35e2f41a72ae10772bbf38da012103fd3d2c65beef2b22b8ffd69f9abc4c737dbd71104fdc8c8effab6db1aff46343ffffffff3237743d3b1999927d4190e1c452d9ef98ed8d821d03f635b4b15e71e1dd5cd6680000006b483045022100b96d784a7b063b17d2adaaa4d11335df5d42e988703db6390b1ef020d4074911022064b1cb831a479d57f01c407ce68fac31a138eeef9adc75e4aa958c9e73134712012103fd3d2c65beef2b22b8ffd69f9abc4c737dbd71104fdc8c8effab6db1aff46343ffffffff013cb00100000000001976a91427276168ff506f57631e733e921d96a63d2fb4f988ac00000000" }, - "GETADDR": { - "message": "", - "payload": "" + "tx": { + "message": "f9beb4d9747800000000000000000000020100009559b1b1010000000159fa9081526b419820f448512991925fc945e4ca032e3fd394fc6f01e88d05e8010000008b4830450221009950a6e0896a2477f3697419141d0759b55a767526fd4797d526110e8154234f022010e25ab9aad98d4fa277335a5149ff8aaf9dea29f7623d9a1dc8fa405b40f42c014104e0c76b3a203295af7a1c57f03222aa46cb87445043ffd910ea3c7b26b7244f31205ab6b36280cf2fcd3dffb993dd4972a90a8c9ff9813de575494a19cfbb6afdffffffff0240420f00000000001976a914069532d8d57a36fd7f12848ff7c4f27f5dc900ab88acb4002300000000001976a914a73e49d9cf3d61329cf15929366761fcb3c0efe088ac00000000" }, - "GETHEADERS": { - "message": "", - "payload": "" + "getdata" : { + "message": "f9beb4d967657464617461000000000025000000ecf888890101000000112a3581d9c5df4589564eff7a5651616cbec774b8b59b54798d5d4d686e4afe" }, - "HEADERS": { - "message": "f9beb4d9686561646572730000000000f400000043385d010302000000b91ddbbfc801b7fe6f470ce9528f98f01b496b53f23c411300000000000000004901c9d18d0a468b20cc62ddf75aee58cf410440ea390300bf7a5f6848be350508d4cb54c0a31a18b9f661ec0002000000a02d6472e3e6fc9a1cebeaad14a90208a715e2bd234ea00600000000000000006f596f650fbbd5478489c66d651c9e3ea56f394d1f1481f90975cf0c8dda45fd3ad4cb54c0a31a1872e262a200020000002e4db38f1970099bf21335edd604e7a591213e189d1d1806000000000000000031a30091f5bdbca8958d2c4ccc0bfa9df93e2a3ea4d00e03222a663179db90a756d6cb54c0a31a187947855000", - "payload": "0302000000b91ddbbfc801b7fe6f470ce9528f98f01b496b53f23c411300000000000000004901c9d18d0a468b20cc62ddf75aee58cf410440ea390300bf7a5f6848be350508d4cb54c0a31a18b9f661ec0002000000a02d6472e3e6fc9a1cebeaad14a90208a715e2bd234ea00600000000000000006f596f650fbbd5478489c66d651c9e3ea56f394d1f1481f90975cf0c8dda45fd3ad4cb54c0a31a1872e262a200020000002e4db38f1970099bf21335edd604e7a591213e189d1d1806000000000000000031a30091f5bdbca8958d2c4ccc0bfa9df93e2a3ea4d00e03222a663179db90a756d6cb54c0a31a187947855000" + "notfound" : { + "message": "f9beb4d96e6f74666f756e6400000000250000001d33d53201010000003a4af715be220eae7b2657582869daddf79ac4afb4a0e1cafa5b57e1afb8dfe2" }, - "TX": { - "message": "", - "payload": "01000000015884e5db9de218238671572340b207ee85b628074e7e467096c267266baf77a4000000006a473044022013fa3089327b50263029265572ae1b022a91d10ac80eb4f32f291c914533670b02200d8a5ed5f62634a7e1a0dc9188a3cc460a986267ae4d58faf50c79105431327501210223078d2942df62c45621d209fab84ea9a7a23346201b7727b9b45a29c4e76f5effffffff0150690f00000000001976a9147821c0a3768aa9d1a37e16cf76002aef5373f1a888ac00000000" + "alert": { + "message": "f9beb4d9616c65727400000000000000bc0000004fe68fe973010000003766404f00000000b305434f00000000f2030000f1030000001027000048ee00000064000000004653656520626974636f696e2e6f72672f666562323020696620796f7520686176652074726f75626c6520636f6e6e656374696e67206166746572203230204665627275617279004730450221008389df45f0703f39ec8c1cc42c13810ffcae14995bb648340219e353b63b53eb022009ec65e1c1aaeec1fd334c6b684bde2b3f573060d5b70c3a46723326e4e8a4f1" }, - "BLOCK": { - "message": "", - "payload": "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000" + "reject": { + "message": "f9beb4d972656a656374000000000000000000005df6e0e2" }, - "VERACK": { - "message": "f9beb4d976657261636b000000000000000000005df6e0e2", - "payload": "" + "notfound": { + "message": "f9beb4d96e6f74666f756e6400000000250000001d33d53201010000003a4af715be220eae7b2657582869daddf79ac4afb4a0e1cafa5b57e1afb8dfe2" }, - "INV": { - "message": "f9beb4d9696e76000000000000000000890200006e4f18431201000000f97347795bf7490ddeba98c129086f54e06633936a49a2a398defb49e5edbb00010000009cb17394db9280b2d5e7d9f01456358384e7453300d48138ca1590e8cd86632f010000000f810b6071b9808117c4b82619b4ae0a7994ed5a0f2c050e50fd5742c3b4e90901000000962798bce9cbfcee9a8c6b5df4cf8bed944eb31ec975e8c1be79b7ab010a1cb501000000baa905bcfc9d2641ecd759724f796a7614e91c7492507b418ba8e077a6e57054010000009a5ac6dae146392edf8b6e96c30951ed7752f9095fab61728207db0f135b18a30100000050e5f153e3f0d89ea432d9a26f088a89b3ca38dbf3f0247ddf15f80779915da801000000c255841510a284b88ba696ed9edfeb4cdb34966271c30bc3bce9fd881106850f0100000000e45af8455894f72cccc4053e1f5e6076a673982b1f4ca5b2abc7b6496823ab01000000c7e6dc049e5b0fdc0f8fac8934e84e9b2f0c3036c7c02638cc05ee2575d6be1701000000aef6435c03c99ee83702aaaf106dc853dee5bcb6025f3af67d1ec72202c437ad010000005369fc3dc81403fe52f766ef585245af908404be1b46e2ef67c93b748ef467e3010000002400f16a63c411ae5e336175afc26515e548c7267c0debdb6aa46830399ba35f01000000ceb4fa6a8ca2713baee2214422b73da47a20934c83d25017bd80053e65ef3091010000000d899968e591703ddd6fd6ce073837588209f8b3b245ecf1bf93e77b9806a6c501000000bddeb56581ad7882d8f2abc78e5a48b3de02d923cd1cfc7525d2dfe80470248a01000000f286188c0947b023f0ba5dd1ea596751d50f67fbe31e64ead39dd711d3585b5801000000e17ad100ebffd2d5a630d37fd2496b2f5ab6ae5c8812da3c642fb6b8dd37f5fd", - "payload": "1201000000f97347795bf7490ddeba98c129086f54e06633936a49a2a398defb49e5edbb00010000009cb17394db9280b2d5e7d9f01456358384e7453300d48138ca1590e8cd86632f010000000f810b6071b9808117c4b82619b4ae0a7994ed5a0f2c050e50fd5742c3b4e90901000000962798bce9cbfcee9a8c6b5df4cf8bed944eb31ec975e8c1be79b7ab010a1cb501000000baa905bcfc9d2641ecd759724f796a7614e91c7492507b418ba8e077a6e57054010000009a5ac6dae146392edf8b6e96c30951ed7752f9095fab61728207db0f135b18a30100000050e5f153e3f0d89ea432d9a26f088a89b3ca38dbf3f0247ddf15f80779915da801000000c255841510a284b88ba696ed9edfeb4cdb34966271c30bc3bce9fd881106850f0100000000e45af8455894f72cccc4053e1f5e6076a673982b1f4ca5b2abc7b6496823ab01000000c7e6dc049e5b0fdc0f8fac8934e84e9b2f0c3036c7c02638cc05ee2575d6be1701000000aef6435c03c99ee83702aaaf106dc853dee5bcb6025f3af67d1ec72202c437ad010000005369fc3dc81403fe52f766ef585245af908404be1b46e2ef67c93b748ef467e3010000002400f16a63c411ae5e336175afc26515e548c7267c0debdb6aa46830399ba35f01000000ceb4fa6a8ca2713baee2214422b73da47a20934c83d25017bd80053e65ef3091010000000d899968e591703ddd6fd6ce073837588209f8b3b245ecf1bf93e77b9806a6c501000000bddeb56581ad7882d8f2abc78e5a48b3de02d923cd1cfc7525d2dfe80470248a01000000f286188c0947b023f0ba5dd1ea596751d50f67fbe31e64ead39dd711d3585b5801000000e17ad100ebffd2d5a630d37fd2496b2f5ab6ae5c8812da3c642fb6b8dd37f5fd" + "getblocks": { + "message": "f9beb4d9676574626c6f636b7300000045000000a2165cd97011010001b91ddbbfc801b7fe6f470ce9528f98f01b496b53f23c41130000000000000000a1112367496906e6d89dc1cfdbf33a95cb8a1270f59e530b0000000000000000" }, - "ADDR": { - "message": "f9beb4d9616464720000000000000000b93a0000480bab8afdf5016816fa53010000000000000000000000000000000000ffff51403eea208ddb2a8854010000000000000000000000000000000000ffff707c60d9208d31413a54010000000000000000000000000000000000ffff5ed5fd13208d182c8854010000000000000000000000000000000000ffff505f3f81208d65ecb853010000000000000000000000000000000000ffff54d72104208dcca94054010000000000000000000000000000000000ffff40bbe1f2208d73238854010000000000000000000000000000000000ffff55d683cd208d5d258854010000000000000000000000000000000000ffffd523a66d208dcbea87540100000000000000200100005ef579fd2c9d47be4b56d671208db3e58754010000000000000000000000000000000000ffffae373156208d11e58754810000000000000000000000000000000000ffff51071276208d38f48754010000000000000000000000000000000000ffff6d4372e0208d4f448854010000000000000000000000000000000000ffffc556cbea208deb208854010000000000000000000000000000000000ffff450eb059208d97336054010000000000000000000000000000000000ffff4d60444a208dad608754010000000000000000000000000000000000ffff01abd3c3208d2cbe8754010000000000000000000000000000000000ffff7963d8b8208d9dfa8754010000000000000000000000000000000000ffffcebe862c208da7f66554010000000000000000000000000000000000ffff62f2a82e208d430d8854010000000000000000000000000000000000ffff6deb6956208d08148854010000000000000000000000000000000000ffff4b9804ef208d950e8854010000000000000000000000000000000000ffff6b8dd2e1208d1cfc8754010000000000000000000000000000000000ffffb2fe1dab208de7458754010000000000000000000000000000000000ffff32cccb82208d12625654010000000000000000000000000000000000ffff55e6f35a208dd7908354010000000000000000000000000000000000ffff4c642e6f208d17398854010000000000000000000000000000000000ffff45ea14df208dc72d8854010000000000000000000000000000000000ffffdaa48b96208d57fb8654010000000000000000000000000000000000ffff5f0080a9208d4e9a6d54010000000000000000000000000000000000ffff6d94e741208d7a418854010000000000000000000000000000000000ffff6d43b0c1208d5c098854010000000000000000000000000000000000ffff9c11e7ea208d442d8854010000000000000000000000000000000000ffffd0424482208db142fb53010000000000000000000000000000000000ffff54344a4a208d70158854010000000000000000000000000000000000ffff538f8201208d39288854010000000000000000000000000000000000ffff57daab71208d262f8854010000000000000000000000000000000000ffff57e045b1208d91430654010000000000000000000000000000000000ffff4212def5208d7c468854010000000000000000000000000000000000ffff62a4f53b208d9c3e8854010000000000000000000000000000000000ffff5b736bcc208d4d074f54010000000000000000000000000000000000ffff43af9d9b208deaca8754010000000000000000000000000000000000ffff4a8084a0208d74f68754010000000000000000000000000000000000ffff46304541208d5d098854010000000000000000000000000000000000ffff6eabb626208d08228854010000000000000000000000000000000000ffff1f07b030208d959a8254010000000000000000000000000000000000ffff4fa996b3208d4bac8754010000000000000000000000000000000000ffff43a0605b208de4038854010000000000000000000000000000000000ffff4cb41a5b208ddc308854010000000000000000000000000000000000ffffb72e6072208deac86f54010000000000000000000000000000000000ffffd5b815e2208d1b118854010000000000000000000000000000000000ffff3ff8d0a4208db1258854010000000000000000000000000000000000ffff5edd3a6a208d20268754010000000000000000000000000000000000ffff59ed3cfc208d83ed8754010000000000000000000000000000000000ffff4460baea208d71278854010000000000000000000000000000000000ffff4583b547208da7968754010000000000000000000000000000000000ffffd1c34463208d9d428854010000000000000000000000000000000000ffff43c15bc5208d33448854010000000000000000000000000000000000ffff2e0d8a5c208dfa2e7154010000000000000000000000000000000000ffffceff2bca208d47288854010000000000000000000000000000000000ffff5b8d0190208d0e847954010000000000000000000000000000000000ffff5501b8c2208d552f8854010000000000000000000000000000000000ffff5cf3b604208d8c2e8854010000000000000000000000000000000000ffff58666b0d208dcbdd8754010000000000000000000000000000000000ffff59a85cf9208d04208854010000000000000000000000000000000000ffff5f60abe0208dba238854010000000000000000000000000000000000ffff2e77ce67208d62c68754010000000000000000000000000000000000ffffcbae5658208d41108854010000000000000000000000000000000000ffff49329ec8208ddf1d8854010000000000000000000000000000000000ffff603ada12208df90c8854010000000000000000000000000000000000ffff51e0c21c208d5dff8754010000000000000000000000000000000000ffff904ca539208de0118854010000000000000000000000000000000000ffff5c0cecf7208d1b2d88540100000000000000200100005ef579fb18f317b2aba4edfc208dfaa27f54010000000000000000000000000000000000ffff54c65a7c208dc2db8754010000000000000000000000000000000000ffffad0b7981208d88a58754010000000000000000000000000000000000ffff188a1995208de0d78754010000000000000000000000000000000000ffff62a7a6bd208d18f66b54010000000000000000000000000000000000ffffdb4fbe80208dfcf78754010000000000000000000000000000000000ffff67145102208d3d3a8854010000000000000000000000000000000000ffff64259bd6208dcaa98654010000000000000000000000000000000000ffff32c737e1208d3a6a5c54010000000000000000000000000000000000ffff6d793f75208d22d38754010000000000000000000000000000000000ffff0595cc0d208da9ea8754010000000000000000000000000000000000ffff62ca37d3208d440e8854010000000000000000000000000000000000ffff123e1c11208dab428854010000000000000000000000000000000000ffff461df51f208d0f0b8854010000000000000000000000000000000000ffff59d36752208d631f8854010000000000000000000000000000000000ffff5ef8c640208dbc1d8854010000000000000000000000000000000000ffff5f5dc582208d04bf8754010000000000000000000000000000000000ffff0e6797e3208db8028854010000000000000000000000000000000000ffff3cf0c4a1208d6a0c8854010000000000000000000000000000000000ffff58bb5c55208d4f438854010000000000000000000000000000000000ffff5d326005208df6068854010000000000000000000000000000000000ffff5d5a5850208d40eb8754010000000000000000000000000000000000ffff5751ed86208dbad18754010000000000000000000000000000000000ffff63e51608208d99108854010000000000000000000000000000000000ffff36e299cd208d9a1e88540100000000000000200100005ef579fd38a43785a5d441b7208d13258854010000000000000000000000000000000000ffffb90de279208db3778754010000000000000000000000000000000000ffff67fcc80c208d3d358854010000000000000000000000000000000000ffffd00c40fc208dfd0d8854010000000000000000000000000000000000ffff5e89c2f3208dd6428854010000000000000000000000000000000000ffff1863e14a208d89e78754010000000000000000000000000000000000ffff43a11d76208d89ce8754010000000000000000000000000000000000ffff3edb6281208d03298854010000000000000000000000000000000000ffff6d5b26dd208d16df6e54010000000000000000000000000000000000ffffd9c3d176208d7c028854010000000000000000000000000000000000ffffbcc2db1f208d67fc8754010000000000000000000000000000000000ffff59910667208d86b18754010000000000000000000000000000000000ffff6c1482b9208d790b8854010000000000000000000000000000000000ffff0263150d208d89c44554010000000000000000000000000000000000ffff72fcd6b8208d331c8854010000000000000000000000000000000000ffff4c7dcfa8208d9fe98754010000000000000000000000000000000000ffffad08fd05208d0ecd8754010000000000000000000000000000000000ffff7c0fec89208d4af98754010000000000000000000000000000000000ffff2ea6a167208d270b8854010000000000000000000000000000000000ffff59d40fca208d11cf4054010000000000000000000000000000000000ffffb52ac058208d63358254010000000000000000000000000000000000ffff6bd9a130208dbde48754010000000000000000000000000000000000ffff4f915dc7208dece78754010000000000000000000000000000000000ffff5f8423ef208dba308854010000000000000000000000000000000000ffff904cba8c208d953d8854010000000000000000000000000000000000ffff8d1421e5208d5f188854010000000000000000000000000000000000ffff4e1bbfb6208d36fc8754010000000000000000000000000000000000ffff3205cfb6208d5e458854010000000000000000000000000000000000ffff493092c2208dd0878754010000000000000000000000000000000000ffff97caacde208dd99d8754010000000000000000000000000000000000ffffc05f3c38208da7018854010000000000000000000000000000000000ffffdcf5f82d208df0f42254010000000000000000000000000000000000ffff58bf997f208d94228854010000000000000000000000000000000000ffffbc8ee72d208de3448854010000000000000000000000000000000000ffff6d780f99208d32448854010000000000000000000000000000000000ffffbc64c76e208d0b3d8854010000000000000000000000000000000000ffff6c414312208d49138854010000000000000000000000000000000000ffff407e52c6208d2d128854010000000000000000000000000000000000ffff6dc987d8208d4acf8754010000000000000000000000000000000000ffffae151a25208de9dd8754010000000000000000000000000000000000ffff522d2d10208d17df8754010000000000000000000000000000000000ffff4d294737208ddbd48754010000000000000000000000000000000000ffff443703fd208d79108854010000000000000000000000000000000000ffff4bb5a154208d1df28753010000000000000000000000000000000000ffff4c44fc51208d5d496a53010000000000000000000000000000000000ffff530924b0208d180b8854010000000000000000000000000000000000ffffc15fced3208d53128854010000000000000000000000000000000000ffff5bc5b9e4208de21f8854010000000000000000000000000000000000ffff2e05fe1f208d31ab6554010000000000000000000000000000000000ffffae865915208d09248854010000000000000000000000000000000000ffff7d71a4b0208d120b8854010000000000000000000000000000000000ffff93e40150208d25d98754010000000000000000000000000000000000ffffc19f7a86208d99e78754010000000000000000000000000000000000ffff4d675993208d2b218854010000000000000000000000000000000000ffff5144776c208d5a208854010000000000000000000000000000000000ffff2e1cce5820f69e8f8154010000000000000000000000000000000000ffff58718848208d9edd6554010000000000000000000000000000000000ffff2ea758c5208d28c98754010000000000000000000000000000000000ffffc60bd694208d181d8854010000000000000000000000000000000000ffffba6b689e208d77868754010000000000000000000000000000000000ffff182016aa208de1698054010000000000000000000000000000000000ffff5cf5daa9208d823d8854010000000000000000000000000000000000ffffbe4d61c8208d2c3b8854010000000000000000000000000000000000ffff71647df2208da2248854010000000000000000000000000000000000ffff4ff359ca208da2dd8754010000000000000000000000000000000000ffff59f1e52b208da6e60d54010000000000000000000000000000000000ffff579596231159ad218854010000000000000000000000000000000000ffff5895f42d208d5ba78754010000000000000000000000000000000000ffff458ae8b0208ddc9b8754010000000000000000000000000000000000ffff18b507a5208d20eb5754010000000000000000000000000000000000ffff5181a537208daf1d8854010000000000000000000000000000000000ffffd5f51eba208d07f58754010000000000000000000000000000000000ffff6dec54bc208d4b3b8854010000000000000000000000000000000000ffff5c3e1991208d13b54054010000000000000000000000000000000000ffffb273845a208d4f338854010000000000000000000000000000000000ffffcfac797b208d4d898754010000000000000000000000000000000000ffffadaf880d208de72f8854010000000000000000000000000000000000ffffd956ef58208dc1e98754010000000000000000000000000000000000ffff980711cf208dfd8f7154010000000000000000000000000000000000ffff50aba98f208d59fe8754010000000000000000000000000000000000ffff5f2ae582208d3ffa8754010000000000000000000000000000000000ffffbca836db208d389b0054010000000000000000000000000000000000ffffded10925208deb154354010000000000000000000000000000000000ffff717532da208dadde7654010000000000000000000000000000000000ffff93afd0e5208d40458854010000000000000000000000000000000000ffff54ee8cb0208d08fa8754010000000000000000000000000000000000ffff3658eba9208d0df58754010000000000000000000000000000000000ffff72c6876e208d5d408854010000000000000000000000000000000000ffffa7582d7c208de8338854010000000000000000000000000000000000ffffad40d7ad208d6c2a8854010000000000000000000000000000000000ffffbc1833a3208d0fc28754010000000000000000000000000000000000ffffcbceb676208d98448854010000000000000000000000000000000000ffff0121ce6e208d77b58754010000000000000000000000000000000000ffff4b40d938208d853b8854010000000000000000000000000000000000ffff5648d643208d1c9c7854010000000000000000000000000000000000ffff6c3230db208d4e028754010000000000000000000000000000000000ffffd8e33c5d208daf738754010000000000000000000000000000000000ffffad4e9d56208dca438854010000000000000000000000000000000000ffff4f8a0353208d7b408854010000000000000000000000000000000000ffff0599e92a208dc62f8854010000000000000000000000000000000000ffff46236049208dc4d58754010000000000000000000000000000000000ffff4400384d208d0ddb4c54010000000000000000000000000000000000ffff71653b5a208d53388854010000000000000000000000000000000000ffff43bddf66208d7c388854010000000000000000000000000000000000ffff47edca96208d8c068854010000000000000000000000000000000000ffffdded3c2c208d89f60554010000000000000000000000000000000000ffff4e60d8d6208da5ff1154010000000000000000000000000000000000ffff55983c68208d72f58754010000000000000000000000000000000000ffffce47f56a208d7e2d8854010000000000000000000000000000000000ffff17e25c12208da7ca8754010000000000000000000000000000000000ffff051d0baa208d19af8754010000000000000000000000000000000000ffffad42d883208dffd68754010000000000000000000000000000000000ffff5418ff88208d87bf8754010000000000000000000000000000000000ffff4c1fe0dc208deef18754010000000000000000000000000000000000ffff4c71d406208d25868754010000000000000000000000000000000000ffffda67a475208d4d348854010000000000000000000000000000000000ffff5d561269208da5918754010000000000000000000000000000000000ffff42b163b3208dbfac3754010000000000000000000000000000000000ffffad4e01eb208d5c408854010000000000000000000000000000000000ffffcc0bb91e208d4a928754010000000000000000000000000000000000ffff531dba30208db9fd8754010000000000000000000000000000000000ffffa3f72b33208dade58754010000000000000000000000000000000000ffffd8b93aeb208db75d4d54010000000000000000000000000000000000ffffae1d5595208de8d18754010000000000000000000000000000000000ffff4405665c208d88e78754010000000000000000000000000000000000ffff6b96180a208dae667354010000000000000000000000000000000000ffff531f490a208d92f98754010000000000000000000000000000000000ffffd447e857208db1428854010000000000000000000000000000000000ffff36c6b4bb208dd42c8854010000000000000000000000000000000000ffff4b4aff14208df71acd53010000000000000000000000000000000000ffff62d17930208da0b88754010000000000000000000000000000000000ffff974bf913208d5faa7953010000000000000000000000000000000000ffff7419d269208d6c1e8854010000000000000000000000000000000000ffff48a72343208d9c2b8854010000000000000000000000000000000000ffff64431e87208de8908754010000000000000000000000000000000000ffff44660d45208d410b7d54010000000000000000000000000000000000ffff43a3340d208d5b298854010000000000000000000000000000000000ffff9ffda678208de7238854010000000000000000000000000000000000ffff189a3759208da5745354010000000000000000000000000000000000ffff57026e31208df23c885401000000000000002a010488006710000523fbe100000001208dddf48754010000000000000000000000000000000000ffffc1531c5b208d61888754010000000000000000000000000000000000ffff42cd8bc1208df92e8854010000000000000000000000000000000000ffff36e1e970208d0cf75754010000000000000000000000000000000000ffffad4e2420208d0f028854010000000000000000000000000000000000ffff6ec61b02208de6228854010000000000000000000000000000000000ffffdcaa807c208d1cac8754010000000000000000000000000000000000ffff6179416c208db6068854010000000000000000000000000000000000ffffd46121e4208d7ea58754010000000000000000000000000000000000ffff80a41679208d91216254010000000000000000000000000000000000ffff69ece091208d0bf78754010000000000000000000000000000000000ffff45320c0b208dd03c8854010000000000000000000000000000000000ffff4859a2a5208d8e308854010000000000000000000000000000000000ffff2e1ce15a208d30f36354010000000000000000000000000000000000ffff4b61ee15208d7aa05553010000000000000000000000000000000000ffffb129186b208dbb238854010000000000000000000000000000000000ffff581a7f6f208d41248754010000000000000000000000000000000000ffffb009055b208dee218854010000000000000000000000000000000000ffff2e7771f9208db4e78754010000000000000000000000000000000000ffff411bf6ee208db5178854010000000000000000000000000000000000ffffacff001b208dbcf487540100000000000000200100009d386ab83061cfabfe3513a3208d76048854010000000000000000000000000000000000ffff2e35dbc2208d83d28754010000000000000000000000000000000000ffffd91bb1d5208d5c3d8854010000000000000000000000000000000000ffff32a47969208df4bd875401000000000000002a0104f8012032250000000000000002208d1bfa8754010000000000000000000000000000000000ffff53a2f4b6208d141c4554010000000000000000000000000000000000ffffb27b8281208d580c7e54010000000000000000000000000000000000ffffc9ea1340208df7228854010000000000000000000000000000000000ffffd90a268d208d41e18754010000000000000000000000000000000000ffff555b8809208d66358854010000000000000000000000000000000000ffff5280ff23208deecd8754010000000000000000000000000000000000ffff84c6a102208df9bc8754010000000000000000000000000000000000ffffbbc96dae208d9d0a8854010000000000000000000000000000000000ffffb2a2d18a208da9458854010000000000000000000000000000000000ffffca16c30e208d2ca88754010000000000000000000000000000000000ffff6baab63e208d923b8854010000000000000000000000000000000000ffffd5b87bee208d12258854010000000000000000000000000000000000ffff59ee408b208d980e8854010000000000000000000000000000000000ffff2edfb0c4208d33378854010000000000000000000000000000000000ffff6caa7b42208d41176d53010000000000000000000000000000000000ffff1fa2df68208dd1c57f54010000000000000000000000000000000000ffffbeab679a208d17c78754010000000000000000000000000000000000ffff3f983f51208dbb278854010000000000000000000000000000000000ffff568dbc28208d5d5ad953010000000000000000000000000000000000ffff0ec112b7208da1b78754010000000000000000000000000000000000ffffbc287698208d99348854010000000000000000000000000000000000ffff5ff1a302208d23ef8454010000000000000000000000000000000000ffff7ba55d22208d90e98754010000000000000000000000000000000000ffff5ae14503208d58f48754010000000000000000000000000000000000ffffd45a3cae208d530c8854010000000000000000000000000000000000ffff18fb80c4208dc6138854010000000000000000000000000000000000ffff4e2fd6eb208d97208854010000000000000000000000000000000000ffff55ddd5b8208d74f88654010000000000000000000000000000000000ffff1809ad86208dc0278854010000000000000000000000000000000000ffff6ef2dfb6208d84338854010000000000000000000000000000000000ffff58d8114e208dcae78754010000000000000000000000000000000000ffffc21c474c208d61e78754010000000000000000000000000000000000ffffd5b3fc7a208d7a078854010000000000000000000000000000000000ffff32740193208de7547b54010000000000000000000000000000000000ffff8046aac3208db5328854010000000000000000000000000000000000ffff5b0c5366208d5f208854010000000000000000000000000000000000ffff6caa054a208d1d682854010000000000000000000000000000000000ffff69e3eb9d208d179e6b54010000000000000000000000000000000000ffff54aac628208ded2e8854010000000000000000000000000000000000ffff4166d226208d8e148854010000000000000000000000000000000000ffff54305af6208d1ed88754010000000000000000000000000000000000ffff3ed2b2a4208d78ec8554010000000000000000000000000000000000ffff6dd2e575208ddd3e7854010000000000000000000000000000000000ffff1b83a102208d421e8854010000000000000000000000000000000000ffff2eb51bf1208dc01f8854010000000000000000000000000000000000ffff58c66033208d08ce8754010000000000000000000000000000000000ffff5d57b84c208df22e8854010000000000000000000000000000000000ffffbf213e3d208d89cb8754010000000000000000000000000000000000ffff53d7ece1208da9d68754010000000000000000000000000000000000ffff71bef412208d38268854010000000000000000000000000000000000ffff84fc8a6f208d4f107254010000000000000000000000000000000000ffff57a072d9208d990f88540100000000000000200100009d3890d71029db21863020f9208df3c36854010000000000000000000000000000000000ffffbba6a6c4208d3ea71f54010000000000000000000000000000000000ffff3e61237b208d60a18754010000000000000000000000000000000000ffff53a56b4b208db2328854010000000000000000000000000000000000ffff44eef265208d2c0f8854010000000000000000000000000000000000ffff566553c0208dec268854010000000000000000000000000000000000ffff57a69ecd208d93148854010000000000000000000000000000000000ffff9b8f4433208d741b8854010000000000000000000000000000000000ffffae17c74c208ddab94f54010000000000000000000000000000000000ffffab19c63a208d5e886f54010000000000000000000000000000000000ffffbc67a037208d5c3ccc53010000000000000000000000000000000000ffff0e77c86e208d26fb8754010000000000000000000000000000000000ffffc1eae172208d61278854010000000000000000000000000000000000ffff5ce15439208d60df8754010000000000000000000000000000000000ffff2599fb3b208d52418854010000000000000000000000000000000000ffff028710e6208dae448854010000000000000000000000000000000000ffff18152eee208d6ff18754010000000000000000000000000000000000ffff542e396d208d8c248854010000000000000000000000000000000000ffff5519d6d8208d01e33e54010000000000000000000000000000000000ffff5027d563208d05cd8754010000000000000000000000000000000000ffff45a5a9c5208d7c1e8854010000000000000000000000000000000000ffff68c813c8208d38a48754010000000000000000000000000000000000ffff3a60a934208d73ec8754010000000000000000000000000000000000ffffae3db8f3208d096c8754010000000000000000000000000000000000ffff4cb88802208d55d38754010000000000000000000000000000000000ffffd31f082e208d0d8a8754010000000000000000000000000000000000ffff6c22c9bb208d81ba7754010000000000000000000000000000000000ffffca5f88da208d5df88754010000000000000000000000000000000000ffffc31abc05208db1d58754010000000000000000000000000000000000ffff5cf614c8208d2b257c54010000000000000000000000000000000000ffff45f9baa0208d38d78754010000000000000000000000000000000000ffff490e0608208d27e88754010000000000000000000000000000000000ffff17f1ccd0208d501c8854010000000000000000000000000000000000ffff5be834a7208d860b8854010000000000000000000000000000000000ffff640173bc208deada8754010000000000000000000000000000000000ffffdc98f8db208d983d3c54010000000000000000000000000000000000ffff021ab398208d99f28754010000000000000000000000000000000000ffff59a9e417208d19468854010000000000000000000000000000000000ffff91ff01a1208ddbf68754010000000000000000000000000000000000ffff50048949208d89118854010000000000000000000000000000000000ffffc8366923208dea3b8854010000000000000000000000000000000000ffffbcbbb5a9208d364a8854010000000000000000000000000000000000ffffc654bd58208dd52ef953010000000000000000000000000000000000ffff7cabb7bc208db086cc53010000000000000000000000000000000000ffff6c4004ea208d0ff48754010000000000000000000000000000000000ffff0536a3cb208d1c0b8854010000000000000000000000000000000000ffffd90bfef3208dbf188854010000000000000000000000000000000000ffff0252431f208dd1c78754010000000000000000000000000000000000ffff25732b19208dd6fa3954010000000000000000000000000000000000ffff6c1131dc208da6108854010000000000000000000000000000000000ffff1fb9b748208daf368854010000000000000000000000000000000000ffff1f2a2981208d9e2f8854010000000000000000000000000000000000ffffbc658482208d6a458854010000000000000000000000000000000000ffff568b843a208dcd428854010000000000000000000000000000000000ffff5d820794208d38258854010000000000000000000000000000000000ffff6440c630208d1cda8754010000000000000000000000000000000000ffff6e9f7be5208ddd388854010000000000000000000000000000000000ffff9f080244208d2e2d8854010000000000000000000000000000000000ffffbb70cf3d208d3e0c8854010000000000000000000000000000000000ffff3ec27a16208d89098854010000000000000000000000000000000000ffffb55fb2fb208d13468854010000000000000000000000000000000000ffff180ab1e5208dfc2a8854010000000000000000000000000000000000ffffd58a5c0e208db9188854010000000000000000000000000000000000ffff81ce808d208d03d06b54010000000000000000000000000000000000ffff4b496858208d319e8454010000000000000000000000000000000000ffff531fac9f208dc8d98754010000000000000000000000000000000000ffffc113e4ea208d06395754010000000000000000000000000000000000ffff7cabf0d6208d722b8854010000000000000000000000000000000000ffff545db4cd208dd91f8854010000000000000000000000000000000000ffff5f12a597208dfbd57054010000000000000000000000000000000000ffff3cf654e6208dd7358854010000000000000000000000000000000000ffffb4998e79208dd7018854010000000000000000000000000000000000ffffb45cc220208d86df5554010000000000000000000000000000000000ffff7660d462208decd28754010000000000000000000000000000000000ffff5b7bdfe2208dafe28754010000000000000000000000000000000000ffff2e76899e208d0c318854010000000000000000000000000000000000ffffc7bcb183208d35258854010000000000000000000000000000000000ffff3ab2d076208db5c38754010000000000000000000000000000000000ffff48b24d30208ddfd18754010000000000000000000000000000000000ffff904cb00c208d40478854010000000000000000000000000000000000ffff02a3b787208deb318854010000000000000000000000000000000000ffffd2564026208de63f8854010000000000000000000000000000000000ffff615d16c0208dbf1d8854010000000000000000000000000000000000ffffc6fff6f0208d4aee8754010000000000000000000000000000000000ffff56c74bc8208d68bc8754010000000000000000000000000000000000ffff31b581f9208ddcca8754010000000000000000000000000000000000ffff0e97397a208db2318854010000000000000000000000000000000000ffffc766348d208de1bd8754010000000000000000000000000000000000ffffd5927a3d208d2aff8754010000000000000000000000000000000000ffff5152d0a5208da7feb753010000000000000000000000000000000000ffff53fe1694208de7418854010000000000000000000000000000000000ffff189f3d99208de7b38754010000000000000000000000000000000000ffff64004655208dacabb653010000000000000000000000000000000000ffff55413231208d43b88754010000000000000000000000000000000000ffff796e1029208d03788754010000000000000000000000000000000000ffff49b90ff6208d04be8754010000000000000000000000000000000000ffffc327ce1c208d33648254010000000000000000000000000000000000ffff2e896478208d9e308854010000000000000000000000000000000000ffffd395d9bf208df13e8854010000000000000000000000000000000000ffff51bb88ed208dec8c5653010000000000000000000000000000000000ffff557f146e208df4538854010000000000000000000000000000000000ffff48d05928208d41bd8154010000000000000000000000000000000000ffff47384162208df6428854010000000000000000000000000000000000ffffc1566314208d753e8854010000000000000000000000000000000000ffff440c9b4f208d2f078854010000000000000000000000000000000000ffff56021a19208d7b0f4754010000000000000000000000000000000000ffff71653a84208dcee88754010000000000000000000000000000000000ffff5401a332208df4db8754010000000000000000000000000000000000ffff5a9db0e3208df8e38754010000000000000000000000000000000000ffff5d5208fa208d2e298854010000000000000000000000000000000000ffff4c61c0bc208d42ea8754010000000000000000000000000000000000ffff4c405ae7208deade8754010000000000000000000000000000000000ffffc009c823208d99358854010000000000000000000000000000000000ffff6cc2b493208df3fd8754010000000000000000000000000000000000ffff904c6007208d1fde8754010000000000000000000000000000000000ffff92732ae8208d666e8354010000000000000000000000000000000000ffff6daa9d16208d18f78754010000000000000000000000000000000000ffff4d6d8d8a208ddea9ce53010000000000000000000000000000000000ffff17e26f7d208d1ae48754010000000000000000000000000000000000ffffc32ebb89208dee258854010000000000000000000000000000000000ffff4a0f10e1208d290a8854010000000000000000000000000000000000ffff42731593208d7cb33354010000000000000000000000000000000000ffff532e8b97208dfd1e8854010000000000000000000000000000000000ffff6ee91680208d480a1354010000000000000000000000000000000000ffff60f220f6208d55838754010000000000000000000000000000000000ffff1f36d48f208d247e8754010000000000000000000000000000000000ffff44b5a49a208d0efa4954010000000000000000000000000000000000ffff63890597208d862e8854010000000000000000000000000000000000ffffc14d8751208d443c8854010000000000000000000000000000000000ffff70d16dba208d72fe8754010000000000000000000000000000000000ffff55983dd3208d236b6854010000000000000000000000000000000000ffff7aea27f9208d67e08754010000000000000000000000000000000000ffff1b20750d208df10a7154010000000000000000000000000000000000ffff7b9fe56e208db32d8854010000000000000000000000000000000000ffff7ba42d54208db3f58754010000000000000000000000000000000000ffff027b4eba208d70d68754010000000000000000000000000000000000ffffbc1b62b9208d97951454010000000000000000000000000000000000ffffb4b6af10208d251288540100000000000000200100009d386ab800773149c4d7e673208d7b168854010000000000000000000000000000000000ffff58969392208d91d18754010000000000000000000000000000000000ffff9ffd6d4e208d19008854010000000000000000000000000000000000ffff4e926cb8208dd1c98754010000000000000000000000000000000000ffff546cdb77208d6a0a8854010000000000000000000000000000000000ffff6caa8c15208d3a3a8854010000000000000000000000000000000000ffff62e28d6a208d3fe48754010000000000000000000000000000000000ffffd9850be7208dcb438854010000000000000000000000000000000000ffff6c2444b3208d78338854010000000000000000000000000000000000ffffb2df574a208d3b2d3054010000000000000000000000000000000000ffff4b529e67208d5b988754010000000000000000000000000000000000ffff36e152e6208d01ab8754010000000000000000000000000000000000ffff58b531bf208d7b038854010000000000000000000000000000000000ffff54717a6f208db0388854010000000000000000000000000000000000ffff4a694e98208d65418854010000000000000000000000000000000000ffffcef84b25208dcf218854010000000000000000000000000000000000ffff5364fed9208db6c68754010000000000000000000000000000000000ffff4d03decd208dc6158854010000000000000000000000000000000000ffffb9044c79208d65418854010000000000000000000000000000000000ffff56b2192f208d23308754010000000000000000000000000000000000ffffd973f7a4208d05158854010000000000000000000000000000000000ffff1b6d9975208d52cd8754010000000000000000000000000000000000ffffdf5b92ad208d3a128854010000000000000000000000000000000000ffff6d93787e208d351e8854010000000000000000000000000000000000ffffb6a4336f208d9bd58754010000000000000000000000000000000000ffff3ba7f521208dc3288854010000000000000000000000000000000000ffff6bbf20cc208d68998754010000000000000000000000000000000000ffff63b30032208d86848754010000000000000000000000000000000000ffffb52ef15c208dff108854010000000000000000000000000000000000ffff4d06718b208d226f0754010000000000000000000000000000000000ffff44248ba1208da3af7354010000000000000000000000000000000000ffff86f9d72c208d9fd68754010000000000000000000000000000000000ffff521f2444208dae428854010000000000000000000000000000000000ffffd445b025208dfafc8754010000000000000000000000000000000000ffffd447fa84208dbb358854010000000000000000000000000000000000ffff3ee2d196208da01a8854010000000000000000000000000000000000ffff45a615b2208d32edb053010000000000000000000000000000000000ffff6d6494da208d77ed8754010000000000000000000000000000000000ffff555dcc44208d09608754010000000000000000000000000000000000ffff45a5d222208d84558754010000000000000000000000000000000000ffffb01c30ab208d71fe8754010000000000000000000000000000000000ffffae5f68c2208de0acdf53010000000000000000000000000000000000ffffae737936208d29e18754010000000000000000000000000000000000ffffb00a63cb208d220b8854010000000000000000000000000000000000ffff9ec4d12f208db4408854010000000000000000000000000000000000ffff4c6d9bcc208d65a58754010000000000000000000000000000000000ffff5962c453208d2d628854010000000000000000000000000000000000ffff257191d50000", - "payload": "fdf5016816fa53010000000000000000000000000000000000ffff51403eea208ddb2a8854010000000000000000000000000000000000ffff707c60d9208d31413a54010000000000000000000000000000000000ffff5ed5fd13208d182c8854010000000000000000000000000000000000ffff505f3f81208d65ecb853010000000000000000000000000000000000ffff54d72104208dcca94054010000000000000000000000000000000000ffff40bbe1f2208d73238854010000000000000000000000000000000000ffff55d683cd208d5d258854010000000000000000000000000000000000ffffd523a66d208dcbea87540100000000000000200100005ef579fd2c9d47be4b56d671208db3e58754010000000000000000000000000000000000ffffae373156208d11e58754810000000000000000000000000000000000ffff51071276208d38f48754010000000000000000000000000000000000ffff6d4372e0208d4f448854010000000000000000000000000000000000ffffc556cbea208deb208854010000000000000000000000000000000000ffff450eb059208d97336054010000000000000000000000000000000000ffff4d60444a208dad608754010000000000000000000000000000000000ffff01abd3c3208d2cbe8754010000000000000000000000000000000000ffff7963d8b8208d9dfa8754010000000000000000000000000000000000ffffcebe862c208da7f66554010000000000000000000000000000000000ffff62f2a82e208d430d8854010000000000000000000000000000000000ffff6deb6956208d08148854010000000000000000000000000000000000ffff4b9804ef208d950e8854010000000000000000000000000000000000ffff6b8dd2e1208d1cfc8754010000000000000000000000000000000000ffffb2fe1dab208de7458754010000000000000000000000000000000000ffff32cccb82208d12625654010000000000000000000000000000000000ffff55e6f35a208dd7908354010000000000000000000000000000000000ffff4c642e6f208d17398854010000000000000000000000000000000000ffff45ea14df208dc72d8854010000000000000000000000000000000000ffffdaa48b96208d57fb8654010000000000000000000000000000000000ffff5f0080a9208d4e9a6d54010000000000000000000000000000000000ffff6d94e741208d7a418854010000000000000000000000000000000000ffff6d43b0c1208d5c098854010000000000000000000000000000000000ffff9c11e7ea208d442d8854010000000000000000000000000000000000ffffd0424482208db142fb53010000000000000000000000000000000000ffff54344a4a208d70158854010000000000000000000000000000000000ffff538f8201208d39288854010000000000000000000000000000000000ffff57daab71208d262f8854010000000000000000000000000000000000ffff57e045b1208d91430654010000000000000000000000000000000000ffff4212def5208d7c468854010000000000000000000000000000000000ffff62a4f53b208d9c3e8854010000000000000000000000000000000000ffff5b736bcc208d4d074f54010000000000000000000000000000000000ffff43af9d9b208deaca8754010000000000000000000000000000000000ffff4a8084a0208d74f68754010000000000000000000000000000000000ffff46304541208d5d098854010000000000000000000000000000000000ffff6eabb626208d08228854010000000000000000000000000000000000ffff1f07b030208d959a8254010000000000000000000000000000000000ffff4fa996b3208d4bac8754010000000000000000000000000000000000ffff43a0605b208de4038854010000000000000000000000000000000000ffff4cb41a5b208ddc308854010000000000000000000000000000000000ffffb72e6072208deac86f54010000000000000000000000000000000000ffffd5b815e2208d1b118854010000000000000000000000000000000000ffff3ff8d0a4208db1258854010000000000000000000000000000000000ffff5edd3a6a208d20268754010000000000000000000000000000000000ffff59ed3cfc208d83ed8754010000000000000000000000000000000000ffff4460baea208d71278854010000000000000000000000000000000000ffff4583b547208da7968754010000000000000000000000000000000000ffffd1c34463208d9d428854010000000000000000000000000000000000ffff43c15bc5208d33448854010000000000000000000000000000000000ffff2e0d8a5c208dfa2e7154010000000000000000000000000000000000ffffceff2bca208d47288854010000000000000000000000000000000000ffff5b8d0190208d0e847954010000000000000000000000000000000000ffff5501b8c2208d552f8854010000000000000000000000000000000000ffff5cf3b604208d8c2e8854010000000000000000000000000000000000ffff58666b0d208dcbdd8754010000000000000000000000000000000000ffff59a85cf9208d04208854010000000000000000000000000000000000ffff5f60abe0208dba238854010000000000000000000000000000000000ffff2e77ce67208d62c68754010000000000000000000000000000000000ffffcbae5658208d41108854010000000000000000000000000000000000ffff49329ec8208ddf1d8854010000000000000000000000000000000000ffff603ada12208df90c8854010000000000000000000000000000000000ffff51e0c21c208d5dff8754010000000000000000000000000000000000ffff904ca539208de0118854010000000000000000000000000000000000ffff5c0cecf7208d1b2d88540100000000000000200100005ef579fb18f317b2aba4edfc208dfaa27f54010000000000000000000000000000000000ffff54c65a7c208dc2db8754010000000000000000000000000000000000ffffad0b7981208d88a58754010000000000000000000000000000000000ffff188a1995208de0d78754010000000000000000000000000000000000ffff62a7a6bd208d18f66b54010000000000000000000000000000000000ffffdb4fbe80208dfcf78754010000000000000000000000000000000000ffff67145102208d3d3a8854010000000000000000000000000000000000ffff64259bd6208dcaa98654010000000000000000000000000000000000ffff32c737e1208d3a6a5c54010000000000000000000000000000000000ffff6d793f75208d22d38754010000000000000000000000000000000000ffff0595cc0d208da9ea8754010000000000000000000000000000000000ffff62ca37d3208d440e8854010000000000000000000000000000000000ffff123e1c11208dab428854010000000000000000000000000000000000ffff461df51f208d0f0b8854010000000000000000000000000000000000ffff59d36752208d631f8854010000000000000000000000000000000000ffff5ef8c640208dbc1d8854010000000000000000000000000000000000ffff5f5dc582208d04bf8754010000000000000000000000000000000000ffff0e6797e3208db8028854010000000000000000000000000000000000ffff3cf0c4a1208d6a0c8854010000000000000000000000000000000000ffff58bb5c55208d4f438854010000000000000000000000000000000000ffff5d326005208df6068854010000000000000000000000000000000000ffff5d5a5850208d40eb8754010000000000000000000000000000000000ffff5751ed86208dbad18754010000000000000000000000000000000000ffff63e51608208d99108854010000000000000000000000000000000000ffff36e299cd208d9a1e88540100000000000000200100005ef579fd38a43785a5d441b7208d13258854010000000000000000000000000000000000ffffb90de279208db3778754010000000000000000000000000000000000ffff67fcc80c208d3d358854010000000000000000000000000000000000ffffd00c40fc208dfd0d8854010000000000000000000000000000000000ffff5e89c2f3208dd6428854010000000000000000000000000000000000ffff1863e14a208d89e78754010000000000000000000000000000000000ffff43a11d76208d89ce8754010000000000000000000000000000000000ffff3edb6281208d03298854010000000000000000000000000000000000ffff6d5b26dd208d16df6e54010000000000000000000000000000000000ffffd9c3d176208d7c028854010000000000000000000000000000000000ffffbcc2db1f208d67fc8754010000000000000000000000000000000000ffff59910667208d86b18754010000000000000000000000000000000000ffff6c1482b9208d790b8854010000000000000000000000000000000000ffff0263150d208d89c44554010000000000000000000000000000000000ffff72fcd6b8208d331c8854010000000000000000000000000000000000ffff4c7dcfa8208d9fe98754010000000000000000000000000000000000ffffad08fd05208d0ecd8754010000000000000000000000000000000000ffff7c0fec89208d4af98754010000000000000000000000000000000000ffff2ea6a167208d270b8854010000000000000000000000000000000000ffff59d40fca208d11cf4054010000000000000000000000000000000000ffffb52ac058208d63358254010000000000000000000000000000000000ffff6bd9a130208dbde48754010000000000000000000000000000000000ffff4f915dc7208dece78754010000000000000000000000000000000000ffff5f8423ef208dba308854010000000000000000000000000000000000ffff904cba8c208d953d8854010000000000000000000000000000000000ffff8d1421e5208d5f188854010000000000000000000000000000000000ffff4e1bbfb6208d36fc8754010000000000000000000000000000000000ffff3205cfb6208d5e458854010000000000000000000000000000000000ffff493092c2208dd0878754010000000000000000000000000000000000ffff97caacde208dd99d8754010000000000000000000000000000000000ffffc05f3c38208da7018854010000000000000000000000000000000000ffffdcf5f82d208df0f42254010000000000000000000000000000000000ffff58bf997f208d94228854010000000000000000000000000000000000ffffbc8ee72d208de3448854010000000000000000000000000000000000ffff6d780f99208d32448854010000000000000000000000000000000000ffffbc64c76e208d0b3d8854010000000000000000000000000000000000ffff6c414312208d49138854010000000000000000000000000000000000ffff407e52c6208d2d128854010000000000000000000000000000000000ffff6dc987d8208d4acf8754010000000000000000000000000000000000ffffae151a25208de9dd8754010000000000000000000000000000000000ffff522d2d10208d17df8754010000000000000000000000000000000000ffff4d294737208ddbd48754010000000000000000000000000000000000ffff443703fd208d79108854010000000000000000000000000000000000ffff4bb5a154208d1df28753010000000000000000000000000000000000ffff4c44fc51208d5d496a53010000000000000000000000000000000000ffff530924b0208d180b8854010000000000000000000000000000000000ffffc15fced3208d53128854010000000000000000000000000000000000ffff5bc5b9e4208de21f8854010000000000000000000000000000000000ffff2e05fe1f208d31ab6554010000000000000000000000000000000000ffffae865915208d09248854010000000000000000000000000000000000ffff7d71a4b0208d120b8854010000000000000000000000000000000000ffff93e40150208d25d98754010000000000000000000000000000000000ffffc19f7a86208d99e78754010000000000000000000000000000000000ffff4d675993208d2b218854010000000000000000000000000000000000ffff5144776c208d5a208854010000000000000000000000000000000000ffff2e1cce5820f69e8f8154010000000000000000000000000000000000ffff58718848208d9edd6554010000000000000000000000000000000000ffff2ea758c5208d28c98754010000000000000000000000000000000000ffffc60bd694208d181d8854010000000000000000000000000000000000ffffba6b689e208d77868754010000000000000000000000000000000000ffff182016aa208de1698054010000000000000000000000000000000000ffff5cf5daa9208d823d8854010000000000000000000000000000000000ffffbe4d61c8208d2c3b8854010000000000000000000000000000000000ffff71647df2208da2248854010000000000000000000000000000000000ffff4ff359ca208da2dd8754010000000000000000000000000000000000ffff59f1e52b208da6e60d54010000000000000000000000000000000000ffff579596231159ad218854010000000000000000000000000000000000ffff5895f42d208d5ba78754010000000000000000000000000000000000ffff458ae8b0208ddc9b8754010000000000000000000000000000000000ffff18b507a5208d20eb5754010000000000000000000000000000000000ffff5181a537208daf1d8854010000000000000000000000000000000000ffffd5f51eba208d07f58754010000000000000000000000000000000000ffff6dec54bc208d4b3b8854010000000000000000000000000000000000ffff5c3e1991208d13b54054010000000000000000000000000000000000ffffb273845a208d4f338854010000000000000000000000000000000000ffffcfac797b208d4d898754010000000000000000000000000000000000ffffadaf880d208de72f8854010000000000000000000000000000000000ffffd956ef58208dc1e98754010000000000000000000000000000000000ffff980711cf208dfd8f7154010000000000000000000000000000000000ffff50aba98f208d59fe8754010000000000000000000000000000000000ffff5f2ae582208d3ffa8754010000000000000000000000000000000000ffffbca836db208d389b0054010000000000000000000000000000000000ffffded10925208deb154354010000000000000000000000000000000000ffff717532da208dadde7654010000000000000000000000000000000000ffff93afd0e5208d40458854010000000000000000000000000000000000ffff54ee8cb0208d08fa8754010000000000000000000000000000000000ffff3658eba9208d0df58754010000000000000000000000000000000000ffff72c6876e208d5d408854010000000000000000000000000000000000ffffa7582d7c208de8338854010000000000000000000000000000000000ffffad40d7ad208d6c2a8854010000000000000000000000000000000000ffffbc1833a3208d0fc28754010000000000000000000000000000000000ffffcbceb676208d98448854010000000000000000000000000000000000ffff0121ce6e208d77b58754010000000000000000000000000000000000ffff4b40d938208d853b8854010000000000000000000000000000000000ffff5648d643208d1c9c7854010000000000000000000000000000000000ffff6c3230db208d4e028754010000000000000000000000000000000000ffffd8e33c5d208daf738754010000000000000000000000000000000000ffffad4e9d56208dca438854010000000000000000000000000000000000ffff4f8a0353208d7b408854010000000000000000000000000000000000ffff0599e92a208dc62f8854010000000000000000000000000000000000ffff46236049208dc4d58754010000000000000000000000000000000000ffff4400384d208d0ddb4c54010000000000000000000000000000000000ffff71653b5a208d53388854010000000000000000000000000000000000ffff43bddf66208d7c388854010000000000000000000000000000000000ffff47edca96208d8c068854010000000000000000000000000000000000ffffdded3c2c208d89f60554010000000000000000000000000000000000ffff4e60d8d6208da5ff1154010000000000000000000000000000000000ffff55983c68208d72f58754010000000000000000000000000000000000ffffce47f56a208d7e2d8854010000000000000000000000000000000000ffff17e25c12208da7ca8754010000000000000000000000000000000000ffff051d0baa208d19af8754010000000000000000000000000000000000ffffad42d883208dffd68754010000000000000000000000000000000000ffff5418ff88208d87bf8754010000000000000000000000000000000000ffff4c1fe0dc208deef18754010000000000000000000000000000000000ffff4c71d406208d25868754010000000000000000000000000000000000ffffda67a475208d4d348854010000000000000000000000000000000000ffff5d561269208da5918754010000000000000000000000000000000000ffff42b163b3208dbfac3754010000000000000000000000000000000000ffffad4e01eb208d5c408854010000000000000000000000000000000000ffffcc0bb91e208d4a928754010000000000000000000000000000000000ffff531dba30208db9fd8754010000000000000000000000000000000000ffffa3f72b33208dade58754010000000000000000000000000000000000ffffd8b93aeb208db75d4d54010000000000000000000000000000000000ffffae1d5595208de8d18754010000000000000000000000000000000000ffff4405665c208d88e78754010000000000000000000000000000000000ffff6b96180a208dae667354010000000000000000000000000000000000ffff531f490a208d92f98754010000000000000000000000000000000000ffffd447e857208db1428854010000000000000000000000000000000000ffff36c6b4bb208dd42c8854010000000000000000000000000000000000ffff4b4aff14208df71acd53010000000000000000000000000000000000ffff62d17930208da0b88754010000000000000000000000000000000000ffff974bf913208d5faa7953010000000000000000000000000000000000ffff7419d269208d6c1e8854010000000000000000000000000000000000ffff48a72343208d9c2b8854010000000000000000000000000000000000ffff64431e87208de8908754010000000000000000000000000000000000ffff44660d45208d410b7d54010000000000000000000000000000000000ffff43a3340d208d5b298854010000000000000000000000000000000000ffff9ffda678208de7238854010000000000000000000000000000000000ffff189a3759208da5745354010000000000000000000000000000000000ffff57026e31208df23c885401000000000000002a010488006710000523fbe100000001208dddf48754010000000000000000000000000000000000ffffc1531c5b208d61888754010000000000000000000000000000000000ffff42cd8bc1208df92e8854010000000000000000000000000000000000ffff36e1e970208d0cf75754010000000000000000000000000000000000ffffad4e2420208d0f028854010000000000000000000000000000000000ffff6ec61b02208de6228854010000000000000000000000000000000000ffffdcaa807c208d1cac8754010000000000000000000000000000000000ffff6179416c208db6068854010000000000000000000000000000000000ffffd46121e4208d7ea58754010000000000000000000000000000000000ffff80a41679208d91216254010000000000000000000000000000000000ffff69ece091208d0bf78754010000000000000000000000000000000000ffff45320c0b208dd03c8854010000000000000000000000000000000000ffff4859a2a5208d8e308854010000000000000000000000000000000000ffff2e1ce15a208d30f36354010000000000000000000000000000000000ffff4b61ee15208d7aa05553010000000000000000000000000000000000ffffb129186b208dbb238854010000000000000000000000000000000000ffff581a7f6f208d41248754010000000000000000000000000000000000ffffb009055b208dee218854010000000000000000000000000000000000ffff2e7771f9208db4e78754010000000000000000000000000000000000ffff411bf6ee208db5178854010000000000000000000000000000000000ffffacff001b208dbcf487540100000000000000200100009d386ab83061cfabfe3513a3208d76048854010000000000000000000000000000000000ffff2e35dbc2208d83d28754010000000000000000000000000000000000ffffd91bb1d5208d5c3d8854010000000000000000000000000000000000ffff32a47969208df4bd875401000000000000002a0104f8012032250000000000000002208d1bfa8754010000000000000000000000000000000000ffff53a2f4b6208d141c4554010000000000000000000000000000000000ffffb27b8281208d580c7e54010000000000000000000000000000000000ffffc9ea1340208df7228854010000000000000000000000000000000000ffffd90a268d208d41e18754010000000000000000000000000000000000ffff555b8809208d66358854010000000000000000000000000000000000ffff5280ff23208deecd8754010000000000000000000000000000000000ffff84c6a102208df9bc8754010000000000000000000000000000000000ffffbbc96dae208d9d0a8854010000000000000000000000000000000000ffffb2a2d18a208da9458854010000000000000000000000000000000000ffffca16c30e208d2ca88754010000000000000000000000000000000000ffff6baab63e208d923b8854010000000000000000000000000000000000ffffd5b87bee208d12258854010000000000000000000000000000000000ffff59ee408b208d980e8854010000000000000000000000000000000000ffff2edfb0c4208d33378854010000000000000000000000000000000000ffff6caa7b42208d41176d53010000000000000000000000000000000000ffff1fa2df68208dd1c57f54010000000000000000000000000000000000ffffbeab679a208d17c78754010000000000000000000000000000000000ffff3f983f51208dbb278854010000000000000000000000000000000000ffff568dbc28208d5d5ad953010000000000000000000000000000000000ffff0ec112b7208da1b78754010000000000000000000000000000000000ffffbc287698208d99348854010000000000000000000000000000000000ffff5ff1a302208d23ef8454010000000000000000000000000000000000ffff7ba55d22208d90e98754010000000000000000000000000000000000ffff5ae14503208d58f48754010000000000000000000000000000000000ffffd45a3cae208d530c8854010000000000000000000000000000000000ffff18fb80c4208dc6138854010000000000000000000000000000000000ffff4e2fd6eb208d97208854010000000000000000000000000000000000ffff55ddd5b8208d74f88654010000000000000000000000000000000000ffff1809ad86208dc0278854010000000000000000000000000000000000ffff6ef2dfb6208d84338854010000000000000000000000000000000000ffff58d8114e208dcae78754010000000000000000000000000000000000ffffc21c474c208d61e78754010000000000000000000000000000000000ffffd5b3fc7a208d7a078854010000000000000000000000000000000000ffff32740193208de7547b54010000000000000000000000000000000000ffff8046aac3208db5328854010000000000000000000000000000000000ffff5b0c5366208d5f208854010000000000000000000000000000000000ffff6caa054a208d1d682854010000000000000000000000000000000000ffff69e3eb9d208d179e6b54010000000000000000000000000000000000ffff54aac628208ded2e8854010000000000000000000000000000000000ffff4166d226208d8e148854010000000000000000000000000000000000ffff54305af6208d1ed88754010000000000000000000000000000000000ffff3ed2b2a4208d78ec8554010000000000000000000000000000000000ffff6dd2e575208ddd3e7854010000000000000000000000000000000000ffff1b83a102208d421e8854010000000000000000000000000000000000ffff2eb51bf1208dc01f8854010000000000000000000000000000000000ffff58c66033208d08ce8754010000000000000000000000000000000000ffff5d57b84c208df22e8854010000000000000000000000000000000000ffffbf213e3d208d89cb8754010000000000000000000000000000000000ffff53d7ece1208da9d68754010000000000000000000000000000000000ffff71bef412208d38268854010000000000000000000000000000000000ffff84fc8a6f208d4f107254010000000000000000000000000000000000ffff57a072d9208d990f88540100000000000000200100009d3890d71029db21863020f9208df3c36854010000000000000000000000000000000000ffffbba6a6c4208d3ea71f54010000000000000000000000000000000000ffff3e61237b208d60a18754010000000000000000000000000000000000ffff53a56b4b208db2328854010000000000000000000000000000000000ffff44eef265208d2c0f8854010000000000000000000000000000000000ffff566553c0208dec268854010000000000000000000000000000000000ffff57a69ecd208d93148854010000000000000000000000000000000000ffff9b8f4433208d741b8854010000000000000000000000000000000000ffffae17c74c208ddab94f54010000000000000000000000000000000000ffffab19c63a208d5e886f54010000000000000000000000000000000000ffffbc67a037208d5c3ccc53010000000000000000000000000000000000ffff0e77c86e208d26fb8754010000000000000000000000000000000000ffffc1eae172208d61278854010000000000000000000000000000000000ffff5ce15439208d60df8754010000000000000000000000000000000000ffff2599fb3b208d52418854010000000000000000000000000000000000ffff028710e6208dae448854010000000000000000000000000000000000ffff18152eee208d6ff18754010000000000000000000000000000000000ffff542e396d208d8c248854010000000000000000000000000000000000ffff5519d6d8208d01e33e54010000000000000000000000000000000000ffff5027d563208d05cd8754010000000000000000000000000000000000ffff45a5a9c5208d7c1e8854010000000000000000000000000000000000ffff68c813c8208d38a48754010000000000000000000000000000000000ffff3a60a934208d73ec8754010000000000000000000000000000000000ffffae3db8f3208d096c8754010000000000000000000000000000000000ffff4cb88802208d55d38754010000000000000000000000000000000000ffffd31f082e208d0d8a8754010000000000000000000000000000000000ffff6c22c9bb208d81ba7754010000000000000000000000000000000000ffffca5f88da208d5df88754010000000000000000000000000000000000ffffc31abc05208db1d58754010000000000000000000000000000000000ffff5cf614c8208d2b257c54010000000000000000000000000000000000ffff45f9baa0208d38d78754010000000000000000000000000000000000ffff490e0608208d27e88754010000000000000000000000000000000000ffff17f1ccd0208d501c8854010000000000000000000000000000000000ffff5be834a7208d860b8854010000000000000000000000000000000000ffff640173bc208deada8754010000000000000000000000000000000000ffffdc98f8db208d983d3c54010000000000000000000000000000000000ffff021ab398208d99f28754010000000000000000000000000000000000ffff59a9e417208d19468854010000000000000000000000000000000000ffff91ff01a1208ddbf68754010000000000000000000000000000000000ffff50048949208d89118854010000000000000000000000000000000000ffffc8366923208dea3b8854010000000000000000000000000000000000ffffbcbbb5a9208d364a8854010000000000000000000000000000000000ffffc654bd58208dd52ef953010000000000000000000000000000000000ffff7cabb7bc208db086cc53010000000000000000000000000000000000ffff6c4004ea208d0ff48754010000000000000000000000000000000000ffff0536a3cb208d1c0b8854010000000000000000000000000000000000ffffd90bfef3208dbf188854010000000000000000000000000000000000ffff0252431f208dd1c78754010000000000000000000000000000000000ffff25732b19208dd6fa3954010000000000000000000000000000000000ffff6c1131dc208da6108854010000000000000000000000000000000000ffff1fb9b748208daf368854010000000000000000000000000000000000ffff1f2a2981208d9e2f8854010000000000000000000000000000000000ffffbc658482208d6a458854010000000000000000000000000000000000ffff568b843a208dcd428854010000000000000000000000000000000000ffff5d820794208d38258854010000000000000000000000000000000000ffff6440c630208d1cda8754010000000000000000000000000000000000ffff6e9f7be5208ddd388854010000000000000000000000000000000000ffff9f080244208d2e2d8854010000000000000000000000000000000000ffffbb70cf3d208d3e0c8854010000000000000000000000000000000000ffff3ec27a16208d89098854010000000000000000000000000000000000ffffb55fb2fb208d13468854010000000000000000000000000000000000ffff180ab1e5208dfc2a8854010000000000000000000000000000000000ffffd58a5c0e208db9188854010000000000000000000000000000000000ffff81ce808d208d03d06b54010000000000000000000000000000000000ffff4b496858208d319e8454010000000000000000000000000000000000ffff531fac9f208dc8d98754010000000000000000000000000000000000ffffc113e4ea208d06395754010000000000000000000000000000000000ffff7cabf0d6208d722b8854010000000000000000000000000000000000ffff545db4cd208dd91f8854010000000000000000000000000000000000ffff5f12a597208dfbd57054010000000000000000000000000000000000ffff3cf654e6208dd7358854010000000000000000000000000000000000ffffb4998e79208dd7018854010000000000000000000000000000000000ffffb45cc220208d86df5554010000000000000000000000000000000000ffff7660d462208decd28754010000000000000000000000000000000000ffff5b7bdfe2208dafe28754010000000000000000000000000000000000ffff2e76899e208d0c318854010000000000000000000000000000000000ffffc7bcb183208d35258854010000000000000000000000000000000000ffff3ab2d076208db5c38754010000000000000000000000000000000000ffff48b24d30208ddfd18754010000000000000000000000000000000000ffff904cb00c208d40478854010000000000000000000000000000000000ffff02a3b787208deb318854010000000000000000000000000000000000ffffd2564026208de63f8854010000000000000000000000000000000000ffff615d16c0208dbf1d8854010000000000000000000000000000000000ffffc6fff6f0208d4aee8754010000000000000000000000000000000000ffff56c74bc8208d68bc8754010000000000000000000000000000000000ffff31b581f9208ddcca8754010000000000000000000000000000000000ffff0e97397a208db2318854010000000000000000000000000000000000ffffc766348d208de1bd8754010000000000000000000000000000000000ffffd5927a3d208d2aff8754010000000000000000000000000000000000ffff5152d0a5208da7feb753010000000000000000000000000000000000ffff53fe1694208de7418854010000000000000000000000000000000000ffff189f3d99208de7b38754010000000000000000000000000000000000ffff64004655208dacabb653010000000000000000000000000000000000ffff55413231208d43b88754010000000000000000000000000000000000ffff796e1029208d03788754010000000000000000000000000000000000ffff49b90ff6208d04be8754010000000000000000000000000000000000ffffc327ce1c208d33648254010000000000000000000000000000000000ffff2e896478208d9e308854010000000000000000000000000000000000ffffd395d9bf208df13e8854010000000000000000000000000000000000ffff51bb88ed208dec8c5653010000000000000000000000000000000000ffff557f146e208df4538854010000000000000000000000000000000000ffff48d05928208d41bd8154010000000000000000000000000000000000ffff47384162208df6428854010000000000000000000000000000000000ffffc1566314208d753e8854010000000000000000000000000000000000ffff440c9b4f208d2f078854010000000000000000000000000000000000ffff56021a19208d7b0f4754010000000000000000000000000000000000ffff71653a84208dcee88754010000000000000000000000000000000000ffff5401a332208df4db8754010000000000000000000000000000000000ffff5a9db0e3208df8e38754010000000000000000000000000000000000ffff5d5208fa208d2e298854010000000000000000000000000000000000ffff4c61c0bc208d42ea8754010000000000000000000000000000000000ffff4c405ae7208deade8754010000000000000000000000000000000000ffffc009c823208d99358854010000000000000000000000000000000000ffff6cc2b493208df3fd8754010000000000000000000000000000000000ffff904c6007208d1fde8754010000000000000000000000000000000000ffff92732ae8208d666e8354010000000000000000000000000000000000ffff6daa9d16208d18f78754010000000000000000000000000000000000ffff4d6d8d8a208ddea9ce53010000000000000000000000000000000000ffff17e26f7d208d1ae48754010000000000000000000000000000000000ffffc32ebb89208dee258854010000000000000000000000000000000000ffff4a0f10e1208d290a8854010000000000000000000000000000000000ffff42731593208d7cb33354010000000000000000000000000000000000ffff532e8b97208dfd1e8854010000000000000000000000000000000000ffff6ee91680208d480a1354010000000000000000000000000000000000ffff60f220f6208d55838754010000000000000000000000000000000000ffff1f36d48f208d247e8754010000000000000000000000000000000000ffff44b5a49a208d0efa4954010000000000000000000000000000000000ffff63890597208d862e8854010000000000000000000000000000000000ffffc14d8751208d443c8854010000000000000000000000000000000000ffff70d16dba208d72fe8754010000000000000000000000000000000000ffff55983dd3208d236b6854010000000000000000000000000000000000ffff7aea27f9208d67e08754010000000000000000000000000000000000ffff1b20750d208df10a7154010000000000000000000000000000000000ffff7b9fe56e208db32d8854010000000000000000000000000000000000ffff7ba42d54208db3f58754010000000000000000000000000000000000ffff027b4eba208d70d68754010000000000000000000000000000000000ffffbc1b62b9208d97951454010000000000000000000000000000000000ffffb4b6af10208d251288540100000000000000200100009d386ab800773149c4d7e673208d7b168854010000000000000000000000000000000000ffff58969392208d91d18754010000000000000000000000000000000000ffff9ffd6d4e208d19008854010000000000000000000000000000000000ffff4e926cb8208dd1c98754010000000000000000000000000000000000ffff546cdb77208d6a0a8854010000000000000000000000000000000000ffff6caa8c15208d3a3a8854010000000000000000000000000000000000ffff62e28d6a208d3fe48754010000000000000000000000000000000000ffffd9850be7208dcb438854010000000000000000000000000000000000ffff6c2444b3208d78338854010000000000000000000000000000000000ffffb2df574a208d3b2d3054010000000000000000000000000000000000ffff4b529e67208d5b988754010000000000000000000000000000000000ffff36e152e6208d01ab8754010000000000000000000000000000000000ffff58b531bf208d7b038854010000000000000000000000000000000000ffff54717a6f208db0388854010000000000000000000000000000000000ffff4a694e98208d65418854010000000000000000000000000000000000ffffcef84b25208dcf218854010000000000000000000000000000000000ffff5364fed9208db6c68754010000000000000000000000000000000000ffff4d03decd208dc6158854010000000000000000000000000000000000ffffb9044c79208d65418854010000000000000000000000000000000000ffff56b2192f208d23308754010000000000000000000000000000000000ffffd973f7a4208d05158854010000000000000000000000000000000000ffff1b6d9975208d52cd8754010000000000000000000000000000000000ffffdf5b92ad208d3a128854010000000000000000000000000000000000ffff6d93787e208d351e8854010000000000000000000000000000000000ffffb6a4336f208d9bd58754010000000000000000000000000000000000ffff3ba7f521208dc3288854010000000000000000000000000000000000ffff6bbf20cc208d68998754010000000000000000000000000000000000ffff63b30032208d86848754010000000000000000000000000000000000ffffb52ef15c208dff108854010000000000000000000000000000000000ffff4d06718b208d226f0754010000000000000000000000000000000000ffff44248ba1208da3af7354010000000000000000000000000000000000ffff86f9d72c208d9fd68754010000000000000000000000000000000000ffff521f2444208dae428854010000000000000000000000000000000000ffffd445b025208dfafc8754010000000000000000000000000000000000ffffd447fa84208dbb358854010000000000000000000000000000000000ffff3ee2d196208da01a8854010000000000000000000000000000000000ffff45a615b2208d32edb053010000000000000000000000000000000000ffff6d6494da208d77ed8754010000000000000000000000000000000000ffff555dcc44208d09608754010000000000000000000000000000000000ffff45a5d222208d84558754010000000000000000000000000000000000ffffb01c30ab208d71fe8754010000000000000000000000000000000000ffffae5f68c2208de0acdf53010000000000000000000000000000000000ffffae737936208d29e18754010000000000000000000000000000000000ffffb00a63cb208d220b8854010000000000000000000000000000000000ffff9ec4d12f208db4408854010000000000000000000000000000000000ffff4c6d9bcc208d65a58754010000000000000000000000000000000000ffff5962c453208d2d628854010000000000000000000000000000000000ffff257191d50000" + "getheaders": { + "message": "f9beb4d967657468656164657273000045000000a2165cd97011010001b91ddbbfc801b7fe6f470ce9528f98f01b496b53f23c41130000000000000000a1112367496906e6d89dc1cfdbf33a95cb8a1270f59e530b0000000000000000" }, - "PING": { - "message": "f9beb4d9706f6e67000000000000000008000000c6466f1e6b86480ae969867c", - "payload": "6b86480ae969867c" + "headers": { + "message": "f9beb4d9686561646572730000000000f400000043385d010302000000b91ddbbfc801b7fe6f470ce9528f98f01b496b53f23c411300000000000000004901c9d18d0a468b20cc62ddf75aee58cf410440ea390300bf7a5f6848be350508d4cb54c0a31a18b9f661ec0002000000a02d6472e3e6fc9a1cebeaad14a90208a715e2bd234ea00600000000000000006f596f650fbbd5478489c66d651c9e3ea56f394d1f1481f90975cf0c8dda45fd3ad4cb54c0a31a1872e262a200020000002e4db38f1970099bf21335edd604e7a591213e189d1d1806000000000000000031a30091f5bdbca8958d2c4ccc0bfa9df93e2a3ea4d00e03222a663179db90a756d6cb54c0a31a187947855000" }, - "PONG": { - "message": "f9beb4d9706f6e67000000000000000008000000c6466f1e6b86480ae969867c", - "payload": "6b86480ae969867c" + "filterload": { + "message": "f9beb4d966696c7465726c6f61640000210000002ef97a71170000000000000000000000000000000000000000000000060000000000000000" }, - "FILTERLOAD": { - "message": "f9beb4d966696c7465726c6f61640000210000002ef97a71170000000000000000000000000000000000000000000000060000000000000000", - "payload": "170000000000000000000000000000000000000000000000060000000000000000" + "filteradd": { + "message": "f9beb4d966696c746572616464000000150000009727ea0a1499108ad8ed9bb6274d3980bab5a85c048f0950c8" }, - "FILTERADD": { - "message": "f9beb4d966696c7465726c6f61640000150000009727ea0a1499108ad8ed9bb6274d3980bab5a85c048f0950c8", - "payload": "1499108ad8ed9bb6274d3980bab5a85c048f0950c8" + "filterclear": { + "message": "f9beb4d966696c746572636c65617200000000005df6e0e2" }, - "FILTERCLEAR": { - "message": "f9beb4d966696c7465726c6365617200000000005df6e0e2", - "payload": "" + "merkleblock": { + "message": "f9beb4d96d65726b6c65626c6f636b00d7000000365913480100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b852907000000043612262624047ee87660be1a707519a443b1c1ce3d248cbfc6c15870f6c5daa2019f5b01d4195ecbc9398fbf3c3b1fa9bb3183301d7a1fb3bd174fcfa40a2b6541ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d06820d2a7bc994987302e5b1ac80fc425fe25f8b63169ea78e68fbaaefa59379bbf011d" }, - "MERKLEBLOCK": { - "message": "f9beb4d96d65726b6c65626c6f636b00d7000000365913480100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b852907000000043612262624047ee87660be1a707519a443b1c1ce3d248cbfc6c15870f6c5daa2019f5b01d4195ecbc9398fbf3c3b1fa9bb3183301d7a1fb3bd174fcfa40a2b6541ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d06820d2a7bc994987302e5b1ac80fc425fe25f8b63169ea78e68fbaaefa59379bbf011d", - "payload": "0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b852907000000043612262624047ee87660be1a707519a443b1c1ce3d248cbfc6c15870f6c5daa2019f5b01d4195ecbc9398fbf3c3b1fa9bb3183301d7a1fb3bd174fcfa40a2b6541ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d06820d2a7bc994987302e5b1ac80fc425fe25f8b63169ea78e68fbaaefa59379bbf011d" + "getaddr": { + "message": "f9beb4d9676574616464720000000000000000005df6e0e2" + }, + "mempool": { + "message": "f9beb4d96d656d706f6f6c0000000000000000005df6e0e2" } } diff --git a/test/inventory.js b/test/inventory.js new file mode 100644 index 00000000..fde8969b --- /dev/null +++ b/test/inventory.js @@ -0,0 +1,96 @@ +'use strict'; + +/*jshint immed: false */ + +var should = require('chai').should(); + +var bitcore = require('bitcore'); +var P2P = require('../'); +var Inventory = P2P.Inventory; +var BufferUtils = bitcore.util.buffer; +var BufferWriter = bitcore.encoding.BufferWriter; + +describe('Inventory', function() { + + var hash = new Buffer('eb951630aba498b9a0d10f72b5ea9e39d5ff04b03dc2231e662f52057f948aa1', 'hex'); + var hashedStr = BufferUtils.reverse(new Buffer(hash, 'hex')).toString('hex'); + var inventoryBuffer = new Buffer( + '01000000eb951630aba498b9a0d10f72b5ea9e39d5ff04b03dc2231e662f52057f948aa1', + 'hex' + ); + + describe('@constructor', function() { + it('should create inventory', function() { + var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hash}); + should.exist(inventory); + }); + + it('should error with string hash', function() { + (function() { + var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hashedStr}); + should.not.exist(inventory); + }).should.throw('Unexpected hash'); + }); + + }); + + describe('#forItem', function() { + it('should handle a string hash (reversed)', function() { + var inventory = Inventory.forItem(Inventory.TYPE.TX, hashedStr); + should.exist(inventory); + inventory.hash.should.deep.equal(new Buffer(hash, 'hex')); + }); + + }); + + describe('#forBlock', function() { + it('should use correct block type', function() { + var inventory = Inventory.forBlock(hash); + should.exist(inventory); + inventory.type.should.equal(Inventory.TYPE.BLOCK); + }); + }); + + describe('#forFilteredBlock', function() { + it('should use correct filtered block type', function() { + var inventory = Inventory.forFilteredBlock(hash); + should.exist(inventory); + inventory.type.should.equal(Inventory.TYPE.FILTERED_BLOCK); + }); + }); + + describe('#forTransaction', function() { + it('should use correct filtered tx type', function() { + var inventory = Inventory.forTransaction(hash); + should.exist(inventory); + inventory.type.should.equal(Inventory.TYPE.TX); + }); + }); + + describe('#toBuffer', function() { + it('should serialize correctly', function() { + var inventory = Inventory.forTransaction(hash); + var buffer = inventory.toBuffer(); + buffer.should.deep.equal(inventoryBuffer); + }); + }); + + describe('#toBufferWriter', function() { + it('should write to a buffer writer', function() { + var bw = new BufferWriter(); + var inventory = Inventory.forTransaction(hash); + inventory.toBufferWriter(bw); + bw.concat().should.deep.equal(inventoryBuffer); + }); + }); + + describe('#fromBuffer', function() { + it('should deserialize a buffer', function() { + var inventory = Inventory.fromBuffer(inventoryBuffer); + should.exist(inventory); + inventory.type.should.equal(Inventory.TYPE.TX); + inventory.hash.should.deep.equal(hash); + }); + }); + +}); diff --git a/test/message.js b/test/message.js new file mode 100644 index 00000000..972cc466 --- /dev/null +++ b/test/message.js @@ -0,0 +1,29 @@ +'use strict'; + +var should = require('chai').should(); +var P2P = require('../'); +var Message = P2P.Message; + +describe('P2P Message', function() { + + describe('@constructor', function() { + it('should construct with magic number and command', function() { + var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'}); + message.command.should.equal('command'); + message.magicNumber.should.equal(0xd9b4bef9); + }); + }); + + describe('#toBuffer', function() { + it('should serialize to a buffer', function() { + var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'}); + message.getPayload = function() { + return new Buffer(0); + }; + var buffer = message.toBuffer(); + var expectedBuffer = new Buffer('f9beb4d9636f6d6d616e640000000000000000005df6e0e2', 'hex'); + buffer.should.deep.equal(expectedBuffer); + }); + }); + +}); diff --git a/test/messages.js b/test/messages.js index 39904800..1d01d2ba 100644 --- a/test/messages.js +++ b/test/messages.js @@ -1,160 +1,79 @@ 'use strict'; var chai = require('chai'); - var should = chai.should(); var Buffers = require('buffers'); -var bitcore = require('bitcore'); -var Data = require('./data/messages'); var P2P = require('../'); -var BloomFilter = P2P.BloomFilter; +var messages = P2P.messages; var Messages = P2P.Messages; -var Networks = bitcore.Networks; -var BufferUtils = bitcore.util.buffer; - -var network = Networks.livenet; +var bitcore = require('bitcore'); describe('Messages', function() { - var commands = { - Version: 'version', - VerAck: 'verack', - Inventory: 'inv', - Addresses: 'addr', - Ping: 'ping', - Pong: 'pong', - Alert: 'alert', - Reject: 'reject', - Block: 'block', - MerkleBlock: 'merkleblock', - FilterLoad: 'filterload', - FilterAdd: 'filteradd', - FilterClear: 'filterclear', - GetBlocks: 'getblocks', - GetHeaders: 'getheaders', - GetData: 'getdata', - GetAddresses: 'getaddr', - Headers: 'headers', - Transaction: 'tx', - NotFound: 'notfound' + var buildMessage = function(hex) { + var m = Buffers(); + m.push(new Buffer(hex, 'hex')); + return m; }; - // TODO: add data for these - var noPayload = ['Reject', 'GetBlocks', 'GetHeaders']; - var names = Object.keys(commands); - describe('named', function() { - names.forEach(function(name) { - var command = commands[name]; - var data = Data[command.toUpperCase()]; - - it('should have data for ' + name, function() { - should.exist(data); - }); - it('command for name ' + name, function() { - Messages.Message.COMMANDS[command].should.equal(Messages[name]); + describe('@constructor', function() { + it('sets properties correctly', function() { + var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + var messages = new Messages({ + magicNumber: magicNumber, + Block: bitcore.Block, + Transaction: bitcore.Transaction }); + should.exist(messages.commands); + messages.magicNumber.should.equal(magicNumber); - describe(name, function() { - var message = new Messages[name](); - it('should be able to create instance', function() { - message.command.should.equal(command); - }); + // check that commands are mapped as messages + for(var key in messages.commands) { + messages[key].should.equal(messages.commands[key]); + } - it('should be able to serialize the payload', function() { - var payload = message.getPayload(); - should.exist(payload); - }); - - it('should be able to serialize the message', function() { - var buffer = message.serialize(Networks.livenet); - should.exist(buffer); - }); - - if (noPayload.indexOf(name) === -1) { - it('should be able to parse payload', function() { - var payload = new Buffer(data.payload, 'hex'); - var m = new Messages[name]().fromBuffer(payload); - should.exist(m); - }); - } - }); }); }); - var buildMessage = function(hex) { - var m = Buffers(); - m.push(new Buffer(hex, 'hex')); - return m; - }; - it('fails with invalid command', function() { - var invalidCommand = 'f9beb4d96d616c6963696f757300000025000000bd5e830c' + - '0102000000ec3995c1bf7269ff728818a65e53af00cbbee6b6eca8ac9ce7bc79d87' + - '7041ed8'; - var fails = function() { - Messages.parseMessage(network, buildMessage(invalidCommand)); - }; - fails.should.throw('Unsupported message command: malicious'); - }); - - it('ignores malformed messages', function() { - var malformed1 = 'd8c4c3d976657273696f6e000000000065000000fc970f1772110' + - '1000100000000000000ba6288540000000001000000000000000000000000000000' + - '0000ffffba8886dceab0010000000000000000000000000000000000ffff0509552' + - '2208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001'; - var malformed2 = 'f9beb4d967657464617461000000000089000000d88134740102' + - '0000006308e4a380c949dbad182747b0f7b6a89e874328ca41f37287f74a81b8f84' + - '86d'; - var malformed3 = 'f9beb4d967657464617461000000000025000000616263640102' + - '00000069ebcbc34a4f9890da9aea0f773beba883a9afb1ab9ad7647dd4a1cd346c3' + - '728'; - [malformed1, malformed2, malformed3].forEach(function(malformed) { - var ret = Messages.parseMessage(network, buildMessage(malformed)); - should.not.exist(ret); + describe('#parseMessage', function() { + it('fails with invalid command', function() { + var invalidCommand = 'f9beb4d96d616c6963696f757300000025000000bd5e830c' + + '0102000000ec3995c1bf7269ff728818a65e53af00cbbee6b6eca8ac9ce7bc79d87' + + '7041ed8'; + var fails = function() { + messages.parseMessage(buildMessage(invalidCommand)); + }; + fails.should.throw('Unsupported message command: malicious'); }); - }); - it('Inventory#from family methods work', function() { - var hash = 'eb951630aba498b9a0d10f72b5ea9e39d5ff04b03dc2231e662f52057f948aa1'; - [Messages.Inventory, Messages.GetData, Messages.NotFound].forEach(function(clazz) { - var b = clazz.forBlock(hash); - var mb = clazz.forMerkleBlock(hash); - (b instanceof clazz).should.equal(true); - var t = clazz.forTransaction(hash); - (t instanceof clazz).should.equal(true); - clazz.forBlock(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(b); - clazz.forMerkleBlock(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(mb); - clazz.forFilteredBlock(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(mb); - clazz.forTransaction(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(t); + it('ignores malformed messages', function() { + var malformed1 = 'd8c4c3d976657273696f6e000000000065000000fc970f1772110' + + '1000100000000000000ba6288540000000001000000000000000000000000000000' + + '0000ffffba8886dceab0010000000000000000000000000000000000ffff0509552' + + '2208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001'; + var malformed2 = 'f9beb4d967657464617461000000000089000000d88134740102' + + '0000006308e4a380c949dbad182747b0f7b6a89e874328ca41f37287f74a81b8f84' + + '86d'; + var malformed3 = 'f9beb4d967657464617461000000000025000000616263640102' + + '00000069ebcbc34a4f9890da9aea0f773beba883a9afb1ab9ad7647dd4a1cd346c3' + + '728'; + [malformed1, malformed2, malformed3].forEach(function(malformed) { + var ret = messages.parseMessage(buildMessage(malformed)); + should.not.exist(ret); + }); }); - }); - it('Version#fromBuffer works w/o fRelay arg', function() { - var messageHex = Data.VERSION_NO_FRELAY.payload; - var message = new Messages.Version() - .fromBuffer(new Buffer(messageHex, 'hex')); }); - it('Version#relay setting works', function() { - [true,false].forEach(function(relay) { - var message = new Messages.Version(null,null,relay); - message.relay.should.equal(relay); - var messageBuf = message.getPayload(); - var newMessage = new Messages.Version().fromBuffer(messageBuf) - newMessage.relay.should.equal(relay); - }); + describe('#discardUntilNextMessage', function() { }); - it('FilterLoad#fromBuffer method works', function() { - var testPayload = Data.FILTERLOAD.payload; - var msg = new Messages.FilterLoad().fromBuffer(new Buffer(testPayload, 'hex')); - msg.getPayload().toString('hex').should.equal(testPayload); + describe('#buildFromBuffer', function() { }); - it('MerkleBlock#fromBuffer method works', function() { - var testPayload = Data.MERKLEBLOCK.payload; - var msg = new Messages.MerkleBlock().fromBuffer(new Buffer(testPayload, 'hex')); - msg.getPayload().toString('hex').should.equal(testPayload); + describe('#build/#buildFromObject', function() { }); + }); diff --git a/test/peer.js b/test/peer.js index 30a2aae0..e575b648 100644 --- a/test/peer.js +++ b/test/peer.js @@ -12,8 +12,8 @@ var fs = require('fs'); var bitcore = require('bitcore'); var _ = bitcore.deps._; -var p2p = require('../'); -var Peer = p2p.Peer; +var P2P = require('../'); +var Peer = P2P.Peer; var Networks = bitcore.Networks; describe('Peer', function() { @@ -52,7 +52,7 @@ describe('Peer', function() { return stub; }; peer.on('connect', function() { - dataCallback(fs.readFileSync('./test/connection.log')); + dataCallback(fs.readFileSync('./test/data/connection.log')); }); var check = function(message) { received[message.command]++; @@ -77,28 +77,28 @@ describe('Peer', function() { }); it('should be able to create instance setting a port', function() { - var peer = new Peer('localhost', 8111); + var peer = new Peer({host: 'localhost', port: 8111}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.livenet); peer.port.should.equal(8111); }); it('should be able to create instance setting a network', function() { - var peer = new Peer('localhost', Networks.testnet); + var peer = new Peer({host: 'localhost', network: Networks.testnet}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.testnet); peer.port.should.equal(Networks.testnet.port); }); it('should be able to create instance setting port and network', function() { - var peer = new Peer('localhost', 8111, Networks.testnet); + var peer = new Peer({host: 'localhost', port: 8111, network: Networks.testnet}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.testnet); peer.port.should.equal(8111); }); it('should support creating instance without new', function() { - var peer = Peer('localhost', 8111, Networks.testnet); + var peer = Peer({host: 'localhost', port: 8111, network: Networks.testnet}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.testnet); peer.port.should.equal(8111); @@ -122,17 +122,17 @@ describe('Peer', function() { }); it('Peer.relay setting set properly', function() { - var peer = new Peer('localhost'); + var peer = new Peer({host: 'localhost'}); peer.relay.should.equal(true); - var peer2 = new Peer('localhost', null, null, false); + var peer2 = new Peer({host: 'localhost', relay: false}); peer2.relay.should.equal(false); - var peer3 = new Peer('localhost', null, null, true); + var peer3 = new Peer({host: 'localhost', relay: true}); peer3.relay.should.equal(true); }); it('Peer.relay setting respected', function() { [true,false].forEach(function(relay) { - var peer = new Peer('localhost', null, null, relay); + var peer = new Peer({host: 'localhost', relay: relay}); var peerSendMessageStub = sinon.stub(Peer.prototype, 'sendMessage', function(message) { message.relay.should.equal(relay); }); diff --git a/test/pool.js b/test/pool.js index b498d9a3..f71bcf12 100644 --- a/test/pool.js +++ b/test/pool.js @@ -10,13 +10,17 @@ var bitcore = require('bitcore'); var P2P = require('../'); var Peer = P2P.Peer; var MessagesData = require('./data/messages'); -var Messages = P2P.Messages; +var messages = P2P.messages; var Pool = P2P.Pool; var Networks = bitcore.Networks; var dns = require('dns'); var sinon = require('sinon'); +function getPayloadBuffer(messageBuffer) { + return new Buffer(messageBuffer.slice(48), 'hex'); +} + describe('Pool', function() { it('should be able to create instance', function() { @@ -95,8 +99,8 @@ describe('Pool', function() { // mock a addr peer event var peerMessageStub = sinon.stub(Peer.prototype, '_readMessage', function() { - var payload = new Buffer(MessagesData.ADDR.payload, 'hex'); - var message = new Messages.Addresses().fromBuffer(payload); + var payloadBuffer = getPayloadBuffer(MessagesData.addr.message); + var message = messages.buildFromBuffer('addr', payloadBuffer); this.emit(message.command, message); }); @@ -145,8 +149,8 @@ describe('Pool', function() { // mock a addr peer event var peerMessageStub = sinon.stub(Peer.prototype, '_readMessage', function() { - var payload = new Buffer(MessagesData.ADDR.payload, 'hex'); - var message = new Messages.Addresses().fromBuffer(payload); + var payloadBuffer = getPayloadBuffer(MessagesData.addr.message); + var message = messages.buildFromBuffer('addr', payloadBuffer); this.emit(message.command, message); }); From 0b20724288044eb7ce0f6fb210f456ad3819b200 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Wed, 11 Mar 2015 17:15:00 -0400 Subject: [PATCH 02/48] Changed options for Pool --- lib/pool.js | 4 ++-- test/pool.js | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 9871f7bd..94e91d21 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -37,7 +37,7 @@ function now() { * @returns {Pool} * @constructor */ -function Pool(network, options) { +function Pool(options) { var self = this; @@ -51,7 +51,7 @@ function Pool(network, options) { this.dnsSeed = options.dnsSeed !== false; this.maxSize = options.maxSize || Pool.MaxConnectedPeers; this.messages = options.messages; - this.network = Networks.get(network) || Networks.defaultNetwork; + this.network = Networks.get(options.network) || Networks.defaultNetwork; this.relay = options.relay === false ? false : true; if (options.addrs) { diff --git a/test/pool.js b/test/pool.js index f71bcf12..d72c9053 100644 --- a/test/pool.js +++ b/test/pool.js @@ -32,7 +32,7 @@ describe('Pool', function() { }); it('should be able to create instance setting the network', function() { - var pool = new Pool(Networks.testnet); + var pool = new Pool({network: Networks.testnet}); pool.network.should.equal(Networks.testnet); }); @@ -40,7 +40,7 @@ describe('Pool', function() { var stub = sinon.stub(dns, 'resolve', function(seed, callback) { callback(null, ['10.10.10.1', '10.10.10.2', '10.10.10.3']); }); - var pool = new Pool(Networks.livenet); + var pool = new Pool({network: Networks.livenet}); pool.connect(); pool.disconnect(); pool._addrs.length.should.equal(3); @@ -52,6 +52,7 @@ describe('Pool', function() { throw new Error('DNS should not be called'); }); var options = { + network: Networks.livenet, dnsSeed: false, maxSize: 1, addrs: [ @@ -67,7 +68,7 @@ describe('Pool', function() { } ] }; - var pool = new Pool(Networks.livenet, options); + var pool = new Pool(options); pool.connect(); pool.disconnect(); pool._addrs.length.should.equal(2); @@ -76,6 +77,7 @@ describe('Pool', function() { it('will add addrs via options argument', function() { var options = { + network: Networks.livenet, dnsSeed: false, addrs: [ { @@ -85,7 +87,7 @@ describe('Pool', function() { } ] }; - var pool = new Pool(Networks.livenet, options); + var pool = new Pool(options); pool._addrs.length.should.equal(1); }); @@ -105,6 +107,7 @@ describe('Pool', function() { }); var options = { + network: Networks.testnet, dnsSeed: false, addrs: [ { @@ -115,7 +118,7 @@ describe('Pool', function() { ] }; - var pool = new Pool(Networks.testnet, options); + var pool = new Pool(options); // listen for the event pool.on('peeraddr', function(peer, message) { @@ -155,6 +158,7 @@ describe('Pool', function() { }); var options = { + network: Networks.testnet, dnsSeed: false, listenAddr: false, addrs: [ @@ -166,7 +170,7 @@ describe('Pool', function() { ] }; - var pool = new Pool(Networks.testnet, options); + var pool = new Pool(options); // listen for the event pool.on('peeraddr', function(peer, message) { @@ -200,7 +204,7 @@ describe('Pool', function() { }); var poolRemoveStub = sinon.stub(Pool.prototype, '_removeConnectedPeer', function() {}); - var pool = new Pool(null, { + var pool = new Pool({ dnsSeed: false, addrs: [ { @@ -241,7 +245,7 @@ describe('Pool', function() { this.emit('connect', this, {}); }); [true, false].forEach(function(relay) { - var pool = new Pool(null,{ relay: relay, dnsSeed: false }); + var pool = new Pool({relay: relay, dnsSeed: false}); pool._addAddr({ ip: { v4: 'localhost' } }); pool.on('peerconnect', function(peer, addr) { peer.relay.should.equal(relay); @@ -264,7 +268,7 @@ describe('Pool', function() { var dnsStub = sinon.stub(dns, 'resolve', function(seed, callback) { callback(new Error('A DNS error')); }); - var pool = new Pool(Networks.livenet, {maxSize: 1}); + var pool = new Pool({network: Networks.livenet, maxSize: 1}); pool.once('seederror', function(error) { should.exist(error); pool.disconnect(); @@ -278,7 +282,7 @@ describe('Pool', function() { var dnsStub = sinon.stub(dns, 'resolve', function(seed, callback) { callback(null, []); }); - var pool = new Pool(Networks.livenet, {maxSize: 1}); + var pool = new Pool({network: Networks.livenet, maxSize: 1}); pool.once('seederror', function(error) { should.exist(error); pool.disconnect(); From e8f07250810770fb3c7bca6fa15ed544c91c9fee Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Wed, 11 Mar 2015 22:29:40 -0400 Subject: [PATCH 03/48] Moved commands, message and messages to directory --- lib/index.js | 7 +------ lib/{ => messages}/commands.js | 4 ++-- lib/{messages.js => messages/index.js} | 5 +++-- lib/{ => messages}/message.js | 0 lib/pool.js | 2 ++ test/{ => messages}/commands.js | 8 ++++---- test/{ => messages}/message.js | 4 ++-- test/{ => messages}/messages.js | 4 ++-- test/pool.js | 3 ++- 9 files changed, 18 insertions(+), 19 deletions(-) rename lib/{ => messages}/commands.js (99%) rename lib/{messages.js => messages/index.js} (94%) rename lib/{ => messages}/message.js (100%) rename test/{ => messages}/commands.js (92%) rename test/{ => messages}/message.js (92%) rename test/{ => messages}/messages.js (97%) diff --git a/lib/index.js b/lib/index.js index e7a608b4..f4f5552e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,15 +2,10 @@ * @namespace P2P */ -var Messages = require('./messages'); - module.exports = { - Message: require('./message'), - Commands: require('./commands'), Inventory: require('./inventory'), BloomFilter: require('./bloomfilter'), - Messages: Messages, - messages: new Messages(), + Messages: require('./messages'), Peer: require('./peer'), Pool: require('./pool') }; diff --git a/lib/commands.js b/lib/messages/commands.js similarity index 99% rename from lib/commands.js rename to lib/messages/commands.js index 5086e00a..1e34f353 100644 --- a/lib/commands.js +++ b/lib/messages/commands.js @@ -1,14 +1,14 @@ 'use strict'; var Message = require('./message'); +var BloomFilter = require('../bloomfilter'); +var packageInfo = require('../../package.json'); var inherits = require('util').inherits; -var packageInfo = require('../package.json'); var bitcore = require('bitcore'); var BN = bitcore.crypto.BN; var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; var BufferUtil = bitcore.util.buffer; -var BloomFilter = require('./bloomfilter'); var Put = require('bufferput'); //todo remove var $ = bitcore.util.preconditions; var _ = bitcore.deps._; diff --git a/lib/messages.js b/lib/messages/index.js similarity index 94% rename from lib/messages.js rename to lib/messages/index.js index 754a38b6..a833e737 100644 --- a/lib/messages.js +++ b/lib/messages/index.js @@ -3,10 +3,9 @@ var bitcore = require('bitcore'); var BufferUtil = bitcore.util.buffer; var Hash = bitcore.crypto.Hash; -var Commands = require('./commands'); function Messages(options) { - this.commands = new Commands(options); + this.commands = new Messages.Commands(options); if (!options) { options = {}; @@ -23,6 +22,8 @@ function Messages(options) { Messages.MINIMUM_LENGTH = 20; Messages.PAYLOAD_START = 16; +Messages.Message = require('./message'); +Messages.Commands = require('./commands'); Messages.prototype.parseMessage = function(dataBuffer) { /* jshint maxstatements: 18 */ diff --git a/lib/message.js b/lib/messages/message.js similarity index 100% rename from lib/message.js rename to lib/messages/message.js diff --git a/lib/pool.js b/lib/pool.js index 94e91d21..7e613e6f 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -38,6 +38,8 @@ function now() { * @constructor */ function Pool(options) { + /* jshint maxcomplexity: 10 */ + /* jshint maxstatements: 20 */ var self = this; diff --git a/test/commands.js b/test/messages/commands.js similarity index 92% rename from test/commands.js rename to test/messages/commands.js index 5e02ff18..2bb09eeb 100644 --- a/test/commands.js +++ b/test/messages/commands.js @@ -1,10 +1,10 @@ 'use strict'; var should = require('chai').should(); -var P2P = require('../'); -var Commands = P2P.Commands; -var commandData = require('./data/messages.json'); -var Data = require('./data/messages');//todo merge with commandData +var P2P = require('../../'); +var Commands = P2P.Messages.Commands; +var commandData = require('../data/messages.json'); +var Data = require('../data/messages');//todo merge with commandData var bitcore = require('bitcore'); function getPayloadBuffer(messageBuffer) { diff --git a/test/message.js b/test/messages/message.js similarity index 92% rename from test/message.js rename to test/messages/message.js index 972cc466..b5e7e9e0 100644 --- a/test/message.js +++ b/test/messages/message.js @@ -1,8 +1,8 @@ 'use strict'; var should = require('chai').should(); -var P2P = require('../'); -var Message = P2P.Message; +var P2P = require('../../'); +var Message = P2P.Messages.Message; describe('P2P Message', function() { diff --git a/test/messages.js b/test/messages/messages.js similarity index 97% rename from test/messages.js rename to test/messages/messages.js index 1d01d2ba..c01aa2c8 100644 --- a/test/messages.js +++ b/test/messages/messages.js @@ -4,9 +4,9 @@ var chai = require('chai'); var should = chai.should(); var Buffers = require('buffers'); -var P2P = require('../'); -var messages = P2P.messages; +var P2P = require('../../'); var Messages = P2P.Messages; +var messages = new Messages(); var bitcore = require('bitcore'); describe('Messages', function() { diff --git a/test/pool.js b/test/pool.js index d72c9053..3daba0c5 100644 --- a/test/pool.js +++ b/test/pool.js @@ -10,7 +10,8 @@ var bitcore = require('bitcore'); var P2P = require('../'); var Peer = P2P.Peer; var MessagesData = require('./data/messages'); -var messages = P2P.messages; +var Messages = P2P.Messages; +var messages = new Messages(); var Pool = P2P.Pool; var Networks = bitcore.Networks; From 11bee8b900a2fa1881559f865b23bc4e6da849f8 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Wed, 11 Mar 2015 23:34:50 -0400 Subject: [PATCH 04/48] Improved API: - Renamed "Commands" to "builder" - "Messages.parseMessage" to "Messages.parseBuffer" - Changed to use private methods for "discardUntilNextMessage" and "buildFromBuffer" - Cleaned up tests --- lib/messages/{commands.js => builder.js} | 20 +++++++++++--- lib/messages/index.js | 33 +++++++++-------------- lib/peer.js | 2 +- test/bloomfilter.js | 8 +++--- test/inventory.js | 18 ++++++------- test/messages/{commands.js => builder.js} | 28 +++++++++---------- test/messages/{messages.js => index.js} | 17 +++++------- test/messages/message.js | 6 ++--- test/peer.js | 16 +++++------ test/pool.js | 24 ++++++++--------- 10 files changed, 88 insertions(+), 84 deletions(-) rename lib/messages/{commands.js => builder.js} (98%) rename test/messages/{commands.js => builder.js} (71%) rename test/messages/{messages.js => index.js} (83%) diff --git a/lib/messages/commands.js b/lib/messages/builder.js similarity index 98% rename from lib/messages/commands.js rename to lib/messages/builder.js index 1e34f353..860dad89 100644 --- a/lib/messages/commands.js +++ b/lib/messages/builder.js @@ -13,7 +13,7 @@ var Put = require('bufferput'); //todo remove var $ = bitcore.util.preconditions; var _ = bitcore.deps._; -function Commands(options) { +function builder(options) { /* jshint maxstatements: 150 */ /* jshint maxcomplexity: 10 */ @@ -33,6 +33,20 @@ function Commands(options) { var commands = {}; + var exported = { + constructors: { + Block: Block, + BlockHeader: BlockHeader, + Transaction: Transaction, + MerkleBlock: MerkleBlock + }, + defaults: { + protocolVersion: protocolVersion, + magicNumber: magicNumber + }, + commands: commands + }; + /* shared */ function checkFinished(parser) { @@ -890,8 +904,8 @@ function Commands(options) { return BufferUtil.EMPTY_BUFFER; }; - return commands; + return exported; } -module.exports = Commands; +module.exports = builder; diff --git a/lib/messages/index.js b/lib/messages/index.js index a833e737..8c687723 100644 --- a/lib/messages/index.js +++ b/lib/messages/index.js @@ -5,17 +5,10 @@ var BufferUtil = bitcore.util.buffer; var Hash = bitcore.crypto.Hash; function Messages(options) { - this.commands = new Messages.Commands(options); - + this.builder = Messages.builder(options); if (!options) { options = {}; } - - // map command messages - for (var key in this.commands) { - this[key] = this.commands[key]; - } - var defaultMagicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); this.magicNumber = options.magicNumber || defaultMagicNumber; } @@ -23,16 +16,20 @@ function Messages(options) { Messages.MINIMUM_LENGTH = 20; Messages.PAYLOAD_START = 16; Messages.Message = require('./message'); -Messages.Commands = require('./commands'); +Messages.builder = require('./builder'); + +Messages.prototype.build = Messages.prototype.buildFromObject = function(command, object) { + return this.builder.commands[command].fromObject(object); +}; -Messages.prototype.parseMessage = function(dataBuffer) { +Messages.prototype.parseBuffer = function(dataBuffer) { /* jshint maxstatements: 18 */ if (dataBuffer.length < Messages.MINIMUM_LENGTH) { return; } // Search the next magic number - if (!this.discardUntilNextMessage(dataBuffer)) return; + if (!this._discardUntilNextMessage(dataBuffer)) return; var payloadLen = (dataBuffer.get(Messages.PAYLOAD_START)) + (dataBuffer.get(Messages.PAYLOAD_START + 1) << 8) + @@ -56,10 +53,10 @@ Messages.prototype.parseMessage = function(dataBuffer) { dataBuffer.skip(messageLength); - return this.buildFromBuffer(command, payload); + return this._buildFromBuffer(command, payload); }; -Messages.prototype.discardUntilNextMessage = function(dataBuffer) { +Messages.prototype._discardUntilNextMessage = function(dataBuffer) { var i = 0; for (;;) { // check if it's the beginning of a new message @@ -79,15 +76,11 @@ Messages.prototype.discardUntilNextMessage = function(dataBuffer) { } }; -Messages.prototype.buildFromBuffer = function(command, payload) { - if (!this.commands[command]) { +Messages.prototype._buildFromBuffer = function(command, payload) { + if (!this.builder.commands[command]) { throw new Error('Unsupported message command: ' + command); } - return this.commands[command].fromBuffer(payload); -}; - -Messages.prototype.build = Messages.prototype.buildFromObject = function(command, object) { - return this.commands[command].fromObject(object); + return this.builder.commands[command].fromBuffer(payload); }; module.exports = Messages; diff --git a/lib/peer.js b/lib/peer.js index 583e0c68..70e00508 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -183,7 +183,7 @@ Peer.prototype._sendPong = function(nonce) { * Internal function that tries to read a message from the data buffer */ Peer.prototype._readMessage = function() { - var message = this.messages.parseMessage(this.dataBuffer); + var message = this.messages.parseBuffer(this.dataBuffer); if (message) { this.emit(message.command, message); this._readMessage(); diff --git a/test/bloomfilter.js b/test/bloomfilter.js index 45c7ac53..aa9c4102 100644 --- a/test/bloomfilter.js +++ b/test/bloomfilter.js @@ -26,7 +26,7 @@ function ParseHex(str) { describe('BloomFilter', function() { - it('BloomFilter#fromBuffer and toBuffer methods work', function() { + it('#fromBuffer and #toBuffer round trip', function() { var testPayloadBuffer = getPayloadBuffer(Data.filterload.message); var filter = new BloomFilter.fromBuffer(testPayloadBuffer); filter.toBuffer().should.deep.equal(testPayloadBuffer); @@ -34,7 +34,7 @@ describe('BloomFilter', function() { // test data from: https://github.com/bitcoin/bitcoin/blob/master/src/test/bloom_tests.cpp - it('correctly serialize filter with public keys added', function() { + it('serialize filter with public keys added', function() { var privateKey = bitcore.PrivateKey.fromWIF('5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C'); var publicKey = privateKey.toPublicKey(); @@ -49,7 +49,7 @@ describe('BloomFilter', function() { }); - it('correctly serialize to a buffer', function() { + it('serialize to a buffer', function() { var filter = BloomFilter.create(3, 0.01, 0, BloomFilter.BLOOM_UPDATE_ALL); @@ -68,7 +68,7 @@ describe('BloomFilter', function() { actual.should.deep.equal(expected); }); - it('correctly deserialize a buffer', function() { + it('deserialize a buffer', function() { var buffer = new Buffer('03614e9b050000000000000001', 'hex'); var filter = BloomFilter.fromBuffer(buffer); diff --git a/test/inventory.js b/test/inventory.js index fde8969b..ef9add80 100644 --- a/test/inventory.js +++ b/test/inventory.js @@ -20,12 +20,12 @@ describe('Inventory', function() { ); describe('@constructor', function() { - it('should create inventory', function() { + it('create inventory', function() { var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hash}); should.exist(inventory); }); - it('should error with string hash', function() { + it('error with string hash', function() { (function() { var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hashedStr}); should.not.exist(inventory); @@ -35,7 +35,7 @@ describe('Inventory', function() { }); describe('#forItem', function() { - it('should handle a string hash (reversed)', function() { + it('handle a string hash (reversed)', function() { var inventory = Inventory.forItem(Inventory.TYPE.TX, hashedStr); should.exist(inventory); inventory.hash.should.deep.equal(new Buffer(hash, 'hex')); @@ -44,7 +44,7 @@ describe('Inventory', function() { }); describe('#forBlock', function() { - it('should use correct block type', function() { + it('use correct block type', function() { var inventory = Inventory.forBlock(hash); should.exist(inventory); inventory.type.should.equal(Inventory.TYPE.BLOCK); @@ -52,7 +52,7 @@ describe('Inventory', function() { }); describe('#forFilteredBlock', function() { - it('should use correct filtered block type', function() { + it('use correct filtered block type', function() { var inventory = Inventory.forFilteredBlock(hash); should.exist(inventory); inventory.type.should.equal(Inventory.TYPE.FILTERED_BLOCK); @@ -60,7 +60,7 @@ describe('Inventory', function() { }); describe('#forTransaction', function() { - it('should use correct filtered tx type', function() { + it('use correct filtered tx type', function() { var inventory = Inventory.forTransaction(hash); should.exist(inventory); inventory.type.should.equal(Inventory.TYPE.TX); @@ -68,7 +68,7 @@ describe('Inventory', function() { }); describe('#toBuffer', function() { - it('should serialize correctly', function() { + it('serialize correctly', function() { var inventory = Inventory.forTransaction(hash); var buffer = inventory.toBuffer(); buffer.should.deep.equal(inventoryBuffer); @@ -76,7 +76,7 @@ describe('Inventory', function() { }); describe('#toBufferWriter', function() { - it('should write to a buffer writer', function() { + it('write to a buffer writer', function() { var bw = new BufferWriter(); var inventory = Inventory.forTransaction(hash); inventory.toBufferWriter(bw); @@ -85,7 +85,7 @@ describe('Inventory', function() { }); describe('#fromBuffer', function() { - it('should deserialize a buffer', function() { + it('deserialize a buffer', function() { var inventory = Inventory.fromBuffer(inventoryBuffer); should.exist(inventory); inventory.type.should.equal(Inventory.TYPE.TX); diff --git a/test/messages/commands.js b/test/messages/builder.js similarity index 71% rename from test/messages/commands.js rename to test/messages/builder.js index 2bb09eeb..1a67c929 100644 --- a/test/messages/commands.js +++ b/test/messages/builder.js @@ -2,7 +2,7 @@ var should = require('chai').should(); var P2P = require('../../'); -var Commands = P2P.Messages.Commands; +var builder = P2P.Messages.builder; var commandData = require('../data/messages.json'); var Data = require('../data/messages');//todo merge with commandData var bitcore = require('bitcore'); @@ -11,39 +11,39 @@ function getPayloadBuffer(messageBuffer) { return new Buffer(messageBuffer.slice(48), 'hex'); } -describe('P2P Command Builder', function() { +describe('Messages Builder', function() { describe('@constructor', function() { it('should return commands based on default', function() { // instantiate - var commands = new Commands(); - should.exist(commands); + var b = builder(); + should.exist(b); }); it('should return commands with customizations', function() { // instantiate - var commands = new Commands({ + var b = builder({ magicNumber: 0xd9b4bef9, Block: bitcore.Block, Transaction: bitcore.Transaction }); - should.exist(commands); + should.exist(b); }); }); describe('Commands', function() { - var commands = new Commands(); + var b = builder(); describe('#fromBuffer/#toBuffer round trip for all commands', function() { - Object.keys(commands).forEach(function(command) { + Object.keys(b.commands).forEach(function(command) { - it('should round trip buffers for command: ' + command, function(done) { + it(command, function(done) { var payloadBuffer = getPayloadBuffer(commandData[command].message); - should.exist(commands[command]); - var message = commands[command].fromBuffer(payloadBuffer); + should.exist(b.commands[command]); + var message = b.commands[command].fromBuffer(payloadBuffer); var outputBuffer = message.getPayload(); outputBuffer.toString('hex').should.equal(payloadBuffer.toString('hex')); outputBuffer.should.deep.equal(payloadBuffer); @@ -58,16 +58,16 @@ describe('P2P Command Builder', function() { describe('version', function() { it('#fromBuffer works w/o fRelay arg', function() { var payloadBuffer = getPayloadBuffer(Data.version.messagenofrelay); - var message = commands.version.fromBuffer(payloadBuffer); + var message = b.commands.version.fromBuffer(payloadBuffer); message.relay.should.equal(true); }); it('#relay setting works', function() { [true,false].forEach(function(relay) { - var message = commands.version.fromObject({relay: relay}); + var message = b.commands.version.fromObject({relay: relay}); message.relay.should.equal(relay); var messageBuf = message.getPayload(); - var newMessage = commands.version.fromBuffer(messageBuf); + var newMessage = b.commands.version.fromBuffer(messageBuf); newMessage.relay.should.equal(relay); }); }); diff --git a/test/messages/messages.js b/test/messages/index.js similarity index 83% rename from test/messages/messages.js rename to test/messages/index.js index c01aa2c8..44a6337d 100644 --- a/test/messages/messages.js +++ b/test/messages/index.js @@ -25,24 +25,21 @@ describe('Messages', function() { Block: bitcore.Block, Transaction: bitcore.Transaction }); - should.exist(messages.commands); + should.exist(messages.builder.commands); + should.exist(messages.builder.constructors); + messages.builder.constructors.Block.should.equal(bitcore.Block); + messages.builder.constructors.Transaction.should.equal(bitcore.Transaction); messages.magicNumber.should.equal(magicNumber); - - // check that commands are mapped as messages - for(var key in messages.commands) { - messages[key].should.equal(messages.commands[key]); - } - }); }); - describe('#parseMessage', function() { + describe('#parseBuffer', function() { it('fails with invalid command', function() { var invalidCommand = 'f9beb4d96d616c6963696f757300000025000000bd5e830c' + '0102000000ec3995c1bf7269ff728818a65e53af00cbbee6b6eca8ac9ce7bc79d87' + '7041ed8'; var fails = function() { - messages.parseMessage(buildMessage(invalidCommand)); + messages.parseBuffer(buildMessage(invalidCommand)); }; fails.should.throw('Unsupported message command: malicious'); }); @@ -59,7 +56,7 @@ describe('Messages', function() { '00000069ebcbc34a4f9890da9aea0f773beba883a9afb1ab9ad7647dd4a1cd346c3' + '728'; [malformed1, malformed2, malformed3].forEach(function(malformed) { - var ret = messages.parseMessage(buildMessage(malformed)); + var ret = messages.parseBuffer(buildMessage(malformed)); should.not.exist(ret); }); }); diff --git a/test/messages/message.js b/test/messages/message.js index b5e7e9e0..f93b0fd6 100644 --- a/test/messages/message.js +++ b/test/messages/message.js @@ -4,10 +4,10 @@ var should = require('chai').should(); var P2P = require('../../'); var Message = P2P.Messages.Message; -describe('P2P Message', function() { +describe('Message', function() { describe('@constructor', function() { - it('should construct with magic number and command', function() { + it('construct with magic number and command', function() { var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'}); message.command.should.equal('command'); message.magicNumber.should.equal(0xd9b4bef9); @@ -15,7 +15,7 @@ describe('P2P Message', function() { }); describe('#toBuffer', function() { - it('should serialize to a buffer', function() { + it('serialize to a buffer', function() { var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'}); message.getPayload = function() { return new Buffer(0); diff --git a/test/peer.js b/test/peer.js index e575b648..98d0de44 100644 --- a/test/peer.js +++ b/test/peer.js @@ -69,42 +69,42 @@ describe('Peer', function() { }); - it('should be able to create instance', function() { + it('create instance', function() { var peer = new Peer('localhost'); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.livenet); peer.port.should.equal(Networks.livenet.port); }); - it('should be able to create instance setting a port', function() { + it('create instance setting a port', function() { var peer = new Peer({host: 'localhost', port: 8111}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.livenet); peer.port.should.equal(8111); }); - it('should be able to create instance setting a network', function() { + it('create instance setting a network', function() { var peer = new Peer({host: 'localhost', network: Networks.testnet}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.testnet); peer.port.should.equal(Networks.testnet.port); }); - it('should be able to create instance setting port and network', function() { + it('create instance setting port and network', function() { var peer = new Peer({host: 'localhost', port: 8111, network: Networks.testnet}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.testnet); peer.port.should.equal(8111); }); - it('should support creating instance without new', function() { + it('create instance without new', function() { var peer = Peer({host: 'localhost', port: 8111, network: Networks.testnet}); peer.host.should.equal('localhost'); peer.network.should.equal(Networks.testnet); peer.port.should.equal(8111); }); - it('should be able to set a proxy', function() { + it('set a proxy', function() { var peer, peer2, socket; peer = new Peer('localhost'); @@ -121,7 +121,7 @@ describe('Peer', function() { peer.should.equal(peer2); }); - it('Peer.relay setting set properly', function() { + it('relay set properly', function() { var peer = new Peer({host: 'localhost'}); peer.relay.should.equal(true); var peer2 = new Peer({host: 'localhost', relay: false}); @@ -130,7 +130,7 @@ describe('Peer', function() { peer3.relay.should.equal(true); }); - it('Peer.relay setting respected', function() { + it('relay setting respected', function() { [true,false].forEach(function(relay) { var peer = new Peer({host: 'localhost', relay: relay}); var peerSendMessageStub = sinon.stub(Peer.prototype, 'sendMessage', function(message) { diff --git a/test/pool.js b/test/pool.js index 3daba0c5..e575d133 100644 --- a/test/pool.js +++ b/test/pool.js @@ -24,7 +24,7 @@ function getPayloadBuffer(messageBuffer) { describe('Pool', function() { - it('should be able to create instance', function() { + it('create instance', function() { var pool = new Pool(); should.exist(pool.network); expect(pool.network).to.satisfy(function(network) { @@ -32,12 +32,12 @@ describe('Pool', function() { }); }); - it('should be able to create instance setting the network', function() { + it('create instance setting the network', function() { var pool = new Pool({network: Networks.testnet}); pool.network.should.equal(Networks.testnet); }); - it('should discover peers via dns', function() { + it('discover peers via dns', function() { var stub = sinon.stub(dns, 'resolve', function(seed, callback) { callback(null, ['10.10.10.1', '10.10.10.2', '10.10.10.3']); }); @@ -48,7 +48,7 @@ describe('Pool', function() { stub.restore(); }); - it('can optionally connect without dns seeds', function() { + it('optionally connect without dns seeds', function() { var stub = sinon.stub(dns, 'resolve', function(seed, callback) { throw new Error('DNS should not be called'); }); @@ -92,7 +92,7 @@ describe('Pool', function() { pool._addrs.length.should.equal(1); }); - it('should add new addrs as they are announced over the network', function(done) { + it('add new addrs as they are announced over the network', function(done) { // only emit an event, no need to connect var peerConnectStub = sinon.stub(Peer.prototype, 'connect', function() { @@ -103,7 +103,7 @@ describe('Pool', function() { // mock a addr peer event var peerMessageStub = sinon.stub(Peer.prototype, '_readMessage', function() { var payloadBuffer = getPayloadBuffer(MessagesData.addr.message); - var message = messages.buildFromBuffer('addr', payloadBuffer); + var message = messages._buildFromBuffer('addr', payloadBuffer); this.emit(message.command, message); }); @@ -154,7 +154,7 @@ describe('Pool', function() { // mock a addr peer event var peerMessageStub = sinon.stub(Peer.prototype, '_readMessage', function() { var payloadBuffer = getPayloadBuffer(MessagesData.addr.message); - var message = messages.buildFromBuffer('addr', payloadBuffer); + var message = messages._buildFromBuffer('addr', payloadBuffer); this.emit(message.command, message); }); @@ -195,7 +195,7 @@ describe('Pool', function() { }); - it('should propagate connect, ready, and disconnect peer events', function(done) { + it('propagate connect, ready, and disconnect peer events', function(done) { var peerConnectStub = sinon.stub(Peer.prototype, 'connect', function() { this.emit('connect', this, {}); this.emit('ready'); @@ -240,7 +240,7 @@ describe('Pool', function() { pool.connect(); }); - it('should propagate Pool.relay property to peers', function(done) { + it('propagate relay property to peers', function(done) { var count = 0; var peerConnectStub = sinon.stub(Peer.prototype, 'connect', function() { this.emit('connect', this, {}); @@ -260,12 +260,12 @@ describe('Pool', function() { peerConnectStub.restore(); }); - it('should output the console correctly', function() { + it('output the console correctly', function() { var pool = new Pool(); pool.inspect().should.equal(''); }); - it('should emit seederrors with error', function(done) { + it('emit seederrors with error', function(done) { var dnsStub = sinon.stub(dns, 'resolve', function(seed, callback) { callback(new Error('A DNS error')); }); @@ -279,7 +279,7 @@ describe('Pool', function() { pool.connect(); }); - it('should emit seederrors with notfound', function(done) { + it('emit seederrors with notfound', function(done) { var dnsStub = sinon.stub(dns, 'resolve', function(seed, callback) { callback(null, []); }); From 32a28b133a8f17ed8240c0ba2b75b1a30e95dc2a Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Wed, 11 Mar 2015 23:53:21 -0400 Subject: [PATCH 05/48] Removed bufferput dependency --- lib/messages/builder.js | 41 ++++++++++++++++++----------------------- package.json | 1 - 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 860dad89..3ef6e4be 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -9,7 +9,6 @@ var BN = bitcore.crypto.BN; var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; var BufferUtil = bitcore.util.buffer; -var Put = require('bufferput'); //todo remove var $ = bitcore.util.preconditions; var _ = bitcore.deps._; @@ -437,18 +436,14 @@ function builder(options) { }; commands.headers.prototype.getPayload = function() { - var put = new Put(); - put.varint(this.headers.length); - + var bw = new BufferWriter(); + bw.writeVarintNum(this.headers.length); for (var i = 0; i < this.headers.length; i++) { - var buffer = this - .headers[i] - .toBuffer(); - put.put(buffer); - put.varint(0); + var buffer = this.headers[i].toBuffer(); + bw.write(buffer); + bw.writeUInt8(0); } - - return put.buffer(); + return bw.concat(); }; /* notfound */ @@ -601,14 +596,14 @@ function builder(options) { }; commands.alert.prototype.getPayload = function() { - var put = new Put(); - put.varint(this.payload.length); - put.put(this.payload); + var bw = new BufferWriter(); + bw.writeVarintNum(this.payload.length); + bw.write(this.payload); - put.varint(this.signature.length); - put.put(this.signature); + bw.writeVarintNum(this.signature.length); + bw.write(this.signature); - return put.buffer(); + return bw.concat(); }; /* reject */ @@ -849,17 +844,17 @@ function builder(options) { }; commands.getheaders.prototype.getPayload = function() { - var put = new Put(); - put.word32le(this.version); - put.varint(this.starts.length); + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeVarintNum(this.starts.length); for (var i = 0; i < this.starts.length; i++) { - put.put(this.starts[i]); + bw.write(this.starts[i]); } if (this.stop.length !== 32) { throw new Error('Invalid hash length: ' + this.stop.length); } - put.put(this.stop); - return put.buffer(); + bw.write(this.stop); + return bw.concat(); }; /* mempool */ diff --git a/package.json b/package.json index 5aedb1b9..164ce337 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "dependencies": { "bitcore": "^0.11.0", "bloom-filter": "^0.1.1", - "bufferput": "^0.1.2", "buffers": "^0.1.1", "socks5-client": "^0.3.6" }, From 42288e8354f5d3693d4df8da8dbd43107277a2c1 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 00:05:27 -0400 Subject: [PATCH 06/48] removed "buildFromObject" alias --- lib/messages/index.js | 2 +- lib/peer.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/messages/index.js b/lib/messages/index.js index 8c687723..ed59af1a 100644 --- a/lib/messages/index.js +++ b/lib/messages/index.js @@ -18,7 +18,7 @@ Messages.PAYLOAD_START = 16; Messages.Message = require('./message'); Messages.builder = require('./builder'); -Messages.prototype.build = Messages.prototype.buildFromObject = function(command, object) { +Messages.prototype.build = function(command, object) { return this.builder.commands[command].fromObject(object); }; diff --git a/lib/peer.js b/lib/peer.js index 70e00508..dd29442d 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -163,7 +163,7 @@ Peer.prototype.sendMessage = function(message) { */ Peer.prototype._sendVersion = function() { // todo: include sending ip address - var message = this.messages.buildFromObject('version', { + var message = this.messages.build('version', { relay: this.relay }); this.sendMessage(message); @@ -173,7 +173,7 @@ Peer.prototype._sendVersion = function() { * Send a PONG message to the remote peer. */ Peer.prototype._sendPong = function(nonce) { - var message = this.messages.buildFromObject('pong', { + var message = this.messages.build('pong', { nonce: nonce }); this.sendMessage(message); From 7e29af45426da7d7d8fab6dff2c77299eece07b4 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 00:18:09 -0400 Subject: [PATCH 07/48] added missing precondition and moved buffer.skip to new file --- lib/buffers.js | 23 +++++++++++++++++++++++ lib/peer.js | 24 +++++++----------------- 2 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 lib/buffers.js diff --git a/lib/buffers.js b/lib/buffers.js new file mode 100644 index 00000000..01456ac8 --- /dev/null +++ b/lib/buffers.js @@ -0,0 +1,23 @@ +'use strict'; + +var Buffers = require('buffers'); + +Buffers.prototype.skip = function(i) { + if (i === 0) { + return; + } + + if (i === this.length) { + this.buffers = []; + this.length = 0; + return; + } + + var pos = this.pos(i); + this.buffers = this.buffers.slice(pos.buf); + this.buffers[0] = new Buffer(this.buffers[0].slice(pos.offset)); + this.length -= i; +}; + +module.exports = Buffers; + diff --git a/lib/peer.js b/lib/peer.js index dd29442d..b55c6f55 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -1,12 +1,13 @@ 'use strict'; -var Buffers = require('buffers'); +var Buffers = require('./buffers'); var EventEmitter = require('events').EventEmitter; var Net = require('net'); var Socks5Client = require('socks5-client'); var bitcore = require('bitcore'); var Networks = bitcore.Networks; var Messages = require('./messages'); +var $ = bitcore.util.preconditions; var util = require('util'); /** @@ -30,6 +31,9 @@ var util = require('util'); * @constructor */ function Peer(options) { + /* jshint maxstatements: 20 */ + /* jshint maxcomplexity: 8 */ + if (!(this instanceof Peer)) { return new Peer(options); } @@ -93,6 +97,8 @@ Peer.STATUS = { * @returns {Peer} The same Peer instance. */ Peer.prototype.setProxy = function(host, port) { + $.checkState(this.status === Peer.STATUS.DISCONNECTED); + this.proxy = { host: host, port: port @@ -203,20 +209,4 @@ Peer.prototype._getSocket = function() { return new Net.Socket(); }; -// TODO: Remove this PATCH (yemel) -Buffers.prototype.skip = function(i) { - if (i === 0) return; - - if (i === this.length) { - this.buffers = []; - this.length = 0; - return; - } - - var pos = this.pos(i); - this.buffers = this.buffers.slice(pos.buf); - this.buffers[0] = new Buffer(this.buffers[0].slice(pos.offset)); - this.length -= i; -}; - module.exports = Peer; From d63c1171db70ce8f885d5cd1d51ae95b06c90620 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 00:37:15 -0400 Subject: [PATCH 08/48] added test for inventory fromBufferReader --- lib/inventory.js | 6 +++--- test/inventory.js | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/inventory.js b/lib/inventory.js index 111000aa..ba6b19e3 100644 --- a/lib/inventory.js +++ b/lib/inventory.js @@ -59,10 +59,10 @@ Inventory.fromBuffer = function(payload) { return new Inventory(obj); }; -Inventory.fromBufferWriter = function(bw) { +Inventory.fromBufferReader = function(br) { var obj = {}; - obj.type = bw.readUInt32LE(); - obj.hash = bw.read(32); + obj.type = br.readUInt32LE(); + obj.hash = br.read(32); return new Inventory(obj); }; diff --git a/test/inventory.js b/test/inventory.js index ef9add80..e269f13b 100644 --- a/test/inventory.js +++ b/test/inventory.js @@ -9,6 +9,7 @@ var P2P = require('../'); var Inventory = P2P.Inventory; var BufferUtils = bitcore.util.buffer; var BufferWriter = bitcore.encoding.BufferWriter; +var BufferReader = bitcore.encoding.BufferReader; describe('Inventory', function() { @@ -93,4 +94,14 @@ describe('Inventory', function() { }); }); + describe('#fromBufferWriter', function() { + it('deserialize from a buffer reader', function() { + var bw = new BufferReader(inventoryBuffer); + var inventory = Inventory.fromBufferReader(bw); + should.exist(inventory); + inventory.type.should.equal(Inventory.TYPE.TX); + inventory.hash.should.deep.equal(hash); + }); + }); + }); From 1dfe7091b4a68f946e217aaef86477f472fc7992 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 00:48:05 -0400 Subject: [PATCH 09/48] added test for pool.sendMessage --- test/pool.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/pool.js b/test/pool.js index e575d133..10b85c0f 100644 --- a/test/pool.js +++ b/test/pool.js @@ -293,4 +293,25 @@ describe('Pool', function() { pool.connect(); }); + it('send message to all peers', function(done) { + var message = 'message'; + var peerConnectStub = sinon.stub(Peer.prototype, 'connect', function() { + var self = this; + process.nextTick(function() { + self.emit('ready'); + }); + }); + var peerMessageStub = sinon.stub(Peer.prototype, 'sendMessage', function(message) { + message.should.equal(message); + peerConnectStub.restore(); + peerMessageStub.restore(); + done(); + }); + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + pool.on('peerready', function() { + pool.sendMessage(message); + }); + pool.connect(); + }); + }); From 608e41de075c6bd0a50f557a7bd02b042beaea06 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 01:12:45 -0400 Subject: [PATCH 10/48] added test coverage for peer --- lib/messages/builder.js | 3 +++ test/peer.js | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 3ef6e4be..7a0ad4f8 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -257,6 +257,9 @@ function builder(options) { /* ping */ commands.ping = function(options) { + if (!options) { + options = {}; + } Message.call(this, options); this.command = 'ping'; this.magicNumber = magicNumber; diff --git a/test/peer.js b/test/peer.js index 98d0de44..02a8c0ab 100644 --- a/test/peer.js +++ b/test/peer.js @@ -14,6 +14,9 @@ var bitcore = require('bitcore'); var _ = bitcore.deps._; var P2P = require('../'); var Peer = P2P.Peer; +var EventEmitter = require('events').EventEmitter; +var Messages = P2P.Messages; +var messages = new Messages(); var Networks = bitcore.Networks; describe('Peer', function() { @@ -121,6 +124,49 @@ describe('Peer', function() { peer.should.equal(peer2); }); + it('send pong on ping', function(done) { + var peer = new Peer({host: 'localhost'}); + var pingMessage = messages.build('ping'); + peer.sendMessage = function(message) { + message.command.should.equal('pong'); + message.nonce.should.equal(pingMessage.nonce); + done(); + }; + peer.emit('ping', pingMessage); + }); + + it('relay error from socket', function(done) { + var peer = new Peer({host: 'localhost'}); + var socket = new EventEmitter(); + socket.connect = sinon.spy(); + peer._getSocket = function() { + return socket; + }; + var error = new Error('error'); + peer.on('error', function(err) { + err.should.equal(error); + done(); + }); + peer.connect(); + peer.socket.emit('error', error); + }); + + it('disconnect with max buffer length', function(done) { + var peer = new Peer({host: 'localhost'}); + var socket = new EventEmitter(); + socket.connect = sinon.spy(); + peer._getSocket = function() { + return socket; + }; + peer.disconnect = function() { + done(); + }; + peer.connect(); + var buffer = new Buffer(Array(Peer.MAX_RECEIVE_BUFFER + 1)); + peer.socket.emit('data', buffer); + + }); + it('relay set properly', function() { var peer = new Peer({host: 'localhost'}); peer.relay.should.equal(true); From 6ca2f6982c09fda22f7d1f5ceeb3c00d5aef66b2 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 04:34:30 -0400 Subject: [PATCH 11/48] Moved block and version messages and shared utils into seperate files. --- lib/messages/builder.js | 252 +++---------------------------- lib/messages/commands/block.js | 35 +++++ lib/messages/commands/version.js | 100 ++++++++++++ lib/messages/utils.js | 109 +++++++++++++ 4 files changed, 267 insertions(+), 229 deletions(-) create mode 100644 lib/messages/commands/block.js create mode 100644 lib/messages/commands/version.js create mode 100644 lib/messages/utils.js diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 7a0ad4f8..97814c9c 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -2,7 +2,7 @@ var Message = require('./message'); var BloomFilter = require('../bloomfilter'); -var packageInfo = require('../../package.json'); + var inherits = require('util').inherits; var bitcore = require('bitcore'); var BN = bitcore.crypto.BN; @@ -11,6 +11,7 @@ var BufferWriter = bitcore.encoding.BufferWriter; var BufferUtil = bitcore.util.buffer; var $ = bitcore.util.preconditions; var _ = bitcore.deps._; +var utils = require('./utils'); function builder(options) { /* jshint maxstatements: 150 */ @@ -46,192 +47,7 @@ function builder(options) { commands: commands }; - /* shared */ - - function checkFinished(parser) { - if(!parser.finished()) { - throw new Error('Data still available after parsing'); - } - } - - function getNonce() { - return bitcore.crypto.Random.getRandomBuffer(8); - } - - function writeIP(ip, bw) { - var words = ip.v6.split(':').map(function(s) { - return new Buffer(s, 'hex'); - }); - for (var i = 0; i < words.length; i++) { - var word = words[i]; - bw.write(word); - } - } - - function writeAddr(addr, bw) { - if (_.isUndefined(addr)) { - var pad = new Buffer(Array(26)); - bw.write(pad); - return; - } - - bw.writeUInt64LEBN(addr.services); - writeIP(addr.ip, bw); - bw.writeUInt16BE(addr.port); - } - - function writeInventory(inventory, bw) { - bw.writeVarintNum(inventory.length); - inventory.forEach(function(value) { - bw.writeUInt32LE(value.type); - bw.write(value.hash); - }); - } - - function parseIP(parser) { - var ipv6 = []; - var ipv4 = []; - for (var a = 0; a < 8; a++) { - var word = parser.read(2); - ipv6.push(word.toString('hex')); - if (a >= 6) { - ipv4.push(word[0]); - ipv4.push(word[1]); - } - } - ipv6 = ipv6.join(':'); - ipv4 = ipv4.join('.'); - return { - v6: ipv6, - v4: ipv4 - }; - } - - function parseAddr(parser) { - var services = parser.readUInt64LEBN(); - var ip = parseIP(parser); - var port = parser.readUInt16BE(); - return { - services: services, - ip: ip, - port: port - }; - } - - function sanitizeStartStop(obj) { - /* jshint maxcomplexity: 10 */ - $.checkArgument(_.isUndefined(options.starts) || _.isArray(options.starts)); - var starts = obj.starts; - var stop = obj.stop; - if (starts) { - starts = starts.map(function(hash) { - if (_.isString(hash)) { - return BufferUtil.reverse(new Buffer(hash, 'hex')); - } else { - return hash; - } - }); - } else { - starts = []; - } - - for (var i = 0; i < starts.length; i++) { - if (starts[i].length !== 32) { - throw new Error('Invalid hash ' + i + ' length: ' + starts[i].length); - } - } - - stop = obj.stop; - if (_.isString(stop)) { - stop = BufferUtil.reverse(new Buffer(stop, 'hex')); - } - if (!stop) { - stop = BufferUtil.NULL_HASH; - } - obj.starts = starts; - obj.stop = stop; - - return obj; - } - - /** - * The version message is used on connection creation to advertise - * the type of node. The remote node will respond with its version, and no - * communication is possible until both peers have exchanged their versions. - * By default, bitcore advertises itself as named `bitcore:0.8`. - * - * @param{Object} obj - properties for the version - * @param{String} obj.subversion - version of the client - * @param{Buffer} obj.nonce - a random 8 byte buffer - */ - commands.version = function(obj) { - Message.call(this, obj); - this.command = 'version'; - _.assign(this, obj); - this.magicNumber = magicNumber; - this.nonce = this.nonce || getNonce(); - this.services = this.services || new BN(1, 10); - this.timestamp = this.timestamp || new Date(); - this.version = this.version || protocolVersion; - this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/'; - this.startHeight = this.startHeight || 0; - }; - inherits(commands.version, Message); - - commands.version.fromObject = function(obj) { - return new commands.version(obj); - }; - - commands.version.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - var obj = {}; - obj.version = parser.readUInt32LE(); - obj.services = parser.readUInt64LEBN(); - obj.timestamp = new Date(parser.readUInt64LEBN().toNumber() * 1000); - - obj.addrMe = { - services: parser.readUInt64LEBN(), - ip: parseIP(parser), - port: parser.readUInt16BE() - }; - obj.addrYou = { - services: parser.readUInt64LEBN(), - ip: parseIP(parser), - port: parser.readUInt16BE() - }; - obj.nonce = parser.read(8); - obj.subversion = parser.readVarLengthBuffer().toString(); - obj.startHeight = parser.readUInt32LE(); - - if(parser.finished()) { - obj.relay = true; - } else { - obj.relay = !!parser.readUInt8(); - } - checkFinished(parser); - - return commands.version.fromObject(obj); - }; - - commands.version.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeUInt32LE(this.version); - bw.writeUInt64LEBN(this.services); - - var timestampBuffer = new Buffer(Array(8)); - timestampBuffer.writeUInt32LE(Math.round(this.timestamp.getTime() / 1000), 0); - bw.write(timestampBuffer); - - writeAddr(this.addrMe, bw); - writeAddr(this.addrYou, bw); - bw.write(this.nonce); - bw.writeVarintNum(this.subversion.length); - bw.write(new Buffer(this.subversion, 'ascii')); - bw.writeUInt32LE(this.startHeight); - bw.writeUInt8(this.relay); - - return bw.concat(); - }; + commands.version = require('./commands/version')({magicNumber: magicNumber}); /* verack */ @@ -263,7 +79,7 @@ function builder(options) { Message.call(this, options); this.command = 'ping'; this.magicNumber = magicNumber; - this.nonce = options.nonce || getNonce(); + this.nonce = options.nonce || utils.getNonce(); }; inherits(commands.ping, Message); @@ -280,7 +96,7 @@ function builder(options) { var parser = new BufferReader(payload); obj.nonce = parser.read(8); - checkFinished(parser); + utils.checkFinished(parser); return commands.ping.fromObject(obj); }; @@ -303,7 +119,7 @@ function builder(options) { var parser = new BufferReader(payload); obj.nonce = parser.read(8); - checkFinished(parser); + utils.checkFinished(parser); return commands.pong.fromObject(obj); }; @@ -311,28 +127,7 @@ function builder(options) { return this.nonce; }; - /* block */ - - commands.block = function(options) { - Message.call(this, options); - this.command = 'block'; - this.magicNumber = magicNumber; - this.block = options.block; - }; - inherits(commands.block, Message); - - commands.block.fromObject = function(options) { - return new commands.block(options); - }; - - commands.block.fromBuffer = function(payload) { - var block = Block.fromBuffer(payload); - return commands.block.fromObject({block: block}); - }; - - commands.block.prototype.getPayload = function() { - return this.block.toBuffer(); - }; + commands.block = require('./commands/block')({Block: Block, magicNumber: magicNumber}); /* tx */ @@ -390,13 +185,13 @@ function builder(options) { obj.inventory.push({type: type, hash: hash}); } - checkFinished(parser); + utils.checkFinished(parser); return commands.getdata.fromObject(obj); }; commands.getdata.prototype.getPayload = function() { var bw = new BufferWriter(); - writeInventory(this.inventory, bw); + utils.writeInventory(this.inventory, bw); return bw.concat(); }; @@ -433,7 +228,7 @@ function builder(options) { $.checkState(txn_count === 0, 'txn_count should always be 0'); } - checkFinished(parser); + utils.checkFinished(parser); return commands.headers.fromObject(obj); }; @@ -476,14 +271,14 @@ function builder(options) { obj.inventory.push({type: type, hash: hash}); } - checkFinished(parser); + utils.checkFinished(parser); return commands.notfound.fromObject(obj); }; commands.notfound.prototype.getPayload = function() { var bw = new BufferWriter(); - writeInventory(this.inventory, bw); + utils.writeInventory(this.inventory, bw); return bw.concat(); }; @@ -504,7 +299,7 @@ function builder(options) { commands.inv.prototype.getPayload = function() { var bw = new BufferWriter(); - writeInventory(this.inventory, bw); + utils.writeInventory(this.inventory, bw); return bw.concat(); }; @@ -521,7 +316,7 @@ function builder(options) { obj.inventory.push({type: type, hash: hash}); } - checkFinished(parser); + utils.checkFinished(parser); return commands.inv.fromObject(obj); }; @@ -550,12 +345,12 @@ function builder(options) { // todo: time only available on versions >=31402 var time = new Date(parser.readUInt32LE() * 1000); - var addr = parseAddr(parser); + var addr = utils.parseAddr(parser); addr.time = time; obj.addresses.push(addr); } - checkFinished(parser); + utils.checkFinished(parser); return commands.addr.fromObject(obj); }; @@ -566,7 +361,7 @@ function builder(options) { for (var i = 0; i < this.addresses.length; i++) { var addr = this.addresses[i]; bw.writeUInt32LE(addr.time.getTime() / 1000); - writeAddr(addr, bw); + utils.writeAddr(addr, bw); } return bw.concat(); @@ -594,7 +389,7 @@ function builder(options) { var parser = new BufferReader(payload); obj.payload = parser.readVarLengthBuffer(); obj.signature = parser.readVarLengthBuffer(); - checkFinished(parser); + utils.checkFinished(parser); return commands.alert.fromObject(obj); }; @@ -717,7 +512,7 @@ function builder(options) { $.checkArgument(payload); var parser = new BufferReader(payload); obj.data = parser.readVarLengthBuffer(); - checkFinished(parser); + utils.checkFinished(parser); return commands.filteradd.fromObject(obj); }; @@ -763,7 +558,7 @@ function builder(options) { this.version = protocolVersion; this.magicNumber = magicNumber; - options = sanitizeStartStop(options); + options = utils.sanitizeStartStop(options); this.starts = options.starts; this.stop = options.stop; @@ -787,7 +582,7 @@ function builder(options) { obj.starts.push(parser.read(32)); } obj.stop = parser.read(32); - checkFinished(parser); + utils.checkFinished(parser); return commands.getblocks.fromObject(obj); }; @@ -818,7 +613,7 @@ function builder(options) { this.version = protocolVersion; this.magicNumber = magicNumber; - options = sanitizeStartStop(options); + options = utils.sanitizeStartStop(options); this.starts = options.starts; this.stop = options.stop; @@ -842,7 +637,7 @@ function builder(options) { obj.starts.push(parser.read(32)); } obj.stop = parser.read(32); - checkFinished(parser); + utils.checkFinished(parser); return commands.getheaders.fromObject(obj); }; @@ -881,7 +676,6 @@ function builder(options) { }; /* getaddr */ - //todo: need test data commands.getaddr = function(options) { Message.call(this, options); this.magicNumber = magicNumber; diff --git a/lib/messages/commands/block.js b/lib/messages/commands/block.js new file mode 100644 index 00000000..f29450f0 --- /dev/null +++ b/lib/messages/commands/block.js @@ -0,0 +1,35 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); + +var Block = bitcore.Block; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function BlockMessage(options) { + Message.call(this, options); + this.command = 'block'; + this.magicNumber = magicNumber; + this.block = options.block; +} +inherits(BlockMessage, Message); + +BlockMessage.fromObject = function(options) { + return new BlockMessage(options); +}; + +BlockMessage.fromBuffer = function(payload) { + var block = Block.fromBuffer(payload); + return BlockMessage.fromObject({block: block}); +}; + +BlockMessage.prototype.getPayload = function() { + return this.block.toBuffer(); +}; + +module.exports = function(options) { + Block = options.Block; + magicNumber = options.magicNumber; + return BlockMessage; +}; diff --git a/lib/messages/commands/version.js b/lib/messages/commands/version.js new file mode 100644 index 00000000..dc78c37f --- /dev/null +++ b/lib/messages/commands/version.js @@ -0,0 +1,100 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferWriter = bitcore.encoding.BufferWriter; +var BufferReader = bitcore.encoding.BufferReader; +var _ = bitcore.deps._; +var BN = bitcore.crypto.BN; + +var utils = require('../utils'); +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +var protocolVersion = 70000; +var packageInfo = require('../../../package.json'); + +/** + * The version message is used on connection creation to advertise + * the type of node. The remote node will respond with its version, and no + * communication is possible until both peers have exchanged their versions. + * By default, bitcore advertises itself as named `bitcore:0.8`. + * + * @param{Object} obj - properties for the version + * @param{String} obj.subversion - version of the client + * @param{Buffer} obj.nonce - a random 8 byte buffer + */ +function VersionMessage(obj) { + /* jshint maxcomplexity: 10 */ + Message.call(this, obj); + this.command = 'version'; + _.assign(this, obj); + this.magicNumber = magicNumber; + this.nonce = this.nonce || utils.getNonce(); + this.services = this.services || new BN(1, 10); + this.timestamp = this.timestamp || new Date(); + this.version = this.version || protocolVersion; + this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/'; + this.startHeight = this.startHeight || 0; +}; +inherits(VersionMessage, Message); + +VersionMessage.fromObject = function(obj) { + return new VersionMessage(obj); +}; + +VersionMessage.fromBuffer = function(payload) { + var parser = new BufferReader(payload); + var obj = {}; + obj.version = parser.readUInt32LE(); + obj.services = parser.readUInt64LEBN(); + obj.timestamp = new Date(parser.readUInt64LEBN().toNumber() * 1000); + + obj.addrMe = { + services: parser.readUInt64LEBN(), + ip: utils.parseIP(parser), + port: parser.readUInt16BE() + }; + obj.addrYou = { + services: parser.readUInt64LEBN(), + ip: utils.parseIP(parser), + port: parser.readUInt16BE() + }; + obj.nonce = parser.read(8); + obj.subversion = parser.readVarLengthBuffer().toString(); + obj.startHeight = parser.readUInt32LE(); + + if(parser.finished()) { + obj.relay = true; + } else { + obj.relay = !!parser.readUInt8(); + } + utils.checkFinished(parser); + + return VersionMessage.fromObject(obj); +}; + +VersionMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeUInt64LEBN(this.services); + + var timestampBuffer = new Buffer(Array(8)); + timestampBuffer.writeUInt32LE(Math.round(this.timestamp.getTime() / 1000), 0); + bw.write(timestampBuffer); + + utils.writeAddr(this.addrMe, bw); + utils.writeAddr(this.addrYou, bw); + bw.write(this.nonce); + bw.writeVarintNum(this.subversion.length); + bw.write(new Buffer(this.subversion, 'ascii')); + bw.writeUInt32LE(this.startHeight); + bw.writeUInt8(this.relay); + + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + protocolVersion = options.protocolVersion || protocolVersion; + return VersionMessage; +}; diff --git a/lib/messages/utils.js b/lib/messages/utils.js new file mode 100644 index 00000000..b97fec81 --- /dev/null +++ b/lib/messages/utils.js @@ -0,0 +1,109 @@ +'use strict'; + +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; +var $ = bitcore.util.preconditions; +var _ = bitcore.deps._; +var utils; + +module.exports = utils = { + checkFinished: function checkFinished(parser) { + if(!parser.finished()) { + throw new Error('Data still available after parsing'); + } + }, + getNonce: function getNonce() { + return bitcore.crypto.Random.getRandomBuffer(8); + }, + writeIP: function writeIP(ip, bw) { + var words = ip.v6.split(':').map(function(s) { + return new Buffer(s, 'hex'); + }); + for (var i = 0; i < words.length; i++) { + var word = words[i]; + bw.write(word); + } + }, + writeAddr: function writeAddr(addr, bw) { + if (_.isUndefined(addr)) { + var pad = new Buffer(Array(26)); + bw.write(pad); + return; + } + + bw.writeUInt64LEBN(addr.services); + utils.writeIP(addr.ip, bw); + bw.writeUInt16BE(addr.port); + }, + writeInventory: function writeInventory(inventory, bw) { + bw.writeVarintNum(inventory.length); + inventory.forEach(function(value) { + bw.writeUInt32LE(value.type); + bw.write(value.hash); + }); + }, + parseIP: function parseIP(parser) { + var ipv6 = []; + var ipv4 = []; + for (var a = 0; a < 8; a++) { + var word = parser.read(2); + ipv6.push(word.toString('hex')); + if (a >= 6) { + ipv4.push(word[0]); + ipv4.push(word[1]); + } + } + ipv6 = ipv6.join(':'); + ipv4 = ipv4.join('.'); + return { + v6: ipv6, + v4: ipv4 + }; + }, + parseAddr: function parseAddr(parser) { + var services = parser.readUInt64LEBN(); + var ip = utils.parseIP(parser); + var port = parser.readUInt16BE(); + return { + services: services, + ip: ip, + port: port + }; + }, + sanitizeStartStop: function sanitizeStartStop(obj) { + /* jshint maxcomplexity: 10 */ + /* jshint maxstatements: 20 */ + $.checkArgument(_.isUndefined(obj.starts) || _.isArray(obj.starts)); + var starts = obj.starts; + var stop = obj.stop; + if (starts) { + starts = starts.map(function(hash) { + if (_.isString(hash)) { + return BufferUtil.reverse(new Buffer(hash, 'hex')); + } else { + return hash; + } + }); + } else { + starts = []; + } + + for (var i = 0; i < starts.length; i++) { + if (starts[i].length !== 32) { + throw new Error('Invalid hash ' + i + ' length: ' + starts[i].length); + } + } + + stop = obj.stop; + if (_.isString(stop)) { + stop = BufferUtil.reverse(new Buffer(stop, 'hex')); + } + if (!stop) { + stop = BufferUtil.NULL_HASH; + } + obj.starts = starts; + obj.stop = stop; + + return obj; + } +}; From 3c2afed5f5d699128b299ac353378efe64a59ddf Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 04:48:58 -0400 Subject: [PATCH 12/48] moved ping and verack to seperate files --- lib/messages/builder.js | 54 ++------------------------------- lib/messages/commands/ping.js | 43 ++++++++++++++++++++++++++ lib/messages/commands/verack.js | 33 ++++++++++++++++++++ 3 files changed, 78 insertions(+), 52 deletions(-) create mode 100644 lib/messages/commands/ping.js create mode 100644 lib/messages/commands/verack.js diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 97814c9c..944714b6 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -5,7 +5,6 @@ var BloomFilter = require('../bloomfilter'); var inherits = require('util').inherits; var bitcore = require('bitcore'); -var BN = bitcore.crypto.BN; var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; var BufferUtil = bitcore.util.buffer; @@ -48,57 +47,8 @@ function builder(options) { }; commands.version = require('./commands/version')({magicNumber: magicNumber}); - - /* verack */ - - commands.verack = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'verack'; - }; - inherits(commands.verack, Message); - - commands.verack.fromObject = function(obj) { - return new commands.verack(obj); - }; - - commands.verack.fromBuffer = function(payload) { - return commands.verack.fromObject({}); - }; - - commands.verack.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; - - /* ping */ - - commands.ping = function(options) { - if (!options) { - options = {}; - } - Message.call(this, options); - this.command = 'ping'; - this.magicNumber = magicNumber; - this.nonce = options.nonce || utils.getNonce(); - }; - inherits(commands.ping, Message); - - commands.ping.prototype.getPayload = function() { - return this.nonce; - }; - - commands.ping.fromObject = function(obj) { - return new commands.ping(obj); - }; - - commands.ping.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - obj.nonce = parser.read(8); - - utils.checkFinished(parser); - return commands.ping.fromObject(obj); - }; + commands.verack = require('./commands/verack')({magicNumber: magicNumber}); + commands.ping = require('./commands/ping')({magicNumber: magicNumber}); /* pong */ diff --git a/lib/messages/commands/ping.js b/lib/messages/commands/ping.js new file mode 100644 index 00000000..462346db --- /dev/null +++ b/lib/messages/commands/ping.js @@ -0,0 +1,43 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function PingMessage(options) { + if (!options) { + options = {}; + } + Message.call(this, options); + this.command = 'ping'; + this.magicNumber = magicNumber; + this.nonce = options.nonce || utils.getNonce(); +}; +inherits(PingMessage, Message); + +PingMessage.prototype.getPayload = function() { + return this.nonce; +}; + +PingMessage.fromObject = function(obj) { + return new PingMessage(obj); +}; + +PingMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.nonce = parser.read(8); + + utils.checkFinished(parser); + return PingMessage.fromObject(obj); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return PingMessage; +}; + diff --git a/lib/messages/commands/verack.js b/lib/messages/commands/verack.js new file mode 100644 index 00000000..56194575 --- /dev/null +++ b/lib/messages/commands/verack.js @@ -0,0 +1,33 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function VerackMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'verack'; +}; +inherits(VerackMessage, Message); + +VerackMessage.fromObject = function(obj) { + return new VerackMessage(obj); +}; + +VerackMessage.fromBuffer = function(payload) { + return VerackMessage.fromObject({}); +}; + +VerackMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return VerackMessage; +}; + From feda6e937022ffa7cfe38ea827651cf5c710fe15 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 04:54:52 -0400 Subject: [PATCH 13/48] pass options directly into command messages --- lib/messages/builder.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 944714b6..37c795c4 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -46,9 +46,9 @@ function builder(options) { commands: commands }; - commands.version = require('./commands/version')({magicNumber: magicNumber}); - commands.verack = require('./commands/verack')({magicNumber: magicNumber}); - commands.ping = require('./commands/ping')({magicNumber: magicNumber}); + commands.version = require('./commands/version')(options); + commands.verack = require('./commands/verack')(options); + commands.ping = require('./commands/ping')(options); /* pong */ @@ -77,7 +77,7 @@ function builder(options) { return this.nonce; }; - commands.block = require('./commands/block')({Block: Block, magicNumber: magicNumber}); + commands.block = require('./commands/block')(options); /* tx */ From e460bf915dd2996d327c44707ba95af0155a3600 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 10:41:35 -0400 Subject: [PATCH 14/48] moved the rest of the commands into seperate files --- lib/messages/builder.js | 626 +-------------------------- lib/messages/commands/addr.js | 60 +++ lib/messages/commands/alert.js | 49 +++ lib/messages/commands/filteradd.js | 51 +++ lib/messages/commands/filterclear.js | 32 ++ lib/messages/commands/filterload.js | 44 ++ lib/messages/commands/getaddr.js | 33 ++ lib/messages/commands/getblocks.js | 74 ++++ lib/messages/commands/getdata.js | 50 +++ lib/messages/commands/getheaders.js | 72 +++ lib/messages/commands/headers.js | 65 +++ lib/messages/commands/inv.js | 50 +++ lib/messages/commands/mempool.js | 32 ++ lib/messages/commands/merkleblock.js | 50 +++ lib/messages/commands/notfound.js | 50 +++ lib/messages/commands/pong.js | 39 ++ lib/messages/commands/reject.js | 34 ++ lib/messages/commands/tx.js | 40 ++ 18 files changed, 844 insertions(+), 607 deletions(-) create mode 100644 lib/messages/commands/addr.js create mode 100644 lib/messages/commands/alert.js create mode 100644 lib/messages/commands/filteradd.js create mode 100644 lib/messages/commands/filterclear.js create mode 100644 lib/messages/commands/filterload.js create mode 100644 lib/messages/commands/getaddr.js create mode 100644 lib/messages/commands/getblocks.js create mode 100644 lib/messages/commands/getdata.js create mode 100644 lib/messages/commands/getheaders.js create mode 100644 lib/messages/commands/headers.js create mode 100644 lib/messages/commands/inv.js create mode 100644 lib/messages/commands/mempool.js create mode 100644 lib/messages/commands/merkleblock.js create mode 100644 lib/messages/commands/notfound.js create mode 100644 lib/messages/commands/pong.js create mode 100644 lib/messages/commands/reject.js create mode 100644 lib/messages/commands/tx.js diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 37c795c4..38082f70 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -1,20 +1,10 @@ 'use strict'; -var Message = require('./message'); -var BloomFilter = require('../bloomfilter'); - -var inherits = require('util').inherits; var bitcore = require('bitcore'); -var BufferReader = bitcore.encoding.BufferReader; -var BufferWriter = bitcore.encoding.BufferWriter; -var BufferUtil = bitcore.util.buffer; -var $ = bitcore.util.preconditions; -var _ = bitcore.deps._; -var utils = require('./utils'); function builder(options) { - /* jshint maxstatements: 150 */ - /* jshint maxcomplexity: 10 */ + /* jshint maxstatements: 50 */ + /* jshint maxcomplexity: 8 */ if (!options) { options = {}; @@ -49,602 +39,24 @@ function builder(options) { commands.version = require('./commands/version')(options); commands.verack = require('./commands/verack')(options); commands.ping = require('./commands/ping')(options); - - /* pong */ - - commands.pong = function(options) { - Message.call(this, options); - this.command = 'pong'; - this.magicNumber = magicNumber; - this.nonce = options.nonce; - }; - inherits(commands.pong, Message); - - commands.pong.fromObject = function(obj) { - return new commands.pong(obj); - }; - - commands.pong.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - obj.nonce = parser.read(8); - - utils.checkFinished(parser); - return commands.pong.fromObject(obj); - }; - - commands.pong.prototype.getPayload = function() { - return this.nonce; - }; - + commands.pong = require('./commands/pong')(options); commands.block = require('./commands/block')(options); - - /* tx */ - - commands.tx = function(options) { - Message.call(this, options); - this.command = 'tx'; - this.magicNumber = magicNumber; - this.transaction = options.transaction; - }; - inherits(commands.tx, Message); - - commands.tx.fromObject = function(options) { - return new commands.tx(options); - }; - - commands.tx.fromBuffer = function(payload) { - var transaction; - if (Transaction.prototype.fromBuffer) { - transaction = Transaction().fromBuffer(payload); - } else { - transaction = Transaction.fromBuffer(payload); - } - return commands.tx.fromObject({transaction: transaction}); - }; - - commands.tx.prototype.getPayload = function() { - return this.transaction.toBuffer(); - }; - - /* getdata */ - - commands.getdata = function(options) { - Message.call(this, options); - this.command = 'getdata'; - this.magicNumber = magicNumber; - this.inventory = options.inventory; - }; - - inherits(commands.getdata, Message); - - commands.getdata.fromObject = function(options) { - return new commands.getdata(options); - }; - - commands.getdata.fromBuffer = function(payload) { - var obj = { - inventory: [] - }; - - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - for (var i = 0; i < count; i++) { - var type = parser.readUInt32LE(); - var hash = parser.read(32); - obj.inventory.push({type: type, hash: hash}); - } - - utils.checkFinished(parser); - return commands.getdata.fromObject(obj); - }; - - commands.getdata.prototype.getPayload = function() { - var bw = new BufferWriter(); - utils.writeInventory(this.inventory, bw); - return bw.concat(); - }; - - /** - * Sent in response to a `getheaders` message. It contains information about - * block headers. - * - * @param{Array} blockheaders - array of block headers - */ - commands.headers = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'headers'; - this.headers = options.headers; - }; - inherits(commands.headers, Message); - - commands.headers.fromObject = function(options) { - return new commands.headers(options); - }; - - commands.headers.fromBuffer = function(payload) { - var obj = {}; - - $.checkArgument(payload && payload.length > 0, 'No data found to create Headers message'); - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - - obj.headers = []; - for (var i = 0; i < count; i++) { - var header = BlockHeader.fromBufferReader(parser); - obj.headers.push(header); - var txn_count = parser.readUInt8(); - $.checkState(txn_count === 0, 'txn_count should always be 0'); - - } - utils.checkFinished(parser); - - return commands.headers.fromObject(obj); - }; - - commands.headers.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.headers.length); - for (var i = 0; i < this.headers.length; i++) { - var buffer = this.headers[i].toBuffer(); - bw.write(buffer); - bw.writeUInt8(0); - } - return bw.concat(); - }; - - /* notfound */ - - commands.notfound = function(options) { - Message.call(this, options); - this.command = 'notfound'; - this.magicNumber = magicNumber; - this.inventory = options.inventory; - }; - inherits(commands.notfound, Message); - - commands.notfound.fromObject = function(options) { - return new commands.notfound(options); - }; - - commands.notfound.fromBuffer = function(payload) { - var obj = { - inventory: [] - }; - - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - for (var i = 0; i < count; i++) { - var type = parser.readUInt32LE(); - var hash = parser.read(32); - obj.inventory.push({type: type, hash: hash}); - } - - utils.checkFinished(parser); - return commands.notfound.fromObject(obj); - - }; - - commands.notfound.prototype.getPayload = function() { - var bw = new BufferWriter(); - utils.writeInventory(this.inventory, bw); - return bw.concat(); - }; - - /* inv */ - - commands.inv = function(options) { - Message.call(this, options); - this.command = 'inv'; - this.magicNumber = magicNumber; - this.inventory = options.inventory; - }; - - inherits(commands.inv, Message); - - commands.inv.fromObject = function(options) { - return new commands.inv(options); - }; - - commands.inv.prototype.getPayload = function() { - var bw = new BufferWriter(); - utils.writeInventory(this.inventory, bw); - return bw.concat(); - }; - - commands.inv.fromBuffer = function(payload) { - var obj = { - inventory: [] - }; - - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - for (var i = 0; i < count; i++) { - var type = parser.readUInt32LE(); - var hash = parser.read(32); - obj.inventory.push({type: type, hash: hash}); - } - - utils.checkFinished(parser); - return commands.inv.fromObject(obj); - }; - - /* addr */ - - commands.addr = function(options) { - Message.call(this, options); - this.command = 'addr'; - this.magicNumber = magicNumber; - this.addresses = options.addresses; - }; - inherits(commands.addr, Message); - - commands.addr.fromObject = function(options) { - return new commands.addr(options); - }; - - commands.addr.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - - var addrCount = parser.readVarintNum(); - - var obj = {}; - obj.addresses = []; - for (var i = 0; i < addrCount; i++) { - // todo: time only available on versions >=31402 - var time = new Date(parser.readUInt32LE() * 1000); - - var addr = utils.parseAddr(parser); - addr.time = time; - obj.addresses.push(addr); - } - - utils.checkFinished(parser); - return commands.addr.fromObject(obj); - }; - - commands.addr.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.addresses.length); - - for (var i = 0; i < this.addresses.length; i++) { - var addr = this.addresses[i]; - bw.writeUInt32LE(addr.time.getTime() / 1000); - utils.writeAddr(addr, bw); - } - - return bw.concat(); - }; - - /* alert */ - - commands.alert = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'alert'; - - this.payload = options.payload || new Buffer(32); - this.signature = options.signature || new Buffer(32); - - }; - inherits(commands.alert, Message); - - commands.alert.fromObject = function(options) { - return new commands.alert(options); - }; - - commands.alert.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - obj.payload = parser.readVarLengthBuffer(); - obj.signature = parser.readVarLengthBuffer(); - utils.checkFinished(parser); - return commands.alert.fromObject(obj); - }; - - commands.alert.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.payload.length); - bw.write(this.payload); - - bw.writeVarintNum(this.signature.length); - bw.write(this.signature); - - return bw.concat(); - }; - - /* reject */ - // todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject - commands.reject = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'reject'; - }; - inherits(commands.reject, Message); - - commands.reject.fromObject = function(options) { - return new commands.reject(options); - }; - - commands.reject.fromBuffer = function(payload) { - var obj = {}; - return commands.reject.fromObject(obj); - }; - - commands.reject.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; - - /** - * Contains information about a MerkleBlock - * - * @name P2P.Message.MerkleBlock - * @param {MerkleBlock} block - */ - commands.merkleblock = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'merkleblock'; - $.checkArgument( - _.isUndefined(options.merkleBlock) || - options.merkleBlock instanceof MerkleBlock - ); - this.merkleBlock = options.merkleBlock; - }; - inherits(commands.merkleblock, Message); - - commands.merkleblock.fromObject = function(options) { - return new commands.merkleblock(options); - }; - - commands.merkleblock.fromBuffer = function(payload) { - var obj = {}; - $.checkArgument(BufferUtil.isBuffer(payload)); - obj.merkleBlock = MerkleBlock.fromBuffer(payload); - return commands.merkleblock.fromObject(obj); - }; - - commands.merkleblock.prototype.getPayload = function() { - return this.merkleBlock ? this.merkleBlock.toBuffer() : BufferUtil.EMPTY_BUFFER; - }; - - /* filterload */ - - commands.filterload = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'filterload'; - $.checkArgument(_.isUndefined(options.filter) || options.filter instanceof BloomFilter, - 'BloomFilter object or undefined required for FilterLoad'); - this.filter = options.filter; - }; - inherits(commands.filterload, Message); - - commands.filterload.fromObject = function(options) { - return new commands.filterload(options); - }; - - commands.filterload.fromBuffer = function(payload) { - var obj = {}; - obj.filter = BloomFilter.fromBuffer(payload); - return commands.filterload.fromObject(obj); - }; - - commands.filterload.prototype.getPayload = function() { - if(this.filter) { - return this.filter.toBuffer(); - } else { - return BufferUtil.EMPTY_BUFFER; - } - }; - - /** - * Request peer to add data to a bloom filter already set by 'filterload' - * - * @name P2P.Message.filteradd - * @param{Buffer} data - Array of bytes representing bloom filter data - */ - commands.filteradd = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'filteradd'; - this.data = options.data || BufferUtil.EMPTY_BUFFER; - }; - inherits(commands.filteradd, Message); - - commands.filteradd.fromObject = function(options) { - return new commands.filteradd(options); - }; - - commands.filteradd.fromBuffer = function(payload) { - var obj = {}; - $.checkArgument(payload); - var parser = new BufferReader(payload); - obj.data = parser.readVarLengthBuffer(); - utils.checkFinished(parser); - return commands.filteradd.fromObject(obj); - }; - - commands.filteradd.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.data.length); - bw.write(this.data); - return bw.concat(); - }; - - /* filterclear */ - - commands.filterclear = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'filterclear'; - }; - inherits(commands.filterclear, Message); - - commands.filterclear.fromObject = function(options) { - return new commands.filterclear(options); - }; - - commands.filterclear.fromBuffer = function(payload) { - return commands.filterclear.fromObject({}); - }; - - commands.filterclear.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; - - /** - * Query another peer about blocks. It can query for multiple block hashes, - * and the response will contain all the chains of blocks starting from those - * hashes. - * - * @param{Array} starts - array of buffers or strings with the starting block hashes - * @param{Buffer} [stop] - hash of the last block - */ - commands.getblocks = function(options) { - Message.call(this, options); - this.command = 'getblocks'; - this.version = protocolVersion; - this.magicNumber = magicNumber; - - options = utils.sanitizeStartStop(options); - this.starts = options.starts; - this.stop = options.stop; - - }; - inherits(commands.getblocks, Message); - - commands.getblocks.fromObject = function(obj) { - return new commands.getblocks(obj); - }; - - commands.getblocks.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - $.checkArgument(!parser.finished(), 'No data received in payload'); - - obj.version = parser.readUInt32LE(); - var startCount = parser.readVarintNum(); - - obj.starts = []; - for (var i = 0; i < startCount; i++) { - obj.starts.push(parser.read(32)); - } - obj.stop = parser.read(32); - utils.checkFinished(parser); - return commands.getblocks.fromObject(obj); - }; - - commands.getblocks.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeUInt32LE(this.version); - bw.writeVarintNum(this.starts.length); - for (var i = 0; i < this.starts.length; i++) { - bw.write(this.starts[i]); - } - if (this.stop.length !== 32) { - throw new Error('Invalid hash length: ' + this.stop.length); - } - bw.write(this.stop); - return bw.concat(); - }; - - /** - * Request block headers starting from a hash - * - * @param{Array} starts - array of buffers with the starting block hashes - * @param{Buffer} [stop] - hash of the last block - */ - //todo: need test data - commands.getheaders = function(options) { - Message.call(this, options); - this.command = 'getheaders'; - this.version = protocolVersion; - this.magicNumber = magicNumber; - - options = utils.sanitizeStartStop(options); - this.starts = options.starts; - this.stop = options.stop; - - }; - inherits(commands.getheaders, Message); - - commands.getheaders.fromObject = function(obj) { - return new commands.getheaders(obj); - }; - - commands.getheaders.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - $.checkArgument(!parser.finished(), 'No data received in payload'); - - obj.version = parser.readUInt32LE(); - var startCount = Math.min(parser.readVarintNum(), 500); - - obj.starts = []; - for (var i = 0; i < startCount; i++) { - obj.starts.push(parser.read(32)); - } - obj.stop = parser.read(32); - utils.checkFinished(parser); - return commands.getheaders.fromObject(obj); - }; - - commands.getheaders.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeUInt32LE(this.version); - bw.writeVarintNum(this.starts.length); - for (var i = 0; i < this.starts.length; i++) { - bw.write(this.starts[i]); - } - if (this.stop.length !== 32) { - throw new Error('Invalid hash length: ' + this.stop.length); - } - bw.write(this.stop); - return bw.concat(); - }; - - /* mempool */ - commands.mempool = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'mempool'; - }; - inherits(commands.mempool, Message); - - commands.mempool.fromObject = function(options) { - return new commands.mempool(options); - }; - - commands.mempool.fromBuffer = function(payload) { - return commands.mempool.fromObject({}); - }; - - commands.mempool.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; - - /* getaddr */ - commands.getaddr = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'getaddr'; - }; - inherits(commands.getaddr, Message); - - commands.getaddr.fromObject = function(options) { - return new commands.getaddr(options); - }; - - commands.getaddr.fromBuffer = function(payload) { - var obj = {}; - return commands.getaddr.fromObject(obj); - }; - - commands.getaddr.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; + commands.tx = require('./commands/tx')(options); + commands.getdata = require('./commands/getdata')(options); + commands.headers = require('./commands/headers')(options); + commands.notfound = require('./commands/notfound')(options); + commands.inv = require('./commands/inv')(options); + commands.addr = require('./commands/addr')(options); + commands.alert = require('./commands/alert')(options); + commands.reject = require('./commands/reject')(options); + commands.merkleblock = require('./commands/merkleblock')(options); + commands.filterload = require('./commands/filterload')(options); + commands.filteradd = require('./commands/filteradd')(options); + commands.filterclear = require('./commands/filterclear')(options); + commands.getblocks = require('./commands/getblocks')(options); + commands.getheaders = require('./commands/getheaders')(options); + commands.mempool = require('./commands/mempool')(options); + commands.getaddr = require('./commands/getaddr')(options); return exported; diff --git a/lib/messages/commands/addr.js b/lib/messages/commands/addr.js new file mode 100644 index 00000000..1aa7d79f --- /dev/null +++ b/lib/messages/commands/addr.js @@ -0,0 +1,60 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function AddrMessage(options) { + Message.call(this, options); + this.command = 'addr'; + this.magicNumber = magicNumber; + this.addresses = options.addresses; +}; +inherits(AddrMessage, Message); + +AddrMessage.fromObject = function(options) { + return new AddrMessage(options); +}; + +AddrMessage.fromBuffer = function(payload) { + var parser = new BufferReader(payload); + + var addrCount = parser.readVarintNum(); + + var obj = {}; + obj.addresses = []; + for (var i = 0; i < addrCount; i++) { + // todo: time only available on versions >=31402 + var time = new Date(parser.readUInt32LE() * 1000); + + var addr = utils.parseAddr(parser); + addr.time = time; + obj.addresses.push(addr); + } + + utils.checkFinished(parser); + return AddrMessage.fromObject(obj); +}; + +AddrMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.addresses.length); + + for (var i = 0; i < this.addresses.length; i++) { + var addr = this.addresses[i]; + bw.writeUInt32LE(addr.time.getTime() / 1000); + utils.writeAddr(addr, bw); + } + + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return AddrMessage; +}; diff --git a/lib/messages/commands/alert.js b/lib/messages/commands/alert.js new file mode 100644 index 00000000..6a3c8f3e --- /dev/null +++ b/lib/messages/commands/alert.js @@ -0,0 +1,49 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function AlertMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'alert'; + + this.payload = options.payload || new Buffer(32); + this.signature = options.signature || new Buffer(32); +} +inherits(AlertMessage, Message); + +AlertMessage.fromObject = function(options) { + return new AlertMessage(options); +}; + +AlertMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.payload = parser.readVarLengthBuffer(); + obj.signature = parser.readVarLengthBuffer(); + utils.checkFinished(parser); + return AlertMessage.fromObject(obj); +}; + +AlertMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.payload.length); + bw.write(this.payload); + + bw.writeVarintNum(this.signature.length); + bw.write(this.signature); + + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return AlertMessage; +}; diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js new file mode 100644 index 00000000..a7966f70 --- /dev/null +++ b/lib/messages/commands/filteradd.js @@ -0,0 +1,51 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferUtil = bitcore.util.buffer; +var BufferWriter = bitcore.encoding.BufferWriter; +var BufferReader = bitcore.encoding.BufferReader; +var $ = bitcore.util.preconditions; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Request peer to add data to a bloom filter already set by 'filterload' + * + * @name P2P.Message.filteradd + * @param{Buffer} data - Array of bytes representing bloom filter data + */ +function FilteraddMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filteradd'; + this.data = options.data || BufferUtil.EMPTY_BUFFER; +}; +inherits(FilteraddMessage, Message); + +FilteraddMessage.fromObject = function(options) { + return new FilteraddMessage(options); +}; + +FilteraddMessage.fromBuffer = function(payload) { + var obj = {}; + $.checkArgument(payload); + var parser = new BufferReader(payload); + obj.data = parser.readVarLengthBuffer(); + utils.checkFinished(parser); + return FilteraddMessage.fromObject(obj); +}; + +FilteraddMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.data.length); + bw.write(this.data); + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return FilteraddMessage; +}; diff --git a/lib/messages/commands/filterclear.js b/lib/messages/commands/filterclear.js new file mode 100644 index 00000000..0da1f5a7 --- /dev/null +++ b/lib/messages/commands/filterclear.js @@ -0,0 +1,32 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function FilterclearMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filterclear'; +}; +inherits(FilterclearMessage, Message); + +FilterclearMessage.fromObject = function(options) { + return new FilterclearMessage(options); +}; + +FilterclearMessage.fromBuffer = function(payload) { + return FilterclearMessage.fromObject({}); +}; + +FilterclearMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return FilterclearMessage; +}; diff --git a/lib/messages/commands/filterload.js b/lib/messages/commands/filterload.js new file mode 100644 index 00000000..7e0ece9b --- /dev/null +++ b/lib/messages/commands/filterload.js @@ -0,0 +1,44 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; +var BloomFilter = require('../../bloomfilter'); +var $ = bitcore.util.preconditions; +var _ = bitcore.deps._; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function FilterloadMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filterload'; + $.checkArgument(_.isUndefined(options.filter) || options.filter instanceof BloomFilter, + 'BloomFilter object or undefined required for FilterLoad'); + this.filter = options.filter; +} +inherits(FilterloadMessage, Message); + +FilterloadMessage.fromObject = function(options) { + return new FilterloadMessage(options); +}; + +FilterloadMessage.fromBuffer = function(payload) { + var obj = {}; + obj.filter = BloomFilter.fromBuffer(payload); + return FilterloadMessage.fromObject(obj); +}; + +FilterloadMessage.prototype.getPayload = function() { + if(this.filter) { + return this.filter.toBuffer(); + } else { + return BufferUtil.EMPTY_BUFFER; + } +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return FilterloadMessage; +}; diff --git a/lib/messages/commands/getaddr.js b/lib/messages/commands/getaddr.js new file mode 100644 index 00000000..104590fc --- /dev/null +++ b/lib/messages/commands/getaddr.js @@ -0,0 +1,33 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function GetaddrMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'getaddr'; +} +inherits(GetaddrMessage, Message); + +GetaddrMessage.fromObject = function(options) { + return new GetaddrMessage(options); +}; + +GetaddrMessage.fromBuffer = function(payload) { + var obj = {}; + return GetaddrMessage.fromObject(obj); +}; + +GetaddrMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return GetaddrMessage; +}; diff --git a/lib/messages/commands/getblocks.js b/lib/messages/commands/getblocks.js new file mode 100644 index 00000000..a45b8181 --- /dev/null +++ b/lib/messages/commands/getblocks.js @@ -0,0 +1,74 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var $ = bitcore.util.preconditions; + +var protocolVersion = 70000; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Query another peer about blocks. It can query for multiple block hashes, + * and the response will contain all the chains of blocks starting from those + * hashes. + * + * @param{Array} starts - array of buffers or strings with the starting block hashes + * @param{Buffer} [stop] - hash of the last block + */ +function GetblocksMessage(options) { + Message.call(this, options); + this.command = 'getblocks'; + this.version = protocolVersion; + this.magicNumber = magicNumber; + + options = utils.sanitizeStartStop(options); + this.starts = options.starts; + this.stop = options.stop; + +}; +inherits(GetblocksMessage, Message); + +GetblocksMessage.fromObject = function(obj) { + return new GetblocksMessage(obj); +}; + +GetblocksMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + $.checkArgument(!parser.finished(), 'No data received in payload'); + + obj.version = parser.readUInt32LE(); + var startCount = parser.readVarintNum(); + + obj.starts = []; + for (var i = 0; i < startCount; i++) { + obj.starts.push(parser.read(32)); + } + obj.stop = parser.read(32); + utils.checkFinished(parser); + return GetblocksMessage.fromObject(obj); +}; + +GetblocksMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeVarintNum(this.starts.length); + for (var i = 0; i < this.starts.length; i++) { + bw.write(this.starts[i]); + } + if (this.stop.length !== 32) { + throw new Error('Invalid hash length: ' + this.stop.length); + } + bw.write(this.stop); + return bw.concat(); +}; + +module.exports = function(options) { + protocolVersion = options.protocolVersion || protocolVersion; + magicNumber = options.magicNumber || magicNumber; + return GetblocksMessage; +}; diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js new file mode 100644 index 00000000..d8d71b1c --- /dev/null +++ b/lib/messages/commands/getdata.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function GetdataMessage(options) { + Message.call(this, options); + this.command = 'getdata'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; +} +inherits(GetdataMessage, Message); + +GetdataMessage.fromObject = function(options) { + return new GetdataMessage(options); +}; + +GetdataMessage.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + utils.checkFinished(parser); + return GetdataMessage.fromObject(obj); +}; + +GetdataMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + utils.writeInventory(this.inventory, bw); + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return GetdataMessage; +}; diff --git a/lib/messages/commands/getheaders.js b/lib/messages/commands/getheaders.js new file mode 100644 index 00000000..e272d7c8 --- /dev/null +++ b/lib/messages/commands/getheaders.js @@ -0,0 +1,72 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var $ = bitcore.util.preconditions; + +var protocolVersion = 70000; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Request block headers starting from a hash + * + * @param{Array} starts - array of buffers with the starting block hashes + * @param{Buffer} [stop] - hash of the last block + */ +function GetheadersMessage(options) { + Message.call(this, options); + this.command = 'getheaders'; + this.version = protocolVersion; + this.magicNumber = magicNumber; + + options = utils.sanitizeStartStop(options); + this.starts = options.starts; + this.stop = options.stop; + +} +inherits(GetheadersMessage, Message); + +GetheadersMessage.fromObject = function(obj) { + return new GetheadersMessage(obj); +}; + +GetheadersMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + $.checkArgument(!parser.finished(), 'No data received in payload'); + + obj.version = parser.readUInt32LE(); + var startCount = Math.min(parser.readVarintNum(), 500); + + obj.starts = []; + for (var i = 0; i < startCount; i++) { + obj.starts.push(parser.read(32)); + } + obj.stop = parser.read(32); + utils.checkFinished(parser); + return GetheadersMessage.fromObject(obj); +}; + +GetheadersMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeVarintNum(this.starts.length); + for (var i = 0; i < this.starts.length; i++) { + bw.write(this.starts[i]); + } + if (this.stop.length !== 32) { + throw new Error('Invalid hash length: ' + this.stop.length); + } + bw.write(this.stop); + return bw.concat(); +}; + +module.exports = function(options) { + protocolVersion = options.protocolVersion || protocolVersion; + magicNumber = options.magicNumber || magicNumber; + return GetheadersMessage; +}; diff --git a/lib/messages/commands/headers.js b/lib/messages/commands/headers.js new file mode 100644 index 00000000..05b15496 --- /dev/null +++ b/lib/messages/commands/headers.js @@ -0,0 +1,65 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var $ = bitcore.util.preconditions; + +var BlockHeader = bitcore.BlockHeader; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Sent in response to a `getheaders` message. It contains information about + * block headers. + * + * @param{Array} blockheaders - array of block headers + */ +function HeadersMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'headers'; + this.headers = options.headers; +}; +inherits(HeadersMessage, Message); + +HeadersMessage.fromObject = function(options) { + return new HeadersMessage(options); +}; + +HeadersMessage.fromBuffer = function(payload) { + var obj = {}; + + $.checkArgument(payload && payload.length > 0, 'No data found to create Headers message'); + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + + obj.headers = []; + for (var i = 0; i < count; i++) { + var header = BlockHeader.fromBufferReader(parser); + obj.headers.push(header); + var txn_count = parser.readUInt8(); + $.checkState(txn_count === 0, 'txn_count should always be 0'); + } + utils.checkFinished(parser); + + return HeadersMessage.fromObject(obj); +}; + +HeadersMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.headers.length); + for (var i = 0; i < this.headers.length; i++) { + var buffer = this.headers[i].toBuffer(); + bw.write(buffer); + bw.writeUInt8(0); + } + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return HeadersMessage; +}; diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js new file mode 100644 index 00000000..3ddc0bee --- /dev/null +++ b/lib/messages/commands/inv.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function InvMessage(options) { + Message.call(this, options); + this.command = 'inv'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; +} +inherits(InvMessage, Message); + +InvMessage.fromObject = function(options) { + return new InvMessage(options); +}; + +InvMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + utils.writeInventory(this.inventory, bw); + return bw.concat(); +}; + +InvMessage.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + utils.checkFinished(parser); + return InvMessage.fromObject(obj); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return InvMessage; +}; diff --git a/lib/messages/commands/mempool.js b/lib/messages/commands/mempool.js new file mode 100644 index 00000000..321f0875 --- /dev/null +++ b/lib/messages/commands/mempool.js @@ -0,0 +1,32 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function MempoolMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'mempool'; +} +inherits(MempoolMessage, Message); + +MempoolMessage.fromObject = function(options) { + return new MempoolMessage(options); +}; + +MempoolMessage.fromBuffer = function(payload) { + return MempoolMessage.fromObject({}); +}; + +MempoolMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return MempoolMessage; +}; diff --git a/lib/messages/commands/merkleblock.js b/lib/messages/commands/merkleblock.js new file mode 100644 index 00000000..af2ade1b --- /dev/null +++ b/lib/messages/commands/merkleblock.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; +var $ = bitcore.util.preconditions; +var _ = bitcore.deps._; + +var MerkleBlock = bitcore.MerkleBlock; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Contains information about a MerkleBlock + * + * @name P2P.Message.MerkleBlock + * @param {MerkleBlock} block + */ +function MerkleblockMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'merkleblock'; + $.checkArgument( + _.isUndefined(options.merkleBlock) || + options.merkleBlock instanceof MerkleBlock + ); + this.merkleBlock = options.merkleBlock; +} +inherits(MerkleblockMessage, Message); + +MerkleblockMessage.fromObject = function(options) { + return new MerkleblockMessage(options); +}; + +MerkleblockMessage.fromBuffer = function(payload) { + var obj = {}; + $.checkArgument(BufferUtil.isBuffer(payload)); + obj.merkleBlock = MerkleBlock.fromBuffer(payload); + return MerkleblockMessage.fromObject(obj); +}; + +MerkleblockMessage.prototype.getPayload = function() { + return this.merkleBlock ? this.merkleBlock.toBuffer() : BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + MerkleBlock = options.MerkleBlock || MerkleBlock; + return MerkleblockMessage; +}; diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js new file mode 100644 index 00000000..d8f27f44 --- /dev/null +++ b/lib/messages/commands/notfound.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function NotfoundMessage(options) { + Message.call(this, options); + this.command = 'notfound'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; +} +inherits(NotfoundMessage, Message); + +NotfoundMessage.fromObject = function(options) { + return new NotfoundMessage(options); +}; + +NotfoundMessage.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + utils.checkFinished(parser); + return NotfoundMessage.fromObject(obj); +}; + +NotfoundMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + utils.writeInventory(this.inventory, bw); + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return NotfoundMessage; +}; diff --git a/lib/messages/commands/pong.js b/lib/messages/commands/pong.js new file mode 100644 index 00000000..b91cf517 --- /dev/null +++ b/lib/messages/commands/pong.js @@ -0,0 +1,39 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function PongMessage(options) { + Message.call(this, options); + this.command = 'pong'; + this.magicNumber = magicNumber; + this.nonce = options.nonce; +} +inherits(PongMessage, Message); + +PongMessage.fromObject = function(obj) { + return new PongMessage(obj); +}; + +PongMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.nonce = parser.read(8); + + utils.checkFinished(parser); + return PongMessage.fromObject(obj); +}; + +PongMessage.prototype.getPayload = function() { + return this.nonce; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return PongMessage; +}; diff --git a/lib/messages/commands/reject.js b/lib/messages/commands/reject.js new file mode 100644 index 00000000..4b38d675 --- /dev/null +++ b/lib/messages/commands/reject.js @@ -0,0 +1,34 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +// todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject +function RejectMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'reject'; +}; +inherits(RejectMessage, Message); + +RejectMessage.fromObject = function(options) { + return new RejectMessage(options); +}; + +RejectMessage.fromBuffer = function(payload) { + var obj = {}; + return RejectMessage.fromObject(obj); +}; + +RejectMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return RejectMessage; +}; diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js new file mode 100644 index 00000000..676809aa --- /dev/null +++ b/lib/messages/commands/tx.js @@ -0,0 +1,40 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); + +var Transaction = bitcore.Transaction; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function TransactionMessage(options) { + Message.call(this, options); + this.command = 'tx'; + this.magicNumber = magicNumber; + this.transaction = options.transaction; +}; +inherits(TransactionMessage, Message); + +TransactionMessage.fromObject = function(options) { + return new TransactionMessage(options); +}; + +TransactionMessage.fromBuffer = function(payload) { + var transaction; + if (Transaction.prototype.fromBuffer) { + transaction = Transaction().fromBuffer(payload); + } else { + transaction = Transaction.fromBuffer(payload); + } + return TransactionMessage.fromObject({transaction: transaction}); +}; + +TransactionMessage.prototype.getPayload = function() { + return this.transaction.toBuffer(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + Transaction = options.Transaction || Transaction; + return TransactionMessage; +}; From 2eb7712ab8061e28c426ecf001b0e1bc49815913 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 10:48:45 -0400 Subject: [PATCH 15/48] organized commands to build from an array --- lib/messages/builder.js | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 38082f70..01a3a18e 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -3,8 +3,8 @@ var bitcore = require('bitcore'); function builder(options) { - /* jshint maxstatements: 50 */ - /* jshint maxcomplexity: 8 */ + /* jshint maxstatements: 20 */ + /* jshint maxcomplexity: 10 */ if (!options) { options = {}; @@ -20,8 +20,6 @@ function builder(options) { var MerkleBlock = options.MerkleBlock || bitcore.MerkleBlock; var protocolVersion = options.protocolVersion || 70000; - var commands = {}; - var exported = { constructors: { Block: Block, @@ -33,30 +31,19 @@ function builder(options) { protocolVersion: protocolVersion, magicNumber: magicNumber }, - commands: commands + commands: {} }; - commands.version = require('./commands/version')(options); - commands.verack = require('./commands/verack')(options); - commands.ping = require('./commands/ping')(options); - commands.pong = require('./commands/pong')(options); - commands.block = require('./commands/block')(options); - commands.tx = require('./commands/tx')(options); - commands.getdata = require('./commands/getdata')(options); - commands.headers = require('./commands/headers')(options); - commands.notfound = require('./commands/notfound')(options); - commands.inv = require('./commands/inv')(options); - commands.addr = require('./commands/addr')(options); - commands.alert = require('./commands/alert')(options); - commands.reject = require('./commands/reject')(options); - commands.merkleblock = require('./commands/merkleblock')(options); - commands.filterload = require('./commands/filterload')(options); - commands.filteradd = require('./commands/filteradd')(options); - commands.filterclear = require('./commands/filterclear')(options); - commands.getblocks = require('./commands/getblocks')(options); - commands.getheaders = require('./commands/getheaders')(options); - commands.mempool = require('./commands/mempool')(options); - commands.getaddr = require('./commands/getaddr')(options); + var commandsArray = [ + 'version', 'verack', 'ping', 'pong', 'block', 'tx', 'getdata', 'headers', 'notfound', + 'inv', 'addr', 'alert', 'reject', 'merkleblock', 'filterload', 'filteradd', 'filterclear', + 'getblocks', 'getheaders', 'mempool', 'getaddr' + ]; + + for (var i = 0; i < commandsArray.length; i++) { + var command = commandsArray[i]; + exported.commands[command] = require('./commands/' + command)(options); + } return exported; From 091893b1e404a3a2e3280c3614bd67ab22681cd0 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 10:58:36 -0400 Subject: [PATCH 16/48] whitespace cleanup --- lib/messages/commands/addr.js | 10 +++++----- lib/messages/commands/alert.js | 6 +++--- lib/messages/commands/getblocks.js | 6 +++--- lib/messages/commands/getdata.js | 4 ++-- lib/messages/commands/getheaders.js | 8 ++++---- lib/messages/commands/inv.js | 4 ++-- lib/messages/commands/ping.js | 3 +-- lib/messages/commands/pong.js | 2 +- lib/messages/commands/verack.js | 1 - 9 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/messages/commands/addr.js b/lib/messages/commands/addr.js index 1aa7d79f..5491c09a 100644 --- a/lib/messages/commands/addr.js +++ b/lib/messages/commands/addr.js @@ -25,18 +25,18 @@ AddrMessage.fromBuffer = function(payload) { var parser = new BufferReader(payload); var addrCount = parser.readVarintNum(); - + var obj = {}; obj.addresses = []; for (var i = 0; i < addrCount; i++) { // todo: time only available on versions >=31402 var time = new Date(parser.readUInt32LE() * 1000); - + var addr = utils.parseAddr(parser); addr.time = time; obj.addresses.push(addr); } - + utils.checkFinished(parser); return AddrMessage.fromObject(obj); }; @@ -44,13 +44,13 @@ AddrMessage.fromBuffer = function(payload) { AddrMessage.prototype.getPayload = function() { var bw = new BufferWriter(); bw.writeVarintNum(this.addresses.length); - + for (var i = 0; i < this.addresses.length; i++) { var addr = this.addresses[i]; bw.writeUInt32LE(addr.time.getTime() / 1000); utils.writeAddr(addr, bw); } - + return bw.concat(); }; diff --git a/lib/messages/commands/alert.js b/lib/messages/commands/alert.js index 6a3c8f3e..7e4926a4 100644 --- a/lib/messages/commands/alert.js +++ b/lib/messages/commands/alert.js @@ -13,7 +13,7 @@ function AlertMessage(options) { Message.call(this, options); this.magicNumber = magicNumber; this.command = 'alert'; - + this.payload = options.payload || new Buffer(32); this.signature = options.signature || new Buffer(32); } @@ -36,10 +36,10 @@ AlertMessage.prototype.getPayload = function() { var bw = new BufferWriter(); bw.writeVarintNum(this.payload.length); bw.write(this.payload); - + bw.writeVarintNum(this.signature.length); bw.write(this.signature); - + return bw.concat(); }; diff --git a/lib/messages/commands/getblocks.js b/lib/messages/commands/getblocks.js index a45b8181..e8ff3881 100644 --- a/lib/messages/commands/getblocks.js +++ b/lib/messages/commands/getblocks.js @@ -24,7 +24,7 @@ function GetblocksMessage(options) { this.command = 'getblocks'; this.version = protocolVersion; this.magicNumber = magicNumber; - + options = utils.sanitizeStartStop(options); this.starts = options.starts; this.stop = options.stop; @@ -40,10 +40,10 @@ GetblocksMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); $.checkArgument(!parser.finished(), 'No data received in payload'); - + obj.version = parser.readUInt32LE(); var startCount = parser.readVarintNum(); - + obj.starts = []; for (var i = 0; i < startCount; i++) { obj.starts.push(parser.read(32)); diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index d8d71b1c..229bcb4f 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -25,7 +25,7 @@ GetdataMessage.fromBuffer = function(payload) { var obj = { inventory: [] }; - + var parser = new BufferReader(payload); var count = parser.readVarintNum(); for (var i = 0; i < count; i++) { @@ -33,7 +33,7 @@ GetdataMessage.fromBuffer = function(payload) { var hash = parser.read(32); obj.inventory.push({type: type, hash: hash}); } - + utils.checkFinished(parser); return GetdataMessage.fromObject(obj); }; diff --git a/lib/messages/commands/getheaders.js b/lib/messages/commands/getheaders.js index e272d7c8..97a5068a 100644 --- a/lib/messages/commands/getheaders.js +++ b/lib/messages/commands/getheaders.js @@ -22,11 +22,11 @@ function GetheadersMessage(options) { this.command = 'getheaders'; this.version = protocolVersion; this.magicNumber = magicNumber; - + options = utils.sanitizeStartStop(options); this.starts = options.starts; this.stop = options.stop; - + } inherits(GetheadersMessage, Message); @@ -38,10 +38,10 @@ GetheadersMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); $.checkArgument(!parser.finished(), 'No data received in payload'); - + obj.version = parser.readUInt32LE(); var startCount = Math.min(parser.readVarintNum(), 500); - + obj.starts = []; for (var i = 0; i < startCount; i++) { obj.starts.push(parser.read(32)); diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js index 3ddc0bee..dba560a4 100644 --- a/lib/messages/commands/inv.js +++ b/lib/messages/commands/inv.js @@ -31,7 +31,7 @@ InvMessage.fromBuffer = function(payload) { var obj = { inventory: [] }; - + var parser = new BufferReader(payload); var count = parser.readVarintNum(); for (var i = 0; i < count; i++) { @@ -39,7 +39,7 @@ InvMessage.fromBuffer = function(payload) { var hash = parser.read(32); obj.inventory.push({type: type, hash: hash}); } - + utils.checkFinished(parser); return InvMessage.fromObject(obj); }; diff --git a/lib/messages/commands/ping.js b/lib/messages/commands/ping.js index 462346db..84536b9d 100644 --- a/lib/messages/commands/ping.js +++ b/lib/messages/commands/ping.js @@ -31,7 +31,7 @@ PingMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); obj.nonce = parser.read(8); - + utils.checkFinished(parser); return PingMessage.fromObject(obj); }; @@ -40,4 +40,3 @@ module.exports = function(options) { magicNumber = options.magicNumber || magicNumber; return PingMessage; }; - diff --git a/lib/messages/commands/pong.js b/lib/messages/commands/pong.js index b91cf517..66eb4f4b 100644 --- a/lib/messages/commands/pong.js +++ b/lib/messages/commands/pong.js @@ -24,7 +24,7 @@ PongMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); obj.nonce = parser.read(8); - + utils.checkFinished(parser); return PongMessage.fromObject(obj); }; diff --git a/lib/messages/commands/verack.js b/lib/messages/commands/verack.js index 56194575..16536120 100644 --- a/lib/messages/commands/verack.js +++ b/lib/messages/commands/verack.js @@ -30,4 +30,3 @@ module.exports = function(options) { magicNumber = options.magicNumber || magicNumber; return VerackMessage; }; - From faf2bb1868ae7a6594b826e650c80d8ae7a2b890 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 12:17:57 -0400 Subject: [PATCH 17/48] updated bitcoind integration test --- integration/bitcoind.js | 58 ++++++++++++++++++-------------- lib/messages/commands/getaddr.js | 3 ++ lib/peer.js | 2 +- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/integration/bitcoind.js b/integration/bitcoind.js index 920e9e78..97e6b39d 100644 --- a/integration/bitcoind.js +++ b/integration/bitcoind.js @@ -16,6 +16,8 @@ var Peer = p2p.Peer; var Pool = p2p.Pool; var Networks = bitcore.Networks; var Messages = p2p.Messages; +var Inventory = p2p.Inventory; +var messages = new Messages(); var Block = bitcore.Block; var Transaction = bitcore.Transaction; @@ -45,7 +47,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() { m.services.toString().should.equal('1'); Math.abs(new Date() - m.timestamp).should.be.below(10000); // less than 10 seconds of time difference m.nonce.length.should.equal(8); - m.start_height.should.be.above(300000); + m.startHeight.should.be.above(300000); cb(); }); peer.once('verack', function(m) { @@ -92,7 +94,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() { }); cb(); }); - var message = new Messages.GetAddresses(); + var message = messages.build('getaddr'); peer.sendMessage(message); }); }); @@ -107,24 +109,23 @@ describe('Integration with ' + network.name + ' bitcoind', function() { cb(); }); peer.once('inv', function(m) { - var message = new Messages.GetData(m.inventory); + var message = messages.build('getdata', {inventory: m.inventory}); peer.sendMessage(message); }); }); }); it('sends tx inv and receives getdata for that tx', function(cb) { connect(function(peer) { - var type = Messages.Inventory.TYPE.TX; + var type = Inventory.TYPE.TX; var inv = [{ type: type, - typeName: Messages.Inventory.TYPE_NAME[type], - hash: Random.getRandomBuffer(32) // needs to be random for repeatability + hash: new Buffer(Random.getRandomBuffer(32)) // needs to be random for repeatability }]; peer.once('getdata', function(message) { message.inventory.should.deep.equal(inv); cb(); }); - var message = new Messages.Inventory(inv); + var message = messages.build('inv', {inventory: inv}); message.inventory[0].hash.length.should.equal(32); peer.sendMessage(message); }); @@ -135,20 +136,23 @@ describe('Integration with ' + network.name + ' bitcoind', function() { (message.block instanceof Block).should.equal(true); cb(); }); - var message = Messages.GetData.forBlock(blockHash[network.name]); + var inventory = Inventory.forBlock(blockHash[network.name]); + var message = messages.build('getdata', {inventory: [inventory]}); peer.sendMessage(message); }); }); var fakeHash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a'; it('handles request tx data not found', function(cb) { connect(function(peer) { - var expected = Messages.NotFound.forTransaction(fakeHash); + var inventory = Inventory.forTransaction(fakeHash); + var expected = messages.build('notfound', {inventory: [inventory]}); peer.once('notfound', function(message) { - (message instanceof Messages.NotFound).should.equal(true); - message.should.deep.equal(expected); + message.command.should.equal('notfound'); + message.inventory[0].type.should.equal(Inventory.TYPE.TX); + message.inventory[0].hash.toString('hex').should.equal(inventory.hash.toString('hex')); cb(); }); - var message = Messages.GetData.forTransaction(fakeHash); + var message = messages.build('getdata', {inventory: [inventory]}); peer.sendMessage(message); }); }); @@ -157,47 +161,51 @@ describe('Integration with ' + network.name + ' bitcoind', function() { it('gets headers', function(cb) { connect(function(peer) { peer.once('headers', function(message) { - (message instanceof Messages.Headers).should.equal(true); + message.command.should.equal('headers'); message.headers.length.should.equal(3); cb(); }); - var message = new Messages.GetHeaders(from, stop); + var message = messages.build('getheaders', {starts: from, stop: stop}); peer.sendMessage(message); }); }); it('gets blocks', function(cb) { connect(function(peer) { peer.once('inv', function(message) { - (message instanceof Messages.Inventory).should.equal(true); + message.command.should.equal('inv'); if (message.inventory.length === 2) { - message.inventory[0].type.should.equal(Messages.Inventory.TYPE.BLOCK); + message.inventory[0].type.should.equal(Inventory.TYPE.BLOCK); cb(); } }); - var message = new Messages.GetBlocks(from, stop); + var message = messages.build('getblocks', {starts: from, stop: stop}); peer.sendMessage(message); }); }); var testInvGetData = function(expected, message, cb) { connect(function(peer) { peer.once('getdata', function(message) { - (message instanceof Messages.GetData).should.equal(true); - message.should.deep.equal(expected); + message.command.should.equal('getdata'); + message.inventory[0].type.should.equal(expected.inventory[0].type); + var expectedHash = expected.inventory[0].hash.toString('hex'); + message.inventory[0].hash.toString('hex').should.equal(expectedHash); cb(); }); peer.sendMessage(message); }); }; it('sends block inv and receives getdata', function(cb) { - var randomHash = Random.getRandomBuffer(32); // needs to be random for repeatability - var expected = Messages.GetData.forBlock(randomHash); - var message = Messages.Inventory.forBlock(randomHash); + var randomHash = new Buffer(Random.getRandomBuffer(32)); // slow buffer + var inventory = Inventory.forBlock(randomHash); + var expected = messages.build('getdata', {inventory: [inventory]}); + var message = messages.build('inv', {inventory: [inventory]}); testInvGetData(expected, message, cb); }); it('sends tx inv and receives getdata', function(cb) { - var randomHash = Random.getRandomBuffer(32); // needs to be random for repeatability - var expected = Messages.GetData.forTransaction(randomHash); - var message = Messages.Inventory.forTransaction(randomHash); + var randomHash = new Buffer(Random.getRandomBuffer(32)); // slow buffer + var inventory = Inventory.forTransaction(randomHash); + var expected = messages.build('getdata', {inventory: [inventory]}); + var message = messages.build('inv', {inventory: [inventory]}); testInvGetData(expected, message, cb); }); }); diff --git a/lib/messages/commands/getaddr.js b/lib/messages/commands/getaddr.js index 104590fc..fa97d9df 100644 --- a/lib/messages/commands/getaddr.js +++ b/lib/messages/commands/getaddr.js @@ -8,6 +8,9 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function GetaddrMessage(options) { + if (!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'getaddr'; diff --git a/lib/peer.js b/lib/peer.js index b55c6f55..ac05bb2c 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -71,7 +71,7 @@ function Peer(options) { this.on('version', function(message) { self.version = message.version; self.subversion = message.subversion; - self.bestHeight = message.start_height; + self.bestHeight = message.startHeight; }); this.on('ping', function(message) { From 7f61fc62d4c2b858cc34e6e9c30b5880cba7a85b Mon Sep 17 00:00:00 2001 From: Patrick Nagurny Date: Thu, 12 Mar 2015 18:22:45 -0400 Subject: [PATCH 18/48] listen for incoming connections --- lib/messages/commands/verack.js | 4 ++ lib/peer.js | 37 +++++++++++++---- lib/pool.js | 74 ++++++++++++++++++++++++++------- 3 files changed, 93 insertions(+), 22 deletions(-) diff --git a/lib/messages/commands/verack.js b/lib/messages/commands/verack.js index 16536120..5def3e26 100644 --- a/lib/messages/commands/verack.js +++ b/lib/messages/commands/verack.js @@ -8,6 +8,10 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function VerackMessage(options) { + if(!options) { + options = {}; + } + Message.call(this, options); this.magicNumber = magicNumber; this.command = 'verack'; diff --git a/lib/peer.js b/lib/peer.js index ac05bb2c..2757543e 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -38,10 +38,17 @@ function Peer(options) { return new Peer(options); } - this.host = options.host || 'localhost'; - this.status = Peer.STATUS.DISCONNECTED; - this.port = options.port; - + if(options.socket) { + this.socket = options.socket; + this.host = this.socket.remoteAddress; + this.port = this.socket.remotePort; + this.status = Peer.STATUS.CONNECTED; + this._addSocketEventHandlers(); + } else { + this.host = options.host || 'localhost'; + this.status = Peer.STATUS.DISCONNECTED; + this.port = options.port; + } this.network = Networks.get(options.network) || Networks.defaultNetwork; if (!this.port) { @@ -61,6 +68,8 @@ function Peer(options) { this.subversion = null; this.relay = options.relay === false ? false : true; + this.versionSent = false; + // set message handlers var self = this; this.on('verack', function() { @@ -72,6 +81,13 @@ function Peer(options) { self.version = message.version; self.subversion = message.subversion; self.bestHeight = message.startHeight; + + var verackResponse = self.messages.build('verack'); + self.sendMessage(verackResponse); + + if(!self.versionSent) { + self._sendVersion(); + } }); this.on('ping', function(message) { @@ -122,6 +138,14 @@ Peer.prototype.connect = function() { self._sendVersion(); }); + this._addSocketEventHandlers(); + this.socket.connect(this.port, this.host); + return this; +}; + +Peer.prototype._addSocketEventHandlers = function() { + var self = this; + this.socket.on('error', self._onError.bind(this)); this.socket.on('end', self.disconnect.bind(this)); @@ -134,13 +158,11 @@ Peer.prototype.connect = function() { } self._readMessage(); }); - - this.socket.connect(this.port, this.host); - return this; }; Peer.prototype._onError = function(e) { this.emit('error', e); + this.disconnect(); }; /** @@ -172,6 +194,7 @@ Peer.prototype._sendVersion = function() { var message = this.messages.build('version', { relay: this.relay }); + this.versionSent = true; this.sendMessage(message); }; diff --git a/lib/pool.js b/lib/pool.js index 7e613e6f..70481bf7 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -7,6 +7,7 @@ var sha256 = bitcore.crypto.Hash.sha256; var Peer = require('./peer'); var Networks = bitcore.Networks; var util = require('util'); +var net = require('net'); function now() { return Math.floor(new Date().getTime() / 1000); @@ -126,6 +127,8 @@ Pool.prototype.connect = function connect() { } else { self._fillConnections(); } + + this.listen(); return this; }; @@ -185,40 +188,61 @@ Pool.prototype._removeConnectedPeer = function _removeConnectedPeer(addr) { Pool.prototype._connectPeer = function _connectPeer(addr) { var self = this; - function addConnectedPeer(addr) { + if (!this._connectedPeers[addr.hash]) { var port = addr.port || self.network.port; var ip = addr.ip.v4 || addr.ip.v6; var peer = new Peer({ - ip: ip, + host: ip, port: port, messages: self.messages, relay: self.relay }); - peer.on('disconnect', function peerDisconnect() { - self.emit('peerdisconnect', peer, addr); - }); + peer.on('connect', function peerConnect() { self.emit('peerconnect', peer, addr); }); - peer.on('ready', function peerReady() { - self.emit('peerready', peer, addr); - }); - Pool.PeerEvents.forEach(function addPeerEvents(event) { - peer.on(event, function peerEvent(message) { - self.emit('peer' + event, peer, message); - }); - }); + + self._addPeerEventHandlers(peer, addr); peer.connect(); self._connectedPeers[addr.hash] = peer; } + return this; +}; + +Pool.prototype._addConnectedPeer = function _addConnectedPeer(socket, addr) { + var self = this; + if (!this._connectedPeers[addr.hash]) { - addConnectedPeer(addr); + var peer = new Peer({ + socket: socket, + messages: self.messages + }); + + self._addPeerEventHandlers(peer, addr); + self._connectedPeers[addr.hash] = peer; + self.emit('peerconnect', peer, addr); } return this; }; +Pool.prototype._addPeerEventHandlers = function(peer, addr) { + var self = this; + + peer.on('disconnect', function peerDisconnect() { + self.emit('peerdisconnect', peer, addr); + }); + peer.on('ready', function peerReady() { + self.emit('peerready', peer, addr); + }); + Pool.PeerEvents.forEach(function addPeerEvents(event) { + peer.on(event, function peerEvent(message) { + self.emit('peer' + event, peer, message); + }); + }); +}; + /** * Will deprioritize an addr in the list of addrs by moving it to the end * of the array, and setting a retryTime @@ -257,7 +281,7 @@ Pool.prototype._addAddr = function _addAddr(addr) { if (!exists) { this._addrs.unshift(addr); } - return this; + return addr; }; /** @@ -313,4 +337,24 @@ Pool.prototype.sendMessage = function(message) { } }; +Pool.prototype.listen = function() { + var self = this; + + // Create server + this.server = net.createServer(function(socket) { + var addr = { + ip: {} + }; + if(net.isIPv6(socket.remoteAddress)) { + addr.ip.v6 = socket.remoteAddress; + } else { + addr.ip.v4 = socket.remoteAddress; + } + + addr = self._addAddr(addr); + self._addConnectedPeer(socket, addr); + }); + this.server.listen(this.network.port); +}; + module.exports = Pool; From 6461748cd52a333d4f91dcea617ddda43b203e71 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 18:50:30 -0400 Subject: [PATCH 19/48] make listening optional and fixed peer test --- lib/peer.js | 3 ++- lib/pool.js | 4 +--- test/peer.js | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/peer.js b/lib/peer.js index 2757543e..9249057b 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -31,7 +31,7 @@ var util = require('util'); * @constructor */ function Peer(options) { - /* jshint maxstatements: 20 */ + /* jshint maxstatements: 25 */ /* jshint maxcomplexity: 8 */ if (!(this instanceof Peer)) { @@ -51,6 +51,7 @@ function Peer(options) { } this.network = Networks.get(options.network) || Networks.defaultNetwork; + if (!this.port) { this.port = this.network.port; } diff --git a/lib/pool.js b/lib/pool.js index 70481bf7..3e60535e 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -127,8 +127,6 @@ Pool.prototype.connect = function connect() { } else { self._fillConnections(); } - - this.listen(); return this; }; @@ -340,7 +338,7 @@ Pool.prototype.sendMessage = function(message) { Pool.prototype.listen = function() { var self = this; - // Create server + // Create server this.server = net.createServer(function(socket) { var addr = { ip: {} diff --git a/test/peer.js b/test/peer.js index 02a8c0ab..ddcd65c9 100644 --- a/test/peer.js +++ b/test/peer.js @@ -139,6 +139,7 @@ describe('Peer', function() { var peer = new Peer({host: 'localhost'}); var socket = new EventEmitter(); socket.connect = sinon.spy(); + socket.destroy = sinon.spy(); peer._getSocket = function() { return socket; }; From 641443f6dda7e91b239544b64887cabf28bf48af Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 10:15:41 -0400 Subject: [PATCH 20/48] expose message construtors by name --- lib/messages/builder.js | 35 ++++++++++++++++++++++++++--------- lib/messages/index.js | 7 +++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 01a3a18e..cc25690f 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -34,15 +34,32 @@ function builder(options) { commands: {} }; - var commandsArray = [ - 'version', 'verack', 'ping', 'pong', 'block', 'tx', 'getdata', 'headers', 'notfound', - 'inv', 'addr', 'alert', 'reject', 'merkleblock', 'filterload', 'filteradd', 'filterclear', - 'getblocks', 'getheaders', 'mempool', 'getaddr' - ]; - - for (var i = 0; i < commandsArray.length; i++) { - var command = commandsArray[i]; - exported.commands[command] = require('./commands/' + command)(options); + exported.commandsMap = { + version: 'Version', + verack: 'VerAck', + ping: 'Ping', + pong: 'Pong', + block: 'Block', + tx: 'Transaction', + getdata: 'GetData', + headers: 'Headers', + notfound: 'NotFound', + inv: 'Inventory', + addr: 'Address', + alert: 'Alert', + reject: 'Reject', + merkleblock: 'MerkleBlock', + filterload: 'FilterLoad', + filteradd: 'FilterAdd', + filterclear: 'FilterClear', + getblocks: 'GetBlocks', + getheaders: 'GetHeaders', + mempool: 'MemPool', + getaddr: 'GetAddr' + }; + + for (var key in exported.commandsMap) { + exported.commands[key] = require('./commands/' + key)(options); } return exported; diff --git a/lib/messages/index.js b/lib/messages/index.js index ed59af1a..8369e999 100644 --- a/lib/messages/index.js +++ b/lib/messages/index.js @@ -6,6 +6,13 @@ var Hash = bitcore.crypto.Hash; function Messages(options) { this.builder = Messages.builder(options); + + // map message constructors by name + for(var key in this.builder.commandsMap) { + var name = this.builder.commandsMap[key]; + this[name] = this.builder.commands[key]; + } + if (!options) { options = {}; } From 4ecb34e123cfc1e144839d9d3787124c65dce305 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 12:06:07 -0400 Subject: [PATCH 21/48] updated bitconid integration test to use exposed message constructors --- integration/bitcoind.js | 39 +++++++++++++--------------- lib/inventory.js | 1 - lib/messages/commands/addr.js | 3 +++ lib/messages/commands/alert.js | 3 +++ lib/messages/commands/block.js | 3 +++ lib/messages/commands/filteradd.js | 3 +++ lib/messages/commands/filterclear.js | 3 +++ lib/messages/commands/filterload.js | 3 +++ lib/messages/commands/getaddr.js | 3 +++ lib/messages/commands/getblocks.js | 3 +++ lib/messages/commands/getdata.js | 28 +++++++++++++++++++- lib/messages/commands/getheaders.js | 3 +++ lib/messages/commands/headers.js | 3 +++ lib/messages/commands/inv.js | 27 ++++++++++++++++++- lib/messages/commands/mempool.js | 3 +++ lib/messages/commands/merkleblock.js | 3 +++ lib/messages/commands/notfound.js | 27 ++++++++++++++++++- lib/messages/commands/ping.js | 3 +++ lib/messages/commands/pong.js | 3 +++ lib/messages/commands/reject.js | 3 +++ lib/messages/commands/tx.js | 3 +++ lib/messages/commands/verack.js | 3 +++ lib/messages/commands/version.js | 3 +++ lib/messages/utils.js | 8 ++++++ 24 files changed, 159 insertions(+), 25 deletions(-) diff --git a/integration/bitcoind.js b/integration/bitcoind.js index 97e6b39d..e1a1448d 100644 --- a/integration/bitcoind.js +++ b/integration/bitcoind.js @@ -94,7 +94,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() { }); cb(); }); - var message = messages.build('getaddr'); + var message = messages.GetAddr(); peer.sendMessage(message); }); }); @@ -108,9 +108,9 @@ describe('Integration with ' + network.name + ' bitcoind', function() { should.exist(message.transaction); cb(); }); - peer.once('inv', function(m) { - var message = messages.build('getdata', {inventory: m.inventory}); - peer.sendMessage(message); + peer.once('inv', function(message) { + var get = messages.GetData(message.inventory); + peer.sendMessage(get); }); }); }); @@ -125,7 +125,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() { message.inventory.should.deep.equal(inv); cb(); }); - var message = messages.build('inv', {inventory: inv}); + var message = messages.Inventory(inv); message.inventory[0].hash.length.should.equal(32); peer.sendMessage(message); }); @@ -136,23 +136,22 @@ describe('Integration with ' + network.name + ' bitcoind', function() { (message.block instanceof Block).should.equal(true); cb(); }); - var inventory = Inventory.forBlock(blockHash[network.name]); - var message = messages.build('getdata', {inventory: [inventory]}); + var message = messages.GetData.forBlock(blockHash[network.name]); peer.sendMessage(message); }); }); var fakeHash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a'; it('handles request tx data not found', function(cb) { connect(function(peer) { - var inventory = Inventory.forTransaction(fakeHash); - var expected = messages.build('notfound', {inventory: [inventory]}); + var expected = messages.NotFound.forTransaction(fakeHash); peer.once('notfound', function(message) { - message.command.should.equal('notfound'); + (message instanceof messages.NotFound).should.equal(true); message.inventory[0].type.should.equal(Inventory.TYPE.TX); - message.inventory[0].hash.toString('hex').should.equal(inventory.hash.toString('hex')); + var expectedHash = expected.inventory[0].hash.toString('hex'); + message.inventory[0].hash.toString('hex').should.equal(expectedHash); cb(); }); - var message = messages.build('getdata', {inventory: [inventory]}); + var message = messages.GetData.forTransaction(fakeHash); peer.sendMessage(message); }); }); @@ -161,11 +160,11 @@ describe('Integration with ' + network.name + ' bitcoind', function() { it('gets headers', function(cb) { connect(function(peer) { peer.once('headers', function(message) { - message.command.should.equal('headers'); + (message instanceof messages.Headers).should.equal(true); message.headers.length.should.equal(3); cb(); }); - var message = messages.build('getheaders', {starts: from, stop: stop}); + var message = messages.GetHeaders({starts: from, stop: stop}); peer.sendMessage(message); }); }); @@ -178,7 +177,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() { cb(); } }); - var message = messages.build('getblocks', {starts: from, stop: stop}); + var message = messages.GetBlocks({starts: from, stop: stop}); peer.sendMessage(message); }); }); @@ -196,16 +195,14 @@ describe('Integration with ' + network.name + ' bitcoind', function() { }; it('sends block inv and receives getdata', function(cb) { var randomHash = new Buffer(Random.getRandomBuffer(32)); // slow buffer - var inventory = Inventory.forBlock(randomHash); - var expected = messages.build('getdata', {inventory: [inventory]}); - var message = messages.build('inv', {inventory: [inventory]}); + var expected = messages.GetData.forBlock(randomHash); + var message = messages.Inventory.forBlock(randomHash); testInvGetData(expected, message, cb); }); it('sends tx inv and receives getdata', function(cb) { var randomHash = new Buffer(Random.getRandomBuffer(32)); // slow buffer - var inventory = Inventory.forTransaction(randomHash); - var expected = messages.build('getdata', {inventory: [inventory]}); - var message = messages.build('inv', {inventory: [inventory]}); + var expected = messages.GetData.forTransaction(randomHash); + var message = messages.Inventory.forTransaction(randomHash); testInvGetData(expected, message, cb); }); }); diff --git a/lib/inventory.js b/lib/inventory.js index ba6b19e3..e5e7e96f 100644 --- a/lib/inventory.js +++ b/lib/inventory.js @@ -13,7 +13,6 @@ function Inventory(obj) { throw new TypeError('Unexpected hash, expected to be a buffer'); } this.hash = obj.hash; - } Inventory.forItem = function(type, hash) { diff --git a/lib/messages/commands/addr.js b/lib/messages/commands/addr.js index 5491c09a..e8c17726 100644 --- a/lib/messages/commands/addr.js +++ b/lib/messages/commands/addr.js @@ -10,6 +10,9 @@ var BufferWriter = bitcore.encoding.BufferWriter; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function AddrMessage(options) { + if (!(this instanceof AddrMessage)) { + return new AddrMessage(options); + } Message.call(this, options); this.command = 'addr'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/alert.js b/lib/messages/commands/alert.js index 7e4926a4..17f7902f 100644 --- a/lib/messages/commands/alert.js +++ b/lib/messages/commands/alert.js @@ -10,6 +10,9 @@ var BufferWriter = bitcore.encoding.BufferWriter; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function AlertMessage(options) { + if (!(this instanceof AlertMessage)) { + return new AlertMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'alert'; diff --git a/lib/messages/commands/block.js b/lib/messages/commands/block.js index f29450f0..29876de8 100644 --- a/lib/messages/commands/block.js +++ b/lib/messages/commands/block.js @@ -8,6 +8,9 @@ var Block = bitcore.Block; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function BlockMessage(options) { + if (!(this instanceof BlockMessage)) { + return new BlockMessage(options); + } Message.call(this, options); this.command = 'block'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js index a7966f70..ed67f8f4 100644 --- a/lib/messages/commands/filteradd.js +++ b/lib/messages/commands/filteradd.js @@ -18,6 +18,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); * @param{Buffer} data - Array of bytes representing bloom filter data */ function FilteraddMessage(options) { + if (!(this instanceof FilteraddMessage)) { + return new FilteraddMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'filteradd'; diff --git a/lib/messages/commands/filterclear.js b/lib/messages/commands/filterclear.js index 0da1f5a7..8acd5828 100644 --- a/lib/messages/commands/filterclear.js +++ b/lib/messages/commands/filterclear.js @@ -8,6 +8,9 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function FilterclearMessage(options) { + if (!(this instanceof FilterclearMessage)) { + return new FilterclearMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'filterclear'; diff --git a/lib/messages/commands/filterload.js b/lib/messages/commands/filterload.js index 7e0ece9b..04c1a973 100644 --- a/lib/messages/commands/filterload.js +++ b/lib/messages/commands/filterload.js @@ -11,6 +11,9 @@ var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function FilterloadMessage(options) { + if (!(this instanceof FilterloadMessage)) { + return new FilterloadMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'filterload'; diff --git a/lib/messages/commands/getaddr.js b/lib/messages/commands/getaddr.js index fa97d9df..70d0e7b8 100644 --- a/lib/messages/commands/getaddr.js +++ b/lib/messages/commands/getaddr.js @@ -8,6 +8,9 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function GetaddrMessage(options) { + if (!(this instanceof GetaddrMessage)) { + return new GetaddrMessage(options); + } if (!options) { options = {}; } diff --git a/lib/messages/commands/getblocks.js b/lib/messages/commands/getblocks.js index e8ff3881..97ae5d78 100644 --- a/lib/messages/commands/getblocks.js +++ b/lib/messages/commands/getblocks.js @@ -20,6 +20,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); * @param{Buffer} [stop] - hash of the last block */ function GetblocksMessage(options) { + if (!(this instanceof GetblocksMessage)) { + return new GetblocksMessage(options); + } Message.call(this, options); this.command = 'getblocks'; this.version = protocolVersion; diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index 229bcb4f..266b17ea 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -6,17 +6,43 @@ var bitcore = require('bitcore'); var utils = require('../utils'); var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; +var Inventory = require('../../inventory'); +var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function GetdataMessage(options) { + if (!(this instanceof GetdataMessage)) { + return new GetdataMessage(options); + } Message.call(this, options); this.command = 'getdata'; this.magicNumber = magicNumber; - this.inventory = options.inventory; + + var inventory; + if (_.isArray(options)) { + inventory = options; + } else { + inventory = options.inventory; + } + + utils.checkInventory(inventory); + this.inventory = inventory; } inherits(GetdataMessage, Message); +GetdataMessage.forTransaction = function(hash) { + return new GetdataMessage([Inventory.forTransaction(hash)]); +}; + +GetdataMessage.forBlock = function(hash) { + return new GetdataMessage([Inventory.forBlock(hash)]); +}; + +GetdataMessage.forFilteredBlock = function(hash) { + return new GetdataMessage([Inventory.forFilteredBlock(hash)]); +}; + GetdataMessage.fromObject = function(options) { return new GetdataMessage(options); }; diff --git a/lib/messages/commands/getheaders.js b/lib/messages/commands/getheaders.js index 97a5068a..0beb5509 100644 --- a/lib/messages/commands/getheaders.js +++ b/lib/messages/commands/getheaders.js @@ -18,6 +18,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); * @param{Buffer} [stop] - hash of the last block */ function GetheadersMessage(options) { + if (!(this instanceof GetheadersMessage)) { + return new GetheadersMessage(options); + } Message.call(this, options); this.command = 'getheaders'; this.version = protocolVersion; diff --git a/lib/messages/commands/headers.js b/lib/messages/commands/headers.js index 05b15496..4911684e 100644 --- a/lib/messages/commands/headers.js +++ b/lib/messages/commands/headers.js @@ -18,6 +18,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); * @param{Array} blockheaders - array of block headers */ function HeadersMessage(options) { + if (!(this instanceof HeadersMessage)) { + return new HeadersMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'headers'; diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js index dba560a4..83cfbc34 100644 --- a/lib/messages/commands/inv.js +++ b/lib/messages/commands/inv.js @@ -6,17 +6,42 @@ var bitcore = require('bitcore'); var utils = require('../utils'); var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; +var Inventory = require('../../inventory'); +var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function InvMessage(options) { + if (!(this instanceof InvMessage)) { + return new InvMessage(options); + } Message.call(this, options); this.command = 'inv'; this.magicNumber = magicNumber; - this.inventory = options.inventory; + + var inventory; + if (_.isArray(options)) { + inventory = options; + } else { + inventory = options.inventory; + } + utils.checkInventory(inventory); + this.inventory = inventory; } inherits(InvMessage, Message); +InvMessage.forTransaction = function(hash) { + return new InvMessage([Inventory.forTransaction(hash)]); +}; + +InvMessage.forBlock = function(hash) { + return new InvMessage([Inventory.forBlock(hash)]); +}; + +InvMessage.forFilteredBlock = function(hash) { + return new InvMessage([Inventory.forFilteredBlock(hash)]); +}; + InvMessage.fromObject = function(options) { return new InvMessage(options); }; diff --git a/lib/messages/commands/mempool.js b/lib/messages/commands/mempool.js index 321f0875..45e3b5ad 100644 --- a/lib/messages/commands/mempool.js +++ b/lib/messages/commands/mempool.js @@ -8,6 +8,9 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function MempoolMessage(options) { + if (!(this instanceof MempoolMessage)) { + return new MempoolMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'mempool'; diff --git a/lib/messages/commands/merkleblock.js b/lib/messages/commands/merkleblock.js index af2ade1b..f2e35398 100644 --- a/lib/messages/commands/merkleblock.js +++ b/lib/messages/commands/merkleblock.js @@ -17,6 +17,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); * @param {MerkleBlock} block */ function MerkleblockMessage(options) { + if (!(this instanceof MerkleblockMessage)) { + return new MerkleblockMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'merkleblock'; diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js index d8f27f44..8905c7f6 100644 --- a/lib/messages/commands/notfound.js +++ b/lib/messages/commands/notfound.js @@ -6,17 +6,42 @@ var bitcore = require('bitcore'); var utils = require('../utils'); var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; +var Inventory = require('../../inventory'); +var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function NotfoundMessage(options) { + if (!(this instanceof NotfoundMessage)) { + return new NotfoundMessage(options); + } Message.call(this, options); this.command = 'notfound'; this.magicNumber = magicNumber; - this.inventory = options.inventory; + + var inventory; + if (_.isArray(options)) { + inventory = options; + } else { + inventory = options.inventory; + } + utils.checkInventory(inventory); + this.inventory = inventory; } inherits(NotfoundMessage, Message); +NotfoundMessage.forTransaction = function(hash) { + return new NotfoundMessage([Inventory.forTransaction(hash)]); +}; + +NotfoundMessage.forBlock = function(hash) { + return new NotfoundMessage([Inventory.forBlock(hash)]); +}; + +NotfoundMessage.forFilteredBlock = function(hash) { + return new NotfoundMessage([Inventory.forFilteredBlock(hash)]); +}; + NotfoundMessage.fromObject = function(options) { return new NotfoundMessage(options); }; diff --git a/lib/messages/commands/ping.js b/lib/messages/commands/ping.js index 84536b9d..93009883 100644 --- a/lib/messages/commands/ping.js +++ b/lib/messages/commands/ping.js @@ -9,6 +9,9 @@ var BufferReader = bitcore.encoding.BufferReader; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function PingMessage(options) { + if (!(this instanceof PingMessage)) { + return new PingMessage(options); + } if (!options) { options = {}; } diff --git a/lib/messages/commands/pong.js b/lib/messages/commands/pong.js index 66eb4f4b..af99a95d 100644 --- a/lib/messages/commands/pong.js +++ b/lib/messages/commands/pong.js @@ -9,6 +9,9 @@ var BufferReader = bitcore.encoding.BufferReader; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function PongMessage(options) { + if (!(this instanceof PongMessage)) { + return new PongMessage(options); + } Message.call(this, options); this.command = 'pong'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/reject.js b/lib/messages/commands/reject.js index 4b38d675..4c197f78 100644 --- a/lib/messages/commands/reject.js +++ b/lib/messages/commands/reject.js @@ -9,6 +9,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); // todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject function RejectMessage(options) { + if (!(this instanceof RejectMessage)) { + return new RejectMessage(options); + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'reject'; diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js index 676809aa..9f4409da 100644 --- a/lib/messages/commands/tx.js +++ b/lib/messages/commands/tx.js @@ -8,6 +8,9 @@ var Transaction = bitcore.Transaction; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function TransactionMessage(options) { + if (!(this instanceof TransactionMessage)) { + return new TransactionMessage(options); + } Message.call(this, options); this.command = 'tx'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/verack.js b/lib/messages/commands/verack.js index 5def3e26..ddc79c23 100644 --- a/lib/messages/commands/verack.js +++ b/lib/messages/commands/verack.js @@ -8,6 +8,9 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); function VerackMessage(options) { + if (!(this instanceof VerackMessage)) { + return new VerackMessage(options); + } if(!options) { options = {}; } diff --git a/lib/messages/commands/version.js b/lib/messages/commands/version.js index dc78c37f..7652cf65 100644 --- a/lib/messages/commands/version.js +++ b/lib/messages/commands/version.js @@ -24,6 +24,9 @@ var packageInfo = require('../../../package.json'); * @param{Buffer} obj.nonce - a random 8 byte buffer */ function VersionMessage(obj) { + if (!(this instanceof VersionMessage)) { + return new VersionMessage(obj); + } /* jshint maxcomplexity: 10 */ Message.call(this, obj); this.command = 'version'; diff --git a/lib/messages/utils.js b/lib/messages/utils.js index b97fec81..d1bd29ea 100644 --- a/lib/messages/utils.js +++ b/lib/messages/utils.js @@ -7,6 +7,14 @@ var _ = bitcore.deps._; var utils; module.exports = utils = { + checkInventory: function(inventory) { + $.checkArgument( + _.isUndefined(inventory) || + inventory.length === 0 || + (!_.isUndefined(inventory[0].type) && !_.isUndefined(inventory[0].hash)), + 'Inventory must be an array of inventory objects' + ); + }, checkFinished: function checkFinished(parser) { if(!parser.finished()) { throw new Error('Data still available after parsing'); From a8b8c590694f310266ac07726f659c73406947e8 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 12:50:55 -0400 Subject: [PATCH 22/48] added default options for all command messages, and added tests --- lib/messages/commands/addr.js | 3 +++ lib/messages/commands/alert.js | 3 +++ lib/messages/commands/block.js | 3 +++ lib/messages/commands/filteradd.js | 3 +++ lib/messages/commands/filterclear.js | 3 +++ lib/messages/commands/filterload.js | 3 +++ lib/messages/commands/getblocks.js | 3 +++ lib/messages/commands/getdata.js | 3 +++ lib/messages/commands/getheaders.js | 3 +++ lib/messages/commands/headers.js | 3 +++ lib/messages/commands/inv.js | 3 +++ lib/messages/commands/mempool.js | 3 +++ lib/messages/commands/merkleblock.js | 3 +++ lib/messages/commands/notfound.js | 3 +++ lib/messages/commands/pong.js | 3 +++ lib/messages/commands/reject.js | 3 +++ lib/messages/commands/tx.js | 3 +++ lib/messages/commands/verack.js | 1 - lib/messages/message.js | 3 +++ test/messages/builder.js | 1 - test/messages/index.js | 15 +++++++++++++++ 21 files changed, 69 insertions(+), 2 deletions(-) diff --git a/lib/messages/commands/addr.js b/lib/messages/commands/addr.js index e8c17726..83125f96 100644 --- a/lib/messages/commands/addr.js +++ b/lib/messages/commands/addr.js @@ -13,6 +13,9 @@ function AddrMessage(options) { if (!(this instanceof AddrMessage)) { return new AddrMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'addr'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/alert.js b/lib/messages/commands/alert.js index 17f7902f..12b07cfd 100644 --- a/lib/messages/commands/alert.js +++ b/lib/messages/commands/alert.js @@ -13,6 +13,9 @@ function AlertMessage(options) { if (!(this instanceof AlertMessage)) { return new AlertMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'alert'; diff --git a/lib/messages/commands/block.js b/lib/messages/commands/block.js index 29876de8..e05bc1d5 100644 --- a/lib/messages/commands/block.js +++ b/lib/messages/commands/block.js @@ -11,6 +11,9 @@ function BlockMessage(options) { if (!(this instanceof BlockMessage)) { return new BlockMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'block'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js index ed67f8f4..48ffc623 100644 --- a/lib/messages/commands/filteradd.js +++ b/lib/messages/commands/filteradd.js @@ -21,6 +21,9 @@ function FilteraddMessage(options) { if (!(this instanceof FilteraddMessage)) { return new FilteraddMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'filteradd'; diff --git a/lib/messages/commands/filterclear.js b/lib/messages/commands/filterclear.js index 8acd5828..38082545 100644 --- a/lib/messages/commands/filterclear.js +++ b/lib/messages/commands/filterclear.js @@ -11,6 +11,9 @@ function FilterclearMessage(options) { if (!(this instanceof FilterclearMessage)) { return new FilterclearMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'filterclear'; diff --git a/lib/messages/commands/filterload.js b/lib/messages/commands/filterload.js index 04c1a973..98acdf2d 100644 --- a/lib/messages/commands/filterload.js +++ b/lib/messages/commands/filterload.js @@ -14,6 +14,9 @@ function FilterloadMessage(options) { if (!(this instanceof FilterloadMessage)) { return new FilterloadMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'filterload'; diff --git a/lib/messages/commands/getblocks.js b/lib/messages/commands/getblocks.js index 97ae5d78..26a78a53 100644 --- a/lib/messages/commands/getblocks.js +++ b/lib/messages/commands/getblocks.js @@ -23,6 +23,9 @@ function GetblocksMessage(options) { if (!(this instanceof GetblocksMessage)) { return new GetblocksMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'getblocks'; this.version = protocolVersion; diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index 266b17ea..d23c82e6 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -15,6 +15,9 @@ function GetdataMessage(options) { if (!(this instanceof GetdataMessage)) { return new GetdataMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'getdata'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/getheaders.js b/lib/messages/commands/getheaders.js index 0beb5509..023cbd04 100644 --- a/lib/messages/commands/getheaders.js +++ b/lib/messages/commands/getheaders.js @@ -21,6 +21,9 @@ function GetheadersMessage(options) { if (!(this instanceof GetheadersMessage)) { return new GetheadersMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'getheaders'; this.version = protocolVersion; diff --git a/lib/messages/commands/headers.js b/lib/messages/commands/headers.js index 4911684e..a7282cc4 100644 --- a/lib/messages/commands/headers.js +++ b/lib/messages/commands/headers.js @@ -21,6 +21,9 @@ function HeadersMessage(options) { if (!(this instanceof HeadersMessage)) { return new HeadersMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'headers'; diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js index 83cfbc34..9c359b4c 100644 --- a/lib/messages/commands/inv.js +++ b/lib/messages/commands/inv.js @@ -15,6 +15,9 @@ function InvMessage(options) { if (!(this instanceof InvMessage)) { return new InvMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'inv'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/mempool.js b/lib/messages/commands/mempool.js index 45e3b5ad..724fe1c1 100644 --- a/lib/messages/commands/mempool.js +++ b/lib/messages/commands/mempool.js @@ -11,6 +11,9 @@ function MempoolMessage(options) { if (!(this instanceof MempoolMessage)) { return new MempoolMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'mempool'; diff --git a/lib/messages/commands/merkleblock.js b/lib/messages/commands/merkleblock.js index f2e35398..4ab72cb7 100644 --- a/lib/messages/commands/merkleblock.js +++ b/lib/messages/commands/merkleblock.js @@ -20,6 +20,9 @@ function MerkleblockMessage(options) { if (!(this instanceof MerkleblockMessage)) { return new MerkleblockMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'merkleblock'; diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js index 8905c7f6..61caa476 100644 --- a/lib/messages/commands/notfound.js +++ b/lib/messages/commands/notfound.js @@ -15,6 +15,9 @@ function NotfoundMessage(options) { if (!(this instanceof NotfoundMessage)) { return new NotfoundMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'notfound'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/pong.js b/lib/messages/commands/pong.js index af99a95d..82d940a1 100644 --- a/lib/messages/commands/pong.js +++ b/lib/messages/commands/pong.js @@ -12,6 +12,9 @@ function PongMessage(options) { if (!(this instanceof PongMessage)) { return new PongMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'pong'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/reject.js b/lib/messages/commands/reject.js index 4c197f78..f19b676e 100644 --- a/lib/messages/commands/reject.js +++ b/lib/messages/commands/reject.js @@ -12,6 +12,9 @@ function RejectMessage(options) { if (!(this instanceof RejectMessage)) { return new RejectMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.magicNumber = magicNumber; this.command = 'reject'; diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js index 9f4409da..047a7717 100644 --- a/lib/messages/commands/tx.js +++ b/lib/messages/commands/tx.js @@ -11,6 +11,9 @@ function TransactionMessage(options) { if (!(this instanceof TransactionMessage)) { return new TransactionMessage(options); } + if(!options) { + options = {}; + } Message.call(this, options); this.command = 'tx'; this.magicNumber = magicNumber; diff --git a/lib/messages/commands/verack.js b/lib/messages/commands/verack.js index ddc79c23..12bad63a 100644 --- a/lib/messages/commands/verack.js +++ b/lib/messages/commands/verack.js @@ -14,7 +14,6 @@ function VerackMessage(options) { if(!options) { options = {}; } - Message.call(this, options); this.magicNumber = magicNumber; this.command = 'verack'; diff --git a/lib/messages/message.js b/lib/messages/message.js index 9d164779..f1a938ae 100644 --- a/lib/messages/message.js +++ b/lib/messages/message.js @@ -9,6 +9,9 @@ var Hash = bitcore.crypto.Hash; * `getPayload` method to modify the message payload. */ function Message(options) { + if(!options) { + options = {}; + } this.command = options.command; this.magicNumber = options.magicNumber; } diff --git a/test/messages/builder.js b/test/messages/builder.js index 1a67c929..86798dff 100644 --- a/test/messages/builder.js +++ b/test/messages/builder.js @@ -51,7 +51,6 @@ describe('Messages Builder', function() { message.toBuffer().should.deep.equal(expectedBuffer); done(); }); - }); }); diff --git a/test/messages/index.js b/test/messages/index.js index 44a6337d..bf1f14bb 100644 --- a/test/messages/index.js +++ b/test/messages/index.js @@ -33,6 +33,21 @@ describe('Messages', function() { }); }); + describe('@constructor for all command messages', function() { + var messages = new Messages(); + Object.keys(messages.builder.commandsMap).forEach(function(command) { + var name = messages.builder.commandsMap[command]; + it('message.' + name, function(done) { + should.exist(messages[name]); + messages[name].super_.should.equal(Messages.Message); + var message = messages[name](); + should.exist(message); + message.should.be.instanceof(messages[name]); + done(); + }); + }); + }); + describe('#parseBuffer', function() { it('fails with invalid command', function() { var invalidCommand = 'f9beb4d96d616c6963696f757300000025000000bd5e830c' + From 8c9babc09308e409683ae23c3ea48c944e61a0b7 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 13:45:51 -0400 Subject: [PATCH 23/48] added tests for command edge cases --- lib/messages/commands/tx.js | 2 +- test/messages/commands/index.js | 138 ++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 test/messages/commands/index.js diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js index 047a7717..6ac0c7d9 100644 --- a/lib/messages/commands/tx.js +++ b/lib/messages/commands/tx.js @@ -28,7 +28,7 @@ TransactionMessage.fromObject = function(options) { TransactionMessage.fromBuffer = function(payload) { var transaction; if (Transaction.prototype.fromBuffer) { - transaction = Transaction().fromBuffer(payload); + transaction = new Transaction().fromBuffer(payload); } else { transaction = Transaction.fromBuffer(payload); } diff --git a/test/messages/commands/index.js b/test/messages/commands/index.js new file mode 100644 index 00000000..4b67a830 --- /dev/null +++ b/test/messages/commands/index.js @@ -0,0 +1,138 @@ +'use strict'; + +var should = require('chai').should(); +var P2P = require('../../../'); +var Messages = P2P.Messages; +var sinon = require('sinon'); + +describe('Command Messages', function() { + + var messages = new Messages(); + var constructors = ['GetData', 'Inventory', 'NotFound']; + + describe('Inventory helpers for: ' + constructors.join(', '), function() { + + var fakeHash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a'; + + describe('#forTransaction', function() { + constructors.forEach(function(name) { + it(name, function() { + var message = messages[name].forTransaction(fakeHash); + should.exist(message); + message.should.be.instanceof(messages[name]); + }); + }); + }); + + describe('#forBlock', function() { + constructors.forEach(function(name) { + it(name, function() { + var message = messages[name].forBlock(fakeHash); + should.exist(message); + message.should.be.instanceof(messages[name]); + }); + }); + }); + + describe('#forFilteredBlock', function() { + constructors.forEach(function(name) { + it(name, function() { + var message = messages[name].forFilteredBlock(fakeHash); + should.exist(message); + message.should.be.instanceof(messages[name]); + }); + }); + }); + + }); + + + describe('FilterLoad', function() { + + it('should return a null payload', function() { + var message = messages.FilterLoad(); + var payload = message.getPayload(); + payload.length.should.equal(0); + payload.should.be.instanceof(Buffer); + }); + + }); + + describe('Transaction', function() { + + it('should be able to pass a custom Transaction', function(done) { + var Transaction = function(){}; + Transaction.prototype.fromBuffer = function() { + done(); + }; + var messagesCustom = new Messages({Transaction: Transaction}); + var message = messagesCustom.Transaction.fromBuffer(); + should.exist(message); + }); + + it('should work with Transaction.fromBuffer', function(done) { + var Transaction = sinon.stub(); + Transaction.fromBuffer = function() { + done(); + }; + var messagesCustom = new Messages({Transaction: Transaction}); + var message = messagesCustom.Transaction.fromBuffer(); + should.exist(message); + }); + + }); + + describe('Block', function() { + + it('should be able to pass a custom Block', function(done) { + var Block = sinon.stub(); + Block.fromBuffer = function() { + done(); + }; + var messagesCustom = new Messages({Block: Block}); + var message = messagesCustom.Block.fromBuffer(); + should.exist(message); + }); + + }); + + describe('GetBlocks', function() { + + it('should error with invalid stop', function() { + var invalidStop = '000000'; + var starts = ['000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9']; + (function() { + var message = messages.GetBlocks({starts: starts, stop: invalidStop}); + var buffer = message.toBuffer(); + should.not.exist(buffer); + }).should.throw('Invalid hash length'); + }); + + }); + + describe('GetHeaders', function() { + + it('should error with invalid stop', function() { + var invalidStop = '000000'; + var starts = ['000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9']; + (function() { + var message = messages.GetHeaders({starts: starts, stop: invalidStop}); + var buffer = message.toBuffer(); + should.not.exist(buffer); + }).should.throw('Invalid hash length'); + }); + + }); + + describe('MerkleBlock', function() { + + it('should return null buffer for payload', function() { + var message = messages.MerkleBlock(); + var payload = message.getPayload(); + payload.length.should.equal(0); + }); + + }); + + +}); From 3b5359328816498f07271221319e5a23577a3baa Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 14:50:17 -0400 Subject: [PATCH 24/48] added tests for pool.listen and improved arguments for tx and block messages --- lib/messages/commands/block.js | 14 ++++-- lib/messages/commands/tx.js | 12 +++++- test/messages/commands/index.js | 41 ++++++++++++++---- test/pool.js | 75 ++++++++++++++++++++++++++++++--- 4 files changed, 123 insertions(+), 19 deletions(-) diff --git a/lib/messages/commands/block.js b/lib/messages/commands/block.js index e05bc1d5..fdb6180a 100644 --- a/lib/messages/commands/block.js +++ b/lib/messages/commands/block.js @@ -17,7 +17,15 @@ function BlockMessage(options) { Message.call(this, options); this.command = 'block'; this.magicNumber = magicNumber; - this.block = options.block; + + var block; + if (options instanceof Block) { + block = options; + } else { + block = options.block; + } + + this.block = block; } inherits(BlockMessage, Message); @@ -35,7 +43,7 @@ BlockMessage.prototype.getPayload = function() { }; module.exports = function(options) { - Block = options.Block; - magicNumber = options.magicNumber; + Block = options.Block || Block; + magicNumber = options.magicNumber || magicNumber; return BlockMessage; }; diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js index 6ac0c7d9..f136a244 100644 --- a/lib/messages/commands/tx.js +++ b/lib/messages/commands/tx.js @@ -17,8 +17,16 @@ function TransactionMessage(options) { Message.call(this, options); this.command = 'tx'; this.magicNumber = magicNumber; - this.transaction = options.transaction; -}; + + var transaction; + if(options instanceof Transaction) { + transaction = options; + } else { + transaction = options.transaction; + } + + this.transaction = transaction; +} inherits(TransactionMessage, Message); TransactionMessage.fromObject = function(options) { diff --git a/test/messages/commands/index.js b/test/messages/commands/index.js index 4b67a830..968d0508 100644 --- a/test/messages/commands/index.js +++ b/test/messages/commands/index.js @@ -4,6 +4,7 @@ var should = require('chai').should(); var P2P = require('../../../'); var Messages = P2P.Messages; var sinon = require('sinon'); +var bitcore = require('bitcore'); describe('Command Messages', function() { @@ -13,7 +14,7 @@ describe('Command Messages', function() { describe('Inventory helpers for: ' + constructors.join(', '), function() { var fakeHash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a'; - + describe('#forTransaction', function() { constructors.forEach(function(name) { it(name, function() { @@ -23,7 +24,7 @@ describe('Command Messages', function() { }); }); }); - + describe('#forBlock', function() { constructors.forEach(function(name) { it(name, function() { @@ -33,7 +34,7 @@ describe('Command Messages', function() { }); }); }); - + describe('#forFilteredBlock', function() { constructors.forEach(function(name) { it(name, function() { @@ -46,9 +47,31 @@ describe('Command Messages', function() { }); + describe('Transaction', function() { + + it('should accept a transaction instance as an argument', function() { + var tx = new bitcore.Transaction(); + var message = messages.Transaction(tx); + message.transaction.should.be.instanceof(bitcore.Transaction); + }); + + }); + + describe('Block', function() { + + it('should accept a block instance as an argument', function() { + var block = new bitcore.Block({ + header: {}, + transactions: [] + }); + var message = messages.Block(block); + message.block.should.be.instanceof(bitcore.Block); + }); + + }); describe('FilterLoad', function() { - + it('should return a null payload', function() { var message = messages.FilterLoad(); var payload = message.getPayload(); @@ -69,7 +92,7 @@ describe('Command Messages', function() { var message = messagesCustom.Transaction.fromBuffer(); should.exist(message); }); - + it('should work with Transaction.fromBuffer', function(done) { var Transaction = sinon.stub(); Transaction.fromBuffer = function() { @@ -83,7 +106,7 @@ describe('Command Messages', function() { }); describe('Block', function() { - + it('should be able to pass a custom Block', function(done) { var Block = sinon.stub(); Block.fromBuffer = function() { @@ -97,7 +120,7 @@ describe('Command Messages', function() { }); describe('GetBlocks', function() { - + it('should error with invalid stop', function() { var invalidStop = '000000'; var starts = ['000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9']; @@ -111,7 +134,7 @@ describe('Command Messages', function() { }); describe('GetHeaders', function() { - + it('should error with invalid stop', function() { var invalidStop = '000000'; var starts = ['000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9']; @@ -125,7 +148,7 @@ describe('Command Messages', function() { }); describe('MerkleBlock', function() { - + it('should return null buffer for payload', function() { var message = messages.MerkleBlock(); var payload = message.getPayload(); diff --git a/test/pool.js b/test/pool.js index 10b85c0f..3acd1356 100644 --- a/test/pool.js +++ b/test/pool.js @@ -17,6 +17,7 @@ var Networks = bitcore.Networks; var dns = require('dns'); var sinon = require('sinon'); +var net = require('net'); function getPayloadBuffer(messageBuffer) { return new Buffer(messageBuffer.slice(48), 'hex'); @@ -49,6 +50,11 @@ describe('Pool', function() { }); it('optionally connect without dns seeds', function() { + sinon.stub(Peer.prototype, 'connect', function() { + this.socket = { + destroy: sinon.stub() + }; + }); var stub = sinon.stub(dns, 'resolve', function(seed, callback) { throw new Error('DNS should not be called'); }); @@ -74,6 +80,7 @@ describe('Pool', function() { pool.disconnect(); pool._addrs.length.should.equal(2); stub.restore(); + Peer.prototype.connect.restore(); }); it('will add addrs via options argument', function() { @@ -295,23 +302,81 @@ describe('Pool', function() { it('send message to all peers', function(done) { var message = 'message'; - var peerConnectStub = sinon.stub(Peer.prototype, 'connect', function() { + sinon.stub(Peer.prototype, 'connect', function() { + this.socket = { + destroy: sinon.stub() + }; var self = this; process.nextTick(function() { self.emit('ready'); }); }); - var peerMessageStub = sinon.stub(Peer.prototype, 'sendMessage', function(message) { + sinon.stub(Peer.prototype, 'sendMessage', function(message) { message.should.equal(message); - peerConnectStub.restore(); - peerMessageStub.restore(); + Peer.prototype.connect.restore(); + Peer.prototype.sendMessage.restore(); + pool.disconnect(); done(); }); - var pool = new Pool({network: Networks.livenet, maxSize: 1}); + var pool = new Pool({ + network: Networks.livenet, + maxSize: 1, + dnsSeed: false, + addrs: [ + { + ip:{ + v4: 'localhost' + } + } + ] + }); pool.on('peerready', function() { pool.sendMessage(message); }); pool.connect(); }); + describe('#listen', function() { + + it('create a server', function(done) { + var netStub = sinon.stub(net, 'createServer', function() { + return { + listen: function() { + netStub.restore(); + done(); + } + }; + }); + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + pool.listen(); + }); + + it('should handle an ipv6 connection', function(done) { + var ipv6 = '2001:0db8:85a3:0042:1000:8a2e:0370:7334'; + sinon.stub(net, 'createServer', function(callback) { + callback({ + remoteAddress: ipv6 + }); + return { + listen: sinon.stub() + }; + }); + sinon.stub(net, 'isIPv6', function() { + return true; + }); + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + pool._addAddr = function(addr) { + should.exist(addr.ip.v6); + addr.ip.v6.should.equal(ipv6); + net.isIPv6.restore(); + net.createServer.restore(); + done(); + }; + pool._addConnectedPeer = sinon.stub(); + pool.listen(); + }); + + }); + + }); From 97f39db0814ef93025394ead44036520edef8d89 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 15:05:08 -0400 Subject: [PATCH 25/48] added tests for pool _addConnectedPeer --- test/pool.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/pool.js b/test/pool.js index 3acd1356..45cfaf8d 100644 --- a/test/pool.js +++ b/test/pool.js @@ -336,6 +336,33 @@ describe('Pool', function() { pool.connect(); }); + describe('#_addConnectedPeer', function() { + + it('should add a peer', function() { + /* jshint sub: true */ + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + pool._addPeerEventHandlers = sinon.stub(); + pool._addConnectedPeer({ + on: sinon.stub() + }, {hash: 'hash'}); + should.exist(pool._connectedPeers['hash']); + pool._addPeerEventHandlers.calledOnce.should.equal(true); + }); + + it('should not already added peer', function() { + /* jshint sub: true */ + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + pool._addPeerEventHandlers = sinon.stub(); + pool._connectedPeers['hash'] = {}; + pool._addConnectedPeer({ + on: sinon.stub() + }, {hash: 'hash'}); + should.exist(pool._connectedPeers['hash']); + pool._addPeerEventHandlers.calledOnce.should.equal(false); + }); + + }); + describe('#listen', function() { it('create a server', function(done) { @@ -376,6 +403,31 @@ describe('Pool', function() { pool.listen(); }); + it('should handle an ipv4 connection', function(done) { + var ipv4 = '127.0.0.1'; + sinon.stub(net, 'createServer', function(callback) { + callback({ + remoteAddress: ipv4 + }); + return { + listen: sinon.stub() + }; + }); + sinon.stub(net, 'isIPv6', function() { + return false; + }); + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + pool._addAddr = function(addr) { + should.exist(addr.ip.v4); + addr.ip.v4.should.equal(ipv4); + net.isIPv6.restore(); + net.createServer.restore(); + done(); + }; + pool._addConnectedPeer = sinon.stub(); + pool.listen(); + }); + }); From 6007dc6faf216432d921d07b285ae6ae4146c7ae Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 20:49:24 -0400 Subject: [PATCH 26/48] updated to use mapped constructers and removed build method --- lib/messages/index.js | 8 +++----- lib/peer.js | 19 +++++-------------- test/peer.js | 2 +- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/lib/messages/index.js b/lib/messages/index.js index 8369e999..7e5ec7a3 100644 --- a/lib/messages/index.js +++ b/lib/messages/index.js @@ -25,10 +25,6 @@ Messages.PAYLOAD_START = 16; Messages.Message = require('./message'); Messages.builder = require('./builder'); -Messages.prototype.build = function(command, object) { - return this.builder.commands[command].fromObject(object); -}; - Messages.prototype.parseBuffer = function(dataBuffer) { /* jshint maxstatements: 18 */ if (dataBuffer.length < Messages.MINIMUM_LENGTH) { @@ -36,7 +32,9 @@ Messages.prototype.parseBuffer = function(dataBuffer) { } // Search the next magic number - if (!this._discardUntilNextMessage(dataBuffer)) return; + if (!this._discardUntilNextMessage(dataBuffer)) { + return; + } var payloadLen = (dataBuffer.get(Messages.PAYLOAD_START)) + (dataBuffer.get(Messages.PAYLOAD_START + 1) << 8) + diff --git a/lib/peer.js b/lib/peer.js index 9249057b..32ff11c6 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -38,7 +38,7 @@ function Peer(options) { return new Peer(options); } - if(options.socket) { + if (options.socket) { this.socket = options.socket; this.host = this.socket.remoteAddress; this.port = this.socket.remotePort; @@ -83,7 +83,7 @@ function Peer(options) { self.subversion = message.subversion; self.bestHeight = message.startHeight; - var verackResponse = self.messages.build('verack'); + var verackResponse = self.messages.VerAck(); self.sendMessage(verackResponse); if(!self.versionSent) { @@ -108,7 +108,6 @@ Peer.STATUS = { /** * Set a socks5 proxy for the connection. Enables the use of the TOR network. - * * @param {String} host - IP address of the proxy * @param {Number} port - Port number of the proxy * @returns {Peer} The same Peer instance. @@ -125,7 +124,6 @@ Peer.prototype.setProxy = function(host, port) { /** * Init the connection with the remote peer. - * * @returns {Socket} The same peer instance. */ Peer.prototype.connect = function() { @@ -168,7 +166,6 @@ Peer.prototype._onError = function(e) { /** * Disconnects the remote connection. - * * @returns {Socket} The same peer instance. */ Peer.prototype.disconnect = function() { @@ -180,7 +177,6 @@ Peer.prototype.disconnect = function() { /** * Send a Message to the remote peer. - * * @param {Message} message - A message instance */ Peer.prototype.sendMessage = function(message) { @@ -191,10 +187,8 @@ Peer.prototype.sendMessage = function(message) { * Internal function that sends VERSION message to the remote peer. */ Peer.prototype._sendVersion = function() { - // todo: include sending ip address - var message = this.messages.build('version', { - relay: this.relay - }); + // todo: include sending local ip address + var message = this.messages.Version({relay: this.relay}); this.versionSent = true; this.sendMessage(message); }; @@ -203,9 +197,7 @@ Peer.prototype._sendVersion = function() { * Send a PONG message to the remote peer. */ Peer.prototype._sendPong = function(nonce) { - var message = this.messages.build('pong', { - nonce: nonce - }); + var message = this.messages.Pong({nonce: nonce}); this.sendMessage(message); }; @@ -222,7 +214,6 @@ Peer.prototype._readMessage = function() { /** * Internal function that creates a socket using a proxy if neccesary. - * * @returns {Socket} A Socket instance not yet connected. */ Peer.prototype._getSocket = function() { diff --git a/test/peer.js b/test/peer.js index ddcd65c9..61963d32 100644 --- a/test/peer.js +++ b/test/peer.js @@ -126,7 +126,7 @@ describe('Peer', function() { it('send pong on ping', function(done) { var peer = new Peer({host: 'localhost'}); - var pingMessage = messages.build('ping'); + var pingMessage = messages.Ping(); peer.sendMessage = function(message) { message.command.should.equal('pong'); message.nonce.should.equal(pingMessage.nonce); From e31f28e97396fef068afd4aa834e69d5ef8512ca Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 20:54:50 -0400 Subject: [PATCH 27/48] cleanup builder options --- lib/messages/builder.js | 75 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/lib/messages/builder.js b/lib/messages/builder.js index cc25690f..33f1cfa7 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -10,54 +10,53 @@ function builder(options) { options = {}; } - var magicNumber = options.magicNumber; - if (!magicNumber) { - magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + if (!options.magicNumber) { + options.magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); } - var Block = options.Block || bitcore.Block; - var BlockHeader = options.BlockHeader || bitcore.BlockHeader; - var Transaction = options.Transaction || bitcore.Transaction; - var MerkleBlock = options.MerkleBlock || bitcore.MerkleBlock; - var protocolVersion = options.protocolVersion || 70000; + + options.Block = options.Block || bitcore.Block; + options.BlockHeader = options.BlockHeader || bitcore.BlockHeader; + options.Transaction = options.Transaction || bitcore.Transaction; + options.MerkleBlock = options.MerkleBlock || bitcore.MerkleBlock; + options.protocolVersion = options.protocolVersion || 70000; var exported = { constructors: { - Block: Block, - BlockHeader: BlockHeader, - Transaction: Transaction, - MerkleBlock: MerkleBlock + Block: options.Block, + BlockHeader: options.BlockHeader, + Transaction: options.Transaction, + MerkleBlock: options.MerkleBlock }, defaults: { - protocolVersion: protocolVersion, - magicNumber: magicNumber + protocolVersion: options.protocolVersion, + magicNumber: options.magicNumber + }, + commandsMap: { + version: 'Version', + verack: 'VerAck', + ping: 'Ping', + pong: 'Pong', + block: 'Block', + tx: 'Transaction', + getdata: 'GetData', + headers: 'Headers', + notfound: 'NotFound', + inv: 'Inventory', + addr: 'Address', + alert: 'Alert', + reject: 'Reject', + merkleblock: 'MerkleBlock', + filterload: 'FilterLoad', + filteradd: 'FilterAdd', + filterclear: 'FilterClear', + getblocks: 'GetBlocks', + getheaders: 'GetHeaders', + mempool: 'MemPool', + getaddr: 'GetAddr' }, commands: {} }; - exported.commandsMap = { - version: 'Version', - verack: 'VerAck', - ping: 'Ping', - pong: 'Pong', - block: 'Block', - tx: 'Transaction', - getdata: 'GetData', - headers: 'Headers', - notfound: 'NotFound', - inv: 'Inventory', - addr: 'Address', - alert: 'Alert', - reject: 'Reject', - merkleblock: 'MerkleBlock', - filterload: 'FilterLoad', - filteradd: 'FilterAdd', - filterclear: 'FilterClear', - getblocks: 'GetBlocks', - getheaders: 'GetHeaders', - mempool: 'MemPool', - getaddr: 'GetAddr' - }; - for (var key in exported.commandsMap) { exported.commands[key] = require('./commands/' + key)(options); } From cfbee2d71f00f10e5463518a7bbdd78db09d82cb Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 21:13:15 -0400 Subject: [PATCH 28/48] update documentation for messages --- docs/messages.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/messages.md b/docs/messages.md index bb674cc5..ad9e6677 100644 --- a/docs/messages.md +++ b/docs/messages.md @@ -1,3 +1,4 @@ +--- title: Messages description: A superclass for the messages of the bitcoin network --- @@ -5,6 +6,42 @@ description: A superclass for the messages of the bitcoin network The bitcoin protocol specifies a set of [messages](https://en.bitcoin.it/wiki/Protocol_specification) that can be sent from peer to peer. `bitcore-p2p` provides support for some of these messages. +To create a messages, you can use any of the message constructors, here is a simple example: + +```javascript +var messages = new Messages(); +var message = messages.Ping(); +``` + +There are also several convenient helpers for inventory based messages: + +```javascript +message = messages.GetData.forTransaction(txHash); +message = messages.GetData.forBlock(blockHash); +message = messages.Inventory.forTransaction(txHash); +``` + +As well as sending "tx" and "block" messages with Bitcore instances: + +```javascript +message = messages.Block(block); +message = messages.Transaction(transaction); +``` + +Note: A list of further messages is available below. + +For advanced usage, you can also customize which constructor is used for Block and Transaction messages by passing it as an argument to Messages, for example: + +```javascript +var messages = new Messages({Block: MyBlock, Transaction: MyTransaction}); +``` + +And additionally custom network magic: + +```javascript +var messages = new Messages({magicNumber: 0x0b120907}); +``` + ## List of Messages ### Version From a57f864a6e608eccf4ea025117e324d0daa52118 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 21:20:42 -0400 Subject: [PATCH 29/48] updated documentation for peer and pool --- docs/peer.md | 17 +++++++++-------- docs/pool.md | 17 +++++++++++++++-- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/peer.md b/docs/peer.md index b931b7e5..e089e37e 100644 --- a/docs/peer.md +++ b/docs/peer.md @@ -1,3 +1,4 @@ +--- title: Peer description: The Peer class provides a simple interface for connecting to a node in the bitcoin network. --- @@ -15,15 +16,15 @@ The code to create a new peer looks like this: var Peer = require('bitcore-p2p').Peer; // default port -var livenetPeer = new Peer('5.9.85.34'); -var testnetPeer = new Peer('5.9.85.34', bitcore.testnet); +var livenetPeer = new Peer({host: '5.9.85.34'}); +var testnetPeer = new Peer({host: '5.9.85.34', network: Networks.testnet}); // custom port -var livenetPeer = new Peer('5.9.85.34', 8334); -var testnetPeer = new Peer('5.9.85.34', 18334, bitcore.testnet); +var livenetPeer = new Peer({host: '5.9.85.34', port: 8334}); +var testnetPeer = new Peer({host: '5.9.85.34', port: 18334, network: Networks.testnet}); // use sock5 proxy (Tor) -var peer = new Peer('5.9.85.34').setProxy('localhost', 9050); +var peer = new Peer({host: '5.9.85.34'}).setProxy('localhost', 9050); ``` ## States @@ -40,7 +41,7 @@ You can subscribe to the change of those states as follows: ```javascript var Peer = require('bitcore-p2p').Peer; -var peer = new Peer('5.9.85.34'); +var peer = new Peer({host: '5.9.85.34'}); peer.on('ready', function() { // peer info @@ -60,7 +61,7 @@ Once connected, a peer instance can send and receive messages. Every time a mess ```javascript var Peer = require('bitcore-p2p').Peer; -var peer = new Peer('5.9.85.34'); +var peer = new Peer({host: '5.9.85.34'}); // handle events peer.on('inv', function(message) { @@ -88,7 +89,7 @@ An example for requesting other connected nodes to a peers looks like this: var p2p = require('bitcore-p2p') var Peer = p2p.Peer; var Messages = p2p.Messages; -var peer = new Peer('5.9.85.34'); +var peer = new Peer({host: '5.9.85.34'}); peer.on('ready', function() { var message = new Messages.GetAddresses(); diff --git a/docs/pool.md b/docs/pool.md index e09aae05..fbffb828 100644 --- a/docs/pool.md +++ b/docs/pool.md @@ -1,3 +1,4 @@ +--- title: Pool description: A simple interface to create and maintain a set of connections to bitcoin nodes. --- @@ -12,7 +13,7 @@ The quickest way to get connected is to run the following: var Pool = require('bitcore-p2p').Pool; var Networks = require('bitcore').Networks; -var pool = new Pool(Networks.livenet); +var pool = new Pool({network: Networks.livenet}); // connect to the network pool.connect(); @@ -35,7 +36,8 @@ By default, peers will be added via DNS discovery and as peers are announced in ```javascript -var pool = new Pool(Networks.livenet, { +var pool = new Pool({ + network: Networks.livenet, // the network object dnsSeed: false, // prevent seeding with DNS discovered known peers upon connecting listenAddr: false, // prevent new peers being added from addr messages addrs: [ // initial peers to connect to @@ -50,3 +52,14 @@ var pool = new Pool(Networks.livenet, { pool.connect(); ``` + +## Listening for Peers + +It's also possible to listen to incoming socket connections to add peers to the pool. To enable this capability, you can do the following: + +```javascript +var pool = new Pool({network: Networks.livenet}); +pool.listen(); +``` + +When there are incoming connections the peer will be added to the pool. \ No newline at end of file From a15f11cc32ad51407f5f6db30bb1838fe53caea6 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 21:54:47 -0400 Subject: [PATCH 30/48] add test for default magic for command messages --- test/messages/commands/index.js | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/messages/commands/index.js b/test/messages/commands/index.js index 968d0508..8cae6d18 100644 --- a/test/messages/commands/index.js +++ b/test/messages/commands/index.js @@ -10,6 +10,29 @@ describe('Command Messages', function() { var messages = new Messages(); var constructors = ['GetData', 'Inventory', 'NotFound']; + var commandsMap = { + version: 'Version', + verack: 'VerAck', + ping: 'Ping', + pong: 'Pong', + block: 'Block', + tx: 'Transaction', + getdata: 'GetData', + headers: 'Headers', + notfound: 'NotFound', + inv: 'Inventory', + addr: 'Address', + alert: 'Alert', + reject: 'Reject', + merkleblock: 'MerkleBlock', + filterload: 'FilterLoad', + filteradd: 'FilterAdd', + filterclear: 'FilterClear', + getblocks: 'GetBlocks', + getheaders: 'GetHeaders', + mempool: 'MemPool', + getaddr: 'GetAddr' + }; describe('Inventory helpers for: ' + constructors.join(', '), function() { @@ -158,4 +181,17 @@ describe('Command Messages', function() { }); + describe('Default Magic Number', function() { + + Object.keys(commandsMap).forEach(function(command) { + it(command, function() { + var messageConstructor = require('../../../lib/messages/commands/' + command)({}); + var message = new messageConstructor(); + var defaultMagic = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + message.magicNumber.should.equal(defaultMagic); + }); + }); + + }); + }); From 6db209b9b629d0ab4359c735dedd303c160d427b Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 21:59:32 -0400 Subject: [PATCH 31/48] added test for message utils --- test/messages/util.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/messages/util.js diff --git a/test/messages/util.js b/test/messages/util.js new file mode 100644 index 00000000..ce745d82 --- /dev/null +++ b/test/messages/util.js @@ -0,0 +1,33 @@ +'use strict'; + +/* jshint unused: false */ + +var should = require('chai').should(); +var utils = require('../../lib/messages/utils'); +var bitcore = require('bitcore'); +var BufferReader = bitcore.encoding.BufferReader; + +describe('Message Utils', function() { + + describe('checkFinished', function() { + it('should throw an error if buffer reader is not finished', function() { + /*jshint immed: false */ + var buffer = new Buffer(Array(32)); + var br = new BufferReader(buffer); + (function() { + utils.checkFinished(br); + }).should.throw('Data still available after parsing'); + }); + }); + + describe('sanitizeStartStop', function() { + it('should throw an error if starts is invalid length', function() { + /*jshint immed: false */ + var stop = '000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9'; + (function() { + utils.sanitizeStartStop({starts: ['0000'], stop: stop}); + }).should.throw('Invalid hash'); + }); + }); + +}); From 1545abbea0c1b83dfb4056d837f6456ddde6888c Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 22:30:00 -0400 Subject: [PATCH 32/48] added test for version message handling --- test/peer.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/peer.js b/test/peer.js index 61963d32..b8c75c95 100644 --- a/test/peer.js +++ b/test/peer.js @@ -71,7 +71,6 @@ describe('Peer', function() { }); }); - it('create instance', function() { var peer = new Peer('localhost'); peer.host.should.equal('localhost'); @@ -168,6 +167,39 @@ describe('Peer', function() { }); + it('should send version on version if not already sent', function(done) { + var peer = new Peer({host:'localhost'}); + var commands = {}; + peer.sendMessage = function(message) { + commands[message.command] = true; + if (commands.verack && commands.version) { + done(); + } + }; + peer.socket = {}; + peer.emit('version', { + version: 'version', + subversion: 'subversion', + startHeight: 'startHeight' + }); + }); + + it('should not send version on version if already sent', function(done) { + var peer = new Peer({host:'localhost'}); + peer.versionSent = true; + var commands = {}; + peer.sendMessage = function(message) { + message.command.should.not.equal('version'); + done(); + }; + peer.socket = {}; + peer.emit('version', { + version: 'version', + subversion: 'subversion', + startHeight: 'startHeight' + }); + }); + it('relay set properly', function() { var peer = new Peer({host: 'localhost'}); peer.relay.should.equal(true); From e62ddd93f6a3bb83b22e2a082751553ca49f8439 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 23:23:52 -0400 Subject: [PATCH 33/48] added test for handling addr times and v6 addresses --- lib/pool.js | 8 +++--- test/pool.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 3e60535e..3a396e64 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -69,9 +69,11 @@ function Pool(options) { var length = addrs.length; for (var i = 0; i < length; i++) { var addr = addrs[i]; - // In case of an invalid time, assume "5 days ago" - if (addr.time <= 100000000 || addr.time > (now() + 10 * 60)) { - addr.time = now() - 5 * 24 * 60 * 60; + var future = new Date().getTime() + (10 * 60 * 1000); + if (addr.time.getTime() <= 100000000000 || addr.time.getTime() > future) { + // In case of an invalid time, assume "5 days ago" + var past = new Date(new Date().getTime() - 5 * 24 * 60 * 60 * 1000); + addr.time = past; } this._addAddr(addr); } diff --git a/test/pool.js b/test/pool.js index 45cfaf8d..44bcfa01 100644 --- a/test/pool.js +++ b/test/pool.js @@ -336,6 +336,80 @@ describe('Pool', function() { pool.connect(); }); + it('not call _fillConnections if keepalive is false', function(done) { + done(); + }); + + it('keep original time for handling peeraddr messages', function(done) { + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + var now = new Date(); + pool._addAddr = function(addr) { + addr.time.should.equal(now); + done(); + }; + pool.emit('peeraddr', {}, { + addresses: [ + { + time: now + } + ] + }); + }); + + it('replace time if time is invalid on peeraddr messages', function(done) { + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + var future = new Date(new Date().getTime() + 10 * 70 * 1000); + var past = new Date(new Date().getTime() - 4 * 24 * 60 * 60 * 1000); // 4 days ago + pool._addAddr = function(addr) { + addr.time.should.not.equal(future); + addr.time.getTime().should.be.below(past.getTime()); + done(); + }; + pool.emit('peeraddr', {}, { + addresses: [ + { + time: future + } + ] + }); + }); + + describe('#_removeConnectedPeer', function() { + it('disconnect peer if peer status is not disconnected', function(done) { + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + /* jshint sub: true */ + pool._connectedPeers['hash'] = { + status: Peer.STATUS.CONNECTED, + disconnect: function() { + done(); + } + }; + pool._removeConnectedPeer({ + hash: 'hash' + }); + }); + }); + + describe('#_connectPeer', function() { + it('connect ipv6 peer', function() { + var connectStub = sinon.stub(Peer.prototype, 'connect'); + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + var ipv6 = '2001:0db8:85a3:0042:1000:8a2e:0370:7334'; + pool._addPeerEventHandlers = sinon.stub(); + pool._connectPeer({ + ip: { + v6: ipv6 + }, + hash: 'hash' + }); + /* jshint sub: true */ + should.exist(pool._connectedPeers['hash']); + pool._addPeerEventHandlers.calledOnce.should.equal(true); + Peer.prototype.connect.calledOnce.should.equal(true); + connectStub.restore(); + }); + }); + describe('#_addConnectedPeer', function() { it('should add a peer', function() { From ede5f0b60c1662f71af964b95eaf5b20ddcca176 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 13 Mar 2015 23:29:26 -0400 Subject: [PATCH 34/48] add test for seed event, and remove test stubs --- test/messages/index.js | 10 ---------- test/pool.js | 13 +++++++++++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/test/messages/index.js b/test/messages/index.js index bf1f14bb..a2b3aa80 100644 --- a/test/messages/index.js +++ b/test/messages/index.js @@ -78,14 +78,4 @@ describe('Messages', function() { }); - describe('#discardUntilNextMessage', function() { - }); - - describe('#buildFromBuffer', function() { - }); - - describe('#build/#buildFromObject', function() { - }); - - }); diff --git a/test/pool.js b/test/pool.js index 44bcfa01..011fd22e 100644 --- a/test/pool.js +++ b/test/pool.js @@ -336,8 +336,17 @@ describe('Pool', function() { pool.connect(); }); - it('not call _fillConnections if keepalive is false', function(done) { - done(); + it('not call _fillConnections if keepalive is false on seed', function(done) { + var pool = new Pool({network: Networks.livenet, maxSize: 1}); + pool._fillConnections = sinon.stub(); + pool.keepalive = false; + pool.on('seed', function() { + process.nextTick(function() { + pool._fillConnections.called.should.equal(false); + done(); + }); + }); + pool.emit('seed', []); }); it('keep original time for handling peeraddr messages', function(done) { From f17bbf5d6f4fa4bc5f213f9622a59f5423ff79f3 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 10:15:34 -0400 Subject: [PATCH 35/48] updated jsdocs for peer and pool --- lib/peer.js | 24 +++++++++++++++--------- lib/pool.js | 26 +++++++++++++++++++++----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/lib/peer.js b/lib/peer.js index 32ff11c6..1b789f11 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -11,27 +11,31 @@ var $ = bitcore.util.preconditions; var util = require('util'); /** - * A Peer instance represents a remote bitcoin node and allows to communicate - * with it using the standard messages of the bitcoin p2p protocol. + * A constructor to create Peer instances to send and recieve messages + * using the standard Bitcoin protocol. * * @example * ```javascript * - * var peer = new Peer('127.0.0.1').setProxy('127.0.0.1', 9050); + * var peer = new Peer({host: '127.0.0.1'}).setProxy('127.0.0.1', 9050); * peer.on('tx', function(tx) { * console.log('New transaction: ', tx.id); * }); * peer.connect(); * ``` * - * @param {String} host - IP address of the remote host - * @param {Number} [port] - Port number of the remote host - * @param {Network} [network] - The context for this communication + * @param {Object} [options] + * @param {String} [options.host] - IP address of the remote host + * @param {Number} [options.port] - Port number of the remote host + * @param {Network} [options.network=Networks.defaultNetwork] - The network configuration + * @param {Boolean} [options.relay] - An option to disable automatic inventory relaying from the remote peer + * @param {Socket} [options.socket] - An existing connected socket + * @returns {Peer} A new instance of Peer. * @constructor */ function Peer(options) { - /* jshint maxstatements: 25 */ + /* jshint maxstatements: 26 */ /* jshint maxcomplexity: 8 */ if (!(this instanceof Peer)) { @@ -95,6 +99,8 @@ function Peer(options) { self._sendPong(message.nonce); }); + return this; + } util.inherits(Peer, EventEmitter); @@ -124,7 +130,7 @@ Peer.prototype.setProxy = function(host, port) { /** * Init the connection with the remote peer. - * @returns {Socket} The same peer instance. + * @returns {Peer} The same peer instance. */ Peer.prototype.connect = function() { this.socket = this._getSocket(); @@ -166,7 +172,7 @@ Peer.prototype._onError = function(e) { /** * Disconnects the remote connection. - * @returns {Socket} The same peer instance. + * @returns {Peer} The same peer instance. */ Peer.prototype.disconnect = function() { this.status = Peer.STATUS.DISCONNECTED; diff --git a/lib/pool.js b/lib/pool.js index 3a396e64..08ac3cdd 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -22,15 +22,15 @@ function now() { * @example * ```javascript * - * var pool = new Pool(Networks.livenet); + * var pool = new Pool({network: Networks.livenet}); * pool.on('peerinv', function(peer, message) { * // do something with the inventory announcement * }); * pool.connect(); * ``` * - * @param {Network|String} network - The network to connect - * @param {Object} [options] - Options object + * @param {Object} [options] + * @param {Network} [options.network=Networks.defaultNetwork] - The network configuration * @param {Boolean} [options.listenAddr=true] - Prevent new peers being added from addr messages * @param {Boolean} [options.dnsSeed=true] - Prevent seeding with DNS discovered known peers * @param {Boolean} [options.relay=true] - Prevent inventory announcements until a filter is loaded @@ -115,7 +115,6 @@ Pool.PeerEvents = ['version', 'inv', 'getdata', 'ping', 'pong', 'addr', 'filterclear' ]; - /** * Will initiatiate connection to peers, if available peers have been added to * the pool, it will connect to those, otherwise will use DNS seeds to find @@ -132,7 +131,6 @@ Pool.prototype.connect = function connect() { return this; }; - /** * Will disconnect all peers that are connected. */ @@ -210,6 +208,12 @@ Pool.prototype._connectPeer = function _connectPeer(addr) { return this; }; +/** + * Adds a peer with a connected socket to the _connectedPeers object, and + * intializes the associated event handlers. + * @param {Socket} - socket - A new connected socket + * @param {Object} - addr - The associated addr object for the peer + */ Pool.prototype._addConnectedPeer = function _addConnectedPeer(socket, addr) { var self = this; @@ -227,6 +231,10 @@ Pool.prototype._addConnectedPeer = function _addConnectedPeer(socket, addr) { return this; }; +/** + * Will add disconnect and ready events for a peer and intialize + * handlers for relay peer message events. + */ Pool.prototype._addPeerEventHandlers = function(peer, addr) { var self = this; @@ -329,6 +337,10 @@ Pool.prototype.inspect = function inspect() { this._addrs.length + '>'; }; +/** + * Will send a message to all of the peers in the pool. + * @param {Message} message - An instance of the message to send + */ Pool.prototype.sendMessage = function(message) { // broadcast to peers for(var key in this._connectedPeers) { @@ -337,6 +349,10 @@ Pool.prototype.sendMessage = function(message) { } }; +/** + * Will enable a listener for peer connections, when a peer connects + * it will be added to the pool. + */ Pool.prototype.listen = function() { var self = this; From 42c829e49c25878cdb5a98fea5d0577b45a35d34 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 10:53:32 -0400 Subject: [PATCH 36/48] added jsdocs for inventory --- lib/inventory.js | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/inventory.js b/lib/inventory.js index e5e7e96f..5d4b1345 100644 --- a/lib/inventory.js +++ b/lib/inventory.js @@ -7,6 +7,13 @@ var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; var _ = bitcore.deps._; +/** + * A constructor for inventory related Bitcoin messages such as + * "getdata", "inv" and "notfound". + * @param {Object} - obj + * @param {Number} - obj.type - Inventory.TYPE + * @param {Buffer} - obj.hash - The hash for the inventory + */ function Inventory(obj) { this.type = obj.type; if (!BufferUtil.isBuffer(obj.hash)) { @@ -15,9 +22,14 @@ function Inventory(obj) { this.hash = obj.hash; } +/** + * A convenience constructor for Inventory. + * @param {Number} - type - Inventory.TYPE + * @param {Buffer|String} - hash - The hash for the inventory + * @returns {Inventory} - A new instance of Inventory + */ Inventory.forItem = function(type, hash) { $.checkArgument(hash); - //todo: is reversing expected behavior? if (_.isString(hash)) { hash = new Buffer(hash, 'hex'); hash = BufferUtil.reverse(hash); @@ -25,18 +37,36 @@ Inventory.forItem = function(type, hash) { return new Inventory({type: type, hash: hash}); }; +/** + * A convenience constructor for Inventory for block inventory types. + * @param {Buffer|String} - hash - The hash for the block inventory + * @returns {Inventory} - A new instance of Inventory + */ Inventory.forBlock = function(hash) { return Inventory.forItem(Inventory.TYPE.BLOCK, hash); }; +/** + * A convenience constructor for Inventory for filtered/merkle block inventory types. + * @param {Buffer|String} - hash - The hash for the filtered block inventory + * @returns {Inventory} - A new instance of Inventory + */ Inventory.forFilteredBlock = function(hash) { return Inventory.forItem(Inventory.TYPE.FILTERED_BLOCK, hash); }; +/** + * A convenience constructor for Inventory for transaction inventory types. + * @param {Buffer|String} - hash - The hash for the transaction inventory + * @returns {Inventory} - A new instance of Inventory + */ Inventory.forTransaction = function(hash) { return Inventory.forItem(Inventory.TYPE.TX, hash); }; +/** + * @returns {Buffer} - Serialized inventory + */ Inventory.prototype.toBuffer = function() { var bw = new BufferWriter(); bw.writeUInt32LE(this.type); @@ -44,12 +74,18 @@ Inventory.prototype.toBuffer = function() { return bw.concat(); }; +/** + * @param {BufferWriter} - An instance of BufferWriter + */ Inventory.prototype.toBufferWriter = function(bw) { bw.writeUInt32LE(this.type); bw.write(this.hash); return bw; }; +/** + * @param {Buffer} - Seralized buffer of the inventory + */ Inventory.fromBuffer = function(payload) { var parser = new BufferReader(payload); var obj = {}; @@ -58,6 +94,9 @@ Inventory.fromBuffer = function(payload) { return new Inventory(obj); }; +/** + * @param {BufferWriter} - An instance of BufferWriter + */ Inventory.fromBufferReader = function(br) { var obj = {}; obj.type = br.readUInt32LE(); From 50d7d37034831e611f6be86233e8ba22dd2c3193 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 11:13:13 -0400 Subject: [PATCH 37/48] added jsdocs for bloomfilter and messages --- lib/bloomfilter.js | 9 ++++++++- lib/inventory.js | 1 + lib/messages/index.js | 13 +++++++++++++ lib/messages/message.js | 8 ++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/bloomfilter.js b/lib/bloomfilter.js index 10c7adc1..ef1bd83a 100644 --- a/lib/bloomfilter.js +++ b/lib/bloomfilter.js @@ -4,8 +4,12 @@ var bitcore = require('bitcore'); var BloomFilter = require('bloom-filter'); var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; -var $ = bitcore.util.preconditions; +/** + * A constructor for Bloom Filters + * @see https://github.com/bitpay/bloom-filter + * @param {Buffer} - payload + */ BloomFilter.fromBuffer = function fromBuffer(payload) { var obj = {}; var parser = new BufferReader(payload); @@ -20,6 +24,9 @@ BloomFilter.fromBuffer = function fromBuffer(payload) { return new BloomFilter(obj); }; +/** + * @returns {Buffer} + */ BloomFilter.prototype.toBuffer = function toBuffer() { var bw = new BufferWriter(); bw.writeVarintNum(this.vData.length); diff --git a/lib/inventory.js b/lib/inventory.js index 5d4b1345..da269830 100644 --- a/lib/inventory.js +++ b/lib/inventory.js @@ -13,6 +13,7 @@ var _ = bitcore.deps._; * @param {Object} - obj * @param {Number} - obj.type - Inventory.TYPE * @param {Buffer} - obj.hash - The hash for the inventory + * @constructor */ function Inventory(obj) { this.type = obj.type; diff --git a/lib/messages/index.js b/lib/messages/index.js index 7e5ec7a3..54bba09d 100644 --- a/lib/messages/index.js +++ b/lib/messages/index.js @@ -4,6 +4,16 @@ var bitcore = require('bitcore'); var BufferUtil = bitcore.util.buffer; var Hash = bitcore.crypto.Hash; +/** + * A factory to build Bitcoin protocol messages. + * @param {Object} - [options] + * @param {Number} - [options.magicNumber] + * @param {Function} - [options.Block] - A block constructor + * @param {Function} - [options.BlockHeader] - A block header constructor + * @param {Function} - [options.MerkleBlock] - A merkle block constructor + * @param {Function} - [options.Transaction] - A transaction constructor + * @constructor + */ function Messages(options) { this.builder = Messages.builder(options); @@ -25,6 +35,9 @@ Messages.PAYLOAD_START = 16; Messages.Message = require('./message'); Messages.builder = require('./builder'); +/** + * @param {Buffers} dataBuffer + */ Messages.prototype.parseBuffer = function(dataBuffer) { /* jshint maxstatements: 18 */ if (dataBuffer.length < Messages.MINIMUM_LENGTH) { diff --git a/lib/messages/message.js b/lib/messages/message.js index f1a938ae..60fa2fb8 100644 --- a/lib/messages/message.js +++ b/lib/messages/message.js @@ -7,6 +7,10 @@ var Hash = bitcore.crypto.Hash; /** * Base message that can be inherited to add an additional * `getPayload` method to modify the message payload. + * @param {Object} - options + * @param {String} - options.command + * @param {Number} - options.magicNumber + * @constructor */ function Message(options) { if(!options) { @@ -16,6 +20,10 @@ function Message(options) { this.magicNumber = options.magicNumber; } +/** + * @returns {Buffer} - Serialized message + * @constructor + */ Message.prototype.toBuffer = Message.prototype.serialize = function() { var commandBuf = new Buffer(Array(12)); From 4c4d53a4c53e3dcf1a364d7f1817e1149aec7de6 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 11:27:05 -0400 Subject: [PATCH 38/48] add jsdocs for inv, getdata and notfound --- lib/messages/commands/getdata.js | 5 +++++ lib/messages/commands/inv.js | 17 +++++++++++++++++ lib/messages/commands/notfound.js | 17 +++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index d23c82e6..4035e6e9 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -11,6 +11,11 @@ var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * @param {Object|Array} - options - If options is an array will use as "inventory" + * @param {Array} options.inventory - An array of inventory items + * @constructor + */ function GetdataMessage(options) { if (!(this instanceof GetdataMessage)) { return new GetdataMessage(options); diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js index 9c359b4c..9f5065cc 100644 --- a/lib/messages/commands/inv.js +++ b/lib/messages/commands/inv.js @@ -11,6 +11,11 @@ var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * @param {Object|Array} - options - If options is an array will use as "inventory" + * @param {Array} options.inventory - An array of inventory items + * @constructor + */ function InvMessage(options) { if (!(this instanceof InvMessage)) { return new InvMessage(options); @@ -33,14 +38,26 @@ function InvMessage(options) { } inherits(InvMessage, Message); +/** + * @param {Buffer|String} hash - The hash of the transaction inventory item + * @returns {InvMessage} + */ InvMessage.forTransaction = function(hash) { return new InvMessage([Inventory.forTransaction(hash)]); }; +/** + * @param {Buffer|String} hash - The hash of the block inventory item + * @returns {InvMessage} + */ InvMessage.forBlock = function(hash) { return new InvMessage([Inventory.forBlock(hash)]); }; +/** + * @param {Buffer|String} hash - The hash of the filtered block inventory item + * @returns {InvMessage} + */ InvMessage.forFilteredBlock = function(hash) { return new InvMessage([Inventory.forFilteredBlock(hash)]); }; diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js index 61caa476..8e124a22 100644 --- a/lib/messages/commands/notfound.js +++ b/lib/messages/commands/notfound.js @@ -11,6 +11,11 @@ var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * @param {Object|Array} - options - If options is an array will use as "inventory" + * @param {Array} options.inventory - An array of inventory items + * @constructor + */ function NotfoundMessage(options) { if (!(this instanceof NotfoundMessage)) { return new NotfoundMessage(options); @@ -33,14 +38,26 @@ function NotfoundMessage(options) { } inherits(NotfoundMessage, Message); +/** + * @param {Buffer|String} hash - The hash of the transaction inventory item + * @returns {InvMessage} + */ NotfoundMessage.forTransaction = function(hash) { return new NotfoundMessage([Inventory.forTransaction(hash)]); }; +/** + * @param {Buffer|String} hash - The hash of the block inventory item + * @returns {InvMessage} + */ NotfoundMessage.forBlock = function(hash) { return new NotfoundMessage([Inventory.forBlock(hash)]); }; +/** + * @param {Buffer|String} hash - The hash of the filtered block inventory item + * @returns {InvMessage} + */ NotfoundMessage.forFilteredBlock = function(hash) { return new NotfoundMessage([Inventory.forFilteredBlock(hash)]); }; From 42985a77166dc830e9bc8e7fc712c682b7cd2909 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 11:41:58 -0400 Subject: [PATCH 39/48] added jsdocs for message commands --- lib/messages/commands/addr.js | 8 +++++++- lib/messages/commands/alert.js | 7 +++++++ lib/messages/commands/block.js | 6 ++++++ lib/messages/commands/filteradd.js | 6 +++--- lib/messages/commands/filterclear.js | 5 +++++ lib/messages/commands/filterload.js | 6 ++++++ lib/messages/commands/getaddr.js | 2 +- lib/messages/commands/getdata.js | 1 + lib/messages/commands/inv.js | 1 + lib/messages/commands/notfound.js | 1 + lib/messages/commands/tx.js | 6 ++++++ 11 files changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/messages/commands/addr.js b/lib/messages/commands/addr.js index 83125f96..3428476c 100644 --- a/lib/messages/commands/addr.js +++ b/lib/messages/commands/addr.js @@ -9,6 +9,12 @@ var BufferWriter = bitcore.encoding.BufferWriter; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * @param {Object} - options + * @param {Array} - options.addresses - An array of addrs + * @extends Message + * @constructor + */ function AddrMessage(options) { if (!(this instanceof AddrMessage)) { return new AddrMessage(options); @@ -20,7 +26,7 @@ function AddrMessage(options) { this.command = 'addr'; this.magicNumber = magicNumber; this.addresses = options.addresses; -}; +} inherits(AddrMessage, Message); AddrMessage.fromObject = function(options) { diff --git a/lib/messages/commands/alert.js b/lib/messages/commands/alert.js index 12b07cfd..48ebf00a 100644 --- a/lib/messages/commands/alert.js +++ b/lib/messages/commands/alert.js @@ -9,6 +9,13 @@ var BufferWriter = bitcore.encoding.BufferWriter; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * @param {Object} - options + * @param {Buffer} - options.payload + * @param {Buffer} - options.signature + * @extends Message + * @constructor + */ function AlertMessage(options) { if (!(this instanceof AlertMessage)) { return new AlertMessage(options); diff --git a/lib/messages/commands/block.js b/lib/messages/commands/block.js index fdb6180a..1c27d0b1 100644 --- a/lib/messages/commands/block.js +++ b/lib/messages/commands/block.js @@ -7,6 +7,12 @@ var bitcore = require('bitcore'); var Block = bitcore.Block; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * @param {Object|Block} - options - If is an instance of Block will use as options.block + * @param {Block} - options.block - An instance of a Block + * @extends Message + * @constructor + */ function BlockMessage(options) { if (!(this instanceof BlockMessage)) { return new BlockMessage(options); diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js index 48ffc623..2f75c45f 100644 --- a/lib/messages/commands/filteradd.js +++ b/lib/messages/commands/filteradd.js @@ -13,9 +13,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** * Request peer to add data to a bloom filter already set by 'filterload' - * - * @name P2P.Message.filteradd - * @param{Buffer} data - Array of bytes representing bloom filter data + * @param {Buffer} data - Array of bytes representing bloom filter data + * @extends Message + * @constructor */ function FilteraddMessage(options) { if (!(this instanceof FilteraddMessage)) { diff --git a/lib/messages/commands/filterclear.js b/lib/messages/commands/filterclear.js index 38082545..e35c429e 100644 --- a/lib/messages/commands/filterclear.js +++ b/lib/messages/commands/filterclear.js @@ -7,6 +7,11 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * Request peer to clear data for a bloom filter + * @extends Message + * @constructor + */ function FilterclearMessage(options) { if (!(this instanceof FilterclearMessage)) { return new FilterclearMessage(options); diff --git a/lib/messages/commands/filterload.js b/lib/messages/commands/filterload.js index 98acdf2d..4c3e9dae 100644 --- a/lib/messages/commands/filterload.js +++ b/lib/messages/commands/filterload.js @@ -10,6 +10,12 @@ var _ = bitcore.deps._; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * Request peer to send inv messages based on a bloom filter + * @param {BloomFilter} options.filter - An instance of BloomFilter + * @extends Message + * @constructor + */ function FilterloadMessage(options) { if (!(this instanceof FilterloadMessage)) { return new FilterloadMessage(options); diff --git a/lib/messages/commands/getaddr.js b/lib/messages/commands/getaddr.js index 70d0e7b8..d684c5a4 100644 --- a/lib/messages/commands/getaddr.js +++ b/lib/messages/commands/getaddr.js @@ -24,7 +24,7 @@ GetaddrMessage.fromObject = function(options) { return new GetaddrMessage(options); }; -GetaddrMessage.fromBuffer = function(payload) { +GetaddrMessage.fromBuffer = function() { var obj = {}; return GetaddrMessage.fromObject(obj); }; diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index 4035e6e9..1cf947a3 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -14,6 +14,7 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** * @param {Object|Array} - options - If options is an array will use as "inventory" * @param {Array} options.inventory - An array of inventory items + * @extends Message * @constructor */ function GetdataMessage(options) { diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js index 9f5065cc..730a6379 100644 --- a/lib/messages/commands/inv.js +++ b/lib/messages/commands/inv.js @@ -14,6 +14,7 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** * @param {Object|Array} - options - If options is an array will use as "inventory" * @param {Array} options.inventory - An array of inventory items + * @extends Message * @constructor */ function InvMessage(options) { diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js index 8e124a22..f7eed046 100644 --- a/lib/messages/commands/notfound.js +++ b/lib/messages/commands/notfound.js @@ -14,6 +14,7 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** * @param {Object|Array} - options - If options is an array will use as "inventory" * @param {Array} options.inventory - An array of inventory items + * @extends Message * @constructor */ function NotfoundMessage(options) { diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js index f136a244..a5931a53 100644 --- a/lib/messages/commands/tx.js +++ b/lib/messages/commands/tx.js @@ -7,6 +7,12 @@ var bitcore = require('bitcore'); var Transaction = bitcore.Transaction; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * @param {Object|Transaction} - options - If is an instance of Transaction will use as options.transaction + * @param {Transaction} - options.transaction - An instance of a Transaction + * @extends Message + * @constructor + */ function TransactionMessage(options) { if (!(this instanceof TransactionMessage)) { return new TransactionMessage(options); From f1aa4d3bc002ef0ad0bad986e54f8782edc85892 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 12:22:43 -0400 Subject: [PATCH 40/48] updated docs for message commands --- lib/messages/commands/filteradd.js | 2 +- lib/messages/commands/filterclear.js | 2 +- lib/messages/commands/getaddr.js | 5 +++++ lib/messages/commands/getblocks.js | 9 ++++----- lib/messages/commands/getheaders.js | 11 ++++++----- lib/messages/commands/headers.js | 6 +++--- lib/messages/commands/mempool.js | 7 +++++++ lib/messages/commands/merkleblock.js | 6 +++--- lib/messages/commands/ping.js | 9 ++++++++- lib/messages/commands/pong.js | 7 +++++++ lib/messages/commands/reject.js | 2 +- lib/messages/commands/verack.js | 7 ++++++- lib/messages/commands/version.js | 15 ++++++++++----- 13 files changed, 62 insertions(+), 26 deletions(-) diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js index 2f75c45f..9c2728cf 100644 --- a/lib/messages/commands/filteradd.js +++ b/lib/messages/commands/filteradd.js @@ -28,7 +28,7 @@ function FilteraddMessage(options) { this.magicNumber = magicNumber; this.command = 'filteradd'; this.data = options.data || BufferUtil.EMPTY_BUFFER; -}; +} inherits(FilteraddMessage, Message); FilteraddMessage.fromObject = function(options) { diff --git a/lib/messages/commands/filterclear.js b/lib/messages/commands/filterclear.js index e35c429e..ba39028c 100644 --- a/lib/messages/commands/filterclear.js +++ b/lib/messages/commands/filterclear.js @@ -22,7 +22,7 @@ function FilterclearMessage(options) { Message.call(this, options); this.magicNumber = magicNumber; this.command = 'filterclear'; -}; +} inherits(FilterclearMessage, Message); FilterclearMessage.fromObject = function(options) { diff --git a/lib/messages/commands/getaddr.js b/lib/messages/commands/getaddr.js index d684c5a4..6bed5d56 100644 --- a/lib/messages/commands/getaddr.js +++ b/lib/messages/commands/getaddr.js @@ -7,6 +7,11 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * Request information about active peers + * @extends Message + * @constructor + */ function GetaddrMessage(options) { if (!(this instanceof GetaddrMessage)) { return new GetaddrMessage(options); diff --git a/lib/messages/commands/getblocks.js b/lib/messages/commands/getblocks.js index 26a78a53..7b0dcc42 100644 --- a/lib/messages/commands/getblocks.js +++ b/lib/messages/commands/getblocks.js @@ -15,9 +15,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); * Query another peer about blocks. It can query for multiple block hashes, * and the response will contain all the chains of blocks starting from those * hashes. - * - * @param{Array} starts - array of buffers or strings with the starting block hashes - * @param{Buffer} [stop] - hash of the last block + * @param {Object} options + * @param {Array} options.starts - Array of buffers or strings with the starting block hashes + * @param {Buffer} options.stop - Hash of the last block */ function GetblocksMessage(options) { if (!(this instanceof GetblocksMessage)) { @@ -34,8 +34,7 @@ function GetblocksMessage(options) { options = utils.sanitizeStartStop(options); this.starts = options.starts; this.stop = options.stop; - -}; +} inherits(GetblocksMessage, Message); GetblocksMessage.fromObject = function(obj) { diff --git a/lib/messages/commands/getheaders.js b/lib/messages/commands/getheaders.js index 023cbd04..fb154d69 100644 --- a/lib/messages/commands/getheaders.js +++ b/lib/messages/commands/getheaders.js @@ -12,10 +12,12 @@ var protocolVersion = 70000; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** - * Request block headers starting from a hash - * - * @param{Array} starts - array of buffers with the starting block hashes - * @param{Buffer} [stop] - hash of the last block + * Query another peer about block headers. It can query for multiple block hashes, + * and the response will contain all the chains of blocks starting from those + * hashes. + * @param {Object} options + * @param {Array} options.starts - Array of buffers or strings with the starting block hashes + * @param {Buffer} options.stop - Hash of the last block */ function GetheadersMessage(options) { if (!(this instanceof GetheadersMessage)) { @@ -32,7 +34,6 @@ function GetheadersMessage(options) { options = utils.sanitizeStartStop(options); this.starts = options.starts; this.stop = options.stop; - } inherits(GetheadersMessage, Message); diff --git a/lib/messages/commands/headers.js b/lib/messages/commands/headers.js index a7282cc4..ce9dfcf7 100644 --- a/lib/messages/commands/headers.js +++ b/lib/messages/commands/headers.js @@ -14,8 +14,8 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** * Sent in response to a `getheaders` message. It contains information about * block headers. - * - * @param{Array} blockheaders - array of block headers + * @param {Object} options + * @param {Array} options.headers - array of block headers */ function HeadersMessage(options) { if (!(this instanceof HeadersMessage)) { @@ -28,7 +28,7 @@ function HeadersMessage(options) { this.magicNumber = magicNumber; this.command = 'headers'; this.headers = options.headers; -}; +} inherits(HeadersMessage, Message); HeadersMessage.fromObject = function(options) { diff --git a/lib/messages/commands/mempool.js b/lib/messages/commands/mempool.js index 724fe1c1..d6f85dbd 100644 --- a/lib/messages/commands/mempool.js +++ b/lib/messages/commands/mempool.js @@ -7,6 +7,13 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * The mempool message sends a request to a node asking for information about + * transactions it has verified but which have not yet confirmed. + * @see https://en.bitcoin.it/wiki/Protocol_documentation#mempool + * @extends Message + * @constructor + */ function MempoolMessage(options) { if (!(this instanceof MempoolMessage)) { return new MempoolMessage(options); diff --git a/lib/messages/commands/merkleblock.js b/lib/messages/commands/merkleblock.js index 4ab72cb7..114a839b 100644 --- a/lib/messages/commands/merkleblock.js +++ b/lib/messages/commands/merkleblock.js @@ -12,9 +12,9 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** * Contains information about a MerkleBlock - * - * @name P2P.Message.MerkleBlock - * @param {MerkleBlock} block + * @see https://en.bitcoin.it/wiki/Protocol_documentation + * @param {Object} options + * @param {MerkleBlock} options.merkleBlock */ function MerkleblockMessage(options) { if (!(this instanceof MerkleblockMessage)) { diff --git a/lib/messages/commands/ping.js b/lib/messages/commands/ping.js index 93009883..c7623463 100644 --- a/lib/messages/commands/ping.js +++ b/lib/messages/commands/ping.js @@ -8,6 +8,13 @@ var BufferReader = bitcore.encoding.BufferReader; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * A message to confirm that a connection is still valid. + * @param {Object} options + * @param {Buffer} options.nonce + * @extends Message + * @constructor + */ function PingMessage(options) { if (!(this instanceof PingMessage)) { return new PingMessage(options); @@ -19,7 +26,7 @@ function PingMessage(options) { this.command = 'ping'; this.magicNumber = magicNumber; this.nonce = options.nonce || utils.getNonce(); -}; +} inherits(PingMessage, Message); PingMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/pong.js b/lib/messages/commands/pong.js index 82d940a1..da7cea73 100644 --- a/lib/messages/commands/pong.js +++ b/lib/messages/commands/pong.js @@ -8,6 +8,13 @@ var BufferReader = bitcore.encoding.BufferReader; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * A message in response to a ping message. + * @param {Object} options + * @param {Buffer} options.nonce + * @extends Message + * @constructor + */ function PongMessage(options) { if (!(this instanceof PongMessage)) { return new PongMessage(options); diff --git a/lib/messages/commands/reject.js b/lib/messages/commands/reject.js index f19b676e..47aebb3c 100644 --- a/lib/messages/commands/reject.js +++ b/lib/messages/commands/reject.js @@ -18,7 +18,7 @@ function RejectMessage(options) { Message.call(this, options); this.magicNumber = magicNumber; this.command = 'reject'; -}; +} inherits(RejectMessage, Message); RejectMessage.fromObject = function(options) { diff --git a/lib/messages/commands/verack.js b/lib/messages/commands/verack.js index 12bad63a..86e8695d 100644 --- a/lib/messages/commands/verack.js +++ b/lib/messages/commands/verack.js @@ -7,6 +7,11 @@ var BufferUtil = bitcore.util.buffer; var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); +/** + * A message in response to a version message. + * @extends Message + * @constructor + */ function VerackMessage(options) { if (!(this instanceof VerackMessage)) { return new VerackMessage(options); @@ -17,7 +22,7 @@ function VerackMessage(options) { Message.call(this, options); this.magicNumber = magicNumber; this.command = 'verack'; -}; +} inherits(VerackMessage, Message); VerackMessage.fromObject = function(obj) { diff --git a/lib/messages/commands/version.js b/lib/messages/commands/version.js index 7652cf65..e4487b11 100644 --- a/lib/messages/commands/version.js +++ b/lib/messages/commands/version.js @@ -17,11 +17,16 @@ var packageInfo = require('../../../package.json'); * The version message is used on connection creation to advertise * the type of node. The remote node will respond with its version, and no * communication is possible until both peers have exchanged their versions. - * By default, bitcore advertises itself as named `bitcore:0.8`. * - * @param{Object} obj - properties for the version - * @param{String} obj.subversion - version of the client - * @param{Buffer} obj.nonce - a random 8 byte buffer + * @see https://en.bitcoin.it/wiki/Protocol_documentation#version + * @param{Object} [obj] - properties for the version + * @param{Buffer} [obj.nonce] - a random 8 byte buffer + * @param{String} [obj.subversion] - version of the client + * @param{BN} [obj.services] + * @param{Date} [obj.timestamp] + * @param{Number} [obj.startHeight] + * @extends Message + * @constructor */ function VersionMessage(obj) { if (!(this instanceof VersionMessage)) { @@ -38,7 +43,7 @@ function VersionMessage(obj) { this.version = this.version || protocolVersion; this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/'; this.startHeight = this.startHeight || 0; -}; +} inherits(VersionMessage, Message); VersionMessage.fromObject = function(obj) { From 39d1ae9ac8d6b87f6de29ec4940fd10c800e612a Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 12:59:29 -0400 Subject: [PATCH 41/48] fix jsdocs for message commands --- lib/messages/commands/filteradd.js | 3 ++- lib/messages/commands/getdata.js | 12 ++++++++++++ lib/messages/commands/notfound.js | 6 +++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js index 9c2728cf..c9349df3 100644 --- a/lib/messages/commands/filteradd.js +++ b/lib/messages/commands/filteradd.js @@ -13,7 +13,8 @@ var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); /** * Request peer to add data to a bloom filter already set by 'filterload' - * @param {Buffer} data - Array of bytes representing bloom filter data + * @param {Object} options + * @param {Buffer} options.data - Array of bytes representing bloom filter data * @extends Message * @constructor */ diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index 1cf947a3..9dfeb49f 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -40,14 +40,26 @@ function GetdataMessage(options) { } inherits(GetdataMessage, Message); +/** + * @param {Buffer|String} hash - The hash of the transaction inventory item + * @returns {GetdataMessage} + */ GetdataMessage.forTransaction = function(hash) { return new GetdataMessage([Inventory.forTransaction(hash)]); }; +/** + * @param {Buffer|String} hash - The hash of the block inventory item + * @returns {GetdataMessage} + */ GetdataMessage.forBlock = function(hash) { return new GetdataMessage([Inventory.forBlock(hash)]); }; +/** + * @param {Buffer|String} hash - The hash of the filtered block inventory item + * @returns {GetdataMessage} + */ GetdataMessage.forFilteredBlock = function(hash) { return new GetdataMessage([Inventory.forFilteredBlock(hash)]); }; diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js index f7eed046..5b96e168 100644 --- a/lib/messages/commands/notfound.js +++ b/lib/messages/commands/notfound.js @@ -41,7 +41,7 @@ inherits(NotfoundMessage, Message); /** * @param {Buffer|String} hash - The hash of the transaction inventory item - * @returns {InvMessage} + * @returns {NotfoundMessage} */ NotfoundMessage.forTransaction = function(hash) { return new NotfoundMessage([Inventory.forTransaction(hash)]); @@ -49,7 +49,7 @@ NotfoundMessage.forTransaction = function(hash) { /** * @param {Buffer|String} hash - The hash of the block inventory item - * @returns {InvMessage} + * @returns {NotfoundMessage} */ NotfoundMessage.forBlock = function(hash) { return new NotfoundMessage([Inventory.forBlock(hash)]); @@ -57,7 +57,7 @@ NotfoundMessage.forBlock = function(hash) { /** * @param {Buffer|String} hash - The hash of the filtered block inventory item - * @returns {InvMessage} + * @returns {NotfoundMessage} */ NotfoundMessage.forFilteredBlock = function(hash) { return new NotfoundMessage([Inventory.forFilteredBlock(hash)]); From 8f2d0089df225e29ca7d20d7062f6c68d56b1988 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 13:50:50 -0400 Subject: [PATCH 42/48] updated readme and fixed image scaling issue --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4e7d7aec..2dfc7be4 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -bitcore payment protocol -P2P Networking capabilities for bitcore +bitcore payment protocol +Bitcore P2P ======= [![NPM Package](https://img.shields.io/npm/v/bitcore-p2p.svg?style=flat-square)](https://www.npmjs.org/package/bitcore-p2p) [![Build Status](https://img.shields.io/travis/bitpay/bitcore-p2p.svg?branch=master&style=flat-square)](https://travis-ci.org/bitpay/bitcore-p2p) [![Coverage Status](https://img.shields.io/coveralls/bitpay/bitcore-p2p.svg?style=flat-square)](https://coveralls.io/r/bitpay/bitcore-p2p?branch=master) -bitcore-p2p adds support for connecting to the bitcoin p2p network in [Node.js](http://nodejs.org/). +`bitcore-p2p` adds [Bitcoin protocol](https://en.bitcoin.it/wiki/Protocol_documentation) support for Bitcore. See [the main bitcore repo](https://github.com/bitpay/bitcore) for more information. @@ -15,12 +15,12 @@ See [the main bitcore repo](https://github.com/bitpay/bitcore) for more informat ```sh npm install bitcore-p2p ``` -In order to connect to the bitcore network, you'll need to know the IP address of at least one node of the network. You can do that by using the known DNS servers. Then, you can connect to it: +In order to connect to the Bitcoin network, you'll need to know the IP address of at least one node of the network, or use [Pool](/docs/pool.md) to discover peers using a DNS seed. ```javascript var Peer = require('bitcore-p2p').Peer; -var peer = new Peer('0.0.0.0'); +var peer = new Peer({host: '127.0.0.1'}); peer.on('ready', function() { // peer info @@ -55,4 +55,3 @@ See [CONTRIBUTING.md](https://github.com/bitpay/bitcore) on the main bitcore rep Code released under [the MIT license](https://github.com/bitpay/bitcore/blob/master/LICENSE). Copyright 2013-2015 BitPay, Inc. Bitcore is a trademark maintained by BitPay, Inc. - From 664ceb2d30e79dbe98057dc1dd131f317573b47a Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 15:44:50 -0400 Subject: [PATCH 43/48] improved peer jsdoc, fixed docs for messages, and switched to use svg for readme icon --- README.md | 2 +- docs/messages.md | 2 +- lib/peer.js | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2dfc7be4..cd77df14 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -bitcore payment protocol +bitcore payment protocol Bitcore P2P ======= diff --git a/docs/messages.md b/docs/messages.md index ad9e6677..9d6bcfe7 100644 --- a/docs/messages.md +++ b/docs/messages.md @@ -6,7 +6,7 @@ description: A superclass for the messages of the bitcoin network The bitcoin protocol specifies a set of [messages](https://en.bitcoin.it/wiki/Protocol_specification) that can be sent from peer to peer. `bitcore-p2p` provides support for some of these messages. -To create a messages, you can use any of the message constructors, here is a simple example: +To create a message, you can use any of the message constructors, here is a simple example: ```javascript var messages = new Messages(); diff --git a/lib/peer.js b/lib/peer.js index 1b789f11..5efb9ed5 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -11,8 +11,11 @@ var $ = bitcore.util.preconditions; var util = require('util'); /** - * A constructor to create Peer instances to send and recieve messages - * using the standard Bitcoin protocol. + * The Peer constructor will create an instance of Peer to send and recieve messages + * using the standard Bitcoin protocol. A Peer instance represents one connection + * on the Bitcoin network. To create a new peer connection provide the host and port + * options and then invoke the connect method. Additionally, a newly connected socket + * can be provided instead of host and port. * * @example * ```javascript From 16aa98924ab1f5b4b546a03b0aed0c99967f30dd Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 15:48:43 -0400 Subject: [PATCH 44/48] fixed spelling --- lib/peer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/peer.js b/lib/peer.js index 5efb9ed5..4ba35dd2 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -11,7 +11,7 @@ var $ = bitcore.util.preconditions; var util = require('util'); /** - * The Peer constructor will create an instance of Peer to send and recieve messages + * The Peer constructor will create an instance of Peer to send and receive messages * using the standard Bitcoin protocol. A Peer instance represents one connection * on the Bitcoin network. To create a new peer connection provide the host and port * options and then invoke the connect method. Additionally, a newly connected socket From f6e9c437d69e69a2c1a493ad8af5e34c01f38a11 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 17:38:58 -0400 Subject: [PATCH 45/48] upgrade bloom-filter to 0.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 164ce337..4dcd3df0 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ }, "dependencies": { "bitcore": "^0.11.0", - "bloom-filter": "^0.1.1", + "bloom-filter": "^0.2.0", "buffers": "^0.1.1", "socks5-client": "^0.3.6" }, From c0e3bdb1908fb228149a4dd020f1b48e63779e9a Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 16 Mar 2015 18:53:14 -0400 Subject: [PATCH 46/48] removed fromObject method that is nolonger needed --- lib/messages/commands/addr.js | 6 +----- lib/messages/commands/alert.js | 6 +----- lib/messages/commands/block.js | 6 +----- lib/messages/commands/filteradd.js | 6 +----- lib/messages/commands/filterclear.js | 6 +----- lib/messages/commands/filterload.js | 6 +----- lib/messages/commands/getaddr.js | 7 +------ lib/messages/commands/getblocks.js | 6 +----- lib/messages/commands/getdata.js | 6 +----- lib/messages/commands/getheaders.js | 6 +----- lib/messages/commands/headers.js | 6 +----- lib/messages/commands/inv.js | 6 +----- lib/messages/commands/mempool.js | 6 +----- lib/messages/commands/merkleblock.js | 6 +----- lib/messages/commands/notfound.js | 6 +----- lib/messages/commands/ping.js | 6 +----- lib/messages/commands/pong.js | 6 +----- lib/messages/commands/reject.js | 6 +----- lib/messages/commands/tx.js | 6 +----- lib/messages/commands/verack.js | 6 +----- lib/messages/commands/version.js | 6 +----- test/messages/builder.js | 2 +- 22 files changed, 22 insertions(+), 107 deletions(-) diff --git a/lib/messages/commands/addr.js b/lib/messages/commands/addr.js index 3428476c..5144cdd2 100644 --- a/lib/messages/commands/addr.js +++ b/lib/messages/commands/addr.js @@ -29,10 +29,6 @@ function AddrMessage(options) { } inherits(AddrMessage, Message); -AddrMessage.fromObject = function(options) { - return new AddrMessage(options); -}; - AddrMessage.fromBuffer = function(payload) { var parser = new BufferReader(payload); @@ -50,7 +46,7 @@ AddrMessage.fromBuffer = function(payload) { } utils.checkFinished(parser); - return AddrMessage.fromObject(obj); + return new AddrMessage(obj); }; AddrMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/alert.js b/lib/messages/commands/alert.js index 48ebf00a..03130a36 100644 --- a/lib/messages/commands/alert.js +++ b/lib/messages/commands/alert.js @@ -32,17 +32,13 @@ function AlertMessage(options) { } inherits(AlertMessage, Message); -AlertMessage.fromObject = function(options) { - return new AlertMessage(options); -}; - AlertMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); obj.payload = parser.readVarLengthBuffer(); obj.signature = parser.readVarLengthBuffer(); utils.checkFinished(parser); - return AlertMessage.fromObject(obj); + return new AlertMessage(obj); }; AlertMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/block.js b/lib/messages/commands/block.js index 1c27d0b1..bdd3d2be 100644 --- a/lib/messages/commands/block.js +++ b/lib/messages/commands/block.js @@ -35,13 +35,9 @@ function BlockMessage(options) { } inherits(BlockMessage, Message); -BlockMessage.fromObject = function(options) { - return new BlockMessage(options); -}; - BlockMessage.fromBuffer = function(payload) { var block = Block.fromBuffer(payload); - return BlockMessage.fromObject({block: block}); + return new BlockMessage({block: block}); }; BlockMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js index c9349df3..ac766829 100644 --- a/lib/messages/commands/filteradd.js +++ b/lib/messages/commands/filteradd.js @@ -32,17 +32,13 @@ function FilteraddMessage(options) { } inherits(FilteraddMessage, Message); -FilteraddMessage.fromObject = function(options) { - return new FilteraddMessage(options); -}; - FilteraddMessage.fromBuffer = function(payload) { var obj = {}; $.checkArgument(payload); var parser = new BufferReader(payload); obj.data = parser.readVarLengthBuffer(); utils.checkFinished(parser); - return FilteraddMessage.fromObject(obj); + return new FilteraddMessage(obj); }; FilteraddMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/filterclear.js b/lib/messages/commands/filterclear.js index ba39028c..0682e064 100644 --- a/lib/messages/commands/filterclear.js +++ b/lib/messages/commands/filterclear.js @@ -25,12 +25,8 @@ function FilterclearMessage(options) { } inherits(FilterclearMessage, Message); -FilterclearMessage.fromObject = function(options) { - return new FilterclearMessage(options); -}; - FilterclearMessage.fromBuffer = function(payload) { - return FilterclearMessage.fromObject({}); + return new FilterclearMessage({}); }; FilterclearMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/filterload.js b/lib/messages/commands/filterload.js index 4c3e9dae..0c4cab76 100644 --- a/lib/messages/commands/filterload.js +++ b/lib/messages/commands/filterload.js @@ -32,14 +32,10 @@ function FilterloadMessage(options) { } inherits(FilterloadMessage, Message); -FilterloadMessage.fromObject = function(options) { - return new FilterloadMessage(options); -}; - FilterloadMessage.fromBuffer = function(payload) { var obj = {}; obj.filter = BloomFilter.fromBuffer(payload); - return FilterloadMessage.fromObject(obj); + return new FilterloadMessage(obj); }; FilterloadMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/getaddr.js b/lib/messages/commands/getaddr.js index 6bed5d56..e139d764 100644 --- a/lib/messages/commands/getaddr.js +++ b/lib/messages/commands/getaddr.js @@ -25,13 +25,8 @@ function GetaddrMessage(options) { } inherits(GetaddrMessage, Message); -GetaddrMessage.fromObject = function(options) { - return new GetaddrMessage(options); -}; - GetaddrMessage.fromBuffer = function() { - var obj = {}; - return GetaddrMessage.fromObject(obj); + return new GetaddrMessage({}); }; GetaddrMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/getblocks.js b/lib/messages/commands/getblocks.js index 7b0dcc42..76fd462b 100644 --- a/lib/messages/commands/getblocks.js +++ b/lib/messages/commands/getblocks.js @@ -37,10 +37,6 @@ function GetblocksMessage(options) { } inherits(GetblocksMessage, Message); -GetblocksMessage.fromObject = function(obj) { - return new GetblocksMessage(obj); -}; - GetblocksMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); @@ -55,7 +51,7 @@ GetblocksMessage.fromBuffer = function(payload) { } obj.stop = parser.read(32); utils.checkFinished(parser); - return GetblocksMessage.fromObject(obj); + return new GetblocksMessage(obj); }; GetblocksMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index 9dfeb49f..6bb3ce6f 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -64,10 +64,6 @@ GetdataMessage.forFilteredBlock = function(hash) { return new GetdataMessage([Inventory.forFilteredBlock(hash)]); }; -GetdataMessage.fromObject = function(options) { - return new GetdataMessage(options); -}; - GetdataMessage.fromBuffer = function(payload) { var obj = { inventory: [] @@ -82,7 +78,7 @@ GetdataMessage.fromBuffer = function(payload) { } utils.checkFinished(parser); - return GetdataMessage.fromObject(obj); + return new GetdataMessage(obj); }; GetdataMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/getheaders.js b/lib/messages/commands/getheaders.js index fb154d69..ec1be4b0 100644 --- a/lib/messages/commands/getheaders.js +++ b/lib/messages/commands/getheaders.js @@ -37,10 +37,6 @@ function GetheadersMessage(options) { } inherits(GetheadersMessage, Message); -GetheadersMessage.fromObject = function(obj) { - return new GetheadersMessage(obj); -}; - GetheadersMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); @@ -55,7 +51,7 @@ GetheadersMessage.fromBuffer = function(payload) { } obj.stop = parser.read(32); utils.checkFinished(parser); - return GetheadersMessage.fromObject(obj); + return new GetheadersMessage(obj); }; GetheadersMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/headers.js b/lib/messages/commands/headers.js index ce9dfcf7..785c01c7 100644 --- a/lib/messages/commands/headers.js +++ b/lib/messages/commands/headers.js @@ -31,10 +31,6 @@ function HeadersMessage(options) { } inherits(HeadersMessage, Message); -HeadersMessage.fromObject = function(options) { - return new HeadersMessage(options); -}; - HeadersMessage.fromBuffer = function(payload) { var obj = {}; @@ -51,7 +47,7 @@ HeadersMessage.fromBuffer = function(payload) { } utils.checkFinished(parser); - return HeadersMessage.fromObject(obj); + return new HeadersMessage(obj); }; HeadersMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js index 730a6379..200e9cf8 100644 --- a/lib/messages/commands/inv.js +++ b/lib/messages/commands/inv.js @@ -63,10 +63,6 @@ InvMessage.forFilteredBlock = function(hash) { return new InvMessage([Inventory.forFilteredBlock(hash)]); }; -InvMessage.fromObject = function(options) { - return new InvMessage(options); -}; - InvMessage.prototype.getPayload = function() { var bw = new BufferWriter(); utils.writeInventory(this.inventory, bw); @@ -87,7 +83,7 @@ InvMessage.fromBuffer = function(payload) { } utils.checkFinished(parser); - return InvMessage.fromObject(obj); + return new InvMessage(obj); }; module.exports = function(options) { diff --git a/lib/messages/commands/mempool.js b/lib/messages/commands/mempool.js index d6f85dbd..e18c28b8 100644 --- a/lib/messages/commands/mempool.js +++ b/lib/messages/commands/mempool.js @@ -27,12 +27,8 @@ function MempoolMessage(options) { } inherits(MempoolMessage, Message); -MempoolMessage.fromObject = function(options) { - return new MempoolMessage(options); -}; - MempoolMessage.fromBuffer = function(payload) { - return MempoolMessage.fromObject({}); + return new MempoolMessage({}); }; MempoolMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/merkleblock.js b/lib/messages/commands/merkleblock.js index 114a839b..486cb959 100644 --- a/lib/messages/commands/merkleblock.js +++ b/lib/messages/commands/merkleblock.js @@ -34,15 +34,11 @@ function MerkleblockMessage(options) { } inherits(MerkleblockMessage, Message); -MerkleblockMessage.fromObject = function(options) { - return new MerkleblockMessage(options); -}; - MerkleblockMessage.fromBuffer = function(payload) { var obj = {}; $.checkArgument(BufferUtil.isBuffer(payload)); obj.merkleBlock = MerkleBlock.fromBuffer(payload); - return MerkleblockMessage.fromObject(obj); + return new MerkleblockMessage(obj); }; MerkleblockMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js index 5b96e168..2860dcad 100644 --- a/lib/messages/commands/notfound.js +++ b/lib/messages/commands/notfound.js @@ -63,10 +63,6 @@ NotfoundMessage.forFilteredBlock = function(hash) { return new NotfoundMessage([Inventory.forFilteredBlock(hash)]); }; -NotfoundMessage.fromObject = function(options) { - return new NotfoundMessage(options); -}; - NotfoundMessage.fromBuffer = function(payload) { var obj = { inventory: [] @@ -81,7 +77,7 @@ NotfoundMessage.fromBuffer = function(payload) { } utils.checkFinished(parser); - return NotfoundMessage.fromObject(obj); + return new NotfoundMessage(obj); }; NotfoundMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/ping.js b/lib/messages/commands/ping.js index c7623463..77c2ff62 100644 --- a/lib/messages/commands/ping.js +++ b/lib/messages/commands/ping.js @@ -33,17 +33,13 @@ PingMessage.prototype.getPayload = function() { return this.nonce; }; -PingMessage.fromObject = function(obj) { - return new PingMessage(obj); -}; - PingMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); obj.nonce = parser.read(8); utils.checkFinished(parser); - return PingMessage.fromObject(obj); + return new PingMessage(obj); }; module.exports = function(options) { diff --git a/lib/messages/commands/pong.js b/lib/messages/commands/pong.js index da7cea73..69ab9649 100644 --- a/lib/messages/commands/pong.js +++ b/lib/messages/commands/pong.js @@ -29,17 +29,13 @@ function PongMessage(options) { } inherits(PongMessage, Message); -PongMessage.fromObject = function(obj) { - return new PongMessage(obj); -}; - PongMessage.fromBuffer = function(payload) { var obj = {}; var parser = new BufferReader(payload); obj.nonce = parser.read(8); utils.checkFinished(parser); - return PongMessage.fromObject(obj); + return new PongMessage(obj); }; PongMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/reject.js b/lib/messages/commands/reject.js index 47aebb3c..0a470386 100644 --- a/lib/messages/commands/reject.js +++ b/lib/messages/commands/reject.js @@ -21,13 +21,9 @@ function RejectMessage(options) { } inherits(RejectMessage, Message); -RejectMessage.fromObject = function(options) { - return new RejectMessage(options); -}; - RejectMessage.fromBuffer = function(payload) { var obj = {}; - return RejectMessage.fromObject(obj); + return new RejectMessage(obj); }; RejectMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js index a5931a53..e203417f 100644 --- a/lib/messages/commands/tx.js +++ b/lib/messages/commands/tx.js @@ -35,10 +35,6 @@ function TransactionMessage(options) { } inherits(TransactionMessage, Message); -TransactionMessage.fromObject = function(options) { - return new TransactionMessage(options); -}; - TransactionMessage.fromBuffer = function(payload) { var transaction; if (Transaction.prototype.fromBuffer) { @@ -46,7 +42,7 @@ TransactionMessage.fromBuffer = function(payload) { } else { transaction = Transaction.fromBuffer(payload); } - return TransactionMessage.fromObject({transaction: transaction}); + return new TransactionMessage({transaction: transaction}); }; TransactionMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/verack.js b/lib/messages/commands/verack.js index 86e8695d..6eef05b5 100644 --- a/lib/messages/commands/verack.js +++ b/lib/messages/commands/verack.js @@ -25,12 +25,8 @@ function VerackMessage(options) { } inherits(VerackMessage, Message); -VerackMessage.fromObject = function(obj) { - return new VerackMessage(obj); -}; - VerackMessage.fromBuffer = function(payload) { - return VerackMessage.fromObject({}); + return new VerackMessage({}); }; VerackMessage.prototype.getPayload = function() { diff --git a/lib/messages/commands/version.js b/lib/messages/commands/version.js index e4487b11..0c9627dd 100644 --- a/lib/messages/commands/version.js +++ b/lib/messages/commands/version.js @@ -46,10 +46,6 @@ function VersionMessage(obj) { } inherits(VersionMessage, Message); -VersionMessage.fromObject = function(obj) { - return new VersionMessage(obj); -}; - VersionMessage.fromBuffer = function(payload) { var parser = new BufferReader(payload); var obj = {}; @@ -78,7 +74,7 @@ VersionMessage.fromBuffer = function(payload) { } utils.checkFinished(parser); - return VersionMessage.fromObject(obj); + return new VersionMessage(obj); }; VersionMessage.prototype.getPayload = function() { diff --git a/test/messages/builder.js b/test/messages/builder.js index 86798dff..4bd10941 100644 --- a/test/messages/builder.js +++ b/test/messages/builder.js @@ -63,7 +63,7 @@ describe('Messages Builder', function() { it('#relay setting works', function() { [true,false].forEach(function(relay) { - var message = b.commands.version.fromObject({relay: relay}); + var message = new b.commands.version({relay: relay}); message.relay.should.equal(relay); var messageBuf = message.getPayload(); var newMessage = b.commands.version.fromBuffer(messageBuf); From 7cfe6d18650d175499f75baa6b5cbe3c5ea46651 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 17 Mar 2015 15:11:16 -0400 Subject: [PATCH 47/48] added unit tests for buffers.skip --- lib/buffers.js | 2 +- test/buffers.js | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 test/buffers.js diff --git a/lib/buffers.js b/lib/buffers.js index 01456ac8..f9633363 100644 --- a/lib/buffers.js +++ b/lib/buffers.js @@ -7,7 +7,7 @@ Buffers.prototype.skip = function(i) { return; } - if (i === this.length) { + if (i >= this.length) { this.buffers = []; this.length = 0; return; diff --git a/test/buffers.js b/test/buffers.js new file mode 100644 index 00000000..0dc7c2be --- /dev/null +++ b/test/buffers.js @@ -0,0 +1,63 @@ +'use strict'; + +var chai = require('chai'); +var should = chai.should(); +var Buffers = require('../lib/buffers'); + +describe('Buffers', function() { + + var buffs = function buffs() { + var b = new Buffers(); + b.push(new Buffer('0123', 'hex')); + b.push(new Buffer('4567', 'hex')); + b.push(new Buffer('89ab', 'hex')); + b.push(new Buffer('cdef', 'hex')); + return b; + }; + + it('set buffers to empty if "i" is greater than the total length', function() { + var b = buffs(); + b.length.should.equal(8); + b.skip(100); + b.buffers.should.deep.equal([]); + b.length.should.equal(0); + }); + + it('set buffers to empty if "i" is equal than the total length', function() { + var b = buffs(); + b.length.should.equal(8); + b.skip(8); + b.buffers.should.deep.equal([]); + b.length.should.equal(0); + }); + + it('do not skip if "i" is zero', function() { + var b = buffs(); + b.skip(0); + b.length.should.equal(8); + }); + + it('remove part of the first buffer', function() { + var b = buffs(); + b.skip(1); + b.length.should.equal(7); + b.buffers[0].should.deep.equal(new Buffer('23', 'hex')); + }); + + it('remove the first three buffers', function() { + var b = buffs(); + b.skip(6); + b.length.should.equal(2); + should.not.exist(b.buffers[1]); + should.not.exist(b.buffers[2]); + should.not.exist(b.buffers[3]); + }); + + it('remove part of the fourth buffer', function() { + var b = buffs(); + b.skip(7); + b.length.should.equal(1); + b.buffers[0].should.deep.equal(new Buffer('ef', 'hex')); + }); + +}); From 34c38466f7c95366a117a9bee8a30904b7bb8d10 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 17 Mar 2015 16:01:52 -0400 Subject: [PATCH 48/48] moved inventory helper functions to builder --- lib/messages/builder.js | 25 ++++++++++++++++++++ lib/messages/commands/getdata.js | 24 ------------------- lib/messages/commands/inv.js | 24 ------------------- lib/messages/commands/notfound.js | 24 ------------------- test/messages/builder.js | 38 ++++++++++++++++++++++++++++++- test/messages/commands/index.js | 37 ------------------------------ 6 files changed, 62 insertions(+), 110 deletions(-) diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 33f1cfa7..985775a4 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -1,6 +1,7 @@ 'use strict'; var bitcore = require('bitcore'); +var Inventory = require('../inventory'); function builder(options) { /* jshint maxstatements: 20 */ @@ -31,6 +32,11 @@ function builder(options) { protocolVersion: options.protocolVersion, magicNumber: options.magicNumber }, + inventoryCommands: [ + 'getdata', + 'inv', + 'notfound' + ], commandsMap: { version: 'Version', verack: 'VerAck', @@ -61,6 +67,25 @@ function builder(options) { exported.commands[key] = require('./commands/' + key)(options); } + exported.inventoryCommands.forEach(function(command) { + + // add forTransaction methods + exported.commands[command].forTransaction = function forTransaction(hash) { + return new exported.commands[command]([Inventory.forTransaction(hash)]); + }; + + // add forBlock methods + exported.commands[command].forBlock = function forBlock(hash) { + return new exported.commands[command]([Inventory.forBlock(hash)]); + }; + + // add forFilteredBlock methods + exported.commands[command].forFilteredBlock = function forFilteredBlock(hash) { + return new exported.commands[command]([Inventory.forFilteredBlock(hash)]); + }; + + }); + return exported; } diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js index 6bb3ce6f..debdc185 100644 --- a/lib/messages/commands/getdata.js +++ b/lib/messages/commands/getdata.js @@ -40,30 +40,6 @@ function GetdataMessage(options) { } inherits(GetdataMessage, Message); -/** - * @param {Buffer|String} hash - The hash of the transaction inventory item - * @returns {GetdataMessage} - */ -GetdataMessage.forTransaction = function(hash) { - return new GetdataMessage([Inventory.forTransaction(hash)]); -}; - -/** - * @param {Buffer|String} hash - The hash of the block inventory item - * @returns {GetdataMessage} - */ -GetdataMessage.forBlock = function(hash) { - return new GetdataMessage([Inventory.forBlock(hash)]); -}; - -/** - * @param {Buffer|String} hash - The hash of the filtered block inventory item - * @returns {GetdataMessage} - */ -GetdataMessage.forFilteredBlock = function(hash) { - return new GetdataMessage([Inventory.forFilteredBlock(hash)]); -}; - GetdataMessage.fromBuffer = function(payload) { var obj = { inventory: [] diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js index 200e9cf8..2b2b73c7 100644 --- a/lib/messages/commands/inv.js +++ b/lib/messages/commands/inv.js @@ -39,30 +39,6 @@ function InvMessage(options) { } inherits(InvMessage, Message); -/** - * @param {Buffer|String} hash - The hash of the transaction inventory item - * @returns {InvMessage} - */ -InvMessage.forTransaction = function(hash) { - return new InvMessage([Inventory.forTransaction(hash)]); -}; - -/** - * @param {Buffer|String} hash - The hash of the block inventory item - * @returns {InvMessage} - */ -InvMessage.forBlock = function(hash) { - return new InvMessage([Inventory.forBlock(hash)]); -}; - -/** - * @param {Buffer|String} hash - The hash of the filtered block inventory item - * @returns {InvMessage} - */ -InvMessage.forFilteredBlock = function(hash) { - return new InvMessage([Inventory.forFilteredBlock(hash)]); -}; - InvMessage.prototype.getPayload = function() { var bw = new BufferWriter(); utils.writeInventory(this.inventory, bw); diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js index 2860dcad..66b2776b 100644 --- a/lib/messages/commands/notfound.js +++ b/lib/messages/commands/notfound.js @@ -39,30 +39,6 @@ function NotfoundMessage(options) { } inherits(NotfoundMessage, Message); -/** - * @param {Buffer|String} hash - The hash of the transaction inventory item - * @returns {NotfoundMessage} - */ -NotfoundMessage.forTransaction = function(hash) { - return new NotfoundMessage([Inventory.forTransaction(hash)]); -}; - -/** - * @param {Buffer|String} hash - The hash of the block inventory item - * @returns {NotfoundMessage} - */ -NotfoundMessage.forBlock = function(hash) { - return new NotfoundMessage([Inventory.forBlock(hash)]); -}; - -/** - * @param {Buffer|String} hash - The hash of the filtered block inventory item - * @returns {NotfoundMessage} - */ -NotfoundMessage.forFilteredBlock = function(hash) { - return new NotfoundMessage([Inventory.forFilteredBlock(hash)]); -}; - NotfoundMessage.fromBuffer = function(payload) { var obj = { inventory: [] diff --git a/test/messages/builder.js b/test/messages/builder.js index 4bd10941..89761c1c 100644 --- a/test/messages/builder.js +++ b/test/messages/builder.js @@ -73,6 +73,42 @@ describe('Messages Builder', function() { }); - }); + describe('Inventory helpers for: ' + b.inventoryCommands.join(', '), function() { + + var constructors = b.inventoryCommands; + var fakeHash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a'; + + describe('#forTransaction', function() { + constructors.forEach(function(name) { + it(name, function() { + should.exist(b.commands[name].forTransaction); + var message = b.commands[name].forTransaction(fakeHash); + should.exist(message); + message.should.be.instanceof(b.commands[name]); + }); + }); + }); + + describe('#forBlock', function() { + constructors.forEach(function(name) { + it(name, function() { + var message = b.commands[name].forBlock(fakeHash); + should.exist(message); + message.should.be.instanceof(b.commands[name]); + }); + }); + }); + describe('#forFilteredBlock', function() { + constructors.forEach(function(name) { + it(name, function() { + var message = b.commands[name].forFilteredBlock(fakeHash); + should.exist(message); + message.should.be.instanceof(b.commands[name]); + }); + }); + }); + + }); + }); }); diff --git a/test/messages/commands/index.js b/test/messages/commands/index.js index 8cae6d18..53bdebdf 100644 --- a/test/messages/commands/index.js +++ b/test/messages/commands/index.js @@ -9,7 +9,6 @@ var bitcore = require('bitcore'); describe('Command Messages', function() { var messages = new Messages(); - var constructors = ['GetData', 'Inventory', 'NotFound']; var commandsMap = { version: 'Version', verack: 'VerAck', @@ -34,42 +33,6 @@ describe('Command Messages', function() { getaddr: 'GetAddr' }; - describe('Inventory helpers for: ' + constructors.join(', '), function() { - - var fakeHash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a'; - - describe('#forTransaction', function() { - constructors.forEach(function(name) { - it(name, function() { - var message = messages[name].forTransaction(fakeHash); - should.exist(message); - message.should.be.instanceof(messages[name]); - }); - }); - }); - - describe('#forBlock', function() { - constructors.forEach(function(name) { - it(name, function() { - var message = messages[name].forBlock(fakeHash); - should.exist(message); - message.should.be.instanceof(messages[name]); - }); - }); - }); - - describe('#forFilteredBlock', function() { - constructors.forEach(function(name) { - it(name, function() { - var message = messages[name].forFilteredBlock(fakeHash); - should.exist(message); - message.should.be.instanceof(messages[name]); - }); - }); - }); - - }); - describe('Transaction', function() { it('should accept a transaction instance as an argument', function() {