diff --git a/.babelrc b/.babelrc index d42eaa2..7af9827 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,8 @@ { - "plugins": ["transform-flow-strip-types"] + "plugins": ["transform-flow-strip-types"], + "env": { + "test": { + "plugins": ["transform-flow-strip-types", "istanbul"] + } + } } diff --git a/flow-typed/npm/chai_v3.5.x.js b/flow-typed/npm/chai_v3.5.x.js new file mode 100644 index 0000000..2435c53 --- /dev/null +++ b/flow-typed/npm/chai_v3.5.x.js @@ -0,0 +1,192 @@ +// flow-typed signature: 9e3d57d259619cd3fcf8c7c3eced99f5 +// flow-typed version: 60acee2512/chai_v3.5.x/flow_>=v0.24.0 + +declare module 'chai' { + declare type ExpectChain = { + and: ExpectChain, + at: ExpectChain, + be: ExpectChain, + been: ExpectChain, + have: ExpectChain, + has: ExpectChain, + is: ExpectChain, + of: ExpectChain, + same: ExpectChain, + that: ExpectChain, + to: ExpectChain, + which: ExpectChain, + with: ExpectChain, + + not: ExpectChain, + deep: ExpectChain, + any: ExpectChain, + all: ExpectChain, + + a: ExpectChain & (type: string) => ExpectChain, + an: ExpectChain & (type: string) => ExpectChain, + + include: ExpectChain & (value: mixed) => ExpectChain, + includes: ExpectChain & (value: mixed) => ExpectChain, + contain: ExpectChain & (value: mixed) => ExpectChain, + contains: ExpectChain & (value: mixed) => ExpectChain, + + eql: (value: T) => ExpectChain, + equal: (value: T) => ExpectChain, + equals: (value: T) => ExpectChain, + + above: (value: T & number) => ExpectChain, + least: (value: T & number) => ExpectChain, + below: (value: T & number) => ExpectChain, + most: (value: T & number) => ExpectChain, + within: (start: T & number, finish: T & number) => ExpectChain, + + instanceof: (constructor: mixed) => ExpectChain, + property: ( +

(name: string, value?: P) => ExpectChain

+ & (name: string) => ExpectChain + ), + + length: (value: number) => ExpectChain | ExpectChain, + lengthOf: (value: number) => ExpectChain, + + match: (regex: RegExp) => ExpectChain, + string: (string: string) => ExpectChain, + + key: (key: string) => ExpectChain, + keys: (key: string | Array, ...keys: Array) => ExpectChain, + + throw: (err: Class | Error | RegExp | string, msg?: RegExp | string) => ExpectChain, + + respondTo: (method: string) => ExpectChain, + itself: ExpectChain, + + satisfy: (method: (value: T) => bool) => ExpectChain, + + closeTo: (expected: T & number, delta: number) => ExpectChain, + + members: (set: mixed) => ExpectChain, + oneOf: (list: Array) => ExpectChain, + + change: (obj: mixed, key: string) => ExpectChain, + increase: (obj: mixed, key: string) => ExpectChain, + decrease: (obj: mixed, key: string) => ExpectChain, + + // dirty-chai + ok: () => ExpectChain, + true: () => ExpectChain, + false: () => ExpectChain, + null: () => ExpectChain, + undefined: () => ExpectChain, + exist: () => ExpectChain, + empty: () => ExpectChain, + + // chai-immutable + size: (n: number) => ExpectChain, + + // sinon-chai + called: () => ExpectChain, + callCount: (n: number) => ExpectChain, + calledOnce: () => ExpectChain, + calledBefore: (spy: mixed) => ExpectChain, + calledAfter: (spy: mixed) => ExpectChain, + calledWith: (...args: Array) => ExpectChain, + calledWithMatch: (...args: Array) => ExpectChain, + calledWithExactly: (...args: Array) => ExpectChain, + + // chai-as-promised + eventually: () => ExpectChain + }; + + declare function expect(actual: T): ExpectChain; + + declare function use(plugin: (chai: Object, utils: Object) => void): void; + + declare class assert { + static(expression: mixed, message?: string): void; + static fail(actual: mixed, expected: mixed, message?: string, operator?: string): void; + + static isOk(object: mixed, message?: string): void; + static isNotOk(object: mixed, message?: string): void; + + static equal(actual: mixed, expected: mixed, message?: string): void; + static notEqual(actual: mixed, expected: mixed, message?: string): void; + + static strictEqual(act: mixed, exp: mixed, msg?: string): void; + static notStrictEqual(act: mixed, exp: mixed, msg?: string): void; + + static deepEqual(act: mixed, exp: mixed, msg?: string): void; + static notDeepEqual(act: mixed, exp: mixed, msg?: string): void; + + static isTrue(val: mixed, msg?: string): void; + static isNotTrue(val: mixed, msg?: string): void; + static isFalse(val: mixed, msg?: string): void; + static isNotFalse(val: mixed, msg?: string): void; + + static isNull(val: mixed, msg?: string): void; + static isNotNull(val: mixed, msg?: string): void; + + static isUndefined(val: mixed, msg?: string): void; + static isDefined(val: mixed, msg?: string): void; + + static isNaN(val: mixed, msg?: string): void; + static isNotNaN(val: mixed, msg?: string): void; + + static isAbove(val: number, abv: number, msg?: string): void; + static isBelow(val: number, blw: number, msg?: string): void; + + static isAtMost(val: number, atmst: number, msg?: string): void; + static isAtLeast(val: number, atlst: number, msg?: string): void; + + static isFunction(val: mixed, msg?: string): void; + static isNotFunction(val: mixed, msg?: string): void; + + static isObject(val: mixed, msg?: string): void; + static isNotObject(val: mixed, msg?: string): void; + + static isArray(val: mixed, msg?: string): void; + static isNotArray(val: mixed, msg?: string): void; + + static isString(val: mixed, msg?: string): void; + static isNotString(val: mixed, msg?: string): void; + + static isNumber(val: mixed, msg?: string): void; + static isNotNumber(val: mixed, msg?: string): void; + + static isBoolean(val: mixed, msg?: string): void; + static isNotBoolean(val: mixed, msg?: string): void; + + static typeOf(val: mixed, type: string, msg?: string): void; + static notTypeOf(val: mixed, type: string, msg?: string): void; + + static instanceOf(val: mixed, constructor: Function, msg?: string): void; + static notInstanceOf(val: mixed, constructor: Function, msg?: string): void; + + static include(exp: string, inc: mixed, msg?: string): void; + static include(exp: Array, inc: mixed, msg?: string): void; + + static notInclude(exp: string, inc: mixed, msg?: string): void; + static notInclude(exp: Array, inc: mixed, msg?: string): void; + + static match(exp: mixed, re: RegExp, msg?: string): void; + static notMatch(exp: mixed, re: RegExp, msg?: string): void; + + static property(obj: Object, prop: string, msg?: string): void; + static notProperty(obj: Object, prop: string, msg?: string): void; + static deepProperty(obj: Object, prop: string, msg?: string): void; + static notDeepProperty(obj: Object, prop: string, msg?: string): void; + + static propertyVal(obj: Object, prop: string, val: mixed, msg?: string): void; + static propertyNotVal(obj: Object, prop: string, val: mixed, msg?: string): void; + + static deepPropertyVal(obj: Object, prop: string, val: mixed, msg?: string): void; + static deepPropertyNotVal(obj: Object, prop: string, val: mixed, msg?: string): void; + + static lengthOf(exp: mixed, len: number, msg?: string): void; + } + + declare var config: { + includeStack: boolean, + showDiff: boolean, + truncateThreshold: number + }; +} diff --git a/flow-typed/npm/flow-bin_v0.x.x.js b/flow-typed/npm/flow-bin_v0.x.x.js new file mode 100644 index 0000000..7418f04 --- /dev/null +++ b/flow-typed/npm/flow-bin_v0.x.x.js @@ -0,0 +1,6 @@ +// flow-typed signature: 6a5610678d4b01e13bbfbbc62bdaf583 +// flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x + +declare module 'flow-bin' { + declare module.exports: string; +} diff --git a/flow-typed/npm/knex_v0.12.x.js b/flow-typed/npm/knex_v0.12.x.js new file mode 100644 index 0000000..a21394d --- /dev/null +++ b/flow-typed/npm/knex_v0.12.x.js @@ -0,0 +1,164 @@ +// flow-typed signature: 70c5066abfa785637d22983bcc46f142 +// flow-typed version: a3fdaed924/knex_v0.12.x/flow_>=v0.33.x + +declare class Knex$Transaction mixins Knex$QueryBuilder, events$EventEmitter, Promise { + commit(connection?: any, value?: any): Promise; + rollback(): Promise; + savepoint(connection?: any): Promise; +} + +declare type Knex$QueryBuilderFn = (qb: Knex$QueryBuilder) => Knex$QueryBuilder; + +declare class Knex$QueryBuilder mixins Promise { + select(key?: string[]): this; + select(...key: string[]): this; + timeout(ms: number, options?: { cancel: bool }): this; + column(key: string[]): this; + column(...key: string[]): this; + with(alias: string, w: string|Knex$QueryBuilderFn): this; + withSchema(schema: string): this; + returning(column: string): this; + returning(...columns: string[]): this; + returning(columns: string[]): this; + as(name: string): this; + transacting(trx: ?Knex$Transaction): this; + where(builder: Knex$QueryBuilderFn): this; + where(column: string, value: any): this; + where(column: string, operator: string, value: any): this; + whereNot(builder: Knex$QueryBuilderFn): this; + whereNot(column: string, value: any): this; + whereNot(column: string, operator: string, value: any): this; + whereIn(column: string, values: any[]): this; + whereNotIn(column: string, values: any[]): this; + whereNull(column: string): this; + whereNotNull(column: string): this; + whereExists(column: string): this; + whereNotExists(column: string): this; + whereBetween(column: string, range: number[]): this; + whereNotBetween(column: string, range: number[]): this; + whereRaw(raw: any): this; + orWhere(builder: Knex$QueryBuilderFn): this; + orWhere(column: string, value: any): this; + orWhere(column: string, operator: string, value: any): this; + orWhereNot(builder: Knex$QueryBuilderFn): this; + orWhereNot(column: string, value: any): this; + orWhereNot(column: string, operator: string, value: any): this; + orWhereIn(column: string, values: any[]): this; + orWhereNotIn(column: string, values: any[]): this; + orWhereNull(column: string): this; + orWhereNotNull(column: string): this; + orWhereExists(column: string): this; + orWhereNotExists(column: string): this; + orWhereBetween(column: string, range: number[]): this; + orWhereNotBetween(column: string, range: number[]): this; + orWhereRaw(raw: any): this; + innerJoin(table: string, c1: string, operator: string, c2: string): this; + innerJoin(table: string, c1: string, c2: string): this; + innerJoin(builder: Knex$QueryBuilder, c1?: string, c2?: string): this; + leftJoin(table: string, c1: string, operator: string, c2: string): this; + leftJoin(table: string, c1: string, c2: string): this; + leftJoin(builder: Knex$QueryBuilder): this; + leftOuterJoin(table: string, c1: string, operator: string, c2: string): this; + leftOuterJoin(table: string, c1: string, c2: string): this; + rightJoin(table: string, c1: string, operator: string, c2: string): this; + rightJoin(table: string, c1: string, c2: string): this; + rightOuterJoin(table: string, c1: string, operator: string, c2: string): this; + rightOuterJoin(table: string, c1: string, c2: string): this; + outerJoin(table: string, c1: string, operator: string, c2: string): this; + outerJoin(table: string, c1: string, c2: string): this; + fullOuterJoin(table: string, c1: string, operator: string, c2: string): this; + fullOuterJoin(table: string, c1: string, c2: string): this; + crossJoin(column: string, c1: string, c2: string): this; + crossJoin(column: string, c1: string, operator: string, c2: string): this; + joinRaw(sql: string, bindings?: mixed[]): this; + distinct(): this; + groupBy(column: string): this; + groupByRaw(): this; + orderBy(column: string, direction?: 'desc' | 'asc'): this; + orderByRaw(): this; + offset(offset: number): this; + limit(limit: number): this; + having(column: string, operator: string, value: mixed): this; + union(): this; + unionAll(): this; + count(column?: string): this; + countDistinct(column?: string): this; + min(column?: string): this; + max(column?: string): this; + sum(column?: string): this; + sumDistinct(column?: string): this; + avg(column?: string): this; + avgDistinct(column?: string): this; + pluck(column: string): this; + first(): this; + from(table: string): this; + from(builder: Knex$QueryBuilderFn|Knex$Knex|Knex$QueryBuilder): this; + + insert(): this; + del(): this; + delete(): this; + update(): this; + returning(columns: string[]): this; +} + +declare class Knex$Knex mixins Knex$QueryBuilder, Promise { + static (config: Knex$Config): Knex$Knex; + static QueryBuilder: typeof Knex$QueryBuilder; + $call: (tableName: string) => Knex$QueryBuilder; + raw(sqlString: string): any; + client: any; + destroy(): Promise; + +} + +declare type Knex$PostgresConfig = { + client?: 'pg', + connection?: { + host?: string, + user?: string, + password?: string, + database?: string, + charset?: string, + }, + searchPath?: string, +} +declare type Knex$MysqlConfig = { + client?: 'mysql', + connection?: { + host?: string, + user?: string, + password?: string, + database?: string, + }, +} +declare type Knex$SqliteConfig = { + client?: 'sqlite3', + connection?: { + filename?: string, + } +} +declare type Knex$Config = Knex$PostgresConfig | Knex$MysqlConfig | Knex$SqliteConfig; + +declare module 'knex' { + declare type Error = { + name: string, + length: number, + severity: string, + code: string, + detail: string, + hint?: string, + position?: any, + intenralPosition?: any, + internalQuery?: any, + where?: any, + schema: string, + table: string, + column?: any, + dataType?: any, + constraint?: string, + file: string, + line: string, + routine: string, + } + declare var exports: typeof Knex$Knex; +} diff --git a/flow-typed/npm/lodash_v4.x.x.js b/flow-typed/npm/lodash_v4.x.x.js new file mode 100644 index 0000000..e88543c --- /dev/null +++ b/flow-typed/npm/lodash_v4.x.x.js @@ -0,0 +1,512 @@ +// flow-typed signature: ef3ba0ad105839652bede588f150ca4b +// flow-typed version: 90ba41eb32/lodash_v4.x.x/flow_>=v0.38.x + +declare module 'lodash' { + declare type TemplateSettings = { + escape?: RegExp, + evaluate?: RegExp, + imports?: Object, + interpolate?: RegExp, + variable?: string, + }; + + declare type TruncateOptions = { + length?: number, + omission?: string, + separator?: RegExp|string, + }; + + declare type DebounceOptions = { + leading?: bool, + maxWait?: number, + trailing?: bool, + }; + + declare type ThrottleOptions = { + leading?: bool, + trailing?: bool, + }; + + declare type NestedArray = Array>; + + declare type matchesIterateeShorthand = Object; + declare type matchesPropertyIterateeShorthand = [string, any]; + declare type propertyIterateeShorthand = string; + + declare type OPredicate = + | ((value: A, key: string, object: O) => any) + | matchesIterateeShorthand + | matchesPropertyIterateeShorthand + | propertyIterateeShorthand; + + declare type OIterateeWithResult = Object|string|((value: V, key: string, object: O) => R); + declare type OIteratee = OIterateeWithResult; + declare type OFlatMapIteratee = OIterateeWithResult>; + + declare type Predicate = + | ((value: T, index: number, array: Array) => any) + | matchesIterateeShorthand + | matchesPropertyIterateeShorthand + | propertyIterateeShorthand; + + declare type _Iteratee = (item: T, index: number, array: ?Array) => mixed; + declare type Iteratee = _Iteratee|Object|string; + declare type Iteratee2 = ((item: T, index: number, array: ?Array) => U)|Object|string; + declare type FlatMapIteratee = ((item: T, index: number, array: ?Array) => Array)|Object|string; + declare type Comparator = (item: T, item2: T) => bool; + + declare type MapIterator = + | ((item: T, index: number, array: Array) => U) + | propertyIterateeShorthand; + + declare type OMapIterator = + | ((item: T, key: string, object: O) => U) + | propertyIterateeShorthand; + + declare class Lodash { + // Array + chunk(array: ?Array, size?: number): Array>; + compact(array: Array): Array; + concat(base: Array, ...elements: Array): Array; + difference(array: ?Array, values?: Array): Array; + differenceBy(array: ?Array, values: Array, iteratee: Iteratee): T[]; + differenceWith(array: T[], values: T[], comparator?: Comparator): T[]; + drop(array: ?Array, n?: number): Array; + dropRight(array: ?Array, n?: number): Array; + dropRightWhile(array: ?Array, predicate?: Predicate): Array; + dropWhile(array: ?Array, predicate?: Predicate): Array; + fill(array: ?Array, value: U, start?: number, end?: number): Array; + findIndex(array: ?Array, predicate?: Predicate): number; + findLastIndex(array: ?Array, predicate?: Predicate): number; + // alias of _.head + first(array: ?Array): T; + flatten(array: Array|X>): Array; + flattenDeep(array: any[]): Array; + flattenDepth(array: any[], depth?: number): any[]; + fromPairs(pairs: Array): Object; + head(array: ?Array): T; + indexOf(array: ?Array, value: T, fromIndex?: number): number; + initial(array: ?Array): Array; + intersection(...arrays: Array>): Array; + //Workaround until (...parameter: T, parameter2: U) works + intersectionBy(a1: Array, iteratee?: Iteratee): Array; + intersectionBy(a1: Array, a2: Array, iteratee?: Iteratee): Array; + intersectionBy(a1: Array, a2: Array, a3: Array, iteratee?: Iteratee): Array; + intersectionBy(a1: Array, a2: Array, a3: Array, a4: Array, iteratee?: Iteratee): Array; + //Workaround until (...parameter: T, parameter2: U) works + intersectionWith(a1: Array, comparator: Comparator): Array; + intersectionWith(a1: Array, a2: Array, comparator: Comparator): Array; + intersectionWith(a1: Array, a2: Array, a3: Array, comparator: Comparator): Array; + intersectionWith(a1: Array, a2: Array, a3: Array, a4: Array, comparator: Comparator): Array; + join(array: ?Array, separator?: string): string; + last(array: ?Array): T; + lastIndexOf(array: ?Array, value: T, fromIndex?: number): number; + nth(array: T[], n?: number): T; + pull(array: ?Array, ...values?: Array): Array; + pullAll(array: ?Array, values: Array): Array; + pullAllBy(array: ?Array, values: Array, iteratee?: Iteratee): Array; + pullAllWith(array?: T[], values: T[], comparator?: Function): T[]; + pullAt(array: ?Array, ...indexed?: Array): Array; + pullAt(array: ?Array, indexed?: Array): Array; + remove(array: ?Array, predicate?: Predicate): Array; + reverse(array: ?Array): Array; + slice(array: ?Array, start?: number, end?: number): Array; + sortedIndex(array: ?Array, value: T): number; + sortedIndexBy(array: ?Array, value: T, iteratee?: Iteratee): number; + sortedIndexOf(array: ?Array, value: T): number; + sortedLastIndex(array: ?Array, value: T): number; + sortedLastIndexBy(array: ?Array, value: T, iteratee?: Iteratee): number; + sortedLastIndexOf(array: ?Array, value: T): number; + sortedUniq(array: ?Array): Array; + sortedUniqBy(array: ?Array, iteratee?: (value: T) => mixed): Array; + tail(array: ?Array): Array; + take(array: ?Array, n?: number): Array; + takeRight(array: ?Array, n?: number): Array; + takeRightWhile(array: ?Array, predicate?: Predicate): Array; + takeWhile(array: ?Array, predicate?: Predicate): Array; + union(...arrays?: Array>): Array; + //Workaround until (...parameter: T, parameter2: U) works + unionBy(a1: Array, iteratee?: Iteratee): Array; + unionBy(a1: Array, a2: Array, iteratee?: Iteratee): Array; + unionBy(a1: Array, a2: Array, a3: Array, iteratee?: Iteratee): Array; + unionBy(a1: Array, a2: Array, a3: Array, a4: Array, iteratee?: Iteratee): Array; + //Workaround until (...parameter: T, parameter2: U) works + unionWith(a1: Array, comparator?: Comparator): Array; + unionWith(a1: Array, a2: Array, comparator?: Comparator): Array; + unionWith(a1: Array, a2: Array, a3: Array, comparator?: Comparator): Array; + unionWith(a1: Array, a2: Array, a3: Array, a4: Array, comparator?: Comparator): Array; + uniq(array: ?Array): Array; + uniqBy(array: ?Array, iteratee?: Iteratee): Array; + uniqWith(array: ?Array, comparator?: Comparator): Array; + unzip(array: ?Array): Array; + unzipWith(array: ?Array, iteratee?: Iteratee): Array; + without(array: ?Array, ...values?: Array): Array; + xor(...array: Array>): Array; + //Workaround until (...parameter: T, parameter2: U) works + xorBy(a1: Array, iteratee?: Iteratee): Array; + xorBy(a1: Array, a2: Array, iteratee?: Iteratee): Array; + xorBy(a1: Array, a2: Array, a3: Array, iteratee?: Iteratee): Array; + xorBy(a1: Array, a2: Array, a3: Array, a4: Array, iteratee?: Iteratee): Array; + //Workaround until (...parameter: T, parameter2: U) works + xorWith(a1: Array, comparator?: Comparator): Array; + xorWith(a1: Array, a2: Array, comparator?: Comparator): Array; + xorWith(a1: Array, a2: Array, a3: Array, comparator?: Comparator): Array; + xorWith(a1: Array, a2: Array, a3: Array, a4: Array, comparator?: Comparator): Array; + zip(a1: A[], a2: B[]): Array<[A, B]>; + zip(a1: A[], a2: B[], a3: C[]): Array<[A, B, C]>; + zip(a1: A[], a2: B[], a3: C[], a4: D[]): Array<[A, B, C, D]>; + zip(a1: A[], a2: B[], a3: C[], a4: D[], a5: E[]): Array<[A, B, C, D, E]>; + + zipObject(props?: Array, values?: Array): Object; + zipObjectDeep(props?: any[], values?: any): Object; + //Workaround until (...parameter: T, parameter2: U) works + zipWith(a1: NestedArray, iteratee?: Iteratee): Array; + zipWith(a1: NestedArray, a2: NestedArray, iteratee?: Iteratee): Array; + zipWith(a1: NestedArray, a2: NestedArray, a3: NestedArray, iteratee?: Iteratee): Array; + zipWith(a1: NestedArray, a2: NestedArray, a3: NestedArray, a4: NestedArray, iteratee?: Iteratee): Array; + + // Collection + countBy(array: ?Array, iteratee?: Iteratee): Object; + countBy(object: T, iteratee?: OIteratee): Object; + // alias of _.forEach + each(array: ?Array, iteratee?: Iteratee): Array; + each(object: T, iteratee?: OIteratee): T; + // alias of _.forEachRight + eachRight(array: ?Array, iteratee?: Iteratee): Array; + eachRight(object: T, iteratee?: OIteratee): T; + every(array: ?Array, iteratee?: Iteratee): bool; + every(object: T, iteratee?: OIteratee): bool; + filter(array: ?Array, predicate?: Predicate): Array; + filter(object: T, predicate?: OPredicate): Array; + find(array: ?Array, predicate?: Predicate): T; + find(object: T, predicate?: OPredicate): V; + findLast(array: ?Array, predicate?: Predicate): T; + findLast(object: T, predicate?: OPredicate): V; + flatMap(array: ?Array, iteratee?: FlatMapIteratee): Array; + flatMap(object: T, iteratee?: OFlatMapIteratee): Array; + flatMapDeep(array: ?Array, iteratee?: FlatMapIteratee): Array; + flatMapDeep(object: T, iteratee?: OFlatMapIteratee): Array; + flatMapDepth(array: ?Array, iteratee?: FlatMapIteratee, depth?: number): Array; + flatMapDepth(object: T, iteratee?: OFlatMapIteratee, depth?: number): Array; + forEach(array: ?Array, iteratee?: Iteratee): Array; + forEach(object: T, iteratee?: OIteratee): T; + forEachRight(array: ?Array, iteratee?: Iteratee): Array; + forEachRight(object: T, iteratee?: OIteratee): T; + groupBy(array: ?Array, iteratee?: Iteratee): Object; + groupBy(object: T, iteratee?: OIteratee): Object; + includes(array: ?Array, value: T, fromIndex?: number): bool; + includes(object: T, value: any, fromIndex?: number): bool; + includes(str: string, value: string, fromIndex?: number): bool; + invokeMap(array: ?Array, path: ((value: T) => Array|string)|Array|string, ...args?: Array): Array; + invokeMap(object: T, path: ((value: any) => Array|string)|Array|string, ...args?: Array): Array; + keyBy(array: ?Array, iteratee?: Iteratee2): {[key: V]: T}; + keyBy(object: T, iteratee?: OIteratee): Object; + map(array: ?Array, iteratee?: MapIterator): Array; + map(object: ?T, iteratee?: OMapIterator): Array; + map(str: ?string, iteratee?: (char: string, index: number, str: string) => any): string; + orderBy(array: ?Array, iteratees?: Array>|string, orders?: Array<'asc'|'desc'>|string): Array; + orderBy(object: T, iteratees?: Array>|string, orders?: Array<'asc'|'desc'>|string): Array; + partition(array: ?Array, predicate?: Predicate): NestedArray; + partition(object: T, predicate?: OPredicate): NestedArray; + reduce(array: ?Array, iteratee?: (accumulator: U, value: T, index: number, array: ?Array) => U, accumulator?: U): U; + reduce(object: T, iteratee?: (accumulator: U, value: any, key: string, object: T) => U, accumulator?: U): U; + reduceRight(array: ?Array, iteratee?: (accumulator: U, value: T, index: number, array: ?Array) => U, accumulator?: U): U; + reduceRight(object: T, iteratee?: (accumulator: U, value: any, key: string, object: T) => U, accumulator?: U): U; + reject(array: ?Array, predicate?: Predicate): Array; + reject(object: T, predicate?: OPredicate): Array; + sample(array: ?Array): T; + sample(object: T): V; + sampleSize(array: ?Array, n?: number): Array; + sampleSize(object: T, n?: number): Array; + shuffle(array: ?Array): Array; + shuffle(object: T): Array; + size(collection: Array|Object): number; + some(array: ?Array, predicate?: Predicate): bool; + some(object?: ?T, predicate?: OPredicate): bool; + sortBy(array: ?Array, ...iteratees?: Array>): Array; + sortBy(array: ?Array, iteratees?: Array>): Array; + sortBy(object: T, ...iteratees?: Array>): Array; + sortBy(object: T, iteratees?: Array>): Array; + + // Date + now(): number; + + // Function + after(n: number, fn: Function): Function; + ary(func: Function, n?: number): Function; + before(n: number, fn: Function): Function; + bind(func: Function, thisArg: any, ...partials: Array): Function; + bindKey(obj: Object, key: string, ...partials: Array): Function; + curry(func: Function, arity?: number): Function; + curryRight(func: Function, arity?: number): Function; + debounce(func: Function, wait?: number, options?: DebounceOptions): Function; + defer(func: Function, ...args?: Array): number; + delay(func: Function, wait: number, ...args?: Array): number; + flip(func: Function): Function; + memoize(func: Function, resolver?: Function): Function; + negate(predicate: Function): Function; + once(func: Function): Function; + overArgs(func: Function, ...transforms: Array): Function; + overArgs(func: Function, transforms: Array): Function; + partial(func: Function, ...partials: any[]): Function; + partialRight(func: Function, ...partials: Array): Function; + partialRight(func: Function, partials: Array): Function; + rearg(func: Function, ...indexes: Array): Function; + rearg(func: Function, indexes: Array): Function; + rest(func: Function, start?: number): Function; + spread(func: Function): Function; + throttle(func: Function, wait?: number, options?: ThrottleOptions): Function; + unary(func: Function): Function; + wrap(value: any, wrapper: Function): Function; + + // Lang + castArray(value: *): any[]; + clone(value: T): T; + cloneDeep(value: T): T; + cloneDeepWith(value: T, customizer?: ?(value: T, key: number|string, object: T, stack: any) => U): U; + cloneWith(value: T, customizer?: ?(value: T, key: number|string, object: T, stack: any) => U): U; + conformsTo(source: T, predicates: T&{[key:string]:(x:any)=>boolean}): boolean; + eq(value: any, other: any): bool; + gt(value: any, other: any): bool; + gte(value: any, other: any): bool; + isArguments(value: any): bool; + isArray(value: any): bool; + isArrayBuffer(value: any): bool; + isArrayLike(value: any): bool; + isArrayLikeObject(value: any): bool; + isBoolean(value: any): bool; + isBuffer(value: any): bool; + isDate(value: any): bool; + isElement(value: any): bool; + isEmpty(value: any): bool; + isEqual(value: any, other: any): bool; + isEqualWith(value: T, other: U, customizer?: (objValue: any, otherValue: any, key: number|string, object: T, other: U, stack: any) => bool|void): bool; + isError(value: any): bool; + isFinite(value: any): bool; + isFunction(value: Function): true; + isFunction(value: number|string|void|null|Object): false; + isInteger(value: any): bool; + isLength(value: any): bool; + isMap(value: any): bool; + isMatch(object?: ?Object, source: Object): bool; + isMatchWith(object: T, source: U, customizer?: (objValue: any, srcValue: any, key: number|string, object: T, source: U) => bool|void): bool; + isNaN(value: any): bool; + isNative(value: any): bool; + isNil(value: any): bool; + isNull(value: any): bool; + isNumber(value: any): bool; + isObject(value: any): bool; + isObjectLike(value: any): bool; + isPlainObject(value: any): bool; + isRegExp(value: any): bool; + isSafeInteger(value: any): bool; + isSet(value: any): bool; + isString(value: any): bool; + isSymbol(value: any): bool; + isTypedArray(value: any): bool; + isUndefined(value: any): bool; + isWeakMap(value: any): bool; + isWeakSet(value: any): bool; + lt(value: any, other: any): bool; + lte(value: any, other: any): bool; + toArray(value: any): Array; + toFinite(value: any): number; + toInteger(value: any): number; + toLength(value: any): number; + toNumber(value: any): number; + toPlainObject(value: any): Object; + toSafeInteger(value: any): number; + toString(value: any): string; + + // Math + add(augend: number, addend: number): number; + ceil(number: number, precision?: number): number; + divide(dividend: number, divisor: number): number; + floor(number: number, precision?: number): number; + max(array: ?Array): T; + maxBy(array: ?Array, iteratee?: Iteratee): T; + mean(array: Array<*>): number; + meanBy(array: Array, iteratee?: Iteratee): number; + min(array: ?Array): T; + minBy(array: ?Array, iteratee?: Iteratee): T; + multiply(multiplier: number, multiplicand: number): number; + round(number: number, precision?: number): number; + subtract(minuend: number, subtrahend: number): number; + sum(array: Array<*>): number; + sumBy(array: Array, iteratee?: Iteratee): number; + + // number + clamp(number: number, lower?: number, upper: number): number; + inRange(number: number, start?: number, end: number): bool; + random(lower?: number, upper?: number, floating?: bool): number; + + // Object + assign(object?: ?Object, ...sources?: Array): Object; + assignIn(a: A, b: B): A & B; + assignIn(a: A, b: B, c: C): A & B & C; + assignIn(a: A, b: B, c: C, d: D): A & B & C & D; + assignIn(a: A, b: B, c: C, d: D, e: E): A & B & C & D & E; + assignInWith(object: T, s1: A, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A) => any|void): Object; + assignInWith(object: T, s1: A, s2: B, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B) => any|void): Object; + assignInWith(object: T, s1: A, s2: B, s3: C, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C) => any|void): Object; + assignInWith(object: T, s1: A, s2: B, s3: C, s4: D, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C|D) => any|void): Object; + assignWith(object: T, s1: A, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A) => any|void): Object; + assignWith(object: T, s1: A, s2: B, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B) => any|void): Object; + assignWith(object: T, s1: A, s2: B, s3: C, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C) => any|void): Object; + assignWith(object: T, s1: A, s2: B, s3: C, s4: D, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C|D) => any|void): Object; + at(object?: ?Object, ...paths: Array): Array; + at(object?: ?Object, paths: Array): Array; + create(prototype: T, properties?: Object): $Supertype; + defaults(object?: ?Object, ...sources?: Array): Object; + defaultsDeep(object?: ?Object, ...sources?: Array): Object; + // alias for _.toPairs + entries(object?: ?Object): NestedArray; + // alias for _.toPairsIn + entriesIn(object?: ?Object): NestedArray; + // alias for _.assignIn + extend(a: A, b: B): A & B; + extend(a: A, b: B, c: C): A & B & C; + extend(a: A, b: B, c: C, d: D): A & B & C & D; + extend(a: A, b: B, c: C, d: D, e: E): A & B & C & D & E; + // alias for _.assignInWith + extendWith(object: T, s1: A, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A) => any|void): Object; + extendWith(object: T, s1: A, s2: B, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B) => any|void): Object; + extendWith(object: T, s1: A, s2: B, s3: C, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C) => any|void): Object; + extendWith(object: T, s1: A, s2: B, s3: C, s4: D, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C|D) => any|void): Object; + findKey(object?: ?T, predicate?: OPredicate): string|void; + findLastKey(object?: ?T, predicate?: OPredicate): string|void; + forIn(object?: ?Object, iteratee?: OIteratee<*>): Object; + forInRight(object?: ?Object, iteratee?: OIteratee<*>): Object; + forOwn(object?: ?Object, iteratee?: OIteratee<*>): Object; + forOwnRight(object?: ?Object, iteratee?: OIteratee<*>): Object; + functions(object?: ?Object): Array; + functionsIn(object?: ?Object): Array; + get(object?: ?Object, path?: ?Array|string, defaultValue?: any): any; + has(object?: ?Object, path?: ?Array|string): bool; + hasIn(object?: ?Object, path?: ?Array|string): bool; + invert(object?: ?Object, multiVal?: bool): Object; + invertBy(object: ?Object, iteratee?: Function): Object; + invoke(object?: ?Object, path?: ?Array|string, ...args?: Array): any; + keys(object?: ?Object): Array; + keysIn(object?: ?Object): Array; + mapKeys(object?: ?Object, iteratee?: OIteratee<*>): Object; + mapValues(object?: ?Object, iteratee?: OIteratee<*>): Object; + merge(object?: ?Object, ...sources?: Array): Object; + mergeWith(object: T, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A) => any|void): Object; + mergeWith(object: T, s1: A, s2: B, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B) => any|void): Object; + mergeWith(object: T, s1: A, s2: B, s3: C, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C) => any|void): Object; + mergeWith(object: T, s1: A, s2: B, s3: C, s4: D, customizer?: (objValue: any, srcValue: any, key: string, object: T, source: A|B|C|D) => any|void): Object; + omit(object?: ?Object, ...props: Array): Object; + omit(object?: ?Object, props: Array): Object; + omitBy(object?: ?T, predicate?: OPredicate): Object; + pick(object?: ?Object, ...props: Array): Object; + pick(object?: ?Object, props: Array): Object; + pickBy(object?: ?T, predicate?: OPredicate): Object; + result(object?: ?Object, path?: ?Array|string, defaultValue?: any): any; + set(object?: ?Object, path?: ?Array|string, value: any): Object; + setWith(object: T, path?: ?Array|string, value: any, customizer?: (nsValue: any, key: string, nsObject: T) => any): Object; + toPairs(object?: ?Object|Array<*>): NestedArray; + toPairsIn(object?: ?Object): NestedArray; + transform(collection: Object|Array, iteratee?: OIteratee<*>, accumulator?: any): any; + unset(object?: ?Object, path?: ?Array|string): bool; + update(object: Object, path: string[]|string, updater: Function): Object; + updateWith(object: Object, path: string[]|string, updater: Function, customizer?: Function): Object; + values(object?: ?Object): Array; + valuesIn(object?: ?Object): Array; + + // Seq + // harder to read, but this is _() + (value: any): any; + chain(value: T): any; + tap(value: T, interceptor: (value:T)=>any): T; + thru(value: T1, interceptor: (value:T1)=>T2): T2; + // TODO: _.prototype.* + + // String + camelCase(string?: ?string): string; + capitalize(string?: string): string; + deburr(string?: string): string; + endsWith(string?: string, target?: string, position?: number): bool; + escape(string?: string): string; + escapeRegExp(string?: string): string; + kebabCase(string?: string): string; + lowerCase(string?: string): string; + lowerFirst(string?: string): string; + pad(string?: string, length?: number, chars?: string): string; + padEnd(string?: string, length?: number, chars?: string): string; + padStart(string?: string, length?: number, chars?: string): string; + parseInt(string: string, radix?: number): number; + repeat(string?: string, n?: number): string; + replace(string?: string, pattern: RegExp|string, replacement: ((string: string) => string)|string): string; + snakeCase(string?: string): string; + split(string?: string, separator: RegExp|string, limit?: number): Array; + startCase(string?: string): string; + startsWith(string?: string, target?: string, position?: number): bool; + template(string?: string, options?: TemplateSettings): Function; + toLower(string?: string): string; + toUpper(string?: string): string; + trim(string?: string, chars?: string): string; + trimEnd(string?: string, chars?: string): string; + trimStart(string?: string, chars?: string): string; + truncate(string?: string, options?: TruncateOptions): string; + unescape(string?: string): string; + upperCase(string?: string): string; + upperFirst(string?: string): string; + words(string?: string, pattern?: RegExp|string): Array; + + // Util + attempt(func: Function): any; + bindAll(object?: ?Object, methodNames: Array): Object; + bindAll(object?: ?Object, ...methodNames: Array): Object; + cond(pairs: NestedArray): Function; + conforms(source: Object): Function; + constant(value: T): () => T; + defaultTo(value: T1, default: T2): T1; + // NaN is a number instead of its own type, otherwise it would behave like null/void + defaultTo(value: T1, default: T2): T1|T2; + defaultTo(value: T1, default: T2): T2; + flow(...funcs?: Array): Function; + flow(funcs?: Array): Function; + flowRight(...funcs?: Array): Function; + flowRight(funcs?: Array): Function; + identity(value: T): T; + iteratee(func?: any): Function; + matches(source: Object): Function; + matchesProperty(path?: ?Array|string, srcValue: any): Function; + method(path?: ?Array|string, ...args?: Array): Function; + methodOf(object?: ?Object, ...args?: Array): Function; + mixin(object?: T, source: Object, options?: { chain: bool }): T; + noConflict(): Lodash; + noop(): void; + nthArg(n?: number): Function; + over(...iteratees: Array): Function; + over(iteratees: Array): Function; + overEvery(...predicates: Array): Function; + overEvery(predicates: Array): Function; + overSome(...predicates: Array): Function; + overSome(predicates: Array): Function; + property(path?: ?Array|string): Function; + propertyOf(object?: ?Object): Function; + range(start: number, end: number, step?: number): Array; + range(end: number, step?: number): Array; + rangeRight(start: number, end: number, step?: number): Array; + rangeRight(end: number, step?: number): Array; + runInContext(context?: Object): Function; + + stubArray(): Array<*>; + stubFalse(): false; + stubObject(): {}; + stubString(): ''; + stubTrue(): true; + times(n: number, ...rest: Array): Array; + times(n: number, iteratee: ((i: number) => T)): Array; + toPath(value: any): Array; + uniqueId(prefix?: string): string; + + // Properties + VERSION: string; + templateSettings: TemplateSettings; + } + + declare var exports: Lodash; +} diff --git a/flow-typed/npm/mkdirp_v0.5.x.js b/flow-typed/npm/mkdirp_v0.5.x.js new file mode 100644 index 0000000..e26f9a4 --- /dev/null +++ b/flow-typed/npm/mkdirp_v0.5.x.js @@ -0,0 +1,13 @@ +// flow-typed signature: 82aa0feffc2bbd64dce3bec492f5d601 +// flow-typed version: 3315d89a00/mkdirp_v0.5.x/flow_>=v0.25.0 + +declare module 'mkdirp' { + declare type Options = number | { mode?: number; fs?: mixed }; + + declare type Callback = (err: ?Error, path: ?string) => void; + + declare module.exports: { + (path: string, options?: Options | Callback, callback?: Callback): void; + sync(path: string, options?: Options): void; + }; +} diff --git a/flow-typed/npm/mocha_v3.1.x.js b/flow-typed/npm/mocha_v3.1.x.js new file mode 100644 index 0000000..6c4d8c7 --- /dev/null +++ b/flow-typed/npm/mocha_v3.1.x.js @@ -0,0 +1,25 @@ +// flow-typed signature: 6b82cf8c1da27b4f0fa7a58e5ed5babf +// flow-typed version: edf70dde46/mocha_v3.1.x/flow_>=v0.22.x + +type TestFunction = ((done: () => void) => void | Promise); + +declare var describe : { + (name:string, spec:() => void): void; + only(description:string, spec:() => void): void; + skip(description:string, spec:() => void): void; + timeout(ms:number): void; +}; + +declare var context : typeof describe; + +declare var it : { + (name:string, spec?:TestFunction): void; + only(description:string, spec:TestFunction): void; + skip(description:string, spec:TestFunction): void; + timeout(ms:number): void; +}; + +declare function before(method : TestFunction):void; +declare function beforeEach(method : TestFunction):void; +declare function after(method : TestFunction):void; +declare function afterEach(method : TestFunction):void; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ed3a189..53b0e56 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1217,6 +1217,11 @@ "from": "minimist@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz" }, + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@>=1.4.7 <2.0.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + }, "readable-stream": { "version": "1.1.14", "from": "readable-stream@>=1.1.12 <2.0.0", @@ -1292,9 +1297,9 @@ "resolved": "https://registry.npmjs.org/libp2p-spdy/-/libp2p-spdy-0.10.3.tgz" }, "libp2p-swarm": { - "version": "0.26.9", + "version": "0.26.12", "from": "libp2p-swarm@>=0.26.2 <0.27.0", - "resolved": "https://registry.npmjs.org/libp2p-swarm/-/libp2p-swarm-0.26.9.tgz", + "resolved": "https://registry.npmjs.org/libp2p-swarm/-/libp2p-swarm-0.26.12.tgz", "dependencies": { "once": { "version": "1.4.0", @@ -1803,11 +1808,6 @@ "from": "node-status-codes@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz" }, - "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.7 <2.0.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" - }, "node-webcrypto-ossl": { "version": "1.0.16", "from": "node-webcrypto-ossl@>=1.0.15 <2.0.0", diff --git a/package.json b/package.json index 64063ec..49c2cb0 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "scripts": { "build-jq": "node scripts/build-jq.js", "postinstall": "npm run build-jq", - "test": "nyc mocha --compilers js:babel-register ./test", + "test": "NODE_ENV=test nyc --require babel-register mocha --recursive './test/**/*_test.js'", "coverage": "nyc report --reporter=text-lcov | coveralls", + "coverage:lcov": "npm run test && nyc report --reporter=lcov", "build": "mkdirp ./lib && babel --copy-files src -d lib", "check": "standard && flow", "cli": "npm run build >/dev/null && node ./lib/client/cli/index.js", @@ -75,21 +76,33 @@ "devDependencies": { "babel-cli": "^6.22.0", "babel-eslint": "^7.1.0", + "babel-plugin-istanbul": "^3.1.2", "babel-plugin-transform-flow-strip-types": "^6.22.0", "babel-register": "^6.22.0", + "chai": "^3.5.0", + "chai-as-promised": "^6.0.0", "coveralls": "^2.11.15", "eslint-plugin-flowtype": "^2.30.0", + "flow-bin": "^0.38.0", "interface-connection": "^0.3.0", "mocha": "^3.2.0", "mocha-eventually": "^1.1.0", "mocha-lcov-reporter": "^1.2.0", "nyc": "^10.0.0", - "standard": "^8.6.0" + "standard": "^8.6.0", + "std-mocks": "^1.0.1" }, "standard": { "parser": "babel-eslint", "plugins": [ "flowtype" + ], + "ignore": [ + "/flow-typed/**" ] + }, + "nyc": { + "sourceMap": false, + "instrument": false } } diff --git a/src/common/util.js b/src/common/util.js index 5fd4864..3f510b0 100644 --- a/src/common/util.js +++ b/src/common/util.js @@ -48,6 +48,20 @@ function flatMap (array: Array, f: (x: T) => Array): Array { return [].concat(...array.map(x => f(x))) } +/** + * Given two `Set`s, return a new `Set` that contains all elements from both. + * @param {Set} a + * @param {Set} b + * @returns {Set} - the union of `a` and `b` + */ +function setUnion (a: Set, b: Set): Set { + const u = new Set(a) + for (const elem of b) { + u.add(elem) + } + return u +} + /** * Returns base58-encoded sha256 multihash for the given Buffer * @param buf - a `Buffer` you want to hash @@ -64,7 +78,8 @@ function b58MultihashForBuffer (buf: Buffer): string { */ function isB58Multihash (str: string): boolean { try { - Multihashing.multihash.fromB58String(str) + const h = Multihashing.multihash.fromB58String(str) + Multihashing.multihash.validate(h) return true } catch (err) { return false @@ -121,6 +136,7 @@ module.exports = { promiseHash, promiseTimeout, flatMap, + setUnion, b58MultihashForBuffer, isB58Multihash, writeln, diff --git a/src/metadata/schema.js b/src/metadata/schema.js index 0e3c5cc..13c0b3f 100644 --- a/src/metadata/schema.js +++ b/src/metadata/schema.js @@ -158,10 +158,6 @@ function validateSelfDescribingSchema (schemaObject: Object): SelfDescribingSche function loadSelfDescribingSchema (filename: string): SelfDescribingSchema { const obj = JSON.parse(fs.readFileSync(filename, 'utf-8')) - if (obj == null || typeof obj !== 'object' || Array.isArray(obj)) { - throw new Error(`Schema file "${filename}" must contain a single json object that defines a json schema.`) - } - return validateSelfDescribingSchema(obj) } diff --git a/src/metadata/schemaver.js b/src/metadata/schemaver.js index 1e39fa0..e649c48 100644 --- a/src/metadata/schemaver.js +++ b/src/metadata/schemaver.js @@ -18,7 +18,7 @@ function isSchemaVer (obj: mixed): boolean { return true } -function parseSchemaVer (version: string | SchemaVer): ?SchemaVer { +function parseSchemaVer (version: string | Object): ?SchemaVer { if (typeof (version) === 'string') { const components = version.split('-') .map(s => Number(s)) diff --git a/src/metadata/statement.js b/src/metadata/statement.js index 91eaa95..9ee356a 100644 --- a/src/metadata/statement.js +++ b/src/metadata/statement.js @@ -1,8 +1,9 @@ // @flow const { signStatement } = require('./signatures') +const { setUnion } = require('../common/util') import type { PublisherId } from '../peer/identity' -import type { StatementMsg, StatementBodyMsg } from '../protobuf/types' +import type { StatementMsg, StatementBodyMsg, SimpleStatementMsg, EnvelopeStatementMsg, CompoundStatementMsg } from '../protobuf/types' function makeStatement ( publisherId: PublisherId, @@ -41,7 +42,52 @@ function makeSimpleStatement ( return makeStatement(publisherId, namespace, {simple: body}, counter, timestampGenerator) } +function statementSource (stmt: StatementMsg): string { + if (stmt.body.envelope !== undefined) { + const envelopeStatement: EnvelopeStatementMsg = (stmt.body.envelope: any) // stupid flow typecast + if (envelopeStatement.body.length > 0) { + return statementSource(envelopeStatement.body[0]) + } + } + return stmt.publisher +} + +function statementRefs (stmt: StatementMsg): Set { + if (stmt.body.simple !== undefined) { + return simpleStmtRefs((stmt.body.simple: any)) + } + if (stmt.body.compound !== undefined) { + return compoundStmtRefs((stmt.body.compound: any)) + } + if (stmt.body.envelope !== undefined) { + return envelopeStmtRefs((stmt.body.envelope: any)) + } + throw new Error('Invalid statement type (expected simple, compound, or envelope)') +} + +function simpleStmtRefs (stmt: SimpleStatementMsg): Set { + return new Set(stmt.refs) +} + +function compoundStmtRefs (stmt: CompoundStatementMsg): Set { + let allRefs: Set = new Set() + for (const simpleStmt of stmt.body) { + allRefs = setUnion(allRefs, simpleStmtRefs(simpleStmt)) + } + return allRefs +} + +function envelopeStmtRefs (stmt: EnvelopeStatementMsg): Set { + let allRefs: Set = new Set() + for (const innerStmt of stmt.body) { + allRefs = setUnion(allRefs, statementRefs(innerStmt)) + } + return allRefs +} + module.exports = { makeStatement, - makeSimpleStatement + makeSimpleStatement, + statementSource, + statementRefs } diff --git a/src/peer/db/statement-db.js b/src/peer/db/statement-db.js index 1fb8fc0..525e1a0 100644 --- a/src/peer/db/statement-db.js +++ b/src/peer/db/statement-db.js @@ -5,8 +5,9 @@ const Knex = require('knex') const locks = require('locks') const temp = require('temp').track() const pb = require('../../protobuf') +const { statementSource, statementRefs } = require('../../metadata/statement') -import type { StatementMsg, SimpleStatementMsg, EnvelopeStatementMsg, CompoundStatementMsg } from '../../protobuf/types' +import type { StatementMsg } from '../../protobuf/types' import type { Mutex } from 'locks' const MIGRATIONS_DIR = path.join(__dirname, 'migrations') @@ -48,7 +49,6 @@ class StatementDB { if (this._migrated) return Promise.resolve(this._db) return new Promise(resolve => this._migrationLock.lock(() => { - if (this._migrated) return resolve(this._db) this._db.migrate.latest({directory: MIGRATIONS_DIR}) .then(() => { this._migrated = true @@ -119,59 +119,6 @@ function namespaceCriteria (field: string, ns: string): string { return `${field} LIKE '%${prefix}%%'` } -// TODO: move these helpers elsewhere - -function statementSource (stmt: StatementMsg): string { - if (stmt.body.envelope !== undefined) { - const envelopeStatement: EnvelopeStatementMsg = (stmt.body.envelope: any) // stupid flow typecast - if (envelopeStatement.body.length > 0) { - return statementSource(envelopeStatement.body[0]) - } - } - return stmt.publisher -} - -function statementRefs (stmt: StatementMsg): Set { - if (stmt.body.simple !== undefined) { - return simpleStmtRefs((stmt.body.simple: any)) - } - if (stmt.body.compound !== undefined) { - return compoundStmtRefs((stmt.body.compound: any)) - } - if (stmt.body.envelope !== undefined) { - return envelopeStmtRefs((stmt.body.envelope: any)) - } - throw new Error('Invalid statement type (expected simple, compound, or envelope)') -} - -function simpleStmtRefs (stmt: SimpleStatementMsg): Set { - return new Set(stmt.refs) -} - -function compoundStmtRefs (stmt: CompoundStatementMsg): Set { - let allRefs: Set = new Set() - for (const simpleStmt of stmt.body) { - allRefs = union(allRefs, simpleStmtRefs(simpleStmt)) - } - return allRefs -} - -function envelopeStmtRefs (stmt: EnvelopeStatementMsg): Set { - let allRefs: Set = new Set() - for (const innerStmt of stmt.body) { - allRefs = union(allRefs, statementRefs(innerStmt)) - } - return allRefs -} - -function union (a: Set, b: Set): Set { - const u = new Set(a) - for (const elem of b) { - u.add(elem) - } - return u -} - module.exports = { StatementDB } diff --git a/src/peer/directory.js b/src/peer/directory.js index 1654993..bb30e28 100644 --- a/src/peer/directory.js +++ b/src/peer/directory.js @@ -4,9 +4,11 @@ const PeerInfo = require('peer-info') const PeerBook = require('peer-book') const Multiaddr = require('multiaddr') const pull = require('pull-stream') +const { values } = require('lodash') const pb = require('../protobuf') const { protoStreamDecode, protoStreamEncode, peerInfoProtoUnmarshal } = require('./util') const { DEFAULT_LISTEN_ADDR, PROTOCOLS } = require('./constants') +const { isB58Multihash } = require('../common/util') import type { Connection } from 'interface-connection' import type { LookupPeerRequestMsg, LookupPeerResponseMsg } from '../protobuf/types' @@ -18,7 +20,7 @@ export type DirectoryNodeOptions = { class DirectoryNode { p2p: P2PNode - registeredPeers: PeerBook + peerBook: PeerBook constructor (options: DirectoryNodeOptions) { let { peerId, listenAddresses } = options @@ -30,7 +32,7 @@ class DirectoryNode { }) this.p2p = new P2PNode({peerInfo}) - this.registeredPeers = new PeerBook() + this.peerBook = new PeerBook() this.p2p.handle(PROTOCOLS.dir.register, this.registerHandler.bind(this)) this.p2p.handle(PROTOCOLS.dir.lookup, this.lookupHandler.bind(this)) this.p2p.handle(PROTOCOLS.dir.list, this.listHandler.bind(this)) @@ -48,40 +50,47 @@ class DirectoryNode { return this.p2p.peerInfo } - registerHandler (protocol: string, conn: Connection) { - // for some reason, conn.peerInfo is always null here, - // so we store the peerInfo from the register message - let peerForConn: ?PeerInfo = null - - const sink = () => (read: Function) => { - read(null, function next (end: ?(boolean | Error), peerInfo: ?PeerInfo) { - if (end === true) { - if (peerForConn) { - this.registeredPeers.removeByB58String(peerForConn.id.toB58String()) - } - return - } - - if (end) throw end - - if (peerInfo) { - peerForConn = peerInfo - this.registeredPeers.put(peerInfo) - } - read(null, next) - }) + get registeredPeers (): Array { + return values(this.peerBook.getAll()) + } + + getPeerInfo (peerId: PeerId | string): ?PeerInfo { + let peerId58 + if (typeof peerId === 'string' && isB58Multihash(peerId)) { + peerId58 = peerId + } else if (peerId instanceof PeerId) { + peerId58 = peerId.toB58String() + } else { + throw new Error('getPeerInfo needs a PeerId or base58-encoded multihash') } - const abortable = this.p2p.newAbortable() + try { + return this.peerBook.getByB58String(peerId58) + } catch (err) { + return null + } + } - pull( - conn, - protoStreamDecode(pb.dir.RegisterPeer), - pull.map(req => req.info), - pull.map(peerInfoProtoUnmarshal), - abortable, - sink - ) + registerHandler (protocol: string, conn: Connection) { + conn.getPeerInfo((err, pInfo) => { + if (err) { + console.error('Error getting peer info for connection:', err) + return + } + + const abortable = this.p2p.newAbortable() + pull( + conn, + protoStreamDecode(pb.dir.RegisterPeer), + pull.map(req => req.info), + pull.map(peerInfoProtoUnmarshal), + pull.through(reqInfo => { + this.peerBook.put(reqInfo) + }), + abortable, + pull.drain() + ) + }) } lookupHandler (protocol: string, conn: Connection) { @@ -103,7 +112,7 @@ class DirectoryNode { } try { - const peerInfo = this.registeredPeers.getByB58String(req.id) + const peerInfo = this.peerBook.getByB58String(req.id) const peer = { id: peerInfo.id.toB58String(), addr: peerInfo.multiaddrs.map(maddr => maddr.buffer) diff --git a/src/peer/identity.js b/src/peer/identity.js index 2fdb3bc..788bd15 100644 --- a/src/peer/identity.js +++ b/src/peer/identity.js @@ -11,6 +11,7 @@ const NODE_KEY_TYPE = 'RSA' const NODE_KEY_BITS = 2048 const PUBLISHER_KEY_TYPE = 'Ed25519' const PUBLISHER_KEY_BITS = 512 +const P2P_CODE = 420 const IPFS_CODE = 421 function generateIdentity (): Promise { @@ -71,18 +72,18 @@ function inflateMultiaddr (multiaddrString: string): PeerInfo { // total hack to patch in support for /p2p/ multiaddrs, which should // be supported upstream soon multiaddrString = multiaddrString.replace('/p2p/', '/ipfs/') - const multiaddr = Multiaddr(multiaddrString) - const ipfsIdB58String = multiaddr.stringTuples().filter((tuple) => { - if (tuple[0] === IPFS_CODE) { + const tuples = multiaddr.stringTuples().filter((tuple) => { + if (tuple[0] === IPFS_CODE || tuple[0] === P2P_CODE) { return true } - })[0][1] - if (ipfsIdB58String == null) { + }) + if (tuples.length < 1) { throw new Error('multiaddr string must contain /p2p/ or /ipfs/ protocol') } + const p2pIdB58String = tuples[0][1] - const peerId = PeerId.createFromB58String(ipfsIdB58String) + const peerId = PeerId.createFromB58String(p2pIdB58String) const peerInfo = new PeerInfo(peerId) peerInfo.multiaddr.add(multiaddr) return peerInfo diff --git a/src/peer/libp2p_node.js b/src/peer/libp2p_node.js index 10620cd..cd7c45e 100644 --- a/src/peer/libp2p_node.js +++ b/src/peer/libp2p_node.js @@ -6,19 +6,15 @@ const TCP = require('libp2p-tcp') const WS = require('libp2p-websockets') const spdy = require('libp2p-spdy') const secio = require('libp2p-secio') -const PeerId = require('peer-id') const PeerInfo = require('peer-info') const PeerBook = require('peer-book') -const multiaddr = require('multiaddr') const Ping = require('libp2p-ping') -const mafmt = require('mafmt') const Abortable = require('pull-abortable') const { promiseTimeout } = require('../common/util') import type { Connection } from 'interface-connection' const OFFLINE_ERROR_MESSAGE = 'The libp2p node is not started yet' -const IPFS_CODE = 421 const DEFAULT_DIAL_TIMEOUT = 10000 type P2PNodeOptions = { @@ -103,13 +99,16 @@ class P2PNode { stop (): Promise { if (!this.isOnline) return Promise.resolve() - this.isOnline = false - return new Promise(resolve => { + return new Promise((resolve, reject) => { // abort any ongoing pull-stream connections this.abortables.forEach(a => { a.abort() }) this.abortables.clear() - this.swarm.close(resolve) + this.swarm.close((err) => { + if (err) return reject(err) + this.isOnline = false + resolve() + }) }) } @@ -121,46 +120,6 @@ class P2PNode { } } - dialById (id: PeerId, protocol: string): Promise { - if (!this.isOnline) { - return Promise.reject(new Error(OFFLINE_ERROR_MESSAGE)) - } - // NOTE, these dialById only works if a previous dial - // was made until we have PeerRouting - // TODO support PeerRouting when it is Ready - return Promise.reject(new Error('not implemented yet')) - } - - dialByMultiaddr (maddr: multiaddr, protocol: string): Promise { - if (!this.isOnline) { - return Promise.reject(new Error(OFFLINE_ERROR_MESSAGE)) - } - - if (typeof maddr === 'string') { - maddr = multiaddr(maddr) - } - - if (!mafmt.IPFS.matches(maddr.toString())) { - return Promise.reject(new Error('multiaddr not valid')) - } - - const ipfsIdB58String = maddr.stringTuples().filter((tuple) => { - if (tuple[0] === IPFS_CODE) { - return true - } - })[0][1] - - let peer - try { - peer = this.peerBook.getByB58String(ipfsIdB58String) - } catch (err) { - peer = new PeerInfo(PeerId.createFromB58String(ipfsIdB58String)) - } - - peer.multiaddr.add(maddr) - return this.dialByPeerInfo(peer, protocol) - } - dialByPeerInfo (peer: PeerInfo, protocol: string): Promise { if (!this.isOnline) { return Promise.reject(new Error(OFFLINE_ERROR_MESSAGE)) @@ -179,39 +138,6 @@ class P2PNode { return promiseTimeout(this.dialTimeout, dialPromise) } - hangUpById (id: PeerId): Promise { - return Promise.reject(new Error('not implemented yet')) - // TODO - } - - hangUpByMultiaddr (maddr: multiaddr): Promise { - if (!this.isOnline) { - return Promise.reject(new Error(OFFLINE_ERROR_MESSAGE)) - } - - if (typeof maddr === 'string') { - maddr = multiaddr(maddr) - } - - if (!mafmt.IPFS.matches(maddr.toString())) { - return Promise.reject(new Error('multiaddr not valid')) - } - - const ipfsIdB58String = maddr.stringTuples().filter((tuple) => { - if (tuple[0] === IPFS_CODE) { - return true - } - })[0][1] - - try { - const peerInfo = this.peerBook.getByB58String(ipfsIdB58String) - return this.hangUpByPeerInfo(peerInfo) - } catch (err) { - // already disconnected - return Promise.resolve() - } - } - hangUpByPeerInfo (peer: PeerInfo): Promise { if (!this.isOnline) { return Promise.reject(new Error(OFFLINE_ERROR_MESSAGE)) diff --git a/src/peer/node.js b/src/peer/node.js index 21abd09..a093cc5 100644 --- a/src/peer/node.js +++ b/src/peer/node.js @@ -3,7 +3,6 @@ const P2PNode = require('./libp2p_node') const PeerId = require('peer-id') const PeerInfo = require('peer-info') const Multiaddr = require('multiaddr') -const Multihash = require('multihashes') const pb = require('../protobuf') const pull = require('pull-stream') const paramap = require('pull-paramap') @@ -22,7 +21,7 @@ const { objectIdsForQueryResult, expandQueryResult } = require('./util') -const { promiseHash } = require('../common/util') +const { promiseHash, isB58Multihash } = require('../common/util') const { pushStatementsToConn } = require('./push') const { mergeFromStreams } = require('./merge') const { makeSimpleStatement } = require('../metadata/statement') @@ -127,54 +126,51 @@ class MediachainNode { pullRepeatedly(req, 5000 * 60), abortable, protoStreamEncode(pb.dir.RegisterPeer), - conn, - pull.onEnd(() => { - console.log('registration connection ended') - }) + conn ) return true }) } lookup (peerId: string | PeerId): Promise { - if (peerId instanceof PeerId) { - peerId = peerId.toB58String() - } else { - // validate that string arguments are legit multihashes + return Promise.resolve().then(() => { + if (peerId instanceof PeerId) { + peerId = peerId.toB58String() + } else if (typeof peerId !== 'string') { + throw new Error(`invalid input: lookup requires a PeerId or base58-encoded multihash string`) + } + + if (!isB58Multihash(peerId)) { + throw new Error('Peer id is not a valid multihash') + } + + // If we've already got an entry for this PeerId in our PeerBook, + // because we already have a multiplex connection open to this peer, + // use the existing entry. + // + // Note that when we close the peer multiplex connection, then entry is + // automatically removed from the peer book, so we should never be returning + // stale results. try { - Multihash.fromB58String(peerId) + return this.p2p.peerBook.getByB58String(peerId) } catch (err) { - return Promise.reject(new Error(`Peer id is not a valid multihash: ${err.message}`)) } - } - - // If we've already got an entry for this PeerId in our PeerBook, - // because we already have a multiplex connection open to this peer, - // use the existing entry. - // - // Note that when we close the peer multiplex connection, then entry is - // automatically removed from the peer book, so we should never be returning - // stale results. - try { - const peerInfo = this.p2p.peerBook.getByB58String(peerId) - return Promise.resolve(peerInfo) - } catch (err) { - } - if (this.directory == null) { - // TODO: support DHT lookups - return Promise.reject(new Error('No known directory server, cannot lookup')) - } + if (this.directory == null) { + // TODO: support DHT lookups + throw new Error('No known directory server, cannot lookup') + } - return this.p2p.dialByPeerInfo(this.directory, PROTOCOLS.dir.lookup) - .then(conn => pullToPromise( - pull.values([{id: peerId}]), - protoStreamEncode(pb.dir.LookupPeerRequest), - conn, - protoStreamDecode(pb.dir.LookupPeerResponse), - pull.map(lookupResponseToPeerInfo), + return this.p2p.dialByPeerInfo(this.directory, PROTOCOLS.dir.lookup) + .then(conn => pullToPromise( + pull.values([ { id: peerId } ]), + protoStreamEncode(pb.dir.LookupPeerRequest), + conn, + protoStreamDecode(pb.dir.LookupPeerResponse), + pull.map(lookupResponseToPeerInfo), + ) ) - ) + }) } _lookupIfNeeded (peer: PeerInfo | PeerId | string): Promise { diff --git a/src/peer/repl/util.js b/src/peer/repl/util.js index 9704806..ed749eb 100644 --- a/src/peer/repl/util.js +++ b/src/peer/repl/util.js @@ -45,7 +45,7 @@ function binaryToB64 (result: Object): Object { } } - return _.cloneDeepWith(result, replacer) + return (_.cloneDeepWith(result, replacer): any) } module.exports = { diff --git a/src/peer/util.js b/src/peer/util.js index f9b57a2..9d5aea7 100644 --- a/src/peer/util.js +++ b/src/peer/util.js @@ -9,7 +9,7 @@ const {decode} = require('../metadata/serialize') const _ = require('lodash') const { flatMap } = require('../common/util') -import type { PeerInfoMsg, LookupPeerResponseMsg, ProtoCodec, QueryResultMsg, QueryResultValueMsg, SimpleValueMsg, DataResultMsg, DataObjectMsg, StatementMsg, StatementBodyMsg, SimpleStatementMsg, CompoundStatementMsg } from '../protobuf/types' // eslint-disable-line no-unused-vars +import type { PeerInfoMsg, LookupPeerResponseMsg, ProtoCodec, QueryResultMsg, QueryResultValueMsg, SimpleValueMsg, DataResultMsg, DataObjectMsg, StatementMsg, KeyValuePairMsg, SimpleStatementMsg, CompoundStatementMsg, EnvelopeStatementMsg } from '../protobuf/types' // eslint-disable-line no-unused-vars // Flow signatures for pull-streams export type PullStreamCallback = (end: ?mixed, value?: ?T) => void @@ -173,11 +173,8 @@ function valuesFromQueryResult (result: QueryResultValueMsg): Array = - _.get(result, 'compound.body') - if (compoundResultBodies != null) { - values = compoundResultBodies.map(b => b.value) - } + const compoundResultBodies: Array = _.get(result, 'compound.body') || [] + values = compoundResultBodies.map(b => b.value) } return values } @@ -193,10 +190,14 @@ function objectIdsFromStatement (stmt: StatementMsg): Array { if (stmt.body.simple !== undefined) { const simpleStmt: SimpleStatementMsg = (stmt.body.simple: any) simpleStatements = [simpleStmt] - } - if (stmt.body.compound !== undefined) { + } else if (stmt.body.compound !== undefined) { const compoundStmt: CompoundStatementMsg = (stmt.body.compound: any) simpleStatements = compoundStmt.body + } else if (stmt.body.envelope !== undefined) { + const envelopeStmt: EnvelopeStatementMsg = (stmt.body.envelope: any) + return flatMap(envelopeStmt.body, objectIdsFromStatement) + } else { + throw new Error(`Unknown statement type. Expected statement body to be simple, compound, or envelope`) } return simpleStatements.map(s => s.object) } @@ -236,7 +237,7 @@ function expandQueryResult (result: QueryResultValueMsg, dataObjects: Array { + it('isB58Multihash returns true for valid multihash', () => { + assert(util.isB58Multihash('QmNLftPEMzsadpbTsGaVP3haETYJb4GfnCgQiaFj5Red9G') === true) + }) + + it('isB58Multihash returns false for non-multihash', () => { + assert(util.isB58Multihash('QmF00123456789') === false) + assert(util.isB58Multihash('foo') === false) + }) +}) + +describe('Stream functions', () => { + before(() => { + stdMocks.use() + }) + + after(() => { + stdMocks.restore() + }) + + it('println and friends', () => { + util.println('Hello world') + util.printlnErr('Oh no!') + const output = stdMocks.flush() + assert.deepEqual(output.stdout, ['Hello world\n']) + assert.deepEqual(output.stderr, ['Oh no!\n']) + }) + + it('consumeStream', () => { + const stream = new PassThrough() + util.writeln('Line 1', stream) + util.writeln('Line 2', stream) + stream.end() + return util.consumeStream(stream) + .then(contents => { + assert.equal(contents, 'Line 1\nLine 2\n') + }) + }) + + it('consumeStream error', () => { + const stream = new PassThrough() + // need to call consumeStream before emitting the error, or it won't be caught + const promise = util.consumeStream(stream) + .catch(err => { + assert.equal(err.message, 'Something went wrong') + }) + util.writeln('Hello', stream) + stream.emit('error', new Error('Something went wrong')) + return promise + }) +}) diff --git a/test/directory_test.js b/test/directory_test.js deleted file mode 100644 index b3de1b8..0000000 --- a/test/directory_test.js +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-env mocha */ - -const assert = require('assert') -const { describe, it, before, afterEach } = require('mocha') -const eventually = require('mocha-eventually') - -const { makeNode, makeDirectory } = require('./util') - -describe('Directory Node', function () { - let dir, node, nodeIdB58 - - before(() => makeDirectory().then(_dir => { dir = _dir }) - .then(() => makeNode({dirInfo: dir.peerInfo})) - .then(_node => { - node = _node - nodeIdB58 = node.peerInfo.id.toB58String() - }) - .then(() => Promise.all([node.start(), dir.start()])) - ) - - afterEach(() => { - dir.registeredPeers.removeByB58String(nodeIdB58) - }) - - it('adds a node to its registry in response to a register message', function () { - // verify that the peer is not registered before the call - assert.throws(() => { - dir.registeredPeers.getByB58String(nodeIdB58) - }) - - return node.register() - .then(eventually(() => { - const entry = dir.registeredPeers.getByB58String(nodeIdB58) - assert(entry, 'registered successfully') - })) - }) - - it('responds to lookup requests for known peers', function () { - // just stuff the node's id into the directory manually - dir.registeredPeers.put(node.peerInfo) - - return node.lookup(nodeIdB58) - .then(peerInfo => { - assert(peerInfo != null) - assert.equal(peerInfo.id.toB58String(), nodeIdB58) - }) - }) -}) diff --git a/test/metadata/schema_test.js b/test/metadata/schema_test.js new file mode 100644 index 0000000..3be92e0 --- /dev/null +++ b/test/metadata/schema_test.js @@ -0,0 +1,175 @@ +const { assert, expect } = require('chai') +const { describe, it } = require('mocha') +const { cloneDeep } = require('lodash') +const temp = require('temp').track() +const fs = require('fs') +const SchemaVer = require('../../src/metadata/schemaver') +const { + validate, + validateSelfDescribingSchema, + isSchemaDescription, + isSelfDescribingRecord, + schemaDescriptionIsEqual, + schemaDescriptionIsCompatible, + schemaDescriptionToWKI, + loadSelfDescribingSchema +} = require('../../src/metadata/schema') + +describe('Schema validation', () => { + const fooSchemaDescription = { + vendor: 'io.mediachain.test', + name: 'foo', + version: '1-0-0', + format: 'jsonschema' + } + + const fooSchema = { + self: fooSchemaDescription, + + type: 'object', + properties: { + 'foo': { + type: 'string' + } + }, + required: ['foo'], + additionalProperties: false + } + + it('validates a correctly formatted object', () => { + const result = validate(fooSchema, {foo: 'bar'}) + assert.equal(result.success, true, 'schema should validate a correct object') + }) + + it('validates a self-describing object', () => { + const schemaRef = {'/': 'QmF001234'} + const result = validate(fooSchema, {schema: schemaRef, data: {foo: 'self-describing-bar'}}) + assert.equal(result.success, true, 'schema should validate a self-describing record') + }) + + it('does not validate an invalid object', () => { + assert.equal(false, validate(fooSchema, {foo: 1}).success, 'schema should not validate invalid object') + + const invalidDescription = { + vendor: 'io.mediachain.test', + name: 'foo', + version: '1-0-0', + format: 'not-valid' + } + assert.equal(false, validate({self: invalidDescription, type: 'string'}, 'hello world').success) + }) + + it('isSchemaDescription works', () => { + assert.equal(true, isSchemaDescription(fooSchemaDescription)) + assert.equal(false, isSchemaDescription('foo')) + assert.equal(false, isSchemaDescription({vendor: 'foo', name: 'bar', format: 'jsonschema', version: 42})) + assert.equal(false, isSchemaDescription({vendor: 'foo', name: 'bar', format: 'jsonschema', version: '1.2.3-not-a-SchemaVer'})) + }) + + it('isSelfDescribingRecord works', () => { + assert.equal(true, + isSelfDescribingRecord({schema: {'/': 'QmF001234'}, data: {foo: 'bar'}}) + ) + + assert.equal(false, + isSelfDescribingRecord('foo') + ) + + assert.equal(false, + isSelfDescribingRecord({schema: fooSchema}), + 'self-describing record requires "data" object field' + ) + }) + + it('validateSelfDescribingSchema works', () => { + const result = validateSelfDescribingSchema(fooSchema) + assert.deepEqual(result, fooSchema) + + expect(() => { + validateSelfDescribingSchema('foobar') + }).to.throw(Error) + + expect(() => { + validateSelfDescribingSchema({foo: 'bar'}) + }).to.throw(Error) + + expect(() => { + validateSelfDescribingSchema({ + self: fooSchemaDescription, + type: 42 + }) + }).to.throw(Error) + }) + + it('schemaDescriptionIsEqual works', () => { + assert.equal(true, schemaDescriptionIsEqual(fooSchemaDescription, fooSchemaDescription)) + const barSchemaDescription = cloneDeep(fooSchemaDescription) + barSchemaDescription.name = 'bar' + assert.equal(false, schemaDescriptionIsEqual(fooSchemaDescription, barSchemaDescription)) + }) + + it('schemaDescriptionIsCompatible works', () => { + const bumpedAddition = cloneDeep(fooSchemaDescription) + const bumpedRevision = cloneDeep(fooSchemaDescription) + const bumpedModel = cloneDeep(fooSchemaDescription) + bumpedAddition.version = '1-0-1' + bumpedRevision.version = '1-1-0' + bumpedModel.version = '2-0-0' + assert.equal(true, schemaDescriptionIsCompatible(fooSchemaDescription, bumpedAddition)) + assert.equal(false, schemaDescriptionIsCompatible(fooSchemaDescription, bumpedRevision)) + assert.equal(false, schemaDescriptionIsCompatible(fooSchemaDescription, bumpedModel)) + }) + + it('schemaDescriptionToWKI works', () => { + const wki = schemaDescriptionToWKI(fooSchemaDescription) + assert.equal(wki, 'schema:io.mediachain.test/foo/jsonschema/1-0-0') + }) + + it('loadSelfDescribingSchema loads a valid schema file', (done) => { + temp.open('schema-test', (err, info) => { + if (err) return done(err) + fs.write(info.fd, JSON.stringify(fooSchema)) + fs.close(info.fd, (err) => { + if (err) return done(err) + const schema = loadSelfDescribingSchema(info.path) + assert.deepEqual(schema, fooSchema) + done() + }) + }) + }) +}) + +describe('SchemaVer helpers', () => { + it('parses a valid SchemaVer string', () => { + const str = '1-0-0' + const parsed = SchemaVer.parseSchemaVer(str) + if (parsed == null) { // flow doesn't know assert will exit scope, so add explicit null check + assert(false, 'SchemaVer parsing failed') + return + } + assert.equal(parsed.model, 1, 'SchemaVer model parsed incorrectly') + assert.equal(parsed.revision, 0, 'SchemaVer revision parsed incorrectly') + assert.equal(parsed.addition, 0, 'SchemaVer addition parsed incorrectly') + }) + + it('returns a valid SchemaVer object unchanged from parse fn', () => { + const ver = {model: 1, revision: 0, addition: 0} + assert.deepEqual(ver, SchemaVer.parseSchemaVer(ver)) + }) + + it('fails to parse invalid inputs', () => { + assert.equal(null, SchemaVer.parseSchemaVer(null)) + assert.equal(null, SchemaVer.parseSchemaVer('1')) + assert.equal(null, SchemaVer.parseSchemaVer('foo')) + assert.equal(null, SchemaVer.parseSchemaVer('foo-bar-baz')) + assert.equal(null, SchemaVer.parseSchemaVer({model: null, revision: 3, addition: 'blah'})) + assert.equal(null, SchemaVer.parseSchemaVer({model: 1, revision: null, addition: 'blah'})) + assert.equal(null, SchemaVer.parseSchemaVer({model: 1, revision: 3})) + }) + + it('considers schemas compatible if model and revision are equal', () => { + assert.equal(true, SchemaVer.isCompatible('1-1-0', '1-1-3')) + assert.equal(false, SchemaVer.isCompatible('1-1-0', '1-2-0')) + assert.equal(false, SchemaVer.isCompatible(null, '1-2-3')) + }) +}) diff --git a/test/signature_test.js b/test/metadata/signature_test.js similarity index 88% rename from test/signature_test.js rename to test/metadata/signature_test.js index 8c04eaa..998db78 100644 --- a/test/signature_test.js +++ b/test/metadata/signature_test.js @@ -5,14 +5,14 @@ const assert = require('assert') const { before, describe, it } = require('mocha') const path = require('path') -const { PublisherId, PrivateSigningKey } = require('../src/peer/identity') -const { makeSimpleStatement } = require('../src/metadata/statement') -const { signStatement, verifyStatement } = require('../src/metadata/signatures') +const { PublisherId, PrivateSigningKey } = require('../../src/peer/identity') +const { makeSimpleStatement } = require('../../src/metadata/statement') +const { signStatement, verifyStatement } = require('../../src/metadata/signatures') const CONCAT_PUBLISHER_ID_PUB58 = '4XTTM4JKrrBeAK6qXmo8FoKmT5RkfjeXfZrnWjJNw9fKvPnEs' -const CONCAT_PUBLISHER_ID_PATH = path.join(__dirname, 'resources', 'publisher_ids', 'concat', +const CONCAT_PUBLISHER_ID_PATH = path.join(__dirname, '..', 'resources', 'publisher_ids', 'concat', `${CONCAT_PUBLISHER_ID_PUB58}.privateKey`) -const CONCAT_MESSAGE_FIXTURES = require('./resources/fixtures/concat-message-signature') +const CONCAT_MESSAGE_FIXTURES = require('./../resources/fixtures/concat-message-signature') describe('Signature verification', () => { let publisherId diff --git a/test/metadata/statement_test.js b/test/metadata/statement_test.js new file mode 100644 index 0000000..21ddf59 --- /dev/null +++ b/test/metadata/statement_test.js @@ -0,0 +1,56 @@ +// @flow + +const { expect } = require('chai') +const { describe, it } = require('mocha') +const { statementSource, statementRefs } = require('../../src/metadata/statement') + +const fixtures = require('../resources/fixtures/test-statements') + +describe('Statement source/refs helpers', () => { + it('statementSource returns the publisher field for simple and compound statements and empty envelope statements', () => { + const simplePublisher = fixtures.publisherIds.simple.id58 + const compoundPublisher = fixtures.publisherIds.compound.id58 + const envelopePublisher = fixtures.publisherIds.envelope.id58 + + for (let i = 0; i < fixtures.statements.simple.length; i++) { + const stmt = fixtures.statements.simple[i] + expect(statementSource(stmt)).to.be.eql(simplePublisher) + } + + for (let i = 0; i < fixtures.statements.compound.length; i++) { + const stmt = fixtures.statements.compound[i] + expect(statementSource(stmt)).to.be.eql(compoundPublisher) + } + + expect(statementSource(fixtures.statements.envelopeEmpty[0])) + .to.be.eql(envelopePublisher) + }) + + it('statementSource returns the publisher of the first contained statement for envelope statements', () => { + for (let i = 0; i < fixtures.statements.compound.length; i++) { + const stmt = fixtures.statements.envelope[i] + expect(statementSource(stmt)).to.be.eql(stmt.body.envelope.body[0].publisher) + } + }) + + it('statementRefs gets all refs from simple, compound, and envelope statements', () => { + const statementTypes = ['simple', 'compound', 'envelope', 'envelopeEmpty'] + for (const type of statementTypes) { + const statements = fixtures.statements[type] + const expectedRefs = fixtures.expectedRefs[type] + for (let i = 0; i < statements.length; i++) { + const stmt = statements[i] + const refs = Array.from(statementRefs(stmt)) + expect(refs.length).to.be.eql(expectedRefs[i].length) + for (const r of expectedRefs[i]) { + expect(refs).to.include(r) + } + } + } + + expect(() => { + const invalidStmt: any = {body: {foo: 'bar'}} + statementRefs(invalidStmt) + }).to.throw(/Invalid statement type/) + }) +}) diff --git a/test/datastore_test.js b/test/peer/datastore_test.js similarity index 93% rename from test/datastore_test.js rename to test/peer/datastore_test.js index b0aa33c..3f799c8 100644 --- a/test/datastore_test.js +++ b/test/peer/datastore_test.js @@ -2,9 +2,9 @@ const assert = require('assert') const { before, describe, it } = require('mocha') -const { makeNode } = require('./util') +const { makeNode } = require('../util') const { zip } = require('lodash') -const { encode } = require('../src/metadata/serialize') +const { encode } = require('../../src/metadata/serialize') const SEED_OBJECTS = [ {'foo': 'bar'}, diff --git a/test/peer/directory_test.js b/test/peer/directory_test.js new file mode 100644 index 0000000..5db9eba --- /dev/null +++ b/test/peer/directory_test.js @@ -0,0 +1,102 @@ +/* eslint-env mocha */ +const chai = require('chai') +chai.use(require('chai-as-promised')) +const { assert, expect } = chai +const { describe, it, before, afterEach } = require('mocha') +const eventually = require('mocha-eventually') +const PeerInfo = require('peer-info') + +const { makeNode, makeDirectory } = require('../util') + +describe('Directory Node', function () { + let dir, node, nodeIdB58 + + before(() => makeDirectory().then(_dir => { dir = _dir }) + .then(() => makeNode()) + .then(_node => { + node = _node + node.setDirectory(dir.peerInfo) + nodeIdB58 = node.peerInfo.id.toB58String() + }) + .then(() => Promise.all([node.start(), dir.start()])) + ) + + afterEach(() => { + dir.peerBook.removeByB58String(nodeIdB58) + }) + + it('adds a node to its registry in response to a register message', function () { + // verify that the peer is not registered before the call + expect(dir.getPeerInfo(nodeIdB58)).to.be.null + + return node.register() + .then(() => eventually((done) => { + const result = dir.getPeerInfo(nodeIdB58) + expect(result).to.not.be.null + expect(result).to.be.an.instanceof(PeerInfo) + expect(result.id.toB58String()).to.be.eql(nodeIdB58) + done() + })) + }) + + it('responds to lookup requests for known peers', function () { + // just stuff the node's id into the directory manually + dir.peerBook.put(node.peerInfo) + + return node.lookup(nodeIdB58) + .then(peerInfo => { + assert(peerInfo != null) + assert.equal(peerInfo.id.toB58String(), nodeIdB58) + }) + }) + + it('node throws during lookup & register if no directory is set', () => + Promise.resolve() + .then(() => { node.directory = null }) + .then(() => + Promise.all([ + expect(node.lookup(nodeIdB58)) + .to.eventually.be.rejectedWith('No known directory server'), + expect(node.register()) + .to.eventually.be.rejectedWith('No known directory server') + ])) + .then(() => { node.setDirectory(dir.peerInfo) }) + ) + + it('node throws if asked to lookup an invalid string or other bogus input', () => + Promise.all([ + expect(node.lookup('foo')) + .to.eventually.be.rejectedWith('not a valid multihash'), + expect(node.lookup(42)) + .to.eventually.be.rejectedWith('invalid input') + ]) + ) + + it('can lookup by string or PeerId', () => { + dir.peerBook.put(node.peerInfo) + return expect(node.lookup(node.peerInfo.id)) + .to.eventually.be.an.instanceof(PeerInfo) + }) + + it('internal _resolvePeer method accepts PeerInfo, multiaddr string', () => { + dir.peerBook.put(node.peerInfo) + + return Promise.all([ + expect(node._resolvePeer(node.peerInfo)) + .to.eventually.eql(node.peerInfo), + + expect(node._resolvePeer(nodeIdB58)) + .to.eventually.be.an.instanceof(PeerInfo), + + expect(node._resolvePeer('/ip4/127.0.0.1/tcp/1234/p2p/QmZvvcVA8t5qrM5DeQ8xM6PK18qzCYxseYNtaqauhSc4Na')) + .to.eventually.be.an.instanceof(PeerInfo), + + expect(node._resolvePeer('/ip4/not-a-real-multiaddr')) + .to.eventually.be.rejectedWith('not a valid multiaddr'), + + expect(node._resolvePeer('QmZvvcVA8t5qrM5DeQ8xM6PK18qzCYxseYNtaqauhSc4Na')) + .to.eventually.be.rejectedWith('Unable to locate peer') + ]) + } + ) +}) diff --git a/test/peer/identity_test.js b/test/peer/identity_test.js new file mode 100644 index 0000000..b871a0e --- /dev/null +++ b/test/peer/identity_test.js @@ -0,0 +1,50 @@ +// @flow + +const chai = require('chai') +chai.use(require('chai-as-promised')) + +const { expect } = chai +const { describe, it, before } = require('mocha') +const temp = require('temp').track() + +const Id = require('../../src/peer/identity') +const PeerId = require('peer-id') +const PeerInfo = require('peer-info') + +describe('Peer Identity tools', () => { + let peerId + before(() => Id.generateIdentity() + .then(id => { peerId = id }) + .then(() => expect(peerId).to.be.an.instanceof(PeerId)) + ) + + it('loads and saves a PeerId to disk', () => { + const tmpPath = temp.path() + Id.saveIdentity(peerId, tmpPath) + return expect( + Id.loadIdentity(tmpPath).then(loadedId => loadedId.toB58String()) + ).to.eventually.be.eql(peerId.toB58String()) + }) + + it('throws when saving a PeerId without a private key', () => { + const id = PeerId.createFromB58String('QmZvvcVA8t5qrM5DeQ8xM6PK18qzCYxseYNtaqauhSc4Nw') + expect(() => Id.saveIdentity(id), temp.path()) + .to.throw(Error) + }) + + it('loadOrGenerateIdentity', () => Promise.all([ + expect(Id.loadOrGenerateIdentity(temp.path())) + .to.eventually.be.an.instanceof(PeerId), + + expect(Id.loadOrGenerateIdentity('/this-dir-probably-doesnt-exist/foo.id')) + .to.eventually.be.rejectedWith('the containing directory does not exist') + ])) + + it('inflateMultiaddr', () => { + expect(Id.inflateMultiaddr('/ip4/127.0.0.1/tcp/1234/p2p/QmZvvcVA8t5qrM5DeQ8xM6PK18qzCYxseYNtaqauhSc4Nw')) + .to.be.an.instanceof(PeerInfo) + + expect(() => { Id.inflateMultiaddr('/ip4/127.0.0.1/tcp/1234') }) + .to.throw('must contain /p2p/ or /ipfs/ protocol') + }) +}) diff --git a/test/peer/libp2p_node_test.js b/test/peer/libp2p_node_test.js new file mode 100644 index 0000000..2485b1a --- /dev/null +++ b/test/peer/libp2p_node_test.js @@ -0,0 +1,105 @@ +// @flow + +const chai = require('chai') +chai.use(require('chai-as-promised')) +const { expect } = chai +const { before, describe, it } = require('mocha') +const { getTestNodeId } = require('../util') +const PeerInfo = require('peer-info') +const Multiaddr = require('multiaddr') +const P2PNode = require('../../src/peer/libp2p_node') + +describe('LibP2P Node base class', () => { + let id1, id2 + + before(() => Promise.all([ + getTestNodeId().then(_id1 => { id1 = _id1 }), + getTestNodeId().then(_id2 => { id2 = _id2 }) + ] + )) + + it('works with websockets', () => { + const info1 = new PeerInfo(id1) + const info2 = new PeerInfo(id2) + info1.multiaddr.add(Multiaddr('/ip4/127.0.0.1/tcp/9090/ws')) + info2.multiaddr.add(Multiaddr('/ip4/127.0.0.1/tcp/9091/ws')) + + const node1 = new P2PNode({peerInfo: info1}) + const node2 = new P2PNode({peerInfo: info2}) + return Promise.all([node1.start(), node2.start()]) + .then(() => node1.ping(node2.peerInfo)) + .then(result => { + expect(result).to.exist + }) + .then(() => Promise.all([node1.stop(), node2.stop()])) + }) + + // note: this test fails if you use raw TCP transport unless you set + // a high timeout (more than ~3 seconds), but succeeds + // if you use websockets. The problem seems to be in libp2p-swarm's + // `.close()` method, which takes a while to shut down if you've opened + // any TCP connections, even if you've since hung up to the peers you + // dialed. + it('fails if you try to dial/hangup when not online', () => { + const info1 = new PeerInfo(id1) + const info2 = new PeerInfo(id2) + info1.multiaddr.add(Multiaddr('/ip4/127.0.0.1/tcp/9090/ws')) + info2.multiaddr.add(Multiaddr('/ip4/127.0.0.1/tcp/9091/ws')) + + const node1 = new P2PNode({peerInfo: info1}) + const node2 = new P2PNode({peerInfo: info2}) + node2.handle('foo-protocol', (_proto, conn) => { }) + + return Promise.all([ + expect(node1.dialByPeerInfo(info2, 'foo-protocol')) + .to.eventually.be.rejectedWith('The libp2p node is not started yet'), + expect(node1.hangUpByPeerInfo(info2)) + .to.eventually.be.rejectedWith('The libp2p node is not started yet'), + expect( + Promise.all([node1.start(), node2.start()]) + .then(() => node1.dialByPeerInfo(info2, 'foo-protocol')) + .then(() => node1.hangUpByPeerInfo(info2)) + .then(() => Promise.all([node1.stop(), node2.stop()])) + ).to.eventually.be.fulfilled + ]) + }) + + it('start/stop are idempotent', () => { + const info = new PeerInfo(id1) + info.multiaddr.add(Multiaddr('/ip4/127.0.0.1/tcp/9094')) + + const node = new P2PNode({peerInfo: info}) + expect(node.isOnline).to.eql(false) + return node.start() + .then(() => { + expect(node.isOnline).to.eql(true) + return node.start() + }) + .then(() => node.stop()) + .then(() => { + expect(node.isOnline).to.eql(false) + node.stop() + }) + }) + + it('aborts long-lived listeners on stop', () => { + const info = new PeerInfo(id1) + info.multiaddr.add(Multiaddr('/ip4/127.0.0.1/tcp/9095')) + + const node = new P2PNode({peerInfo: info}) + const abortable = node.newAbortable() + let aborted = false + abortable((end, cb) => { + aborted = end + cb(end) + }) + expect(node.abortables.size).to.be.eql(1) + + return node.start() + .then(() => node.stop()) + .then(() => { + expect(node.abortables.size).to.be.eql(0) + expect(aborted).to.be.eql(true) + }) + }) +}) diff --git a/test/merge_test.js b/test/peer/merge_test.js similarity index 83% rename from test/merge_test.js rename to test/peer/merge_test.js index d2ad469..3961ec1 100644 --- a/test/merge_test.js +++ b/test/peer/merge_test.js @@ -5,14 +5,14 @@ const { before, describe, it } = require('mocha') const uuid = require('uuid') -const { makeNode, mockQueryHandler } = require('./util') -const { PROTOCOLS } = require('../src/peer/constants') -const { b58MultihashForBuffer } = require('../src/common/util') -const { makeSimpleStatement } = require('../src/metadata/statement') -const serialize = require('../src/metadata/serialize') -const { PublisherId } = require('../src/peer/identity') +const { makeNode, mockQueryHandler } = require('../util') +const { PROTOCOLS } = require('../../src/peer/constants') +const { b58MultihashForBuffer } = require('../../src/common/util') +const { makeSimpleStatement } = require('../../src/metadata/statement') +const serialize = require('../../src/metadata/serialize') +const { PublisherId } = require('../../src/peer/identity') -import type { QueryResultMsg, StatementMsg } from '../src/protobuf/types' +import type { QueryResultMsg, StatementMsg } from '../../src/protobuf/types' const TEST_NAMESPACE = 'scratch.merge-test' diff --git a/test/node_info_test.js b/test/peer/node_info_test.js similarity index 69% rename from test/node_info_test.js rename to test/peer/node_info_test.js index 574321f..6d5a4b8 100644 --- a/test/node_info_test.js +++ b/test/peer/node_info_test.js @@ -2,15 +2,15 @@ const assert = require('assert') const { before, describe, it } = require('mocha') -const { makeNode } = require('./util') +const { makeNode } = require('../util') describe('Node Info', function () { const infoMessage = 'tests are great!' let p1, p2 before(() => Promise.all([ - makeNode().then(_p1 => { p1 = _p1 }), - makeNode({infoMessage}).then(_p2 => { p2 = _p2 }) + makeNode({listenAddresses: ['/ip4/127.0.0.1/tcp/9090/ws']}).then(_p1 => { p1 = _p1 }), + makeNode({listenAddresses: ['/ip4/127.0.0.1/tcp/9091/ws']}).then(_p2 => { p2 = _p2; p2.setInfoMessage(infoMessage) }) ])) it('retrieves the ids and info message from another node', () => { @@ -22,5 +22,6 @@ describe('Node Info', function () { assert.equal(result.info, infoMessage, 'node info response should include correct info message') }) + .then(() => Promise.all([p1.stop(), p2.stop()])) }) }) diff --git a/test/ping_test.js b/test/peer/ping_test.js similarity index 90% rename from test/ping_test.js rename to test/peer/ping_test.js index f2b6c15..bd7a7b9 100644 --- a/test/ping_test.js +++ b/test/peer/ping_test.js @@ -2,9 +2,10 @@ const assert = require('assert') const { before, describe, it } = require('mocha') -const { getTestNodeId, makeNode } = require('./util') +const { getTestNodeId, makeNode } = require('../util') const PeerInfo = require('peer-info') const Multiaddr = require('multiaddr') +const Ping = require('libp2p-ping') describe('Ping', function () { let p1, p2, p3, invalidPeer @@ -32,7 +33,7 @@ describe('Ping', function () { }) it('falls back to mediachain ping if libp2p-ping fails', () => { - p3.p2p.ping = () => Promise.reject(new Error('I only support artisanal, hand-crafted ping protocols!')) + Ping.unmount(p3.p2p.swarm) return Promise.all([p1.start(), p3.start()]) .then(() => p2.ping(p3.peerInfo)) .then(result => { diff --git a/test/peer/push_test.js b/test/peer/push_test.js new file mode 100644 index 0000000..a514ced --- /dev/null +++ b/test/peer/push_test.js @@ -0,0 +1,83 @@ +// @flow + +const chai = require('chai') +chai.use(require('chai-as-promised')) + +const { expect } = chai +const { before, describe, it } = require('mocha') + +const uuid = require('uuid') + +const { makeNode, mockPushHandler } = require('../util') +const { PROTOCOLS } = require('../../src/peer/constants') +const { b58MultihashForBuffer } = require('../../src/common/util') +const { makeSimpleStatement } = require('../../src/metadata/statement') +const serialize = require('../../src/metadata/serialize') +const { PublisherId } = require('../../src/peer/identity') + +import type { StatementMsg } from '../../src/protobuf/types' + +const TEST_NAMESPACE = 'scratch.push-test' + +const SEED_OBJECT_BUFFERS = [ + {id: uuid.v4(), foo: 'bar'}, + {id: uuid.v4(), test: 'yep'} +].map(obj => serialize.encode(obj)) + +function makeSeedStatements (publisherId: PublisherId, seedObjectBuffers: Array): Promise> { + return Promise.all( + seedObjectBuffers.map((buf, idx) => { + const object = b58MultihashForBuffer(buf) + return makeSimpleStatement(publisherId, TEST_NAMESPACE, {object, refs: [`merge-test:${idx.toString()}`]}, idx) + }) + ) +} + +describe('Push', () => { + let alephNode + let mockDestination + let publisherId + let seedStatements + + before(() => + makeNode() + .then(node => { alephNode = node }) + .then(() => makeNode()) + .then(dest => { mockDestination = dest }) + .then(() => PublisherId.generate()) + .then(pubId => { publisherId = pubId }) + .then(() => makeSeedStatements(publisherId, SEED_OBJECT_BUFFERS)) + .then(statements => { seedStatements = statements }) + ) + + it('handles rejection', () => + alephNode.start() + .then(() => { + mockDestination.p2p.handle(PROTOCOLS.node.push, mockPushHandler({reject: {error: 'not authorized'}}, 0)) + return mockDestination.start() + }) + .then(() => + expect(alephNode.pushStatements(mockDestination.peerInfo, seedStatements)) + .to.eventually.be.rejectedWith('not authorized') + ) + ) + + it('sends statements if authorized', () => { + const result = { + objects: seedStatements.length, + statements: seedStatements.length, + error: '' + } + + return alephNode.start() + .then(() => Promise.all(seedStatements.map(s => alephNode.db.put(s)))) + .then(() => { + mockDestination.p2p.handle(PROTOCOLS.node.push, mockPushHandler({accept: {}}, seedStatements.length, result)) + return mockDestination.start() + }) + .then(() => + expect(alephNode.pushStatementsById(mockDestination.peerInfo, seedStatements.map(s => s.id))) + .to.eventually.be.deep.eql(result) + ) + }) +}) diff --git a/test/remote_query_test.js b/test/peer/remote_query_test.js similarity index 61% rename from test/remote_query_test.js rename to test/peer/remote_query_test.js index e65e5f7..a92dbf7 100644 --- a/test/remote_query_test.js +++ b/test/peer/remote_query_test.js @@ -3,11 +3,12 @@ const assert = require('assert') const { before, describe, it } = require('mocha') -const { PROTOCOLS } = require('../src/peer/constants') +const { PROTOCOLS } = require('../../src/peer/constants') const pull = require('pull-stream') -const { makeNode, mockQueryHandler } = require('./util') +const { PublisherId } = require('../../src/peer/identity') +const { makeNode, mockQueryHandler } = require('../util') -import type Node from '../src/peer/node' +import type Node from '../../src/peer/node' function startNodes (...nodes: Array): Promise<*> { return Promise.all(nodes.map(n => n.start())) @@ -43,18 +44,10 @@ describe('Remote Query', () => { remote.p2p.handle(PROTOCOLS.node.query, mockQueryHandler(responses)) }) .then(() => startNodes(local, remote)) // start both peers - .then(() => local.remoteQueryStream(remote.p2p.peerInfo, 'SELECT * FROM foo.bar')) - .then(resultStream => - new Promise(resolve => { - pull( - resultStream, - pull.collect((err, results) => { - assert(err == null, 'query should not return an error') - assert.deepEqual(results, expected, 'query should return all expected results') - resolve() - })) - }) - ) + .then(() => local.remoteQuery(remote.p2p.peerInfo, 'SELECT * FROM foo.bar')) + .then(results => { + assert.deepEqual(results, expected, 'query should return all expected results') + }) }) it('ends the stream with an error when it gets an error response', function () { @@ -90,3 +83,35 @@ describe('Remote Query', () => { })) }) }) + +describe('Remote Query with inline data', () => { + let local, remote + + const seedObject = {foo: 'bar'} + + before(() => + makeNode().then(peer => { local = peer }) + .then(() => PublisherId.generate()) + .then(publisherId => makeNode({publisherId})) + .then(peer => { + remote = peer + }) + .then(() => + remote.ingestSimpleStatement('scratch.test.queryWithData', seedObject, {refs: ['test-1']}) + ) + .then(stmtId => remote.db.get(stmtId)) + .then(stmt => { + remote.p2p.handle(PROTOCOLS.node.query, mockQueryHandler([{value: {simple: {stmt}}}])) + }) + ) + + it('returns query results with data objects inline', () => + Promise.all([local.start(), remote.start()]) + .then(() => local.remoteQueryWithData(remote.peerInfo, 'SELECT * FROM scratch.test.queryWithData')) + .then(result => { + assert(result != null) + assert(Array.isArray(result)) + assert.deepEqual(result[0].simple.stmt.body.simple.object.data, seedObject) + }) + ) +}) diff --git a/test/statement_db_test.js b/test/peer/statement_db_test.js similarity index 70% rename from test/statement_db_test.js rename to test/peer/statement_db_test.js index 229e9e8..608ac01 100644 --- a/test/statement_db_test.js +++ b/test/peer/statement_db_test.js @@ -1,11 +1,13 @@ // @flow -const assert = require('assert') +const { assert, expect } = require('chai') const { before, describe, it } = require('mocha') +const path = require('path') +const { StatementDB } = require('../../src/peer/db/statement-db') -const { StatementDB } = require('../src/peer/db/statement-db') +import type { StatementMsg } from '../../src/protobuf/types' -import type { StatementMsg } from '../src/protobuf/types' +const MIGRATIONS_DIR = path.join(__dirname, '..', '..', 'src', 'peer', 'db', 'migrations') const SEED_STATEMENTS: Array = [ { @@ -41,7 +43,7 @@ const SEED_STATEMENTS: Array = [ ] describe('Statement DB', () => { - const db = new StatementDB() + const db = new StatementDB(null) before(() => db.sqlDB() .then(() => Promise.all(SEED_STATEMENTS.map(stmt => db.put(stmt))))) @@ -84,3 +86,25 @@ describe('Statement DB', () => { }) ])) }) + +describe('StatementDB migrations', () => { + it('migrates and rolls back', () => { + const db = new StatementDB() + let sqlDB + return db.sqlDB() + .then(_sqlDB => { sqlDB = _sqlDB }) + .then(() => sqlDB.select().table('Statement')) + .then(result => { + expect(result).to.exist + }) + .then(() => sqlDB.migrate.rollback({ + directory: MIGRATIONS_DIR + })) + .then(() => + sqlDB.select().table('Statement') + .catch(err => { + expect(err).to.be.an.instanceof(Error) + }) + ) + }) +}) diff --git a/test/peer/util_test.js b/test/peer/util_test.js new file mode 100644 index 0000000..269cecc --- /dev/null +++ b/test/peer/util_test.js @@ -0,0 +1,69 @@ +const { expect } = require('chai') +const { describe, it } = require('mocha') + +const PeerInfo = require('peer-info') +const util = require('../../src/peer/util') +const serialize = require('../../src/metadata/serialize') + +const statementFixtures = require('../resources/fixtures/test-statements') + +describe('P2P utils', () => { + it('lookupResponseToPeerInfo converts from directory lookup to PeerInfo object', () => { + expect(util.lookupResponseToPeerInfo({})).to.be.null + + const noAddrs = {peer: {id: 'QmZvvcVA8t5qrM5DeQ8xM6PK18qzCYxseYNtaqauhSc4Nw'}} + const noAddrsResult = util.lookupResponseToPeerInfo((noAddrs)) + expect(noAddrsResult).to.be.an.instanceof(PeerInfo) + expect(noAddrsResult.multiaddrs).to.be.empty + }) + + it('statementsFromQueryResult', () => { + const simpleStatements = statementFixtures.statements.simple + const stmt = simpleStatements[0] + const simpleStatementValue = { stmt } + const simpleStatementResult = { simple: simpleStatementValue } + const compoundQueryResult = { compound: { body: [ { key: '1', value: simpleStatementValue } ] } } + expect(util.statementsFromQueryResult(simpleStatementResult)).to.deep.eql([stmt]) + expect(util.statementsFromQueryResult(compoundQueryResult)).to.deep.eql([stmt]) + expect(util.statementsFromQueryResult({compound: {}})).to.deep.eql([]) + expect(util.statementsFromQueryResult({simple: {intValue: 0}})).to.deep.eql([]) + }) + + it('objectIdsFromStatement', () => { + const statementTypes = ['simple', 'compound', 'envelope', 'envelopeEmpty'] + for (const type of statementTypes) { + const statements = statementFixtures.statements[type] + const expectedIds = statementFixtures.objectIds[type] + for (let i = 0; i < statements.length; i++) { + const stmt = statements[i] + const ids = util.objectIdsFromStatement(stmt) + expect(ids.length).to.be.eql(expectedIds[i].length) + for (let id of expectedIds[i]) { + expect(ids).to.include(id) + } + } + } + + expect(() => { + util.objectIdsFromStatement({body: {foo: 'bar'}}) + }).to.throw('Unknown statement type') + }) + + it('objectIdsFromQueryResult', () => { + const stmt = statementFixtures.statements.simple[0] + const simpleStatementResult = { simple: { stmt } } + const expectedIds = statementFixtures.objectIds.simple[0] + expect(util.objectIdsForQueryResult(simpleStatementResult)).to.deep.eql(expectedIds) + }) + + it('expandQueryResult', () => { + const stmt = statementFixtures.statements.simple[0] + const simpleStatementResult = { simple: { stmt } } + const dataObj = {key: 'foo', data: serialize.encode({foo: 'bar'})} + + const expanded = util.expandQueryResult(simpleStatementResult, [dataObj]) + expect(expanded).to.exist + expect(expanded).to.be.an('object') + expect(expanded.simple.stmt.body.simple.object.data).to.deep.eql({foo: 'bar'}) + }) +}) diff --git a/test/resources/fixtures/test-statements.js b/test/resources/fixtures/test-statements.js new file mode 100644 index 0000000..eaeb0c7 --- /dev/null +++ b/test/resources/fixtures/test-statements.js @@ -0,0 +1,86 @@ +const SIMPLE_STMT_1 = { + id: '4XTTM81cjwraTF9FW33DyCz2PbdQ9peqCXWTz9rBhU3bwm4TE:1485446977027:0', + publisher: '4XTTM81cjwraTF9FW33DyCz2PbdQ9peqCXWTz9rBhU3bwm4TE', + namespace: 'scratch.test', + timestamp: 1485446977027, + body: { simple: { object: 'foo', refs: [ 'simple-1' ], deps: [], tags: [] } }, + signature: Buffer.from('rYo/HI3zfEO7JtlFzIF9r7bJbqV7p3SjLDoedYQgI3X8zutAUNwayhXJURHVB0Yz/CShfLn+7Mc94iLCCBtJDw==', 'base64') +} + +const SIMPLE_STMT_2 = { + id: '4XTTM81cjwraTF9FW33DyCz2PbdQ9peqCXWTz9rBhU3bwm4TE:1485447081587:1', + publisher: '4XTTM81cjwraTF9FW33DyCz2PbdQ9peqCXWTz9rBhU3bwm4TE', + namespace: 'scratch.test', + timestamp: 1485447081587, + body: { simple: { object: 'foo', refs: [ 'simple-2' ], deps: [], tags: [] } }, + signature: Buffer.from('6uuCL0zQSuSBuN2a7FeJGp75P5FBJUAwuBzIjC7nZrgVmHFqkiaVPUhO2lGikMh+DaU/Okgrf+thjfFDEFyxCQ==', 'base64') +} + +const COMPOUND_STMT = { + id: '4XTTMDah7ai6vqk6yzAhDtW9ATaEmTDJPNK3kcPT4bLKRuotG:1485447651564:0', + publisher: '4XTTMDah7ai6vqk6yzAhDtW9ATaEmTDJPNK3kcPT4bLKRuotG', + namespace: 'scratch.test.compound-stmt', + timestamp: 1485447651564, + body: { + compound: { + body: [ + { object: 'foo', refs: [ 'compound-1' ], deps: [], tags: [] }, + { object: 'foo', refs: [ 'compound-2' ], deps: [], tags: [] } + ] + } + }, + signature: Buffer.from('VP6DTYwD6XaX4RmcncCXJOb7WssNQDfxvmsx+5K+qIoCoggWuhsCtqCXGCfwwmTxigxtMieK5CY1DXeL/rQLAw==', 'base64') +} + +const ENVELOPE_EMPTY = { + id: '4XTTM2hkDuu73NXYakvw2uD6QfNAxB5emTd1P11uYt7YkmcXv:1485448028036:0', + publisher: '4XTTM2hkDuu73NXYakvw2uD6QfNAxB5emTd1P11uYt7YkmcXv', + namespace: 'scratch.test.envelope-stmt', + timestamp: 1485448028036, + body: { envelope: { body: [] } }, + signature: Buffer.from('2Gk0n6XgzeaY3SCxjXXqwYuZfVqPnulEwyTW3eYeCI1NTq1g8D1I57602ItxEgBfg1CYXr9TBCdkMWnYXWIbDw==', 'base64') +} + +const ENVELOPE_STMT = { + id: '4XTTM2hkDuu73NXYakvw2uD6QfNAxB5emTd1P11uYt7YkmcXv:1485448141505:1', + publisher: '4XTTM2hkDuu73NXYakvw2uD6QfNAxB5emTd1P11uYt7YkmcXv', + namespace: 'scratch.test.envelope-stmt', + timestamp: 1485448141505, + body: { envelope: { body: [ SIMPLE_STMT_1, SIMPLE_STMT_2 ] } }, + signature: Buffer.from('/94sZ6ETWTNCaHO78h+ifrqrViN4v95//Qx/+j3OmvLqJ3eLrK5damMqbQw06kstVC5II58udNR7zCJFqsYbDw==', 'base64') +} + +module.exports = { + publisherIds: { + simple: { + id58: '4XTTM81cjwraTF9FW33DyCz2PbdQ9peqCXWTz9rBhU3bwm4TE', + privateKey58: 'K3TgUjU7LqKoaGET8vHzwVY33PEk95fU4goQHfBvEWR1bVCLxd2V1RQnLqaTVHXQ2h14EXGxqwtvSir8vBxLmQ43KEjWbbJTgKAKexSr3apeHtQL2scWeaAHShHwVLffX4BzMu9E' + }, + compound: { + id58: '4XTTMDah7ai6vqk6yzAhDtW9ATaEmTDJPNK3kcPT4bLKRuotG', + privateKey58: 'K3TgUsAVZtKR71WG9jUrAt5GzXEVoDyjgDs2eiCaNuTcS8SzFjoBxvrKZ9c5btp4qByMLFrX7XXK9H5G4MEo2Z9hptfWAYK9iBnrWrdVPLLX7SbJz2CPZidrJxMTbF5xM7zX3edA' + }, + envelope: { + id58: '4XTTM2hkDuu73NXYakvw2uD6QfNAxB5emTd1P11uYt7YkmcXv', + privateKey58: 'K3TgTjd6PriGenibzwXWkZcxSYWLPKqMejgFdP9BUjErgwSDPkgPL7AACkvzkXExcvEepo1ZbfoHbo79HJzfkchVFP42UzZ3pYdD2wsq1JHL1UrJ28rza43CqfVW29yYjC81NgJY' + } + }, + statements: { + simple: [ SIMPLE_STMT_1, SIMPLE_STMT_2 ], + compound: [ COMPOUND_STMT ], + envelope: [ ENVELOPE_STMT ], + envelopeEmpty: [ ENVELOPE_EMPTY ] + }, + expectedRefs: { + simple: [ ['simple-1'], ['simple-2'] ], + compound: [ ['compound-1', 'compound-2'] ], + envelope: [ ['simple-1', 'simple-2'] ], + envelopeEmpty: [ [] ] + }, + objectIds: { + simple: [ ['foo'], ['foo'] ], + compound: [ ['foo', 'foo'] ], + envelope: [ ['foo', 'foo'] ], + envelopeEmpty: [ [] ] + } +} diff --git a/test/resources/test_node_ids.json b/test/resources/test_node_ids.json index 47a83cc..75c52c4 100644 --- a/test/resources/test_node_ids.json +++ b/test/resources/test_node_ids.json @@ -1 +1,202 @@ -[{"id":"QmeKrNsVh4HjQ6FDbfKvpVpXFcASpP8VYLDQ4utH8CHNcb","privKey":"CAASqQkwggSlAgEAAoIBAQCwgtRHpXpLm3P58/y8u2OZY9wOEMbYmxUO9SwUk/GXMI4ugSa0wD5GH5LK1oXD4iDG/Qv6tTmWoPsyFzPiqwG3nFlWwNfPZaFgxPqH+YXIfYtcDtFYYBBbpZpkO3IMXPCjlPXLY412nkYsr3Jhcoj7seQwNHGkNtsagmtTyuW7uQIXBpFRyU+/K0iNAJoRjH/CX2gKzP/yHK4pXq/hzOyZMWUBlpd61M7Fm3qnNqTMqXiZ5AbpPTjpBnkTGs65O6AQf2fZUzGrnZSqCE4CZ+8yW3EF36HHRMPwtfLIXjc+/PIoTB1aFFKx/YlLq9KgDh2dLoxYcdx+cSFze4xQbsoJAgMBAAECggEBAKLbIucz64Ek3b2p+u9bacrV0uv7WKvzcluJo/EcixP2zchkiwTMNujWKscRZPvVgG54fPwFByxOPVX7CHlDVGNHUzpy1OtbFqlFNO4OL/xKsTDvzwFeKNmWErwWCOUVP0V+o8XThWsDHzYNblgwp+iqOiKr0Io1vrRXzpTSKGKpCNuIST77B2yDrIyWDZwQQpa+qB0VhrUSIHfq5GazjDn9WfWZKsDErQBUdSOzQuRTddGeGIphlGycwI3J1IaLXUMHwEuNw9Le+HAE6ReksHK3k9bhkLzw0R9N93wXnO3aGRfcsuUKe6CKhU9rBLD47RJh3kElR/rlKrTqD6yz+gECgYEA1bUIY4DKaK04QoSAVxluN+HqcP2imAtU25e6Bm5fxmUmOLBfjKgtRM8DoLEEZWr5x9b/ZaFUi3nTBW6hPVkz78WBWVnNuUXU+mm4sho6eWmEc9ixoPNGM59EBnwkpBLs0mVoX1aq4+XA9Man4pMILFAYAIiyiodECGpkMOmnhkkCgYEA03FX/nG5KK+nndpksNm82g3hHr9x1Fqg3agl6nYW7yZonIT0ZvHM0Vf5oZw8k0l+TKI1jF5Z7zROFu0TxFJxy3jCQQjhNppc68NRSyaWcoXZgXPkyo3Rd0ifH4EYWT6V2skpHwNXrqehnk3QmFDOfTydbONujZaTK4uDBJTqJcECgYEAlGYKtjXn2Dp498/U4ya4n7uUNfQapSQxP++jcPXSMg1pNjJbl2vtS3vJVBu4JsjbPIcygJs//z6QZ5RECTrZUuW+hZJTxhUU6PLjG2IVOtwn6t4Nsutb01sEPNCWDQ1LQGrEyHeBYZHYpOESEcbyBPt1P2GtQX7eok1m14RZHWkCgYBQALnXp3X0pAna21Bv7eLb6pZ8SNmsDKKDW+UEwqvIzxKT7mZ/MQxbRVtJv9DXtd4Y5uM1+lky/epyGD/7b2ppH4b4joCQZXW1KxDuS2pvaJ+0sIIn9eMErDCw/eqVke2ne6eYeW5KwliPikfb6e9HSZETKzagUhHbTw1CxYmWwQKBgQChCEwU4tCVKmDNvj2nE0pZH5UuqjsOTvxMCCTZlmjPmvMTQaH3yrgb47EdSOkiuAcNmk6YwJTjty0OXm3kEp/GNhVgO5bwKRpuTkvYCVGgpAAqn/AzTCDFWuRgnhNSCtj+AjCIWvZzKdXBGQCIj4yvUjhF5DGj3FIsT8/M+Bw7eg==","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwgtRHpXpLm3P58/y8u2OZY9wOEMbYmxUO9SwUk/GXMI4ugSa0wD5GH5LK1oXD4iDG/Qv6tTmWoPsyFzPiqwG3nFlWwNfPZaFgxPqH+YXIfYtcDtFYYBBbpZpkO3IMXPCjlPXLY412nkYsr3Jhcoj7seQwNHGkNtsagmtTyuW7uQIXBpFRyU+/K0iNAJoRjH/CX2gKzP/yHK4pXq/hzOyZMWUBlpd61M7Fm3qnNqTMqXiZ5AbpPTjpBnkTGs65O6AQf2fZUzGrnZSqCE4CZ+8yW3EF36HHRMPwtfLIXjc+/PIoTB1aFFKx/YlLq9KgDh2dLoxYcdx+cSFze4xQbsoJAgMBAAE="},{"id":"QmQmLrVTa28T6HpVZCLnhpuSKhvqxWzMkxioBC4JA9xreL","privKey":"CAASqQkwggSlAgEAAoIBAQD9BZNYQBwJHwRX5kXxdHilcQ65mSWpNbjl5NBwB+A6tMzrupsZqci6W6kPj0tq1Ez/OjDD8RLRLTFCLmuYokIrP+j+2dJMa3muXrTZdIQYK7l2SMx4854KshJyl+gF8zJqgp1fBVNwmGNoa7hEhp0idnf14yo0c0mQy2XDCIRO8f/vQ/ol6/4RiU7nCh5L3tXPDTeZvSkzH6FBGcnBc6GWQWYDbNxffpGgx3wx9lpVceydnpMmdSBSog4nmNzHRK9q2EJuTl+kY5ljrEKbrWcc2ZzQ3tUD3j/X1PXlK+xVyujZgG45XiG2tPx8cn33aIyW1FCcVpPN91xzRZjtuSFlAgMBAAECggEBAOtJFwaWvztgI3rVwK7Svyohoy7GEjZypZjqmUXEnSgaXwDKS1anAukBOYoll10lROCGLTjYYcYwf0Dqx9Vs9ZkgWjs7NQspWkLjAMvCRN/xMQqkkbfb21hY1TzIs9NsnrJa+ZqV6kym+LHukbDveQnGHrGB6ApUNtG1wtCFh/ubxyrSk050AVVvN2t5otNU0jXvxed3Yj0YKe7uUFTnrYPqjNILLE6hk5AfwHIhLlyI+YiL1NQ5n9Oz1ZGFYD1YspD60C47RPFMiIGr0zv4A9ld6D1YyUUssRfVUW9QrnVuIzJK3IpK4CYzbZjy05JoVUFfben68J5gjNF0j7UezgECgYEA/32sc2NaDBVy/0FsN/PqQdBEGqSKyL2aHxzXaSbRGzFePJfdaRqMCdCjzRRu6l5NsiItN8j0HALG5wzyt5rP5hEnPlS/0+NVrFc4vUVvJ543sVyMoWKs3lJLvJjqdOqZ0nay/ZN7/UaYPCcBG1Cu2uXoMYzu82oac8IXPAHBOIECgYEA/Yakdavz15tk9MvxeX4LTBB8uIgtGzFONxNgN7fd+72oymybOSRAAriUsiLYLQml7WTZpiLzAnMeIpdTDaBBdHpHSyDdMrXrshqz4R0M4OtxX+Dg2DpbZh1r2PqE4SClApLqnT3MSeldoZIi4ryEyuaB0qCwYABRyob5wfHGluUCgYA2u92t+qLX4+B2/rUIZ6DP6KYubwGsb7IM6Ejdpgs2ICVoJcCxO5a0o5Xz4WTBoOJUPy4gvVROi+nApe15mBjh2NrnZ5CQ/CrmeOkW40Ek46havtEB4fnWxxqilL/lvDyn8fX2jRoK8Iy8lj6Oc4KCMq7DrsWiMCDqm7IOkPGsAQKBgQCGT+6Q6KgmcYzqo6EqXDxf79wjZwF+hoCJbRpsalyQIcpqc19Ixoui5WbheNROB+5/9g3YlmtnwNUWPlUhwuTqcF2Uz7RgSUC0+rCRcnKANju6L8juxPvm/W9FZHNBUaoZ0vu4kiTIg3m3P1R8CqPPWo8spzTJp2GtihSHSy+KgQKBgQCISKYS5Pj4McVRj9vEJYi1YhEeKVlPCv24gP9KENhFlJ6VsKeQr4iJCIMr/eXAZwJ1gfIN0VGgIHQug+7wt3PlYXP3ML/Ti+D4kFPcLPwzfJ0baLqhTTlTWYXYwcjtyS1aAaG2PF0YCRgtxxur/Rrn4Qvq6Z4L9yo7UdUM4X/yGQ==","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD9BZNYQBwJHwRX5kXxdHilcQ65mSWpNbjl5NBwB+A6tMzrupsZqci6W6kPj0tq1Ez/OjDD8RLRLTFCLmuYokIrP+j+2dJMa3muXrTZdIQYK7l2SMx4854KshJyl+gF8zJqgp1fBVNwmGNoa7hEhp0idnf14yo0c0mQy2XDCIRO8f/vQ/ol6/4RiU7nCh5L3tXPDTeZvSkzH6FBGcnBc6GWQWYDbNxffpGgx3wx9lpVceydnpMmdSBSog4nmNzHRK9q2EJuTl+kY5ljrEKbrWcc2ZzQ3tUD3j/X1PXlK+xVyujZgG45XiG2tPx8cn33aIyW1FCcVpPN91xzRZjtuSFlAgMBAAE="},{"id":"QmTj8FRrtWcyw83jkoVAQtzZG9wm6P1mNaA2PG14H5QugM","privKey":"CAASpwkwggSjAgEAAoIBAQDEaSnbCRWJ3P2Sg5zLeyGswWDfF2DtsneFAbDcoPC6NB4RTPaCTBWCaKiqM5+eOQg/VUv/1bfuvl4N6LGnSL2Epg0zQhih3vvI0Ent9+Pay7COkQ/aVtPpjAHCf1X2wdqz2+1Y3eT1mi0VPx2bEqr3Mmgn6KMkxHVGFORR3IbH7dxubT0HNnwFWhq1xuteSJtyGqDcMJfdezNft+XxMTiHq9gyAX1LfuEaEii/9QTeSK1T+Dv+wpPk+m1aXajkxi6ZeRG2gL7J3+iX2hZE6nv9suHMQFcQZLEnWiqFm4hzgSVzJY8XWBYpg7OhjoaIBjS7kjcM3MlUnGHqyUjlLdzZAgMBAAECggEBAJCWbjaNkGqF4bGa67ErutWHIf35IuS4zZ1/aihw3eXlegD4bDvtMKRo0UlCaY27pGOjMihqt1WslGae1zEzrjhZWTxTuvhmw2nIU7Q4GWe3LQk2prnv016JL75zqN/XQ13GWApkhyl6Txfl97Bx5KPHWr+OVZM6gK6PsKjGs/22xIn69VBXe6S7rMteSQD6clV+JykQcVfyfCPjubfYsLLrGbG6Ma1pAfIfwHXtAoc15+hzsmrNCU8FiyB9YDZEJpVjY/y3WtW2c4Kv1a4ivpZremkNg8J5Bm8YWijqKYNp5YPbNpWuFgUb3Ak7/HyU/bww1Hm+bRx2W6LQE0Cq7kUCgYEA9BuinSYMeALZjxp9hv53feCNUisYDq03fAPCVUR3VhMNul5cnyVA87m3b6FTRUg/nReNQu5fOswWqyaNdFrF/KS68W+SpsOj7+XPNyvFC6+r7E3NVbKf6DvB8kgyTNNAqHZAymnBbEwehSfYl4VkLzKA9YgEz7M0hs/QinFmMocCgYEAzfqtuEat1sHGWf+MP7TqhSmsnF0OvQdNGj/ywUF1RvaZwGPQvjU2Zq6N30zBrdHQNNIZQIakB9eiW7j9/+9PoWfXNkgRr2wBsRY/yJAP9eDNkUWhXJoczNM2qbZt0g92ojJUOUR+pMvb1XURTa0SiJ1fFZ9hd1NVFPQDw8KHbZ8CgYALdRCY16RWLawQb2nc4Cyzl8qKRMOITNEB3RtN7Ty8qYBHcw5d2d3GkrztXGEQK2fw854Jui52EAYwWkrhXzFTvleX5Wi4sw4KwHqNWFyTQ9eJ68zrbMrcl5Fu0byqpm+ueH5qAbS2ZIw6K7iaxAQDwxcSbyPqq7qXaUGcfd4SGQKBgDGSDpS2jceNQssN8Raz12zMprnKu21KgIe8tBNGSY8WcdIXM3lTHRCBGauPsVy/jbfoiCM7Q+aPQoeIcbhSQ3u4ndZ2aasp9w5DKH6ADRdDTrWTaE382MmCWzlkqfFPv2Exc4uErGXEmdc5ITzsVfWCygtOHAq3h1ZPk0hdXHJJAoGAbTL5VIqTtPBY2Gv0aGS5fVllq7FZ3eQDjBJZD17PJ26A7Otc/m0qU1JRoMsH8jj8P/rCQj0O9D/+FwrIW8JVbSRxuT2unXyKsEWFOxg7ehs34zrJTEScz4vUfraBgJYBoxXiJDlJs8r3Bu9n4YOpoUSaRhzdvbro3STIGe6SOfM=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEaSnbCRWJ3P2Sg5zLeyGswWDfF2DtsneFAbDcoPC6NB4RTPaCTBWCaKiqM5+eOQg/VUv/1bfuvl4N6LGnSL2Epg0zQhih3vvI0Ent9+Pay7COkQ/aVtPpjAHCf1X2wdqz2+1Y3eT1mi0VPx2bEqr3Mmgn6KMkxHVGFORR3IbH7dxubT0HNnwFWhq1xuteSJtyGqDcMJfdezNft+XxMTiHq9gyAX1LfuEaEii/9QTeSK1T+Dv+wpPk+m1aXajkxi6ZeRG2gL7J3+iX2hZE6nv9suHMQFcQZLEnWiqFm4hzgSVzJY8XWBYpg7OhjoaIBjS7kjcM3MlUnGHqyUjlLdzZAgMBAAE="},{"id":"QmefXHQVduBtXgZgNHp9g7P42bh4LyNV5egghVCm9dPsdC","privKey":"CAASpwkwggSjAgEAAoIBAQC0GU6EP5luaEJUiK/BWs2oFI3cQ8f5gpL97ClmjuCIJ2bACnsupgxI5HvnfbVVll+L+/BLdA6WAFzwmQz5GhDQyN6mauvZvYM35uKNnionny8kafqL39nXFQZ6V7dNLJyE6BjRwGcZR6pgYpoz975QJPY3BCQuV69SeNyOvVxUl3+AxTH/xuk+LAxHT2QuAOn0fMd5RDJ8YbbUXQX1ZTMcHn+6wuTh1W+NU8DbgckIi6lU5W7WhoPTx51l7G14/sO6h/YwVKk9MiwiX4sdZw7vtzxfSNPYsa5V/MA6A4hRtkDBc1kZEwOin/u6qJ4Bd/VonwVrQfBKQ7KMbaJnaHAhAgMBAAECggEBAJkJ8Ch9CtpBCixfQkph6ORtIucLSgECLfpMFhoqSy4nnS6POYG4ZDQExC1AKzkhYOvJGLZlVoeso01oIRgESEnCh0CPFv+eBMUaE2YYAuZMZHji/Z6ih7A3E5wesYqI4DAoMf77KiDXPzZMU5DZcwUTg43YnQ4sQLUqMvGwg6b08NJNZsobgvb3nNGQQmZP8e+m14vF+kdoxajNzl1bUdRAesDOcB/WJObxcjwFdimFteLx6aexKz7EALBw1AgBWd6zlHX9wvxRr+EL282HI47lhuMQ7rMyVE0nxKazclm4SNjQboq56KNWSr+IFkfgZWwGdTC+M+UESOlM9/zHEOECgYEA2gvqPgMa0nFsxGhu0uYN5ZCAgwDgh7hI51S3W2vyPnMKnS7pWRs42BTgwYgr/16b/olh3QHRqdKVkg3jyWx3gEttQyoa5IhTCDu8Ey2Vy/ZGaZl466BizRWI3gKmk1oc7zMMPmgooqqBNbS8pqqS4M2YBev2RxUY6DZDtkjY3+0CgYEA03J0ZqT/zWwZjh+0416S0mDf1bqMvDyosBXk1L/QKmsVMe2szg+/Kn3yCleTJ7rRBvDg1mZSjo+g57Kahmw/Uqiaw0701p2XCGFzK3mlVDGxOf9WmL1IJKdNhzk6n2LqJb40UiktSzhjst058sxCWFo2Qez/iKWiK+vD2O7lQoUCgYBpEobNXTemo0vTlbOZ44xczHZxKpIINNDE0PkOkAUK/Orndbyy2MBauCzadeElzRSE4vUKyB6dpEG5QQI8hQ1NAIvlarvM1riUZhyqbTQbNlEWAAPaH86cnzz2JvbYWPtKdSH4TL8QK/PZyn+CHP5sYAZbDCsAc/UtqUCV7aK8vQKBgHBUXYIqJbr7spfOQEqJ83XQARmKATn+/56MAwUC4ZOnT5s2qJbPxr7MKclUvNheZq1JSiicMz+JDfII8A55cyp2Z8FaMLfTAGlzW7u85BfWTlCBVX/PDoRqXMV5oasK4Vkrsp/zQBy12EEtRZu254gIZMqhHR963qx2k5SrVSABAoGAM7zx4KRdCnGe1RA72WQrJ15/rvSzGR5O7dCzRvWcqur/5taRHy8sHPk/8WdSI4tESfIJRA2mtd2565eawy6iWBuqSnWCra9VqRHmpyJ59b2tSD0pnJ//lpG4vceWi26+F+c+nQp6MuRzWo/zciT69Q6TJXFTc4lzK537Bwz5xsQ=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0GU6EP5luaEJUiK/BWs2oFI3cQ8f5gpL97ClmjuCIJ2bACnsupgxI5HvnfbVVll+L+/BLdA6WAFzwmQz5GhDQyN6mauvZvYM35uKNnionny8kafqL39nXFQZ6V7dNLJyE6BjRwGcZR6pgYpoz975QJPY3BCQuV69SeNyOvVxUl3+AxTH/xuk+LAxHT2QuAOn0fMd5RDJ8YbbUXQX1ZTMcHn+6wuTh1W+NU8DbgckIi6lU5W7WhoPTx51l7G14/sO6h/YwVKk9MiwiX4sdZw7vtzxfSNPYsa5V/MA6A4hRtkDBc1kZEwOin/u6qJ4Bd/VonwVrQfBKQ7KMbaJnaHAhAgMBAAE="},{"id":"Qme4AkND5vpgCGWu8aEa7QV1mAQJntPTAXdqs9wXZvHNnB","privKey":"CAASqQkwggSlAgEAAoIBAQDYIK6UrqLTawgQiPKupGx+uGDKuswrb77kAxXlK9nMwTp0LOFaoJkTpyBNmnkNbYdmRyNQjRASFlYHiMxlucDWw8eOg9RqPrvRffPCqaniPnZ+sNnmviOOdsg2jzyIw4k1nmgVtI8hYWY+sqh0x7zRwfZdv9RlAuUon4yky+Bye8IFLVFrJLnbi8v2fraP/qs2GId6N+gLMpolRTZrBvPAwy9ojTwQRHB51J4FYsaYP2P4dDI2lnI8p9OyjMQzYYnLWeR5lhrwcCCnGAb4CM0JVuqpxfyiTOS/UUr72MoGMJzttpwprDEmwdDE0CquvXcDSKGXV/hmyJxNCm4Kyim5AgMBAAECggEBAIW9+6rAQrxHsG7jACI4V3c3S5ZIb821UdAxZJwzPY0qSazYlP+LAA8cTa4YGC7KB46wGNdg43/92HDDI5ETmJSOBHBZfdndRkaGBZEBgsIhWxxc/gp+wOwI7y5sBs6vCbpncwtzd1r1tdq4neveKic3Ouiaq6lyWDKCQPs9un+8l7XuUxxLI/sbZH9+SD+f4w1LhLkgg8zH1fE+ylPL/MhvN0Rt+7S835qWnmU17GL0ZTv8q2Jva4tG4QIrgm8k/3K2W82ez1LzRDbOcgaUdUti+a96F7t6agKk5jiGIjldTBVAmu477PkpEinukGJJLG+bWQo5sblFDDegW9snbk0CgYEA/owOoTbmKcVFyiZo4D01XyFLIR0+NsUH+hYr2N+p0eu4OHDAx+CdfuXBaEgw1nQNAfovQCK/WBcsUGdm8LuU4PxPm1nSS4+r3xgCNsYHWKzozHUBHtGK23G3/ifqHsKJ6v2kV74RtrrerkoRG9QIhJXvGfDKTnczfTSioiJ3dEsCgYEA2Vx8jbwBAJ+I2gubQHEl7xXqgZwcUqYsoMWS4dE9gNgRBSxSmrzh5+EvpCGzB4xxADFMq29CBAEey1fYVlurKXhg1PguoqsuEfAG8TjDYqY+7qbfphK/PKKj5+PBWpAt+T7j4EzU0PUx0r20LgNrGymc0O8inKRj9RG/n9mU74sCgYEArwmMfduMhii7hvjGoXw1mUcBrLG5OFxg0d4xJJ8Y3dtkAJu4VTnl038WcilCPBXtY/CwUUATp/W3aUrRhLTY+aVG8KLhc0hGfdC6pQOjxZyRMRc8P/8Dp0cZqNG1spU/1bG5MwTmlWsG+X7BKQuzF8kqdAIae76IT47ETw24auUCgYA0qUmr9S6w/fXkDgfRNxifskmcmTvrckNBuFx7xXwHKVIGVNS2lKiOy2oFc/yyCX6fM1KF/nCaE8NoNKI/WfpAE+Ax+OcjliGVSvgDnWMJFYoNu/RXghc/E5BucCm6TVA2INyIzVSKQ72Md1WeCEn8zLonjKhwFwv4phItIkQJPQKBgQDs7GdWIXI90KI8rRePrYUeZ+bS+wIL8TCaS8ckGKxR55x6TEqiyrI1jXrzHBajtYQPsnYHLpz7N8T6acTN8iEuC8q1F6SHHTm+OekuoX4lduKBwZH33NzLtpTxCty05Z5JnNkCC96rhX3PEBDMJxFiJu5A9zR/0V8DPGIhFnI5kA==","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDYIK6UrqLTawgQiPKupGx+uGDKuswrb77kAxXlK9nMwTp0LOFaoJkTpyBNmnkNbYdmRyNQjRASFlYHiMxlucDWw8eOg9RqPrvRffPCqaniPnZ+sNnmviOOdsg2jzyIw4k1nmgVtI8hYWY+sqh0x7zRwfZdv9RlAuUon4yky+Bye8IFLVFrJLnbi8v2fraP/qs2GId6N+gLMpolRTZrBvPAwy9ojTwQRHB51J4FYsaYP2P4dDI2lnI8p9OyjMQzYYnLWeR5lhrwcCCnGAb4CM0JVuqpxfyiTOS/UUr72MoGMJzttpwprDEmwdDE0CquvXcDSKGXV/hmyJxNCm4Kyim5AgMBAAE="},{"id":"QmVCsZRC3hnK5H93TKrWSz8bPT9KDRThWe5KiVH25eXTrJ","privKey":"CAASqAkwggSkAgEAAoIBAQDpUgOsvoQKX3vaTOPLU14uN6m4XRmZGqdJ35KvlHtXugq0KauRWqxLKgRTfRO07OZzkOj6B6sR7J6PdPt/0HDPxM9jz34S2jHYtiW9gRL3h3dawBzmf/25xTmtVeZJ35CYfrgKJN+HQYUEHmhifWm3p4sI+Egc0PuW0pnYcHf4U8ZDLzKZ6WASj7tHjjIPXWaYPgwSwqWXa1UsfzovcO9B/jf3pEANuAqA/JT9WzRMVW1/8IC2uf1q3yOIc79AtiRb/nn7C2YxKFw3ZFJADdAp5oiKqdE2mnMfp3T2Qbv8w8Uo4alc/ZxW24dcUjc9nCf1GfgqydMqroZCdU9Be0RfAgMBAAECggEBAM8hzTtdQxGGZQiO/Ce0kkbLibOvixsdy2fMwId5dOqTsg7xc2uuKIjt+zs3LfiP26K7Ael+R0O5YaaPvvpgTNZWrZq5SP5pkJkIU4biALmZHL+HD+EdvV1flu2n9bKdc9u9m5r90ydbZkAa0qalGeY+KjNDX5mdec0SmBvK9OaRnrsmZL/wJL7BQHSq1xGkJko9DQrq9Z+pZN3dqLaSidBPvkNVxSosh76ySddrQPg6urcNWacTAO3n1321vqY/NBPUv9lZSZ8DNHX99wj53Jw8Z6dq2mkTP4WKqPg0PVmu5Th2zgvHc2KIYyNfZWB65nzrsEWQW4P6zDgnVYM+pKkCgYEA+sy7nbVFwp/HlVcg2jFuT6uHgjlANRsyKPc/XLaZK8ThkYAD6k7bc6T+BEsrsYNwTCOJhY/4j5HetnWugzcjevkcjGMBU3i/PC7TzD+3uI2FwxmlhxmhUvn+wlWiAPDfKlvzgQ2O+rNeHUwct9Z7VgfE9sLiV1Tn8u7eM6LW+GUCgYEA7ih/27TNV5tMMFCbVXZeZNE3Lo2K3haKzyHh6rcYAvpV0VdViB0BueaDybV8NyeD6mRV1ddlx7pmdjW29qqCnPt6OXWXNasxkV+wNCoxzWYjbRNoWQDYVWyJ7QaRbHGv9VfpGXvhH+uko04F6X9oEgPHx78REqDLDKrQjVq2g3MCgYEA0Gbzt/b7guA2fngqCmVs3rMQJTvhiL+SjMv3UNlCqkYtkjQoDLl+Tbhag8LbCeTImdwl2GBsERELWd6sJsjYqMT73gQfA/egIShgQqbnyWmAL0DOdVbs4QBvm5iAx/tf1dkWnLRhjR0jJYErk++cCT7ngo0qe5tIKmwnJA06SKkCgYA5x6GdkDPxeT/2yNem2Fz2YLvek4CJeTacIkBJTbqJnmqLq4CY0VDF5WM74MgOKYwbpdQdWXDcECEjtMx688tUgr6YiirxXmNcPbbmMQZ8KreVkwvR7B6QOxELbHiDsuFZyMZI5IEt2WHMLPXKPJrnI/uoPnbJ6jDZFegbI1L8eQKBgHuHR+HJ9x8g2PKQcoAKzcjakPz93X4CYJH28cNzwlu15PtgdxCgA8m39Zc/d+QScMQKmfeWe/ScprnRsKDfCNRpF4CPmINlGTkz6zhlpxh0Yl9B2RJn5fq8hSrDgQY+3se72Jb9+tLvuIBFR9NIFq9iiHRvHGKiVkkCLf42c6Tp","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpUgOsvoQKX3vaTOPLU14uN6m4XRmZGqdJ35KvlHtXugq0KauRWqxLKgRTfRO07OZzkOj6B6sR7J6PdPt/0HDPxM9jz34S2jHYtiW9gRL3h3dawBzmf/25xTmtVeZJ35CYfrgKJN+HQYUEHmhifWm3p4sI+Egc0PuW0pnYcHf4U8ZDLzKZ6WASj7tHjjIPXWaYPgwSwqWXa1UsfzovcO9B/jf3pEANuAqA/JT9WzRMVW1/8IC2uf1q3yOIc79AtiRb/nn7C2YxKFw3ZFJADdAp5oiKqdE2mnMfp3T2Qbv8w8Uo4alc/ZxW24dcUjc9nCf1GfgqydMqroZCdU9Be0RfAgMBAAE="},{"id":"Qmex6ZndSe4SLWEZdEDuUKsrKzPRuNBMmNVQcSzYZP75Qd","privKey":"CAASqQkwggSlAgEAAoIBAQCkLmaaLxWh85CT1w0OVX6Eu3oJKXwLaYx+hHMwb1b0+FVgFi9nQrvA73d/zncaOCVMwZuPrLTQL3nNNdKgykXENptugh2Da5aBtXvzBZnB7cFZ1/qxv7KVUIthtVa7NTeDESCZ3XNkzQzFv4eobLYhu0CWWCScMV1p7PrrAJrQ+YG0un2dxOn3zO4uIOQdNlLrSVpZo3XUU+DHSdcacms8GG9ejVAnwu1BxDwxSZE58OnJKgmC69C2Cna10y3ttC0IbAZSE3G2UaTpdnOjp6ZatvqfIACtkGKQNu4nYmWWQ1YtubIhmtXwJaHfAEYTUxIhJtTsJ52AYLiEQsCWie4HAgMBAAECggEBAIbF6EfNOu7U5+vUbs1DdTY0R7tZqJAdtszsoTozS6iQalUUIRmc+E8qxGfP7olBaa0WiUJpvYvgTRYdhqejI54sTW9rtBp7qPMVLYiTImLFbyD3YL2k5U4Y7jHpECdhSg53ctGbrewjPsbdsNibVO9frWc9N+BXzZz0gg2DU5MSzaXShnJeeluYzEbAoSObYypBonE4H/cs2H1NfzKzBTqaoYpeboMyhlgbPhX6L30gU0SG9CtQAoUd4m1SUPiTMF1pUg6VnMZxtyTHhI53lqFtQyk1BJ9e6fTcy6w1KcHCSySZe3z8pazaSEV+ojs9zBspQceEW+ULCdyO/NaNclECgYEA0rh2MgHc4E8OTHMT6jUE1oHqJ5tybudTjx0ATjfh6qy3Ctz1GnlFjIglYJbleXJ0Q/AGN2ny85WM5fAatWcKUa5GKLtiBUgHg5XUlcUyG+KMOCfm07s2J9oNYxyVek1B1DYQ/B9hB6qlje9ak3BOHzyYN5kJLFHBq/NFOprrx70CgYEAx3XbL/oGpCZMS0x1N6I4oS/zldI7VoLOR5+7nRRPKIKDljf9VYIsXHB71YQQB/IzUR0SYdBRX5nLFZZGPtHZAm+Z1XFGAd4mqXdYxOVvOdEay/O3J2zITC+dntU7/j0jSf5CyshBsTfczpYRc5YzHoiYt5twRdWrkbs2rSNqtxMCgYBVPZdajVRbTL+c/2RL9tyZJX6iPQiigmNT3kh0W7jA5NM6yr0/01TmD2EHXKNBaKJNpTfujKZiENopXeKABUmwVw48heiV+FhJy39ugpHNo+xS/xkqB25V2qhVsodpLP13KdPfGZZ3snk8HFoIkIKZsjplKzwBuKTeuQ8FMQw2BQKBgQCHm7RbCQUs4zDp3OCw42TXCe/1Q4q+RY6TmaEHqcArzZhDMGvXj/ozGvLGdtQjYuLK2hYeVHCOq9QsKBA4M5n0EwYm3K15JZ1piE/dBhQ53dX3Jgq3Djmbup/SVi1ErDJq4yhsD/zBzTYdamAREuRMdRhHdLG/eQb9vKfuzO6H6wKBgQCAdNfJqIJj9FsnQ7tGeX9kOPidYQl+wUKD89/i8HCTKHCo5ftZjpxAJ5JW8abD65olMwpARjSMer5bIn0lvKCfuYqgER99D7ViqaVdhzbGljgxG8SL0+qMftt6RRoWx3YbAKCKQXq0Ns/vwz2kuRPSiEsVZEoW9Wbvi+tFCRBUbg==","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkLmaaLxWh85CT1w0OVX6Eu3oJKXwLaYx+hHMwb1b0+FVgFi9nQrvA73d/zncaOCVMwZuPrLTQL3nNNdKgykXENptugh2Da5aBtXvzBZnB7cFZ1/qxv7KVUIthtVa7NTeDESCZ3XNkzQzFv4eobLYhu0CWWCScMV1p7PrrAJrQ+YG0un2dxOn3zO4uIOQdNlLrSVpZo3XUU+DHSdcacms8GG9ejVAnwu1BxDwxSZE58OnJKgmC69C2Cna10y3ttC0IbAZSE3G2UaTpdnOjp6ZatvqfIACtkGKQNu4nYmWWQ1YtubIhmtXwJaHfAEYTUxIhJtTsJ52AYLiEQsCWie4HAgMBAAE="},{"id":"QmWn9a9Tp6RHbp7kBJoeKntWAehDEM4h6SJz7GDDZ74xT6","privKey":"CAASpwkwggSjAgEAAoIBAQDo+BBORhRA7Ovd3N57O/s3uDAvZ0FE1fGb6Fhpuq5x7ZN34F6lK+UjgCS9e0vadFsF0ReKovF0RUIaVxuqEEhT7PQw3SFxYe/uKhaXUdimb6ecmStd9+jfA30txQDOQQkOzQ0TFAwhQP8o6yiPk6FQ9dIs8WIZj3blj9IZ0pqkORKl8elDczX5fzrmyNGFTa2CfSvQ38NH7egXQMRWk0apeQZfBAr6m20a5rePltyv9qvFaSwGbzEq5wHqMrZs62VCdvTKPq68bh+XqpJi62A+QxUhqgWC1orNqnVehkoksihsS5sGnz8Mr0z4SYF5s9SanExuszVC3vyWRaRMk9FhAgMBAAECggEAcDBFaexFhJ6TV1wtp5rS95ozWiCXRdbhavsQ2JmVuDd4dJoP+eqRhe8HwlfFx4WQ79QpShH9xMg1dQnNwK/mpMPPNFjI6hkHueF6z5QSiNPsG3WHdNEVy4+akJf2M10OR5pnWXCmBr2OIt5bwgiN/rbiZ9I2M6jq0NHZk6Xf4oTORF8bYYg9EIRhBQuVqstgf9y/l1SLVv4W0pQ903+KVax1Ir6BYN7QrVsqhJE1Xq3pkfhbF5+a8+A9vrvYjyJfbYYAgpwuQUNPLt58o3YEiL3atnCK26AZ5XUAvUH6hc6JlF0+5ZA1+EENpvliZP8+oKaOxj5dHj9LxAQhp2gIAQKBgQD3CsTOI8g5TvVPbYs5VaH7CUnDgkQ6rXGCYoiuXJSooD3npMhN0EsQjJrc5GiRe0MIBv1YdknZMX8OpA/wlMHe3feCZRxD7GlkqWsY9xGMp0eZxifYSKpvjOKrLC6tOW6lbG6ry70V6bZ4U35D1S2EyNzZwVToOpD/BlCof0mpcQKBgQDxaqh32X1xqSoPvKBWqH3bQ6/gEKqyXYpd6ddLtZ6YJ0ktyfj/CTegAo1U3OxIWfz3QtnHGpql4bHI1729xgiSRMlPacTFSwqhvBrpWDtcEXrFwQnbGs4pnTIpsyGf10SvTB735x1g8LA7sULTffchStwR8uQlecPyw2rYy9ku8QKBgErcxI8DYOWc+9G6DHqvbIq6YdQRYcO2U7ODUJAmFhaTmK7dzKVn/ZdiRHuz68xKYiHInHV2QyLCURKKvRnWunljImItacdbmQScIoxKMtNAzf3XgrOUMbHkK7xbgzmTMeiHV3JQJ1jAnj8Qbi74JvcaN3L1l1lYhiACNecZy6TBAoGAYy+9E7Cp1373JwwKnFARlXqT0e1HFI1WAgvHRvXRuRW6/TMUOERjNDyYMJusAHXodvZiX2lQzIKT9r6c2FF6yA224AfGroCMV7LfI6v6h3MvC/ypvfmfZn2NVJCiafeZ4qPVvWiZK3D4Fy5XFGmWlXkaPc2basJCfYVIatYl3yECgYEAmoqqo+wazk64q6lixYUf5hPZvQzic+8k6DGDfC296dXwal5du9SGCXyWPh7n+3+NQBVn/rtNFAJtu9Z02NgSDD5p2H4YLjsKcFYYOyp5kTY0N8iw9S9DnvFfjXIdJB30Ec/TPWnblm77XjOb6WVdEqZJt0mncHKDG9dLO/UCkvk=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDo+BBORhRA7Ovd3N57O/s3uDAvZ0FE1fGb6Fhpuq5x7ZN34F6lK+UjgCS9e0vadFsF0ReKovF0RUIaVxuqEEhT7PQw3SFxYe/uKhaXUdimb6ecmStd9+jfA30txQDOQQkOzQ0TFAwhQP8o6yiPk6FQ9dIs8WIZj3blj9IZ0pqkORKl8elDczX5fzrmyNGFTa2CfSvQ38NH7egXQMRWk0apeQZfBAr6m20a5rePltyv9qvFaSwGbzEq5wHqMrZs62VCdvTKPq68bh+XqpJi62A+QxUhqgWC1orNqnVehkoksihsS5sGnz8Mr0z4SYF5s9SanExuszVC3vyWRaRMk9FhAgMBAAE="},{"id":"QmTf2i9cxP5HnTc7jAjsTabtKqPYyCRWGFyxQ2JoXre8Vy","privKey":"CAASqAkwggSkAgEAAoIBAQCqk2sdkh0jN/p9Znnw2pNvPULjt6s4DAFsnvl7Vjup1XFC7K7JKkycueATSDjjjzRgQknPp+GdJLAVDBRRhmHUKVWjdirGKid7qqwYe3MhMS3KFwKVRHUh0zdkk3D9I3UZmfmmCtrlFU3zrKLFL2Gq9wUFomyn4QI6FXG29m1MZaN8jMMRJq4zl2PaSdHFeingd5Cc7gbwd+m4DWPY9tCYCpOF/e75OAOf4vmx3G02woAjIbAUtSJSFoMnexqkzGgzxf14KrFqWur0t13UHPwTecMntkIjIrv3b7deVfAssXaaqtts7/hBMfGJHIPGOEA41AiGpg1hwitoU9FJbtYTAgMBAAECggEAaGSygLWEyaIf1pFZ/dL3unPGtTEaQ8HtOToOO7Kp7yXnHGtYCVuu6ZhEj+h6kKJ7DpJjNAht5sumTCtJYB+bTcpCVDkksoToByKMEquy6VR8zprIhOwRt7LZyt8FnpcNuq5mgH1SPDOOspwsPfvijUyCsVEXqKLnxv6KyswEVmU5PABwY3EEOFOC17NkHqD+wofXdzjwUfaAzeaZRZNgz92/Mr8Lqt8/9+znHoznATZfsV3IJfXzvvYcTv6+Cui+eWtqGy8TFT0+d5q11lXB/L9AoDtH4KOcEErgYfpuCH7yVBaJpHLZgmfX/MGL2jXy/BOHH5AqI1qHLCUmz4SuyQKBgQDjOcmJsNjg6l3NbZnDV64NLp2JMzw+VVy0sZbOX/xfhNp8RzFli+yblRP7Qijzz/AAeK87TDSXOB6Nz+z0ioM1YZHj2K+ipIo01fG2hXRjo2uD0Eo5Yn9hGHvYQAcWPtM7pRZckYf412HjGV6E7CmU4DxXTOf/C2jQUuP1vOH8zQKBgQDALSc2KP1Z7lT7DShh5oes393OtzvzaiceWgWQ4eKTHKFrgwCAWfSuOWinpi0uPUThyy4yggMQLEf89/Mqhy5TRo8lBl3++UhUaOrueRlfw2k1wHEoolY11VC1alUbLdWvY/5fN/NusudyNkNtKfgEJamWfPzbPB5gjKFMLboeXwKBgQCAVSTkZUM4VGXCHkKXty+QNmG3GX7O3btMOnin+kI5WHllc3gMmCh7/oiYgddLTIeKXJ7raLDTL1jiUyrLOme8gGy7OKiJvVBpYuPvaBRCKgwRGFSzcFPs7L0Vd6QJBlaa+zRT98XJ3ytj7fIaNJ+Pe83YqJDm1PTPo+En6xPfiQKBgQCBgCvzSE3pwzxK7QPwbQuyiC0NNtbUYg04W8tyZXVsUxfHvkChPX0vxonqp4W8qhb33RYlfQ0ZYe2k32VinDOwdFTTy2WeA1glYYom5z4dFS6ZxTO8ASD9J+E6TwxIw2tCxKxOeAK37yEeCskHEAgMq2CLg3BVQD7sKvgqwImyGwKBgCHRfBffD/BA5SaFlL/AbIs2cP27NqT/rNjf5YmJjUx9YKBAYEtoKKGuiOLuuN1yrXablZ1fis4/jDqmZl44cP45dXMdOgFP+nItL0jEfF53sz9NxHa2DR2Gtd1QxeylmHZjMsSeSB+9f33VPARa/wh5tIvSy7cDOweAukxxxDqy","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqk2sdkh0jN/p9Znnw2pNvPULjt6s4DAFsnvl7Vjup1XFC7K7JKkycueATSDjjjzRgQknPp+GdJLAVDBRRhmHUKVWjdirGKid7qqwYe3MhMS3KFwKVRHUh0zdkk3D9I3UZmfmmCtrlFU3zrKLFL2Gq9wUFomyn4QI6FXG29m1MZaN8jMMRJq4zl2PaSdHFeingd5Cc7gbwd+m4DWPY9tCYCpOF/e75OAOf4vmx3G02woAjIbAUtSJSFoMnexqkzGgzxf14KrFqWur0t13UHPwTecMntkIjIrv3b7deVfAssXaaqtts7/hBMfGJHIPGOEA41AiGpg1hwitoU9FJbtYTAgMBAAE="},{"id":"QmaXs4U4QE5W6hTwnnWoeTtLpRv97rnnRaBrSHQ5SEBhsy","privKey":"CAASqgkwggSmAgEAAoIBAQDpL5CNWxaC53PTJcNB5oo1x7FHl02I8Q3+aH6MC1Ln/ycL8gLVcvpAYsWStEt6tsU3Br86kl810f9P0Rb/DI4QVEgiTKCrTPVX1Ri69abJPt7wMUUM6zY/loKIX7oTDfNqRePoZK8lTGbiTwQohuJ9YCG1Pmfr5agYuOoN7KwflWidK1YFFf2MCeC6Lw9mMrCeNtiGmgnQEauh35LBdsYPHdPCRjtJZHkI5JVLl9D5bZAW8kuqVcRXnLLqklE5ijnvwOW6qDnIa3L8TZN/H1Kc2V1Ay56CUWwtvESPYCxxGzhbKs9GOu8n8qboPlWM6jpxhf3bGD9zzjA/AOS/tkXbAgMBAAECggEBAJ6CQ6ujAHFpbdVY9Rql4Mtf/AUjN4Rf/9gxJdyCkg+75Mn876oypyP/QDhSMICxHuwhvPnOp8PJBNk30376BNPaxtHx7sGi5Z+X7mCgPYjNIPR7OWnntY+R+xdCoxTyxcU2zw6Y6mnbPpCVj8kQr4L0RtWSH0g+a36qrAddGz+3nxCPSzoWm7ULAQu7blu4G3O8aQDNhqp33nM8U4lo+18Fl21bl51QAH5hfH/5ynvgO8qTHqHFGlzEKUQ02DN677y9JBSqnTtMdZtfwC9eM8ATZ3a/WczVCVsCIgNNYJqGyJP/hER+4OyCts0EE8a6CyL1wyqvRHOlzd+Um2/3EmkCgYEA+XeYrWQCNk1E1bliK2mSwEwPQL/029sI31fUK2WayBRrC+Sb0icS2n6dEBvZNFwMDdaR0ioOFa3R+LBP5mjHUPlnGNN0W7gGdEiNhnMKY0DH63aXO2DEFsbSZv3pt6Y1ZEv2DR5pERCV4uQKNeTzgFG/Any16Og169A3BasfTwcCgYEA70rRy+kQeyKSyKN2mFtEQibWpkGAgLZXyiOzFl6TDxwKJ0TuCRIXzJ/ups/wBHMpwiLegtEYJBCKrsdJ+LYjGtf4RyS1fcyrEuVwHrSYWcEPyVHD5BY4kPDoH8s9yPNJnYs1eC5XWaNsZmtWHvwiLscWL1fRtD92xKD5vsgriY0CgYEAoefy2SW9Ic0ITcWhZxd/vPogNB/hJQeFPJ7d/sRUrmJCD1fxoXTmkZP3D8tjLs5GrHyD62iL95n2WnbjhGCga3dSaBRkwlovJi0oXQgwx7X1imZPRKRK35VuXJWSqDRhFpUOPflsqzyeasEht1S+jJuBYnXaAi0jQcbCzxT+nuUCgYEAglTfBc/wMRD4n50gqAT1nY4n1S/R/MjT8f4VubZTJKSiBxdtCrW3DrWBN4wD73iGIv348YlPO71iu6VB7dvOLP26dY1R6K3D86vRsHeujxwvK/iD2EQeFRuRYlfBVCTmUI+U1NtwqhrvnK0ToBa1y4KV271aM3Fpj9kZjcbap7UCgYEA+L3SqThkI4fhRFxNvpxddLskL127L+GqBR74TZHI3P9ACFXOfW+6me+PZVhai6YRwohglkBPx1NE3TYYAPysACMIypXjTDbJ6sXW0kBXIdkSunnaUXAjlJg2RnEOBeKOIn4GoDqrhpdiF5hOBMLrFjuoVnF9YdBQTEN5trki0/I=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpL5CNWxaC53PTJcNB5oo1x7FHl02I8Q3+aH6MC1Ln/ycL8gLVcvpAYsWStEt6tsU3Br86kl810f9P0Rb/DI4QVEgiTKCrTPVX1Ri69abJPt7wMUUM6zY/loKIX7oTDfNqRePoZK8lTGbiTwQohuJ9YCG1Pmfr5agYuOoN7KwflWidK1YFFf2MCeC6Lw9mMrCeNtiGmgnQEauh35LBdsYPHdPCRjtJZHkI5JVLl9D5bZAW8kuqVcRXnLLqklE5ijnvwOW6qDnIa3L8TZN/H1Kc2V1Ay56CUWwtvESPYCxxGzhbKs9GOu8n8qboPlWM6jpxhf3bGD9zzjA/AOS/tkXbAgMBAAE="},{"id":"QmVRPqfdzzxgSB5wjpSewPcmGdFFcK1Sjvy9jJYQCYKt8x","privKey":"CAASpwkwggSjAgEAAoIBAQDO0uSs63dS6MliDuqAz1ElaBkut9tjug90F4L/SCKiLFSJjO+RgeYw/I7zwGcuB7njhI9XQpwIqnkMhGeqGm3xAB88Gn3VUsOQBa6YBrF+1yvzl3lpdTIwnbZ+G41PE56jPVdbN44WHXmGK8MeZhHRiSzsP1b9nQsO8lOFa6lYbjhRJi0hKdZ8QcnLswA1mzUe2D24g7CQ+QHcgKtKHIxc2g/Zmz3Gm6Sp/HBP3BFigsAgZ5RXljYqSKci6fqR7DV1H5cEHpr55oC3RqKBWi09KISVQ7gt7204d/to66LUnBzQ2yXNt7UgYBwtl2kAK46FlnRlXMGf5Cakmuo2Mw3vAgMBAAECggEAewiFtEAWqaGFOun4okuxz/jPEjWZMwgZ7UuXR9lXkPWiDHlkgkCt+PIk7+fC+WTrNedDhfs/6v/OJp4VNOGoCTezXQO9W0tyvphCtnk02WmS4cN4HD31b2pCh15If4QCWMVC1CaGRa2e/yvFVK4PZ4Cc0qUTUKc9TtTkznevM8S6ndQGgdVakQm71Sl8NqRoLT0AOnJqOfeBk3wPdIaUNAHV2ugMQURS+UDaujyW9XXn4g5YiALksUFUedLStQbqytmSt395IN3o2InzFZeJhO0WeTK1MxpCeUguVdM2pUisLm3Qtpbdwg7/4H026FT9jrdjxLnTP0TODIieQKHP4QKBgQD8KWCnNI4/SC9wznoBaPLliBW41yZzKRnHWB+RtyjJO+y5JTynZahLgqYZ8DGtB+giDGnc5I6IjYo5W6VfM2WYCeAuxsZ5nzgOk52+keUoT7vygYsMDS20YskrW9HsVnbN0Ss4lgckPO0cnmtpqelLSYtjlXVwmqYbIgIqiENaSQKBgQDR+Nfrj5M462sbd/sDFHbVdIBfPrjPsNp23pd6JlEDqPHKqdMK4qXB5DdN72jt/+26F0J6CZbtbJXWNCu3b24s/VKfnTacaE58ScDLdWinZElCmEBEZGvLzTiQ//8rC02GbPQADCgm7Q66lqnhYjyaYvMpG3W+QL9Nuwz7qZNmdwKBgQDaeMsZ8ynvYA5iUTJZuGECR0ZysE3K5uhD03D+oc8/kqDFxKJpcmU+jwJ0ZKvVe40+uryZuphXbTI+Ac4kznYOJThLrsLVKwK5dmHsc6E0rOhs8pnTJjPcHt6mp1YYys0CA2DMFQg4+GSDMCpmEOVk6mJPNHXcytBxn6R6zzZzqQKBgDf8A4Y5V+W7T2mZigH+ONkdIQVEMf6ehwFexEC7aqpxJ1cEBNNILy4NbXmINhAPRxFxFjZ3zBgt9Z2Rd36HZXb1bExGnMMDRLn66D7XZToMkUQCWFBj/iHu7AAtYZnWf6sx7bT9iM8oMd8qQM1pZgM8Et2JBtHdbaSKFSIcysJtAoGACy8BFGvZ+R5vk/f2HvW0nKsnjJhr6z9Q5ocAmTz9LNDDDiK7BO14pKJusD1GGIMWAG+4rpyp4CnLozlKPZzP7t8LWQoSOQx4/ehFr3lzlWPkgVXcHCJ3vUdae03EY+2DNlKgv4O30g4soQDIPewMyiJxaMnnLtvb/9ZkIYLa+XA=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDO0uSs63dS6MliDuqAz1ElaBkut9tjug90F4L/SCKiLFSJjO+RgeYw/I7zwGcuB7njhI9XQpwIqnkMhGeqGm3xAB88Gn3VUsOQBa6YBrF+1yvzl3lpdTIwnbZ+G41PE56jPVdbN44WHXmGK8MeZhHRiSzsP1b9nQsO8lOFa6lYbjhRJi0hKdZ8QcnLswA1mzUe2D24g7CQ+QHcgKtKHIxc2g/Zmz3Gm6Sp/HBP3BFigsAgZ5RXljYqSKci6fqR7DV1H5cEHpr55oC3RqKBWi09KISVQ7gt7204d/to66LUnBzQ2yXNt7UgYBwtl2kAK46FlnRlXMGf5Cakmuo2Mw3vAgMBAAE="},{"id":"QmdPr1j6pALmjygUjC48AhczryG25NAXDrjkDQCKitwS3x","privKey":"CAASqQkwggSlAgEAAoIBAQDiBAOQE6X87s9A+utaDvlxcNUIy4g9Nk3Pf5mGGo7o8tSm0pMxuoljRJcX7fxcRH3Wg9LSMIHuTT7V85BI8Afsb3pFqWWp6X+XnEqg2sUU4tgf/+r8gow5b/wVWKdM13s7ebOl7DkaXA/ShSqTv/b5l1yXxlk7iCD51MCnVeHf2sUtzrwnznMttsHBHLmokis145vze1iftGzcq41XEUgaCzPhy5MMwLQRKxSdgyVUU5w4Ihiduimi2B9Ju94K6kb/7JO0IgF9vuxWfHlZYm6aYYxUmYsHvLzg2pxDZa4sQlCAhxLxqMydfHS9QhVMrjAkgN+nOx0kKlupUTugikjdAgMBAAECggEBAMME3aIjIhW2ZgTWvd8ujpQQ3PFC1eKjic5idrEdF3Tvmwf7KOE+6K9MDueqBRgETWScyHtvOZ3K704jZLtayit8IVAck8lh3iOOy2Iygt27wusRosyRRLeG5kfxACKwx/eQ0q93ven9x/VJzGxCtrO0pEOd9X8FvhZ0OmvpEgZZmpGd4ZucfHayBE37Gkg5Rgnafvw/pGkJiYmpwLNFoRWhNkKywHkDlyhEtFwLaWoS7UfOqWuZHH30EKLpz92nCYrKn8dlbYaT+0q90wwyCVAg6ci766BHgyYa35gWQgUBsNCEaP96V3ltXgHuz1DfFBgVdKe11lU1cMBecWJT+G0CgYEA9lXhNidHBDq8u5Esby9w6d1A5+c81e0rPiMrSAhKrLYWxuFNjR1EiL5m19WT8nJmSAsJv/n+EAQB4N0k8itpaiMx/uwIxymUBqq3NmtkFIBspa8IjR/LBryw83Ec5J9aasxiPpTmxmeuOpgkGz2BR+oJez8Y7Ykp1QN1aBvBH7MCgYEA6uIMWyyJOcvWkX5as9a14/95EsS1n4uSYialmDdIkV70oUfaITUdggV8Z2d+5zzmrCWd3te9NEskkXIfxBZXBdwk4yK7ztx2CaTnCegpj3FeTfprDIusXBYg+AkvO0ziumtt7hJq8ArSbR9p8a03pmU0KF5KHUQlq6qaJ0rxLS8CgYEA82KG0eqc0CD4uJoHPROyjF5+8qIWCYf7Ybl92f3XSi3e80FITlTMkg8NrdLoCnPdevDtATEPROOwt2vIRT1/Oc7sZuLvCVibWLrzqmEQ0PDSaCE4Ybc38cAdVWo9EeYgf8TQjOSx3vYxvtLzWba7NUKXe54sEwWiv0RbjDFf7uECgYEA1ZkHf4o6qJKT5++t8ElbU6GpqsW0HQjB7/wRLyFnHrvpXxmGZhh1Yx42CZr2rraVVwtGS7aJWxrSgGn2NU3TiYKvENcvT2jw9sg9SqcT1FxWVDdcTiZecce155oCl7zNmpXDfHLHSM+umImuwJazOuBh1Cwa3g5w63HTidP/CwUCgYBWXa51w5kVG1mPR3AJLE7/Ip2395aq8H39Tlsvfq0zfTtVaeXzYWYhw0+/u9DXhPd0TJm12YqkMCxSs0LLAAj28rGMQeqUI0OxsvanCpNvzZ9iKHVncsjPJ6qm7U7Qj4lQBqy/VNudQjdwWSTW0Kp0/ZJUTWv2nltOTBMfPZF7Ow==","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiBAOQE6X87s9A+utaDvlxcNUIy4g9Nk3Pf5mGGo7o8tSm0pMxuoljRJcX7fxcRH3Wg9LSMIHuTT7V85BI8Afsb3pFqWWp6X+XnEqg2sUU4tgf/+r8gow5b/wVWKdM13s7ebOl7DkaXA/ShSqTv/b5l1yXxlk7iCD51MCnVeHf2sUtzrwnznMttsHBHLmokis145vze1iftGzcq41XEUgaCzPhy5MMwLQRKxSdgyVUU5w4Ihiduimi2B9Ju94K6kb/7JO0IgF9vuxWfHlZYm6aYYxUmYsHvLzg2pxDZa4sQlCAhxLxqMydfHS9QhVMrjAkgN+nOx0kKlupUTugikjdAgMBAAE="},{"id":"QmWbooxGKEpPutuQh2G7hRRsvygkr9a4s42s2MaC44bXpe","privKey":"CAASpwkwggSjAgEAAoIBAQCxwXcAe498rEQmP0n/XytL8zH4NPIUCtYfARYcn2nlFNxBv9uRIuXKKkC0vs8IUNNcTIXse3Qn/wLX0CDffje41/myVDqXzCW/EsgYmnq5iFDSesuarCe7e0v+J5lpWIoDH6OYdZpni1e2Ucfsd8Gvj7rX/6y5vNnFUukfW6gYNGwMZIreGQhhL9PII80t8hR8+ffsJuHZwhDsdJQhxaXRD4pi6QYoWA8M+xDbJv61i5ITmS3f1VjC8b9qAgX/OaoqZYcv1A4sN69cf3sJOFp5AvVaDtlAu9rrLbwiMMLt8TjTav4ym5EIFAjZrNk+AawKKSFLWSGIEItEyY7bZS49AgMBAAECggEBALBnrgDFntV8ZDmMz5Pmz6IVHL8VVDS9uFeWjWokbkQQ2rFJhM5ZUrC6E/ML9YxsIgXUT4gr0E+tDRY+D5GcwhHoDH6Eho2E5OTl77wy+p5LgOIRSSQbu2ac6wK0K9RXMMf+yy0PcQdXStm9+WlcQ9RGEIgxzSmbGIdyS7Z6EKzLGaQXXaCOLg2WnTfImm9NGos4BlchAO/itAxO3jo+DgZoJhEdGg1IUavDTWrDwq462N+/O2+QwVUPPjB2rWyqkm1I40Q43u0UMNyGwpA0tEfaEXVs/ZfmJEukdymgyttLC894WAWDzvOqK7SHyKZTbZgQDer/UiHlZW0INF5Yd6ECgYEA3zuNF+3feXKTmWhL96HyweLWYybweuaeirc6yBpYwa7sNZPCdZukysl7lPidz7tgfA6vuMVqWH6h4iT6kihhlEk3dAARTFElWmLWC8VEHX3ix3Whh8yj1E9o/4WI0ljth2CRn39ISQKflhoBCUGLNNl1VlMW4gfFoENIq12zeMUCgYEAy9kFUkblAy+JnGzNK6A1BQErCK4WKUqLk0eXiXseNx/LnAgpE0ITBDMKj7X1dr+Dr+0dJkaVjfugLO/9fZx4w7rWUkUfFvZZK4+Kyq94tKruI26gKyfaRogVwxwtjoCD/m3IYTldrb11jYXzQ4Wnu+ulE2rbfeEte4mrnQldBxkCgYAmohnDAGnijM+6hvBOxPpMT8OmmUCZJHxfqWJE6zpdGrbKdu36iJs2dTQsDfLR1q3WCnyiz5eeBRxjFN9AmDh6/0jXjINx38FFKUG9+7/UEstClbsE04eu4KddQXDyZm2FNlM9dTu/VvLjeVCgl3VOCixiajLVn162Y66Z6bd7uQKBgAKELk2itGcCqjc1HJ9CPgVOy2IzC/D8i5aI1rUKwQe7K79EVoV3VlbB7opxm5MYsB521dpIGsaC6apEWCO5z7/SyZyAX+7foaApCHSSD/Ji/ceQMQf3WPlv6pza9zEAIyD75/JTS/W+FfpmQed+eeCW+xWvRwBZzD7vJyAsImrZAoGAOcT/3WaWaTv4ZNf86weXLTQ7vWLhrHj2tUpT6t19ejEi/gtZ7vbHDdaTJD8pQuoOWZ5jdoUWZiGbp4KeO+aGKZs43DcfZEecFA1uRbcnYnq1vYu3QvuALbRoby/EWO4vNJxvpsj9sEJ+QAWxVi1kNU4Ui0bN0FUdxdUH2REqTDE=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxwXcAe498rEQmP0n/XytL8zH4NPIUCtYfARYcn2nlFNxBv9uRIuXKKkC0vs8IUNNcTIXse3Qn/wLX0CDffje41/myVDqXzCW/EsgYmnq5iFDSesuarCe7e0v+J5lpWIoDH6OYdZpni1e2Ucfsd8Gvj7rX/6y5vNnFUukfW6gYNGwMZIreGQhhL9PII80t8hR8+ffsJuHZwhDsdJQhxaXRD4pi6QYoWA8M+xDbJv61i5ITmS3f1VjC8b9qAgX/OaoqZYcv1A4sN69cf3sJOFp5AvVaDtlAu9rrLbwiMMLt8TjTav4ym5EIFAjZrNk+AawKKSFLWSGIEItEyY7bZS49AgMBAAE="},{"id":"QmNbcu5LYC7yFfiG7YSphDvKeCdnrZaLDVH5myCfa4F3KH","privKey":"CAASpwkwggSjAgEAAoIBAQC4QngGmdiupunaaeDYdSSYLz7y7kxkWBgCM2KoarH47acTWqQIYNjas94SqFRYEZELAlAQgtX+bQ/BvSg7iF0aikutQNGuIoq/oK5U8prglhdBhcLv8bF6fb+g9bVgyxe7c+lvulK9N4B6+UOzEPCcHy858ZfydIWEBX2VuhDK9erCo4Mo8uicTglF3L9b5MwxoZ0crIbB6RZ3Zzh0VoRRr2bP9tKa1hg8MA6qJ4Z0qINJupO5vp3AtUuguFrQbTn45T+0UEqRZxOaMFI4yO2CIx7CcE+lstgmAkup39dB6CDivLFeD54140xnqmvqJBTcfocG2TJ114dv/PUjVN53AgMBAAECggEARZxe6ed/U+QdHvW3Cy+eRpw6xN5AH/yj/VjaYdQFLozh/M7Wf4/O/TjMofHUA32HdUhHZSV4oOkk9cV3iJ4oxlkO5AJD9Ox5zJFwqwkId+ZcHNXi4nFF4ofVOIL7Jfk0Gw5cV2hlz7RDJrI64FB7BhBF8DcUd3WL52eHnWQw6eu+l7DHgHhYmVvPQJqcwicd9hKKavNqssCXhywubtTSHTazxMdpdpaTpuG8MxYXCO7/uTA1/6Dz1UqZgYFDcm2Jke60LyLJl13hufy9hTUk2399NYiYs0WKvxI/7hcUBR3fxC6vJyRYPmmj/xMe9wenUMfqubUhX+BUl6u1GbcB6QKBgQD1NzbN4DKgGci1KAftYCWPncFHNAPiOa3vxLoZcoYDB5or8NQnlHmbPUhXAhwSMQ1Mzxwjmvp8gRedyZsumlgw4dh+0+Wr7L0zWnFalSQrE5+hBFLBE+dErFUVTqYJHMS8l90OWMKJk217QnRdSrypljKKC4hGdtzvThO/L5oezQKBgQDAXPmYNNlLUKCuWLo+WHl5Ql0MM9m/27CeB9OUFUVn7QHP7wk1k5SEN+oDEMW2OHr/eFBXKW/rNfmR4XdjCRA6Qrtrm2OcupGGa1VNC6S89ziijTidCTyUBFeO9r6omYXQ+4hYhnBAj1/XR5wr3JkeR+hMVQ49jifIvUBLOgxqUwKBgQDwfXAgRSshXWg8UsBDi7IKwbrWrCrK6aqupg+l+W149zG6unw3PSRunZGe05DYPoc+tzhvqwTSqoCQ3TJ2aCTp+/UpnYN4M7vy61aUcgSAwsQhVG4csfp0nVbsiK/J7A7+rym7ck2IQ7Bx3GDnRe71mzqBrIHAKb7Qa5BuRq8uWQKBgAkyKKhzFlQ4ZYYQLc8fMadvWb1kmStH2FilrSKuJaRDO0vydHFAPeBYP/KnkZwVdSPTX5vbGkOXT/f+XFO6sjDm0cjiI8/xR1WyueA2B0kVVHrI34T6VRAKaRkniJPMyw3jiHAa9TDQ6dcD0F50bEl7TIqhok2lBxfcnD88Zi2LAoGAeQnGHrhr/BkSNk0xCGiyIb2u3HKnwapdzTm85JAGbWv/C007iNIeS4INnNmHpIvYkB32pzpj/XNAJb0B2bZ6B2gouBDDv0fWGEZqBfv5A+zaJCKdkPI3FrZ+52Fr/V9vJJYKpWDV4X3lGAV36YzBUzlhwtVHiNL0kFE5WTsr+k4=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4QngGmdiupunaaeDYdSSYLz7y7kxkWBgCM2KoarH47acTWqQIYNjas94SqFRYEZELAlAQgtX+bQ/BvSg7iF0aikutQNGuIoq/oK5U8prglhdBhcLv8bF6fb+g9bVgyxe7c+lvulK9N4B6+UOzEPCcHy858ZfydIWEBX2VuhDK9erCo4Mo8uicTglF3L9b5MwxoZ0crIbB6RZ3Zzh0VoRRr2bP9tKa1hg8MA6qJ4Z0qINJupO5vp3AtUuguFrQbTn45T+0UEqRZxOaMFI4yO2CIx7CcE+lstgmAkup39dB6CDivLFeD54140xnqmvqJBTcfocG2TJ114dv/PUjVN53AgMBAAE="},{"id":"QmVTyFS1kFgrsnYgc6tANEiSjUgCERhvx6GdDjc16QhLDu","privKey":"CAASpwkwggSjAgEAAoIBAQDHm7xKULYNUvHj1W0v+8HN41vBnaH6U5EOc+XSJF+E/ZNwEHpJiDr0gnJTj3eycst89hyvo4FOFhCwGpl6V3aWPVxxWMm+xr3PjUnxun0grusPIRCHEiB1oiY6OOlnJGbouISM8egndKjlpDykCH8kNar36oREH9z33nYmsd8mFSDbT0B9i5lXAHluhEbDbor8QpFDihD0gUCxGTyu9DL7xJFa0xLXsa0x8RxO5Q1jPV0Fe9jMog17zlXAmyHc6Ikeh5MnIlr771BHDjbbXhzEj/FXgyNnHsds89gO1tyrtvp6TJZhDDTD/fkw6IQ0gjVfRIt9nwj9HqY8nrph8ZoPAgMBAAECggEADhoMhVuKYQqZGDV9z0QD/x2u39DZHqxEtTau59e1/9lCT0p6uTMspxWIKisak7rUcXGZuTbd7bxVVXurmB8Ru69+FmvlZNr5ke4zaSZ5rJNu+9SkLEa1kFnyRW673pONyacvhh2Y/yCYxhCCUDxhbxdziqxAx+dQXxqSUk2CrFf11Vg0RndoqoDBMb51v3U5tL+yVWnxQaeis+8pPwJEHx7a/zd1D00Vr8sKF1nALcnkeSfuH7Ha5Fxpam4x3xtkus7IxH+ZuUHTsr2/jdBZB8bEcmbEJ85sjkwzsFzpKQ2cEUmBioy+QGF4R+97o6QCm958++OGOf4uqKTpLmUIUQKBgQDnWkM2dEDTO72Y2Nh237aU2Z7P7+KYn7q3Rxu1HPglLl/AZGCPMQCzUIY3X6I5F/43wwDMbcWJFYwAwoDKT5Iqj3FIIwoc50Q1LHoiJ1whHqH579AeNAYe8TPZTbXc7n12RHQA/2R+aoPhkVZsT9KSsXqlKonfsB2lZvu73MvkCwKBgQDc37RB1/1Nk2L2kCk90FAPPTZwbdjdGDy6+qQApj+OxrRt/J1nvgQwyrU2aa9yrKqeLgAeozP0vW/nlEvd1rHUpo6910nFf2ft2Am5m1pnIMxyh7OK3OuFKt8uKdFnzMY6Ee4Z97NjytcOXgIG6XXlsuzoS1BRIy5Oaw7u2w8AjQKBgAoL82tD9SS3LW6bzBqtcGJaaPkis7ZpCVS4M83TqjsDHoleUoslV6Bx2MJC1uqfzhxhzYETK0betqu5xpoChphujj7Rtri2mlGs0sj06J1PlT0jWuhU3gct9lBzfXhQqwYdxuPcM2MCASQkiZo+hSh1qJvPkxkxrrfmFvx7hCctAoGBAJ5Q3oSfi5mSPkobFOSQrwHtT1sQ8XOwjr87Llik0UPGm8eGXVDh+2D5uyAQasiYiQcuU7miI3iWXGKyghq1a+vPotriOKeOZfEue/IFL6xh7wlcyU2MgmiGTUhqgwWS2rb+6RrF2Fh8KN/MdEl+B5qoQHVUnnI59FQTGrYMYFl1AoGAdgCOMz+1kpIsl36CgLtOasFbY/62IHWh27+9QhyIbdFhPFaGa0Lk88bFIRZOab+12xL0ZCk/5xeR3is2qWuFwPNUeW8q+un092uKFJgc0Whe/24Q9c40pSimP2hPUMqp/qh4huNyz+y3ClNMAiVmE6+75yWV6+d8tAe8rD8w2XE=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHm7xKULYNUvHj1W0v+8HN41vBnaH6U5EOc+XSJF+E/ZNwEHpJiDr0gnJTj3eycst89hyvo4FOFhCwGpl6V3aWPVxxWMm+xr3PjUnxun0grusPIRCHEiB1oiY6OOlnJGbouISM8egndKjlpDykCH8kNar36oREH9z33nYmsd8mFSDbT0B9i5lXAHluhEbDbor8QpFDihD0gUCxGTyu9DL7xJFa0xLXsa0x8RxO5Q1jPV0Fe9jMog17zlXAmyHc6Ikeh5MnIlr771BHDjbbXhzEj/FXgyNnHsds89gO1tyrtvp6TJZhDDTD/fkw6IQ0gjVfRIt9nwj9HqY8nrph8ZoPAgMBAAE="},{"id":"QmV1dRHcUPPQh2UEf8hJyxb1LM675eBf9gkQU7KNEdcbKt","privKey":"CAASpgkwggSiAgEAAoIBAQC2cDaFxSecoxvHcyEuo1Qm+1RVCqWggRmwXwdCN67BJe2n/xP4nzV+f4LoOGJWWfZ23IVoV7sK7Ur5biTbIP+P6p/n7EBlZJdR6IanjK1gof8Xd8pMgDDgbaw1h7v5mK7PBTtHTGmOQjPimZpTEgJvZrY5SUcGP1JSnoWzn2Nhlvwi6boaY48ww2KB1d3G7cpjmFNQCsbgUUNLkc4ZdpFe1ZcGYtybXbjcpHHlpgkNYKcXUT9qkF3BO9tAP377N8MCUM8XKm7ZhuUPB0gzpk/HUlHw/HaVcFDy9ciYDJevvVO9KGJLbzp7wxd7om7B99HdnwzRah/75UjhKWytwAnnAgMBAAECggEAYJ5FActMEzR+vb5HUH/HTW3FV9RvqvU0OafaHEOJFVmU52e7l+wZVIkLNxjFW54BXSmkrYHo2PyavEaM1lA/iGRlk1oXMJt5WDOhPJLQL2Eby73xMVqih/kNPrxH3QPzcEgBAN2C+YguGk/m3x+mkoD++I/KQZC/dGnnvR0ocMG0lnd6mJcUyNCzKaFNZPnb7A16lpNOe3OPAoT7tsmG5u45W2L+LROwGGLaCXcuOwdN9MQSpHWPPjgSB+CIhjBCDdL60nz0N/LX2hsl7bkcsDyqjsI8+XMnn8VXdkt0hjCYZfZVHS8EGgq9HJSSrfBAy9iIqXAFpcWYYcLSRHv8QQKBgQDYJuGit952LGf+xCRNLDbU00kLQa1Y+gaAKS7Rm4QkS1BEsjeixEu588QISlytynvBUeIz8j5c4fagAYhP6L85SRsw3yoVb/BJizl+mzC9jbQG0Y1vWkMHzE9du32vc4X9std7Ag5k4Uhfvj5z81+vyIoR0FlnWAXfMOvYPPLzGQKBgQDYEj9BMMPXUGBCAGWo/Jyx1u4jHhsRkD7TI6fFf+KSAtBJje8HGA1idAWZKd3v8QprICQzB6zgLyQRYVVgpagvGoDvZqXQL58MAP5hDxlbC0I4+4W33p/HstAkBt+5skew+uUqDl7oPeCKMRcKZU4PSbxKNLh0RI2dJZxJAX+E/wKBgAyOXacVJUAaHkRnYnB1NA4y49hajVZQ19XUDpswfIGbOmNzMtYqrvdzWJzerPmPfJbjSc+Yr9S3b5sl26RAsfMC8yhonko2gW/UPZfELhWkyKNowt2IEcK/NJEOViO+AzeUjv3fRbTIfWrKY1ha9+D/Nj4Iq1jcfSLU9a5PBBHpAoGALjnxKHxLWdRLU7OMDFRHHdESz5aQSfinSDq3mQxcJ8rMTwA7pDPkIS0bSYJohf32saaFuBkrRicvuzwsrOmkCrfL3+wBC4I0r7fCtHcrgIvvFgvRtt29J1c9KogB81ON/PIgdxft/BfI4yfPexzjT2gCDYor8Ev+VCGIxyG363cCgYAaNRo2uat3MrN55U5ACqYy+W5wy4N7JmHqWKIepHaDVuRHuwuI3eKioL85X8/aqzFGA2oiL0jFI82fCOqoKSAjncefTO1r0uN4hfrJUrQNVsIq18bkfIlqwH9jyJPG6dB0cTpaeqz6BUkqPPS6PmhK14tfYIi81mPCHf5q712raA==","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2cDaFxSecoxvHcyEuo1Qm+1RVCqWggRmwXwdCN67BJe2n/xP4nzV+f4LoOGJWWfZ23IVoV7sK7Ur5biTbIP+P6p/n7EBlZJdR6IanjK1gof8Xd8pMgDDgbaw1h7v5mK7PBTtHTGmOQjPimZpTEgJvZrY5SUcGP1JSnoWzn2Nhlvwi6boaY48ww2KB1d3G7cpjmFNQCsbgUUNLkc4ZdpFe1ZcGYtybXbjcpHHlpgkNYKcXUT9qkF3BO9tAP377N8MCUM8XKm7ZhuUPB0gzpk/HUlHw/HaVcFDy9ciYDJevvVO9KGJLbzp7wxd7om7B99HdnwzRah/75UjhKWytwAnnAgMBAAE="},{"id":"QmbRMpnAPh8enrK4n9d47eCgmb3VwL8BxUgETXNk9wBqWN","privKey":"CAASqgkwggSmAgEAAoIBAQCxZG1AHWVEICGrcW5QAACmboRWDVrO1k6CrfDunyLNnZF34h3Bs70mlIFzg4W1+tuY7NoQ/sQF+jX1O6TDlW7eLjHLGLcF5Pcbju2gw2VS8MrzxfWf2WyMBpSobjLpN7JgOCBop+EzkHeNh+pmy2NYlqkJglAz2shqrtOIzfgpZbkWHs1KH+8ttXes8/OORHIbHSWKTZJt45AtvEBuxxbGcU+GmJSOOJIAwhrjpnErXz5L2cgS35Z6+eitOpa9kueQU584bcLRvpydpImRY89BU4EqymjsLk++TD9Gk+G0RTdW0jaW9UUJWdwMZEaudoNG0RMdWUz9sJ87vH7cqoQBAgMBAAECggEBAIRrbUqBJwj122x5nllFxzZ2JX9/NJxWSgEvCz11uas3xlFxj7j7DWXxUbkDIxs3ihncJOSu6XF02DaaMWJ2+G4Omcj8SBO0WepOhYCOm+KQMVBMJBERYwAcIXih2otRsmw1OawmNewiGbyjCw11IlINFV4t0uUCmr1YiDwFVQzAziL+T8tRZvQIXGWQMewpQa4x+npM407l/NL7Z8i2fq5hal/Vbmp2EnDul6CF4pQ/T7/KIJ3WvSZSsPbai8Pyg8ZtMgrbxlZbWupsrS8LxtsMr7yDRkjFg37PkcxeOrQjJHC5WdG2haEi1RO359P6LgQavamecCRxSqXHr5qYoYkCgYEA37gETfKwgBgzXipqPzpHEGeqyPdYMoDKJxAFC1bydRsSDgyiXE0Y4Y0CFk98GBCfQdhfx8yjWvnhWQhNlcC+xwSnqN4KfOmPLc8db7F+dWcquxXa5ijQ26ecxzvbDGSwmcPm6yGQm6JUhtXQHUyzU7kxjdKPldInlld4syHpK7MCgYEAyv0lwRwMB6UEQjC0N/EtcNDzfl27y3jhzDMd3w3waRZssz3pNauBI/zQKddQxanzrQriNI+U9HfrHNFQKZzWGJuTDThgGy9/8DctASKhWITLH4PNo8retpM7PqtpSe10m8uiLdB2onnYmgLs+bYlmB0qdrYs45+c1ynuzr3853sCgYEAqDW4JtOfGfEYTmkd0cSDwjXwF4LCcdacKJrG8CvDyYE9Jgsh2LyvRIe9VgEVkRKOa/fArH2VJcZURZkUnNB7oTBCz5mD0T/bQ9pCi8StDKExY9Ge8QcnYsC4BKVgogTjM/o0Gf72t85qN6dz/1O4Ue8g7z2ucDu/QC8staN9qjkCgYEAqnePRi4EmELnYaK/Jh+/NnfzlFKpTAXXMgJXeqOdGtN/M8OxXqYJzKwaMJkmtuizv8VdFCDkSa2PX+MT++4/CelUxAxtAUeRnKzpeYOYldrnLi8k+gF7qT3ZyhZvVGs6uz92khz4FBhZY3VDh62Ewsrzh0AUUJARScxoRWzdv5MCgYEA2gQoIxEiH1AOeoQPJsmYoxWECcKTns0E5g+eEaOHI0FA6Bv1fS053Tn8W7loeIq1t2qsD3O/xnrlybfg+s84ZQUkb8ELCoHyhL2zS1QouRL8X1BPTByYt75aUq9j+D6OJ113b/RvPvbHIVSJ/OJR83HvwErsi/fVoAQYUR3r77w=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxZG1AHWVEICGrcW5QAACmboRWDVrO1k6CrfDunyLNnZF34h3Bs70mlIFzg4W1+tuY7NoQ/sQF+jX1O6TDlW7eLjHLGLcF5Pcbju2gw2VS8MrzxfWf2WyMBpSobjLpN7JgOCBop+EzkHeNh+pmy2NYlqkJglAz2shqrtOIzfgpZbkWHs1KH+8ttXes8/OORHIbHSWKTZJt45AtvEBuxxbGcU+GmJSOOJIAwhrjpnErXz5L2cgS35Z6+eitOpa9kueQU584bcLRvpydpImRY89BU4EqymjsLk++TD9Gk+G0RTdW0jaW9UUJWdwMZEaudoNG0RMdWUz9sJ87vH7cqoQBAgMBAAE="},{"id":"QmZHtWFomPUN1NQzejuWhwXENC1vV8UPbMgvisGr3Sruuw","privKey":"CAASqAkwggSkAgEAAoIBAQCurUNlwA/O+b524a4v1FXVNBog9PQxodMhUMgxeNoeWH1SVP7EV+Acl46V9W3ENWi9BNxy0vgsWAxYHxlmethWRf4+sW58UOVF3gY8sQ9tPD+h/grTXr1k4HBKfkYiJK4vTlT9xoE2shJ/wOOKJ6QqOSOddqt+AudUl0r//2t5yAHLFlHOmJqb6N2gUuibJfsO9w8znSCVhSxlA3JHXm8k19RdXSfz837Ic8/3Kp+eUDvr8OrdrAZEhTYasNmcVvMyAiY5NT7Mi4Mx4fFrLtB554eWgmik6XSvbP5e0sF7j8gcBgCCm/jnAMFPvqYPQSwzi+5uK/F5djKUEObgQUlZAgMBAAECggEAJNRPU5LNQnyR1gr7aQZCGrPjlnNPUW+nTmzz6MpsYt2RftyA4vEMRbsGy0N2I1qHfOMbf/JPdToTUDkuZ9ca6gXT92/9taRw0uxfMEi80fFUEVm4+pwNHsnbozlAnE5Tk5HWhbNm5xtgnyMSVrzI9kYZ5kdxHR6Lm+BSZet6w7A4oDKHsZoreZ44A36YcXpRUlirGs8SyF6WamDHi+rnKTUc+NfeUJ4HTuxW100T+a+EGwFRKq3DaEsV2zUDQQgKJwSUSg6gsuanpW7d2BU/3g4FQNr/Q0CYRcQ8ebSzocgN8ekfHDp5wmQaHoYRTYay++4ZixXLKdaYPrf9xWmSqQKBgQDiPqIN4pEbforyxT5dIyKGE6AfFoR4niO6suYD3mzVCbOXnAvRCboz3/p6gTFA7eLCW8Hiu4dA1328UFlrIk5NvNw6gxQC6XNnp1cqq6aWlfqIOxperSXQk9A5iUD+kqcdrjPFck1nTXa2r4/+gSu0UhOm3gVG4YAIdKWq8l4zcwKBgQDFpmgwIhehuewF33D02LVCk8a8xIk7JtH/OIWD0FBKLi6I5m3sTdpwIPyLFVdQHPc0hgq3boeqFGOWR2E0fNzm1ja3+TdNt+b3Cs98eeOZnIbH30+U6ZNVJBaO2VO1rEI6awXAsHVKG7WbRP215u/sjKKanT46QV93zH1YEEjVAwKBgA2//rtXTLheDXXJtQuWChAZ+z5IcQ6flc5P9xi/GOAtscOCEVnH/JAQibmORn8KtiBX0UXFXef78MFUJMsVD6s70iqgMsQmigP/sJWATySRwuMqrGSao1S01w/YHqIaKZvtLWpPkxK3K4LY0qiGEzDnWBhpH+r5430gEb1882qvAoGBAJI6zqSl5IQD0Qp7SM/tsRVjzcqFQd6glwdTYvyl13E6f7VLfpIw+2awEQg5rtKXy5FxhmY2AWslEJTzlIhHxQuH+w3b9T2rzNaDigTXDTvuIY2ouvE6aoW4YHQpeBRWMN2VprjsFKrSm2cYAPleta12Uys/IPXqvd4dTbMkwS3PAoGBAJUWOWgYMjQqhrY99iDD75M33+G9fCmH5kZziwrcZsMQN1j1yxhZuqFZpBs2rTdwAEHma3hQul1jO7LHNqsDIZdNypOt8lVgW0NE9mFJOSVS2iAKfaQa+/fSWr6lH4LlisQ6TIi8OwSXDYDd3TJ6DCuxGgd+l7lF+BFwUEAha7Kt","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCurUNlwA/O+b524a4v1FXVNBog9PQxodMhUMgxeNoeWH1SVP7EV+Acl46V9W3ENWi9BNxy0vgsWAxYHxlmethWRf4+sW58UOVF3gY8sQ9tPD+h/grTXr1k4HBKfkYiJK4vTlT9xoE2shJ/wOOKJ6QqOSOddqt+AudUl0r//2t5yAHLFlHOmJqb6N2gUuibJfsO9w8znSCVhSxlA3JHXm8k19RdXSfz837Ic8/3Kp+eUDvr8OrdrAZEhTYasNmcVvMyAiY5NT7Mi4Mx4fFrLtB554eWgmik6XSvbP5e0sF7j8gcBgCCm/jnAMFPvqYPQSwzi+5uK/F5djKUEObgQUlZAgMBAAE="},{"id":"QmQwBqadEKfKChHBpWG54Zhm7LiRqGgbuxovYwc2LSnrv6","privKey":"CAASqgkwggSmAgEAAoIBAQCgvKgMNyVnCdGFJLlnjQsE8Q74gm/sR0w9cESGbvAcVfcySQi7YqzRJwAGTFlvhhn5xn6wZRt0SnL8LlvED70PoC2oh2blRuXhlQoXVMymBRmwYjiEVX8xWaVsAd6Nr0LthQSKa1pq5v/FgfmLbuiUksViAiea31ey5jJ45mRiB1CWUvLIJsqMXrAp3QyELQV4MWnc1LbbSIjJpJKukCh3PqBu50IMM/F2uMxzgQfpl+Tmcxf/YYBhcL+M1rbglUkd4fjbRsalrX1qOTYDqcl7PzSq44L1cXUHTbsnHqMZMI2ZwkjuZpJ9gzqGdWwWlQipk9KKx+q8bDqorulAWpVdAgMBAAECggEBAIGJiKTPjIUNlJ0Ii6K4ODkzTC1acXipvPeGkrMElN1BKoaL9U2OqFemZo6s2SbXm/d/0Hm4AfgmR6SUd7/6LxN4s1MuS+5axMiXpBpJbOQfGCTeA04i2O6coIP6vbDRnc5tdMtY2PVuVJKHvORx7V5PKmNh+SkG9VZnziVBB4U3uOqzn4nbD6xr5c9upQ8e4M2MkbvX/uwCVl8ogY5p3WA0WbC+ye+PsnIz8CdILkKGxQeaps3zha/RaASfIU5rd9JzY4Y91YwaubYvrq8KxLDOjMPOrAl6GPSUdPSo5CYaHIwVC2CVuMx9IKxo8amLGoJqtqNikXCZ7hREhab1JsECgYEAzgr2u/nKxP96HuHME7yfegzysfrBXFdonx+qu5N/OU5tVFEMECgkHfs46IfRHNvEo6dv54p4BnQPVLHV4VwpmTNota7anbdOs13XHGqpm6EwUrkh2Y5Zxn2UE1pI0H+pAQ33gMKMCQlu46RP+X8kZ5iOnsVu2wyXCpNiYasOpnMCgYEAx7WQvrCbBoWzsuJfqmKPj7OdLi/kEnMeBUPsMAZN6SOc+ql8m16z6r0nx49INDYgxT96tHyKJZydP/CbDoexiBBrgbYmErP4235DDjQjDz125FZJTSdQl+YuF73M8GJ5zzYC+J06z7R/JIF4NImmvbH+VXASxooqMQXGoFgiEO8CgYEAwP7eDEc3Gn2fGwwvhxE4ZC96qSGTOvaZBeu4lcjb84a1u7PK09t1sSufK5gEVL66Gvz8Qeqe8JSHw7Jr/5K/WEQiQYQOPIr2SeaNVo3GXwOF6waEYW8IQndgWxxHdh0N5fH6Mn6IgvJ2Dv7a9n2UUQFCHhAa/U12sS5KieebamcCgYEAgRqYQx4qafH/NHJxg9WJl013HEerel3fF2pMgtNghIoYTldJDawQRX0Y/UJcXBxt3PXnnkwwhpYTdiVWJ/OCykJLLaSSQl8ETuj18nW0AIMsm/pIEs+Ko1gTjheOKAyRUGpLS2thfKrj0ra7/cLK/zDVCWiVSrJBCkYAnOG8zBMCgYEAiMkBelGls7+yjGcHywRCzNrur78BVgaPS98d/8KqkIxJil8t3R2O7f5uKhX6BxfQyeFGVN78wqIQOSSDzO+osUZJMYwF8Xaz/PVue9n191UOA4XPVY4rY/LmjU9aEuCRGN9K79iBuSvfLxYHU1LweOCD+tmyKdZyLbmaCFlk2Xs=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgvKgMNyVnCdGFJLlnjQsE8Q74gm/sR0w9cESGbvAcVfcySQi7YqzRJwAGTFlvhhn5xn6wZRt0SnL8LlvED70PoC2oh2blRuXhlQoXVMymBRmwYjiEVX8xWaVsAd6Nr0LthQSKa1pq5v/FgfmLbuiUksViAiea31ey5jJ45mRiB1CWUvLIJsqMXrAp3QyELQV4MWnc1LbbSIjJpJKukCh3PqBu50IMM/F2uMxzgQfpl+Tmcxf/YYBhcL+M1rbglUkd4fjbRsalrX1qOTYDqcl7PzSq44L1cXUHTbsnHqMZMI2ZwkjuZpJ9gzqGdWwWlQipk9KKx+q8bDqorulAWpVdAgMBAAE="},{"id":"Qmf3RfXHY9XHn8qhvFbjCb3aoc77Lo77fXanEpHum17bbq","privKey":"CAASpwkwggSjAgEAAoIBAQDoAE2wmrt1TI5ovQ5m3C59Sbo05E3T3Rfjnq4390V5vJ6hbjZFvOe+U8CLEbBI7unxn6QXxg60pDWl57OVdElbMFViEy5JdG15wlOTAJkAOrELrCwWh6EqMPY6J74bDGfN9bdUsAZiHvo3lSufcjhkH+Qy6cmZaSQFqfKQdmXTHr/2dOTFWgBt7nEK6ZmR86jmMdDUMgAaEMQaIzR7OnGY7DEPXafSGFAMxJS4+4xAsQa6l+i1RmcirYMEHzU17Tk3xCpBejCGY9T6VQui3nYwshXTCeaz2ZZJW0hYiLt4Uhq5SQwyIhRy39AVRc9EvVVPprmvEdh5zJqsV6R7B8d/AgMBAAECggEBAKtCHhdyAVMzciSK+qiffInr9NK6fWEZ4lqadkzF8blNrWPJMbMFIaTzujxukE0mX/eHOuB5yb07Qob3pS98KfJwmXbOZeq/UVkCRCNN26O5r5vIOrte329/uSNw1vqsi6wT6INRdUTVTSvep9cJv+rZR+D1h2YBoR+1NmPBoqTWtERJ6VI1EDTEx9fq+xaLtVlVfWL3+lBSRgrvnoi8ucTUPZLyMmPVlrhABZ1W3cYV7VwRDtU78pzCaE6OfYQBV8YCjflWnLV8te9Nt6EV9Yim3tWkrJ9bNraUOsb1x00NDjkC8RiSUqnPjFyOmtK4H0oGzXJSLOm1Ln0dT8wZBiECgYEA+Vz6HqUxHP1ebdITgxX3bpwP3uWYUYgWONQJrxj4cl8bVXO8ae/Llr2wpxyBhxHB4Rbe4UI7hX0h9lotBdwoocI122dsouC1ON8NGam2zPE7/jVBWbzKXbd8Mnjaj0UQSAph44WoupaqzHYyRBWKFXDB0hPrGjpLF+8wBKdbHPkCgYEA7i0IBWGrH+cyRdnWBN2XTkE6Qv9zzCNxf6hTRutOH27pjqPFBwfL6UtmyST9BnrkayEySb3k63xDxz9+qDLxmorZCXUd317/xr8nsWEFlxK2fpbgBeaKLXrGDHgu4ARfAUq0WQgQr527jGZSzbGMy4GaB84fZE0QCQdzZWZCfjcCgYAa0DOY7fxc+KWZu3gbpVYIz8EpH5kEU1TIYYx1BoNYkP94bcx5u64xlE4Vegw3gOJPGM14fU23KeBoBARJWAYKdr6hy/ozfWQfLGTVOJmOafYgfIt9Yb6E20EAkFw4AZ4hkPQ7qdidz6un9qGrDoHdklVVCI1EMbZ3aWHJblFuyQKBgFM4UswmkzdjffJgKeKrVnnWWrgcL+bNH5Q+SVLHVxWumFbprCdHtxz95p0zmmFQQU2ZXMvdWQZa9gfBjMzSeA7Kowkl7dklhXN5STI2N1DRDgjvSLlnEzLO6jJjL+XAQT3fT2KoacQi9ewZuKb8ebiFwT8m61sNfiPZgZ2VfHhDAoGAUO3WHxmVc8phtNa7J7uTQb11Y2Fu8Sn6KG/gX1BdWMK9ArdkVVffhHvemmukxgNGHLXWileEuxE1WBOWXVZHKsYMw8cJVn+DTMqB5BJnsIYKIbL9eKPUKzUa8XmLJ1vX2jVgpB/kmuVdziaiHFkpo1OYWqJ9GxArllnYfycmAOo=","pubKey":"CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDoAE2wmrt1TI5ovQ5m3C59Sbo05E3T3Rfjnq4390V5vJ6hbjZFvOe+U8CLEbBI7unxn6QXxg60pDWl57OVdElbMFViEy5JdG15wlOTAJkAOrELrCwWh6EqMPY6J74bDGfN9bdUsAZiHvo3lSufcjhkH+Qy6cmZaSQFqfKQdmXTHr/2dOTFWgBt7nEK6ZmR86jmMdDUMgAaEMQaIzR7OnGY7DEPXafSGFAMxJS4+4xAsQa6l+i1RmcirYMEHzU17Tk3xCpBejCGY9T6VQui3nYwshXTCeaz2ZZJW0hYiLt4Uhq5SQwyIhRy39AVRc9EvVVPprmvEdh5zJqsV6R7B8d/AgMBAAE="}] \ No newline at end of file +[ + { + "id": "QmeKrNsVh4HjQ6FDbfKvpVpXFcASpP8VYLDQ4utH8CHNcb", + "privKey": "CAASqQkwggSlAgEAAoIBAQCwgtRHpXpLm3P58/y8u2OZY9wOEMbYmxUO9SwUk/GXMI4ugSa0wD5GH5LK1oXD4iDG/Qv6tTmWoPsyFzPiqwG3nFlWwNfPZaFgxPqH+YXIfYtcDtFYYBBbpZpkO3IMXPCjlPXLY412nkYsr3Jhcoj7seQwNHGkNtsagmtTyuW7uQIXBpFRyU+/K0iNAJoRjH/CX2gKzP/yHK4pXq/hzOyZMWUBlpd61M7Fm3qnNqTMqXiZ5AbpPTjpBnkTGs65O6AQf2fZUzGrnZSqCE4CZ+8yW3EF36HHRMPwtfLIXjc+/PIoTB1aFFKx/YlLq9KgDh2dLoxYcdx+cSFze4xQbsoJAgMBAAECggEBAKLbIucz64Ek3b2p+u9bacrV0uv7WKvzcluJo/EcixP2zchkiwTMNujWKscRZPvVgG54fPwFByxOPVX7CHlDVGNHUzpy1OtbFqlFNO4OL/xKsTDvzwFeKNmWErwWCOUVP0V+o8XThWsDHzYNblgwp+iqOiKr0Io1vrRXzpTSKGKpCNuIST77B2yDrIyWDZwQQpa+qB0VhrUSIHfq5GazjDn9WfWZKsDErQBUdSOzQuRTddGeGIphlGycwI3J1IaLXUMHwEuNw9Le+HAE6ReksHK3k9bhkLzw0R9N93wXnO3aGRfcsuUKe6CKhU9rBLD47RJh3kElR/rlKrTqD6yz+gECgYEA1bUIY4DKaK04QoSAVxluN+HqcP2imAtU25e6Bm5fxmUmOLBfjKgtRM8DoLEEZWr5x9b/ZaFUi3nTBW6hPVkz78WBWVnNuUXU+mm4sho6eWmEc9ixoPNGM59EBnwkpBLs0mVoX1aq4+XA9Man4pMILFAYAIiyiodECGpkMOmnhkkCgYEA03FX/nG5KK+nndpksNm82g3hHr9x1Fqg3agl6nYW7yZonIT0ZvHM0Vf5oZw8k0l+TKI1jF5Z7zROFu0TxFJxy3jCQQjhNppc68NRSyaWcoXZgXPkyo3Rd0ifH4EYWT6V2skpHwNXrqehnk3QmFDOfTydbONujZaTK4uDBJTqJcECgYEAlGYKtjXn2Dp498/U4ya4n7uUNfQapSQxP++jcPXSMg1pNjJbl2vtS3vJVBu4JsjbPIcygJs//z6QZ5RECTrZUuW+hZJTxhUU6PLjG2IVOtwn6t4Nsutb01sEPNCWDQ1LQGrEyHeBYZHYpOESEcbyBPt1P2GtQX7eok1m14RZHWkCgYBQALnXp3X0pAna21Bv7eLb6pZ8SNmsDKKDW+UEwqvIzxKT7mZ/MQxbRVtJv9DXtd4Y5uM1+lky/epyGD/7b2ppH4b4joCQZXW1KxDuS2pvaJ+0sIIn9eMErDCw/eqVke2ne6eYeW5KwliPikfb6e9HSZETKzagUhHbTw1CxYmWwQKBgQChCEwU4tCVKmDNvj2nE0pZH5UuqjsOTvxMCCTZlmjPmvMTQaH3yrgb47EdSOkiuAcNmk6YwJTjty0OXm3kEp/GNhVgO5bwKRpuTkvYCVGgpAAqn/AzTCDFWuRgnhNSCtj+AjCIWvZzKdXBGQCIj4yvUjhF5DGj3FIsT8/M+Bw7eg==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwgtRHpXpLm3P58/y8u2OZY9wOEMbYmxUO9SwUk/GXMI4ugSa0wD5GH5LK1oXD4iDG/Qv6tTmWoPsyFzPiqwG3nFlWwNfPZaFgxPqH+YXIfYtcDtFYYBBbpZpkO3IMXPCjlPXLY412nkYsr3Jhcoj7seQwNHGkNtsagmtTyuW7uQIXBpFRyU+/K0iNAJoRjH/CX2gKzP/yHK4pXq/hzOyZMWUBlpd61M7Fm3qnNqTMqXiZ5AbpPTjpBnkTGs65O6AQf2fZUzGrnZSqCE4CZ+8yW3EF36HHRMPwtfLIXjc+/PIoTB1aFFKx/YlLq9KgDh2dLoxYcdx+cSFze4xQbsoJAgMBAAE=" + }, + { + "id": "QmQmLrVTa28T6HpVZCLnhpuSKhvqxWzMkxioBC4JA9xreL", + "privKey": "CAASqQkwggSlAgEAAoIBAQD9BZNYQBwJHwRX5kXxdHilcQ65mSWpNbjl5NBwB+A6tMzrupsZqci6W6kPj0tq1Ez/OjDD8RLRLTFCLmuYokIrP+j+2dJMa3muXrTZdIQYK7l2SMx4854KshJyl+gF8zJqgp1fBVNwmGNoa7hEhp0idnf14yo0c0mQy2XDCIRO8f/vQ/ol6/4RiU7nCh5L3tXPDTeZvSkzH6FBGcnBc6GWQWYDbNxffpGgx3wx9lpVceydnpMmdSBSog4nmNzHRK9q2EJuTl+kY5ljrEKbrWcc2ZzQ3tUD3j/X1PXlK+xVyujZgG45XiG2tPx8cn33aIyW1FCcVpPN91xzRZjtuSFlAgMBAAECggEBAOtJFwaWvztgI3rVwK7Svyohoy7GEjZypZjqmUXEnSgaXwDKS1anAukBOYoll10lROCGLTjYYcYwf0Dqx9Vs9ZkgWjs7NQspWkLjAMvCRN/xMQqkkbfb21hY1TzIs9NsnrJa+ZqV6kym+LHukbDveQnGHrGB6ApUNtG1wtCFh/ubxyrSk050AVVvN2t5otNU0jXvxed3Yj0YKe7uUFTnrYPqjNILLE6hk5AfwHIhLlyI+YiL1NQ5n9Oz1ZGFYD1YspD60C47RPFMiIGr0zv4A9ld6D1YyUUssRfVUW9QrnVuIzJK3IpK4CYzbZjy05JoVUFfben68J5gjNF0j7UezgECgYEA/32sc2NaDBVy/0FsN/PqQdBEGqSKyL2aHxzXaSbRGzFePJfdaRqMCdCjzRRu6l5NsiItN8j0HALG5wzyt5rP5hEnPlS/0+NVrFc4vUVvJ543sVyMoWKs3lJLvJjqdOqZ0nay/ZN7/UaYPCcBG1Cu2uXoMYzu82oac8IXPAHBOIECgYEA/Yakdavz15tk9MvxeX4LTBB8uIgtGzFONxNgN7fd+72oymybOSRAAriUsiLYLQml7WTZpiLzAnMeIpdTDaBBdHpHSyDdMrXrshqz4R0M4OtxX+Dg2DpbZh1r2PqE4SClApLqnT3MSeldoZIi4ryEyuaB0qCwYABRyob5wfHGluUCgYA2u92t+qLX4+B2/rUIZ6DP6KYubwGsb7IM6Ejdpgs2ICVoJcCxO5a0o5Xz4WTBoOJUPy4gvVROi+nApe15mBjh2NrnZ5CQ/CrmeOkW40Ek46havtEB4fnWxxqilL/lvDyn8fX2jRoK8Iy8lj6Oc4KCMq7DrsWiMCDqm7IOkPGsAQKBgQCGT+6Q6KgmcYzqo6EqXDxf79wjZwF+hoCJbRpsalyQIcpqc19Ixoui5WbheNROB+5/9g3YlmtnwNUWPlUhwuTqcF2Uz7RgSUC0+rCRcnKANju6L8juxPvm/W9FZHNBUaoZ0vu4kiTIg3m3P1R8CqPPWo8spzTJp2GtihSHSy+KgQKBgQCISKYS5Pj4McVRj9vEJYi1YhEeKVlPCv24gP9KENhFlJ6VsKeQr4iJCIMr/eXAZwJ1gfIN0VGgIHQug+7wt3PlYXP3ML/Ti+D4kFPcLPwzfJ0baLqhTTlTWYXYwcjtyS1aAaG2PF0YCRgtxxur/Rrn4Qvq6Z4L9yo7UdUM4X/yGQ==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD9BZNYQBwJHwRX5kXxdHilcQ65mSWpNbjl5NBwB+A6tMzrupsZqci6W6kPj0tq1Ez/OjDD8RLRLTFCLmuYokIrP+j+2dJMa3muXrTZdIQYK7l2SMx4854KshJyl+gF8zJqgp1fBVNwmGNoa7hEhp0idnf14yo0c0mQy2XDCIRO8f/vQ/ol6/4RiU7nCh5L3tXPDTeZvSkzH6FBGcnBc6GWQWYDbNxffpGgx3wx9lpVceydnpMmdSBSog4nmNzHRK9q2EJuTl+kY5ljrEKbrWcc2ZzQ3tUD3j/X1PXlK+xVyujZgG45XiG2tPx8cn33aIyW1FCcVpPN91xzRZjtuSFlAgMBAAE=" + }, + { + "id": "QmTj8FRrtWcyw83jkoVAQtzZG9wm6P1mNaA2PG14H5QugM", + "privKey": "CAASpwkwggSjAgEAAoIBAQDEaSnbCRWJ3P2Sg5zLeyGswWDfF2DtsneFAbDcoPC6NB4RTPaCTBWCaKiqM5+eOQg/VUv/1bfuvl4N6LGnSL2Epg0zQhih3vvI0Ent9+Pay7COkQ/aVtPpjAHCf1X2wdqz2+1Y3eT1mi0VPx2bEqr3Mmgn6KMkxHVGFORR3IbH7dxubT0HNnwFWhq1xuteSJtyGqDcMJfdezNft+XxMTiHq9gyAX1LfuEaEii/9QTeSK1T+Dv+wpPk+m1aXajkxi6ZeRG2gL7J3+iX2hZE6nv9suHMQFcQZLEnWiqFm4hzgSVzJY8XWBYpg7OhjoaIBjS7kjcM3MlUnGHqyUjlLdzZAgMBAAECggEBAJCWbjaNkGqF4bGa67ErutWHIf35IuS4zZ1/aihw3eXlegD4bDvtMKRo0UlCaY27pGOjMihqt1WslGae1zEzrjhZWTxTuvhmw2nIU7Q4GWe3LQk2prnv016JL75zqN/XQ13GWApkhyl6Txfl97Bx5KPHWr+OVZM6gK6PsKjGs/22xIn69VBXe6S7rMteSQD6clV+JykQcVfyfCPjubfYsLLrGbG6Ma1pAfIfwHXtAoc15+hzsmrNCU8FiyB9YDZEJpVjY/y3WtW2c4Kv1a4ivpZremkNg8J5Bm8YWijqKYNp5YPbNpWuFgUb3Ak7/HyU/bww1Hm+bRx2W6LQE0Cq7kUCgYEA9BuinSYMeALZjxp9hv53feCNUisYDq03fAPCVUR3VhMNul5cnyVA87m3b6FTRUg/nReNQu5fOswWqyaNdFrF/KS68W+SpsOj7+XPNyvFC6+r7E3NVbKf6DvB8kgyTNNAqHZAymnBbEwehSfYl4VkLzKA9YgEz7M0hs/QinFmMocCgYEAzfqtuEat1sHGWf+MP7TqhSmsnF0OvQdNGj/ywUF1RvaZwGPQvjU2Zq6N30zBrdHQNNIZQIakB9eiW7j9/+9PoWfXNkgRr2wBsRY/yJAP9eDNkUWhXJoczNM2qbZt0g92ojJUOUR+pMvb1XURTa0SiJ1fFZ9hd1NVFPQDw8KHbZ8CgYALdRCY16RWLawQb2nc4Cyzl8qKRMOITNEB3RtN7Ty8qYBHcw5d2d3GkrztXGEQK2fw854Jui52EAYwWkrhXzFTvleX5Wi4sw4KwHqNWFyTQ9eJ68zrbMrcl5Fu0byqpm+ueH5qAbS2ZIw6K7iaxAQDwxcSbyPqq7qXaUGcfd4SGQKBgDGSDpS2jceNQssN8Raz12zMprnKu21KgIe8tBNGSY8WcdIXM3lTHRCBGauPsVy/jbfoiCM7Q+aPQoeIcbhSQ3u4ndZ2aasp9w5DKH6ADRdDTrWTaE382MmCWzlkqfFPv2Exc4uErGXEmdc5ITzsVfWCygtOHAq3h1ZPk0hdXHJJAoGAbTL5VIqTtPBY2Gv0aGS5fVllq7FZ3eQDjBJZD17PJ26A7Otc/m0qU1JRoMsH8jj8P/rCQj0O9D/+FwrIW8JVbSRxuT2unXyKsEWFOxg7ehs34zrJTEScz4vUfraBgJYBoxXiJDlJs8r3Bu9n4YOpoUSaRhzdvbro3STIGe6SOfM=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEaSnbCRWJ3P2Sg5zLeyGswWDfF2DtsneFAbDcoPC6NB4RTPaCTBWCaKiqM5+eOQg/VUv/1bfuvl4N6LGnSL2Epg0zQhih3vvI0Ent9+Pay7COkQ/aVtPpjAHCf1X2wdqz2+1Y3eT1mi0VPx2bEqr3Mmgn6KMkxHVGFORR3IbH7dxubT0HNnwFWhq1xuteSJtyGqDcMJfdezNft+XxMTiHq9gyAX1LfuEaEii/9QTeSK1T+Dv+wpPk+m1aXajkxi6ZeRG2gL7J3+iX2hZE6nv9suHMQFcQZLEnWiqFm4hzgSVzJY8XWBYpg7OhjoaIBjS7kjcM3MlUnGHqyUjlLdzZAgMBAAE=" + }, + { + "id": "QmefXHQVduBtXgZgNHp9g7P42bh4LyNV5egghVCm9dPsdC", + "privKey": "CAASpwkwggSjAgEAAoIBAQC0GU6EP5luaEJUiK/BWs2oFI3cQ8f5gpL97ClmjuCIJ2bACnsupgxI5HvnfbVVll+L+/BLdA6WAFzwmQz5GhDQyN6mauvZvYM35uKNnionny8kafqL39nXFQZ6V7dNLJyE6BjRwGcZR6pgYpoz975QJPY3BCQuV69SeNyOvVxUl3+AxTH/xuk+LAxHT2QuAOn0fMd5RDJ8YbbUXQX1ZTMcHn+6wuTh1W+NU8DbgckIi6lU5W7WhoPTx51l7G14/sO6h/YwVKk9MiwiX4sdZw7vtzxfSNPYsa5V/MA6A4hRtkDBc1kZEwOin/u6qJ4Bd/VonwVrQfBKQ7KMbaJnaHAhAgMBAAECggEBAJkJ8Ch9CtpBCixfQkph6ORtIucLSgECLfpMFhoqSy4nnS6POYG4ZDQExC1AKzkhYOvJGLZlVoeso01oIRgESEnCh0CPFv+eBMUaE2YYAuZMZHji/Z6ih7A3E5wesYqI4DAoMf77KiDXPzZMU5DZcwUTg43YnQ4sQLUqMvGwg6b08NJNZsobgvb3nNGQQmZP8e+m14vF+kdoxajNzl1bUdRAesDOcB/WJObxcjwFdimFteLx6aexKz7EALBw1AgBWd6zlHX9wvxRr+EL282HI47lhuMQ7rMyVE0nxKazclm4SNjQboq56KNWSr+IFkfgZWwGdTC+M+UESOlM9/zHEOECgYEA2gvqPgMa0nFsxGhu0uYN5ZCAgwDgh7hI51S3W2vyPnMKnS7pWRs42BTgwYgr/16b/olh3QHRqdKVkg3jyWx3gEttQyoa5IhTCDu8Ey2Vy/ZGaZl466BizRWI3gKmk1oc7zMMPmgooqqBNbS8pqqS4M2YBev2RxUY6DZDtkjY3+0CgYEA03J0ZqT/zWwZjh+0416S0mDf1bqMvDyosBXk1L/QKmsVMe2szg+/Kn3yCleTJ7rRBvDg1mZSjo+g57Kahmw/Uqiaw0701p2XCGFzK3mlVDGxOf9WmL1IJKdNhzk6n2LqJb40UiktSzhjst058sxCWFo2Qez/iKWiK+vD2O7lQoUCgYBpEobNXTemo0vTlbOZ44xczHZxKpIINNDE0PkOkAUK/Orndbyy2MBauCzadeElzRSE4vUKyB6dpEG5QQI8hQ1NAIvlarvM1riUZhyqbTQbNlEWAAPaH86cnzz2JvbYWPtKdSH4TL8QK/PZyn+CHP5sYAZbDCsAc/UtqUCV7aK8vQKBgHBUXYIqJbr7spfOQEqJ83XQARmKATn+/56MAwUC4ZOnT5s2qJbPxr7MKclUvNheZq1JSiicMz+JDfII8A55cyp2Z8FaMLfTAGlzW7u85BfWTlCBVX/PDoRqXMV5oasK4Vkrsp/zQBy12EEtRZu254gIZMqhHR963qx2k5SrVSABAoGAM7zx4KRdCnGe1RA72WQrJ15/rvSzGR5O7dCzRvWcqur/5taRHy8sHPk/8WdSI4tESfIJRA2mtd2565eawy6iWBuqSnWCra9VqRHmpyJ59b2tSD0pnJ//lpG4vceWi26+F+c+nQp6MuRzWo/zciT69Q6TJXFTc4lzK537Bwz5xsQ=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0GU6EP5luaEJUiK/BWs2oFI3cQ8f5gpL97ClmjuCIJ2bACnsupgxI5HvnfbVVll+L+/BLdA6WAFzwmQz5GhDQyN6mauvZvYM35uKNnionny8kafqL39nXFQZ6V7dNLJyE6BjRwGcZR6pgYpoz975QJPY3BCQuV69SeNyOvVxUl3+AxTH/xuk+LAxHT2QuAOn0fMd5RDJ8YbbUXQX1ZTMcHn+6wuTh1W+NU8DbgckIi6lU5W7WhoPTx51l7G14/sO6h/YwVKk9MiwiX4sdZw7vtzxfSNPYsa5V/MA6A4hRtkDBc1kZEwOin/u6qJ4Bd/VonwVrQfBKQ7KMbaJnaHAhAgMBAAE=" + }, + { + "id": "Qme4AkND5vpgCGWu8aEa7QV1mAQJntPTAXdqs9wXZvHNnB", + "privKey": "CAASqQkwggSlAgEAAoIBAQDYIK6UrqLTawgQiPKupGx+uGDKuswrb77kAxXlK9nMwTp0LOFaoJkTpyBNmnkNbYdmRyNQjRASFlYHiMxlucDWw8eOg9RqPrvRffPCqaniPnZ+sNnmviOOdsg2jzyIw4k1nmgVtI8hYWY+sqh0x7zRwfZdv9RlAuUon4yky+Bye8IFLVFrJLnbi8v2fraP/qs2GId6N+gLMpolRTZrBvPAwy9ojTwQRHB51J4FYsaYP2P4dDI2lnI8p9OyjMQzYYnLWeR5lhrwcCCnGAb4CM0JVuqpxfyiTOS/UUr72MoGMJzttpwprDEmwdDE0CquvXcDSKGXV/hmyJxNCm4Kyim5AgMBAAECggEBAIW9+6rAQrxHsG7jACI4V3c3S5ZIb821UdAxZJwzPY0qSazYlP+LAA8cTa4YGC7KB46wGNdg43/92HDDI5ETmJSOBHBZfdndRkaGBZEBgsIhWxxc/gp+wOwI7y5sBs6vCbpncwtzd1r1tdq4neveKic3Ouiaq6lyWDKCQPs9un+8l7XuUxxLI/sbZH9+SD+f4w1LhLkgg8zH1fE+ylPL/MhvN0Rt+7S835qWnmU17GL0ZTv8q2Jva4tG4QIrgm8k/3K2W82ez1LzRDbOcgaUdUti+a96F7t6agKk5jiGIjldTBVAmu477PkpEinukGJJLG+bWQo5sblFDDegW9snbk0CgYEA/owOoTbmKcVFyiZo4D01XyFLIR0+NsUH+hYr2N+p0eu4OHDAx+CdfuXBaEgw1nQNAfovQCK/WBcsUGdm8LuU4PxPm1nSS4+r3xgCNsYHWKzozHUBHtGK23G3/ifqHsKJ6v2kV74RtrrerkoRG9QIhJXvGfDKTnczfTSioiJ3dEsCgYEA2Vx8jbwBAJ+I2gubQHEl7xXqgZwcUqYsoMWS4dE9gNgRBSxSmrzh5+EvpCGzB4xxADFMq29CBAEey1fYVlurKXhg1PguoqsuEfAG8TjDYqY+7qbfphK/PKKj5+PBWpAt+T7j4EzU0PUx0r20LgNrGymc0O8inKRj9RG/n9mU74sCgYEArwmMfduMhii7hvjGoXw1mUcBrLG5OFxg0d4xJJ8Y3dtkAJu4VTnl038WcilCPBXtY/CwUUATp/W3aUrRhLTY+aVG8KLhc0hGfdC6pQOjxZyRMRc8P/8Dp0cZqNG1spU/1bG5MwTmlWsG+X7BKQuzF8kqdAIae76IT47ETw24auUCgYA0qUmr9S6w/fXkDgfRNxifskmcmTvrckNBuFx7xXwHKVIGVNS2lKiOy2oFc/yyCX6fM1KF/nCaE8NoNKI/WfpAE+Ax+OcjliGVSvgDnWMJFYoNu/RXghc/E5BucCm6TVA2INyIzVSKQ72Md1WeCEn8zLonjKhwFwv4phItIkQJPQKBgQDs7GdWIXI90KI8rRePrYUeZ+bS+wIL8TCaS8ckGKxR55x6TEqiyrI1jXrzHBajtYQPsnYHLpz7N8T6acTN8iEuC8q1F6SHHTm+OekuoX4lduKBwZH33NzLtpTxCty05Z5JnNkCC96rhX3PEBDMJxFiJu5A9zR/0V8DPGIhFnI5kA==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDYIK6UrqLTawgQiPKupGx+uGDKuswrb77kAxXlK9nMwTp0LOFaoJkTpyBNmnkNbYdmRyNQjRASFlYHiMxlucDWw8eOg9RqPrvRffPCqaniPnZ+sNnmviOOdsg2jzyIw4k1nmgVtI8hYWY+sqh0x7zRwfZdv9RlAuUon4yky+Bye8IFLVFrJLnbi8v2fraP/qs2GId6N+gLMpolRTZrBvPAwy9ojTwQRHB51J4FYsaYP2P4dDI2lnI8p9OyjMQzYYnLWeR5lhrwcCCnGAb4CM0JVuqpxfyiTOS/UUr72MoGMJzttpwprDEmwdDE0CquvXcDSKGXV/hmyJxNCm4Kyim5AgMBAAE=" + }, + { + "id": "QmVCsZRC3hnK5H93TKrWSz8bPT9KDRThWe5KiVH25eXTrJ", + "privKey": "CAASqAkwggSkAgEAAoIBAQDpUgOsvoQKX3vaTOPLU14uN6m4XRmZGqdJ35KvlHtXugq0KauRWqxLKgRTfRO07OZzkOj6B6sR7J6PdPt/0HDPxM9jz34S2jHYtiW9gRL3h3dawBzmf/25xTmtVeZJ35CYfrgKJN+HQYUEHmhifWm3p4sI+Egc0PuW0pnYcHf4U8ZDLzKZ6WASj7tHjjIPXWaYPgwSwqWXa1UsfzovcO9B/jf3pEANuAqA/JT9WzRMVW1/8IC2uf1q3yOIc79AtiRb/nn7C2YxKFw3ZFJADdAp5oiKqdE2mnMfp3T2Qbv8w8Uo4alc/ZxW24dcUjc9nCf1GfgqydMqroZCdU9Be0RfAgMBAAECggEBAM8hzTtdQxGGZQiO/Ce0kkbLibOvixsdy2fMwId5dOqTsg7xc2uuKIjt+zs3LfiP26K7Ael+R0O5YaaPvvpgTNZWrZq5SP5pkJkIU4biALmZHL+HD+EdvV1flu2n9bKdc9u9m5r90ydbZkAa0qalGeY+KjNDX5mdec0SmBvK9OaRnrsmZL/wJL7BQHSq1xGkJko9DQrq9Z+pZN3dqLaSidBPvkNVxSosh76ySddrQPg6urcNWacTAO3n1321vqY/NBPUv9lZSZ8DNHX99wj53Jw8Z6dq2mkTP4WKqPg0PVmu5Th2zgvHc2KIYyNfZWB65nzrsEWQW4P6zDgnVYM+pKkCgYEA+sy7nbVFwp/HlVcg2jFuT6uHgjlANRsyKPc/XLaZK8ThkYAD6k7bc6T+BEsrsYNwTCOJhY/4j5HetnWugzcjevkcjGMBU3i/PC7TzD+3uI2FwxmlhxmhUvn+wlWiAPDfKlvzgQ2O+rNeHUwct9Z7VgfE9sLiV1Tn8u7eM6LW+GUCgYEA7ih/27TNV5tMMFCbVXZeZNE3Lo2K3haKzyHh6rcYAvpV0VdViB0BueaDybV8NyeD6mRV1ddlx7pmdjW29qqCnPt6OXWXNasxkV+wNCoxzWYjbRNoWQDYVWyJ7QaRbHGv9VfpGXvhH+uko04F6X9oEgPHx78REqDLDKrQjVq2g3MCgYEA0Gbzt/b7guA2fngqCmVs3rMQJTvhiL+SjMv3UNlCqkYtkjQoDLl+Tbhag8LbCeTImdwl2GBsERELWd6sJsjYqMT73gQfA/egIShgQqbnyWmAL0DOdVbs4QBvm5iAx/tf1dkWnLRhjR0jJYErk++cCT7ngo0qe5tIKmwnJA06SKkCgYA5x6GdkDPxeT/2yNem2Fz2YLvek4CJeTacIkBJTbqJnmqLq4CY0VDF5WM74MgOKYwbpdQdWXDcECEjtMx688tUgr6YiirxXmNcPbbmMQZ8KreVkwvR7B6QOxELbHiDsuFZyMZI5IEt2WHMLPXKPJrnI/uoPnbJ6jDZFegbI1L8eQKBgHuHR+HJ9x8g2PKQcoAKzcjakPz93X4CYJH28cNzwlu15PtgdxCgA8m39Zc/d+QScMQKmfeWe/ScprnRsKDfCNRpF4CPmINlGTkz6zhlpxh0Yl9B2RJn5fq8hSrDgQY+3se72Jb9+tLvuIBFR9NIFq9iiHRvHGKiVkkCLf42c6Tp", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpUgOsvoQKX3vaTOPLU14uN6m4XRmZGqdJ35KvlHtXugq0KauRWqxLKgRTfRO07OZzkOj6B6sR7J6PdPt/0HDPxM9jz34S2jHYtiW9gRL3h3dawBzmf/25xTmtVeZJ35CYfrgKJN+HQYUEHmhifWm3p4sI+Egc0PuW0pnYcHf4U8ZDLzKZ6WASj7tHjjIPXWaYPgwSwqWXa1UsfzovcO9B/jf3pEANuAqA/JT9WzRMVW1/8IC2uf1q3yOIc79AtiRb/nn7C2YxKFw3ZFJADdAp5oiKqdE2mnMfp3T2Qbv8w8Uo4alc/ZxW24dcUjc9nCf1GfgqydMqroZCdU9Be0RfAgMBAAE=" + }, + { + "id": "Qmex6ZndSe4SLWEZdEDuUKsrKzPRuNBMmNVQcSzYZP75Qd", + "privKey": "CAASqQkwggSlAgEAAoIBAQCkLmaaLxWh85CT1w0OVX6Eu3oJKXwLaYx+hHMwb1b0+FVgFi9nQrvA73d/zncaOCVMwZuPrLTQL3nNNdKgykXENptugh2Da5aBtXvzBZnB7cFZ1/qxv7KVUIthtVa7NTeDESCZ3XNkzQzFv4eobLYhu0CWWCScMV1p7PrrAJrQ+YG0un2dxOn3zO4uIOQdNlLrSVpZo3XUU+DHSdcacms8GG9ejVAnwu1BxDwxSZE58OnJKgmC69C2Cna10y3ttC0IbAZSE3G2UaTpdnOjp6ZatvqfIACtkGKQNu4nYmWWQ1YtubIhmtXwJaHfAEYTUxIhJtTsJ52AYLiEQsCWie4HAgMBAAECggEBAIbF6EfNOu7U5+vUbs1DdTY0R7tZqJAdtszsoTozS6iQalUUIRmc+E8qxGfP7olBaa0WiUJpvYvgTRYdhqejI54sTW9rtBp7qPMVLYiTImLFbyD3YL2k5U4Y7jHpECdhSg53ctGbrewjPsbdsNibVO9frWc9N+BXzZz0gg2DU5MSzaXShnJeeluYzEbAoSObYypBonE4H/cs2H1NfzKzBTqaoYpeboMyhlgbPhX6L30gU0SG9CtQAoUd4m1SUPiTMF1pUg6VnMZxtyTHhI53lqFtQyk1BJ9e6fTcy6w1KcHCSySZe3z8pazaSEV+ojs9zBspQceEW+ULCdyO/NaNclECgYEA0rh2MgHc4E8OTHMT6jUE1oHqJ5tybudTjx0ATjfh6qy3Ctz1GnlFjIglYJbleXJ0Q/AGN2ny85WM5fAatWcKUa5GKLtiBUgHg5XUlcUyG+KMOCfm07s2J9oNYxyVek1B1DYQ/B9hB6qlje9ak3BOHzyYN5kJLFHBq/NFOprrx70CgYEAx3XbL/oGpCZMS0x1N6I4oS/zldI7VoLOR5+7nRRPKIKDljf9VYIsXHB71YQQB/IzUR0SYdBRX5nLFZZGPtHZAm+Z1XFGAd4mqXdYxOVvOdEay/O3J2zITC+dntU7/j0jSf5CyshBsTfczpYRc5YzHoiYt5twRdWrkbs2rSNqtxMCgYBVPZdajVRbTL+c/2RL9tyZJX6iPQiigmNT3kh0W7jA5NM6yr0/01TmD2EHXKNBaKJNpTfujKZiENopXeKABUmwVw48heiV+FhJy39ugpHNo+xS/xkqB25V2qhVsodpLP13KdPfGZZ3snk8HFoIkIKZsjplKzwBuKTeuQ8FMQw2BQKBgQCHm7RbCQUs4zDp3OCw42TXCe/1Q4q+RY6TmaEHqcArzZhDMGvXj/ozGvLGdtQjYuLK2hYeVHCOq9QsKBA4M5n0EwYm3K15JZ1piE/dBhQ53dX3Jgq3Djmbup/SVi1ErDJq4yhsD/zBzTYdamAREuRMdRhHdLG/eQb9vKfuzO6H6wKBgQCAdNfJqIJj9FsnQ7tGeX9kOPidYQl+wUKD89/i8HCTKHCo5ftZjpxAJ5JW8abD65olMwpARjSMer5bIn0lvKCfuYqgER99D7ViqaVdhzbGljgxG8SL0+qMftt6RRoWx3YbAKCKQXq0Ns/vwz2kuRPSiEsVZEoW9Wbvi+tFCRBUbg==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkLmaaLxWh85CT1w0OVX6Eu3oJKXwLaYx+hHMwb1b0+FVgFi9nQrvA73d/zncaOCVMwZuPrLTQL3nNNdKgykXENptugh2Da5aBtXvzBZnB7cFZ1/qxv7KVUIthtVa7NTeDESCZ3XNkzQzFv4eobLYhu0CWWCScMV1p7PrrAJrQ+YG0un2dxOn3zO4uIOQdNlLrSVpZo3XUU+DHSdcacms8GG9ejVAnwu1BxDwxSZE58OnJKgmC69C2Cna10y3ttC0IbAZSE3G2UaTpdnOjp6ZatvqfIACtkGKQNu4nYmWWQ1YtubIhmtXwJaHfAEYTUxIhJtTsJ52AYLiEQsCWie4HAgMBAAE=" + }, + { + "id": "QmWn9a9Tp6RHbp7kBJoeKntWAehDEM4h6SJz7GDDZ74xT6", + "privKey": "CAASpwkwggSjAgEAAoIBAQDo+BBORhRA7Ovd3N57O/s3uDAvZ0FE1fGb6Fhpuq5x7ZN34F6lK+UjgCS9e0vadFsF0ReKovF0RUIaVxuqEEhT7PQw3SFxYe/uKhaXUdimb6ecmStd9+jfA30txQDOQQkOzQ0TFAwhQP8o6yiPk6FQ9dIs8WIZj3blj9IZ0pqkORKl8elDczX5fzrmyNGFTa2CfSvQ38NH7egXQMRWk0apeQZfBAr6m20a5rePltyv9qvFaSwGbzEq5wHqMrZs62VCdvTKPq68bh+XqpJi62A+QxUhqgWC1orNqnVehkoksihsS5sGnz8Mr0z4SYF5s9SanExuszVC3vyWRaRMk9FhAgMBAAECggEAcDBFaexFhJ6TV1wtp5rS95ozWiCXRdbhavsQ2JmVuDd4dJoP+eqRhe8HwlfFx4WQ79QpShH9xMg1dQnNwK/mpMPPNFjI6hkHueF6z5QSiNPsG3WHdNEVy4+akJf2M10OR5pnWXCmBr2OIt5bwgiN/rbiZ9I2M6jq0NHZk6Xf4oTORF8bYYg9EIRhBQuVqstgf9y/l1SLVv4W0pQ903+KVax1Ir6BYN7QrVsqhJE1Xq3pkfhbF5+a8+A9vrvYjyJfbYYAgpwuQUNPLt58o3YEiL3atnCK26AZ5XUAvUH6hc6JlF0+5ZA1+EENpvliZP8+oKaOxj5dHj9LxAQhp2gIAQKBgQD3CsTOI8g5TvVPbYs5VaH7CUnDgkQ6rXGCYoiuXJSooD3npMhN0EsQjJrc5GiRe0MIBv1YdknZMX8OpA/wlMHe3feCZRxD7GlkqWsY9xGMp0eZxifYSKpvjOKrLC6tOW6lbG6ry70V6bZ4U35D1S2EyNzZwVToOpD/BlCof0mpcQKBgQDxaqh32X1xqSoPvKBWqH3bQ6/gEKqyXYpd6ddLtZ6YJ0ktyfj/CTegAo1U3OxIWfz3QtnHGpql4bHI1729xgiSRMlPacTFSwqhvBrpWDtcEXrFwQnbGs4pnTIpsyGf10SvTB735x1g8LA7sULTffchStwR8uQlecPyw2rYy9ku8QKBgErcxI8DYOWc+9G6DHqvbIq6YdQRYcO2U7ODUJAmFhaTmK7dzKVn/ZdiRHuz68xKYiHInHV2QyLCURKKvRnWunljImItacdbmQScIoxKMtNAzf3XgrOUMbHkK7xbgzmTMeiHV3JQJ1jAnj8Qbi74JvcaN3L1l1lYhiACNecZy6TBAoGAYy+9E7Cp1373JwwKnFARlXqT0e1HFI1WAgvHRvXRuRW6/TMUOERjNDyYMJusAHXodvZiX2lQzIKT9r6c2FF6yA224AfGroCMV7LfI6v6h3MvC/ypvfmfZn2NVJCiafeZ4qPVvWiZK3D4Fy5XFGmWlXkaPc2basJCfYVIatYl3yECgYEAmoqqo+wazk64q6lixYUf5hPZvQzic+8k6DGDfC296dXwal5du9SGCXyWPh7n+3+NQBVn/rtNFAJtu9Z02NgSDD5p2H4YLjsKcFYYOyp5kTY0N8iw9S9DnvFfjXIdJB30Ec/TPWnblm77XjOb6WVdEqZJt0mncHKDG9dLO/UCkvk=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDo+BBORhRA7Ovd3N57O/s3uDAvZ0FE1fGb6Fhpuq5x7ZN34F6lK+UjgCS9e0vadFsF0ReKovF0RUIaVxuqEEhT7PQw3SFxYe/uKhaXUdimb6ecmStd9+jfA30txQDOQQkOzQ0TFAwhQP8o6yiPk6FQ9dIs8WIZj3blj9IZ0pqkORKl8elDczX5fzrmyNGFTa2CfSvQ38NH7egXQMRWk0apeQZfBAr6m20a5rePltyv9qvFaSwGbzEq5wHqMrZs62VCdvTKPq68bh+XqpJi62A+QxUhqgWC1orNqnVehkoksihsS5sGnz8Mr0z4SYF5s9SanExuszVC3vyWRaRMk9FhAgMBAAE=" + }, + { + "id": "QmTf2i9cxP5HnTc7jAjsTabtKqPYyCRWGFyxQ2JoXre8Vy", + "privKey": "CAASqAkwggSkAgEAAoIBAQCqk2sdkh0jN/p9Znnw2pNvPULjt6s4DAFsnvl7Vjup1XFC7K7JKkycueATSDjjjzRgQknPp+GdJLAVDBRRhmHUKVWjdirGKid7qqwYe3MhMS3KFwKVRHUh0zdkk3D9I3UZmfmmCtrlFU3zrKLFL2Gq9wUFomyn4QI6FXG29m1MZaN8jMMRJq4zl2PaSdHFeingd5Cc7gbwd+m4DWPY9tCYCpOF/e75OAOf4vmx3G02woAjIbAUtSJSFoMnexqkzGgzxf14KrFqWur0t13UHPwTecMntkIjIrv3b7deVfAssXaaqtts7/hBMfGJHIPGOEA41AiGpg1hwitoU9FJbtYTAgMBAAECggEAaGSygLWEyaIf1pFZ/dL3unPGtTEaQ8HtOToOO7Kp7yXnHGtYCVuu6ZhEj+h6kKJ7DpJjNAht5sumTCtJYB+bTcpCVDkksoToByKMEquy6VR8zprIhOwRt7LZyt8FnpcNuq5mgH1SPDOOspwsPfvijUyCsVEXqKLnxv6KyswEVmU5PABwY3EEOFOC17NkHqD+wofXdzjwUfaAzeaZRZNgz92/Mr8Lqt8/9+znHoznATZfsV3IJfXzvvYcTv6+Cui+eWtqGy8TFT0+d5q11lXB/L9AoDtH4KOcEErgYfpuCH7yVBaJpHLZgmfX/MGL2jXy/BOHH5AqI1qHLCUmz4SuyQKBgQDjOcmJsNjg6l3NbZnDV64NLp2JMzw+VVy0sZbOX/xfhNp8RzFli+yblRP7Qijzz/AAeK87TDSXOB6Nz+z0ioM1YZHj2K+ipIo01fG2hXRjo2uD0Eo5Yn9hGHvYQAcWPtM7pRZckYf412HjGV6E7CmU4DxXTOf/C2jQUuP1vOH8zQKBgQDALSc2KP1Z7lT7DShh5oes393OtzvzaiceWgWQ4eKTHKFrgwCAWfSuOWinpi0uPUThyy4yggMQLEf89/Mqhy5TRo8lBl3++UhUaOrueRlfw2k1wHEoolY11VC1alUbLdWvY/5fN/NusudyNkNtKfgEJamWfPzbPB5gjKFMLboeXwKBgQCAVSTkZUM4VGXCHkKXty+QNmG3GX7O3btMOnin+kI5WHllc3gMmCh7/oiYgddLTIeKXJ7raLDTL1jiUyrLOme8gGy7OKiJvVBpYuPvaBRCKgwRGFSzcFPs7L0Vd6QJBlaa+zRT98XJ3ytj7fIaNJ+Pe83YqJDm1PTPo+En6xPfiQKBgQCBgCvzSE3pwzxK7QPwbQuyiC0NNtbUYg04W8tyZXVsUxfHvkChPX0vxonqp4W8qhb33RYlfQ0ZYe2k32VinDOwdFTTy2WeA1glYYom5z4dFS6ZxTO8ASD9J+E6TwxIw2tCxKxOeAK37yEeCskHEAgMq2CLg3BVQD7sKvgqwImyGwKBgCHRfBffD/BA5SaFlL/AbIs2cP27NqT/rNjf5YmJjUx9YKBAYEtoKKGuiOLuuN1yrXablZ1fis4/jDqmZl44cP45dXMdOgFP+nItL0jEfF53sz9NxHa2DR2Gtd1QxeylmHZjMsSeSB+9f33VPARa/wh5tIvSy7cDOweAukxxxDqy", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqk2sdkh0jN/p9Znnw2pNvPULjt6s4DAFsnvl7Vjup1XFC7K7JKkycueATSDjjjzRgQknPp+GdJLAVDBRRhmHUKVWjdirGKid7qqwYe3MhMS3KFwKVRHUh0zdkk3D9I3UZmfmmCtrlFU3zrKLFL2Gq9wUFomyn4QI6FXG29m1MZaN8jMMRJq4zl2PaSdHFeingd5Cc7gbwd+m4DWPY9tCYCpOF/e75OAOf4vmx3G02woAjIbAUtSJSFoMnexqkzGgzxf14KrFqWur0t13UHPwTecMntkIjIrv3b7deVfAssXaaqtts7/hBMfGJHIPGOEA41AiGpg1hwitoU9FJbtYTAgMBAAE=" + }, + { + "id": "QmaXs4U4QE5W6hTwnnWoeTtLpRv97rnnRaBrSHQ5SEBhsy", + "privKey": "CAASqgkwggSmAgEAAoIBAQDpL5CNWxaC53PTJcNB5oo1x7FHl02I8Q3+aH6MC1Ln/ycL8gLVcvpAYsWStEt6tsU3Br86kl810f9P0Rb/DI4QVEgiTKCrTPVX1Ri69abJPt7wMUUM6zY/loKIX7oTDfNqRePoZK8lTGbiTwQohuJ9YCG1Pmfr5agYuOoN7KwflWidK1YFFf2MCeC6Lw9mMrCeNtiGmgnQEauh35LBdsYPHdPCRjtJZHkI5JVLl9D5bZAW8kuqVcRXnLLqklE5ijnvwOW6qDnIa3L8TZN/H1Kc2V1Ay56CUWwtvESPYCxxGzhbKs9GOu8n8qboPlWM6jpxhf3bGD9zzjA/AOS/tkXbAgMBAAECggEBAJ6CQ6ujAHFpbdVY9Rql4Mtf/AUjN4Rf/9gxJdyCkg+75Mn876oypyP/QDhSMICxHuwhvPnOp8PJBNk30376BNPaxtHx7sGi5Z+X7mCgPYjNIPR7OWnntY+R+xdCoxTyxcU2zw6Y6mnbPpCVj8kQr4L0RtWSH0g+a36qrAddGz+3nxCPSzoWm7ULAQu7blu4G3O8aQDNhqp33nM8U4lo+18Fl21bl51QAH5hfH/5ynvgO8qTHqHFGlzEKUQ02DN677y9JBSqnTtMdZtfwC9eM8ATZ3a/WczVCVsCIgNNYJqGyJP/hER+4OyCts0EE8a6CyL1wyqvRHOlzd+Um2/3EmkCgYEA+XeYrWQCNk1E1bliK2mSwEwPQL/029sI31fUK2WayBRrC+Sb0icS2n6dEBvZNFwMDdaR0ioOFa3R+LBP5mjHUPlnGNN0W7gGdEiNhnMKY0DH63aXO2DEFsbSZv3pt6Y1ZEv2DR5pERCV4uQKNeTzgFG/Any16Og169A3BasfTwcCgYEA70rRy+kQeyKSyKN2mFtEQibWpkGAgLZXyiOzFl6TDxwKJ0TuCRIXzJ/ups/wBHMpwiLegtEYJBCKrsdJ+LYjGtf4RyS1fcyrEuVwHrSYWcEPyVHD5BY4kPDoH8s9yPNJnYs1eC5XWaNsZmtWHvwiLscWL1fRtD92xKD5vsgriY0CgYEAoefy2SW9Ic0ITcWhZxd/vPogNB/hJQeFPJ7d/sRUrmJCD1fxoXTmkZP3D8tjLs5GrHyD62iL95n2WnbjhGCga3dSaBRkwlovJi0oXQgwx7X1imZPRKRK35VuXJWSqDRhFpUOPflsqzyeasEht1S+jJuBYnXaAi0jQcbCzxT+nuUCgYEAglTfBc/wMRD4n50gqAT1nY4n1S/R/MjT8f4VubZTJKSiBxdtCrW3DrWBN4wD73iGIv348YlPO71iu6VB7dvOLP26dY1R6K3D86vRsHeujxwvK/iD2EQeFRuRYlfBVCTmUI+U1NtwqhrvnK0ToBa1y4KV271aM3Fpj9kZjcbap7UCgYEA+L3SqThkI4fhRFxNvpxddLskL127L+GqBR74TZHI3P9ACFXOfW+6me+PZVhai6YRwohglkBPx1NE3TYYAPysACMIypXjTDbJ6sXW0kBXIdkSunnaUXAjlJg2RnEOBeKOIn4GoDqrhpdiF5hOBMLrFjuoVnF9YdBQTEN5trki0/I=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpL5CNWxaC53PTJcNB5oo1x7FHl02I8Q3+aH6MC1Ln/ycL8gLVcvpAYsWStEt6tsU3Br86kl810f9P0Rb/DI4QVEgiTKCrTPVX1Ri69abJPt7wMUUM6zY/loKIX7oTDfNqRePoZK8lTGbiTwQohuJ9YCG1Pmfr5agYuOoN7KwflWidK1YFFf2MCeC6Lw9mMrCeNtiGmgnQEauh35LBdsYPHdPCRjtJZHkI5JVLl9D5bZAW8kuqVcRXnLLqklE5ijnvwOW6qDnIa3L8TZN/H1Kc2V1Ay56CUWwtvESPYCxxGzhbKs9GOu8n8qboPlWM6jpxhf3bGD9zzjA/AOS/tkXbAgMBAAE=" + }, + { + "id": "QmVRPqfdzzxgSB5wjpSewPcmGdFFcK1Sjvy9jJYQCYKt8x", + "privKey": "CAASpwkwggSjAgEAAoIBAQDO0uSs63dS6MliDuqAz1ElaBkut9tjug90F4L/SCKiLFSJjO+RgeYw/I7zwGcuB7njhI9XQpwIqnkMhGeqGm3xAB88Gn3VUsOQBa6YBrF+1yvzl3lpdTIwnbZ+G41PE56jPVdbN44WHXmGK8MeZhHRiSzsP1b9nQsO8lOFa6lYbjhRJi0hKdZ8QcnLswA1mzUe2D24g7CQ+QHcgKtKHIxc2g/Zmz3Gm6Sp/HBP3BFigsAgZ5RXljYqSKci6fqR7DV1H5cEHpr55oC3RqKBWi09KISVQ7gt7204d/to66LUnBzQ2yXNt7UgYBwtl2kAK46FlnRlXMGf5Cakmuo2Mw3vAgMBAAECggEAewiFtEAWqaGFOun4okuxz/jPEjWZMwgZ7UuXR9lXkPWiDHlkgkCt+PIk7+fC+WTrNedDhfs/6v/OJp4VNOGoCTezXQO9W0tyvphCtnk02WmS4cN4HD31b2pCh15If4QCWMVC1CaGRa2e/yvFVK4PZ4Cc0qUTUKc9TtTkznevM8S6ndQGgdVakQm71Sl8NqRoLT0AOnJqOfeBk3wPdIaUNAHV2ugMQURS+UDaujyW9XXn4g5YiALksUFUedLStQbqytmSt395IN3o2InzFZeJhO0WeTK1MxpCeUguVdM2pUisLm3Qtpbdwg7/4H026FT9jrdjxLnTP0TODIieQKHP4QKBgQD8KWCnNI4/SC9wznoBaPLliBW41yZzKRnHWB+RtyjJO+y5JTynZahLgqYZ8DGtB+giDGnc5I6IjYo5W6VfM2WYCeAuxsZ5nzgOk52+keUoT7vygYsMDS20YskrW9HsVnbN0Ss4lgckPO0cnmtpqelLSYtjlXVwmqYbIgIqiENaSQKBgQDR+Nfrj5M462sbd/sDFHbVdIBfPrjPsNp23pd6JlEDqPHKqdMK4qXB5DdN72jt/+26F0J6CZbtbJXWNCu3b24s/VKfnTacaE58ScDLdWinZElCmEBEZGvLzTiQ//8rC02GbPQADCgm7Q66lqnhYjyaYvMpG3W+QL9Nuwz7qZNmdwKBgQDaeMsZ8ynvYA5iUTJZuGECR0ZysE3K5uhD03D+oc8/kqDFxKJpcmU+jwJ0ZKvVe40+uryZuphXbTI+Ac4kznYOJThLrsLVKwK5dmHsc6E0rOhs8pnTJjPcHt6mp1YYys0CA2DMFQg4+GSDMCpmEOVk6mJPNHXcytBxn6R6zzZzqQKBgDf8A4Y5V+W7T2mZigH+ONkdIQVEMf6ehwFexEC7aqpxJ1cEBNNILy4NbXmINhAPRxFxFjZ3zBgt9Z2Rd36HZXb1bExGnMMDRLn66D7XZToMkUQCWFBj/iHu7AAtYZnWf6sx7bT9iM8oMd8qQM1pZgM8Et2JBtHdbaSKFSIcysJtAoGACy8BFGvZ+R5vk/f2HvW0nKsnjJhr6z9Q5ocAmTz9LNDDDiK7BO14pKJusD1GGIMWAG+4rpyp4CnLozlKPZzP7t8LWQoSOQx4/ehFr3lzlWPkgVXcHCJ3vUdae03EY+2DNlKgv4O30g4soQDIPewMyiJxaMnnLtvb/9ZkIYLa+XA=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDO0uSs63dS6MliDuqAz1ElaBkut9tjug90F4L/SCKiLFSJjO+RgeYw/I7zwGcuB7njhI9XQpwIqnkMhGeqGm3xAB88Gn3VUsOQBa6YBrF+1yvzl3lpdTIwnbZ+G41PE56jPVdbN44WHXmGK8MeZhHRiSzsP1b9nQsO8lOFa6lYbjhRJi0hKdZ8QcnLswA1mzUe2D24g7CQ+QHcgKtKHIxc2g/Zmz3Gm6Sp/HBP3BFigsAgZ5RXljYqSKci6fqR7DV1H5cEHpr55oC3RqKBWi09KISVQ7gt7204d/to66LUnBzQ2yXNt7UgYBwtl2kAK46FlnRlXMGf5Cakmuo2Mw3vAgMBAAE=" + }, + { + "id": "QmdPr1j6pALmjygUjC48AhczryG25NAXDrjkDQCKitwS3x", + "privKey": "CAASqQkwggSlAgEAAoIBAQDiBAOQE6X87s9A+utaDvlxcNUIy4g9Nk3Pf5mGGo7o8tSm0pMxuoljRJcX7fxcRH3Wg9LSMIHuTT7V85BI8Afsb3pFqWWp6X+XnEqg2sUU4tgf/+r8gow5b/wVWKdM13s7ebOl7DkaXA/ShSqTv/b5l1yXxlk7iCD51MCnVeHf2sUtzrwnznMttsHBHLmokis145vze1iftGzcq41XEUgaCzPhy5MMwLQRKxSdgyVUU5w4Ihiduimi2B9Ju94K6kb/7JO0IgF9vuxWfHlZYm6aYYxUmYsHvLzg2pxDZa4sQlCAhxLxqMydfHS9QhVMrjAkgN+nOx0kKlupUTugikjdAgMBAAECggEBAMME3aIjIhW2ZgTWvd8ujpQQ3PFC1eKjic5idrEdF3Tvmwf7KOE+6K9MDueqBRgETWScyHtvOZ3K704jZLtayit8IVAck8lh3iOOy2Iygt27wusRosyRRLeG5kfxACKwx/eQ0q93ven9x/VJzGxCtrO0pEOd9X8FvhZ0OmvpEgZZmpGd4ZucfHayBE37Gkg5Rgnafvw/pGkJiYmpwLNFoRWhNkKywHkDlyhEtFwLaWoS7UfOqWuZHH30EKLpz92nCYrKn8dlbYaT+0q90wwyCVAg6ci766BHgyYa35gWQgUBsNCEaP96V3ltXgHuz1DfFBgVdKe11lU1cMBecWJT+G0CgYEA9lXhNidHBDq8u5Esby9w6d1A5+c81e0rPiMrSAhKrLYWxuFNjR1EiL5m19WT8nJmSAsJv/n+EAQB4N0k8itpaiMx/uwIxymUBqq3NmtkFIBspa8IjR/LBryw83Ec5J9aasxiPpTmxmeuOpgkGz2BR+oJez8Y7Ykp1QN1aBvBH7MCgYEA6uIMWyyJOcvWkX5as9a14/95EsS1n4uSYialmDdIkV70oUfaITUdggV8Z2d+5zzmrCWd3te9NEskkXIfxBZXBdwk4yK7ztx2CaTnCegpj3FeTfprDIusXBYg+AkvO0ziumtt7hJq8ArSbR9p8a03pmU0KF5KHUQlq6qaJ0rxLS8CgYEA82KG0eqc0CD4uJoHPROyjF5+8qIWCYf7Ybl92f3XSi3e80FITlTMkg8NrdLoCnPdevDtATEPROOwt2vIRT1/Oc7sZuLvCVibWLrzqmEQ0PDSaCE4Ybc38cAdVWo9EeYgf8TQjOSx3vYxvtLzWba7NUKXe54sEwWiv0RbjDFf7uECgYEA1ZkHf4o6qJKT5++t8ElbU6GpqsW0HQjB7/wRLyFnHrvpXxmGZhh1Yx42CZr2rraVVwtGS7aJWxrSgGn2NU3TiYKvENcvT2jw9sg9SqcT1FxWVDdcTiZecce155oCl7zNmpXDfHLHSM+umImuwJazOuBh1Cwa3g5w63HTidP/CwUCgYBWXa51w5kVG1mPR3AJLE7/Ip2395aq8H39Tlsvfq0zfTtVaeXzYWYhw0+/u9DXhPd0TJm12YqkMCxSs0LLAAj28rGMQeqUI0OxsvanCpNvzZ9iKHVncsjPJ6qm7U7Qj4lQBqy/VNudQjdwWSTW0Kp0/ZJUTWv2nltOTBMfPZF7Ow==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiBAOQE6X87s9A+utaDvlxcNUIy4g9Nk3Pf5mGGo7o8tSm0pMxuoljRJcX7fxcRH3Wg9LSMIHuTT7V85BI8Afsb3pFqWWp6X+XnEqg2sUU4tgf/+r8gow5b/wVWKdM13s7ebOl7DkaXA/ShSqTv/b5l1yXxlk7iCD51MCnVeHf2sUtzrwnznMttsHBHLmokis145vze1iftGzcq41XEUgaCzPhy5MMwLQRKxSdgyVUU5w4Ihiduimi2B9Ju94K6kb/7JO0IgF9vuxWfHlZYm6aYYxUmYsHvLzg2pxDZa4sQlCAhxLxqMydfHS9QhVMrjAkgN+nOx0kKlupUTugikjdAgMBAAE=" + }, + { + "id": "QmWbooxGKEpPutuQh2G7hRRsvygkr9a4s42s2MaC44bXpe", + "privKey": "CAASpwkwggSjAgEAAoIBAQCxwXcAe498rEQmP0n/XytL8zH4NPIUCtYfARYcn2nlFNxBv9uRIuXKKkC0vs8IUNNcTIXse3Qn/wLX0CDffje41/myVDqXzCW/EsgYmnq5iFDSesuarCe7e0v+J5lpWIoDH6OYdZpni1e2Ucfsd8Gvj7rX/6y5vNnFUukfW6gYNGwMZIreGQhhL9PII80t8hR8+ffsJuHZwhDsdJQhxaXRD4pi6QYoWA8M+xDbJv61i5ITmS3f1VjC8b9qAgX/OaoqZYcv1A4sN69cf3sJOFp5AvVaDtlAu9rrLbwiMMLt8TjTav4ym5EIFAjZrNk+AawKKSFLWSGIEItEyY7bZS49AgMBAAECggEBALBnrgDFntV8ZDmMz5Pmz6IVHL8VVDS9uFeWjWokbkQQ2rFJhM5ZUrC6E/ML9YxsIgXUT4gr0E+tDRY+D5GcwhHoDH6Eho2E5OTl77wy+p5LgOIRSSQbu2ac6wK0K9RXMMf+yy0PcQdXStm9+WlcQ9RGEIgxzSmbGIdyS7Z6EKzLGaQXXaCOLg2WnTfImm9NGos4BlchAO/itAxO3jo+DgZoJhEdGg1IUavDTWrDwq462N+/O2+QwVUPPjB2rWyqkm1I40Q43u0UMNyGwpA0tEfaEXVs/ZfmJEukdymgyttLC894WAWDzvOqK7SHyKZTbZgQDer/UiHlZW0INF5Yd6ECgYEA3zuNF+3feXKTmWhL96HyweLWYybweuaeirc6yBpYwa7sNZPCdZukysl7lPidz7tgfA6vuMVqWH6h4iT6kihhlEk3dAARTFElWmLWC8VEHX3ix3Whh8yj1E9o/4WI0ljth2CRn39ISQKflhoBCUGLNNl1VlMW4gfFoENIq12zeMUCgYEAy9kFUkblAy+JnGzNK6A1BQErCK4WKUqLk0eXiXseNx/LnAgpE0ITBDMKj7X1dr+Dr+0dJkaVjfugLO/9fZx4w7rWUkUfFvZZK4+Kyq94tKruI26gKyfaRogVwxwtjoCD/m3IYTldrb11jYXzQ4Wnu+ulE2rbfeEte4mrnQldBxkCgYAmohnDAGnijM+6hvBOxPpMT8OmmUCZJHxfqWJE6zpdGrbKdu36iJs2dTQsDfLR1q3WCnyiz5eeBRxjFN9AmDh6/0jXjINx38FFKUG9+7/UEstClbsE04eu4KddQXDyZm2FNlM9dTu/VvLjeVCgl3VOCixiajLVn162Y66Z6bd7uQKBgAKELk2itGcCqjc1HJ9CPgVOy2IzC/D8i5aI1rUKwQe7K79EVoV3VlbB7opxm5MYsB521dpIGsaC6apEWCO5z7/SyZyAX+7foaApCHSSD/Ji/ceQMQf3WPlv6pza9zEAIyD75/JTS/W+FfpmQed+eeCW+xWvRwBZzD7vJyAsImrZAoGAOcT/3WaWaTv4ZNf86weXLTQ7vWLhrHj2tUpT6t19ejEi/gtZ7vbHDdaTJD8pQuoOWZ5jdoUWZiGbp4KeO+aGKZs43DcfZEecFA1uRbcnYnq1vYu3QvuALbRoby/EWO4vNJxvpsj9sEJ+QAWxVi1kNU4Ui0bN0FUdxdUH2REqTDE=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxwXcAe498rEQmP0n/XytL8zH4NPIUCtYfARYcn2nlFNxBv9uRIuXKKkC0vs8IUNNcTIXse3Qn/wLX0CDffje41/myVDqXzCW/EsgYmnq5iFDSesuarCe7e0v+J5lpWIoDH6OYdZpni1e2Ucfsd8Gvj7rX/6y5vNnFUukfW6gYNGwMZIreGQhhL9PII80t8hR8+ffsJuHZwhDsdJQhxaXRD4pi6QYoWA8M+xDbJv61i5ITmS3f1VjC8b9qAgX/OaoqZYcv1A4sN69cf3sJOFp5AvVaDtlAu9rrLbwiMMLt8TjTav4ym5EIFAjZrNk+AawKKSFLWSGIEItEyY7bZS49AgMBAAE=" + }, + { + "id": "QmNbcu5LYC7yFfiG7YSphDvKeCdnrZaLDVH5myCfa4F3KH", + "privKey": "CAASpwkwggSjAgEAAoIBAQC4QngGmdiupunaaeDYdSSYLz7y7kxkWBgCM2KoarH47acTWqQIYNjas94SqFRYEZELAlAQgtX+bQ/BvSg7iF0aikutQNGuIoq/oK5U8prglhdBhcLv8bF6fb+g9bVgyxe7c+lvulK9N4B6+UOzEPCcHy858ZfydIWEBX2VuhDK9erCo4Mo8uicTglF3L9b5MwxoZ0crIbB6RZ3Zzh0VoRRr2bP9tKa1hg8MA6qJ4Z0qINJupO5vp3AtUuguFrQbTn45T+0UEqRZxOaMFI4yO2CIx7CcE+lstgmAkup39dB6CDivLFeD54140xnqmvqJBTcfocG2TJ114dv/PUjVN53AgMBAAECggEARZxe6ed/U+QdHvW3Cy+eRpw6xN5AH/yj/VjaYdQFLozh/M7Wf4/O/TjMofHUA32HdUhHZSV4oOkk9cV3iJ4oxlkO5AJD9Ox5zJFwqwkId+ZcHNXi4nFF4ofVOIL7Jfk0Gw5cV2hlz7RDJrI64FB7BhBF8DcUd3WL52eHnWQw6eu+l7DHgHhYmVvPQJqcwicd9hKKavNqssCXhywubtTSHTazxMdpdpaTpuG8MxYXCO7/uTA1/6Dz1UqZgYFDcm2Jke60LyLJl13hufy9hTUk2399NYiYs0WKvxI/7hcUBR3fxC6vJyRYPmmj/xMe9wenUMfqubUhX+BUl6u1GbcB6QKBgQD1NzbN4DKgGci1KAftYCWPncFHNAPiOa3vxLoZcoYDB5or8NQnlHmbPUhXAhwSMQ1Mzxwjmvp8gRedyZsumlgw4dh+0+Wr7L0zWnFalSQrE5+hBFLBE+dErFUVTqYJHMS8l90OWMKJk217QnRdSrypljKKC4hGdtzvThO/L5oezQKBgQDAXPmYNNlLUKCuWLo+WHl5Ql0MM9m/27CeB9OUFUVn7QHP7wk1k5SEN+oDEMW2OHr/eFBXKW/rNfmR4XdjCRA6Qrtrm2OcupGGa1VNC6S89ziijTidCTyUBFeO9r6omYXQ+4hYhnBAj1/XR5wr3JkeR+hMVQ49jifIvUBLOgxqUwKBgQDwfXAgRSshXWg8UsBDi7IKwbrWrCrK6aqupg+l+W149zG6unw3PSRunZGe05DYPoc+tzhvqwTSqoCQ3TJ2aCTp+/UpnYN4M7vy61aUcgSAwsQhVG4csfp0nVbsiK/J7A7+rym7ck2IQ7Bx3GDnRe71mzqBrIHAKb7Qa5BuRq8uWQKBgAkyKKhzFlQ4ZYYQLc8fMadvWb1kmStH2FilrSKuJaRDO0vydHFAPeBYP/KnkZwVdSPTX5vbGkOXT/f+XFO6sjDm0cjiI8/xR1WyueA2B0kVVHrI34T6VRAKaRkniJPMyw3jiHAa9TDQ6dcD0F50bEl7TIqhok2lBxfcnD88Zi2LAoGAeQnGHrhr/BkSNk0xCGiyIb2u3HKnwapdzTm85JAGbWv/C007iNIeS4INnNmHpIvYkB32pzpj/XNAJb0B2bZ6B2gouBDDv0fWGEZqBfv5A+zaJCKdkPI3FrZ+52Fr/V9vJJYKpWDV4X3lGAV36YzBUzlhwtVHiNL0kFE5WTsr+k4=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4QngGmdiupunaaeDYdSSYLz7y7kxkWBgCM2KoarH47acTWqQIYNjas94SqFRYEZELAlAQgtX+bQ/BvSg7iF0aikutQNGuIoq/oK5U8prglhdBhcLv8bF6fb+g9bVgyxe7c+lvulK9N4B6+UOzEPCcHy858ZfydIWEBX2VuhDK9erCo4Mo8uicTglF3L9b5MwxoZ0crIbB6RZ3Zzh0VoRRr2bP9tKa1hg8MA6qJ4Z0qINJupO5vp3AtUuguFrQbTn45T+0UEqRZxOaMFI4yO2CIx7CcE+lstgmAkup39dB6CDivLFeD54140xnqmvqJBTcfocG2TJ114dv/PUjVN53AgMBAAE=" + }, + { + "id": "QmVTyFS1kFgrsnYgc6tANEiSjUgCERhvx6GdDjc16QhLDu", + "privKey": "CAASpwkwggSjAgEAAoIBAQDHm7xKULYNUvHj1W0v+8HN41vBnaH6U5EOc+XSJF+E/ZNwEHpJiDr0gnJTj3eycst89hyvo4FOFhCwGpl6V3aWPVxxWMm+xr3PjUnxun0grusPIRCHEiB1oiY6OOlnJGbouISM8egndKjlpDykCH8kNar36oREH9z33nYmsd8mFSDbT0B9i5lXAHluhEbDbor8QpFDihD0gUCxGTyu9DL7xJFa0xLXsa0x8RxO5Q1jPV0Fe9jMog17zlXAmyHc6Ikeh5MnIlr771BHDjbbXhzEj/FXgyNnHsds89gO1tyrtvp6TJZhDDTD/fkw6IQ0gjVfRIt9nwj9HqY8nrph8ZoPAgMBAAECggEADhoMhVuKYQqZGDV9z0QD/x2u39DZHqxEtTau59e1/9lCT0p6uTMspxWIKisak7rUcXGZuTbd7bxVVXurmB8Ru69+FmvlZNr5ke4zaSZ5rJNu+9SkLEa1kFnyRW673pONyacvhh2Y/yCYxhCCUDxhbxdziqxAx+dQXxqSUk2CrFf11Vg0RndoqoDBMb51v3U5tL+yVWnxQaeis+8pPwJEHx7a/zd1D00Vr8sKF1nALcnkeSfuH7Ha5Fxpam4x3xtkus7IxH+ZuUHTsr2/jdBZB8bEcmbEJ85sjkwzsFzpKQ2cEUmBioy+QGF4R+97o6QCm958++OGOf4uqKTpLmUIUQKBgQDnWkM2dEDTO72Y2Nh237aU2Z7P7+KYn7q3Rxu1HPglLl/AZGCPMQCzUIY3X6I5F/43wwDMbcWJFYwAwoDKT5Iqj3FIIwoc50Q1LHoiJ1whHqH579AeNAYe8TPZTbXc7n12RHQA/2R+aoPhkVZsT9KSsXqlKonfsB2lZvu73MvkCwKBgQDc37RB1/1Nk2L2kCk90FAPPTZwbdjdGDy6+qQApj+OxrRt/J1nvgQwyrU2aa9yrKqeLgAeozP0vW/nlEvd1rHUpo6910nFf2ft2Am5m1pnIMxyh7OK3OuFKt8uKdFnzMY6Ee4Z97NjytcOXgIG6XXlsuzoS1BRIy5Oaw7u2w8AjQKBgAoL82tD9SS3LW6bzBqtcGJaaPkis7ZpCVS4M83TqjsDHoleUoslV6Bx2MJC1uqfzhxhzYETK0betqu5xpoChphujj7Rtri2mlGs0sj06J1PlT0jWuhU3gct9lBzfXhQqwYdxuPcM2MCASQkiZo+hSh1qJvPkxkxrrfmFvx7hCctAoGBAJ5Q3oSfi5mSPkobFOSQrwHtT1sQ8XOwjr87Llik0UPGm8eGXVDh+2D5uyAQasiYiQcuU7miI3iWXGKyghq1a+vPotriOKeOZfEue/IFL6xh7wlcyU2MgmiGTUhqgwWS2rb+6RrF2Fh8KN/MdEl+B5qoQHVUnnI59FQTGrYMYFl1AoGAdgCOMz+1kpIsl36CgLtOasFbY/62IHWh27+9QhyIbdFhPFaGa0Lk88bFIRZOab+12xL0ZCk/5xeR3is2qWuFwPNUeW8q+un092uKFJgc0Whe/24Q9c40pSimP2hPUMqp/qh4huNyz+y3ClNMAiVmE6+75yWV6+d8tAe8rD8w2XE=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHm7xKULYNUvHj1W0v+8HN41vBnaH6U5EOc+XSJF+E/ZNwEHpJiDr0gnJTj3eycst89hyvo4FOFhCwGpl6V3aWPVxxWMm+xr3PjUnxun0grusPIRCHEiB1oiY6OOlnJGbouISM8egndKjlpDykCH8kNar36oREH9z33nYmsd8mFSDbT0B9i5lXAHluhEbDbor8QpFDihD0gUCxGTyu9DL7xJFa0xLXsa0x8RxO5Q1jPV0Fe9jMog17zlXAmyHc6Ikeh5MnIlr771BHDjbbXhzEj/FXgyNnHsds89gO1tyrtvp6TJZhDDTD/fkw6IQ0gjVfRIt9nwj9HqY8nrph8ZoPAgMBAAE=" + }, + { + "id": "QmV1dRHcUPPQh2UEf8hJyxb1LM675eBf9gkQU7KNEdcbKt", + "privKey": "CAASpgkwggSiAgEAAoIBAQC2cDaFxSecoxvHcyEuo1Qm+1RVCqWggRmwXwdCN67BJe2n/xP4nzV+f4LoOGJWWfZ23IVoV7sK7Ur5biTbIP+P6p/n7EBlZJdR6IanjK1gof8Xd8pMgDDgbaw1h7v5mK7PBTtHTGmOQjPimZpTEgJvZrY5SUcGP1JSnoWzn2Nhlvwi6boaY48ww2KB1d3G7cpjmFNQCsbgUUNLkc4ZdpFe1ZcGYtybXbjcpHHlpgkNYKcXUT9qkF3BO9tAP377N8MCUM8XKm7ZhuUPB0gzpk/HUlHw/HaVcFDy9ciYDJevvVO9KGJLbzp7wxd7om7B99HdnwzRah/75UjhKWytwAnnAgMBAAECggEAYJ5FActMEzR+vb5HUH/HTW3FV9RvqvU0OafaHEOJFVmU52e7l+wZVIkLNxjFW54BXSmkrYHo2PyavEaM1lA/iGRlk1oXMJt5WDOhPJLQL2Eby73xMVqih/kNPrxH3QPzcEgBAN2C+YguGk/m3x+mkoD++I/KQZC/dGnnvR0ocMG0lnd6mJcUyNCzKaFNZPnb7A16lpNOe3OPAoT7tsmG5u45W2L+LROwGGLaCXcuOwdN9MQSpHWPPjgSB+CIhjBCDdL60nz0N/LX2hsl7bkcsDyqjsI8+XMnn8VXdkt0hjCYZfZVHS8EGgq9HJSSrfBAy9iIqXAFpcWYYcLSRHv8QQKBgQDYJuGit952LGf+xCRNLDbU00kLQa1Y+gaAKS7Rm4QkS1BEsjeixEu588QISlytynvBUeIz8j5c4fagAYhP6L85SRsw3yoVb/BJizl+mzC9jbQG0Y1vWkMHzE9du32vc4X9std7Ag5k4Uhfvj5z81+vyIoR0FlnWAXfMOvYPPLzGQKBgQDYEj9BMMPXUGBCAGWo/Jyx1u4jHhsRkD7TI6fFf+KSAtBJje8HGA1idAWZKd3v8QprICQzB6zgLyQRYVVgpagvGoDvZqXQL58MAP5hDxlbC0I4+4W33p/HstAkBt+5skew+uUqDl7oPeCKMRcKZU4PSbxKNLh0RI2dJZxJAX+E/wKBgAyOXacVJUAaHkRnYnB1NA4y49hajVZQ19XUDpswfIGbOmNzMtYqrvdzWJzerPmPfJbjSc+Yr9S3b5sl26RAsfMC8yhonko2gW/UPZfELhWkyKNowt2IEcK/NJEOViO+AzeUjv3fRbTIfWrKY1ha9+D/Nj4Iq1jcfSLU9a5PBBHpAoGALjnxKHxLWdRLU7OMDFRHHdESz5aQSfinSDq3mQxcJ8rMTwA7pDPkIS0bSYJohf32saaFuBkrRicvuzwsrOmkCrfL3+wBC4I0r7fCtHcrgIvvFgvRtt29J1c9KogB81ON/PIgdxft/BfI4yfPexzjT2gCDYor8Ev+VCGIxyG363cCgYAaNRo2uat3MrN55U5ACqYy+W5wy4N7JmHqWKIepHaDVuRHuwuI3eKioL85X8/aqzFGA2oiL0jFI82fCOqoKSAjncefTO1r0uN4hfrJUrQNVsIq18bkfIlqwH9jyJPG6dB0cTpaeqz6BUkqPPS6PmhK14tfYIi81mPCHf5q712raA==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2cDaFxSecoxvHcyEuo1Qm+1RVCqWggRmwXwdCN67BJe2n/xP4nzV+f4LoOGJWWfZ23IVoV7sK7Ur5biTbIP+P6p/n7EBlZJdR6IanjK1gof8Xd8pMgDDgbaw1h7v5mK7PBTtHTGmOQjPimZpTEgJvZrY5SUcGP1JSnoWzn2Nhlvwi6boaY48ww2KB1d3G7cpjmFNQCsbgUUNLkc4ZdpFe1ZcGYtybXbjcpHHlpgkNYKcXUT9qkF3BO9tAP377N8MCUM8XKm7ZhuUPB0gzpk/HUlHw/HaVcFDy9ciYDJevvVO9KGJLbzp7wxd7om7B99HdnwzRah/75UjhKWytwAnnAgMBAAE=" + }, + { + "id": "QmbRMpnAPh8enrK4n9d47eCgmb3VwL8BxUgETXNk9wBqWN", + "privKey": "CAASqgkwggSmAgEAAoIBAQCxZG1AHWVEICGrcW5QAACmboRWDVrO1k6CrfDunyLNnZF34h3Bs70mlIFzg4W1+tuY7NoQ/sQF+jX1O6TDlW7eLjHLGLcF5Pcbju2gw2VS8MrzxfWf2WyMBpSobjLpN7JgOCBop+EzkHeNh+pmy2NYlqkJglAz2shqrtOIzfgpZbkWHs1KH+8ttXes8/OORHIbHSWKTZJt45AtvEBuxxbGcU+GmJSOOJIAwhrjpnErXz5L2cgS35Z6+eitOpa9kueQU584bcLRvpydpImRY89BU4EqymjsLk++TD9Gk+G0RTdW0jaW9UUJWdwMZEaudoNG0RMdWUz9sJ87vH7cqoQBAgMBAAECggEBAIRrbUqBJwj122x5nllFxzZ2JX9/NJxWSgEvCz11uas3xlFxj7j7DWXxUbkDIxs3ihncJOSu6XF02DaaMWJ2+G4Omcj8SBO0WepOhYCOm+KQMVBMJBERYwAcIXih2otRsmw1OawmNewiGbyjCw11IlINFV4t0uUCmr1YiDwFVQzAziL+T8tRZvQIXGWQMewpQa4x+npM407l/NL7Z8i2fq5hal/Vbmp2EnDul6CF4pQ/T7/KIJ3WvSZSsPbai8Pyg8ZtMgrbxlZbWupsrS8LxtsMr7yDRkjFg37PkcxeOrQjJHC5WdG2haEi1RO359P6LgQavamecCRxSqXHr5qYoYkCgYEA37gETfKwgBgzXipqPzpHEGeqyPdYMoDKJxAFC1bydRsSDgyiXE0Y4Y0CFk98GBCfQdhfx8yjWvnhWQhNlcC+xwSnqN4KfOmPLc8db7F+dWcquxXa5ijQ26ecxzvbDGSwmcPm6yGQm6JUhtXQHUyzU7kxjdKPldInlld4syHpK7MCgYEAyv0lwRwMB6UEQjC0N/EtcNDzfl27y3jhzDMd3w3waRZssz3pNauBI/zQKddQxanzrQriNI+U9HfrHNFQKZzWGJuTDThgGy9/8DctASKhWITLH4PNo8retpM7PqtpSe10m8uiLdB2onnYmgLs+bYlmB0qdrYs45+c1ynuzr3853sCgYEAqDW4JtOfGfEYTmkd0cSDwjXwF4LCcdacKJrG8CvDyYE9Jgsh2LyvRIe9VgEVkRKOa/fArH2VJcZURZkUnNB7oTBCz5mD0T/bQ9pCi8StDKExY9Ge8QcnYsC4BKVgogTjM/o0Gf72t85qN6dz/1O4Ue8g7z2ucDu/QC8staN9qjkCgYEAqnePRi4EmELnYaK/Jh+/NnfzlFKpTAXXMgJXeqOdGtN/M8OxXqYJzKwaMJkmtuizv8VdFCDkSa2PX+MT++4/CelUxAxtAUeRnKzpeYOYldrnLi8k+gF7qT3ZyhZvVGs6uz92khz4FBhZY3VDh62Ewsrzh0AUUJARScxoRWzdv5MCgYEA2gQoIxEiH1AOeoQPJsmYoxWECcKTns0E5g+eEaOHI0FA6Bv1fS053Tn8W7loeIq1t2qsD3O/xnrlybfg+s84ZQUkb8ELCoHyhL2zS1QouRL8X1BPTByYt75aUq9j+D6OJ113b/RvPvbHIVSJ/OJR83HvwErsi/fVoAQYUR3r77w=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxZG1AHWVEICGrcW5QAACmboRWDVrO1k6CrfDunyLNnZF34h3Bs70mlIFzg4W1+tuY7NoQ/sQF+jX1O6TDlW7eLjHLGLcF5Pcbju2gw2VS8MrzxfWf2WyMBpSobjLpN7JgOCBop+EzkHeNh+pmy2NYlqkJglAz2shqrtOIzfgpZbkWHs1KH+8ttXes8/OORHIbHSWKTZJt45AtvEBuxxbGcU+GmJSOOJIAwhrjpnErXz5L2cgS35Z6+eitOpa9kueQU584bcLRvpydpImRY89BU4EqymjsLk++TD9Gk+G0RTdW0jaW9UUJWdwMZEaudoNG0RMdWUz9sJ87vH7cqoQBAgMBAAE=" + }, + { + "id": "QmZHtWFomPUN1NQzejuWhwXENC1vV8UPbMgvisGr3Sruuw", + "privKey": "CAASqAkwggSkAgEAAoIBAQCurUNlwA/O+b524a4v1FXVNBog9PQxodMhUMgxeNoeWH1SVP7EV+Acl46V9W3ENWi9BNxy0vgsWAxYHxlmethWRf4+sW58UOVF3gY8sQ9tPD+h/grTXr1k4HBKfkYiJK4vTlT9xoE2shJ/wOOKJ6QqOSOddqt+AudUl0r//2t5yAHLFlHOmJqb6N2gUuibJfsO9w8znSCVhSxlA3JHXm8k19RdXSfz837Ic8/3Kp+eUDvr8OrdrAZEhTYasNmcVvMyAiY5NT7Mi4Mx4fFrLtB554eWgmik6XSvbP5e0sF7j8gcBgCCm/jnAMFPvqYPQSwzi+5uK/F5djKUEObgQUlZAgMBAAECggEAJNRPU5LNQnyR1gr7aQZCGrPjlnNPUW+nTmzz6MpsYt2RftyA4vEMRbsGy0N2I1qHfOMbf/JPdToTUDkuZ9ca6gXT92/9taRw0uxfMEi80fFUEVm4+pwNHsnbozlAnE5Tk5HWhbNm5xtgnyMSVrzI9kYZ5kdxHR6Lm+BSZet6w7A4oDKHsZoreZ44A36YcXpRUlirGs8SyF6WamDHi+rnKTUc+NfeUJ4HTuxW100T+a+EGwFRKq3DaEsV2zUDQQgKJwSUSg6gsuanpW7d2BU/3g4FQNr/Q0CYRcQ8ebSzocgN8ekfHDp5wmQaHoYRTYay++4ZixXLKdaYPrf9xWmSqQKBgQDiPqIN4pEbforyxT5dIyKGE6AfFoR4niO6suYD3mzVCbOXnAvRCboz3/p6gTFA7eLCW8Hiu4dA1328UFlrIk5NvNw6gxQC6XNnp1cqq6aWlfqIOxperSXQk9A5iUD+kqcdrjPFck1nTXa2r4/+gSu0UhOm3gVG4YAIdKWq8l4zcwKBgQDFpmgwIhehuewF33D02LVCk8a8xIk7JtH/OIWD0FBKLi6I5m3sTdpwIPyLFVdQHPc0hgq3boeqFGOWR2E0fNzm1ja3+TdNt+b3Cs98eeOZnIbH30+U6ZNVJBaO2VO1rEI6awXAsHVKG7WbRP215u/sjKKanT46QV93zH1YEEjVAwKBgA2//rtXTLheDXXJtQuWChAZ+z5IcQ6flc5P9xi/GOAtscOCEVnH/JAQibmORn8KtiBX0UXFXef78MFUJMsVD6s70iqgMsQmigP/sJWATySRwuMqrGSao1S01w/YHqIaKZvtLWpPkxK3K4LY0qiGEzDnWBhpH+r5430gEb1882qvAoGBAJI6zqSl5IQD0Qp7SM/tsRVjzcqFQd6glwdTYvyl13E6f7VLfpIw+2awEQg5rtKXy5FxhmY2AWslEJTzlIhHxQuH+w3b9T2rzNaDigTXDTvuIY2ouvE6aoW4YHQpeBRWMN2VprjsFKrSm2cYAPleta12Uys/IPXqvd4dTbMkwS3PAoGBAJUWOWgYMjQqhrY99iDD75M33+G9fCmH5kZziwrcZsMQN1j1yxhZuqFZpBs2rTdwAEHma3hQul1jO7LHNqsDIZdNypOt8lVgW0NE9mFJOSVS2iAKfaQa+/fSWr6lH4LlisQ6TIi8OwSXDYDd3TJ6DCuxGgd+l7lF+BFwUEAha7Kt", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCurUNlwA/O+b524a4v1FXVNBog9PQxodMhUMgxeNoeWH1SVP7EV+Acl46V9W3ENWi9BNxy0vgsWAxYHxlmethWRf4+sW58UOVF3gY8sQ9tPD+h/grTXr1k4HBKfkYiJK4vTlT9xoE2shJ/wOOKJ6QqOSOddqt+AudUl0r//2t5yAHLFlHOmJqb6N2gUuibJfsO9w8znSCVhSxlA3JHXm8k19RdXSfz837Ic8/3Kp+eUDvr8OrdrAZEhTYasNmcVvMyAiY5NT7Mi4Mx4fFrLtB554eWgmik6XSvbP5e0sF7j8gcBgCCm/jnAMFPvqYPQSwzi+5uK/F5djKUEObgQUlZAgMBAAE=" + }, + { + "id": "QmQwBqadEKfKChHBpWG54Zhm7LiRqGgbuxovYwc2LSnrv6", + "privKey": "CAASqgkwggSmAgEAAoIBAQCgvKgMNyVnCdGFJLlnjQsE8Q74gm/sR0w9cESGbvAcVfcySQi7YqzRJwAGTFlvhhn5xn6wZRt0SnL8LlvED70PoC2oh2blRuXhlQoXVMymBRmwYjiEVX8xWaVsAd6Nr0LthQSKa1pq5v/FgfmLbuiUksViAiea31ey5jJ45mRiB1CWUvLIJsqMXrAp3QyELQV4MWnc1LbbSIjJpJKukCh3PqBu50IMM/F2uMxzgQfpl+Tmcxf/YYBhcL+M1rbglUkd4fjbRsalrX1qOTYDqcl7PzSq44L1cXUHTbsnHqMZMI2ZwkjuZpJ9gzqGdWwWlQipk9KKx+q8bDqorulAWpVdAgMBAAECggEBAIGJiKTPjIUNlJ0Ii6K4ODkzTC1acXipvPeGkrMElN1BKoaL9U2OqFemZo6s2SbXm/d/0Hm4AfgmR6SUd7/6LxN4s1MuS+5axMiXpBpJbOQfGCTeA04i2O6coIP6vbDRnc5tdMtY2PVuVJKHvORx7V5PKmNh+SkG9VZnziVBB4U3uOqzn4nbD6xr5c9upQ8e4M2MkbvX/uwCVl8ogY5p3WA0WbC+ye+PsnIz8CdILkKGxQeaps3zha/RaASfIU5rd9JzY4Y91YwaubYvrq8KxLDOjMPOrAl6GPSUdPSo5CYaHIwVC2CVuMx9IKxo8amLGoJqtqNikXCZ7hREhab1JsECgYEAzgr2u/nKxP96HuHME7yfegzysfrBXFdonx+qu5N/OU5tVFEMECgkHfs46IfRHNvEo6dv54p4BnQPVLHV4VwpmTNota7anbdOs13XHGqpm6EwUrkh2Y5Zxn2UE1pI0H+pAQ33gMKMCQlu46RP+X8kZ5iOnsVu2wyXCpNiYasOpnMCgYEAx7WQvrCbBoWzsuJfqmKPj7OdLi/kEnMeBUPsMAZN6SOc+ql8m16z6r0nx49INDYgxT96tHyKJZydP/CbDoexiBBrgbYmErP4235DDjQjDz125FZJTSdQl+YuF73M8GJ5zzYC+J06z7R/JIF4NImmvbH+VXASxooqMQXGoFgiEO8CgYEAwP7eDEc3Gn2fGwwvhxE4ZC96qSGTOvaZBeu4lcjb84a1u7PK09t1sSufK5gEVL66Gvz8Qeqe8JSHw7Jr/5K/WEQiQYQOPIr2SeaNVo3GXwOF6waEYW8IQndgWxxHdh0N5fH6Mn6IgvJ2Dv7a9n2UUQFCHhAa/U12sS5KieebamcCgYEAgRqYQx4qafH/NHJxg9WJl013HEerel3fF2pMgtNghIoYTldJDawQRX0Y/UJcXBxt3PXnnkwwhpYTdiVWJ/OCykJLLaSSQl8ETuj18nW0AIMsm/pIEs+Ko1gTjheOKAyRUGpLS2thfKrj0ra7/cLK/zDVCWiVSrJBCkYAnOG8zBMCgYEAiMkBelGls7+yjGcHywRCzNrur78BVgaPS98d/8KqkIxJil8t3R2O7f5uKhX6BxfQyeFGVN78wqIQOSSDzO+osUZJMYwF8Xaz/PVue9n191UOA4XPVY4rY/LmjU9aEuCRGN9K79iBuSvfLxYHU1LweOCD+tmyKdZyLbmaCFlk2Xs=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgvKgMNyVnCdGFJLlnjQsE8Q74gm/sR0w9cESGbvAcVfcySQi7YqzRJwAGTFlvhhn5xn6wZRt0SnL8LlvED70PoC2oh2blRuXhlQoXVMymBRmwYjiEVX8xWaVsAd6Nr0LthQSKa1pq5v/FgfmLbuiUksViAiea31ey5jJ45mRiB1CWUvLIJsqMXrAp3QyELQV4MWnc1LbbSIjJpJKukCh3PqBu50IMM/F2uMxzgQfpl+Tmcxf/YYBhcL+M1rbglUkd4fjbRsalrX1qOTYDqcl7PzSq44L1cXUHTbsnHqMZMI2ZwkjuZpJ9gzqGdWwWlQipk9KKx+q8bDqorulAWpVdAgMBAAE=" + }, + { + "id": "Qmf3RfXHY9XHn8qhvFbjCb3aoc77Lo77fXanEpHum17bbq", + "privKey": "CAASpwkwggSjAgEAAoIBAQDoAE2wmrt1TI5ovQ5m3C59Sbo05E3T3Rfjnq4390V5vJ6hbjZFvOe+U8CLEbBI7unxn6QXxg60pDWl57OVdElbMFViEy5JdG15wlOTAJkAOrELrCwWh6EqMPY6J74bDGfN9bdUsAZiHvo3lSufcjhkH+Qy6cmZaSQFqfKQdmXTHr/2dOTFWgBt7nEK6ZmR86jmMdDUMgAaEMQaIzR7OnGY7DEPXafSGFAMxJS4+4xAsQa6l+i1RmcirYMEHzU17Tk3xCpBejCGY9T6VQui3nYwshXTCeaz2ZZJW0hYiLt4Uhq5SQwyIhRy39AVRc9EvVVPprmvEdh5zJqsV6R7B8d/AgMBAAECggEBAKtCHhdyAVMzciSK+qiffInr9NK6fWEZ4lqadkzF8blNrWPJMbMFIaTzujxukE0mX/eHOuB5yb07Qob3pS98KfJwmXbOZeq/UVkCRCNN26O5r5vIOrte329/uSNw1vqsi6wT6INRdUTVTSvep9cJv+rZR+D1h2YBoR+1NmPBoqTWtERJ6VI1EDTEx9fq+xaLtVlVfWL3+lBSRgrvnoi8ucTUPZLyMmPVlrhABZ1W3cYV7VwRDtU78pzCaE6OfYQBV8YCjflWnLV8te9Nt6EV9Yim3tWkrJ9bNraUOsb1x00NDjkC8RiSUqnPjFyOmtK4H0oGzXJSLOm1Ln0dT8wZBiECgYEA+Vz6HqUxHP1ebdITgxX3bpwP3uWYUYgWONQJrxj4cl8bVXO8ae/Llr2wpxyBhxHB4Rbe4UI7hX0h9lotBdwoocI122dsouC1ON8NGam2zPE7/jVBWbzKXbd8Mnjaj0UQSAph44WoupaqzHYyRBWKFXDB0hPrGjpLF+8wBKdbHPkCgYEA7i0IBWGrH+cyRdnWBN2XTkE6Qv9zzCNxf6hTRutOH27pjqPFBwfL6UtmyST9BnrkayEySb3k63xDxz9+qDLxmorZCXUd317/xr8nsWEFlxK2fpbgBeaKLXrGDHgu4ARfAUq0WQgQr527jGZSzbGMy4GaB84fZE0QCQdzZWZCfjcCgYAa0DOY7fxc+KWZu3gbpVYIz8EpH5kEU1TIYYx1BoNYkP94bcx5u64xlE4Vegw3gOJPGM14fU23KeBoBARJWAYKdr6hy/ozfWQfLGTVOJmOafYgfIt9Yb6E20EAkFw4AZ4hkPQ7qdidz6un9qGrDoHdklVVCI1EMbZ3aWHJblFuyQKBgFM4UswmkzdjffJgKeKrVnnWWrgcL+bNH5Q+SVLHVxWumFbprCdHtxz95p0zmmFQQU2ZXMvdWQZa9gfBjMzSeA7Kowkl7dklhXN5STI2N1DRDgjvSLlnEzLO6jJjL+XAQT3fT2KoacQi9ewZuKb8ebiFwT8m61sNfiPZgZ2VfHhDAoGAUO3WHxmVc8phtNa7J7uTQb11Y2Fu8Sn6KG/gX1BdWMK9ArdkVVffhHvemmukxgNGHLXWileEuxE1WBOWXVZHKsYMw8cJVn+DTMqB5BJnsIYKIbL9eKPUKzUa8XmLJ1vX2jVgpB/kmuVdziaiHFkpo1OYWqJ9GxArllnYfycmAOo=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDoAE2wmrt1TI5ovQ5m3C59Sbo05E3T3Rfjnq4390V5vJ6hbjZFvOe+U8CLEbBI7unxn6QXxg60pDWl57OVdElbMFViEy5JdG15wlOTAJkAOrELrCwWh6EqMPY6J74bDGfN9bdUsAZiHvo3lSufcjhkH+Qy6cmZaSQFqfKQdmXTHr/2dOTFWgBt7nEK6ZmR86jmMdDUMgAaEMQaIzR7OnGY7DEPXafSGFAMxJS4+4xAsQa6l+i1RmcirYMEHzU17Tk3xCpBejCGY9T6VQui3nYwshXTCeaz2ZZJW0hYiLt4Uhq5SQwyIhRy39AVRc9EvVVPprmvEdh5zJqsV6R7B8d/AgMBAAE=" + }, + { + "id": "QmR3SNKZnyEoPHEdTc6VdzpXJqb22L85ZxVadStvrEyWZk", + "privKey": "CAASpwkwggSjAgEAAoIBAQDJQ/nhzFsJI0eLh/4WizBLifpLNRqIkYB50KYo3byOjq42QoKhy42MS1SL5lIUZWzemKOh5ZrXas7F9t839gt99CtgkyznuP9YDg5eIJK0E0N1WPTxqbGCZVvGUxtdcDbAVlR0jrypbZH7TUwNC0PKqY8izrSz7uRensW0GIK+KiZnIJ/nTMZEzmc0X5YehTwpVSwPHtRBo/+R2WpmO1X6dv7RUCbIhjpOiIVJoxLq9rLx/o2038gSZ7YXpI5bLfsqp2PEN8s/TzKqOx5cn7KGeB8Luy+cP9mVZY5R4lb8NvLqoVYKBCrK8P2EO/giqOGMUMsLOZWvEEXtshusI3Q5AgMBAAECggEAEeHvTue4Yvx2e+hOZDRjyyJsUchbHi1HbAtk6BajjUsSIZIRR0ztu7xpu7BwNI26eaC4UUc0VdWQn7hKpw8+gCd0vzT0dXZTgcBl+GkOf9+CS+fmg55fPkXBPMLfmJLC2p4RDPYtvyQUJ277xeDtWhFsvVv+ZEpikiGrUVAoNbsirfIblEV/1oz/d8/mNxuM15QkfRTSVqm6DEOaXLTb/pHX9R9rugSvng8eAmldh+pStew+8GiIZCOGgu4hE1q0tuviqkuh6lJnuWiweJ22B4oexM6OSOBmUZNiFlPysYKsD0vQthVqKbm+sV9MJAUQw77nfVVW8EHevVdQMJYpDQKBgQDkcTS5VYzE+vp+g+2GGBa8zlCDKu1CdtdMbh4X22CB/VLEoWxFrqQOgpdlhIxhTRPQ/4eWQDcGJ36vH1JKGkx0YHPlsc7U+UN4m/AVpdI/qA0TOtNedzEp+6EQ1a+YZZztCiPHhjS6PbghvZ3wJrBdNh2d3wq1vQBxrmX15bUZVwKBgQDhi363SrOO+xifisvsSEyl0WmvY13dyK+9jzGwWNvDtBTF3GL8E1CUZbG5wEe3O1OIpXFB5rlXMD6Yg3AeHaG7eYBnKK+775lrXoIrbo/1j8K9bqH0bbL4JEQWW/iq6pmaKuHhnErg6prVv2hfgHfND5Ch+TFeZw6vqQcOstoU7wKBgQCXERHnud5EaSB7x6ysb4OhyeHOS/XobQSFg5CdADL641X3uDQMhKfy/IM33AalVb5UCJdWUhLRHA8XqePWIzwJ/brG2+XGSyxGNwD2n9jAxdYXsGAU6Ud21vM1WcosQo4a5wiwKlz2CFia33TxWfRpP58oRlYjxt/jTYwZp5UyMwKBgG7NXN8m77daW+plAXF+w0ji7AyhwEdohA2X/00nGi+2lE3vyyETTA7bNsiOUa0OlCcZP96kPoE6zkWWrMnevMmhExeawVqOhsxcjY5QrqBwk7cEVsQB8F4/UsO5QxyUTAmU74sqgQ6lspM3iUsBjdlLwD4ScdJyxqA9ZZ4u9zw5AoGAZdkfTXyD+oiCTRR+VSIlDDyfNGoOqhn4GGVfttIqFfEKPEjJxDiYkm+uIvd7Mj6srppKZ7wmeDPpIFvBkFt9PN32jVTvStUBnS8G0vQK2SH1vkYxgriiAjG2T6KkTWdCnAplBB5j91AjdaY0ZntVg3wUqbGs7ppnwzOcZYzLtCE=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJQ/nhzFsJI0eLh/4WizBLifpLNRqIkYB50KYo3byOjq42QoKhy42MS1SL5lIUZWzemKOh5ZrXas7F9t839gt99CtgkyznuP9YDg5eIJK0E0N1WPTxqbGCZVvGUxtdcDbAVlR0jrypbZH7TUwNC0PKqY8izrSz7uRensW0GIK+KiZnIJ/nTMZEzmc0X5YehTwpVSwPHtRBo/+R2WpmO1X6dv7RUCbIhjpOiIVJoxLq9rLx/o2038gSZ7YXpI5bLfsqp2PEN8s/TzKqOx5cn7KGeB8Luy+cP9mVZY5R4lb8NvLqoVYKBCrK8P2EO/giqOGMUMsLOZWvEEXtshusI3Q5AgMBAAE=" + }, + { + "id": "QmcKqdVt7CJsUYCrnQCbif4QqeMZguQNCpTgZMh2Fn5hBs", + "privKey": "CAASqQkwggSlAgEAAoIBAQCvZQ+yokI12PkNaXU/ryrVh+K2B4Eu5aoQwfYbD2X5TV1Sa5P2BfEsf1teqr9qjwIIRd2gGpqSUgN2vufFQYvpN/wph20C9ILW2oA9A9WkY2Rz4K6Q6OCDmSSzh0adLfsvTa/RE6mVWsNdDChP7I5OzYKXjWXnYYU+AiigvmeFJkDGsnJ3phvR+2G01GldOVTcNU+chPsZZsTKZoQuLjE2Qn4P7CiZU/m1/s0sE5SOnAJHXGED+TzjELR+4JM8K1i5tuZwLOxjDHL1c0VnlR0tyT8hhzuHIAQLjAi/xwf7pSOcutxwVqSqVFZOMYv8B4Yu13ytSRh3bVV1ssZIDmaFAgMBAAECggEBAKLGApSAmzvH+hJlXYfVs8XpTxAp1MzGHVdh+llQqA1+q+3yoqU/DHyEUTKA+hYVVAiDdlPAfNTwQgCw2P2qgALXjzCnWxbT/p0QuNRnO51NFIup57faN4pJ1NK751+1rZhP26wIOGjAi/BNeRf7bHJbrSei9QCP840Bidzr97yLo2Dhd2Ghdx5ElV9LmTZMmcAVuRV3UemBT+8X0ISZFVmjWcysIFsz8QP4oHAPa4i5m5LYI1/d4Q3I4V1UfbnQx/+uH4LFweKFmKHXp3PHuSxwnbNBU0eD4IrjAdFJNLVfBZ7vr5vlZVfX+asi3L7GyO8rvUqMJkLW5dc/9DLBW2ECgYEA1I5iHi5/rph9nfV9ButPm/vuMkJ4O11EakDMglb6iT+GE9V1w7NQyzTUFLMfrvdxQLFQuM2WcGeYwMPalgCW0dDlYzLnpMThuwGtAnG0XfJKrrsg12m8Q3Ca8icgPCW2fSb7Qb8doJc2GdVQAEDG5lRza1wcRPCQMDcLMBRKo0kCgYEA0z5GRLlx0QBzgA7BCMrK4/hK8ssD8SZwBZaavtOFQN9GoF2IZFqoPmTonp4RpOEtwp2NMLvTOTIVasQ2YF83XTmdgSoqHagLJUobBLcBmmxiNyJwryi2HpUK4wz+kn9MSzDLTIo76063nNe3tlzKSrIAg0Fm39jIy8SFVesTbV0CgYBPN7WXvAq4HnsKqNhtALwNzWnr71vx/AOoeff+R75d8n/c2nxmcGkXItw7zrprMitSSgNkrLJ5uBJ1HUlVCGktT2Z+fxVYEkxyruA0/f2T2d49mt5VikGw/MGyqCRaZk/J+wI/caPwGwr9u8NyJLI8W5McmxqqEuIaJhmUVKuwiQKBgQCy3zqVGwpbVax3tQaTsVRX8XlVWb7+xfN5Z+ukxmtBYU7moAzMHU/1dXDcTzDVkq8y6kISEBlthxFHlNoOhNHWqODL7K446oB1lOiH94buAfSwhwMv7qM4BjBvziMKXax2xtx+9VaDi+ZCcw9m5e+NjGiOvCSmS2TL1p6pACM1KQKBgQC2GEeuAMvQ649Mb8/B0bJmfzieaAHO0C8/bLx57Ft5oUUQQ3OQeCxlXkEtCvEOCF0Lrwrg3MmpwNtxSeonLVo+NzznITcandI6kjcjZiCPPR32XJxdA9v8UITPF7bPBbFPTRqoED9A0i+70PHmZD5MJViErlwAvpgzOFqRI6h9xg==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvZQ+yokI12PkNaXU/ryrVh+K2B4Eu5aoQwfYbD2X5TV1Sa5P2BfEsf1teqr9qjwIIRd2gGpqSUgN2vufFQYvpN/wph20C9ILW2oA9A9WkY2Rz4K6Q6OCDmSSzh0adLfsvTa/RE6mVWsNdDChP7I5OzYKXjWXnYYU+AiigvmeFJkDGsnJ3phvR+2G01GldOVTcNU+chPsZZsTKZoQuLjE2Qn4P7CiZU/m1/s0sE5SOnAJHXGED+TzjELR+4JM8K1i5tuZwLOxjDHL1c0VnlR0tyT8hhzuHIAQLjAi/xwf7pSOcutxwVqSqVFZOMYv8B4Yu13ytSRh3bVV1ssZIDmaFAgMBAAE=" + }, + { + "id": "QmV7ewMcwQNLHkm5tjFKdwcTPD2mwn9MnFRFmkWDPxACHa", + "privKey": "CAASpwkwggSjAgEAAoIBAQDqUjeS41aLt1vgJd3nf486paPTOPapZ0Nuv8jJS0WnG83SP4p2sJB/dhITzCvg98mJOLsoltMOqFjc74l7EsuLHQySB9MWqNKalN+RL5BQpFNoOCKzSvfLVJU8Bxqvg+7XYxRtCrbmIWVrbHZWEbRbx3mABv8QwO64z0vy7rhoLmSD4Is2DqkJChh76xzMjhr1l/rnk6KBaEoUuAVLULvNaD98FdPF8QAJLMMqbqlr7jbev3REkmL13HBDduS6r/ZKC4ESoXO3pPU57og7QMgxkxyFm3rMkl8KqC9c3gXKq/fJZ98LP6xi1mk5SY7XJngJWLi4I/4Siu+XA5xExjehAgMBAAECggEBAL443nq1aaXkxYYy13E+gbT3kQZBISUm+nwm30Zif/oIPU9oW+y8q6F95XEC98AbCBYI4w8xWcd1bSZ2oyIxgOG/RFwsU/X6OXZaUyBW/44nyLo7kBgDTSRY1wmzLru7nCJogcV/xDkQdXf+xhZbKxKy3fTMOTaNmX6YcqyVlUikitJyKzwYFyHErovv1mJuBcV5Pd35IG0FuzbBoXLp1SrxSMWtVjsVTqe+4O6jYv4ARrUliW14Q8AB+UbfWLwe03x8kda8gW37XprShs/jukHgNqh07JIuSMdEFM+2xZVwlV6fA72zXQ5esBYC1tPfkd0WkjnhUrjQv1e56AXabM0CgYEA//5FNDf6CBF7n+pSwvbriEY/Sjf+9wt86T9dphoqPWfhaR5h9jJ+3v0qOkKgEicB4887Aj9C/gPl6fiTcOP99G/A2Bx0B1FZ5lg9k+t4GdYEJPHZKf/8/VbP54UYBs32xuYzO4aVuW7PU6xbtIwcepJN7BfIL7TkIBHqTU1ysOcCgYEA6lPM4hqfU55hj7IlEvX5EXU9VWzNnXF4/fMNS82ESL3jPma4JMTK6X/tauMjV5kh56Bej0V59n/jG5Z0Q6bFt9T4NX4M202v0/XwP/5qGvJPAHdRmZw08JSKS8yS2vD054WK+qzLHh7s/OHPE9VhRtOkc6K0QMKJmQFWV45nWjcCgYASVs3wYr43y8DWgPvy2auf9KBeGcrbOeh18UMtPhyej42XcUTFVXK/WNWonNNI2aCKCHOKwqty9SaIb3VolfzAgS0IjmlFiO9u0A2BRlIxT6K2XtkW6C7gm1KDnIZPHnw6XY14Czk4spaJwQRE1/DGWyrX6hA+UrAxF/d4GD9KKwKBgGf4zOTaOS2qDdHrkoWqPFxhr9gykb01NyE+8fxbcUGxd54dh8FQzFP03kxaAjmBCckzoCFkC2yza/q2tFm8KuiZh5R8nInGG6kp6/MJGN1h/GnRh5Rn+hlxXzeoytR4q8H6wCLf/rQbtr2yF+bN+/6oM8TXQbGmX9VTAZOR7QvbAoGASaspQxmHz4of2zxR9gpmIzg1dfCLDgVrsQCoPNdh/xn4zA73F1hVK85TqrpOql9LBjgXUQWUjD0eOPK7/K/dZ59tqMOJEg6PZAWhqlwtS4f40trE58eh6ar/g0yEqyUvcXJkjKu2zSvE0R+SNFvK9Ads5TGFo4KfygUGNb3sL/c=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqUjeS41aLt1vgJd3nf486paPTOPapZ0Nuv8jJS0WnG83SP4p2sJB/dhITzCvg98mJOLsoltMOqFjc74l7EsuLHQySB9MWqNKalN+RL5BQpFNoOCKzSvfLVJU8Bxqvg+7XYxRtCrbmIWVrbHZWEbRbx3mABv8QwO64z0vy7rhoLmSD4Is2DqkJChh76xzMjhr1l/rnk6KBaEoUuAVLULvNaD98FdPF8QAJLMMqbqlr7jbev3REkmL13HBDduS6r/ZKC4ESoXO3pPU57og7QMgxkxyFm3rMkl8KqC9c3gXKq/fJZ98LP6xi1mk5SY7XJngJWLi4I/4Siu+XA5xExjehAgMBAAE=" + }, + { + "id": "Qma78ZuAGDYoAoyyynE17JQkhnSHnF8UoyrhHBGz7P3Hku", + "privKey": "CAASpwkwggSjAgEAAoIBAQDROOeI8VJ5b9vVd+/D37NZKYShuxZlNExlUsernVEz1gxxWeFlBQ6P6+bmiR6ywSWWITRP2AdjQXhb0weEJZMrCMFbJjlZx2hCqSDWOrRbznc8asGnni+N5yX7h+PWdMASOgYXska/zcbHyNCaxmbKW9q16aZNVyzKqvnu8IVM/r2kyq7exk9MIrc9MaNEe2buTEpD90adBLnqJaVZYqrbWV7yss7W1lPF9eJl0nmBawZwFjxyEMiu9AMDaDbXMotvHt1JBAZw2Cbl0zVJh6krO8T36PMhdVqcsXGRJ+PKzcjYz7pJQ7G+xoRp2mro6V87pZR2YHri4M6s/QhXtbbnAgMBAAECggEBAK5DEUZUifcvnhDNFt00o7MuC5SRuXON/due+UQNgicEr9RxonR7o8doGygFKZaFnYLQITwnmO+pBlClEmuUyGFdp3799WK73jE3oWwN9U9KRrIB0U6FSOKOlmpMow5qq9gCkDSIfrke8eRRdL6VZeRCYYaHF8bvRxV/FzFXEBAx0kKg0C0JABEMBLq9wfcivrdtD5IXl5lIUdKUL5xImCMie28h9WOuvYD6DdTGKz3g/riVvwuUadS9/H2wKVPDUo748hTrTTdtfWzkeY9R0mXC04QcsG3AabNYv+XKpy69xj5OQWvz0J01T72K/T/yKXWMkx4MGOCzJ81N9BkQWqECgYEA/O4UJemW8GGSMOCb8EHcRKjnPbOV9aR5vIpV2HwLJxd65oNBDX8ZDvAwDRnUK7bjYlgdQuEZXh6P5+ECEFpk2LhUyWWpzIEmAflupSv8pt3SMxCVYyfsArrCr0QAsP8lx6q0C1x123y9+aYUz8x3aJ2zvKlKwGU9GGW821oWQJ8CgYEA08MDn3hAWit/8U4s3lSzMp577ky4DUE3mV0epSOwSDvESf+OjGfJkcmSZombzFnjUx7m5uT7oWk7TtPQGT5luQGkQ2DMo6H2iXZqXeqO0Yep9wWOY6m66H2MjqBIY3qe36Dpy0KVvmQlzWAUweaxYds7QTULMNSlOET0LMUsfLkCgYBg/ziQ1YUCCJkUz35d5BQB7LhJjE5tFrds26xSSjfItRN3xUw0TdKhTPFQhOA+J3QKKfqGxGaDFrtfVIYDGXdlOUOivK8ic0DNLwgEpdrNb3IGDU0M6qly3oZ8ylpthlvJt6g2Vy4Hj04XqF6A1kvQvfHI3y+AmxKUAagsgt7XxwKBgB5O9yHH8E33k+RRsBJSRSOlk09UI7Oh9fc5YL16Ei0EgN+meByYbj0zgIS0hVwGMYcgESRS+cDsctShdFNont6P4ZbTL/bro4GhNTAj9jT/GdCFNxAEpv/HmGqBflrengab3Vp+OTh61P5EFjR6dEDxQ9XdZbCjftJC7D/MKMXhAoGAR4XswSAIVDjN/W1hlfPYThb0NKhdfq4nlhdIwpmPeUwJezoHq3DwRYgDTWRrBRQPJbr3lhtOrgnTAZCniG8RsICNEl02VoCvAkqMhJmz40BUqBIwnqZOdbZjK79SINOrjmOTpfl48nanBgvKrWg0uOONoiqSQBa+1oaREh75u80=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDROOeI8VJ5b9vVd+/D37NZKYShuxZlNExlUsernVEz1gxxWeFlBQ6P6+bmiR6ywSWWITRP2AdjQXhb0weEJZMrCMFbJjlZx2hCqSDWOrRbznc8asGnni+N5yX7h+PWdMASOgYXska/zcbHyNCaxmbKW9q16aZNVyzKqvnu8IVM/r2kyq7exk9MIrc9MaNEe2buTEpD90adBLnqJaVZYqrbWV7yss7W1lPF9eJl0nmBawZwFjxyEMiu9AMDaDbXMotvHt1JBAZw2Cbl0zVJh6krO8T36PMhdVqcsXGRJ+PKzcjYz7pJQ7G+xoRp2mro6V87pZR2YHri4M6s/QhXtbbnAgMBAAE=" + }, + { + "id": "QmVWofTULvSRs48WFfw3GR761vdNPVNA5MDnBbjk6D4osa", + "privKey": "CAASqQkwggSlAgEAAoIBAQDHMD4tKT0nwnW8GZSN55OIVPe2KHcIcwSacdWPwnQEwYCDVY2CKOhXP6cxlJwCa0qGfclSOSGLakFg0gZe1SELQ4kjbRQAZqcIJeGegURaZBJTAes8DnhUgPCGdCxx8u+kD+A7ePwl3DGgFXWLfkd4GYq+jcv4OgmxBX+MsX8lQdl00hcxnm1KFQ3N5iZUT0DArbrF2xino5QKayn109fYuUg/hbaCJVWBp4T0LPfDvqkWd0WlqCpuzvnmM/gdUbo5dJaX38EEzSnrvGKvWUXwSMbD504nM2c5BfCiKRC3o6H7L7Rrzp9l+4/ijIR+huSYcKHfCc/QVONHZFUGt6D/AgMBAAECggEBALbeBIcjkCCbd8lr1qV80EZec1iJx9Y6+iPslDs6M5dWKuzoM1Umxz+qkY2EOMJDzBSzEIvrJW+/6ivARdb0ZjTYGOQsNSu1Em35kSwa0y0FKImKfnMRecli4u8dYtxuQzHk3WDcTulXQTwN129sTKLnGCifN345kkIxoQsCvY7uSVNwFxXHWqbJe1Q14h95t2ZMuEAQLmp1qS/fZvEnIpP54ig0TJFDpIKAzVJ9csfnFEhIXFSPdu8AdIuiekVs5WVt4YDye6ukrTreYYUj4wKoMtzFf7/HZv5ylTZEq4mPHxBVJZl1NzifTgEZt6Xbi2M5fc1upj/zyIR2r2J0n3kCgYEA7YyYnf1tt8yYvGfnRNt3Cr2TsSkkz5szY5i6ayqz+kbTwPDvimtho6noPU56inOEjiwgLqbUfLp59EU55dkVVysl+/tvzwiODZHOrtuo7NDYoR+TG1rqwmS7sG2YH/Z96rDkuirdT6g9NTN6hlXddn09uvo01lFatPUrJd7njTMCgYEA1qjiqybcvyXlLo6V83xGTzEtO6QsVVnOwRN5LH9ar3J377ysinIdVy4rexUj60WOdMwI26daUgG4sXN0UkSdJYriAzq723FXkWl0vqHHLcsqiBpZAREAtnhr81Ep0S2vYeXxJDh/YaVrtuFC6V4IlX1owU+z4tyuXdIsb/kQpQUCgYEA1Any1Nu5ix26nDpW6lTH/rNX2QRz0kij51wV0sEMY/PC4m9uMm1Fe3SXU9nwchNJGCr1Ew9hjnBZmZwNYUiTzvo57qHynKfzPNOQ3O4/7BtDsxbfFcTi1mBaVNKOvsEkhlAd6ruGTqtK72kjDjQo+bIzXuomLcPypqJNQIGmyJcCgYEAz+LdKh3H2QKtMmp6REUNnnIkuUbkfIAuIIRobLSlsZYtGyrg+m9LhPu8tkWh9J73OJvE1Fz9tr/eC1ShyW6TtiELg08lUX8x7CxyGJB6YhoJBahmnAbIZu62tPzcf6071Zan84kXrAxVBdagQ3uC2quKgPRcZQ+NemUqDgLqoLUCgYBUT8NoBjKQn+y+/MCNC16ydElQ6VC5FZR2n3/gMNsJQ87/Bjo79kTBaV2wu9yvHXrpQTH4pXrmUUkJKZVkeyzLsPlruIE2O3+UQPN8TIQmPUb7HbZYfEN5O1BUN1u6M/VNq14o7gsuC4lLZ7SM7u0TlWsFjxiCBMmqc9ebvtAdZg==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHMD4tKT0nwnW8GZSN55OIVPe2KHcIcwSacdWPwnQEwYCDVY2CKOhXP6cxlJwCa0qGfclSOSGLakFg0gZe1SELQ4kjbRQAZqcIJeGegURaZBJTAes8DnhUgPCGdCxx8u+kD+A7ePwl3DGgFXWLfkd4GYq+jcv4OgmxBX+MsX8lQdl00hcxnm1KFQ3N5iZUT0DArbrF2xino5QKayn109fYuUg/hbaCJVWBp4T0LPfDvqkWd0WlqCpuzvnmM/gdUbo5dJaX38EEzSnrvGKvWUXwSMbD504nM2c5BfCiKRC3o6H7L7Rrzp9l+4/ijIR+huSYcKHfCc/QVONHZFUGt6D/AgMBAAE=" + }, + { + "id": "QmWLMXM2fja2TJiScgBiP818p1zKwBVQu5u7BfkLMuHSkS", + "privKey": "CAASpwkwggSjAgEAAoIBAQDjJRdKtjxJNabP6q+5j/CYoaaBn3mMkvggRkIqEoI+Vws0FcnxBhS+tDK1AyC45YRq/pdQBF21coq4SbYthlqk7F9uzGG8dEAc36fh0IW1hf6GGzsreVZ7PDicsDoA9vRyNSv9pQpn1oVeJoXfYLbd7LJ2jXFoQj6Gg6h9+/rnc89PH7ZWOLQAOTgnyW0X/qYE1rYE1d2DGgorHaXxuAUoiKnk7v9cAJo+6buvlb2BpjdjE/0FuE/VYgWAIvkSC6xylVo//BbLwLa3L/GhaqhM3baAQrLDn3QXAZkCSHbEHUz7OJT5jGkkuKzxwY8v2EqHtOJ2hjUQ1/kEyuGc//vlAgMBAAECggEAHSe5oaFag5sbWjypaYwMOcsl8CVkGKMUri/WdkgB57aMfZb+YK2DJCdFCu7n2FO9d25ORbsJvCyDmbv8YkOIhvrAFRyuvt29DPe4OMfnIbGdReFg6uecLh1Atvv3tf/8yFwC1X/FoiZq3/bZ4B4t2rZzAbyN/e/izqk/i0w4nFSHM/txEjp5rM2xggN4eJtRdljyL0V7AB+uVmdFNHpJpNoiYhQLk68UTR2XycRLZuYOGnEb/svUloF3hKqXDth3enlb3apCGrLK744bJrR3K/UE8nrCTyZM2IkBLteX1Qt4IN35ktV+kiIKrKapx5lq00gszp8mtY3ZDi3iO68vgQKBgQDxqKn+7w/NhRxDHOj0D4jJxvKrONkLWmpVbDF5YG046n1g6ZCpmQdD+qGnkdf2a7Y3nP9ktcBB8bHwyMVDEnLMLldy+N6RhE90S98JyxJeuTaHA6xeZoQPBYbby1BbeM/pKFoYPYure+w0+BjOmJ/2aYTFB5nVRvO5QWyqF5ge7QKBgQDwn+13r9jN6cki0nxVm2VdGqMkuAiucWZZYluxBLVelsrpqQEpGDn5/SISMB7C+K2A6kIrfOSYl2v4xmWClZWEwdezNOmrmF27l3j+k4a7QrI8SJ35fpCAAZkemqSjvCsGsHobUziSk/bOcaUl7HTBSgqegS8p9k5w0eLkFnc52QKBgBxWX+DSHbNG6aKBEFwWOj8XAjvLxb/MEW4E6uRFx2kGLj+gA+OvWyfAixZARufRh6OL2HLx0SKqeeg3UtJjpmyE/qIvxsGfl0WW5Aec9YGADIHWj27ebJvqVP8PH1QqnY7EuZPgG7G3LWKkuRv4z8EwbG5CTuqwkEHoBxsG+0KdAoGBAIs0WlL1fj2R1HS6xIdaAQgd0uf5N7yDiiyHxHfiO3LfVcBbr3ows8m18bIH+KPe7bE41NMHy8+jOimPzUYAd6F36uuX3t8QpJlUBgFxI27OJQLPF+UwnTQQuJ6F5F19DH8wNVP9jyrtQskn4dzQsLLpvL76CWkDP1nxcot1m7/xAoGAfmfi7946neKjYp7cJOsj9g6nXTBbFeFczt7zkGCiO8MnpwdxmlFCyl8+yhKNdQy8tGzVFEEkJdSSxnFi3MjqMZYl7DP74k9TaqdrAp+6AafUxtaQ6ivuQbZtI9HTHCt30i8KtYR2KmUBVzhf70F9DLRke7wA1O/JM9YQh2/5pgc=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjJRdKtjxJNabP6q+5j/CYoaaBn3mMkvggRkIqEoI+Vws0FcnxBhS+tDK1AyC45YRq/pdQBF21coq4SbYthlqk7F9uzGG8dEAc36fh0IW1hf6GGzsreVZ7PDicsDoA9vRyNSv9pQpn1oVeJoXfYLbd7LJ2jXFoQj6Gg6h9+/rnc89PH7ZWOLQAOTgnyW0X/qYE1rYE1d2DGgorHaXxuAUoiKnk7v9cAJo+6buvlb2BpjdjE/0FuE/VYgWAIvkSC6xylVo//BbLwLa3L/GhaqhM3baAQrLDn3QXAZkCSHbEHUz7OJT5jGkkuKzxwY8v2EqHtOJ2hjUQ1/kEyuGc//vlAgMBAAE=" + }, + { + "id": "QmRMiv45CpntUvuTrYK729xTAey1v2fbDj45tvybnPv3RS", + "privKey": "CAASpgkwggSiAgEAAoIBAQD062HIcNUubYjjp7FBH3ArDRrHD5GARlblGfmFqO9lqFtJzqiVZ3N0j5+hVCEurPysugmekfvmMMvyWSGZQ9wuzZUnhLoA9qxTIRYirIFcsFHN8zWt55lI8pmU+Annn094J8eivioOcK2smMZUP2th8bO13RQhk4ysUFTmTycxUeEHUjvChJfBGmhviTyyj0ZjjlMokzxIQUpz4mRIIJrjTpz0FmnS3Tky+FM/7xGkWxsRBHg7LoTDUecglYuDBUBHTZxiQJps5ZwSjHcgNu2Z4n7n5o/9eqdqQstKROHMgVU/xevq2mkTfsYQg4yV5wfzMi0nAQkKsgMCQnF61B5VAgMBAAECggEAM4UYKqvhjESqWQk7djGAzO2SxnD18H/bY5IVNRKWWVIS8fGw8My0Mr7Lm4ow+LqW5Fa2E08uJ/v3yuEI+k6WEJpaV9fJsM4y7cG2AFE9XRwR+TrB/fUL/5ZYzEdo/fo7I1Fq40cRXbxCLF5uyeYVVqMbZJQ+2dlJ7C1xzoiz++GOE0x1asnhL7QTCF2AXDIAWBf9A9Llcx+8e7bS1WXlOHVCXe5xk6TM78YxLIyNFcg/tm3BeWEdCndUOAq4RCUM9xg0BKjDHm/lIWN5Pe5tVCMVFQjVvclT2uwCjxmw/BUACOonOBIPBNlyXHdUFJYqrQtli2AikURckS8V9SXWgQKBgQD95RCvFjvjWCYlzA8wpSUhPa6wgUTv3v35QJcUPF05da4T76w0ynhRAmxoHLy6oWLLy03f6p5ZGWvEpVKWj6C9iUTlNvTeQt8uF0FLJT3FzY1zismpjhO0lHyxO4b2l7N72frC0PcRr5DIX1ToqLQ3PaSAo4Ik4p7Bufo28js6BQKBgQD280PgP//WlfF/k2K10ivHS6zO9skFB4cj+aFHkvklhClgqXu1dVDz1zS8QQ/FKPnEeIeKoF0+uU4JiSJXzI7KpBbYR/1pp+ht4i0m1D5I/XWqwz89SsNbfQ0MEpmFVn1UVGizxkOfdeV2sWkpVPXruVcbBn3HMvoT6CKo6B90EQKBgBUHR954hPVLc71lrUxmITEjnrhXvJHxci+WveybI02DBav5CdZFfrmW7cuhta/vdfNTH1wQXpUameZrmu+FbdbE68PgTV+rQjjbDDlhGECtya1sLU+xyMgIFFvJhmAeKGt1nmNMU2pPlDpESxAKCQMaQ+mzoS/U5Tk+Yg7y+RzRAoGAFKU7zHitQ9xfR0IkI31BqbS66saZAbGia7CH+jrgV3GYH/urWv1G2kvoncB2uA3366QD+OP1R7QoQQXU+DAi0/Ap9MT3+6hM9zrMeY2O8301JCSUmeQxNunaeGLpsMsNSUqRQk5WAvPRTj4Aw7LVqHJjROAjDc9GNvnWQ/a8AnECgYAe4HhdJzLjqtiRbKRgpjxs4IuoklRX90wIYNLmMJl6QIcmfJrUbUrfA4RTcf27tXEwtR1X7r5q88bC+dahV4Qb64qf1fkPdh13Vjlw3ncNeCIVuRTl5u3Oho13xUphjgWcdVjrAbll+52lPuFl9D/8Lv5joz7rQxefAahbzXa7rw==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD062HIcNUubYjjp7FBH3ArDRrHD5GARlblGfmFqO9lqFtJzqiVZ3N0j5+hVCEurPysugmekfvmMMvyWSGZQ9wuzZUnhLoA9qxTIRYirIFcsFHN8zWt55lI8pmU+Annn094J8eivioOcK2smMZUP2th8bO13RQhk4ysUFTmTycxUeEHUjvChJfBGmhviTyyj0ZjjlMokzxIQUpz4mRIIJrjTpz0FmnS3Tky+FM/7xGkWxsRBHg7LoTDUecglYuDBUBHTZxiQJps5ZwSjHcgNu2Z4n7n5o/9eqdqQstKROHMgVU/xevq2mkTfsYQg4yV5wfzMi0nAQkKsgMCQnF61B5VAgMBAAE=" + }, + { + "id": "QmPBJx7XLo4dgVJSDWeQ2RQGUsWL5DMakxp1nuEHbfRBCh", + "privKey": "CAASqQkwggSlAgEAAoIBAQDis8cGry5aggtcfDuIm3TcHIyFsoumWMdiuz2G50RZRFuWcvTN/unNV0VZQKFXiCPcl5nTxgM7MpwUJ7rUmOmRTcslipoNBS6b2Qie+QQ5R7gi3sv3G7Xqt5DXH/On6nRJ/J8Nc2QIhjY+RqYdlJLsBzoU2ZXvhn3+XZ7msENvM4C7r1AkX1PQ29GTgLZq+aohYgPxw3T/zkkXA4BpsyKuTq4lobG3/17j30x5dbSkvbD+9Xa+h8ULzs1Yb0Oxa1Uv3oAWQB+G1EVV9jiS5ZNC624scXSnfNPPX2Sc7gbZ2r/6KxaUX404xDidZ8PL+nQEn71dOwTfH5BgUf3sLpbtAgMBAAECggEBANthFayGQdkcF1p8bjAYH9IRXnrS+QkUyH3mq/esPRwVZnwE6pmdoSsw1M82F+vYtrt1iUrCR8Y1h4yJlea9wuj12yyNH96RsTTnpcwRNeEoYix8ZezPSA/Ha8aCCQvpendOm6wsihUlq8BAJehiKnt10uLyXIHaKHrKpkafM+KapipskYO/RoCYWwlKfzTXLEaT4hylnahw81MmNkkdSh8O+2Aoqp+tj7K2soEoHfKB8gUImZVKpGhVhBLgc5kaiaywVSn/5+Zg61vIMfXw382K3QIgKBCGkOG9Su8QroY9ukg9M0LUZ3qT6Hl+QPTllGL/VaH393SdBJ7UE/J66ZECgYEA8e6tBxPuJV8P5JjfNNP4rJf7aLm5+ZSsUngRVAS7kBRKdHi6m6k/RNfNbVlRW7RJAdH4aSoYS2wdmAeenf4TFK9Ms629Qvy8C01H+G0Wc2SQFwCxVLbh34RMjAJnNaDQkMRR+KqClZCCVJCstKp6jjBAlVEjQUnEi9giEuyt4X8CgYEA7+JkSzn/48SuJ0K30YRQHnfZXyLi7ngKCRNjGFPF4JpmScr7rXn6NwM2Qq80OQ5LbaRU4OUyznXyOlXqRxOqQ5r+MopdntGm31pyjPJ37WKqSSbiv/J72cvARWVoIgHZwmyzA3c5wENo77voRmtXuHTBGPYYVuqS7W1dhSchZZMCgYAHkF/Ghd//OR6lFgZ8f1gaBVJt7siavgQ5O9nG17MJiSjexVuIPF1nCJw6m/1IMbXKocbzesZxrTqroylZZTUxg12RjJALRR2X+N2ULczy5KUAoxvoOyOKuncuNXEGozWCITEy5hnZFUCN5kwQBfh485dBU90XUHgdqy89ijrRfwKBgQDgqyRL4HJtHJQPaUlIDagMISuREtd/iSW9Ak9ZRXyas4EBs/6rpwZV58g2BGng1iaG9dYZHBDNlWHciQL2ttBMqSlSpIyyZqDnp4qJv73aswHRqX3NUIn2ngHh/OvejeUpVy0CHfQUwg3SGiedbIOn2pHPkw2QufoKLNfeEZwRyQKBgQDpI/WIsMBz/DHft4mEZ1FGguioSjXcaPLKEdIrzX7nS9ZI62C2srbtEh5WWokim7MmUcLMYR16xfXaN5ll35SONZ2nrys6QT6hKdRcDQPwAZdLQe716gMarYg7FMHCgnrYJMrPdhd86Or7NVzceY4y1gwOc5WV1OClo+vbZLDsHw==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDis8cGry5aggtcfDuIm3TcHIyFsoumWMdiuz2G50RZRFuWcvTN/unNV0VZQKFXiCPcl5nTxgM7MpwUJ7rUmOmRTcslipoNBS6b2Qie+QQ5R7gi3sv3G7Xqt5DXH/On6nRJ/J8Nc2QIhjY+RqYdlJLsBzoU2ZXvhn3+XZ7msENvM4C7r1AkX1PQ29GTgLZq+aohYgPxw3T/zkkXA4BpsyKuTq4lobG3/17j30x5dbSkvbD+9Xa+h8ULzs1Yb0Oxa1Uv3oAWQB+G1EVV9jiS5ZNC624scXSnfNPPX2Sc7gbZ2r/6KxaUX404xDidZ8PL+nQEn71dOwTfH5BgUf3sLpbtAgMBAAE=" + }, + { + "id": "QmQBgs5nT1eTpZ696WjEwfyzG88Qp1voMcS9TvzxHRVLu5", + "privKey": "CAASpgkwggSiAgEAAoIBAQCkRWtbeLJBKVcxoikKflKuXj+YYGLozExhPPubZ5To3m9uHC15YRkyr7aBR6u7jjXuqFF89iE26nZusfz/OjuSAn/XApzxVjf1sKwfIyRCUSmdQVSxjFx6rHxLTggZ8DyLXAl2MM7GkSFnaEgD3F0CdCtdiv69M8dJXZMpHZpSp4gIf6KQNZKMDRCTvatETnOWnn9auoMsR7L/r40EDiNfog/B2w6hiWkJU1KcqAGdEIw1jNWp54C2NEUT9SwVfwJF9xWS7uPQjcwGTzUkB1gwd0kkLiuJOeTJuQdGNWcSejOTBhf6j+E1zqDBjjnYpqvjrJ1AzIy6W464WOv8rGEzAgMBAAECggEAT8IU2hjEE7dKyqJ9Ysbqxhaksgs6euWWwC+nylCEkHxPhhrQSXrHjUCbUDHIRAxMS1AWIKIwhkVYs25f9FwFyGLGR1LReKvwB658I7IgTMKzfdLWmE4aKMXOo5GDhXSLMO7C+UvBWc/ldBvBu34m+qYE2CpAfQTC4fA5H2FsKGy/3gfQ2lgyA21ZLlV4r212W7hHLKwRFmwt3jZbqDijlC5V5cZjugxZBmXVBv0xInth5t98wYWUyPHPHxC6yxmYp49E+5DMeIMW8Sb7cyUdTKaiVPC3U6mQ77kGANcNuzA5frC2ghJ4L5K9QtZ2csrstEpyzDUtbgXpILGY1kzzYQKBgQDWbakUXbHhc7sVvX8rdAuwB8iyUHMJfoD5GUQkv456OTE2+SAk8lfGJKO0byu6Fk5hczuwFNikOLjAn7zlDBXN1nHc1gaihx3xro9kbVyZAaBL1VKnrO7SMwKasZqij8rF9dmTLoj2WCapUkL+fShLZ45xRy6HtqG6OivhRgI+LwKBgQDEHmWLcc5ZTueFJ0lqoZS/K/xtU39DVlSLqzyiG7kSwvxubMlh2EGbBOBP+KBac5MNCa7s3wX+Mg++HYZ+nWBK671fhoHKVdSE/yvwTFYbbobNdVJLBpqmxZ3jaPVmntPyuS+Q+J8+hSKBUVXn1moCprt63I7ZYi635Spbx0FwPQKBgBQ7tMCfNrG3YefjQaSwBopNsbphrPkwXqsinzN4f9E4pFQznbzewTmJK4lTFCUzBaJcZT1wOiEE2SoT6JcUVNpfac8UwvuDFGhBuHYZe6tUY/PD5VC9GKvs9En0iErelpunXyhsM7BLERrZEElDNhIna2F0/gcATLJt2sYe/QcxAoGARBsmOmm4Ni1/my8MgNpCFK2KyNCRRmopjCNYOBeWitxp21MuECuQhOwJldwKm769F28Ab0P4CdGKxAPu2/N9KCd7qTvFwOVTmygeMyHfWUFl0N3oSSOu+zFvZjTh29RWWFlVGmwsqQKE/BGp2owMXoVWYBFODlHZVYVg02R75iUCgYBVnTqbTUn72npovyPawUzLIIpYCNyWU9gKiHl9WtVNZaErtz2EFid4GspVG+OfhgVtvcBLRY7LQjPR6kvPm46DzkWfCZJ8gnIUAgiIz9rguKCQzeHX+kphs4uF9Am6FqKhyHFENx67eftVVc/wjynbqGRNh6M8YoBCO1b/co6f6A==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkRWtbeLJBKVcxoikKflKuXj+YYGLozExhPPubZ5To3m9uHC15YRkyr7aBR6u7jjXuqFF89iE26nZusfz/OjuSAn/XApzxVjf1sKwfIyRCUSmdQVSxjFx6rHxLTggZ8DyLXAl2MM7GkSFnaEgD3F0CdCtdiv69M8dJXZMpHZpSp4gIf6KQNZKMDRCTvatETnOWnn9auoMsR7L/r40EDiNfog/B2w6hiWkJU1KcqAGdEIw1jNWp54C2NEUT9SwVfwJF9xWS7uPQjcwGTzUkB1gwd0kkLiuJOeTJuQdGNWcSejOTBhf6j+E1zqDBjjnYpqvjrJ1AzIy6W464WOv8rGEzAgMBAAE=" + }, + { + "id": "QmT1CJvgzYwXZ62aYrEHfEHQBBWGDy4eUfWM3nWr4Jh8f9", + "privKey": "CAASpgkwggSiAgEAAoIBAQDTlIuqJQX+QkocvDbMmg1M3ougjVu6lsp6CNVbuSIIgSzJL+XSvr8lq/n2sgj7T77ZWE12ZNcboywh0XMd9c94m7ZAEnCxU2j/fKbsC0pfydrETXKSBkLqMsG8BpNCBE4k861z5vg4GKrqv6LK5kyEmoXzMW6D2lz/iUGW8HMoWNP6G+XjC6kuf5fXzUPqV8qjZ146k8sOMQVM0j4ydfaTWPoMQ6vUlziRSEPhvtFm+rIN6+Olrp8yLUqbwZE3Qr0HRa2GuK4gUY/ABDC2C038iZK3jlfphAoAXu1Fk6kLC2B97eAziAY4x1S9anhMkl/QSaPmKlhtBdniyeiylhLNAgMBAAECggEASFlBMNscI4dzZEQP0QLUjXaD9FCiDznE3tnqa0TxGEtjjyArzFahcjZOcUcLvzPTpOqHAncU/0RasO24GYSWfz1vopUrp9dHziVHxutNcVfK0NlS1hACDadxi+QLssVaG1Sf+UP/mN8GguMovYzmmWllaHV8vuWKa2752K08E408huq5P2NYMd76dmF9/OVttlsa5WCOnEXhRxBKvPXLaxmbTMf7hwslOwjcKYzOPu7UYEWgPTHYJY3TW+oa1T9FDQ0t1m4XBZ5VQYfIMwZFK3O5F1QRK+ZvdPcJahHcyjAggcJrmRq/C+pUg4LyEI3Q59b/tfBHOPLGq23evZB4QQKBgQD/PK7r8xaSeyJMZ7e4x+ZNHhw6H9VYVu8yD2aloRqN+tv/YXTDqBwPxPnpTXd5pzU1AszFGMnCvMoglT8NzU0U8YuFko8WZn9fcj4puBh5/bK9kIrxvVGcGB2E9etqh+FA3RIXLKdb0Cg/IxQXpCddi3weQaMc9KGu4mg00JWIRQKBgQDUNnRan9ON2z3mL/++SM9ciDs/g+onijhfJNGtqgC5iDt48BaetYg/XmlG1bOGfzdvHfhjT29MXqer9X62UwUkthZVVQ6juU2NXGgVceQ0LyxR1sjQV79s1x1ZxJLCHDSrv/0DFhzR50rAXNzKfZqse3/e05H63jKAyhdVfFec6QKBgFNT6He3wSLJeIUhR2XYPWPn9Kc1RV215Bmf1Y81bYn04GYb1xcZhy/OyxhT5jGdWU2ZQGkIkrY1FHhWwWMsJ4Iy6a4ODxDtHaB3l7vfEn6hSP2Cdxo9CKvBY8HI17bL7cpTYliWh3z+FGWoeiLNwNxVcYwfObV2WGLUH9k+T9NRAoGAeIEfm2KMwE4SHUd+YzB3R+U4N74R1jggAK8ovgP+e1yH4Qn83kzZPJrBD29EjYhqExgit0ohOm25quUx+hi0n/8iOXyeXoxYJICnvh25Z4SuFx7vU5ewZP7WdOYyLDXWFv1jkEAbR3qesRNKcVXX4kATAZaEm79XUFjx3SJJohkCgYAM0G8Pn9NrHRpS0s57DIMjZnt2F4LzGyjByWB+i7pOCAHsDnm6FdAWkcyI3hpKZcsjTBJcV5hCfzxdmJsTS0RpsAgJdoiwB5bkdLqm2SyYaLmxCaVmNTaU9DtYbPqH1hJ1WwW4dmGu1gCl/V+hOt1DrmKW3EuE5ZC9lCuebikseQ==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTlIuqJQX+QkocvDbMmg1M3ougjVu6lsp6CNVbuSIIgSzJL+XSvr8lq/n2sgj7T77ZWE12ZNcboywh0XMd9c94m7ZAEnCxU2j/fKbsC0pfydrETXKSBkLqMsG8BpNCBE4k861z5vg4GKrqv6LK5kyEmoXzMW6D2lz/iUGW8HMoWNP6G+XjC6kuf5fXzUPqV8qjZ146k8sOMQVM0j4ydfaTWPoMQ6vUlziRSEPhvtFm+rIN6+Olrp8yLUqbwZE3Qr0HRa2GuK4gUY/ABDC2C038iZK3jlfphAoAXu1Fk6kLC2B97eAziAY4x1S9anhMkl/QSaPmKlhtBdniyeiylhLNAgMBAAE=" + }, + { + "id": "QmR5UUZVurL8evbznj7XQp4DpKFBrDyH9KFXFRAySeZpx3", + "privKey": "CAASqQkwggSlAgEAAoIBAQDJx/BetELzrq/0sXVMtMy1tkAIog0tVxK6HKCKaHSBXKl+c1ZdMGXb39oJlbYx8a4lDBm+Tuyz1dztXQdK7vm4IvNWpTBEEGafbP/QiVt0PTcXLEGa4baBN7sFFYITof9IA1NjFwHaopidEarMQKRgWIOdMRMUkLZBW/LuW6FyY3TaSkkOcEoga+iNvJBprqb6B+OgaDc9UHrYhHd8Pu8reMdIpc9Dihk33MvH/kBzuRuc9XmyJYr0TA+NoVwFBFdmkVfqXZh4AefwoFY4j5dxkMmYl6YIrbKOYC5AjsyV9/Ut6MNGKMZHj26IuA5/Qv671lit3zaCzRgliBLbWwwlAgMBAAECggEBAKKT2fQ0QY2dvl/la6F2O0pdoZJTH1LcyVXzItbh+p0QT/B2dat4B1n1LvRyD+6Fz85Bxaw0YzsyoWMOhYW2rrXSU0YIqq7smAL0C9m2El3BxNWC7wkfu7C5epJKFhhDejBHnbRFIVVLlLNZPMh9fPOz3beVhs4XGwHmWAH9CkPiuEDi5803zcC61okxd+Pb4uv5o0yBgRZhyFZkAvHthNUOGYCEkdEUS6IZ+Mzv4zm/faNyEiPc1QzV/0Tyl4+JKqeYr4c/l3mzAPsPLKnSUSFfJpWVQiSfps6yXciM/k7pPAPY7Qfli/cwv0AM04ZRoMrNpq1KVa4O1QF9PAivjoECgYEA52lvAKWA2VAx/F95biy0ch1WSF489nV5co99qmcYBhzSqXBDtB8AIZhqSNwTh634/J89W4+dUy/7IUbA93jhb+DQ5f33dYPvs7CvxLDf2ZTwRqlJgYjjO8xTLJhsbvq8hWRPM20usvqKm+HSNlPEZIlqU+8o1F6ovVE52mdtpXkCgYEA3ziGiM/LffV58NC7emjJOh481BtXYfE6nnC4pJnkJeru4lYiI2pSe2IP9tXsJ/EKaqVyMz5xXnxMEBBMDqrPzAolt3wLE1//YWKrvvY1iQ7BUuBulmbEETAxwLxsuM9DrwVm07KlFDB4PCmaSxz7vwpiUKvvhME86QNrG6BWjQ0CgYEAupUG/AV1i0Ie23Z7aPrAjxXaYMUi8DCtuFG7p+4xr5QDwrwSyqMILw49+/r+ADKkg8M87Jsx5UXVfPIYWjBrzBH/PMF1iZnUZhR60+MsyblNtht+eePOG4PWOCm5nA2sfPzw4u0QbxyVL7R3Enid69XkFkLJr9mWwfZA6DjmbjkCgYAdn0mzNCgkAgR5oamPeAJkNQkLeMli1Plbd0f5h/aP1Zp37MhA/OYPO5c0wXTCZClmt6ftBvdSe5o4c4E0XRSQVjRlIg/yNUYVrVtvTwsRsiSTk/b4/JA+W6/ujNK8v7ySqFvZ8hYYFMqP6VFxcFo0IvOjEv4JFv+3SF3ZX7C8rQKBgQCKLTjP0Sk0ojST3Z+7L3G9jDuyquyo4CizrfDMTnu7PDX2ICbwdpA6xdrWQMS2LFmmBjiNo6+0Ge4FkDbo2tZXBZE2w/oySre/vFrU6qKm31ICNHls5qLXGIAkHivVcLObWAGm3KwPOXw8QY6oCmRZfzMnXrGFn1MCnmy99TRwLw==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJx/BetELzrq/0sXVMtMy1tkAIog0tVxK6HKCKaHSBXKl+c1ZdMGXb39oJlbYx8a4lDBm+Tuyz1dztXQdK7vm4IvNWpTBEEGafbP/QiVt0PTcXLEGa4baBN7sFFYITof9IA1NjFwHaopidEarMQKRgWIOdMRMUkLZBW/LuW6FyY3TaSkkOcEoga+iNvJBprqb6B+OgaDc9UHrYhHd8Pu8reMdIpc9Dihk33MvH/kBzuRuc9XmyJYr0TA+NoVwFBFdmkVfqXZh4AefwoFY4j5dxkMmYl6YIrbKOYC5AjsyV9/Ut6MNGKMZHj26IuA5/Qv671lit3zaCzRgliBLbWwwlAgMBAAE=" + }, + { + "id": "QmNnW81gX8pPMGraJ2SAUDiHwc2j7bHtetibjjmFCQKpwP", + "privKey": "CAASqAkwggSkAgEAAoIBAQC+bfAus9butA/hM9mMIqyOLveGjGfe0GAm0W/2MvJcEu4QpQJTNekf+DEDbW/6ij7i5xXp1iVoAsbWP3ocZiq4F0jK5P0zm+v7DKqBI1kSkYoipM9ax+yzkuWNuNnKep438+m/c+JIz0O9ediJzKArvYLXwXz6Zts5H/mu+XTSmgH7JBhQezbVCyiEYykTMHg2zXtf9p29ngTYrspgL/3bt5IJYHPtJs7sE3Vk3c4s0MdwDBruTEZxvBa4F8/E/SO04ytwnDM7sC/bXpSJszIjzO/gNpgFR2YoohDbXgfOg2LNY9SDzZvgRjh4jh/a0hL+C5qcV4f1dSzUcNWPMB5XAgMBAAECggEBAJ1i1ZBz2F7++OTi6pk0izMUREWyw6JWzDEpBgUQRFiu2I1vI4XMV7mxcijoCRWjrqfv35+c+tuE9/tZCT5xfjsaG3J9YXuHEbky1bTXlyWOngFV/36DH/GmpgzEy1PaleuerwEp8MrGarEuBDaXpYWhIoPZicaWvfbvo3fm+pOXgqG+5r2QoiaO+DKFfiQ59Q6d2nY+1YOVYxQKH4CvFOh+gGDJflaxseFf7Hb10/h07UnVqK/dVOV/uePX9iQywpWnv6MKH8UP6k1VbUUATy3tl/03sLrvDogNjdES2Jgg5MVOZg4d4Ng8GQL7HjPz3BE1wVIMdZwe6mYr1MafGEkCgYEA4WSed0byDNX4+Ib+JXdRfqvtbMnXh6FdjW//BtgqlSLh2ddntVb8xyv7xzR8zX+Rq+XIyC/auWQ2nkvvnt1CNZ82CjFuDpQIrtBkv25mUBcDXqihMFuMPbH/xFjvWqNLazZKvfuK7Kwxu0pEvIVwSB8W6vTV8NOE9pxe9bAqnsUCgYEA2EnfgbzMPuoAKZoue9BtTlHzvKacLuXX14ozJwB/GCP/PGXbikLF8E/z2KyahG4xhUrLM6zsTuvzxpG0E0yE3LfhnTHrXImsEptjpsSaY4QvKNGcSWNbhOn+OxEFip6kWeMI3tDzh0x7cNhEdSa+Ggpz6TBA1kJOZ4wIDHw82msCgYAOCsnAnp/n4tDPEwH0bZmbLbTuCSkvXwNIE4ks8Py+K90sSDpC1veKpRfbkk6QMvltXzSCHFEhKbgpZou4Nwqv8opYWjoEGH7t/Te5oXQoWzaviAv6yklTcqp/nhbTa8cAF/LSIUDHe/bhN0BPCnUzWRSBcN1/Un4/KwDTh7UwDQKBgG3tlfCdoX10dyKt32REedmto2DFmWnonbxJ3Fe9U9tUKFxtNJjf+TR7nmcIyvv1+atSaVbZU72FBQBue4S3hmub9cpsXcBJpLBMU+il0gY0X7eyWWmfWxuBxhoFO7jJiqS41IBS2QNyJucitCCK0WHw4Bqr0LhVOP492LVny5IDAoGBAIBN20z54PzaibSYueuaN0h+ANT8UX0/s/lXHASnCZ/mv4XPUQXAKfKdhpJ+07BkqGL0uRGntVZAGmUb+hE1YcptcKetb2dc7Aw1cr6RWhtags57eUy7dfQQqhQKk5nybn8TRkHszx2zV29l96oZwBSI75AQb71Al17kVr3mCH34", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+bfAus9butA/hM9mMIqyOLveGjGfe0GAm0W/2MvJcEu4QpQJTNekf+DEDbW/6ij7i5xXp1iVoAsbWP3ocZiq4F0jK5P0zm+v7DKqBI1kSkYoipM9ax+yzkuWNuNnKep438+m/c+JIz0O9ediJzKArvYLXwXz6Zts5H/mu+XTSmgH7JBhQezbVCyiEYykTMHg2zXtf9p29ngTYrspgL/3bt5IJYHPtJs7sE3Vk3c4s0MdwDBruTEZxvBa4F8/E/SO04ytwnDM7sC/bXpSJszIjzO/gNpgFR2YoohDbXgfOg2LNY9SDzZvgRjh4jh/a0hL+C5qcV4f1dSzUcNWPMB5XAgMBAAE=" + }, + { + "id": "QmY2sc2J35kcTnSHBgAU5SZANWCMJViKefno98gVShZDeb", + "privKey": "CAASqQkwggSlAgEAAoIBAQC5R2O6fBbhpwMzx1WINzRDarzRv2rY8xYxdNFeipC+miimQRw7fnxbGsuaARhQxQ+I7bA4X6L/bjsaiwBYbb8Onud8R0Is9YDjX9z0nq27gCx1mD6HTgAe+J/gMwuSBiGGfk1cn60UwGpF/wVC17RMHbqmNNHbfdnd143SZkJyLTxGVmRpzZdQxu93CMtlcgjtLm15cFMIVapQR0U94bOBzQePTB1hM0dyCSt80GzxBANRHhg+Y/PwqxcD2rLsiQmQ+loZMwpfkv+nD248UL20kXoIR1+SEA97hhbNxPCkdWQGKQoGOvGsEFEmwf8cZLOEPLAiJF1xKTTL/fdH4mllAgMBAAECggEAOjOFzW/rlCjtpZdJdAe7YC4ocE8MFmiOIS0MFAG9JFKy0D8Fyl6aW9gX+yG67Kxqi932jE7mmgybfmQJ0L1w1fvuxahwzkS0nzhe5WzvMEY8MRiJE+HZweDiAACo8s8AOwpz7NOwACPNfn10vqGZ3os8NbfD8IohJUHwKBa9arXoIvrmWLjYp8YYWpsBY2tmBQtyg0mkW6AxXzE54a1dbcAxTGDY+If+BxR/WF+wLPPFakD1vt0BL+3qWEmdHwjMiDFeICQdSW3AI44wniNadB2lcB4wC7ynlW3ZYfbhiZ2BeuRUtVZpiHza3zVjzQkKHG9dGzctwi/in1b9zHkdxQKBgQDguhB7d/XUa4R8jGBIxk/QuDF36CMsFpbtb+XsyC6MAJaVJ3IOIRBk9NCCHV0QYcVP3N0cLA7LdpLjwPnGrfS35L3310b1/gnQO9tuuQ/Sw0tCLIWBzSv8RIBbWmmHu//Ji+Y1+F82nekbEzmFyMbyB9p7vaMFraUMHEcsdXtPOwKBgQDTD/wHg8P49JH4JjezIBmb6wGdjkPq1H2z3mVJWjcgQPDSkjQlpC0y59UwQdrWzOYCgNiK9vqLHMfhA2+CoCuldcvq3Mj7jc3bWqkSX2D+JhWqOfHjamFKtiC+U08H29/lmufFhY5y1XgvBlx0LFkv7YyG2sJENhjY02hJxxzf3wKBgQDMDacxtXdubkJapUw4SHno15dzfUGSouBgsJ5iJlK9pTsWLD8QPU2ZngJGY0tBuMVlAvNyxT6Z2Qz/6RXkJchzwiwMg1lXvFhE0FUrcLbBdbs8VxjTB+3Mnpa+XJY+PaLA61sZ3nD2MuSbRRWSOQEMe70TZtpEKawYFwU5hWN2SQKBgQC9DbFTzdwpUHC7lQgJY/Rsa+mLhlYhr2pVRhz1wmBkfKb6Bsm8+b0evvh//E0YYHqavWy5tRagqwwC6n/kwrG1yLj6S/+fC9rb4+5TramEnPqE+thHOw+gy1izAo5k3LdFxCZdEA+TntDDVnBUAzF0M9TPKn4eeVJyTZM5mqergwKBgQCT+lVnPx43/sRaSzjtYl6QvJsf03znj0KRtHUoIcI1m79EnNZ6o6T83Db0TZ60z5pkYFsQBYDVFKuW7QIkimA+7YZ8KmUDuxtiJOj5VeXZiuUOcxP+1x5Wxght0DovMCkortCMpcNA5ZCrZpiDMaJ+eIu0p9sBwbD/MHLKsHAA9A==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5R2O6fBbhpwMzx1WINzRDarzRv2rY8xYxdNFeipC+miimQRw7fnxbGsuaARhQxQ+I7bA4X6L/bjsaiwBYbb8Onud8R0Is9YDjX9z0nq27gCx1mD6HTgAe+J/gMwuSBiGGfk1cn60UwGpF/wVC17RMHbqmNNHbfdnd143SZkJyLTxGVmRpzZdQxu93CMtlcgjtLm15cFMIVapQR0U94bOBzQePTB1hM0dyCSt80GzxBANRHhg+Y/PwqxcD2rLsiQmQ+loZMwpfkv+nD248UL20kXoIR1+SEA97hhbNxPCkdWQGKQoGOvGsEFEmwf8cZLOEPLAiJF1xKTTL/fdH4mllAgMBAAE=" + }, + { + "id": "QmWMy83qeZBRWC5U94ZV8Ga15SL3yDZKob1KokRhUzaUEX", + "privKey": "CAASpwkwggSjAgEAAoIBAQDH+Xy/ud3fl3ttFxFtYbKzNu2kFM8OvJZdcC7nKpVLnnRn9xmOZaiE4pF+e5H6R8bfVvRzNVUD/KZLZgGKLVzJkrE+yDc1El9bCo+w3Ap0cYIF5wLS6w+QDo1iC0d9wtI236j8tPBIRjjvifbmBP839EzZkTZpHt9XJAbb9HG5dHUtjzJfzGmHCdWCbNFi/jest9fpjHGzjz00F4t0Nk+5/nTGWZBccrNc29T8JHCWc5B8SSGgVi7pQrxJMTkXyO+5fnpDwSN7zQdETmhOihlEr0Gz11tEA6cy5i5zZVrt7R2moFLaG9YwE7R8B87iAz82De9/mwxlwP8adyjGY3AZAgMBAAECggEAEgVnupdzaTzrgI89oM/XHNNsYwcFCdzw6Aa+E1MBFUl4UA4ynWSH5UHNvspAA4AIagQaD2/U84EHM3BD30HrRLcVhgThBMVdFoVDIbsSrh+0ifehh4RraJ4HG7jMfobaDtvlDLD5nmnFo8CZIW192nsHmeu+LYb8tYunmpbS0NWG/4M41wJsrQ9aYyAFlaCorjAW++XIbLXdwCknrl+omySTgUXQKboLta0NF7pb2pY7ejK8T3M/IsySnLAAODJOmsXU46ZX6y84H8npGlXO6vFwTuE16kd6k7InKMNDkWvZruNDp4jL2Lc2BPIHEFinlQ3jDWrIpFKYfm4rrneOAQKBgQDy+UXF+2eKFjIDR6j3qU0UTbQfJuh2fu4M2uYxR9m0UeKJ9vTOYKbxy8U515RuwmfCD4qTjgymgNkHKFWCMfgnGeS4nZehe6nVlIpj/L39LjmG0PxW9jN9DUBSlfqR4iYSR84+4NLf1DN3P8lpqczVxayawFyIb5ayq57Q1mBSIQKBgQDSshEPA/AzHtTlzRUi6H6rnQ1hYDZjpb/bkB2uHHBqvO3E7rgBjGNna25kJYo3iTpFLGj36eh/6P0x/wWiQa+Lzk700c0dBoDZ0wwxNw6MO+UpOwqppZI609284FinCIIp2pM0A9eCj2lacY6VqfbIjVuxBFgscSB3j5Xu1v3O+QKBgQCtnm27MRAlXgLS0Svt/K41aejhsb2+nqifyvNeF4d7EGAeUbdQmUPx17fQwzkvg/78bnqJQaHMgJmUQcAIToiR+DC1svacz27mTUQuenWQKq20pjUKNtdH7mYeHUl1YPHvchvwVlqY0lsMRSo5NLpHrv7NfdMhMwuuFHBhri4ZwQKBgBnn3wUTnOMMNW0T7DdgksKTCuQgKk/SP2bFr4CK6p+VHz8JgwJdupMquGYD35y4eLusnDNvRTEE+qy9CHpMzULJWRfs++VHuWwO8P7UICvJ6YFKSyNUwkgNaMQiaYQQHcD7qQHKPyNEctW2BKwOO9Oo3c9EE3tTZYSN2QeG06oxAoGAazko3bkFfCb39VLPWD8Ob0PQ0kt2hjiQe1d6IakjbBjPIRGcJMNNkgyvADXXhgBhk3SvZuJFlzAj+8UUGzmqBVbS0zf6Q/hFcKSH9bX7H3kRgjJfxaoVPiMy7zemsl9GNel+vcKZbqQrr738WQhHIXXfCtf5H1cuGwSAD0QXdfs=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDH+Xy/ud3fl3ttFxFtYbKzNu2kFM8OvJZdcC7nKpVLnnRn9xmOZaiE4pF+e5H6R8bfVvRzNVUD/KZLZgGKLVzJkrE+yDc1El9bCo+w3Ap0cYIF5wLS6w+QDo1iC0d9wtI236j8tPBIRjjvifbmBP839EzZkTZpHt9XJAbb9HG5dHUtjzJfzGmHCdWCbNFi/jest9fpjHGzjz00F4t0Nk+5/nTGWZBccrNc29T8JHCWc5B8SSGgVi7pQrxJMTkXyO+5fnpDwSN7zQdETmhOihlEr0Gz11tEA6cy5i5zZVrt7R2moFLaG9YwE7R8B87iAz82De9/mwxlwP8adyjGY3AZAgMBAAE=" + }, + { + "id": "QmUcouCpCsH3fXT3DYYf97oNHYEqbM21tRbDwfVEtsqBJ7", + "privKey": "CAASpwkwggSjAgEAAoIBAQCg2SOx+qiAzhS9/9S7H999uF3PswsucwszR6KQgDrw58MBHfhTfuVPgZpoI16nK7XelHZ4BbKyirdfffuzS5EW0tKfTjiRQPQNv5C58l4nIrN2Veld3CywEnD6JNLrG0TX4J2fdUVhsIzLKmwRKZDTk/fNE8Vbav5kwtxmkS5PZQPX50+IkcYq6O30UmSyzwYbjcXjxtl9/bSJNPSmehauDcJOpPjuL4jh2eWwF4/jL75B8ihLJqS509/js0Bxjtj5HXMEqteNhw3fz2L9qDVzgRWdfAycGs80vRAnHvgCL0sQ3zTcPlxZP1TasLoMG7GmsHTbQ4GrRitwQVfR4nurAgMBAAECggEAeWovFaP+Gex1n85DiFTQ4ReQSL4ADLnulHDYd3c0BrELdAZpcq1w7wkHwuAgDNRj3nX2zzaN41Pug3iXzImYJcBSGMp0zMBY+R/HYXVgOO5qxUKoe589ZFMnUMsHI01cNBBrKvjcvJcGUD3fgTz9A5jIDteDvDQRf4HP2ORyLp8BU/VRYtaIgHCeW+4qO9K5BmoqprK/I0FhcfJJsonDO5Qx0ORC/vFp5QEajcvACRGbEVe/iUn2jSFlVRl+Y7Hhvq0Z8hJEX2DGYaDEvYA8BKLbOcqFXnRFzaZy4E9zCPes09QzLNc99Xg5BlC07W78CevzqeKfnnaKjpB6xyOgsQKBgQDPB1UnDKpnBgsgcrlq/dtK2kE22d9XnL0dPOM1Z6MoA2+nvyeJsyjg5AQz75o+ibgFNJFnOQmSsucz35fyG4pebSFfeJ6QQIy2sX2ZWCKvFk9AjxVQV6HGtkCCfCMaVMCDf3XvBz91k56HDRHMwCBckiofwiiECsIPjOOJoe5g3QKBgQDG5VbmLDDWWM5Iz0+QaLNp+7jfjhAW4nXmEr7LHMAcxHlgMrRXHAlvmqtF7zI4nqrsEkeMUBxjXr32GMADjaAEWaudUPiCHrLLVWeFKUIJcuytHQbZiAmbXf9cUQsQDB5Xu00wVzQFuWoftT+mxe7NNFxIoKTKDySrRGutbBACJwKBgDSMnO2jhmFOJGbhF/VzaEVQrvr4RHudmlcgjF+S5dQTQzUmDSgjpL2owvRX3iP/mwkfNcUosihdZthA6fWXTgD33F+6Yy0kZVM+LC5PM8aTeBxf9T4GcFbbDfbRAav+VTtLkCjTAyWaCcQn6hu5rL0ykKStCNGzVVzTGzJlOve1AoGAbqXpaRVOegBfijvijHjizuQd3ihfJQwJANyOzdoJujdHOtAaaZeMXE0UWUyPiNYkSj6FFTcAA/wR+9WbBJ8GVUU5ZsRD68d+OgYiF/9y4GTR7CCIzf2TMp7hUA8580x6mN33PBxXMZQLyOYBKhtVFU26xSCxbENuZ+UhbhH++eMCgYEAkKJ8TWDxkGtk+vqyWFgNT5uITKkcfK1Y6U0lYZQf/64ByxR1+JckxETo8yFTsojW0SFa+U9ZWjFe1d3Y/3YeGK/sKUPZ/dgA4LjjIIf7YMzRnG8zjPknT/HjA6SH17FDTyLEPO3efKn7OjbNYaRfjH8jFEhrH25BNn1HbYRvS9E=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCg2SOx+qiAzhS9/9S7H999uF3PswsucwszR6KQgDrw58MBHfhTfuVPgZpoI16nK7XelHZ4BbKyirdfffuzS5EW0tKfTjiRQPQNv5C58l4nIrN2Veld3CywEnD6JNLrG0TX4J2fdUVhsIzLKmwRKZDTk/fNE8Vbav5kwtxmkS5PZQPX50+IkcYq6O30UmSyzwYbjcXjxtl9/bSJNPSmehauDcJOpPjuL4jh2eWwF4/jL75B8ihLJqS509/js0Bxjtj5HXMEqteNhw3fz2L9qDVzgRWdfAycGs80vRAnHvgCL0sQ3zTcPlxZP1TasLoMG7GmsHTbQ4GrRitwQVfR4nurAgMBAAE=" + }, + { + "id": "QmWPmsph7Bi4wHLhHSYh3kJzpX2gKydreF72dwXh7fFGwb", + "privKey": "CAASqQkwggSlAgEAAoIBAQDMp+6jO4hCpNLjLcB1mWT47YCuE+zWJMzTS4hZZLE7Gp59N5ufAsKZ+HzwWOkRNDUOu9EbcWxJ/RPYGiTju45bhyqbitj2YdQ6UTGGhB8bIKfmwXn6qWFtAL9Z6uLqZIUY1Z6Pbx2ZUV7I4rJmNq/JN4nBGS/3tejA+vRuMiYv43jmoD5qpGFRzc3yyGpbtH+hckq1YpLaRvNxvN8q6pxf7unH/ceyyuLJ0pMdIRC9dGY0KV0AhoJCIS7RFxSKJiCyxAhQIUM0l7XUyCmnCoBnGSmi8+mdQFXvGgPYWXRhwiXmyQE6CYz4nGbJm0MnQePSAGpDa7fZgQeNP3DW1rEtAgMBAAECggEBAMk69nKaPskIuDOY8Pyw74+0OCryfJWzaZL5WgBL+dhbvCrzWplnZWFv66mIQuMfqmvqURni1M+LgM+kJVGnQbrNlR+Lzvm0B7Gv1Dqn/s31tiI3a3EEyz8a1Qvc4XIHlLdulJIEd94hHEn6XeATNZVsfo7X7UpsG4hlOaKAvPuntSxka3JHiY+PNThKKq4Hcq+JClE8gJj7IZ8MJGBcZUP9AcMG4tNmAXwxtiVj6+CecvDqcaJ/fsESMcVjGQXMjF0qjMcKv+1r4LEVZlUnPquAjBTO4UVKXzTP4qH+zU0xIk5IX2e6yg0KR9lMVJ4O/ut4UIKRHWAWKHPzA+xKUwECgYEA6SmTYbnFYEAM9BG7rPUfaXNrDagCXSxsq06Y7GLhCYG0XAEl9BqOhIDABJTWJsiTL7aDMUmRSAXuZmSjDptkQWupsFfijkHxBceWmKXiT8oSQP1zFLNLoOuGmRVFRGoOsI/ii+I0VmW1J83vgXN0tC+8cA/LJLBEr1LlBIog5KUCgYEA4LOSsdKkmzaVxKaracBC5ZcFAaTBfE6/YmFQXYR/psMDxX1Uc0mjCPGAcqngoarZ89j3vmYBhZ5eHyAQG8NSV6CivftkqHUA9GTSoyxjs5L0ko+YElS6LfIhpDcrxaSA1Rl0uLgvh3Vcb9EploB1xGStEB6U2QMVeUW3m17ti+kCgYEA0aj8CS1WKXWO8E2d2CnW5CjQA8imz4ep8+TKKA3F05mZR/4UVcsnikiH4dbANsG3cHkKwWAozn3e4J3v7auXXChmvtZYN5leFNdjOMI5nYvUHDKLr/qFsB75ul/hGGLR8LyBd02hyGrOmgtZKatbLseaB558CfKi4JCGtS1L5K0CgYAq5FxWMV1We37vQNlroFWkucUs4SRF9Eo4H1dV4przXDp2EkxyjVXfuSnJuX+1nIVqNLplz9buiKJy0o4AnrIzI+is1zYJqfv6qtxCZZDKkHy7FUn0BdRcPOILFLT4T0SAaRGMySvZcZ+pgIWtwuu4B3e5ofzLx/jrY1F/CQnTMQKBgQCRb30nkzKgyFncTg8cfbbYQkHRAd7TnxlsZ9JjVLiV++ZxKLx4XRjs06NxXHK8q6SXVnZeGg6PAHyH5u+1NwDiZ4mr8kDamgHE9qY8A0EZwlSWdPJVkONRXdaeEMM69fKGe/7U3DfnKHKnu3T79RVIKn0r6K+Dz6lJDuv5+iYT0A==", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMp+6jO4hCpNLjLcB1mWT47YCuE+zWJMzTS4hZZLE7Gp59N5ufAsKZ+HzwWOkRNDUOu9EbcWxJ/RPYGiTju45bhyqbitj2YdQ6UTGGhB8bIKfmwXn6qWFtAL9Z6uLqZIUY1Z6Pbx2ZUV7I4rJmNq/JN4nBGS/3tejA+vRuMiYv43jmoD5qpGFRzc3yyGpbtH+hckq1YpLaRvNxvN8q6pxf7unH/ceyyuLJ0pMdIRC9dGY0KV0AhoJCIS7RFxSKJiCyxAhQIUM0l7XUyCmnCoBnGSmi8+mdQFXvGgPYWXRhwiXmyQE6CYz4nGbJm0MnQePSAGpDa7fZgQeNP3DW1rEtAgMBAAE=" + }, + { + "id": "QmUKfz8ij6zpL5beYPsiREHbF1E13MtHKKoG7P9jNDwvZf", + "privKey": "CAASqAkwggSkAgEAAoIBAQDBbN3D9OD1FV6vnR/7BuXqGyVQmmVxE8voUeixN+zX1wc+KDu+xH1qlWvh3Fs7Wvv2Pmll5Pv2yJqNg9GeQ74yvFrATBJzJecH053o6YsjGwrz+wvPZczZxo2X6PEXCttc4OxkAbolQCLziiGkd0bLTyRnF+iO7t632mZNXH+mYkNrtqGtLFnx4M6zSjmvTf8PbdTLMzQXhPrlgLFq3rOV/zIOJ1mW7cdzd1nROzjcPhr2oa0GzLkYA/a3QGuwUchOUWX6INAldecYxByu34OmXzLf1yRz3mnzVraIwor9j31yW5IFTW1MLlf6gGC7SLG6RcsdXL1TiAAGGthhRA07AgMBAAECggEADaZyLA8PBRzB9uC+zgdZmGDcDEkpGYbNIBMPudn5g+3v2+oY9UiGlUwANgtIAPNVzrXu2DsFsQ/NIGmKFIIxXQE/4kw8DRZCrKG5hiNdJ1gUOM2hsrw7ba7+dRcCVJ72hiJ1bEVW7qQNE2TPJP0p6NUn+4KLbfLbktv91j3N4F71fKZcvuMmt15vVKCQJmnnSmgZbrg/R6ONBG2XWkr7LgWbx5M/rFHcsGTHpnxDDGsHDs4BeyP9jEOORi3KoBD8IibBA1mvEaH4ELYbRJUL+R5HWYVmZ7WsWKksSZO6CjxGbcjspGGyn7sm7RsIWAl8i0Vz+8cxUwIblsxHm94kcQKBgQDfH+evxm0nI+u972Pfd/SCFShlLGOpv9o/PniFcgjIjkwr7d+l33hW3KEy3oMjW46fqioAMRoEH021mLbZyqDrp6uKDmGt0wLMkzcj1jvsq8E8IeBTi5buwjLq3sXPpN1OHWf9FiDTJxnbUTxsjZC0m6SH6hvnEeR6JFK1tY/N+QKBgQDd7LkdVp4eyzyYmBFDFKQsbJsEot58O/H9Prde9P919jhSKy7HuOxx2R8aUDZdwP/hfJB06/qDzjJEWCeNssoJJVp2H70B7IuyQebdIegjrEfsA9TVmyui6m1qbO8Uy91gpYYEj551pQJn8g4ESC+gTxABpsMEhN3ith3oUn3R0wKBgQCZnR2lprb/e3uSCH56G9v6sxZckXMMK0nWwOu49AmYCl/m3p5bVL9FDiDtJMSkUySeSEkiWKvQqryVic5Zo2em1okDQ2N8iQokTjZoNdaL2rPu8mO/IRuIxarIiRcnHho+7X/qfpGazhC/Le6rzcsSjRV5fXQ4YdkrPlazQ4z+AQKBgGPe5UubsFrhUWrGfHY4ss0/Lt/+P6ue+n7iG3mDPfejD6OCxXGh+d4ePM4FrQGyGjUqboP/smRgheSV2278N8m34qboD5HvFsrot3FqkeKD5HYFH2Hli9SsNQNA9LLiL8UPmGkzDLeaw6Ntnfs1VSGKkwR7PKYq+QPOu1TS0sZ7AoGBAJf0vzFP7+P6zc0i5KtDUJGhuYK4dC9AFr65fR6iu9h7g+0hjkyBR030O+XSZD89ri/mMQGpuD0shWHIDb85RcbOy38ls8Ft4znCf9Tu9yi5bnCczWpoLpxd+YbWAv2BJ2WiYIMIR1torapRRMA8J5XssLDQVoELm4r8FnDHO3oP", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBbN3D9OD1FV6vnR/7BuXqGyVQmmVxE8voUeixN+zX1wc+KDu+xH1qlWvh3Fs7Wvv2Pmll5Pv2yJqNg9GeQ74yvFrATBJzJecH053o6YsjGwrz+wvPZczZxo2X6PEXCttc4OxkAbolQCLziiGkd0bLTyRnF+iO7t632mZNXH+mYkNrtqGtLFnx4M6zSjmvTf8PbdTLMzQXhPrlgLFq3rOV/zIOJ1mW7cdzd1nROzjcPhr2oa0GzLkYA/a3QGuwUchOUWX6INAldecYxByu34OmXzLf1yRz3mnzVraIwor9j31yW5IFTW1MLlf6gGC7SLG6RcsdXL1TiAAGGthhRA07AgMBAAE=" + }, + { + "id": "QmPJLu9fVrhGyFak9Ynv8pZ3aaqs8gWREXZWjF21RbdAdk", + "privKey": "CAASpwkwggSjAgEAAoIBAQDIQ/mMSOZgzmgAkq+y737z8rpMWPyFFikimxAgaVSfJ2Ey4+DN2D3dWqQzdgLWFdxZ8YrKrkfT1UFmOLJDkWTLnP5lb/5RWKU8r94BJ71Z6YsNTza94eBzwNlLggwm0RBgDrvhL8WhWW7G6eO0bqM8RjFYZNvA+LStfCBwQQc0bvgSwTsBJnNEzsbiqIhxqG0RhO0XAQ3UdubtppP2occFsQfItGQYZF8Izw1jOXjTwg8n0GYS5TwTlOrv44r5sQP/pQUGpeSxEnVjEjTTdvdamgNNixtms2JeJpLidyXD+EihYr6wyU3G5Nm9QEqxNnon/ciDGFZ5I5AMfDvjQOdrAgMBAAECggEAdZa8I2v2oabwmE2JDubjQzglvYiOlVKCysgDuxQ91MmZn/j/2b377Uo4meLUHYJuhP13wjR9Blv0b6Igwd3QMmI3WbZdvMPxGeYl08XSv8WKmUEG4o2LbkwxCs4MdopRNCXSmZGkWG4bxn407o/8FvscJEvsYK1n4Wm93kvAgD8SUb4jDYa8IealqGvn7saQw+seMmu061aMW4ua1BTS6+GmJTXfRkgPuipo4bF76K1yg2GY+9TrX7pG+zHP6OZ1ZojBG1aBHU4/+9Rn6JruW+NepE3VsQ0DdlmtnRoqwupwjRGX6GQe4soyKFH5foiOc1AKVHATdPBfzFV+jSOsWQKBgQDiyHApVOyIvgke9wpi4tKOX5ys5a7NntVg082fhHCETNdSuckL8Vii62S2U7OnDTxWiP62Z4yqiYCRZx3YnmU2NzpgV62uJpnsYilU0bBDRhojh/VT61CKK7ELQqIO9mFEECCjMMTiwdwzd9GYrsn9DmhsCQnhkAmpRy5j6IG9lQKBgQDiEPYWxw7Gq9JzrvZCLEzJ7cnSInJXQrT4tIxOsO3Xw1MT5i9kErF1YfF5hvmZDdYSBrnQatltvLwmq/dPtOXiLFn1/BU1eJl1BnErVaq+Hr8SExXIV0cPSP9OWnCx8SPneo1E33ZgNbDLj/zSzbbx7zI9R4bqp8loBSCjyQzQ/wKBgQCpzQYeDFYmdJODudju/YJF09UNp9PgZWLC4xN8sr9Eb/xxFmYwKP8CzVlVIwrKs6BP8rc0gAfU0KuRMzIntUF2/Jca98s1L/XkBgOIXJCrblu36t6HfROKwPgIxFVu+o8NVT36slNYrWpgpwAqYAUhEp6g9YuOdjilpIRZjgdGmQKBgAfAOx/GzWvkYj/TJOBZ8nwN6hiJU9BIGjpYvmLIUC9QpB2Pi9prhagXppbbGN21irxtfJt3vmKz0OOmi8Ew25zolmEHtpZxvxkzElhBBKymEzkEbH/E9kvA3fgKw8hmEywRmJxe6QkdbiVcl3eG1BnxcJCHTknnXNFzM45KC2VzAoGAWOtJ24zt7GTrFBmDl8B0/nqcXp1FCgzvSFkOplrSi3DSrpVih+FL6jfRAFaQnwVGXnI9KFPU6v3JN4/zC8Dlpv7dSup5VH+0ayndVlpKcYhKFfx+JTkxzgxqBOgwjkRRe0V/HDZNHsBtRBQysN3Ul9vSMa7nJxeaX5MTMLo5nXo=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIQ/mMSOZgzmgAkq+y737z8rpMWPyFFikimxAgaVSfJ2Ey4+DN2D3dWqQzdgLWFdxZ8YrKrkfT1UFmOLJDkWTLnP5lb/5RWKU8r94BJ71Z6YsNTza94eBzwNlLggwm0RBgDrvhL8WhWW7G6eO0bqM8RjFYZNvA+LStfCBwQQc0bvgSwTsBJnNEzsbiqIhxqG0RhO0XAQ3UdubtppP2occFsQfItGQYZF8Izw1jOXjTwg8n0GYS5TwTlOrv44r5sQP/pQUGpeSxEnVjEjTTdvdamgNNixtms2JeJpLidyXD+EihYr6wyU3G5Nm9QEqxNnon/ciDGFZ5I5AMfDvjQOdrAgMBAAE=" + }, + { + "id": "QmX5regf3GkAcXnfE5hiPrci4KJjkAVf2ffU1s86FDvhZu", + "privKey": "CAASqAkwggSkAgEAAoIBAQCxMSX2Nh89KO1V0SyiiVgSmLIsHH693B6DP9oTNOIv0eZSxhdBvsO6FqQG/VZpxlkmtbaJkh2T6VzgOigHUvI6nByCfDqOxVMpmpydHrlt2GXLopoCa1ORlil03kwOULykGIB5280ugkADEkrGlXv970cd2dGVqTaM1E0vJSY0SswOJwppW6riVgupZMDsVThzFEZzkcaGgoaUnpkm1yXi2U3BGozhG8CaA9ShDmyhYJ6K0IoQsbqbEKe2dZSG65dLaK2wP70jfjrQqsgV0taFVgzfZzpRfIBCj+oxn0cDpi7e7HMT7um5p6WzYOr+eeIpej0X25sHhsiJKo6EWIrvAgMBAAECggEAQ8KNx9funk7WY+j37rgjDAmEqUXEWgqhSIPUe1/P14WnVKQYJLY01ybtVxbuQsjtMtZulbFgyj2FnOHgiNx5cVxdsrCbHBntsY1Esvka5BzK4NKvnpk4fgY0mFKiNx885GvSRcd+cXixzfriyYpEvlkQkyPG+Dqbd8OAOLU2JOyJ7hQW/4irU2+pyv5tZM1yfb2sS7RX1zwIGvCxuyb3vhCSR55H8IhEkXgMFxBDnFBLafxNGgaOBN+xQ1fk/JJJTZ7KmtovQlY1yPaP5dYwCchs1pLdifrglKyOwgCFWnKXp8ALp7yzbarbXPuTpP9dYcQ6WBO1FBMClVhciZiPoQKBgQDqtiPJK0f6ola7iakCv+kIbgj17nEwximANDWzWnsOCgtp7jZvZFu0Jwmd2JbwuHlyfD6scNwX4fHgAC3Ignt2evFtIu7FfKtNDk3KLpKvjCbWBbxDUabahTFlAHJCoqXW+TE6slSrKH3tm6Pew8zX3IpiCcXNuxeMaKnEBEchlwKBgQDBQ3CvDdOC2tysNnLTnJynNnYJeSD1FDYjaszUwwQyb3Jsnd8IZVtCbRZj+1pDYDtSxIQv+L8aBA7YBkUYznNgsqNWG82GHtoJcw2S/hb3zWZOSv4Mv3pXzm+/fd/8EyARraKrlaHkvElQEXUB7Uy7g3JGgp0SvXgYMCmpPeLcaQKBgQCxelDNCSFAcAjNfmaCYcNAUJQOAuMsUmuHOfNxT3XFQ+sliq82w9hj/TDUXrakB5ot660oj56doIhZLUlxWNkq2gQ81jFeu8p8VdiUjWr1uzCSnXuiG9fjXNvg19mOcZadW4jSLY7AbbnrNclP5Ra8E1PAlkswKczAq1PTmLiAuQKBgG/28kF4cSFOSqo6oSuXf+of0yKJ2l4h53t9VE2u7a+KgXALxjrG/afuKxqhdBm0lqnM0Ag6w/v+fdsJRt6oPI+47ofkc51QImF3FlYifovxo2OYbrZT+D0GjBcUhb80FVAomCVv+urZal/IvVDweZPpAzt2UCmbOwhIXtp90ACBAoGBAJY59H0ogVYU66+BeI/Az5fM7ryUWawFqBo4pcP0xxNDHQEenGEy04rMTwQczjaLDkh2KXGEJLB2lWYGlOYfokCTXxrkTwyHmc9PNp8xmmNA6AAQqeKSG7aj06qdLq/pJ3eXKwk7uETjDAAwzbE8PZyI0KFTt4At8/iGisHRQeZF", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxMSX2Nh89KO1V0SyiiVgSmLIsHH693B6DP9oTNOIv0eZSxhdBvsO6FqQG/VZpxlkmtbaJkh2T6VzgOigHUvI6nByCfDqOxVMpmpydHrlt2GXLopoCa1ORlil03kwOULykGIB5280ugkADEkrGlXv970cd2dGVqTaM1E0vJSY0SswOJwppW6riVgupZMDsVThzFEZzkcaGgoaUnpkm1yXi2U3BGozhG8CaA9ShDmyhYJ6K0IoQsbqbEKe2dZSG65dLaK2wP70jfjrQqsgV0taFVgzfZzpRfIBCj+oxn0cDpi7e7HMT7um5p6WzYOr+eeIpej0X25sHhsiJKo6EWIrvAgMBAAE=" + }, + { + "id": "QmbhAQLZQ57kswDSrHjN91sP4t9VfRR7MEgVCxgAqe2PTP", + "privKey": "CAASpwkwggSjAgEAAoIBAQC1eB5b8c2r8N44Jp27miSak+TB5WtnvpjE0l2sRuSEvnx3G9wUEgNt9OQrk2CO6OFQ9l7xSqpthhBNhyem+1sHAuu5+wSvs0iDTy1NFI36czvs+5s9QquybaYxub6fqQn+PJ3+9XmIs2pPZhMAGTTFNQnMOKQY/I/rJ2B/tNGTaNiESszLbn+CGQlXO87AKe6hr6KlypBXd0tMzgr44tLECOSRT8+RdypYZ7aGvXC7N1x67zIfX9oRFTfUXX0O70rhn48GdVsiCoK+TV5Zdrz8Td8iWh7tDZP5zZTjxzHb83jvhh281gvCNqokyrX5aO2Qifyr3XTK8u4IbGapDLS3AgMBAAECggEAAYRznIk1Ew78jLOl9f+f9rrt2sE0WLW7SUoJBnvBRWriV4ygFnrgFHdLwSGGWSuRVr/4o0PgkPHiCQPsTPUlGbXoirGSpHV3UcHDuzVEyqdUBPNFpQl9rgxEq33GHkenkbyoBY2/6mi1ptxPpFyRmGSNJGwE00CpY2n84JR+fCEMso010JrdL9p8rPxQbn2WdilH6nmWbWRL2be+GENUpAhNF/yI8ZmmU1PMWmgxgInCoFX4viy3Ny3bOtcktMea6LAl2LItnUDczrtfOyz1cze+luzCJGoPwxHW3dRHru0oEURfBOqf9nfcd0nKbsdhpLJAk+3r0U5EbrWwtx50IQKBgQDiYt0vzBhz3TQ6XdAyhHeaN1sfbsJc9KYjlk0wZul/tlhRKMXPuxteqIxeR2AFio3tA7Rclw2YxkS/bfHQLaY7CHKJY5g7LD87OnWP5XMH3EjmJueRDIUd7dQxS8yRhuK3xnoLOg+Bf27ZtvL73DflipX3+8CIO+OWZEYH7XkhtQKBgQDNNRd8Yg4ze+/F4Kp7KBEvKQyI71KGFO54/bNk2FC6H++VJIIXIcKapYWJq306Q7FM2fyqfG2igl86aQyWfhp99aVMP9DVey1aY/4hWcPh46pvU5Ow1TnlopVysonNjLtEsx6f7IS6VYyENplCQbdfz8/rYHa8VgcOTxIVepAwOwKBgQDPkSgOYAp3iXUOot92wiBLRMAG1O3efu4pqPCDFYi38T+OwlyEd6DDrLRu6PBwhDZW/1OKpXDxB7EW976ESqakb71cnPWCI7KiIpqR4iYTKfWpGDivtJQE+f/ZZw9cyxjquU9DJev7pxTDB8xIAwU5MFUKt0gzYP3t8WiIckLjFQKBgGNKEwkN8M0cZJGb+SEJ6/ajp/sBwS47Jn/IUWqz96t3FFdXiNJkdWKaQBiyry7n3fvPZZ+gAPQmnKkIdxzB+1OC7e4M2qDQrssTorzEUDEWFTs0dzuSkCb6GdGbbW3VcC73dBPxPu9hwVGfkVwrq+xiArpivFC4V0WnIJrf6LBNAoGAdnK7h6LXgbCWwRET6m9xr7cGUWIl/f9I2x18porKwPx/d1e7T5mAZR0s3EZPmxrHyi+kwoxY9peDDBsiX/bdvtzfWvFntVEZig8oeBXXyMZ9FL5AGXux6nLYe9MzIc5IuewIyz2kyri8Cy06ASzMqg9Yzhv5mT5+HhqZy3zYvHo=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1eB5b8c2r8N44Jp27miSak+TB5WtnvpjE0l2sRuSEvnx3G9wUEgNt9OQrk2CO6OFQ9l7xSqpthhBNhyem+1sHAuu5+wSvs0iDTy1NFI36czvs+5s9QquybaYxub6fqQn+PJ3+9XmIs2pPZhMAGTTFNQnMOKQY/I/rJ2B/tNGTaNiESszLbn+CGQlXO87AKe6hr6KlypBXd0tMzgr44tLECOSRT8+RdypYZ7aGvXC7N1x67zIfX9oRFTfUXX0O70rhn48GdVsiCoK+TV5Zdrz8Td8iWh7tDZP5zZTjxzHb83jvhh281gvCNqokyrX5aO2Qifyr3XTK8u4IbGapDLS3AgMBAAE=" + } +] diff --git a/test/schema_test.js b/test/schema_test.js deleted file mode 100644 index 107e81e..0000000 --- a/test/schema_test.js +++ /dev/null @@ -1,44 +0,0 @@ -// @flow - -const assert = require('assert') -const { describe, it } = require('mocha') - -const { validate } = require('../src/metadata/schema') - -describe('schema validation', () => { - const fooSchemaDescription = { - vendor: 'io.mediachain.test', - name: 'foo', - version: '1-0-0', - format: 'jsonschema' - } - - const fooSchema = { - self: fooSchemaDescription, - - type: 'object', - properties: { - 'foo': { - type: 'string' - } - }, - required: ['foo'], - additionalProperties: false - } - - it('validates a correctly formatted object', () => { - const result = validate(fooSchema, {foo: 'bar'}) - assert.equal(result.success, true, 'schema should validate a correct object') - }) - - it('validates a self-describing object', () => { - const schemaRef = {'/': 'QmF001234'} - const result = validate(fooSchema, {schema: schemaRef, data: {foo: 'self-describing-bar'}}) - assert.equal(result.success, true, 'schema should validate a self-describing record') - }) - - it('does not validate an invalid object', () => { - const result = validate(fooSchema, {foo: 1}) - assert.equal(result.success, false, 'schema should not validate invalid object') - }) -}) diff --git a/test/util.js b/test/util.js index 6095e09..a602e02 100644 --- a/test/util.js +++ b/test/util.js @@ -7,6 +7,7 @@ const thenify = require('thenify') const PeerId = require('peer-id') const path = require('path') const pull = require('pull-stream') +const lp = require('pull-length-prefixed') const pb = require('../src/protobuf') const { protoStreamEncode, protoStreamDecode } = require('../src/peer/util') @@ -15,7 +16,7 @@ const nodeIdObjects = require('./resources/test_node_ids.json') const testNodeIds = Promise.all(nodeIdObjects.map(id => createFromJSON(id))) import type { Connection } from 'interface-connection' -import type { QueryResultMsg } from '../src/protobuf/types' +import type { QueryResultMsg, PushResponseMsg, PushEndMsg } from '../src/protobuf/types' function getTestNodeId (): Promise { return testNodeIds.then(ids => { @@ -64,9 +65,32 @@ const mockQueryHandler = (results: Array) => (protocol: string, conn ) +const mockPushHandler = (handshakeResponse: PushResponseMsg, expectedStatements: number = 0, result?: PushEndMsg) => (protocol: string, conn: Connection) => { + let sentHandshake = false + let statementsReceived = 0 + pull( + conn, + pull.asyncMap((val, cb) => { + if (!sentHandshake) { + sentHandshake = true + return cb(null, pb.node.PushResponse.encode(handshakeResponse)) + } + statementsReceived++ + if (statementsReceived === expectedStatements && result != null) { + return cb(null, pb.node.PushEnd.encode(result)) + } + cb(null, null) + }), + pull.filter(val => val !== null), + lp.encode(), + conn + ) +} + module.exports = { getTestNodeId, makeNode, makeDirectory, - mockQueryHandler + mockQueryHandler, + mockPushHandler }