diff --git a/.eslintfiles b/.eslintfiles index a30ce329a..7bd79fa2b 100644 --- a/.eslintfiles +++ b/.eslintfiles @@ -11,3 +11,4 @@ bin/hsw-cli etc/genesis lib/ test/ +scripts/ diff --git a/.eslintrc.json b/.eslintrc.json index 3de38b429..d5d0f1ef7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,19 +1,9 @@ { "env": { - "es6": true, + "es2024": true, "node": true }, "extends": "eslint:recommended", - "globals": { - "Atomics": "readable", - "BigInt": "readable", - "BigInt64Array": "readable", - "BigUint64Array": "readable", - "queueMicrotask": "readable", - "SharedArrayBuffer": "readable", - "TextEncoder": "readable", - "TextDecoder": "readable" - }, "overrides": [ { "files": ["*.mjs"], @@ -44,9 +34,8 @@ } } ], - "parser": "babel-eslint", "parserOptions": { - "ecmaVersion": 10, + "ecmaVersion": "latest", "ecmaFeatures": { "globalReturn": true }, diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 998ade93a..44442320b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,7 +54,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node: [14.x, 16.x, 18.x] + node: [14.x, 16.x, 18.x, 20.x] steps: - uses: actions/checkout@v3 @@ -64,9 +64,6 @@ jobs: with: node-version: ${{ matrix.node }} - - name: Update npm - run: npm i -g npm - - name: Install libunbound if: contains(matrix.os, 'ubuntu') run: sudo apt-get update && sudo apt-get install -y libunbound-dev diff --git a/CHANGELOG.md b/CHANGELOG.md index f58b8c421..3543fcd8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,112 @@ # HSD Release Notes & Changelog +## Unreleased + +**When upgrading to this version of hsd, you must pass `--wallet-migrate=4` when +you run it for the first time.** + +### Primitives +- TX Changes: + - tx.test no longer updates the filter. + - Introduce TX.testAndMaybeUpdate method for potentially updating filter while + testing. (old tx.test) + +### Node Changes + Add support for the interactive rescan, that allows more control over rescan +process and allows parallel rescans. + +#### Node HTTP API + - `GET /` or `getInfo()` now has more properties: + - `treeRootHeight` - height at which the block txns are accumulated + in the current branch. + - `indexers` + - `indexTX` - is tx indexer enabled. + - `indexAddress` - is addr indexer enabled. + - `options` + - `spv` is the Node SPV? + - `prune` does node have pruning enabled. + - `treeCompaction` + - `compacted` - whethere tree is compacted or not. + - `compactOnInit` - is tree compaction on init enabled. + - `compactInterval` - what is the current compaction interval config. + - `nextCompaction` - when will the next compaction trigger after restart. + - `lastCompaction` - when was the last compaction run. + - Introduce `scan interactive` hook (start, filter, fullLock) + +### Node HTTP Client: + - Introduce `scanInteractive` method that starts interactive rescan. + - expects ws hook for `block rescan interactive` params `rawEntry, rawTXs` + that returns scanAction object. + - expects ws hook for `block rescan interactive abort` param `message`. + - Add `getMempoolRejectionFilter` and `checkMempoolRejectionFilter` NodeClient + aliases. + - Add `getFee`, an HTTP alternative to estimateFee socket call. + +### Wallet Changes +- Add migration that recalculates txdb balances to fix any inconsistencies. +- Wallet will now use `interactive scan` for initial sync(on open) and rescan. + +#### Configuration +- Wallet now has option `wallet-migrate-no-rescan`/`migrate-no-rescan` if you + want to disable rescan when migration recommends it. It may result in the + incorrect txdb state, but can be useful if you know the issue does not affect + your wallet or is not critical. +- Add `--wallet-preload-all` (or `--preload-all` for standalone wallet node) + that will open all wallets before starting other services (e.g. HTTP). + By default this is set to `false`. + +#### Wallet API + +- WalletNode now emits `open` and `close` events. +- WalletDB Now emits events for: `open`, `close`, `connect`, `disconnect`. +- WalletDB + - `open()` no longer calls `connect` and needs separate call `connect`. + - `open()` no longer calls scan, instead only rollbacks and waits for + sync to do the rescan. + - emits events for: `open`, `close`, `connect`, `disconnect`, `sync done`. + +#### Wallet HTTP + - All transaction creating endpoints now accept `hardFee` for specifying the + exact fee. + - All transaction sending endpoints now fundlock/queue tx creation. (no more + conflicting transactions) + - Add options to `getNames` for passing `own`. + - Rename `createAuctionTxs` to `createAuctionTXs`. + - All `bid` serializations will include `height` of the bid. (`-1` if + it was migrated not-owned bid) + - `GET /wallet/:id/auction` (`getAuctions`) + - `GET /wallet/:id/auction/:name` (`getAuctionByName`) + - `GET /wallet/:id/bid` (`getBids`) + - `GET /wallet/:id/bid/:name` (`getBidsByName`) + - All `reveal` serializations will include `bidPrevout` of the bid. (`null` if + it was migrated not-owned reveal) + - `GET /wallet/:id/auction` (`getAuctions`) + - `GET /wallet/:id/auction/:name` (`getAuctionByName`) + - `GET /wallet/:id/reveal` (`getReveals`) + - `GET /wallet/:id/reveal/:name` (`getRevealsByName`) + +## v6.0.0 + +### Node and Wallet HTTP API + Validation errors, request paremeter errors or bad HTTP requests will no +longer return (and log) `500` status code, instead will return `400`. + +### Wallet Changes +#### Configuration + `hsd.conf` can now be used to define wallet options, when wallet is running as +a plugin. Configurations with `wallet-` prefix will be passed to the wallet. +`hsd.conf` wont be used if the wallet is running in standalone mode. + +- Remove `check-lookahead` option from walletdb. + +#### Wallet API + +- HTTP Changes: + - `/wallet/:id/open` no longer accepts `force` flag. (it was not used) +- RPC Changes: + - `createopen` and `sendopen` no longer accept `force` as an argument. (was not used) + - Introduce new API to modify account: `PATCH /wallet/:id/account/:account`. + ## v5.0.0 **When upgrading to this version of hsd, you must pass `--wallet-migrate=2` when @@ -10,10 +117,10 @@ you run it for the first time.** - HTTP API endpoint `/` (`hsd-cli getinfo`) now includes "public" networking settings. - RPCs `getnameinfo` `getnameresource` `verifymessagewithname` and `getnamebyhash` -now accept an additional boolean parameter `safe` which will resolve the name from the Urkel -tree at the last "safe height" (committed tree root with > 12 confirmations). SPV -nodes can use this option and retrieve Urkel proofs from the p2p network to respond -to these calls. +now accept an additional boolean parameter `safe` which will resolve the name +from the Urkel tree at the last "safe height" (committed tree root with > 12 +confirmations). SPV nodes can use this option and retrieve Urkel proofs from the +p2p network to respond to these calls. - New RPC methods: - `decoderesource` like `decodescript` accepts hex string as input and returns @@ -29,11 +136,15 @@ to these calls. of outputs with any combination of covenants. - Updates related to nonces and blinds - - Multisig wallets will compute nonces based on the LOWEST public key in the group. - This makes multiparty bidding and revealing more deteministic. Older versions would - always use the wallet's OWN public key. To preserve compatability with older software: - - RPC method `importnonce` now returns an array of blinds instead of a single blind. - - HTTP endpoint `/wallet/:id/nonce/:name`'s response replaces 2 string fields (`nonce`, `blind`) with arrays of the same type (`nonces`, `blinds`) + - Multisig wallets will compute nonces based on the LOWEST public key in the + group. + This makes multiparty bidding and revealing more deteministic. Older versions + would always use the wallet's OWN public key. To preserve compatability with + older software: + - RPC method `importnonce` now returns an array of blinds instead of a + single blind. + - HTTP endpoint `/wallet/:id/nonce/:name`'s response replaces 2 string + fields (`nonce`, `blind`) with arrays of the same type (`nonces`, `blinds`) ## v4.0.0 diff --git a/bin/cli b/bin/cli index 1ac51179b..5f9bb71c4 100755 --- a/bin/cli +++ b/bin/cli @@ -4,11 +4,11 @@ console.error('%s%s', 'Warning: The `hsd cli` interface is deprecated.\n', - 'Please use `hsd-cli` ($ npm install hs-client).'); + 'Please use `hsd-cli` and `hsw-cli`.\n'); if (process.argv.length > 2 && process.argv[2] === 'wallet') { process.argv.splice(2, 1); // Evil hack. - require('hs-client/bin/hsw-cli'); + require('./hsw-cli'); } else { - require('hs-client/bin/hsd-cli'); + require('./hsd-cli'); } diff --git a/bin/hsd-cli b/bin/hsd-cli index 46792ae78..c920e00ed 100755 --- a/bin/hsd-cli +++ b/bin/hsd-cli @@ -1,5 +1,267 @@ #!/usr/bin/env node - 'use strict'; +'use strict'; - require('hs-client/bin/hsd-cli'); +const Config = require('bcfg'); +const {NodeClient} = require('../lib/client'); + +// NOTE: This is part of generated `hs-client`. +// Don't introduce any unnecessary dependencies to this. +// This needs to be remain as is for hs-client to be simple. + +const ports = { + main: 12037, + testnet: 13037, + regtest: 14037, + simnet: 15037 +}; + +const HELP = ` +Commands: + $ block [hash/height]: View block. + $ broadcast [tx-hex]: Broadcast transaction. + $ coin [hash+index/address]: View coins. + $ header [hash/height]: View block header. + $ help: Show help message. + $ info: Get server info. + $ mempool: Get mempool snapshot. + $ reset [height/hash]: Reset chain to desired block. + $ rpc [command] [args]: Execute RPC command. + $ tx [hash/address]: View transactions. + +For additional information and a complete list of commands +visit https://hsd-dev.org/api-docs/ +`; + +class CLI { + constructor() { + this.config = new Config('hsd', { + suffix: 'network', + fallback: 'main', + alias: { + 'n': 'network', + 'u': 'url', + 'uri': 'url', + 'k': 'apikey', + 's': 'ssl', + 'h': 'httphost', + 'p': 'httpport' + } + }); + + this.config.load({ + argv: true, + env: true + }); + + this.config.open('hsd.conf'); + + this.argv = this.config.argv; + this.network = this.config.str('network', 'main'); + + this.client = new NodeClient({ + url: this.config.str('url'), + apiKey: this.config.str('api-key'), + ssl: this.config.bool('ssl'), + host: this.config.str('http-host'), + port: this.config.uint('http-port') + || ports[this.network] + || ports.main, + timeout: this.config.uint('timeout'), + limit: this.config.uint('limit') + }); + } + + log(json) { + if (typeof json === 'string') + return console.log.apply(console, arguments); + return console.log(JSON.stringify(json, null, 2)); + } + + async getInfo() { + const info = await this.client.getInfo(); + this.log(info); + } + + async getTX() { + const hash = this.config.str(0, ''); + + if (hash.length !== 64) { + const txs = await this.client.getTXByAddress(hash); + this.log(txs); + return; + } + + const tx = await this.client.getTX(hash); + + if (!tx) { + this.log('TX not found.'); + return; + } + + this.log(tx); + } + + async getBlock() { + let hash = this.config.str(0, ''); + + if (hash.length !== 64) + hash = parseInt(hash, 10); + + const block = await this.client.getBlock(hash); + + if (!block) { + this.log('Block not found.'); + return; + } + + this.log(block); + } + + async getBlockHeader() { + let hash = this.config.str(0, ''); + + if (hash.length !== 64) + hash = parseInt(hash, 10); + + const header = await this.client.getBlockHeader(hash); + + if (!header) { + this.log('Block header not found.'); + return; + } + + this.log(header); + } + + async getCoin() { + const hash = this.config.str(0, ''); + const index = this.config.uint(1); + + if (hash.length !== 64) { + const coins = await this.client.getCoinsByAddress(hash); + this.log(coins); + return; + } + + const coin = await this.client.getCoin(hash, index); + + if (!coin) { + this.log('Coin not found.'); + return; + } + + this.log(coin); + } + + async getMempool() { + const txs = await this.client.getMempool(); + + this.log(txs); + } + + async broadcast() { + const raw = this.config.str([0, 'tx']); + const tx = await this.client.broadcast(raw); + + this.log('Broadcasted:'); + this.log(tx); + } + + async reset() { + let hash = this.config.str(0); + + if (hash.length !== 64) + hash = parseInt(hash, 10); + + await this.client.reset(hash); + + this.log('Chain has been reset.'); + } + + async rpc() { + const method = this.argv.shift(); + if (!method) { + this.log('Missing RPC method'); + return; + } + + const params = []; + + for (const arg of this.argv) { + let param; + try { + param = JSON.parse(arg); + } catch (e) { + param = arg; + } + params.push(param); + } + + let result; + try { + result = await this.client.execute(method, params); + } catch (e) { + if (e.type === 'RPCError') { + this.log(e.message); + return; + } + throw e; + } + + this.log(result); + } + + async open() { + switch (this.argv.shift()) { + case 'block': + await this.getBlock(); + break; + case 'broadcast': + await this.broadcast(); + break; + case 'coin': + await this.getCoin(); + break; + case 'header': + await this.getBlockHeader(); + break; + case 'help': + process.stdout.write(HELP + '\n'); + break; + case 'info': + await this.getInfo(); + break; + case 'mempool': + await this.getMempool(); + break; + case 'reset': + await this.reset(); + break; + case 'rpc': + await this.rpc(); + break; + case 'tx': + await this.getTX(); + break; + default: + process.stdout.write('Unrecognized command.\n'); + process.stdout.write(HELP + '\n'); + break; + } + } + + async destroy() { + if (this.client && this.client.opened) + await this.client.close(); + } +} + +(async () => { + const cli = new CLI(); + await cli.open(); + await cli.destroy(); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); diff --git a/bin/hsd-rpc b/bin/hsd-rpc new file mode 100755 index 000000000..9dc58d916 --- /dev/null +++ b/bin/hsd-rpc @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +// insert `rpc` as first argument +process.argv.splice(2, 0, 'rpc'); +require('./hsd-cli'); diff --git a/bin/hsw-cli b/bin/hsw-cli index b3650c5ad..d21f11d0e 100755 --- a/bin/hsw-cli +++ b/bin/hsw-cli @@ -1,5 +1,738 @@ #!/usr/bin/env node - 'use strict'; +'use strict'; - require('hs-client/bin/hsw-cli'); +const Config = require('bcfg'); +const {WalletClient} = require('../lib/client'); +const EXP = 6; + +// NOTE: This is part of generated `hs-client`. +// Don't introduce any unnecessary dependencies to this. +// This needs to be remain as is for hs-client to be simple. + +const ports = { + main: 12039, + testnet: 13039, + regtest: 14039, + simnet: 15039 +}; + +const HELP = ` +Commands: + $ abandon [hash]: Abandon a transaction. + $ account create [account-name]: Create account. + $ account modify [account-name]: Set account options. + $ account get [account-name]: Get account details. + $ account list: List account names. + $ address [account-name]: Derive new address. + $ balance: Get wallet balance. + $ block [height]: View wallet block. + $ blocks: List wallet blocks. + $ change [account-name]: Derive new change address. + $ coins: View wallet coins. + $ dump [address]: Get wallet key WIF by address. + $ get: View wallet. + $ help: Show help message. + $ history: View TX history. + $ import [wif|hex]: Import private or public key. + $ key [address]: Get wallet key by address. + $ listen: Listen for events. + $ lock: Lock wallet. + $ mkauctiontxs [name] [bid] [lockup] [broadcast]: Create bid and reveal TXs. + $ mktx [address] [value]: Create transaction. + $ mkwallet [id]: Create wallet. + $ pending: View pending TXs. + $ resendwallet [id]: Resend pending transactions for a single wallet. + $ retoken: Create new api key. + $ send [address] [value]: Send transaction. + $ shared add [account-name] [xpubkey]: Add key to account. + $ shared remove [account-name] [xpubkey]: Remove key from account. + $ shared list [account-name]: List keys in account. + $ sign [tx-hex]: Sign transaction. + $ tx [hash]: View transaction details. + $ unlock [passphrase] [timeout?]: Unlock wallet. + $ view [tx-hex]: Parse and view transaction. + $ watch [address]: Import an address. + $ zap [age]: Zap pending wallet TXs. + +If node is run with wallet-auth flag, then wallet commands +require authorization token. +Admin commands require admin permissions for provided authorization token: + $ backup [path]: Backup the wallet db. + $ master: View wallet master key. + $ rescan [height]: Rescan for transactions. + $ resend: Resend pending transactions for all wallets. + $ rpc [command] [args]: Execute RPC command. + $ wallets: List all wallets. + +Other options: + --id [wallet id]: Wallet id. + --passphrase [passphrase]: For signing/account-creation. + --account [account-name]: Account name. + --token [token]: Wallet-specific or admin authorization token. + --api-key [key]: General API authorization key. + +For additional information and a complete list of commands +visit https://hsd-dev.org/api-docs/ +`; + +class CLI { + constructor() { + this.config = new Config('hsd', { + suffix: 'network', + fallback: 'main', + alias: { + 'n': 'network', + 'u': 'url', + 'uri': 'url', + 'k': 'apikey', + 's': 'ssl', + 'h': 'httphost', + 'p': 'httpport' + } + }); + + this.config.load({ + argv: true, + env: true + }); + + this.config.open('hsw.conf'); + + this.argv = this.config.argv; + this.network = this.config.str('network', 'main'); + + const id = this.config.str('id', 'primary'); + const token = this.config.str('token', ''); + + this.client = new WalletClient({ + url: this.config.str('url'), + apiKey: this.config.str('api-key'), + ssl: this.config.bool('ssl'), + host: this.config.str('http-host'), + port: this.config.uint('http-port') + || ports[this.network] + || ports.main, + timeout: this.config.uint('timeout'), + token + }); + + this.wallet = this.client.wallet(id, token); + } + + log(json) { + if (typeof json === 'string') + return console.log.apply(console, arguments); + return console.log(JSON.stringify(json, null, 2)); + } + + async getWallets() { + const wallets = await this.client.getWallets(); + this.log(wallets); + } + + async createWallet() { + const id = this.config.str([0, 'id']); + + const options = { + type: this.config.str('type'), + master: this.config.str('master'), + mnemonic: this.config.str('mnemonic'), + m: this.config.uint('m'), + n: this.config.uint('n'), + witness: this.config.bool('witness'), + passphrase: this.config.str('passphrase'), + bip39Passphrase: this.config.str('bip39Passphrase'), + watchOnly: this.config.has('key') ? true : this.config.bool('watch'), + accountKey: this.config.str('key'), + lookahead: this.config.uint('lookahead'), + language: this.config.str('language') + }; + + const wallet = await this.client.createWallet(id, options); + + this.log(wallet); + } + + async getMaster() { + const master = await this.wallet.getMaster(); + + this.log(master); + } + + async getKey() { + const address = this.config.str(0); + const key = await this.wallet.getKey(address); + + this.log(key); + } + + async getWIF() { + const address = this.config.str(0); + const passphrase = this.config.str('passphrase'); + const key = await this.wallet.getWIF(address, passphrase); + + if (!key) { + this.log('Key not found.'); + return; + } + + this.log(key.privateKey); + } + + async addSharedKey() { + const key = this.config.str(0); + const account = this.config.str('account'); + + await this.wallet.addSharedKey(account, key); + + this.log('Added key.'); + } + + async removeSharedKey() { + const key = this.config.str(0); + const account = this.config.str('account'); + + await this.wallet.removeSharedKey(account, key); + + this.log('Removed key.'); + } + + async getSharedKeys() { + const acct = this.config.str([0, 'account']); + const account = await this.wallet.getAccount(acct); + + if (!account) { + this.log('Account not found.'); + return; + } + + this.log(account.keys); + } + + async getAccount() { + const acct = this.config.str([0, 'account']); + const account = await this.wallet.getAccount(acct); + + this.log(account); + } + + async createAccount() { + const name = this.config.str([0, 'name']); + + const options = { + type: this.config.str('type'), + m: this.config.uint('m'), + n: this.config.uint('n'), + witness: this.config.bool('witness'), + accountKey: this.config.str('key'), + lookahead: this.config.uint('lookahead') + }; + + const account = await this.wallet.createAccount(name, options); + + this.log(account); + } + + async modifyAccount() { + const name = this.config.str([0, 'name']); + + const options = { + lookahead: this.config.uint('lookahead') + }; + + const account = await this.wallet.modifyAccount(name, options); + + this.log(account); + } + + async createAddress() { + const account = this.config.str([0, 'account']); + const addr = await this.wallet.createAddress(account); + + this.log(addr); + } + + async createChange() { + const account = this.config.str([0, 'account']); + const addr = await this.wallet.createChange(account); + + this.log(addr); + } + + async getAccounts() { + const accounts = await this.wallet.getAccounts(); + this.log(accounts); + } + + async getWallet() { + const info = await this.wallet.getInfo(); + this.log(info); + } + + async getWalletHistory() { + const account = this.config.str('account'); + const txs = await this.wallet.getHistory(account); + + this.log(txs); + } + + async getWalletPending() { + const account = this.config.str('account'); + const txs = await this.wallet.getPending(account); + + this.log(txs); + } + + async getWalletCoins() { + const account = this.config.str('account'); + const coins = await this.wallet.getCoins(account); + + this.log(coins); + } + + async listenWallet() { + await this.client.open(); + await this.wallet.open(); + + this.wallet.on('tx', (details) => { + this.log('TX:'); + this.log(details); + }); + + this.wallet.on('confirmed', (details) => { + this.log('TX confirmed:'); + this.log(details); + }); + + this.wallet.on('unconfirmed', (details) => { + this.log('TX unconfirmed:'); + this.log(details); + }); + + this.wallet.on('conflict', (details) => { + this.log('TX conflict:'); + this.log(details); + }); + + this.wallet.on('address', (receive) => { + this.log('New addresses allocated:'); + this.log(receive); + }); + + this.wallet.on('balance', (balance) => { + this.log('Balance:'); + this.log(balance); + }); + + return new Promise((resolve, reject) => { + this.client.once('disconnect', resolve); + }); + } + + async getBalance() { + const account = this.config.str('account'); + const balance = await this.wallet.getBalance(account); + + this.log(balance); + } + + async getMempool() { + const txs = await this.wallet.getMempool(); + + this.log(txs); + } + + async sendTX() { + const outputs = []; + + if (this.config.has('script')) { + outputs.push({ + script: this.config.str('script'), + value: this.config.ufixed([0, 'value'], EXP) + }); + } else { + outputs.push({ + address: this.config.str([0, 'address']), + value: this.config.ufixed([1, 'value'], EXP) + }); + } + + const options = { + account: this.config.str('account'), + passphrase: this.config.str('passphrase'), + outputs: outputs, + smart: this.config.bool('smart'), + rate: this.config.ufixed('rate', EXP), + subtractFee: this.config.bool('subtract-fee') + }; + + const tx = await this.wallet.send(options); + + this.log(tx); + } + + async createAuctionTXs() { + const options = { + name: this.config.str([0, 'name']), + bid: this.config.ufixed([1, 'bid'], EXP), + lockup: this.config.ufixed([2, 'lockup'], EXP), + broadcastBid: this.config.bool([3, 'broadcastBid']), + passphrase: this.config.str('passphrase') + }; + + const txs = await this.wallet.createAuctionTXs(options); + + this.log(txs); + } + + async createTX() { + let output; + + if (this.config.has('script')) { + output = { + script: this.config.str('script'), + value: this.config.ufixed([0, 'value'], EXP) + }; + } else { + output = { + address: this.config.str([0, 'address']), + value: this.config.ufixed([1, 'value'], EXP) + }; + } + + const options = { + account: this.config.str('account'), + passphrase: this.config.str('passphrase'), + outputs: [output], + smart: this.config.bool('smart'), + rate: this.config.ufixed('rate', EXP), + subtractFee: this.config.bool('subtract-fee') + }; + + const tx = await this.wallet.createTX(options); + + this.log(tx); + } + + async signTX() { + const passphrase = this.config.str('passphrase'); + const tx = this.config.str([0, 'tx']); + const signedTx = await this.wallet.sign({tx, passphrase}); + + this.log(signedTx); + } + + async zapWallet() { + const age = this.config.uint([0, 'age'], 72 * 60 * 60); + const account = this.config.str('account'); + + await this.wallet.zap(account, age); + + this.log('Zapped!'); + } + + async abandonTX() { + const hash = this.config.str(0); + + await this.wallet.abandon(hash); + + this.log('Abandoned tx: ' + hash); + } + + async viewTX() { + const raw = this.config.str([0, 'tx']); + const tx = await this.wallet.fill(raw); + + this.log(tx); + } + + async getDetails() { + const hash = this.config.str(0); + const details = await this.wallet.getTX(hash); + + this.log(details); + } + + async getWalletBlocks() { + const blocks = await this.wallet.getBlocks(); + this.log(blocks); + } + + async getWalletBlock() { + const height = this.config.uint(0); + const block = await this.wallet.getBlock(height); + + this.log(block); + } + + async retoken() { + const passphrase = this.config.str('passphrase'); + const result = await this.wallet.retoken(passphrase); + + this.log(result); + } + + async rescan() { + const height = this.config.uint(0); + + await this.client.rescan(height); + + this.log('Rescanning...'); + } + + async resend() { + await this.client.resend(); + + this.log('Resending...'); + } + + async resendWallet() { + await this.wallet.resend(); + + this.log('Resending...'); + } + + async backup() { + const path = this.config.str(0); + + await this.client.backup(path); + + this.log('Backup complete.'); + } + + async importKey() { + const key = this.config.str(0); + const account = this.config.str('account'); + const passphrase = this.config.str('passphrase'); + + if (!key) + throw new Error('No key for import.'); + + if (key.length === 66 || key.length === 130) { + await this.wallet.importPublic(account, key); + this.log('Imported public key.'); + return; + } + + await this.wallet.importPrivate(account, key, passphrase); + + this.log('Imported private key.'); + } + + async importAddress() { + const address = this.config.str(0); + const account = this.config.str('account'); + + await this.wallet.importAddress(account, address); + + this.log('Imported address.'); + } + + async lock() { + await this.wallet.lock(); + + this.log('Locked.'); + } + + async unlock() { + const passphrase = this.config.str(0); + const timeout = this.config.uint(1); + + await this.wallet.unlock(passphrase, timeout); + + this.log('Unlocked.'); + } + + async rpc() { + const method = this.argv.shift(); + if (!method) { + this.log('Missing RPC method'); + return; + } + const params = []; + + for (const arg of this.argv) { + let param; + try { + param = JSON.parse(arg); + } catch (e) { + param = arg; + } + params.push(param); + } + + let result; + try { + result = await this.client.execute(method, params); + } catch (e) { + if (e.type === 'RPCError') { + this.log(e.message); + return; + } + throw e; + } + + this.log(result); + } + + async handleWallet() { + switch (this.argv.shift()) { + case 'abandon': + await this.abandonTX(); + break; + case 'account': + if (this.argv[0] === 'list') { + this.argv.shift(); + await this.getAccounts(); + break; + } + if (this.argv[0] === 'create') { + this.argv.shift(); + await this.createAccount(); + break; + } + if (this.argv[0] === 'modify') { + this.argv.shift(); + await this.modifyAccount(); + break; + } + if (this.argv[0] === 'get') + this.argv.shift(); + await this.getAccount(); + break; + case 'address': + await this.createAddress(); + break; + case 'backup': + await this.backup(); + break; + case 'balance': + await this.getBalance(); + break; + case 'block': + await this.getWalletBlock(); + break; + case 'blocks': + await this.getWalletBlocks(); + break; + case 'change': + await this.createChange(); + break; + case 'coins': + await this.getWalletCoins(); + break; + case 'dump': + await this.getWIF(); + break; + case 'get': + await this.getWallet(); + break; + case 'help': + process.stdout.write(HELP + '\n'); + break; + case 'history': + await this.getWalletHistory(); + break; + case 'import': + await this.importKey(); + break; + case 'key': + await this.getKey(); + break; + case 'listen': + await this.listenWallet(); + break; + case 'lock': + await this.lock(); + break; + case 'master': + await this.getMaster(); + break; + case 'mkauctiontxs': + await this.createAuctionTXs(); + break; + case 'mktx': + await this.createTX(); + break; + case 'mkwallet': + await this.createWallet(); + break; + case 'pending': + await this.getWalletPending(); + break; + case 'rescan': + await this.rescan(); + break; + case 'resend': + await this.resend(); + break; + case 'resendwallet': + await this.resendWallet(); + break; + case 'retoken': + await this.retoken(); + break; + case 'rpc': + await this.rpc(); + break; + case 'send': + await this.sendTX(); + break; + case 'shared': + if (this.argv[0] === 'add') { + this.argv.shift(); + await this.addSharedKey(); + break; + } + if (this.argv[0] === 'remove') { + this.argv.shift(); + await this.removeSharedKey(); + break; + } + if (this.argv[0] === 'list') + this.argv.shift(); + await this.getSharedKeys(); + break; + case 'sign': + await this.signTX(); + break; + case 'tx': + await this.getDetails(); + break; + case 'unlock': + await this.unlock(); + break; + case 'view': + await this.viewTX(); + break; + case 'wallets': + await this.getWallets(); + break; + case 'watch': + await this.importAddress(); + break; + case 'zap': + await this.zapWallet(); + break; + default: + process.stdout.write('Unrecognized command.\n'); + process.stdout.write(HELP + '\n'); + break; + } + } + + async destroy() { + if (this.client.opened) + await this.client.close(); + } +} + +(async () => { + const cli = new CLI(); + await cli.handleWallet(); + await cli.destroy(); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); diff --git a/bin/hsw-rpc b/bin/hsw-rpc new file mode 100755 index 000000000..46e4418e0 --- /dev/null +++ b/bin/hsw-rpc @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +// insert `rpc` as first argument +process.argv.splice(2, 0, 'rpc'); +require('./hsw-cli'); diff --git a/docs/release-files.md b/docs/release-files.md index a98b74d10..e673ac402 100644 --- a/docs/release-files.md +++ b/docs/release-files.md @@ -1,29 +1,32 @@ -Releasing hsd -============= +Releasing hsd and hs-client +=========================== This document contains information about bundling, signing and distributing the files. -`hsd` is distributed through several platforms: `github`, `npm`, `brew`. +`hsd/hs-client` is distributed through several platforms: `github`, `npm`, `brew`. -- [Deploying to github (tag)](#deploying-to-github-tag) - * [Major, minor and patches](#major-minor-and-patches) - + [Major](#major) - + [Minor, Patch](#minor-patch) -- [Deploying to npm](#deploying-to-npm) - * [Deploying latest version, minor and patches included](#deploying-latest-version-minor-and-patches-included) - * [Deploying support versions (previous and life-support)](#deploying-support-versions-previous-and-life-support) -- [Deploying to homebrew](#deploying-to-homebrew) -- [Deploying to handshake.org](#deploying-to-handshakeorg) - * [Building tarball](#building-tarball) - * [Signing and upload](#signing-and-upload) +- [hsd](#hsd) + * [Deploying to github (tag)](#deploying-to-github-tag) + + [Major, minor and patches](#major-minor-and-patches) + - [Major](#major) + - [Minor, Patch](#minor-patch) + * [Deploying to npm](#deploying-to-npm) + + [Deploying latest version, minor and patches included](#deploying-latest-version-minor-and-patches-included) + + [Deploying support versions (previous and life-support)](#deploying-support-versions-previous-and-life-support) + * [Deploying to homebrew](#deploying-to-homebrew) + * [Deploying to handshake.org](#deploying-to-handshakeorg) + + [Building tarball](#building-tarball) + + [Signing and upload](#signing-and-upload) +- [hs-client](#hs-client) +# hsd ## Deploying to github (tag) This does not need many additional actions as we use github as our primary @@ -119,6 +122,33 @@ and create PR with the relevant updates to the `download/index.html` and - Update `download/index.html` with new links. - Create PR to the main repository. +# hs-client + Since hsd v6 `hs-client` is part of the `hsd`. Original [hs-client repo][hsclient] is now used to +publish generated content. `hs-client` version will now be strictly tied to +the `hsd` version. It is then generated from `hsd` code to release separately on +`git` and `npm`. Most of the process is done by the introduced helper script +`scripts/gen-hsclient.js`. It can help you setup `hs-client` that just needs +publishing on `git` and `npm`. It also gives instructions how to do both. + After `hsd` has been released we can also release `hs-client` from the same +commit/tag, just run: `./scripts/gen-hsclient.js` which will generate `hs-client` +package with `git` setup in `tmp` directory. You can alternatively pass +`DIRECTORY` as argument for custom path. If generating git failed for some +reason, it will list commands that needs executing and you can proceed manually +or fix the issues and rerun the script. NOTE, that the script will never try to +publish by itself, only generate files to review locally. + - `./scripts/gen-hsclient.js /tmp/hs-client` - script will also list the commands, + that needs running for publishing to the git and npm. + - `cd /tmp/hs-client` + - `git push -f origin master` - rewrite whole `hs-client` repo with the new content. + - `git push -f origin vVersion` - push newly generated tag to the `hs-client`. + - You can check the `gen-hsclient` output for the proper version or + - `git tag -l` to list. + - `npm publish` - this will also tag it as `latest`. If you want to tag it differently + you can do so, same as above hsd `npm publish`. + - NOTE: You can use `npm publish --dry-run` to see the details before actual + release. + + [homebrew]: https://brew.sh/ [homebrew-repo]: https://github.com/Homebrew/homebrew-core [homebrew-new-formula]: https://github.com/Homebrew/homebrew-core/pull/51014 @@ -126,3 +156,4 @@ and create PR with the relevant updates to the `download/index.html` and [homebrew-guidelines]: https://github.com/Homebrew/homebrew-core/blob/master/CONTRIBUTING.md [handshake-web]: https://github.com/handshake-org/handshake-web/ [bpkg]: https://github.com/chjj/bpkg +[hsclient]: https://github.com/handshake-org/hs-client diff --git a/docs/release-notes/release-notes-5.x.md b/docs/release-notes/release-notes-5.x.md new file mode 100644 index 000000000..38087ba67 --- /dev/null +++ b/docs/release-notes/release-notes-5.x.md @@ -0,0 +1,72 @@ +v5.x Release notes +================== + + + +- [v5.0](#v50) + * [How to Upgrade](#how-to-upgrade) + * [Notable Changes](#notable-changes) + + [Node changes](#node-changes) + + [Wallet changes](#wallet-changes) + * [Changelog](#changelog) + + + +# v5.0 +## How to Upgrade + When upgrading to this version from **hsd v4.x** you must pass `--wallet-migrate=2` when you run it for the first time (**MAJOR** [#769](https://github.com/handshake-org/hsd/pull/769)). + +## Notable Changes +### Node changes + - HTTP API endpoint `/` (`hsd-cli getinfo`) now includes "public" networking settings ([#696](https://github.com/handshake-org/hsd/pull/696)). + + - RPCs `getnameinfo` `getnameresource` `verifymessagewithname` and `getnamebyhash` + now accept an additional boolean parameter `safe` which will resolve the name from the Urkel + tree at the last "safe height" (committed tree root with > 12 confirmations). SPV + nodes can use this option and retrieve Urkel proofs from the p2p network to respond + to these calls. ([#647](https://github.com/handshake-org/hsd/pull/647)) + + - New RPC methods: + - `decoderesource` like `decodescript` accepts hex string as input and returns + JSON formatted DNS records resource. ([#719](https://github.com/handshake-org/hsd/pull/719)) + +### Wallet changes + - HTTP Changes: + - Wallet and account create methods now accept `lookahead` values up to `2^32 - 1` (**MAJOR** [#769](https://github.com/handshake-org/hsd/pull/769)). + + - New RPC methods: + - `createbatch` and `sendbatch` create batch transactions with any number + of outputs with any combination of covenants. ([#686](https://github.com/handshake-org/hsd/pull/686)) + + - Updates related to nonces and blinds (**MAJOR** [#767](https://github.com/handshake-org/hsd/pull/767)) + - Multisig wallets will compute nonces based on the LOWEST public key in the group. + This makes multiparty bidding and revealing more deteministic. Older versions would + always use the wallet's OWN public key. To preserve compatability with older software: + - RPC method `importnonce` now returns an array of blinds instead of a single blind. + - HTTP endpoint `/wallet/:id/nonce/:name`'s response replaces 2 string fields (`nonce`, `blind`) with arrays of the same type (`nonces`, `blinds`) + + +## Changelog + - \[[`7d00f353`](https://github.com/handshake-org/hsd/commit/7d00f353)] - [#769](https://github.com/handshake-org/hsd/pull/769) - **SEMVER-MAJOR wallet**: increase lookahead configuration to 2 ** 32. (@nodech - Nodari Chkuaselidze) + - \[[`b456147e`](https://github.com/handshake-org/hsd/commit/b456147e)] - [#767](https://github.com/handshake-org/hsd/pull/767) - **SEMVER-MAJOR wallet**: derive all keys in generateNonce for multisig (@rithvikvibhu - Rithvik Vibhu) + - \[[`bc4a6796`](https://github.com/handshake-org/hsd/commit/bc4a6796)] - [#768](https://github.com/handshake-org/hsd/pull/768) - **SEMVER-MINOR mempool**: reject TXs that exceed consensus covenant block limits (@pinheadmz - Matthew Zipkin) + - \[[`455bf60f`](https://github.com/handshake-org/hsd/commit/455bf60f)] - [#785](https://github.com/handshake-org/hsd/pull/785) - **SEMVER-MINOR net**: update seeds and checkpoint for 5.x (@pinheadmz - Matthew Zipkin) + - \[[`a747dc8d`](https://github.com/handshake-org/hsd/commit/a747dc8d)] - [#686](https://github.com/handshake-org/hsd/pull/686) - **SEMVER-MINOR wallet**: create batch transactions with any combination of covenants (@pinheadmz - Matthew Zipkin) + - \[[`3fd74e0d`](https://github.com/handshake-org/hsd/commit/3fd74e0d)] - [#696](https://github.com/handshake-org/hsd/pull/696) - **SEMVER-MINOR node-http**: Clarify ports / public ports in node HTTP getinfo (@pinheadmz - Matthew Zipkin) + - \[[`49bf5f49`](https://github.com/handshake-org/hsd/commit/49bf5f49)] - [#647](https://github.com/handshake-org/hsd/pull/647) - **SEMVER-MINOR rpc/pool**: use urkel proofs for namestate in spv mode (@pinheadmz - Matthew Zipkin) + - \[[`5a23a5b5`](https://github.com/handshake-org/hsd/commit/5a23a5b5)] - [#719](https://github.com/handshake-org/hsd/pull/719) - **SEMVER-MINOR node-rpc**: decoderesource (@pinheadmz - Matthew Zipkin) + - \[[`6f112121`](https://github.com/handshake-org/hsd/commit/6f112121)] - [#764](https://github.com/handshake-org/hsd/pull/764) - **wallet**: Follow-up improvements to sendbatch API, renew all (@pinheadmz - Matthew Zipkin) + - \[[`6314c1aa`](https://github.com/handshake-org/hsd/commit/6314c1aa)] - [#788](https://github.com/handshake-org/hsd/pull/788) - **mempool**: Mempool reorg covenants (@pinheadmz - Matthew Zipkin) + - \[[`bf614e0d`](https://github.com/handshake-org/hsd/commit/bf614e0d)] - [#706](https://github.com/handshake-org/hsd/pull/706) - **pool/wallet/spvnode**: test imported names are added to filter and sent (@pinheadmz - Matthew Zipkin) + - \[[`b19d0017`](https://github.com/handshake-org/hsd/commit/b19d0017)] - [#790](https://github.com/handshake-org/hsd/pull/790) - **pkg**: Update package dependencies. (@nodech - Nodari Chkuaselidze) + - \[[`99f43d21`](https://github.com/handshake-org/hsd/commit/99f43d21)] - [#739](https://github.com/handshake-org/hsd/pull/739) - **docs**: Update release process. (@nodech - Nodari Chkuaselidze) + - \[[`185b459a`](https://github.com/handshake-org/hsd/commit/185b459a)] - [#789](https://github.com/handshake-org/hsd/pull/789) - **docs**: Backport v4 release docs (@nodech - Nodari Chkuaselidze) + - \[[`862cba0d`](https://github.com/handshake-org/hsd/commit/862cba0d)] - [#771](https://github.com/handshake-org/hsd/pull/771) - **test**: Chain reset/reorg tests for tree/txn state (@nodech - Nodari Chkuaselidze) + - \[[`309df94a`](https://github.com/handshake-org/hsd/commit/309df94a)] - [#787](https://github.com/handshake-org/hsd/pull/787) - **pkg**: Update minimum required Node.js version to 14 - (@handshake-enthusiast) + - \[[`234a5974`](https://github.com/handshake-org/hsd/commit/234a5974)] - [#784](https://github.com/handshake-org/hsd/pull/784) - **wallet**: use correct fee in rpc gettransaction (@rithvikvibhu - Rithvik Vibhu) + - \[[`eb5e6a82`](https://github.com/handshake-org/hsd/commit/eb5e6a82)] - [#778](https://github.com/handshake-org/hsd/pull/778) - **wallet**: Remove Coinselector.MAX_FEE (@pinheadmz - Matthew Zipkin) + - \[[`e07ba542`](https://github.com/handshake-org/hsd/commit/e07ba542)] - [#781](https://github.com/handshake-org/hsd/pull/781) - **wallet**: Ignore unknown bids during reveal (@pinheadmz - Matthew Zipkin) + - \[[`1326351c`](https://github.com/handshake-org/hsd/commit/1326351c)] - [#779](https://github.com/handshake-org/hsd/pull/779) - **hostlist**: only return MANUAL addrs in getLocal() for all networks (@pinheadmz - Matthew Zipkin) + - \[[`9cf8cb83`](https://github.com/handshake-org/hsd/commit/9cf8cb83)] - [#776](https://github.com/handshake-org/hsd/pull/776) - **docker/docs**: Fix npm warning (@NetOpWibby) + - \[[`395878a0`](https://github.com/handshake-org/hsd/commit/395878a0)] - [#775](https://github.com/handshake-org/hsd/pull/775) - **docs**: fix doc (@Falci - Fernando Falci) + - \[[`fb5501c5`](https://github.com/handshake-org/hsd/commit/fb5501c5)] - [#765](https://github.com/handshake-org/hsd/pull/765) - **chore(BIP39)**: added Portuguese wordlist (@Falci - Fernando Falci) diff --git a/docs/release-notes/release-notes-6.x.md b/docs/release-notes/release-notes-6.x.md new file mode 100644 index 000000000..dc4cbf4e2 --- /dev/null +++ b/docs/release-notes/release-notes-6.x.md @@ -0,0 +1,91 @@ +v6.x Release notes +================== + + + +- [v6.0.0](#v600) + * [How to Upgrade](#how-to-upgrade) + * [Notable Changes](#notable-changes) + + [Network](#network) + + [Package](#package) + + [Node and Wallet HTTP API](#node-and-wallet-http-api) + + [Wallet Changes](#wallet-changes) + - [Configuration](#configuration) + - [Wallet API](#wallet-api) + * [Changelog](#changelog) +- [v6.1.0](#v610) + * [Changelog](#changelog-1) + + + +# v6.0.0 +## How to Upgrade + This version does not have any migrations, so upgrade does not need an action. + +## Notable Changes +### Network + ICANN Lockup soft fork has been included. ([#819](https://github.com/handshake-org/hsd/pull/819), [#828](https://github.com/handshake-org/hsd/pull/828), [#834](https://github.com/handshake-org/hsd/pull/834)) + Miners who want to support the soft-fork need to start signalling with `icannlockup` bit. + +### Package + [hs-client][hs-client] is now part of the `hsd`. [hs-client][hs-client] will be generated from the `hsd`. ([#796](https://github.com/handshake-org/hsd/pull/796)). + +### Node and Wallet HTTP API + Validation errors, request paremeter errors or bad HTTP requests will no +longer return (and log) `500` status code, instead will return `400`. ([#807](https://github.com/handshake-org/hsd/pull/807), [#835](https://github.com/handshake-org/hsd/pull/835)) + +### Wallet Changes +#### Configuration + `hsd.conf` can now be used to define wallet options, when wallet is running as +a plugin. Configurations with `wallet-` prefix will be passed to the wallet. +`hsd.conf` wont be used if the wallet is running in standalone mode. ([#806](https://github.com/handshake-org/hsd/pull/806)) + +- Remove `check-lookahead` option from walletdb. ([#797](https://github.com/handshake-org/hsd/pull/797)) + +#### Wallet API + +- HTTP Changes: + - `/wallet/:id/open` no longer accepts `force` flag. (it was not used) ([#815](https://github.com/handshake-org/hsd/pull/815)) +- RPC Changes: + - `createopen` and `sendopen` no longer accept `force` as an argument. (was not used) ([#815](https://github.com/handshake-org/hsd/pull/815)) + - Introduce new API to modify account: `PATCH /wallet/:id/account/:account`. ([#797](https://github.com/handshake-org/hsd/pull/797)) + +## Changelog + - \[[`90cdf84e`](https://github.com/handshake-org/hsd/commit/90cdf84e)] - [#819](https://github.com/handshake-org/hsd/pull/819) - **SEMVER-MAJOR chain**: Icann lockup - SOFT-FORK (@nodech - Nodari Chkuaselidze) + - \[[`2fab14e8`](https://github.com/handshake-org/hsd/commit/2fab14e8)] - [#834](https://github.com/handshake-org/hsd/pull/834) - **SEMVER-MAJOR chain**: icannlockup soft fork - update start time and timeout. (@nodech - Nodari Chkuaselidze) + - \[[`d7a7cf92`](https://github.com/handshake-org/hsd/commit/d7a7cf92)] - [#828](https://github.com/handshake-org/hsd/pull/828) - **SEMVER-MAJOR chain**: icann lockup soft fork - Add 10k alexa to the soft-fork. (@nodech - Nodari Chkuaselidze) + - \[[`0f2e73f5`](https://github.com/handshake-org/hsd/commit/0f2e73f5)] - [#797](https://github.com/handshake-org/hsd/pull/797) - **SEMVER-MAJOR wallet-http**: endpoint for updating account lookahead (@nodech - Nodari Chkuaselidze) + - \[[`b03d7007`](https://github.com/handshake-org/hsd/commit/b03d7007)] - [#807](https://github.com/handshake-org/hsd/pull/807) - **SEMVER-MAJOR http**: update validator status codes. (@nodech - Nodari Chkuaselidze) + - \[[`93ee4420`](https://github.com/handshake-org/hsd/commit/93ee4420)] - [#806](https://github.com/handshake-org/hsd/pull/806) - **SEMVER-MAJOR pkg**: allow plugins to inherit configurations from the hsd.conf (@nodech - Nodari Chkuaselidze) + - \[[`03898507`](https://github.com/handshake-org/hsd/commit/03898507)] - [#796](https://github.com/handshake-org/hsd/pull/796) - **SEMVER-MAJOR pkg**: Import `hs-client` into the project. (@nodech - Nodari Chkuaselidze) + - \[[`5fe70c05`](https://github.com/handshake-org/hsd/commit/5fe70c05)] - [#815](https://github.com/handshake-org/hsd/pull/815) - **SEMVER-MAJOR wallet**: remove force option from wallet.makeOpen. (@nodech - Nodari Chkuaselidze) + - \[[`c721c5be`](https://github.com/handshake-org/hsd/commit/c721c5be)] - [#836](https://github.com/handshake-org/hsd/pull/836) - **SEMVER-MINOR network**: Update network seeds, checkpoints and deps (@nodech - Nodari Chkuaselidze) + - \[[`15f74ccf`](https://github.com/handshake-org/hsd/commit/15f74ccf)] - [#824](https://github.com/handshake-org/hsd/pull/824) - **SEMVER-MINOR wallet-rpc**: Added totalSigs to JSON response for scriptToJSON (@Nathanwoodburn - Nathan Woodburn) + - \[[`5bed384b`](https://github.com/handshake-org/hsd/commit/5bed384b)] - [#818](https://github.com/handshake-org/hsd/pull/818) - **SEMVER-MINOR wallet-http**: reveals all now can be created without broadcasting. (@nodech - Nodari Chkuaselidze) + - \[[`5387e3a0`](https://github.com/handshake-org/hsd/commit/5387e3a0)] - [#802](https://github.com/handshake-org/hsd/pull/802) - **SEMVER-MINOR wallet-http**: add output paths to JSON (@pinheadmz - Matthew Zipkin) + - \[[`32482a5c`](https://github.com/handshake-org/hsd/commit/32482a5c)] - [#746](https://github.com/handshake-org/hsd/pull/746) - **bin**: replace shell scripts with js require (@rithvikvibhu - Rithvik Vibhu) + - \[[`5210af2d`](https://github.com/handshake-org/hsd/commit/5210af2d)] - [#833](https://github.com/handshake-org/hsd/pull/833) - **bin**: fix config api-key alias (@rithvikvibhu - Rithvik Vibhu) + - \[[`fec9fddf`](https://github.com/handshake-org/hsd/commit/fec9fddf)] - [#835](https://github.com/handshake-org/hsd/pull/835) - **pkg**: update bweb. (@nodech - Nodari Chkuaselidze) + - \[[`81bddcd2`](https://github.com/handshake-org/hsd/commit/81bddcd2)] - [#825](https://github.com/handshake-org/hsd/pull/825) - **wallet**: fix makeBatch to generate addresses early (@rithvikvibhu - Rithvik Vibhu) + - \[[`ed27e7f6`](https://github.com/handshake-org/hsd/commit/ed27e7f6)] - [#826](https://github.com/handshake-org/hsd/pull/826) - **mempool**: more invalidation tests. (@nodech - Nodari Chkuaselidze) + - \[[`5eae8f62`](https://github.com/handshake-org/hsd/commit/5eae8f62)] - [#827](https://github.com/handshake-org/hsd/pull/827) - **docs**: Update checkpoints guide in release-process.md - @handshake-enthusiast + - \[[`aefc311f`](https://github.com/handshake-org/hsd/commit/aefc311f)] - [#820](https://github.com/handshake-org/hsd/pull/820) - **wallet**: minor clean up (@nodech - Nodari Chkuaselidze) + - \[[`614bfaf5`](https://github.com/handshake-org/hsd/commit/614bfaf5)] - [#813](https://github.com/handshake-org/hsd/pull/813) - **mempool**: invalidate claims when period ends. (@nodech - Nodari Chkuaselidze) + - \[[`433d5e9e`](https://github.com/handshake-org/hsd/commit/433d5e9e)] - [#817](https://github.com/handshake-org/hsd/pull/817) - **chain**: clean up unused hardened option from getNameStatus. (@nodech - Nodari Chkuaselidze) + - \[[`61c1e057`](https://github.com/handshake-org/hsd/commit/61c1e057)] - [#816](https://github.com/handshake-org/hsd/pull/816) - **wallet**: refactor createTX to be more flexible (@nodech - Nodari Chkuaselidze) + - \[[`e122b127`](https://github.com/handshake-org/hsd/commit/e122b127)] - [#814](https://github.com/handshake-org/hsd/pull/814) - **test**: minor mempool test cleanup. (@nodech - Nodari Chkuaselidze) + - \[[`c1b2180c`](https://github.com/handshake-org/hsd/commit/c1b2180c)] - [#808](https://github.com/handshake-org/hsd/pull/808) - **test**: fix .rejects usage. (@nodech - Nodari Chkuaselidze) + - \[[`2bb8f0b0`](https://github.com/handshake-org/hsd/commit/2bb8f0b0)] - [#810](https://github.com/handshake-org/hsd/pull/810) - **docs**: Minor doc fix about release docs (@nodech - Nodari Chkuaselidze) + - \[[`6b47c3a0`](https://github.com/handshake-org/hsd/commit/6b47c3a0)] - [#805](https://github.com/handshake-org/hsd/pull/805) - **net**: update last checkpoint. (@nodech - Nodari Chkuaselidze) + - \[[`008374ca`](https://github.com/handshake-org/hsd/commit/008374ca)] - [#800](https://github.com/handshake-org/hsd/pull/800) - **docs**: backport v5.0.0 release notes. (@nodech - Nodari Chkuaselidze) + - \[[`500d638d`](https://github.com/handshake-org/hsd/commit/500d638d)] - [#838](https://github.com/handshake-org/hsd/pull/838) - **scripts**: Update hs-client generator. (@nodech - Nodari Chkuaselidze) + +[hs-client]: https://github.com/handshake-org/hs-client + +# v6.1.0 +Re-enable bip9 signalling that was disabled in v2. See [#842](https://github.com/handshake-org/hsd/pull/842) +- `getblocktemplate` can now start signalling soft-forks again using `rules` parameter. (e.g. `getblocktemplate '{ "rules": [ "icannlockup" ] }'`) +- `getwork` will now signal **ALL** soft-forks again. + +## Changelog + - \[[`6dc5249d`](https://github.com/handshake-org/hsd/commit/6dc5249d)] - [#842](https://github.com/handshake-org/hsd/pull/842) - **SEMVER-MINOR miner**: Fix bip9 signalling. (@rithvikvibhu - Rithvik Vibhu) diff --git a/docs/release-process.md b/docs/release-process.md index c3023a5f0..772dc3607 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -124,7 +124,7 @@ only `patch`es until release(feature lock). Process example (e.g. `latest` was `3.1.0` and we are releasing `4.0.0-rc.1`): - Update network seeds. - - Add new checkpoint. Last checkpoint + 6 months. + - Add a new checkpoint which should be ~4-6 months before the release. - update `package.json` version to `4.99.0` in `master` branch. - update `package-lock.json` by running `npm install`. - create branch `4.x` from the `master`. @@ -187,9 +187,9 @@ Above process continues until last week where we do the actual release: This applies to all minor and patches that are being backported to the `latest`, `previous` versions. Example `v4.0.0` is `latest` and we want to release minor(process is the same for `previous` minor and patch releases): - - update `package.json` version to `4.1.0` in the `4.x-proposal` branch. - - update `package-lock.json` by running `npm install`. - - Create Release doc file `docs/release-notes/release-notes-4.1.0.md`. + - Update `package.json` version to `4.1.0` in the `4.x-proposal` branch. + - Update `package-lock.json` by running `npm install`. + - Update Release doc file `docs/release-notes/release-notes-4.x.md`. - NO CHANGES TO CHANGELOG - this is minor. - Create PR `Release v4.1.0` from the branch `4.x-proposal` to `4.x`. - PR description containing list of PRs (similar to release notes) diff --git a/docs/release-schedule.md b/docs/release-schedule.md index ec03baf3f..bf3d78813 100644 --- a/docs/release-schedule.md +++ b/docs/release-schedule.md @@ -5,5 +5,8 @@ Maintenance period | ------- | ------------ | --------------- | ----------- | | v3.x | 2021-10-14 | 2022-10-31 | 2023-04-30 | | v4.x | 2022-04-30 | 2023-04-30 | 2023-10-31 | -| v5.x | 2022-10-31 | 2023-10-31 | 2024-04-30 | -| v6.x | 2023-04-30 | after v8.0 | after v9.0 | +| v5.x | 2023-01-19 | 2023-10-31 | 2024-04-30 | +| v6.x | 2023-08-10 | 2024-04-30 | 2024-10-31 | +| v7.x | 2024-04-30 | 2024-10-31 | 2024-04-30 | +| v8.x | 2024-10-31 | 2025-04-30 | 2025-10-31 | +| v9.x | 2025-04-30 | 2025-10-30 | 2026-04-30 | diff --git a/jsdoc.json b/jsdoc.json index ecc67ec95..df88c540b 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -8,7 +8,10 @@ "includePattern": ".+\\.js(doc)?$", "excludePattern": "(^|\\/|\\\\)_" }, - "plugins": ["plugins/markdown"], + "plugins": [ + "plugins/markdown", + "plugins/rm-imports" + ], "templates": { "cleverLinks": false, "monospaceLinks": false diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 2b50df898..5b89853e3 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -28,8 +28,14 @@ const {OwnershipProof} = require('../covenants/ownership'); const AirdropProof = require('../primitives/airdropproof'); const {CriticalError} = require('../errors'); const thresholdStates = common.thresholdStates; +const scanActions = common.scanActions; const {states} = NameState; +const { + VERIFY_COVENANTS_HARDENED, + VERIFY_COVENANTS_LOCKUP +} = rules.nameFlags; + /** * Blockchain * @alias module:blockchain.Chain @@ -126,6 +132,26 @@ class Chain extends AsyncEmitter { return this.db.close(); } + /** + * Get compaction heights. + * @returns {Promise} + */ + + async getCompactionHeights() { + if (this.options.spv) + return null; + + const {keepBlocks} = this.network.block; + const {compactionHeight} = await this.db.getTreeState(); + const {compactTreeInitInterval} = this.options; + const compactFrom = compactionHeight + keepBlocks + compactTreeInitInterval; + + return { + compactionHeight, + compactFrom + }; + } + /** * Check if we need to compact tree data. * @returns {Promise} - Should we sync @@ -145,9 +171,7 @@ class Chain extends AsyncEmitter { if (this.height <= startFrom) return true; - const {compactionHeight} = await this.db.getTreeState(); - const {compactTreeInitInterval} = this.options; - const compactFrom = compactionHeight + keepBlocks + compactTreeInitInterval; + const {compactFrom} = await this.getCompactionHeights(); if (compactFrom > this.height) { this.logger.debug( @@ -201,10 +225,9 @@ class Chain extends AsyncEmitter { assert(state); const view = new CoinView(); - const hardened = state.hasHardening(); for (const tx of block.txs) - await this.verifyCovenants(tx, view, height, hardened); + await this.verifyCovenants(tx, view, height, state.nameFlags); // If the chain replay crosses a tree interval, it will commit // and write to disk in saveNames(), resetting the `txn` like usual. @@ -699,7 +722,11 @@ class Chain extends AsyncEmitter { // Disable RSA-1024. if (await this.isActive(prev, deployments.hardening)) - state.hardening = true; + state.nameFlags |= rules.nameFlags.VERIFY_COVENANTS_HARDENED; + + // Disable ICANN, TOP100 and CUSTOM TLDs from getting auctioned. + if (await this.isActive(prev, deployments.icannlockup)) + state.nameFlags |= rules.nameFlags.VERIFY_COVENANTS_LOCKUP; return state; } @@ -721,6 +748,9 @@ class Chain extends AsyncEmitter { if (this.height === this.network.deflationHeight) this.logger.warning('Name claim deflation has been activated.'); + if (!this.state.hasICANNLockup() && state.hasICANNLockup()) + this.logger.warning('ICANN lockup has been activated.'); + this.state = state; } @@ -733,7 +763,6 @@ class Chain extends AsyncEmitter { */ async updateInputs(block, prev, state) { - const hardened = state.hasHardening(); const view = new CoinView(); const height = prev.height + 1; @@ -749,7 +778,7 @@ class Chain extends AsyncEmitter { 'BUG: Spent inputs in historical data!'); } - await this.verifyCovenants(tx, view, height, hardened); + await this.verifyCovenants(tx, view, height, state.nameFlags); view.addTX(tx, height); } @@ -777,7 +806,6 @@ class Chain extends AsyncEmitter { async verifyInputs(block, prev, state) { const network = this.network; - const hardened = state.hasHardening(); const view = new CoinView(); const height = prev.height + 1; const interval = network.halvingInterval; @@ -854,7 +882,7 @@ class Chain extends AsyncEmitter { } // Verify covenants. - await this.verifyCovenants(tx, view, height, hardened); + await this.verifyCovenants(tx, view, height, state.nameFlags); // Add new coins. view.addTX(tx, height); @@ -915,7 +943,7 @@ class Chain extends AsyncEmitter { * Verify a renewal. * @param {Hash} hash * @param {Number} height - * @returns {Boolean} + * @returns {Promise} */ async verifyRenewal(hash, height) { @@ -961,14 +989,14 @@ class Chain extends AsyncEmitter { * @param {TX} tx * @param {CoinView} view * @param {Number} height - * @param {Boolean} hardened + * @param {NameFlags} nameFlags */ - async verifyCovenants(tx, view, height, hardened) { + async verifyCovenants(tx, view, height, nameFlags) { assert(tx); assert(view instanceof CoinView); assert((height >>> 0) === height); - assert(typeof hardened === 'boolean'); + assert(typeof nameFlags === 'number'); const {types} = rules; const network = this.network; @@ -997,11 +1025,8 @@ class Chain extends AsyncEmitter { const ns = await view.getNameState(this.db, nameHash); if (ns.isNull()) { - if (!covenant.isClaim() && !covenant.isOpen()) { - const error = new CriticalError('Database inconsistency.'); - this.emit('abort', error); - throw error; - } + if (!covenant.isClaim() && !covenant.isOpen()) + throw new Error('Database inconsistency.'); const name = covenant.get(2); ns.set(name, height); @@ -1055,7 +1080,7 @@ class Chain extends AsyncEmitter { // any weak algorithms (i.e. RSA-1024). // Any future emergency soft-forks should // also be included below this check. - if (hardened && weak) { + if ((nameFlags & VERIFY_COVENANTS_HARDENED) && weak) { throw new VerifyError(tx, 'invalid', 'bad-claim-algorithm', @@ -1160,6 +1185,16 @@ class Chain extends AsyncEmitter { 100); } + // Make sure locked up names are not opened if ICANN LOCKUP has + // activated. + const isLockUpActive = nameFlags & VERIFY_COVENANTS_LOCKUP; + if (isLockUpActive && rules.isLockedUp(nameHash, height, network)) { + throw new VerifyError(tx, + 'invalid', + 'bad-open-lockedup', + 100); + } + // On mainnet, names are released on a // weekly basis for the first year. if (!rules.hasRollout(nameHash, height, network)) { @@ -1307,7 +1342,7 @@ class Chain extends AsyncEmitter { // registrar's keys are compromised. if (ns.isClaimable(height, network)) { // Soft-fork #1 (RSA hardening). - if (hardened && ns.weak) { + if ((nameFlags & VERIFY_COVENANTS_HARDENED) && ns.weak) { throw new VerifyError(tx, 'invalid', 'bad-register-state', @@ -2216,7 +2251,7 @@ class Chain extends AsyncEmitter { /** * Scan the blockchain for transactions containing specified address hashes. - * @param {Hash} start - Block hash to start at. + * @param {Hash|Number} start - Block hash or height to start at. * @param {Bloom} filter - Bloom filter containing tx and address hashes. * @param {Function} iter - Iterator. * @returns {Promise} @@ -2231,6 +2266,121 @@ class Chain extends AsyncEmitter { } } + /** @typedef {import('./common').ScanAction} ScanAction */ + + /** + * @callback ScanInteractiveIterCB + * @param {ChainEntry} entry + * @param {TX[]} txs + * @returns {Promise} + */ + + /** + * Interactive scan the blockchain for transactions containing specified + * address hashes. Allows repeat and abort. + * @param {Hash|Number} start - Block hash or height to start at. + * @param {BloomFilter} filter - Starting bloom filter containing tx, + * address and name hashes. + * @param {ScanInteractiveIterCB} iter - Iterator. + * @param {Boolean} [fullLock=false] + * @returns {Promise} + */ + + async scanInteractive(start, filter, iter, fullLock = false) { + if (fullLock) { + const unlock = await this.locker.lock(); + try { + // We lock the whole chain, no longer lock per block scan. + return await this._scanInteractive(start, filter, iter, false); + } catch (e) { + this.logger.debug('Scan(interactive) errored. Error: %s', e.message); + throw e; + } finally { + unlock(); + } + } + + return this._scanInteractive(start, filter, iter, true); + } + + /** + * Interactive scan the blockchain for transactions containing specified + * address hashes. Allows repeat and abort. + * @param {Hash|Number} start - Block hash or height to start at. + * @param {BloomFilter} filter - Starting bloom filter containing tx, + * address and name hashes. + * @param {ScanInteractiveIterCB} iter - Iterator. + * @param {Boolean} [lockPerScan=true] - if we should lock per block scan. + * @returns {Promise} + */ + + async _scanInteractive(start, filter, iter, lockPerScan = true) { + if (start == null) + start = this.network.genesis.hash; + + if (typeof start === 'number') + this.logger.info('Scanning(interactive) from height %d.', start); + else + this.logger.info('Scanning(interactive) from block %x.', start); + + let hash = start; + + while (hash != null) { + let unlock; + + if (lockPerScan) + unlock = await this.locker.lock(); + + try { + const {entry, txs} = await this.db.scanBlock(hash, filter); + + const action = await iter(entry, txs); + + if (!action || typeof action !== 'object') + throw new Error('Did not get proper action'); + + switch (action.type) { + case scanActions.REPEAT: { + break; + } + case scanActions.REPEAT_SET: { + // try again with updated filter. + filter = action.filter; + break; + } + case scanActions.REPEAT_ADD: { + if (!filter) + throw new Error('No filter set.'); + + for (const chunk of action.chunks) + filter.add(chunk); + break; + } + case scanActions.NEXT: { + const next = await this.getNext(entry); + hash = next && next.hash; + break; + } + case scanActions.ABORT: { + this.logger.info('Scan(interactive) aborted at %x (%d).', + entry.hash, entry.height); + throw new Error('scan request aborted.'); + } + default: + this.logger.debug('Scan(interactive) aborting. Unknown action: %d', + action.type); + throw new Error('Unknown action.'); + } + } catch (e) { + this.logger.debug('Scan(interactive) errored. Error: %s', e.message); + throw e; + } finally { + if (lockPerScan) + unlock(); + } + } + } + /** * Add a block to the chain, perform all necessary verification. * @param {Block} block @@ -3563,7 +3713,7 @@ class Chain extends AsyncEmitter { const state = await this.getState(prev, deployment); if (state === thresholdStates.LOCKED_IN - || (state === thresholdStates.STARTED && deployment.force)) { + || state === thresholdStates.STARTED) { version |= 1 << deployment.bit; } } @@ -3860,8 +4010,8 @@ class ChainOptions { } if (options.prune != null) { - assert(!options.spv, 'Can not prune in spv mode.'); assert(typeof options.prune === 'boolean'); + assert(!options.prune || !options.spv, 'Can not prune in spv mode.'); this.prune = options.prune; } @@ -3942,11 +4092,15 @@ class DeploymentState { this.tip = tip; this.flags = Script.flags.MANDATORY_VERIFY_FLAGS; this.lockFlags = common.lockFlags.MANDATORY_LOCKTIME_FLAGS; - this.hardening = false; + this.nameFlags = rules.nameFlags.MANDATORY_VERIFY_COVENANT_FLAGS; } hasHardening() { - return this.hardening; + return (this.nameFlags & VERIFY_COVENANTS_HARDENED) !== 0; + } + + hasICANNLockup() { + return (this.nameFlags & VERIFY_COVENANTS_LOCKUP) !== 0; } } diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index 773ce37ec..278f007c1 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -15,10 +15,8 @@ const {Tree} = require('urkel'); const {BufferMap, BufferSet} = require('buffer-map'); const ChainMigrator = require('./migrations'); const Amount = require('../ui/amount'); -const Network = require('../protocol/network'); const CoinView = require('../coins/coinview'); const UndoCoins = require('../coins/undocoins'); -const layout = require('./layout'); const consensus = require('../protocol/consensus'); const Block = require('../primitives/block'); const Outpoint = require('../primitives/outpoint'); @@ -31,6 +29,13 @@ const NameState = require('../covenants/namestate'); const NameUndo = require('../covenants/undo'); const {BitField} = require('../covenants/bitfield'); const {types} = rules; +const layout = require('./layout'); +const { + TreeState, + StateCache, + ChainState, + ChainFlags +} = require('./records'); /** * ChainDB @@ -1158,7 +1163,7 @@ class ChainDB { /** * Get bitfield. - * @returns {Buffer} + * @returns {Promise} */ async getField() { @@ -1172,9 +1177,8 @@ class ChainDB { /** * Get a coin (unspents only). - * @private * @param {Outpoint} prevout - * @returns {Promise} - Returns {@link CoinEntry}. + * @returns {Promise} - Returns {@link CoinEntry}. */ async readCoin(prevout) { @@ -1316,16 +1320,13 @@ class ChainDB { /** * Get name status. * @param {Buffer} nameHash + * @param {Number} height - used for expiration checks. * @returns {Promise} */ - async getNameStatus(nameHash, height, hardened) { - if (hardened == null) - hardened = false; - + async getNameStatus(nameHash, height) { assert(Buffer.isBuffer(nameHash)); assert((height >>> 0) === height); - assert(typeof hardened === 'boolean'); const network = this.network; const ns = await this.getNameState(nameHash); @@ -1603,7 +1604,7 @@ class ChainDB { entry.hash, entry.height); for (const tx of block.txs) { - if (tx.test(filter)) + if (tx.testAndMaybeUpdate(filter)) txs.push(tx); } @@ -1615,6 +1616,57 @@ class ChainDB { this.logger.info('Finished scanning %d blocks.', total); } + /** + * @typedef {Object} ScanBlockResult + * @property {ChainEntry} entry + * @property {TX[]} txs + */ + + /** + * Interactive scans block checks. + * @param {Hash|Number} blockID - Block hash or height to start at. + * @param {BloomFilter} [filter] - Starting bloom filter containing tx, + * address and name hashes. + * @returns {Promise} + */ + + async scanBlock(blockID, filter) { + assert(blockID != null); + + const entry = await this.getEntry(blockID); + + if (!entry) + throw new Error('Could not find entry.'); + + if (!await this.isMainChain(entry)) + throw new Error('Cannot rescan an alternate chain.'); + + const block = await this.getBlock(entry.hash); + + if (!block) + throw new Error('Block not found.'); + + this.logger.info( + 'Scanning block %x (%d)', + entry.hash, entry.height); + + let txs = []; + + if (!filter) { + txs = block.txs; + } else { + for (const tx of block.txs) { + if (tx.testAndMaybeUpdate(filter)) + txs.push(tx); + } + } + + return { + entry, + txs + }; + } + /** * Save an entry to the database and optionally * connect it as the tip. Note that this method @@ -2447,352 +2499,6 @@ class ChainDB { } } -/** - * ChainFlags - */ - -class ChainFlags extends bio.Struct { - /** - * Create chain flags. - * @alias module:blockchain.ChainFlags - * @constructor - */ - - constructor(options) { - super(); - - this.network = Network.primary; - this.spv = false; - this.prune = false; - this.indexTX = false; - this.indexAddress = false; - - if (options) - this.fromOptions(options); - } - - fromOptions(options) { - this.network = Network.get(options.network); - - if (options.spv != null) { - assert(typeof options.spv === 'boolean'); - this.spv = options.spv; - } - - if (options.prune != null) { - assert(typeof options.prune === 'boolean'); - this.prune = options.prune; - } - - if (options.indexTX != null) { - assert(typeof options.indexTX === 'boolean'); - this.indexTX = options.indexTX; - } - - if (options.indexAddress != null) { - assert(typeof options.indexAddress === 'boolean'); - this.indexAddress = options.indexAddress; - } - - return this; - } - - getSize() { - return 12; - } - - write(bw) { - let flags = 0; - - if (this.spv) - flags |= 1 << 0; - - if (this.prune) - flags |= 1 << 1; - - if (this.indexTX) - flags |= 1 << 2; - - if (this.indexAddress) - flags |= 1 << 3; - - bw.writeU32(this.network.magic); - bw.writeU32(flags); - bw.writeU32(0); - - return bw; - } - - read(br) { - this.network = Network.fromMagic(br.readU32()); - - const flags = br.readU32(); - - this.spv = (flags & 1) !== 0; - this.prune = (flags & 2) !== 0; - this.indexTX = (flags & 4) !== 0; - this.indexAddress = (flags & 8) !== 0; - - return this; - } -} - -/** - * Chain State - */ - -class ChainState extends bio.Struct { - /** - * Create chain state. - * @alias module:blockchain.ChainState - * @constructor - */ - - constructor() { - super(); - this.tip = consensus.ZERO_HASH; - this.tx = 0; - this.coin = 0; - this.value = 0; - this.burned = 0; - this.committed = false; - } - - inject(state) { - this.tip = state.tip; - this.tx = state.tx; - this.coin = state.coin; - this.value = state.value; - this.burned = state.burned; - return this; - } - - connect(block) { - this.tx += block.txs.length; - } - - disconnect(block) { - this.tx -= block.txs.length; - } - - add(coin) { - this.coin += 1; - this.value += coin.value; - } - - spend(coin) { - this.coin -= 1; - this.value -= coin.value; - } - - burn(coin) { - this.coin += 1; - this.burned += coin.value; - } - - unburn(coin) { - this.coin -= 1; - this.burned -= coin.value; - } - - commit(hash) { - assert(Buffer.isBuffer(hash)); - this.tip = hash; - this.committed = true; - return this.encode(); - } - - getSize() { - return 64; - } - - write(bw) { - bw.writeHash(this.tip); - bw.writeU64(this.tx); - bw.writeU64(this.coin); - bw.writeU64(this.value); - bw.writeU64(this.burned); - return bw; - } - - read(br) { - this.tip = br.readHash(); - this.tx = br.readU64(); - this.coin = br.readU64(); - this.value = br.readU64(); - this.burned = br.readU64(); - return this; - } -} - -/** - * State Cache - */ - -class StateCache { - /** - * Create state cache. - * @alias module:blockchain.StateCache - * @constructor - */ - - constructor(network) { - this.network = network; - this.bits = []; - this.updates = []; - this.init(); - } - - init() { - for (let i = 0; i < 32; i++) - this.bits.push(null); - - for (const {bit} of this.network.deploys) { - assert(!this.bits[bit]); - this.bits[bit] = new BufferMap(); - } - } - - set(bit, entry, state) { - const cache = this.bits[bit]; - - assert(cache); - - if (cache.get(entry.hash) !== state) { - cache.set(entry.hash, state); - this.updates.push(new CacheUpdate(bit, entry.hash, state)); - } - } - - get(bit, entry) { - const cache = this.bits[bit]; - - assert(cache); - - const state = cache.get(entry.hash); - - if (state == null) - return -1; - - return state; - } - - commit() { - this.updates.length = 0; - } - - drop() { - for (const {bit, hash} of this.updates) { - const cache = this.bits[bit]; - assert(cache); - cache.delete(hash); - } - - this.updates.length = 0; - } - - insert(bit, hash, state) { - const cache = this.bits[bit]; - assert(cache); - cache.set(hash, state); - } -} - -/** - * Cache Update - */ - -class CacheUpdate { - /** - * Create cache update. - * @constructor - * @ignore - */ - - constructor(bit, hash, state) { - this.bit = bit; - this.hash = hash; - this.state = state; - } - - encode() { - const data = Buffer.allocUnsafe(1); - data[0] = this.state; - return data; - } -} - -/** - * Tree related state. - */ - -class TreeState extends bio.Struct { - /** - * Create tree state. - * @constructor - * @ignore - */ - - constructor() { - super(); - this.treeRoot = consensus.ZERO_HASH; - this.commitHeight = 0; - this.compactionRoot = consensus.ZERO_HASH; - this.compactionHeight = 0; - - this.committed = false; - } - - inject(state) { - this.treeRoot = state.treeRoot; - this.commitHeight = state.treeHeight; - this.compactionHeight = state.compactionHeight; - this.compactionRoot = state.compactionRoot; - - return this; - } - - compact(hash, height) { - assert(Buffer.isBuffer(hash)); - assert((height >>> 0) === height); - - this.compactionRoot = hash; - this.compactionHeight = height; - }; - - commit(hash, height) { - assert(Buffer.isBuffer(hash)); - assert((height >>> 0) === height); - - this.treeRoot = hash; - this.commitHeight = height; - this.committed = true; - return this.encode(); - } - - getSize() { - return 72; - } - - write(bw) { - bw.writeHash(this.treeRoot); - bw.writeU32(this.commitHeight); - bw.writeHash(this.compactionRoot); - bw.writeU32(this.compactionHeight); - - return bw; - } - - read(br) { - this.treeRoot = br.readHash(); - this.commitHeight = br.readU32(); - this.compactionRoot = br.readHash(); - this.compactionHeight = br.readU32(); - - return this; - } -} - /* * Helpers */ diff --git a/lib/blockchain/common.js b/lib/blockchain/common.js index 80b8f162b..6a9ab8339 100644 --- a/lib/blockchain/common.js +++ b/lib/blockchain/common.js @@ -23,7 +23,7 @@ exports.lockFlags = {}; * @default */ -exports.lockFlags.MANDATORY_LOCKTIME_FLAGS = 0;; +exports.lockFlags.MANDATORY_LOCKTIME_FLAGS = 0; /** * Standard locktime flags (used for mempool validation). @@ -69,3 +69,54 @@ exports.flags = { exports.flags.DEFAULT_FLAGS = 0 | exports.flags.VERIFY_POW | exports.flags.VERIFY_BODY; + +/** + * Interactive scan actions. + * @enum {Number} + * @default + */ + +exports.scanActions = { + NONE: 0, + ABORT: 1, + NEXT: 2, + REPEAT_SET: 3, + REPEAT_ADD: 4, + REPEAT: 5 +}; + +/** + * @typedef {Object} ActionAbort + * @property {exports.scanActions} type - ABORT + */ + +/** + * @typedef {Object} ActionNext + * @property {exports.scanActions} type - NEXT + */ + +/** + * @typedef {Object} ActionRepeat + * @property {exports.ScanAction} type - REPEAT + */ + +/** + * @typedef {Object} ActionRepeatAdd + * @property {exports.scanActions} type - REPEAT_ADD + * @property {Buffer[]} chunks + */ + +/** + * @typedef {Object} ActionRepeatSet + * @property {exports.scanActions} type - REPEAT_SET + * @property {BloomFilter} filter + */ + +/** + * @typedef {ActionAbort + * | ActionNext + * | ActionRepeat + * | ActionRepeatAdd + * | ActionRepeatSet + * } ScanAction + */ diff --git a/lib/blockchain/migrations.js b/lib/blockchain/migrations.js index 6a9039ccb..3edfef0f8 100644 --- a/lib/blockchain/migrations.js +++ b/lib/blockchain/migrations.js @@ -8,14 +8,15 @@ const assert = require('bsert'); const Logger = require('blgr'); -const {encoding} = require('bufio'); +const bio = require('bufio'); +const {encoding} = bio; +const bdb = require('bdb'); const Network = require('../protocol/network'); const rules = require('../covenants/rules'); const Block = require('../primitives/block'); const CoinView = require('../coins/coinview'); const UndoCoins = require('../coins/undocoins'); const layout = require('./layout'); -const MigrationState = require('../migrations/state'); const AbstractMigration = require('../migrations/migration'); const { Migrator, @@ -23,6 +24,8 @@ const { types } = require('../migrations/migrator'); +/** @typedef {import('../types').Hash} Hash */ + /** * Switch to new migrations layout. */ @@ -40,6 +43,7 @@ class MigrateMigrations extends AbstractMigration { this.logger = options.logger.context('chain-migration-migrate'); this.db = options.db; this.ldb = options.ldb; + this.layout = MigrateMigrations.layout(); } async check() { @@ -54,8 +58,11 @@ class MigrateMigrations extends AbstractMigration { async migrate(b) { this.logger.info('Migrating migrations..'); - const state = new MigrationState(); - state.nextMigration = 1; + + const oldLayout = this.layout.oldLayout.wdb; + const newLayout = this.layout.newLayout.wdb; + let nextMigration = 1; + const skipped = []; const oldMigrations = await this.ldb.keys({ gte: oldLayout.M.min(), @@ -67,15 +74,36 @@ class MigrateMigrations extends AbstractMigration { b.del(oldLayout.M.encode(id)); if (id === 1) { - if (this.options.prune) - state.skipped.push(1); + if (this.options.prune) { + skipped.push(1); + } - state.nextMigration = 2; + nextMigration = 2; } } this.db.writeVersion(b, 2); - b.put(layout.M.encode(), state.encode()); + b.put(newLayout.M.encode(), + this.encodeMigrationState(nextMigration, skipped)); + } + + encodeMigrationState(nextMigration, skipped) { + let size = 4; + size += encoding.sizeVarint(nextMigration); + size += encoding.sizeVarint(skipped.length); + + for (const id of skipped) + size += encoding.sizeVarint(id); + + const bw = bio.write(size); + bw.writeU32(0); + bw.writeVarint(nextMigration); + bw.writeVarint(skipped.length); + + for (const id of skipped) + bw.writeVarint(id); + + return bw.render(); } static info() { @@ -84,6 +112,21 @@ class MigrateMigrations extends AbstractMigration { description: 'ChainDB migration layout has changed.' }; } + + static layout() { + return { + oldLayout: { + wdb: { + M: bdb.key('M', ['uint32']) + } + }, + newLayout: { + wdb: { + M: bdb.key('M') + } + } + }; + } } /** @@ -105,6 +148,7 @@ class MigrateChainState extends AbstractMigration { this.logger = options.logger.context('chain-migration-chainstate'); this.db = options.db; this.ldb = options.ldb; + this.layout = MigrateChainState.layout(); } /** @@ -144,16 +188,18 @@ class MigrateChainState extends AbstractMigration { this.logger.info('Migrating chain state.'); this.logger.info('This may take a few minutes...'); - const state = await this.db.getState(); - const tipHeight = await this.db.getHeight(state.tip); - const pending = state.clone(); - - pending.coin = 0; - pending.value = 0; - pending.burned = 0; + const rawState = await this.ldb.get(this.layout.R.encode()); + const tipHash = rawState.slice(0, 32); + const rawTipHeight = await this.ldb.get(this.layout.h.encode(tipHash)); + const tipHeight = rawTipHeight.readUInt32LE(0); + const pending = { + coin: 0, + value: 0, + burned: 0 + }; for (let height = 0; height <= tipHeight; height++) { - const hash = await this.db.getHash(height); + const hash = await this.ldb.get(this.layout.H.encode(height)); const block = await this.getBlock(hash); assert(block); @@ -172,7 +218,8 @@ class MigrateChainState extends AbstractMigration { continue; } - pending.spend(output); + pending.coin -= 1; + pending.value -= output.value; } } @@ -182,8 +229,10 @@ class MigrateChainState extends AbstractMigration { if (output.isUnspendable()) continue; - if (output.covenant.isRegister()) - pending.burn(output); + if (output.covenant.isRegister()) { + pending.coin += 1; + pending.burned += output.value; + } if (output.covenant.type >= rules.types.REGISTER && output.covenant.type <= rules.types.REVOKE) { @@ -195,12 +244,18 @@ class MigrateChainState extends AbstractMigration { continue; } - pending.add(output); + pending.coin += 1; + pending.value += output.value; } } } - b.put(layout.R.encode(), pending.encode()); + // prefix hash + tx (8) + // we write coin (8) + value (8) + burned (8) + encoding.writeU64(rawState, pending.coin, 40); + encoding.writeU64(rawState, pending.value, 40 + 8); + encoding.writeU64(rawState, pending.burned, 40 + 16); + b.put(this.layout.R.encode(), rawState); } /** @@ -211,7 +266,7 @@ class MigrateChainState extends AbstractMigration { async getBlock(hash) { assert(Buffer.isBuffer(hash)); - const raw = await this.ldb.get(layout.b.encode(hash)); + const raw = await this.ldb.get(this.layout.b.encode(hash)); if (!raw) return null; @@ -228,7 +283,7 @@ class MigrateChainState extends AbstractMigration { async getBlockView(block) { const hash = block.hash(); const view = new CoinView(); - const raw = await this.ldb.get(layout.u.encode(hash)); + const raw = await this.ldb.get(this.layout.u.encode(hash)); if (!raw) return view; @@ -260,6 +315,21 @@ class MigrateChainState extends AbstractMigration { description: 'Chain state is corrupted.' }; } + + static layout() { + return { + // R -> tip hash + R: bdb.key('R'), + // h[hash] -> height + h: bdb.key('h', ['hash256']), + // H[height] -> hash + H: bdb.key('H', ['uint32']), + // b[hash] -> block + b: bdb.key('b', ['hash256']), + // u[hash] -> undo coins + u: bdb.key('u', ['hash256']) + }; + } } /** @@ -280,6 +350,7 @@ class MigrateBlockStore extends AbstractMigration { this.db = options.db; this.ldb = options.ldb; this.blocks = options.db.blocks; + this.layout = MigrateBlockStore.layout(); } /** @@ -323,8 +394,8 @@ class MigrateBlockStore extends AbstractMigration { let parent = this.ldb.batch(); const iter = this.ldb.iterator({ - gte: layout.b.min(), - lte: layout.b.max(), + gte: this.layout.b.min(), + lte: this.layout.b.max(), keys: true, values: true }); @@ -357,8 +428,8 @@ class MigrateBlockStore extends AbstractMigration { let parent = this.ldb.batch(); const iter = this.ldb.iterator({ - gte: layout.u.min(), - lte: layout.u.max(), + gte: this.layout.u.min(), + lte: this.layout.u.max(), keys: true, values: true }); @@ -388,6 +459,15 @@ class MigrateBlockStore extends AbstractMigration { + ' blockstore from the chainDB.' }; } + + static layout() { + return { + // b[hash] -> block + b: bdb.key('b', ['hash256']), + // u[hash] -> undo coins + u: bdb.key('u', ['hash256']) + }; + } }; /** @@ -409,6 +489,7 @@ class MigrateTreeState extends AbstractMigration { this.db = options.db; this.ldb = options.ldb; this.network = options.network; + this.layout = MigrateTreeState.layout(); } async check() { @@ -422,10 +503,12 @@ class MigrateTreeState extends AbstractMigration { } const {treeInterval} = this.network.names; - const state = await this.db.getState(); - const tipHeight = await this.db.getHeight(state.tip); + const rawState = await this.ldb.get(this.layout.R.encode()); + const tipHash = rawState.slice(0, 32); + const rawTipHeight = await this.ldb.get(this.layout.h.encode(tipHash)); + const tipHeight = rawTipHeight.readUInt32LE(0); const lastCommitHeight = tipHeight - (tipHeight % treeInterval); - const hash = await this.ldb.get(layout.s.encode()); + const hash = await this.ldb.get(this.layout.s.encode()); assert(hash && hash.length === 32); // new tree root @@ -435,7 +518,7 @@ class MigrateTreeState extends AbstractMigration { encoding.writeU32(buff, lastCommitHeight, 32); this.db.writeVersion(b, 3); - b.put(layout.s.encode(), buff); + b.put(this.layout.s.encode(), buff); } static info() { @@ -444,6 +527,17 @@ class MigrateTreeState extends AbstractMigration { description: 'Add compaction information to the tree state.' }; } + + static layout() { + return { + // R -> tip hash + R: bdb.key('R'), + // h[hash] -> height + h: bdb.key('h', ['hash256']), + // s -> tree state + s: bdb.key('s') + }; + } } /** diff --git a/lib/blockchain/records.js b/lib/blockchain/records.js new file mode 100644 index 000000000..6a3462b49 --- /dev/null +++ b/lib/blockchain/records.js @@ -0,0 +1,369 @@ +/*! + * records.js - chaindb records + * Copyright (c) 2024 The Handshake Developers (MIT License). + * https://github.com/handshake-org/hsd + */ + +'use strict'; + +const assert = require('bsert'); +const bio = require('bufio'); +const {BufferMap} = require('buffer-map'); +const consensus = require('../protocol/consensus'); +const Network = require('../protocol/network'); + +/** + * ChainFlags + */ + +class ChainFlags extends bio.Struct { + /** + * Create chain flags. + * @alias module:blockchain.ChainFlags + * @constructor + */ + + constructor(options) { + super(); + + this.network = Network.primary; + this.spv = false; + this.prune = false; + this.indexTX = false; + this.indexAddress = false; + + if (options) + this.fromOptions(options); + } + + fromOptions(options) { + this.network = Network.get(options.network); + + if (options.spv != null) { + assert(typeof options.spv === 'boolean'); + this.spv = options.spv; + } + + if (options.prune != null) { + assert(typeof options.prune === 'boolean'); + this.prune = options.prune; + } + + if (options.indexTX != null) { + assert(typeof options.indexTX === 'boolean'); + this.indexTX = options.indexTX; + } + + if (options.indexAddress != null) { + assert(typeof options.indexAddress === 'boolean'); + this.indexAddress = options.indexAddress; + } + + return this; + } + + getSize() { + return 12; + } + + write(bw) { + let flags = 0; + + if (this.spv) + flags |= 1 << 0; + + if (this.prune) + flags |= 1 << 1; + + if (this.indexTX) + flags |= 1 << 2; + + if (this.indexAddress) + flags |= 1 << 3; + + bw.writeU32(this.network.magic); + bw.writeU32(flags); + bw.writeU32(0); + + return bw; + } + + read(br) { + this.network = Network.fromMagic(br.readU32()); + + const flags = br.readU32(); + + this.spv = (flags & 1) !== 0; + this.prune = (flags & 2) !== 0; + this.indexTX = (flags & 4) !== 0; + this.indexAddress = (flags & 8) !== 0; + + return this; + } +} + +/** + * Chain State + */ + +class ChainState extends bio.Struct { + /** + * Create chain state. + * @alias module:blockchain.ChainState + * @constructor + */ + + constructor() { + super(); + this.tip = consensus.ZERO_HASH; + this.tx = 0; + this.coin = 0; + this.value = 0; + this.burned = 0; + this.committed = false; + } + + inject(state) { + this.tip = state.tip; + this.tx = state.tx; + this.coin = state.coin; + this.value = state.value; + this.burned = state.burned; + return this; + } + + connect(block) { + this.tx += block.txs.length; + } + + disconnect(block) { + this.tx -= block.txs.length; + } + + add(coin) { + this.coin += 1; + this.value += coin.value; + } + + spend(coin) { + this.coin -= 1; + this.value -= coin.value; + } + + burn(coin) { + this.coin += 1; + this.burned += coin.value; + } + + unburn(coin) { + this.coin -= 1; + this.burned -= coin.value; + } + + commit(hash) { + assert(Buffer.isBuffer(hash)); + this.tip = hash; + this.committed = true; + return this.encode(); + } + + getSize() { + return 64; + } + + write(bw) { + bw.writeHash(this.tip); + bw.writeU64(this.tx); + bw.writeU64(this.coin); + bw.writeU64(this.value); + bw.writeU64(this.burned); + return bw; + } + + read(br) { + this.tip = br.readHash(); + this.tx = br.readU64(); + this.coin = br.readU64(); + this.value = br.readU64(); + this.burned = br.readU64(); + return this; + } +} + +/** + * State Cache + */ + +class StateCache { + /** + * Create state cache. + * @alias module:blockchain.StateCache + * @constructor + */ + + constructor(network) { + this.network = network; + this.bits = []; + this.updates = []; + this.init(); + } + + init() { + for (let i = 0; i < 32; i++) + this.bits.push(null); + + for (const {bit} of this.network.deploys) { + assert(!this.bits[bit]); + this.bits[bit] = new BufferMap(); + } + } + + set(bit, entry, state) { + const cache = this.bits[bit]; + + assert(cache); + + if (cache.get(entry.hash) !== state) { + cache.set(entry.hash, state); + this.updates.push(new CacheUpdate(bit, entry.hash, state)); + } + } + + get(bit, entry) { + const cache = this.bits[bit]; + + assert(cache); + + const state = cache.get(entry.hash); + + if (state == null) + return -1; + + return state; + } + + commit() { + this.updates.length = 0; + } + + drop() { + for (const {bit, hash} of this.updates) { + const cache = this.bits[bit]; + assert(cache); + cache.delete(hash); + } + + this.updates.length = 0; + } + + insert(bit, hash, state) { + const cache = this.bits[bit]; + assert(cache); + cache.set(hash, state); + } +} + +/** + * Cache Update + */ + +class CacheUpdate { + /** + * Create cache update. + * @constructor + * @ignore + */ + + constructor(bit, hash, state) { + this.bit = bit; + this.hash = hash; + this.state = state; + } + + encode() { + const data = Buffer.allocUnsafe(1); + data[0] = this.state; + return data; + } +} + +/** + * Tree related state. + */ + +class TreeState extends bio.Struct { + /** + * Create tree state. + * @constructor + * @ignore + */ + + constructor() { + super(); + this.treeRoot = consensus.ZERO_HASH; + this.commitHeight = 0; + this.compactionRoot = consensus.ZERO_HASH; + this.compactionHeight = 0; + + this.committed = false; + } + + inject(state) { + this.treeRoot = state.treeRoot; + this.commitHeight = state.treeHeight; + this.compactionHeight = state.compactionHeight; + this.compactionRoot = state.compactionRoot; + + return this; + } + + compact(hash, height) { + assert(Buffer.isBuffer(hash)); + assert((height >>> 0) === height); + + this.compactionRoot = hash; + this.compactionHeight = height; + }; + + commit(hash, height) { + assert(Buffer.isBuffer(hash)); + assert((height >>> 0) === height); + + this.treeRoot = hash; + this.commitHeight = height; + this.committed = true; + return this.encode(); + } + + getSize() { + return 72; + } + + write(bw) { + bw.writeHash(this.treeRoot); + bw.writeU32(this.commitHeight); + bw.writeHash(this.compactionRoot); + bw.writeU32(this.compactionHeight); + + return bw; + } + + read(br) { + this.treeRoot = br.readHash(); + this.commitHeight = br.readU32(); + this.compactionRoot = br.readHash(); + this.compactionHeight = br.readU32(); + + return this; + } +} + +/* + * Expose + */ + +exports.ChainFlags = ChainFlags; +exports.ChainState = ChainState; +exports.StateCache = StateCache; +exports.TreeState = TreeState; +exports.CacheUpdate = CacheUpdate; diff --git a/lib/blockstore/abstract.js b/lib/blockstore/abstract.js index d024f3e23..efde2ddf1 100644 --- a/lib/blockstore/abstract.js +++ b/lib/blockstore/abstract.js @@ -20,6 +20,7 @@ class AbstractBlockStore { /** * Create an abstract blockstore. * @constructor + * @param {Object} [options] */ constructor(options) { @@ -192,7 +193,7 @@ class AbstractBlockStore { /** * Create batch. - * @returns {Batch} + * @returns {AbstractBatch} */ batch() { @@ -206,7 +207,6 @@ class AbstractBlockStore { * @abstract */ -// eslint-disable-next-line no-unused-vars class AbstractBatch { /** * Create AbstractBatch. @@ -218,9 +218,9 @@ class AbstractBatch { /** * Write merkle block data to the batch. - * @property {Buffer} hash - * @property {Buffer} data - * @returns {Batch} + * @param {Buffer} hash + * @param {Buffer} data + * @returns {this} */ writeMerkle(hash, data) { @@ -231,7 +231,7 @@ class AbstractBatch { * Write undo coin data to the batch. * @param {Buffer} hash * @param {Buffer} data - * @returns {Batch} + * @returns {this} */ writeUndo(hash, data) { @@ -242,7 +242,7 @@ class AbstractBatch { * Write block data to the batch. * @param {Buffer} hash * @param {Buffer} data - * @returns {Batch} + * @returns {this} */ writeBlock(hash, data) { @@ -252,7 +252,7 @@ class AbstractBatch { /** * Remove merkle block data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneMerkle(hash) { @@ -262,7 +262,7 @@ class AbstractBatch { /** * Remove undo data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneUndo(hash) { @@ -272,7 +272,7 @@ class AbstractBatch { /** * Prune block data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneBlock(hash) { @@ -281,7 +281,7 @@ class AbstractBatch { /** * Clear the batch. - * @returns {Batch} + * @returns {this} */ clear() { @@ -293,7 +293,7 @@ class AbstractBatch { * @returns {Promise} */ - write() { + commit() { throw new Error('Abstract method.'); } } @@ -302,4 +302,5 @@ class AbstractBatch { * Expose */ +AbstractBlockStore.AbstractBatch = AbstractBatch; module.exports = AbstractBlockStore; diff --git a/lib/blockstore/file.js b/lib/blockstore/file.js index 188cb3279..b323c0ad7 100644 --- a/lib/blockstore/file.js +++ b/lib/blockstore/file.js @@ -15,6 +15,7 @@ const Network = require('../protocol/network'); const consensus = require('../protocol/consensus'); const Headers = require('../primitives/headers'); const AbstractBlockStore = require('./abstract'); +const {AbstractBatch} = AbstractBlockStore; const {BlockRecord, FileRecord} = require('./records'); const layout = require('./layout'); const {types, prefixes} = require('./common'); @@ -33,6 +34,7 @@ class FileBlockStore extends AbstractBlockStore { /** * Create a blockstore that stores blocks in files. * @constructor + * @param {Object} [options] */ constructor(options) { @@ -234,7 +236,7 @@ class FileBlockStore extends AbstractBlockStore { * @private * @param {Number} type - The type of block data * @param {Number} fileno - The number of the file. - * @returns {Promise} + * @returns {String} */ filepath(type, fileno) { @@ -266,7 +268,7 @@ class FileBlockStore extends AbstractBlockStore { * @private * @param {Number} type - The type of block data * @param {Number} length - The number of bytes - * @returns {Promise} + * @returns {Promise} */ async allocate(type, length) { @@ -321,7 +323,7 @@ class FileBlockStore extends AbstractBlockStore { * This method stores merkle block data in files. * @param {Buffer} hash - The block hash * @param {Buffer} data - The block data - * @returns {Promise} + * @returns {Promise} */ async writeMerkle(hash, data) { @@ -332,7 +334,7 @@ class FileBlockStore extends AbstractBlockStore { * This method stores block undo coin data in files. * @param {Buffer} hash - The block hash * @param {Buffer} data - The block data - * @returns {Promise} + * @returns {Promise} */ async writeUndo(hash, data) { @@ -343,7 +345,7 @@ class FileBlockStore extends AbstractBlockStore { * This method stores block data in files. * @param {Buffer} hash - The block hash * @param {Buffer} data - The block data - * @returns {Promise} + * @returns {Promise} */ async writeBlock(hash, data) { @@ -358,7 +360,7 @@ class FileBlockStore extends AbstractBlockStore { * @param {Number} type - The type of block data * @param {Buffer} hash - The block hash * @param {Buffer} data - The block data - * @returns {Promise} + * @returns {Promise} - Whether the data was written. */ async _write(type, hash, data) { @@ -451,7 +453,7 @@ class FileBlockStore extends AbstractBlockStore { /** * This method will retrieve merkle block data. * @param {Buffer} hash - The block hash - * @returns {Promise} + * @returns {Promise} */ async readMerkle(hash) { @@ -461,7 +463,7 @@ class FileBlockStore extends AbstractBlockStore { /** * This method will retrieve block undo coin data. * @param {Buffer} hash - The block hash - * @returns {Promise} + * @returns {Promise} */ async readUndo(hash) { @@ -488,9 +490,9 @@ class FileBlockStore extends AbstractBlockStore { * @private * @param {Number} type - The type of block data * @param {Buffer} hash - The block hash - * @param {Number} offset - The offset within the block - * @param {Number} length - The number of bytes of the data - * @returns {Promise} + * @param {Number} [offset] - The offset within the block + * @param {Number} [length] - The number of bytes of the data + * @returns {Promise} */ async _read(type, hash, offset, length) { @@ -536,7 +538,7 @@ class FileBlockStore extends AbstractBlockStore { /** * This will free resources for storing merkle block data. * @param {Buffer} hash - The block hash - * @returns {Promise} + * @returns {Promise} */ async pruneMerkle(hash) { @@ -546,7 +548,7 @@ class FileBlockStore extends AbstractBlockStore { /** * This will free resources for storing the block undo coin data. * @param {Buffer} hash - The block hash - * @returns {Promise} + * @returns {Promise} */ async pruneUndo(hash) { @@ -556,7 +558,7 @@ class FileBlockStore extends AbstractBlockStore { /** * This will free resources for storing the block data. * @param {Buffer} hash - The block hash - * @returns {Promise} + * @returns {Promise} */ async pruneBlock(hash) { @@ -569,8 +571,9 @@ class FileBlockStore extends AbstractBlockStore { * block is removed and will not be able to be read. The underlying * file is unlinked when all blocks in a file have been pruned. * @private + * @param {Number} type - The type of block data * @param {Buffer} hash - The block hash - * @returns {Promise} + * @returns {Promise} */ async _prune(type, hash) { @@ -656,13 +659,16 @@ class FileBlockStore extends AbstractBlockStore { * @alias module:blockstore.FileBatch */ -class FileBatch { +class FileBatch extends AbstractBatch { /** * Create AbstractBatch. * @constructor + * @param {FileBlockStore} blocks */ constructor(blocks) { + super(); + this.blocks = blocks; this.writes = []; this.prunes = []; @@ -676,76 +682,83 @@ class FileBatch { /** * Write merkle block data to the batch. - * @property {Buffer} hash - * @property {Buffer} data - * @returns {Batch} + * @param {Buffer} hash + * @param {Buffer} data + * @returns {this} */ writeMerkle(hash, data) { this.writes.push(new WriteOp(types.MERKLE, hash, data)); + return this; } /** * Write undo coin data to the batch. * @param {Buffer} hash * @param {Buffer} data - * @returns {Batch} + * @returns {this} */ writeUndo(hash, data) { this.writes.push(new WriteOp(types.UNDO, hash, data)); + return this; } /** * Write block data to the batch. * @param {Buffer} hash * @param {Buffer} data - * @returns {Batch} + * @returns {this} */ writeBlock(hash, data) { this.writes.push(new WriteOp(types.BLOCK, hash, data)); + return this; } /** * Remove merkle block data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneMerkle(hash) { this.prunes.push(new PruneOp(types.MERKLE, hash)); + return this; } /** * Remove undo data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneUndo(hash) { this.prunes.push(new PruneOp(types.UNDO, hash)); + return this; } /** * Prune block data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneBlock(hash) { this.prunes.push(new PruneOp(types.BLOCK, hash)); + return this; } /** * Clear the batch. - * @returns {Batch} + * @returns {this} */ clear() { assert(!this.written, 'Already written all.'); this.writes.length = 0; this.prunes.length = 0; + return this; } /** diff --git a/lib/blockstore/level.js b/lib/blockstore/level.js index 69eb22e7b..693eeabf9 100644 --- a/lib/blockstore/level.js +++ b/lib/blockstore/level.js @@ -10,6 +10,7 @@ const assert = require('bsert'); const bdb = require('bdb'); const fs = require('bfile'); const AbstractBlockStore = require('./abstract'); +const {AbstractBatch} = require('./abstract'); const layout = require('./layout'); const {types} = require('./common'); @@ -24,6 +25,7 @@ class LevelBlockStore extends AbstractBlockStore { /** * Create a blockstore that stores blocks in LevelDB. * @constructor + * @param {Object} [options] */ constructor(options) { @@ -246,13 +248,15 @@ class LevelBlockStore extends AbstractBlockStore { * @alias module:blockstore.LevelBatch */ -class LevelBatch { +class LevelBatch extends AbstractBatch { /** * Create LevelBatch - * @param {DB} db + * @param {bdb.DB} db */ constructor(db) { + super(); + this.writesBatch = db.batch(); this.prunesBatch = db.batch(); this.committedWrites = false; @@ -265,9 +269,9 @@ class LevelBatch { /** * Write merkle block data to the batch. - * @property {Buffer} hash - * @property {Buffer} data - * @returns {Batch} + * @param {Buffer} hash + * @param {Buffer} data + * @returns {this} */ writeMerkle(hash, data) { @@ -279,7 +283,7 @@ class LevelBatch { * Write undo coin data to the batch. * @param {Buffer} hash * @param {Buffer} data - * @returns {Batch} + * @returns {this} */ writeUndo(hash, data) { @@ -291,7 +295,7 @@ class LevelBatch { * Write block data to the batch. * @param {Buffer} hash * @param {Buffer} data - * @returns {Batch} + * @returns {this} */ writeBlock(hash, data) { @@ -302,7 +306,7 @@ class LevelBatch { /** * Remove merkle block data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneMerkle(hash) { @@ -313,7 +317,7 @@ class LevelBatch { /** * Remove undo data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneUndo(hash) { @@ -324,7 +328,7 @@ class LevelBatch { /** * Prune block data from the batch. * @param {Buffer} hash - * @returns {Batch} + * @returns {this} */ pruneBlock(hash) { @@ -334,7 +338,7 @@ class LevelBatch { /** * Clear the batch. - * @returns {Batch} + * @returns {this} */ clear() { diff --git a/lib/blockstore/records.js b/lib/blockstore/records.js index 9c275c938..2236eef7f 100644 --- a/lib/blockstore/records.js +++ b/lib/blockstore/records.js @@ -9,6 +9,8 @@ const assert = require('bsert'); const bio = require('bufio'); +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /** * @module blockstore/records */ @@ -46,8 +48,7 @@ class BlockRecord extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} data + * @param {bio.BufferReader} br */ read(br) { @@ -61,8 +62,8 @@ class BlockRecord extends bio.Struct { /** * Serialize the block record. * Write block record to a buffer writer - * @param {BufferWriter} bw - * @returns {BufferWriter} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -107,8 +108,7 @@ class FileRecord extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -121,14 +121,15 @@ class FileRecord extends bio.Struct { /** * Write serialized file record to the buffer writer. - * @param {BufferWriter} bw - * @returns {BufferWriter} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { bw.writeU32(this.blocks); bw.writeU32(this.used); bw.writeU32(this.length); + return bw; } } diff --git a/lib/client/index.js b/lib/client/index.js new file mode 100644 index 000000000..d1b09377c --- /dev/null +++ b/lib/client/index.js @@ -0,0 +1,16 @@ +/*! + * client/index.js - http clients for hs + * Copyright (c) 2017, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +// NOTE: This is part of generated `hs-client`. + +/** + * @module client + */ + +exports.NodeClient = require('./node'); +exports.WalletClient = require('./wallet'); diff --git a/lib/client/node.js b/lib/client/node.js new file mode 100644 index 000000000..aae765d5c --- /dev/null +++ b/lib/client/node.js @@ -0,0 +1,391 @@ +/*! + * client.js - http client for wallets + * Copyright (c) 2017, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +// NOTE: This is part of generated `hs-client`. +// Don't introduce any unnecessary dependencies to this. + +const assert = require('bsert'); +const {Client} = require('bcurl'); + +/** + * Node Client + * @alias module:client.NodeClient + * @extends {bcurl.Client} + */ + +class NodeClient extends Client { + /** + * Creat a node client. + * @param {Object?} options + */ + + constructor(options) { + super(options); + } + + /** + * Auth with server. + * @returns {Promise} + */ + + async auth() { + await this.call('auth', this.password); + await this.watchChain(); + await this.watchMempool(); + } + + /** + * Make an RPC call. + * @returns {Promise} + */ + + execute(name, params) { + return super.execute('/', name, params); + } + + /** + * Get a mempool snapshot. + * @returns {Promise} + */ + + getMempool() { + return this.get('/mempool'); + } + + /** + * Get a mempool rejection filter. + * @param {Object} options + * @returns {Promise} + */ + + getMempoolRejectionFilter(options) { + return this.get('/mempool/invalid', options); + } + + /** + * Check against mempool rejection filter. + * @param {Hash} hash - transaction hash + * @returns {Promise} + */ + + checkMempoolRejectionFilter(hash) { + return this.get(`/mempool/invalid/${hash}`); + } + + /** + * Get some info about the server (network and version). + * @returns {Promise} + */ + + getInfo() { + return this.get('/'); + } + + /** + * Get coins that pertain to an address from the mempool or chain database. + * Takes into account spent coins in the mempool. + * @param {String} address + * @returns {Promise} + */ + + getCoinsByAddress(address) { + assert(typeof address === 'string'); + return this.get(`/coin/address/${address}`); + } + + /** + * Get coins that pertain to addresses from the mempool or chain database. + * Takes into account spent coins in the mempool. + * @param {String[]} addresses + * @returns {Promise} + */ + + getCoinsByAddresses(addresses) { + assert(Array.isArray(addresses)); + return this.post('/coin/address', { addresses }); + } + + /** + * Retrieve a coin from the mempool or chain database. + * Takes into account spent coins in the mempool. + * @param {Hash} hash + * @param {Number} index + * @returns {Promise} + */ + + getCoin(hash, index) { + assert(typeof hash === 'string'); + assert((index >>> 0) === index); + return this.get(`/coin/${hash}/${index}`); + } + + /** + * Retrieve transactions pertaining to an + * address from the mempool or chain database. + * @param {String} address + * @returns {Promise} + */ + + getTXByAddress(address) { + assert(typeof address === 'string'); + return this.get(`/tx/address/${address}`); + } + + /** + * Retrieve transactions pertaining to + * addresses from the mempool or chain database. + * @param {String[]} addresses + * @returns {Promise} + */ + + getTXByAddresses(addresses) { + assert(Array.isArray(addresses)); + return this.post('/tx/address', { addresses }); + } + + /** + * Retrieve a transaction from the mempool or chain database. + * @param {Hash} hash + * @returns {Promise} + */ + + getTX(hash) { + assert(typeof hash === 'string'); + return this.get(`/tx/${hash}`); + } + + /** + * Retrieve a block from the chain database. + * @param {Hash|Number} block + * @returns {Promise} + */ + + getBlock(block) { + assert(typeof block === 'string' || typeof block === 'number'); + return this.get(`/block/${block}`); + } + + /** + * Retrieve a block header. + * @param {Hash|Number} block + * @returns {Promise} + */ + + getBlockHeader(block) { + assert(typeof block === 'string' || typeof block === 'number'); + return this.get(`/header/${block}`); + } + + /** + * Add a transaction to the mempool and broadcast it. + * @param {TX} tx + * @returns {Promise} + */ + + broadcast(tx) { + assert(typeof tx === 'string'); + return this.post('/broadcast', { tx }); + } + + /** + * Add a claim to the mempool and broadcast it. + * @param {Claim} claim + * @returns {Promise} + */ + + broadcastClaim(claim) { + assert(typeof claim === 'string'); + return this.post('/claim', { claim }); + } + + /** + * Estimate smart fee. Same as estimateFee, but + * an HTTP call instead of websocket call. + * @param {Number} blocks + * @returns {Promise} + */ + + getSmartFee(blocks) { + return this.get('/fee', { blocks }); + } + + /** + * Reset the chain. + * @param {Number} height + * @returns {Promise} + */ + + reset(height) { + return this.post('/reset', { height }); + } + + /** + * Watch the blockchain. + * @private + * @returns {Promise} + */ + + watchChain() { + return this.call('watch chain'); + } + + /** + * Watch the blockchain. + * @private + * @returns {Promise} + */ + + watchMempool() { + return this.call('watch mempool'); + } + + /** + * Get chain tip. + * @returns {Promise} + */ + + getTip() { + return this.call('get tip'); + } + + /** + * Get chain entry. + * @param {Hash} hash + * @returns {Promise} + */ + + getEntry(block) { + return this.call('get entry', block); + } + + /** + * Get hashes. + * @param {Number} [start=-1] + * @param {Number} [end=-1] + * @returns {Promise} + */ + + getHashes(start, end) { + return this.call('get hashes', start, end); + } + + /** + * Send a transaction. Do not wait for promise. + * @param {TX} tx + * @returns {Promise} + */ + + send(tx) { + assert(Buffer.isBuffer(tx)); + return this.call('send', tx); + } + + /** + * Send a claim. Do not wait for promise. + * @param {Claim} claim + * @returns {Promise} + */ + + sendClaim(claim) { + assert(Buffer.isBuffer(claim)); + return this.call('send claim', claim); + } + + /** + * Get name state. + * @param {Buffer} nameHash + * @returns {Promise} + */ + + getNameStatus(nameHash) { + assert(Buffer.isBuffer(nameHash)); + return this.call('get name', nameHash); + } + + /** + * Set bloom filter. + * @param {Bloom} filter + * @returns {Promise} + */ + + setFilter(filter) { + assert(Buffer.isBuffer(filter)); + return this.call('set filter', filter); + } + + /** + * Add data to filter. + * @param {Buffer} data + * @returns {Promise} + */ + + addFilter(chunks) { + if (!Array.isArray(chunks)) + chunks = [chunks]; + + return this.call('add filter', chunks); + } + + /** + * Reset filter. + * @returns {Promise} + */ + + resetFilter() { + return this.call('reset filter'); + } + + /** + * Esimate smart fee. + * @param {Number?} blocks + * @returns {Promise} + */ + + estimateFee(blocks) { + assert(blocks == null || typeof blocks === 'number'); + return this.call('estimate fee', blocks); + } + + /** + * Rescan for any missed transactions. + * @param {Number|Hash} start - Start block. + * @returns {Promise} + */ + + rescan(start) { + if (start == null) + start = 0; + + assert(typeof start === 'number' || Buffer.isBuffer(start)); + + return this.call('rescan', start); + } + + /** + * Rescan for any missed transactions. (Interactive) + * @param {Number|Hash} start - Start block. + * @param {BloomFilter} [filter] + * @param {Boolean} [fullLock=false] + * @returns {Promise} + */ + + rescanInteractive(start, filter = null, fullLock = false) { + if (start == null) + start = 0; + + assert(typeof start === 'number' || Buffer.isBuffer(start)); + + return this.call('rescan interactive', start, filter, fullLock); + } +} + +/* + * Expose + */ + +module.exports = NodeClient; diff --git a/lib/client/wallet.js b/lib/client/wallet.js new file mode 100644 index 000000000..cf9d16a52 --- /dev/null +++ b/lib/client/wallet.js @@ -0,0 +1,1605 @@ +/*! + * wallet.js - http wallet for bcoin + * Copyright (c) 2017, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +// NOTE: This is part of generated `hs-client`. +// Don't introduce any unnecessary dependencies to this. + +const assert = require('bsert'); +const EventEmitter = require('events'); +const {Client} = require('bcurl'); + +/** + * Wallet Client + * @alias module:client.WalletClient + * @extends {bcurl.Client} + */ + +class WalletClient extends Client { + /** + * Create a wallet client. + * @param {Object?} options + */ + + constructor(options) { + super(options); + this.wallets = new Map(); + } + + /** + * Open the client. + * @private + * @returns {Promise} + */ + + init() { + this.bind('tx', (id, details) => { + this.dispatch(id, 'tx', details); + }); + + this.bind('confirmed', (id, details) => { + this.dispatch(id, 'confirmed', details); + }); + + this.bind('unconfirmed', (id, details) => { + this.dispatch(id, 'unconfirmed', details); + }); + + this.bind('conflict', (id, details) => { + this.dispatch(id, 'conflict', details); + }); + + this.bind('updated', (id, details) => { + this.dispatch(id, 'updated', details); + }); + + this.bind('address', (id, receive) => { + this.dispatch(id, 'address', receive); + }); + + this.bind('balance', (id, balance) => { + this.dispatch(id, 'balance', balance); + }); + } + + /** + * Dispatch event. + * @private + */ + + dispatch(id, event, ...args) { + const wallet = this.wallets.get(id); + + if (wallet) + wallet.emit(event, ...args); + } + + /** + * Open the client. + * @returns {Promise} + */ + + async open() { + await super.open(); + this.init(); + } + + /** + * Close the client. + * @returns {Promise} + */ + + async close() { + await super.close(); + this.wallets = new Map(); + } + + /** + * Auth with server. + * @returns {Promise} + */ + + async auth() { + await this.call('auth', this.password); + } + + /** + * Make an RPC call. + * @returns {Promise} + */ + + execute(name, params) { + return super.execute('/', name, params); + } + + /** + * Create a wallet object. + * @param {String} id + * @param {String} [token] + * @returns {Wallet} + */ + + wallet(id, token) { + return new Wallet(this, id, token); + } + + /** + * Join a wallet. + */ + + all(token) { + return this.call('join', '*', token); + } + + /** + * Leave a wallet. + */ + + none() { + return this.call('leave', '*'); + } + + /** + * Join a wallet. + */ + + join(id, token) { + return this.call('join', id, token); + } + + /** + * Leave a wallet. + */ + + leave(id) { + return this.call('leave', id); + } + + /** + * Rescan the chain. + * @param {Number} height + * @returns {Promise} + */ + + rescan(height) { + return this.post('/rescan', { height }); + } + + /** + * Resend pending transactions. + * @returns {Promise} + */ + + resend() { + return this.post('/resend'); + } + + /** + * Backup the walletdb. + * @param {String} path + * @returns {Promise} + */ + + backup(path) { + return this.post('/backup', { path }); + } + + /** + * Get list of all wallet IDs. + * @returns {Promise} + */ + + getWallets() { + return this.get('/wallet'); + } + + /** + * Create a wallet. + * @param {Object} options + * @returns {Promise} + */ + + createWallet(id, options) { + if (id == null) + throw new Error('Wallet id is required.'); + + return this.put(`/wallet/${id}`, options); + } + + /** + * Get wallet transaction history. + * @param {String} account + * @returns {Promise} + */ + + getHistory(id, account) { + return this.get(`/wallet/${id}/tx/history`, { account }); + } + + /** + * Get wallet coins. + * @param {String} account + * @returns {Promise} + */ + + getCoins(id, account) { + return this.get(`/wallet/${id}/coin`, { account }); + } + + /** + * Get all unconfirmed transactions. + * @param {String} account + * @returns {Promise} + */ + + getPending(id, account) { + return this.get(`/wallet/${id}/tx/unconfirmed`, { account }); + } + + /** + * Calculate wallet balance. + * @param {String} account + * @returns {Promise} + */ + + getBalance(id, account) { + return this.get(`/wallet/${id}/balance`, { account }); + } + + /** + * Get last N wallet transactions. + * @param {String} account + * @param {Number} limit - Max number of transactions. + * @returns {Promise} + */ + + getLast(id, account, limit) { + return this.get(`/wallet/${id}/tx/last`, { account, limit }); + } + + /** + * Get wallet transactions by timestamp range. + * @param {String} account + * @param {Object} options + * @param {Number} options.start - Start time. + * @param {Number} options.end - End time. + * @param {Number?} options.limit - Max number of records. + * @param {Boolean?} options.reverse - Reverse order. + * @returns {Promise} + */ + + getRange(id, account, options) { + return this.get(`/wallet/${id}/tx/range`, { + account: account, + start: options.start, + end: options.end, + limit: options.limit, + reverse: options.reverse + }); + } + + /** + * Get transaction (only possible if the transaction + * is available in the wallet history). + * @param {Hash} hash + * @returns {Promise} + */ + + getTX(id, hash) { + return this.get(`/wallet/${id}/tx/${hash}`); + } + + /** + * Get wallet blocks. + * @param {Number} height + * @returns {Promise} + */ + + getBlocks(id) { + return this.get(`/wallet/${id}/block`); + } + + /** + * Get wallet block. + * @param {Number} height + * @returns {Promise} + */ + + getBlock(id, height) { + return this.get(`/wallet/${id}/block/${height}`); + } + + /** + * Get unspent coin (only possible if the transaction + * is available in the wallet history). + * @param {Hash} hash + * @param {Number} index + * @returns {Promise} + */ + + getCoin(id, hash, index) { + return this.get(`/wallet/${id}/coin/${hash}/${index}`); + } + + /** + * Get name state for the given name. + * {@see hsd.NameState} + * @param {String} id + * @param {String} name + * @returns {Promise} + */ + + getName(id, name) { + return this.get(`/wallet/${id}/name/${name}`); + } + + /** + * Get name state for all names + * that the wallet is managing. + * {@see hsd.NameState} + * @param {String} id + * @param {Object} options + * @param {Boolean} [optoins.own=false] + * @returns {Promise} + */ + + getNames(id, options) { + return this.get(`/wallet/${id}/name`, options); + } + + /** + * Get bids, reveals and name state + * for the given name. + * {@see hsd.NameState} + * {@see hsd.BlindBid} + * {@see hsd.BidReveal} + * @param {String} id + * @param {String} name + * @returns {Promise} + */ + + getAuctionByName(id, name) { + return this.get(`/wallet/${id}/auction/${name}`); + } + + /** + * Get bids, reveals and name state + * for all names the wallet manages. + * {@see hsd.NameState} + * {@see hsd.BlindBid} + * {@see hsd.BidReveal} + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + getAuctions(id, options) { + return this.get(`/wallet/${id}/auction`, options); + } + + /** + * Get bids for a given name. + * {@see hsd.BlindBid} + * @param {String} id + * @param {String?} name + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getBidsByName(id, name, options) { + return this.get(`/wallet/${id}/bid/${name}`, options); + } + + /** + * Get bids for all names. + * the wallet manages. + * {@see hsd.BlindBid} + * @param {String} id + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getBids(id, options) { + return this.get(`/wallet/${id}/bid`, options); + } + + /** + * Get wallet reveal for a given name. + * {@see hsd.BidReveal} + * @param {String} id + * @param {String?} name + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getRevealsByName(id, name, options) { + return this.get(`/wallet/${id}/reveal/${name}`, options); + } + + /** + * Get wallet reveals for all names + * the wallet manages. + * {@see hsd.BidReveal} + * @param {String} id + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getReveals(id, options) { + return this.get(`/wallet/${id}/reveal`, options); + } + + /** + * Get name resource. + * {@see hsd.Resource} + * @param {String} id + * @param {String} name + * @returns {Promise} + */ + + getResource(id, name) { + return this.get(`/wallet/${id}/resource/${name}`); + } + + /* + * Deterministically regenerate a bid's nonce. + * @param {String} id + * @param {String} name + * @param {Object} options + * @param {String} options.address + * @param {Number} options.bid + * @returns {Promise} + */ + + getNonce(id, name, options) { + return this.get(`/wallet/${id}/nonce/${name}`, options); + } + + /** + * @param {Number} now - Current time. + * @param {Number} age - Age delta. + * @returns {Promise} + */ + + zap(id, account, age) { + return this.post(`/wallet/${id}/zap`, { account, age }); + } + + /** + * @param {Number} id + * @param {Hash} hash + * @returns {Promise} + */ + + abandon(id, hash) { + return this.del(`/wallet/${id}/tx/${hash}`); + } + + /** + * Create a transaction, fill. + * @param {Object} options + * @returns {Promise} + */ + + createTX(id, options) { + return this.post(`/wallet/${id}/create`, options); + } + + /** + * Create pre-signed bid and reveal txs, + * fill, and optionally sign and broadcast. + * @param {Object} options + * @param {String} options.name + * @param {Number} options.bid + * @param {Number} options.lockup + * @param {String} options.passphrase + * @param {Boolean} options.sign + * @param {Boolean} options.broadcastBid + * @returns {Promise} + */ + + createAuctionTXs(id, options) { + return this.post(`/wallet/${id}/auction`, options); + } + + /** + * Create a transaction, fill, sign, and broadcast. + * @param {Object} options + * @param {String} options.address + * @param {Amount} options.value + * @returns {Promise} + */ + + send(id, options) { + return this.post(`/wallet/${id}/send`, options); + } + + /** + * Create open transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createOpen(id, options) { + return this.post(`/wallet/${id}/open`, options); + } + + /** + * Create bid transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createBid(id, options) { + return this.post(`/wallet/${id}/bid`, options); + } + + /** + * Create reveal transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createReveal(id, options) { + return this.post(`/wallet/${id}/reveal`, options); + } + + /** + * Create redeem transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createRedeem(id, options) { + return this.post(`/wallet/${id}/redeem`, options); + } + + /** + * Create update transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createUpdate(id, options) { + return this.post(`/wallet/${id}/update`, options); + } + + /** + * Create renewal transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createRenewal(id, options) { + return this.post(`/wallet/${id}/renewal`, options); + } + + /** + * Create transfer transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createTransfer(id, options) { + return this.post(`/wallet/${id}/transfer`, options); + } + + /** + * Create cancel transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createCancel(id, options) { + return this.post(`/wallet/${id}/cancel`, options); + } + + /** + * Create finalize transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createFinalize(id, options) { + return this.post(`/wallet/${id}/finalize`, options); + } + + /** + * Create revoke transaction. + * @param {String} id + * @param {Object} options + * @returns {Promise} + */ + + createRevoke(id, options) { + return this.post(`/wallet/${id}/revoke`, options); + } + + /** + * Sign a transaction. + * @param {Object} options + * @returns {Promise} + */ + + sign(id, options) { + return this.post(`/wallet/${id}/sign`, options); + } + + /** + * Get the raw wallet JSON. + * @returns {Promise} + */ + + getInfo(id) { + return this.get(`/wallet/${id}`); + } + + /** + * Get wallet accounts. + * @returns {Promise} - Returns Array. + */ + + getAccounts(id) { + return this.get(`/wallet/${id}/account`); + } + + /** + * Get wallet master key. + * @returns {Promise} + */ + + getMaster(id) { + return this.get(`/wallet/${id}/master`); + } + + /** + * Get wallet account. + * @param {String} account + * @returns {Promise} + */ + + getAccount(id, account) { + return this.get(`/wallet/${id}/account/${account}`); + } + + /** + * Create account. + * @param {String} name + * @param {Object} options + * @returns {Promise} + */ + + createAccount(id, name, options) { + if (name == null) + throw new Error('Account name is required.'); + + return this.put(`/wallet/${id}/account/${name}`, options); + } + + /** + * Modify account. + * @param {String} id + * @param {String} name + * @param {Object} options + * @returns {Promise} + */ + + modifyAccount(id, name, options) { + return this.patch(`/wallet/${id}/account/${name}`, options); + } + + /** + * Create address. + * @param {Object} options + * @returns {Promise} + */ + + createAddress(id, account) { + return this.post(`/wallet/${id}/address`, { account }); + } + + /** + * Create change address. + * @param {Object} options + * @returns {Promise} + */ + + createChange(id, account) { + return this.post(`/wallet/${id}/change`, { account }); + } + + /** + * Change or set master key`s passphrase. + * @param {String|Buffer} passphrase + * @param {(String|Buffer)?} old + * @returns {Promise} + */ + + setPassphrase(id, passphrase, old) { + return this.post(`/wallet/${id}/passphrase`, { passphrase, old }); + } + + /** + * Generate a new token. + * @param {(String|Buffer)?} passphrase + * @returns {Promise} + */ + + retoken(id, passphrase) { + return this.post(`/wallet/${id}/retoken`, { + passphrase + }); + } + + /** + * Import private key. + * @param {Number|String} account + * @param {String} key + * @returns {Promise} + */ + + importPrivate(id, account, privateKey, passphrase) { + return this.post(`/wallet/${id}/import`, { + account, + privateKey, + passphrase + }); + } + + /** + * Import public key. + * @param {Number|String} account + * @param {String} key + * @returns {Promise} + */ + + importPublic(id, account, publicKey) { + return this.post(`/wallet/${id}/import`, { + account, + publicKey + }); + } + + /** + * Import address. + * @param {Number|String} account + * @param {String} address + * @returns {Promise} + */ + + importAddress(id, account, address) { + return this.post(`/wallet/${id}/import`, { account, address }); + } + + /** + * Lock a coin. + * @param {String} hash + * @param {Number} index + * @returns {Promise} + */ + + lockCoin(id, hash, index) { + return this.put(`/wallet/${id}/locked/${hash}/${index}`); + } + + /** + * Unlock a coin. + * @param {String} hash + * @param {Number} index + * @returns {Promise} + */ + + unlockCoin(id, hash, index) { + return this.del(`/wallet/${id}/locked/${hash}/${index}`); + } + + /** + * Get locked coins. + * @returns {Promise} + */ + + getLocked(id) { + return this.get(`/wallet/${id}/locked`); + } + + /** + * Lock wallet. + * @returns {Promise} + */ + + lock(id) { + return this.post(`/wallet/${id}/lock`); + } + + /** + * Unlock wallet. + * @param {String} passphrase + * @param {Number} timeout + * @returns {Promise} + */ + + unlock(id, passphrase, timeout) { + return this.post(`/wallet/${id}/unlock`, { passphrase, timeout }); + } + + /** + * Get wallet key. + * @param {String} address + * @returns {Promise} + */ + + getKey(id, address) { + return this.get(`/wallet/${id}/key/${address}`); + } + + /** + * Get wallet key WIF dump. + * @param {String} address + * @param {String?} passphrase + * @returns {Promise} + */ + + getWIF(id, address, passphrase) { + return this.get(`/wallet/${id}/wif/${address}`, { passphrase }); + } + + /** + * Add a public account key to the wallet for multisig. + * @param {String} account + * @param {String} key - Account (bip44) key (base58). + * @returns {Promise} + */ + + addSharedKey(id, account, accountKey) { + return this.put(`/wallet/${id}/shared-key`, { account, accountKey }); + } + + /** + * Remove a public account key to the wallet for multisig. + * @param {String} account + * @param {String} key - Account (bip44) key (base58). + * @returns {Promise} + */ + + removeSharedKey(id, account, accountKey) { + return this.del(`/wallet/${id}/shared-key`, { account, accountKey }); + } + + /** + * Resend wallet transactions. + * @returns {Promise} + */ + + resendWallet(id) { + return this.post(`/wallet/${id}/resend`); + } +} + +/** + * Wallet Instance + * @extends {EventEmitter} + */ + +class Wallet extends EventEmitter { + /** @type {WalletClient} */ + client; + + /** @type {WalletClient} */ + parent; + + /** @type {String} */ + id; + + /** @type {String} */ + token; + + /** + * Create a wallet client. + * @param {Object?} options + */ + + constructor(parent, id, token) { + super(); + this.parent = parent; + this.client = parent.clone(); + this.client.token = token; + this.id = id; + this.token = token; + } + + /** + * Open wallet. + * @returns {Promise} + */ + + async open() { + await this.parent.join(this.id, this.token); + this.parent.wallets.set(this.id, this); + } + + /** + * Close wallet. + * @returns {Promise} + */ + + async close() { + await this.parent.leave(this.id); + this.parent.wallets.delete(this.id); + } + + /** + * Get wallet transaction history. + * @param {String} account + * @returns {Promise} + */ + + getHistory(account) { + return this.client.getHistory(this.id, account); + } + + /** + * Get wallet coins. + * @param {String} account + * @returns {Promise} + */ + + getCoins(account) { + return this.client.getCoins(this.id, account); + } + + /** + * Get all unconfirmed transactions. + * @param {String} account + * @returns {Promise} + */ + + getPending(account) { + return this.client.getPending(this.id, account); + } + + /** + * Calculate wallet balance. + * @param {String} account + * @returns {Promise} + */ + + getBalance(account) { + return this.client.getBalance(this.id, account); + } + + /** + * Get last N wallet transactions. + * @param {String} account + * @param {Number} limit - Max number of transactions. + * @returns {Promise} + */ + + getLast(account, limit) { + return this.client.getLast(this.id, account, limit); + } + + /** + * Get wallet transactions by timestamp range. + * @param {String} account + * @param {Object} options + * @param {Number} options.start - Start time. + * @param {Number} options.end - End time. + * @param {Number?} options.limit - Max number of records. + * @param {Boolean?} options.reverse - Reverse order. + * @returns {Promise} + */ + + getRange(account, options) { + return this.client.getRange(this.id, account, options); + } + + /** + * Get transaction (only possible if the transaction + * is available in the wallet history). + * @param {Hash} hash + * @returns {Promise} + */ + + getTX(hash) { + return this.client.getTX(this.id, hash); + } + + /** + * Get wallet blocks. + * @param {Number} height + * @returns {Promise} + */ + + getBlocks() { + return this.client.getBlocks(this.id); + } + + /** + * Get wallet block. + * @param {Number} height + * @returns {Promise} + */ + + getBlock(height) { + return this.client.getBlock(this.id, height); + } + + /** + * Get unspent coin (only possible if the transaction + * is available in the wallet history). + * @param {Hash} hash + * @param {Number} index + * @returns {Promise} + */ + + getCoin(hash, index) { + return this.client.getCoin(this.id, hash, index); + } + + /** + * Get name state for the given name. + * {@see hsd.NameState} + * @param {String} name + * @returns {Promise} + */ + + getName(name) { + return this.client.getName(this.id, name); + } + + /** + * Get name state for all names + * that the wallet is managing. + * {@see hsd.NameState} + * @param {Object} options + * @param {Boolean} [optoins.own=false] + * @returns {Promise} + */ + + getNames(options) { + return this.client.getNames(this.id, options); + } + + /** + * Get bids, reveals and name state + * for the given name. + * {@see hsd.NameState} + * {@see hsd.BlindBid} + * {@see hsd.BidReveal} + * @param {String} name + * @returns {Promise} + */ + + getAuctionByName(name) { + return this.client.getAuctionByName(this.id, name); + } + + /** + * Get bids, reveals and name state + * for all names the wallet manages. + * {@see hsd.NameState} + * {@see hsd.BlindBid} + * {@see hsd.BidReveal} + * @param {Object} options + * @returns {Promise} + */ + + getAuctions(options) { + return this.client.getAuctions(this.id, options); + } + + /** + * Get bids for a given name. + * {@see hsd.BlindBid} + * @param {String?} name + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getBidsByName(name, options) { + return this.client.getBidsByName(this.id, name, options); + } + + /** + * Get bids for all names. + * the wallet manages. + * {@see hsd.BlindBid} + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getBids(options) { + return this.client.getBids(this.id, options); + } + + /** + * Get wallet reveal for a given name. + * {@see hsd.BidReveal} + * @param {String?} name + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getRevealsByName(name, options) { + return this.client.getRevealsByName(this.id, name, options); + } + + /** + * Get wallet reveals for all names + * the wallet manages. + * {@see hsd.BidReveal} + * @param {Object?} options + * @param {Boolean?} options.own + * @returns {Promise} + */ + + getReveals(options) { + return this.client.getReveals(this.id, options); + } + + /** + * Get name resource. + * {@see hsd.Resource} + * @param {String} name + * @returns {Promise} + */ + + getResource(name) { + return this.client.getResource(this.id, name); + } + + /* + * Deterministically regenerate a bid's nonce. + * @param {String} name + * @param {Object} options + * @param {String} options.address + * @param {Number} options.bid + * @returns {Promise} + */ + + getNonce(name, options) { + return this.client.getNonce(this.id, name, options); + } + + /** + * @param {Number} now - Current time. + * @param {Number} age - Age delta. + * @returns {Promise} + */ + + zap(account, age) { + return this.client.zap(this.id, account, age); + } + + /** + * Used to remove a pending transaction from the wallet. + * That is likely the case if it has a policy or low fee + * that prevents it from proper network propagation. + * @param {Hash} hash + * @returns {Promise} + */ + + abandon(hash) { + return this.client.abandon(this.id, hash); + } + + /** + * Create a transaction, fill. + * @param {Object} options + * @returns {Promise} + */ + + createTX(options) { + return this.client.createTX(this.id, options); + } + + /** + * Create pre-signed bid and reveal txs, + * fill, and optionally sign and broadcast. + * @param {Object} options + * @param {String} options.name + * @param {Number} options.bid + * @param {Number} options.lockup + * @param {String} options.passphrase + * @param {Boolean} options.sign + * @param {Boolean} options.broadcastBid + * @returns {Promise} + */ + + createAuctionTXs(options) { + return this.client.createAuctionTXs(this.id, options); + } + + /** + * Create a transaction, fill, sign, and broadcast. + * @param {Object} options + * @param {String} options.address + * @param {Amount} options.value + * @returns {Promise} + */ + + send(options) { + return this.client.send(this.id, options); + } + + /** + * Create open transaction. + * @param {Object} options + * @returns {Promise} + */ + + createOpen(options) { + return this.client.createOpen(this.id, options); + } + + /** + * Create bid transaction. + * @param {Object} options + * @returns {Promise} + */ + + createBid(options) { + return this.client.createBid(this.id, options); + } + + /** + * Create reveal transaction. + * @param {Object} options + * @returns {Promise} + */ + + createReveal(options) { + return this.client.createReveal(this.id, options); + } + + /** + * Create redeem transaction. + * @param {Object} options + * @returns {Promise} + */ + + createRedeem(options) { + return this.client.createRedeem(this.id, options); + } + + /** + * Create update transaction. + * @param {Object} options + * @returns {Promise} + */ + + createUpdate(options) { + return this.client.createUpdate(this.id, options); + } + + /** + * Create renewal transaction. + * @param {Object} options + * @returns {Promise} + */ + + createRenewal(options) { + return this.client.createRenewal(this.id, options); + } + + /** + * Create transfer transaction. + * @param {Object} options + * @returns {Promise} + */ + + createTransfer(options) { + return this.client.createTransfer(this.id, options); + } + + /** + * Create cancel transaction. + * @param {Object} options + * @returns {Promise} + */ + + createCancel(options) { + return this.client.createCancel(this.id, options); + } + + /** + * Create finalize transaction. + * @param {Object} options + * @returns {Promise} + */ + + createFinalize(options) { + return this.client.createFinalize(this.id, options); + } + + /** + * Create revoke transaction. + * @param {Object} options + * @returns {Promise} + */ + + createRevoke(options) { + return this.client.createRevoke(this.id, options); + } + + /** + * Sign a transaction. + * @param {Object} options + * @returns {Promise} + */ + + sign(options) { + return this.client.sign(this.id, options); + } + + /** + * Get the raw wallet JSON. + * @returns {Promise} + */ + + getInfo() { + return this.client.getInfo(this.id); + } + + /** + * Get wallet accounts. + * @returns {Promise} - Returns Array. + */ + + getAccounts() { + return this.client.getAccounts(this.id); + } + + /** + * Get wallet master key. + * @returns {Promise} + */ + + getMaster() { + return this.client.getMaster(this.id); + } + + /** + * Get wallet account. + * @param {String} account + * @returns {Promise} + */ + + getAccount(account) { + return this.client.getAccount(this.id, account); + } + + /** + * Create account. + * @param {String} name + * @param {Object} options + * @returns {Promise} + */ + + createAccount(name, options) { + return this.client.createAccount(this.id, name, options); + } + + /** + * Modify account. + * @param {String} name + * @param {Object} options + * @returns {Promise} + */ + + modifyAccount(name, options) { + return this.client.modifyAccount(this.id, name, options); + } + + /** + * Create address. + * @param {Object} options + * @returns {Promise} + */ + + createAddress(account) { + return this.client.createAddress(this.id, account); + } + + /** + * Create change address. + * @param {Object} options + * @returns {Promise} + */ + + createChange(account) { + return this.client.createChange(this.id, account); + } + + /** + * Change or set master key`s passphrase. + * @param {String|Buffer} passphrase + * @param {(String|Buffer)?} old + * @returns {Promise} + */ + + setPassphrase(passphrase, old) { + return this.client.setPassphrase(this.id, passphrase, old); + } + + /** + * Generate a new token. + * @param {(String|Buffer)?} passphrase + * @returns {Promise} + */ + + async retoken(passphrase) { + const result = await this.client.retoken(this.id, passphrase); + + assert(result); + assert(typeof result.token === 'string'); + + this.token = result.token; + + return result; + } + + /** + * Import private key. + * @param {Number|String} account + * @param {String} key + * @returns {Promise} + */ + + importPrivate(account, privateKey, passphrase) { + return this.client.importPrivate(this.id, account, privateKey, passphrase); + } + + /** + * Import public key. + * @param {Number|String} account + * @param {String} key + * @returns {Promise} + */ + + importPublic(account, publicKey) { + return this.client.importPublic(this.id, account, publicKey); + } + + /** + * Import address. + * @param {Number|String} account + * @param {String} address + * @returns {Promise} + */ + + importAddress(account, address) { + return this.client.importAddress(this.id, account, address); + } + + /** + * Lock a coin. + * @param {String} hash + * @param {Number} index + * @returns {Promise} + */ + + lockCoin(hash, index) { + return this.client.lockCoin(this.id, hash, index); + } + + /** + * Unlock a coin. + * @param {String} hash + * @param {Number} index + * @returns {Promise} + */ + + unlockCoin(hash, index) { + return this.client.unlockCoin(this.id, hash, index); + } + + /** + * Get locked coins. + * @returns {Promise} + */ + + getLocked() { + return this.client.getLocked(this.id); + } + + /** + * Lock wallet. + * @returns {Promise} + */ + + lock() { + return this.client.lock(this.id); + } + + /** + * Unlock wallet. + * @param {String} passphrase + * @param {Number} timeout + * @returns {Promise} + */ + + unlock(passphrase, timeout) { + return this.client.unlock(this.id, passphrase, timeout); + } + + /** + * Get wallet key. + * @param {String} address + * @returns {Promise} + */ + + getKey(address) { + return this.client.getKey(this.id, address); + } + + /** + * Get wallet key WIF dump. + * @param {String} address + * @param {String?} passphrase + * @returns {Promise} + */ + + getWIF(address, passphrase) { + return this.client.getWIF(this.id, address, passphrase); + } + + /** + * Add a public account key to the wallet for multisig. + * @param {String} account + * @param {String} key - Account (bip44) key (base58). + * @returns {Promise} + */ + + addSharedKey(account, accountKey) { + return this.client.addSharedKey(this.id, account, accountKey); + } + + /** + * Remove a public account key to the wallet for multisig. + * @param {String} account + * @param {String} key - Account (bip44) key (base58). + * @returns {Promise} + */ + + removeSharedKey(account, accountKey) { + return this.client.removeSharedKey(this.id, account, accountKey); + } + + /** + * Resend wallet transactions. + * @returns {Promise} + */ + + resend() { + return this.client.resendWallet(this.id); + } +} + +/* + * Expose + */ + +WalletClient.Wallet = Wallet; + +module.exports = WalletClient; diff --git a/lib/coins/coinentry.js b/lib/coins/coinentry.js index 01df7d433..b57bb0900 100644 --- a/lib/coins/coinentry.js +++ b/lib/coins/coinentry.js @@ -13,6 +13,10 @@ const Output = require('../primitives/output'); const compress = require('./compress'); const {encoding} = bio; +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../primitives/outpoint')} Outpoint */ +/** @typedef {import('../primitives/tx')} TX */ + /* * Constants */ @@ -79,8 +83,7 @@ class CoinEntry extends bio.Struct { /** * Inject properties from TX. - * @param {TX} tx - * @param {Number} index + * @param {Output} output */ fromOutput(output) { @@ -90,8 +93,7 @@ class CoinEntry extends bio.Struct { /** * Instantiate a coin from a TX - * @param {TX} tx - * @param {Number} index - Output index. + * @param {Output} output * @returns {CoinEntry} */ @@ -100,9 +102,8 @@ class CoinEntry extends bio.Struct { } /** - * Inject properties from TX. - * @param {TX} tx - * @param {Number} index + * Inject properties from Coin. + * @param {Coin} coin */ fromCoin(coin) { @@ -117,7 +118,7 @@ class CoinEntry extends bio.Struct { /** * Instantiate a coin from a TX - * @param {TX} tx + * @param {Coin} coin * @returns {CoinEntry} */ @@ -129,6 +130,7 @@ class CoinEntry extends bio.Struct { * Inject properties from TX. * @param {TX} tx * @param {Number} index + * @param {Number} height */ fromTX(tx, index, height) { @@ -146,6 +148,7 @@ class CoinEntry extends bio.Struct { * Instantiate a coin from a TX * @param {TX} tx * @param {Number} index - Output index. + * @param {Number} height * @returns {CoinEntry} */ @@ -172,7 +175,8 @@ class CoinEntry extends bio.Struct { /** * Write the coin to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -213,8 +217,7 @@ class CoinEntry extends bio.Struct { /** * Inject properties from serialized buffer writer. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -237,7 +240,6 @@ class CoinEntry extends bio.Struct { /** * Inject properties from serialized data. - * @private * @param {Buffer} data */ diff --git a/lib/coins/coins.js b/lib/coins/coins.js index d543196cb..639b567bf 100644 --- a/lib/coins/coins.js +++ b/lib/coins/coins.js @@ -9,6 +9,11 @@ const assert = require('bsert'); const CoinEntry = require('./coinentry'); +/** @typedef {import('../primitives/coin')} Coin */ +/** @typedef {import('../primitives/output')} Output */ +/** @typedef {import('../primitives/tx')} TX */ +/** @typedef {import('../primitives/outpoint')} Outpoint */ + /** * Coins * Represents the outputs for a single transaction. @@ -23,6 +28,7 @@ class Coins { */ constructor() { + /** @type Map */ this.outputs = new Map(); } diff --git a/lib/coins/coinview.js b/lib/coins/coinview.js index eff167f69..b958784bd 100644 --- a/lib/coins/coinview.js +++ b/lib/coins/coinview.js @@ -13,6 +13,17 @@ const CoinEntry = require('./coinentry'); const View = require('../covenants/view'); const {BitView} = require('../covenants/bitfield'); +/** @typedef {import('bufio').BufferReader} BufferReader */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../primitives/coin')} Coin */ +/** @typedef {import('../primitives/input')} Input */ +/** @typedef {import('../primitives/output')} Output */ +/** @typedef {import('../primitives/tx')} TX */ +/** @typedef {import('../primitives/outpoint')} Outpoint */ +/** @typedef {import('../wallet/path')} Path */ +/** @typedef {import('../blockchain/chaindb')} ChainDB */ + /** * Coin View * Represents a coin viewpoint: @@ -84,8 +95,8 @@ class CoinView extends View { /** * Remove coins from the collection. - * @param {Coins} coins - * @returns {Coins|null} + * @param {Hash} hash + * @returns {Coins?} */ remove(hash) { @@ -400,6 +411,18 @@ class CoinView extends View { return null; } + /** + * Add an HD path to the collection. + * Implemented in {@link WalletCoinView} + * @param {Outpoint} prevout + * @param {Path} path + * @returns {Path|null} + */ + + addPath(prevout, path) { + return null; + } + /** * Get coins height by input. * @param {Input} input @@ -497,6 +520,7 @@ class CoinView extends View { /** * Calculate serialization size. + * @param {TX} tx * @returns {Number} */ @@ -520,8 +544,9 @@ class CoinView extends View { /** * Write coin data to buffer writer * as it pertains to a transaction. - * @param {BufferWriter} bw + * @param {BufioWriter} bw * @param {TX} tx + * @returns {BufioWriter} */ write(bw, tx) { @@ -543,7 +568,6 @@ class CoinView extends View { /** * Read serialized view data from a buffer * reader as it pertains to a transaction. - * @private * @param {BufferReader} br * @param {TX} tx */ diff --git a/lib/coins/compress.js b/lib/coins/compress.js index 282db09d7..be9ea8793 100644 --- a/lib/coins/compress.js +++ b/lib/coins/compress.js @@ -6,6 +6,11 @@ 'use strict'; +/** @typedef {import('bufio').BufferReader} BufferReader */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../primitives/output')} Output */ + /** * @module coins/compress * @ignore @@ -16,7 +21,7 @@ const {encoding} = require('bufio'); /** * Compress an output. * @param {Output} output - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ function compressOutput(output, bw) { @@ -41,6 +46,7 @@ function decompressOutput(output, br) { /** * Calculate output size. + * @param {Output} output * @returns {Number} */ @@ -56,7 +62,7 @@ function sizeOutput(output) { * Compress value using an exponent. Takes advantage of * the fact that many values are divisible by 10. * @see https://github.com/btcsuite/btcd/blob/master/blockchain/compress.go - * @param {Amount} value + * @param {AmountValue} value * @returns {Number} */ @@ -82,7 +88,7 @@ function compressValue(value) { /** * Decompress value. * @param {Number} value - Compressed value. - * @returns {Amount} value + * @returns {AmountValue} value */ function decompressValue(value) { diff --git a/lib/coins/undocoins.js b/lib/coins/undocoins.js index f60fffa88..dbcfb0251 100644 --- a/lib/coins/undocoins.js +++ b/lib/coins/undocoins.js @@ -10,6 +10,10 @@ const assert = require('bsert'); const bio = require('bufio'); const CoinEntry = require('../coins/coinentry'); +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../primitives/outpoint')} Outpoint */ +/** @typedef {import('./coinview')} CoinView */ + /** * Undo Coins * Coins need to be resurrected from somewhere @@ -28,12 +32,13 @@ class UndoCoins extends bio.Struct { constructor() { super(); + /** @type {CoinEntry[]} */ this.items = []; } /** * Push coin entry onto undo coin array. - * @param {CoinEntry} + * @param {CoinEntry} coin * @returns {Number} */ @@ -59,7 +64,8 @@ class UndoCoins extends bio.Struct { /** * Serialize all undo coins. - * @returns {Buffer} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -73,9 +79,8 @@ class UndoCoins extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {Buffer} data - * @returns {UndoCoins} + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { diff --git a/lib/covenants/locked-browser.js b/lib/covenants/locked-browser.js new file mode 100644 index 000000000..d6463a846 --- /dev/null +++ b/lib/covenants/locked-browser.js @@ -0,0 +1,116 @@ +'use strict'; + +const assert = require('bsert'); +const sha3 = require('bcrypto/lib/sha3'); +const data = require('./lockup.json'); + +/* + * Constants + */ + +const ZERO_HASH = sha3.zero.toString('hex'); + +/** + * Locked up + */ + +class LockedUp { + constructor(data) { + const meta = data[ZERO_HASH]; + + this.data = data; + this.size = meta[0]; + } + + has(hash) { + assert(Buffer.isBuffer(hash) && hash.length === 32); + + const hex = hash.toString('hex'); + const item = this.data[hex]; + + if (!item) + return false; + + return Array.isArray(item); + } + + get(hash) { + assert(Buffer.isBuffer(hash) && hash.length === 32); + + const hex = hash.toString('hex'); + const item = this.data[hex]; + + if (!item || !Array.isArray(item)) + return null; + + const target = item[0]; + const flags = item[1]; + const index = target.indexOf('.'); + + assert(index !== -1); + + const root = (flags & 1) !== 0; + const custom = (flags & 2) !== 0; + const name = target.substring(0, index); + + return { + name, + hash, + target, + root, + custom + }; + } + + hasByName(name) { + assert(typeof name === 'string'); + + if (name.length === 0 || name.length > 63) + return false; + + return this.has(hashName(name)); + } + + getByName(name) { + assert(typeof name === 'string'); + + if (name.length === 0 || name.length > 63) + return null; + + return this.get(hashName(name)); + } + + *entries() { + const keys = Object.keys(this.data); + + for (const key of keys) { + const hash = Buffer.from(key, 'hex'); + + yield [hash, this.get(hash)]; + } + } + + *keys() { + const keys = Object.keys(this.data); + + for (const key of keys) + yield Buffer.from(key, 'hex'); + } + + *values() { + for (const [, item] of this.entries()) + yield item; + } + + [Symbol.iterator]() { + return this.entries(); + } +} + +function hashName(name) { + const raw = Buffer.from(name.toLowerCase(), 'ascii'); + return sha3.digest(raw); +} + +exports.LockedUp = LockedUp; +exports.locked = new LockedUp(data); diff --git a/lib/covenants/locked.js b/lib/covenants/locked.js new file mode 100644 index 000000000..abdc86877 --- /dev/null +++ b/lib/covenants/locked.js @@ -0,0 +1,173 @@ +'use strict'; + +const assert = require('bsert'); +const Path = require('path'); +const fs = require('bfile'); +const sha3 = require('bcrypto/lib/sha3'); + +const FILE = Path.resolve(__dirname, 'lockup.db'); +const DATA = fs.readFileSync(FILE); + +/** + * Locked up + */ + +class LockedUp { + constructor(data) { + this.data = data; + this.size = readU32(data, 0); + } + + get prefixSize() { + return 4; + } + + _compare(b, off) { + const a = this.data; + + for (let i = 0; i < 32; i++) { + const x = a[off + i]; + const y = b[i]; + + if (x < y) + return -1; + + if (x > y) + return 1; + } + + return 0; + } + + _find(key) { + let start = 0; + let end = this.size - 1; + + while (start <= end) { + const index = (start + end) >>> 1; + const pos = this.prefixSize + index * 36; + const cmp = this._compare(key, pos); + + if (cmp === 0) + return readU32(this.data, pos + 32); + + if (cmp < 0) + start = index + 1; + else + end = index - 1; + } + + return -1; + } + + _target(pos) { + const len = this.data[pos]; + return this.data.toString('ascii', pos + 1, pos + 1 + len); + } + + _flags(pos) { + const len = this.data[pos]; + return this.data[pos + 1 + len]; + } + + _index(pos) { + const len = this.data[pos]; + return this.data[pos + 1 + len + 1]; + } + + _get(hash, pos) { + const target = this._target(pos); + const flags = this._flags(pos); + const index = this._index(pos); + const root = (flags & 1) !== 0; + const custom = (flags & 2) !== 0; + const name = target.substring(0, index); + + return { + name, + hash, + target, + root, + custom + }; + } + + has(hash) { + assert(Buffer.isBuffer(hash) && hash.length === 32); + + return this._find(hash) !== -1; + } + + get(hash) { + assert(Buffer.isBuffer(hash) && hash.length === 32); + + const pos = this._find(hash); + + if (pos === -1) + return null; + + return this._get(hash, pos); + } + + hasByName(name) { + assert(typeof name === 'string'); + + if (name.length === 0 || name.length > 63) + return false; + + return this.has(hashName(name)); + } + + getByName(name) { + assert(typeof name === 'string'); + + if (name.length === 0 || name.length > 63) + return null; + + return this.get(hashName(name)); + } + + *entries() { + for (let i = 0; i < this.size; i++) { + const pos = this.prefixSize + i * 36; + const hash = this.data.slice(pos, pos + 32); + const ptr = readU32(this.data, pos + 32); + const item = this._get(hash, ptr); + + yield [hash, item]; + } + } + + *keys() { + for (let i = 0; i < this.size; i++) { + const pos = this.prefixSize + i * 36; + + yield this.data.slice(pos, pos + 32); + } + } + + *values() { + for (const [, item] of this.entries()) + yield item; + } + + [Symbol.iterator]() { + return this.entries(); + } +} + +/* + * Helpers + */ + +function readU32(data, off) { + return data.readUInt32LE(off); +} + +function hashName(name) { + const raw = Buffer.from(name.toLowerCase(), 'ascii'); + return sha3.digest(raw); +} + +exports.LockedUp = LockedUp; +exports.locked = new LockedUp(DATA); diff --git a/lib/covenants/lockup.db b/lib/covenants/lockup.db new file mode 100644 index 000000000..9f70052ac Binary files /dev/null and b/lib/covenants/lockup.db differ diff --git a/lib/covenants/lockup.json b/lib/covenants/lockup.json new file mode 100644 index 000000000..738823e55 --- /dev/null +++ b/lib/covenants/lockup.json @@ -0,0 +1,11557 @@ +{ + "0000000000000000000000000000000000000000000000000000000000000000": [11554], + "00034c407484cdb33f3552247f0fae3d4a38c9e537fc8f39ddb51f6aa4b438c5": ["nec.", 1], + "0006bfcfa9fd6c312279c693a65b764beee7259f883d7636ca334fcf90565745": ["openclassrooms.com.", 0], + "00119fcbd21c6318b9fb8a8df72507ea77863fad17394b02c070d922927eb5b9": ["sparknotes.com.", 0], + "0012f848322e006f57b600036eb570644266ba479caee6582b4e598bcfaa8e0c": ["banimode.com.", 0], + "00144a4f9ce686dfa16720eb8a3ef97e335401d85edf482e0ffe50cc230e9d47": ["bluehost.com.", 0], + "0015d8d08b1426985bfd0c35e2c1fab174f15d89f82a67c2e7e1be8290546376": ["homeaffairs.gov.au.", 0], + "001f8d564676383ea20e80aa1ba533e9441b5a04d04ef5688b1aba0fbe01c69a": ["channelnewsasia.com.", 0], + "0025f795b4347167dbae359f9037cce2f3faa816072c8bb20a00ccd6559598f1": ["exploit-db.com.", 0], + "0026a6288f78a87823085536720fcefd894b4ca307bdcc6f3168a9001f0f1583": ["hateblo.jp.", 0], + "00298f3f3fab70f917f4a8f41d374472ad5325a1ee46c7114b524076383cdb73": ["hootsuite.com.", 0], + "002cfdff34fae41ac41bae88815ada129950f836fb836625d204c9ff998987cf": ["kuantokusta.pt.", 0], + "0031278a24ca90ac79354a8e313651ac78bc2ae41fdb3b85aa6d0abc5f55665d": ["bni.co.id.", 0], + "003eba830a97ec77c47417504a810e15c6a2ed081632790c081bcb18ac449aae": ["realclearpolitics.com.", 0], + "003ebcebf9bdecb26644cbb2b95280e6ef4da96225ddca6d6fbef89685f9fd72": ["streamable.com.", 0], + "003ebf0b5800013fdc41db524478d79b08304e72f9dbd63e9628fed03b1df4b2": ["cint.com.", 0], + "00410528b530676546f4a27a88a25180e48e038980b9f22ecc9f4fe80fdc6df3": ["giga.de.", 0], + "00415257ddd99e9688cd55d751a0f60dd9a74179fad93a2b47f6b480803676f3": ["zerohedge.com.", 0], + "004ae0d995d45424cb4584aeaf71f0815f6b7140c503a26e977556825cdcc280": ["startpage.com.", 0], + "0053c03688f58eabbad02b96448b8bedc2c6827962ba335bcf1ef505e17271b3": ["techwiser.com.", 0], + "00555ad28b64197c349c5f00ff2a279400c4315db2a1dfc5f1035ccbdae7ad03": ["cern.", 1], + "005c9ff6cb80b914d1d4c2c7a60178355e1d72b1a708b71ebc6102ea394a3d8c": ["claims.", 1], + "005fc80ac981d5ec3e96c3da3f37201173d9be4c979dad3a68a19ad341e6c7ce": ["rekhta.org.", 0], + "00628530b9c3a7132c8e2fa4c02ec872867cef237d9458318151e88dcab6c8c8": ["yto.net.cn.", 0], + "00631845dc40e286cca5882ff86028d4a1fd02ca3c70f162eec8142e7ef8c0f1": ["a9vg.com.", 0], + "007134461e894fe90d26d3a550bf1e3a1ca2045706ffed58608be743175a5dd0": ["fsu.edu.", 0], + "007425a2116a413d4a436c9e4ba2937accb220bddc47a5775839f8e494b539b5": ["awin1.com.", 0], + "0076836450e01b4fe1029179dca91902863f0540284bc9c1e7d69be34211e6e2": ["mutaz.pro.", 0], + "0079f0f481c3d7a1a506dc74c51ed40e3a22918952801a5fc0e56660571db855": ["cafe.", 1], + "007aa7ef873c031e86191e530686e754c392de51d2cf7018eda893298e7f763c": ["pantone.com.", 0], + "007aae6a0f33060fd8f752b6a68f338fc660c171927edf058d1611c129ff5376": ["teacherspayteachers.com.", 0], + "007bfc75bc0ef8142e8cf734994325ed5adb26229a00078dfa12771d3e7f75b5": ["houzz.com.", 0], + "007f2760d41588622558bb8bd6ffe66a385f65983bae7789830e14519b23d1ff": ["kobo.com.", 0], + "0081b598f442d9d09eb9e8c006796fa296aed7e9a62bc12394dd7bde4b972ec1": ["unl.edu.", 0], + "0082679f2ca5139c35af182d55e2000f6dc5dbc1aecb1b2d1c76555c17ded8cc": ["sustech.edu.cn.", 0], + "0084aaac69d290064c9b162063664beeefd5fc8d2bc722c03557fa2772d18fd3": ["3d66.com.", 0], + "008555291ba7655c40029272fd4de36d80727f600cab20a137294fdd7ce560a1": ["crateandbarrel.com.", 0], + "008868fcb78a22ae3a1b2102c2540b94ff8b280977235c47f477df404ad3919d": ["cnnturk.com.", 0], + "008c9f44d4a211cb5982f0966d5c6d49e32e1f80fdccee8154caf3395c5894d9": ["dxlive.com.", 0], + "008f1128371a1c3ed15179bdac19299f723f338afe348ea3486c10314e8d7bf5": ["web-ace.jp.", 0], + "00957a3a963bee13b0cc07e7dc760cc6f0563905355a59ba5126d915556f6e66": ["mini.", 1], + "009c0b9fbe602f251afec8fb9927c68a4c89124cedb0d476eb8f2a8a3b1b8a52": ["bdjobs.com.", 0], + "00a8dbee17e06f48e719bd00fb34b9cb5b0128351475baab6a960cf6d4f8d375": ["republika.co.id.", 0], + "00b3e2189b770132507269d4ab2bccfc7ce4b78fe7e3085e2ff08d1522b9f152": ["querobolsa.com.br.", 0], + "00bf18f86b1e916b252bd6db3af8197311b5f9bdbba358a41538d2481eb59092": ["soundofhope.org.", 0], + "00c239da1783736babd065d7b8a7b7ca97616ca3e89d8dc491871ce258b424e8": ["intellipaat.com.", 0], + "00c3a053afc117c4a85187c8a2e0d079d4642a3fbc227e11517637974509ffc9": ["easybib.com.", 0], + "00c548e1a0b17caf0ee0f2af04dc091adc9a1ed38bb76bfa0b9019302185691a": ["metart.com.", 0], + "00c635cfef5651a09430d6874ad1103083a937420cc838b8e011500dc5d295df": ["anyflip.com.", 0], + "00c72016bd38ca96b341a88473a66c5dc1b39f6fdc9ef9aff0b17ffebfddfece": ["halqat.news.", 0], + "00d14d0f843b7e9f930ae85403e646cd93561ed08bc34771a6684bd44ce88e49": ["agah.com.", 0], + "00d722c6d0d65c6f620511d0e1a261522cb7df3e799752d5876726c52e064c9d": ["cumt.edu.cn.", 0], + "00d87df1e72e6c4992be9f119a2c3f59ab9571bc601c687ded59281a17ceadd9": ["mofa.gov.sa.", 0], + "00da3732406c7fc4208839ad53816c7f59732cbe3c8916adcae426f8564609cc": ["shamela.ws.", 0], + "00dfbefab11545d04301b64e287261131235a1cd7679f917222c8bcc0aeb1639": ["center.", 1], + "00e4bb88e4b6e2aaa0ff31c8a1c71f75402b726f81a3762be48ad6ab33f9395c": ["comic-days.com.", 0], + "00e5625bfc67eb432a766b0c156d2dbb38196cf714e6007e15204c3203898a68": ["thegamer.com.", 0], + "00ef86b294b1f6d419d2afb740294a941af8380b455e3fccc233fdbd6b6d8b86": ["yzw.cn.", 0], + "00f36ac38607d4abf4b17a27979c00c9b514fcaddec325ce9ae019f2d5f0e23a": ["ag.", 1], + "00fe480aa0715ebe8d5d1666caa8083436cb7e4c2e4bdce98093dd5cbc288dd0": ["twitchquotes.com.", 0], + "010a803299b55b83c62447656704c00e774fd496bdd657cd39698d4a8dccebde": ["softportal.com.", 0], + "010ad68f56e52f2e3b6e0e0301af866747c3205518cb3653c0a83c87d0a08990": ["uscis.gov.", 0], + "010f5025f05716019b962b825d69ca2b826ba8ffa9caeb3ff9e613eeba0b75e3": ["ejemplos.co.", 0], + "01130d9e566126632506d5531384f5393f07df98192d42aab9a9ae1f3a4b4f4b": ["leagueofgraphs.com.", 0], + "011359ddc93876eb81e8854331cf8d9818be6e752ce5c7f301ffbfd7e79d3ba8": ["envato.com.", 0], + "0113dd0dd8f1853b445d23e450db989df7837a024c1a471f5878685c3e7dff6d": ["123rf.com.", 0], + "011a0fe408a04e0f318a8674d2e0400e20f14071cbdf83ad4a5647f8541773c5": ["fbsbx.com.", 0], + "01235ac737d8d36de8ef0304cb9358f4607d6dcf20b14b3aa66466a2d28852e1": ["empik.com.", 0], + "01357075dd20b6076e1a5bc530e86e4065a0407f14aedbba5fef515ce1a71458": ["manyvids.com.", 0], + "0141018ec5e976e83c89f6b4e720588ec3389aa318498ef7dcbac243f9a096d7": ["heute.at.", 0], + "0145c801bd8b34662d7b920d22ca35892cdf0ad5f4d58e8e9efd8ca60288a69d": ["thepiratebay3.to.", 0], + "014af05fcc85e295f124f411b744e6f284e0f9c5fb9963d5987086824c0dff69": ["info.", 1], + "014b1ad1db2c82872ff37498081a40fa76a2584e461327e91e3f809e0cc967d2": ["open2ch.net.", 0], + "014fd7b4eeeb49f6588208995cf6b72016d6c75ae8d96700356f9acc856f87b1": ["themuse.com.", 0], + "015047419520879ede7bb7ce730d489af4248ba908af17ab4f530e5840a560db": ["javhub.net.", 0], + "01509d95e42139628e7a79d68971538854183a36072130146db352737443c9b5": ["cbr.com.", 0], + "0154041073753fc39e385e1eb4c94128f4c25277f80841f1100fb08d9fdc14ff": ["cinemablend.com.", 0], + "0156b396b3c049c89ebc7153a30f8508520d1c2090bca3433a1f7ed21f41f762": ["issuu.com.", 0], + "015f76f0974b0e34f6e147e6d30a520f0df8c62cc28a235186b99508397734ed": ["shipxy.com.", 0], + "01618863cda161e4c85024ca0e35fe026b0471325f334c91a2eafc896ac13b4c": ["beanfun.com.", 0], + "01647e003b34ce6ca2c1dc2689c3a6490210acafb0a29eab2d521c800a171081": ["gelocal.it.", 0], + "0166d9d5fc19ef7195daa63b563303fe16af056e6d0c7f93b2d7f04f21491111": ["itch.io.", 0], + "0168aa5f4a1b7e37996fa3d1fed3f527bc71121eba19c1ab567e6e3e7488d6a0": ["khabarban.com.", 0], + "0177f3066f9d2d5841b4d115f0f14062c7745ca694f5526afcb8415441ab6a8e": ["correiobraziliense.com.br.", 0], + "018fbb3b55ac4ff6e64a3715d502a7a4d83145f507665fc555b6d0d3ec2c7539": ["iefans.net.", 0], + "019d43b97895a044204688533e701c7bb5aee23ee77e643963743fab436eb2f3": ["skf.com.", 0], + "019f2e083711a52cdc316ced004963dc7fd98857b6f3af21dc255f34104feaac": ["camhub.cc.", 0], + "01a861f104fe292efe3c1406b56054d8946abf76e42632d9a674f93ed332c762": ["alltrails.com.", 0], + "01cbeb123910f3392daca59e5d69e20ccd720938aac7be68f33101631425e38c": ["digitalocean.com.", 0], + "01cedfc98426ba4a5791080d08ffd66706eaa8dd58a3bd072fc2efd67965c7f5": ["gesetze-im-internet.de.", 0], + "01d01d1c2b37de1c0ec20c831a3a6defa27b81edf8fb93aa18533bee5cfec7a2": ["opencorporates.com.", 0], + "01d502cb6dbe7c9d0b1f2bfdf4e7dc2a9173d3ba0d8219305bfa0a76360b9d8d": ["eghtesadonline.com.", 0], + "01ef3513afd4a111fdc1b5489a8b29b4ac53c72dbd928894ff759b8eef263615": ["sr.", 1], + "020ca8fd7c034118f444e6f1ff72f03d724fcb3bad4e713c6fe37010c7581220": ["dongqiudi.com.", 0], + "0216c9ea960ecce0b4e36baa6bf2882141a1d6ee7b29338b25f51209fc652281": ["ve.", 1], + "0217281d7257ce25d9b397cad665daa932c14bfb61519158a56e2cbb283960e1": ["gazprombank.ru.", 0], + "02172d572d4810afe7a96c03b458dd61e77725a0f4f887491e4c51f4e8229d3e": ["coursera.org.", 0], + "022a4257d1072db163d4808149cbfe9597dcadbf84d1f0c67e13f980f8fc64e6": ["dtf.ru.", 0], + "022d66b1899c7063e5a42c4fa0677a4f1b9e2de0025c4840a1215d5d26ac3bc3": ["skatteverket.se.", 0], + "02466b8b615033433f7481bf397c023cfb6e3fd1305289c6dbee8e1b75cf8d68": ["waploaded.com.", 0], + "024d7327cce156431b90607009536a0ec35db30a53790939e41c4bed5007beaa": ["everyeye.it.", 0], + "025458018f4cfb702ad245b1aab4468051da450d3332146ad4801f8eb9813cb1": ["neo4j.com.", 0], + "0269505510c3fba37ef0fabfb038029512ee6979dc69f3b234ce8e46e4dbab2a": ["mycdn.me.", 0], + "0279d0916951764626ec841ad4a5f4461d5ee9f1f3495d8c0b44dd53a3a76a14": ["lieferando.de.", 0], + "0279ee0279bf15cbb9188d0ee3dc15ec4fe610fce19b7be63a403476046025ca": ["bancociudad.com.ar.", 0], + "027b90db8f05c40c3a09f4b1d7c0f9fa1a140677d3d1f9ea629f91635dbb28ee": ["myrecipes.com.", 0], + "027e4c73ee9c30f96ef3ce84a375a4ac4b3388cbcdc120d7a43657633ccadb0b": ["dorama.land.", 0], + "0286e1eba4dad0b2065bc7faab1d12ca96ebed8b2bda230f04dc74b7546981eb": ["xhcdn.com.", 0], + "028f20bfb0fea4b1d4c0bdc5d41b3e22233eec964983697c35a2559032d5232a": ["umu.se.", 0], + "02972a5fe366d4350ec7e352cc9789ed89e59e52eec475fceec8d082a0993f2b": ["nanning.gov.cn.", 0], + "02974b8672620c0d7f9e9df25e063be20f41479faca264c6e2658abf41a83653": ["by.", 1], + "0297acf081bc19fbb50aef113e6621eb2a9372ea5419813afba36ec8cca7345b": ["wiki.", 1], + "0297de2fc57514034dba7f1d22bb77b3e0dfd150302f6bc1c37818c0afff67cf": ["thetimes.co.uk.", 0], + "029a26720dd3478a2b47dee9a41270afb930e7e6fa911f6441b43628d2fbc19b": ["hotcelebshome.com.", 0], + "029c097e3b27f50c822eaa330d8a50ad359267654e66f0ae32d915a52438a55e": ["claveunica.gob.cl.", 0], + "029f72215f3560a10878fe21acbacea0c83c84f55c945102926f77c22671e857": ["edeka.", 1], + "029fd53d9e5c91993d47c3a8c3003aed20c22e7cbacd449d52aa84f7f599f1d3": ["tallysolutions.com.", 0], + "02a047575778ddb87c1004a44d16d9d6f49bd393bf2acf335ea4ad4aa2ae8140": ["sx.", 1], + "02ab5a24bdbc953cf743a0e03c18b20e0f19604c52350ab0bd9e5a084314c0d6": ["heyvagroup.com.", 0], + "02ae439e143421a90a8cf94a31674a1123c75aac6922b22b6ac561aafe3f4fd8": ["ltaaa.cn.", 0], + "02bc17ad0cb17705bf9b2b70b294a16f2f3ac3296af9b674d3d16248dda31fae": ["paycomonline.net.", 0], + "02c0255f794d627891c49b50bd991d7b18f18135b2599f123998ea822860b731": ["skelbiu.lt.", 0], + "02c601baf9c0110defff8a41cabf734d0d31f531a35c3b0bd36ecbb98d82f106": ["vodacom.co.za.", 0], + "02c9b50952f4002162920a050bcde293cb5a3f546af9f9705bffa5f400a2edb9": ["somoskudasai.com.", 0], + "02cfda4a4cf6d1a66cceb69528f281d0d5f08237f128e6c0fa351006acec82cf": ["fex.net.", 0], + "02d96064289d3f0b4f0bede6ebe0fae33ab483ec74ff72c2b3b0c3e5ebcfa3cb": ["xju.edu.cn.", 0], + "02dab0b7832c13ccd5f3c6a20caeb9477c77868d9ea6c6ed3a954f79584949b5": ["byrdie.com.", 0], + "02db9213ca7ee8a092eabc9ab37e8f039460551ad7cc7741c1547dddbf3be5d2": ["dzexams.com.", 0], + "02f37b3a316f4fa79ceef49c1e2d14976349d196c890840c71f842a34293994c": ["country.", 1], + "02fa0703e4683f83e01835e37e1248548ac49d74f5e1f1de4c8d923254310338": ["chroniclelive.co.uk.", 0], + "03061baaa94adc02403e38fb74a2cb9ed4ed395e053a798d41307f0f7bea32b8": ["ralphlauren.com.", 0], + "030651c68debf37ada9ca8e2a11ec7974bb032556cae364fb91d7dc6f1b16fa3": ["sindonews.com.", 0], + "030912e6a12894cdbd13c6597e9b58add55cc1ad9ee1f3b43b1e4760b577de67": ["lifelabs.com.", 0], + "030d4c87e44d4696f40d6827625116372557fdfdc267a2ab651bbb32625ffb0f": ["megaup.net.", 0], + "03166276bb435eecc83f401a0462afa09bc34f0da7789129ba03d5231d1877b5": ["ohio-state.edu.", 0], + "0321c30ecb4902f94d5ef0f0b90be49a22e269a3a726cd489c81441f4736eb50": ["theme-fusion.com.", 0], + "0322981e65d1da5ec9ec911d114c72cd301661d1dae742df496ffe8d6ed159ee": ["dot.", 1], + "03269d5e926bce48a5257a93c32e4f1a68a7fcdf55746a5fb17a86521fca2d89": ["nav.no.", 0], + "032a28f7e6bf513c0c82b87bd6d701382e4ae43f3327e213abf216d3aa8f8f6f": ["loverslab.com.", 0], + "032c9602d475bda50c394ac5a1bb1617732ccd2a58c6f51e03833dc99f170325": ["skin.", 1], + "0334043b5c0c779c640f60f00f5380cd8006178467aabfe9bbb7f996f9b12943": ["wageworks.com.", 0], + "0335935c215b6e7d901ac4535835edfe23da4cc686935c9474fd076989294236": ["gayboystube.com.", 0], + "033db097bea446450c8207d09305582ec79a85e07369427ea065ff3d4401e8c5": ["kred.", 1], + "0345277bc2f03893ba3dcb728a7efa8c3bde4a9462e0ba88591b59a812a66953": ["royalmail.com.", 0], + "0345c1c7390d9015227d812ebb6a64128db4322754fdcd28563570fbe89a1fd2": ["diskgenius.cn.", 0], + "0350355b2ba1a53b2f4c883a51e3ec8d8f55d51f8f38acb862fe236248179de9": ["csob.cz.", 0], + "0354ea4d35b49772e9068b805630397bad907d143bb63113a51db90125d6bc5c": ["blsspainvisa.com.", 0], + "0356716b4521fadc5407cd54bb1572663c8edf1c638ea1556553c6c5bc31b7e2": ["soy.", 1], + "035a13b3d5d303879dc9e5b5d6222f8e8620004b3a84b389db1ec8908ac73f58": ["southampton.ac.uk.", 0], + "0366226b9aa3adffd32ebd9ae314fb71dccd4fcee3abaac4448b2d8671ff05c3": ["catawiki.com.", 0], + "03716e3ff797179884c81f6d39032c304dbe986067c6017bae34a702313ed8d3": ["careers.", 1], + "037da9c58fd66ba3dcbf75751ebfacb00a5aca7a7fe84ad4a0141d0f0a1d8fc4": ["oreillyauto.com.", 0], + "0392702accd85e10fa0706392c00864090ff9ea339c9ce1f372584829c456920": ["mikanani.me.", 0], + "039278ea0a093c55e3e14659a9ca608c55acb4007f5d80814566a4a782cad76d": ["build.", 1], + "03977549a971daeeda9373b462191c1140f94b66219509ff0a4c024b56f229df": ["deref-mail.com.", 0], + "039850e2c345532ba06f1d780cdf503db95b3e0a9051077baf50c85accaf3f7f": ["ankiweb.net.", 0], + "039981413ce248049553e3cfbf704a246a4f71c2556b234d4a0119841e296326": ["origins.", 1], + "03a24ec6c3ef17e27ef68e6ed6733edd335280d73ca746d2de57ea0926cebf88": ["zulily.com.", 0], + "03a528588010061abc1ee48f3906647f3c24f0a4c77eecc543639c2c19e04b2e": ["ubuntu.com.", 0], + "03aaf9e35ca6dc64a29d70b4de5652c5f8de19df7cb37c99f0e08bd46ba9304b": ["kyounoryouri.jp.", 0], + "03acb3e3788c4a20a5b4cc3d73e75f25c05fc5df88b6219a5f6bb888c7154e52": ["tele2.ru.", 0], + "03b8e9c782f9dbcfbb398e2e11024759128bd2447d4eb8e58140a590b13161d3": ["smbc.co.jp.", 0], + "03ba5e5fa1691fd61c21023f40a1930b53e8d31e2625bb1f012c85c9d1110713": ["a101.com.tr.", 0], + "03bd74dff9cc252b2a61b0610dff03292ac90bcc52ff9d7f5477a651e20f1cd5": ["21vek.by.", 0], + "03be717d09d7e2d36b255940563df5b23b2f854f44fdf4af7d034823967060cc": ["sap.", 1], + "03cc1a1e684cafea512af5a545236f1ad640afaacc1d3b7fc1e00fd5c10295cc": ["mx.", 1], + "03d1e5715edb669df0a43d871ee427f705590e2774d970b079b36fdb776abdbc": ["saramin.co.kr.", 0], + "03d9092ddcb1c4faf6cdc2ecbc077e38aae0df7db63673a541cfd0381f5ce358": ["pl.", 1], + "03d989b90c067c21d54335d3ad3b9d3754fbfbc3d9ec47ee29736b21bace84d5": ["liputan6.com.", 0], + "03d9ea99954881e413d91d5125f831b8066fa28dfa9928e3a9bf55cc7d63e19c": ["openai.com.", 0], + "03e05b0b31ff8444bdcb44e8171a822499461bb90e7ac9e335523b6710f4cb59": ["benzinga.com.", 0], + "03e1ebfd55b70d5e069bc45f6ce09df9455fa7b79ed83f93431358bed45f10f1": ["hollisterco.com.", 0], + "03e3d983829942093c65c29548168eab12e8538e6d82464dc6512fce1d0a4b17": ["appen.com.", 0], + "03e8ece4ceada1f4d80c5e1e3f284096bc2b1e19d11376c06fb9fca238ad9881": ["yiche.com.", 0], + "03e8f6cd7de7e055787d7edeb761fc3de8fea3d2d6f5be283eb90c16ca0c402a": ["youdao.com.", 0], + "03eb7ae9f640fa595e7698b3dcb0e086e5b7b656d3b7d1498d963fed275aaf20": ["petsmart.com.", 0], + "03ed44559212f5477097b6977f9f3186b426091a71304950aca791888e34bd4b": ["hospital.", 1], + "03ed8a861ec38d9641428df67192ab3469e0c7c15143f178151c28113800bac8": ["zamunda.net.", 0], + "03f7c2b166aa64677b203ee691be290eb12b19881c71e64b685b857299502179": ["songsara.net.", 0], + "03f7da4540e7ebea1c99c71dceb7638056726b8616a0c8e9c441cd74b23a320a": ["cardekho.com.", 0], + "03f8f9ad44c81393a9d17f48bc9a2e35a5937dcdf495f720e982e0418801d675": ["shangri-la.com.", 0], + "0405c24daac66fb28c9d8a99322cdf076d7e327471c8740830ecdf0ffbeae10f": ["marples.name.", 2], + "041d803ab214d7b0ca7d932473307d20073d04efd71fca3b643c8a9097f10283": ["motor-talk.de.", 0], + "0425cfce630b934ae7a5e445a1fdb0679a5e0dfb5991d56eddab7e8a1782e04d": ["officeworks.com.au.", 0], + "042d66f7c382f7f57830f9c44ea33fff4d49caac2f20b1ecc07bbaa7a85fb9e4": ["akdn.", 1], + "0436498b283b65dec0ebccb9a6378d90954a47ea1780925086b21d037ec3fa17": ["createsend.com.", 0], + "043674ceb0f737751baab2fb373792700ebff0793f2ce2ced204724a9c77d859": ["milliyet.com.tr.", 0], + "04367a5a1aec3200eadd0c819d3d179183fe5dae9ceb6cbf38cc77575caf9022": ["monster.", 1], + "043a9c4ae4a400e4f223b178b7ee608e31c96f1c0ceaa7c88ad2f803ce2666f3": ["wanikani.com.", 0], + "043b06850a9377ff53d8b0fed6b13692d6f03fc5141d0d69e7f58d405e60b634": ["ahrefs.com.", 0], + "0444239e19a6b696f4a0a9ea060d2689bfcb74708da66507d0700e7b2fcda0b6": ["strava.com.", 0], + "0445d9133684a42f339893d23df33763d0eabca2e38b3725e9cf5870aabbec4d": ["udel.edu.", 0], + "0449682a78f13980f70707d31ed963aeb19486fa26ffbe667659747f9438680b": ["templatelab.com.", 0], + "045244d067da0060594e593da09efeaf1697176759e63b069c44e96a5c8e0dea": ["collegeboard.org.", 0], + "0456188eb26387507d28ab14e5741ba4ad36b5b3e0a74fe4ee64aae563326e9c": ["base58.capital.", 0], + "045856428bf1a8a6c1864f24b8394456424e19ac865324d4b6593e6ac390853d": ["tokyodisneyresort.jp.", 0], + "045a4c191b0bead8dfe91acbf543e96fcd581d21314c6b1ced96dc7356b4905f": ["startsiden.no.", 0], + "045e34809352376059e8a633648c9347bba39d5484bef24aabc383d803a60c4a": ["diamonds.", 1], + "04620cbf4898ce6c74104b7389a2744bc44d7da499cb90379819f9aaffd2e98b": ["memorial.", 1], + "04669c2df948e0868aa5e6df09395828650ae25e6e6e2e2723531061c098c8f9": ["uakino.club.", 0], + "046c8d48a677178145fb489da279779ce7a0dff7070fc84ecc30b3859546be7c": ["jb51.net.", 0], + "0473425db1a30e75a4c7cc51cfffa2fbbf38e91c383b2be39347b6f86fefe446": ["az.", 1], + "04758a03fa40a532193bd9720015f3b63ff65d708a74de6b8f044ba9d2b7b428": ["hatelabo.jp.", 0], + "04787b05aa41ff2ce317bc9b1c3b58b620a992769a83697d41c6fd1f62e8cdf5": ["avistaz.to.", 0], + "047edec67cdc90c96085ed56deb96ea5976af06f27e0cd91b71ad4b8a1d4989e": ["alibaba-inc.com.", 0], + "048c6eab4ac67ec8580b13eb417f97608fc0ce9a09b3dda4e6f223d353986a39": ["tamilgun.news.", 0], + "048d1858900f7218d8607027ae48118e9626fa3b3be31f67b93f9290dfd9c127": ["durban.", 1], + "048f5bc2428c6485df73d56b4888c4c0a8467d228055e8dd7d82d69997f93f76": ["zekamashi.net.", 0], + "0498a5145a7a0874268056c41bc34b6f2e8ffc5869d59922438e7a63502156ed": ["thewrap.com.", 0], + "049d9f3c6cdc60ec78c8ce9981390228a38c0c80be3e6d65a3ecd2ff524f4121": ["steamdb.info.", 0], + "049f5988d5158f7fbd2562cb3135511315ac2c92070ee4a66e547ce04c571e34": ["literatureandlatte.com.", 0], + "04ad91e01b40ce8cd083eba1a9216c565b30c271a316f374622b90b154d90758": ["thespruceeats.com.", 0], + "04b827b5fc18cb94936efd87cefb58ac9c0ab83958529091f4d2b7d21ee63e3c": ["nettalk.com.", 0], + "04bdcb7d534242923da4bd186081038f07dc1bd31be870d2cd21e43226d6a6db": ["blocket.se.", 0], + "04c06d74cbfcec5654b3b9e4666799b41005df70490dac7f0af577bb4f24d480": ["merckmsd.", 1], + "04c510a5d365492d34603ab656fc776486a52dac2592f2c6421522266660060b": ["lensa.com.", 0], + "04ca71f7903a9c699aac7fc662b2e259dbb91d627179e10c6d30e3ada2e82b97": ["verywellhealth.com.", 0], + "04cbaa604a493e12f797edcad4ffa5b3d3be26bc52069d5e197408f1098d8424": ["dailymail.co.uk.", 0], + "04d09c567a71bd1053f4727c7e330b6ccfe8aeee4a747a487210292b3e827fd7": ["webuy.com.", 0], + "04d5158038ba9c151e1bb570341a1acd073378e3c47a1642297badf34dc0ba37": ["quest.", 1], + "04d96e8db178f8a839de68bc65852bd823a816206555e2783f7fc05b2a22ecd7": ["flightsim.to.", 0], + "04de20f2f6f027099e825f6db4050375c2e5710633be776142afd93da13ee328": ["bharian.com.my.", 0], + "04e2a38e2d15f71948e907ebaee41451eddfef44e284a13b01441a16d4da99ed": ["gp.", 1], + "04e4c6e73e1c550cc187e65995efe502977edd55c44c49efd8091ddf33c8857a": ["imobiliare.ro.", 0], + "04ef0f8c3b4013fe5318758b577d6df4086c81a2a59ad7fcfb173b8b872d776c": ["hqew.com.", 0], + "04f37bd3d5e4a7d647345f5c3476b296796d0ebea7f820f9d781b6e5febffb67": ["kaggle.com.", 0], + "04f3886dcca1074e91788f4205196472d370424e6bd745f079086827357203c3": ["csc.gov.in.", 0], + "04f5f482a9f2e559dfccb56ffbf1b5b54208f24b08a3cc5531b8e5c24575c1bd": ["mormon.", 1], + "04f642ed74b29f35e9a412c81957ef34c3221541775b3586e5b0270a3f4706b4": ["submarino.com.br.", 0], + "050406423c488c7fde4d3a2a308f469138b371ae1b4d5a5d2f77aa4e40754234": ["awin.com.", 0], + "0506c3ae4b1a8035f117f3bb1f118ea16ada82e93449257d5a06b2b8a79fc2f2": ["pravo.gov.ru.", 0], + "051e45daaaad5038f4caf1f5afd44eab3f23f690e0343546438c2dac9a7cebe3": ["easyen.ru.", 0], + "052b76400b24201928e6c4e9e5d2b6443091ac513881ae0a5af582c3ccb4d6c6": ["tunein.com.", 0], + "052ef811fec6b9713d09875f80a24fb87653e90f804f41b0ad1871fde0792046": ["zerodha.com.", 0], + "0532cae022bfca6876d64e4a1b342f7a19cee1299fca97aa2fd03069628f2b70": ["menshealth.com.", 0], + "0532ecc8b68ea77f01083b161835c4f620ead485b4478a94c357897ccca1ccbd": ["ubereats.com.", 0], + "053ce61c08092328be44c705e19e0458c9fb52080c9713dd80b5c85ba76c6f18": ["concursolutions.com.", 0], + "0540e786ea1bf405458e90148542b70bfd3503cb73efd2e05dd362811020d0d9": ["apkmirror.com.", 0], + "05493bdc60e9a79c86ad11a06d5d4f8022afa88d137dcc463f424a81be347320": ["24chasa.bg.", 0], + "0549e267d21f0827607435e44f1453fac71f678b116c6a19a5746db604c0965d": ["shop-pro.jp.", 0], + "054b034f5ccc03cd6798b7eb436a2f64d0f6d8c44301989151afa9f13b8b6e59": ["france-visas.gouv.fr.", 0], + "055bdc4ffa30af2d16046312dbb05c7a0b110ab0669e6bc687990e55ae60fd23": ["world-fusigi.net.", 0], + "0563fed2d11ccde1a41447c0183ce52366c4e04470d4ad64764b3bdf3db7c567": ["paycor.com.", 0], + "05655b50c3c96be16e71534cef156e79535cb1c3f0157fddd139a0cd8db5a26b": ["eur.nl.", 0], + "056993e36dd5c1144b47dbee4f6e3b8897365cfdb303cb12f5150796c9868633": ["mariadb.com.", 0], + "056a5839acf7913dc7becb8733c864780cfb78cdb19f9552f5d5bca01ed32dbb": ["geekbang.org.", 0], + "056c29f4558b21ae808cfcb433d69122ec80861adb1df4f187d006b8f062f9fe": ["al-sharq.com.", 0], + "056eac588e2c091fddbb8cd69286da125c01befc6a0808847fc3d40ccd4c4396": ["ouest-france.fr.", 0], + "0575277731d4cf23b3169504faedb5c9863440ec838a5ac00bffbb9846ae7d3a": ["lease.", 1], + "05754716daeae03e98725a3bd4789c4de28d8340e9ca5de819b6ff8219aab43b": ["sportybet.com.", 0], + "057b0889c08014ccac70810d62f646e8bbd4f773a1b96b24aebc9cec9f907b62": ["utk.edu.", 0], + "058605537148f694d4f31706a437eab4c5ad54de4d540e3dba716818e31b616d": ["bakuelectronics.az.", 0], + "058bd894d127d0455b0e9359e3fab3eeeb26fc8c3d20920abdaaf7240e70a8ea": ["usnews.com.", 0], + "059161ceb14ca23cebcafaca84db8350b5010eaa014c0bd84280e43c6f539148": ["thepiratebay.org.", 0], + "0593f88e16bc4d036119e281edb5111fd542f2e65a388e771d55ee5d7bf38058": ["altema.jp.", 0], + "059a5d838f14efc004ef92b7d8dd8f566a4480c2bd4ad30921f27d1169ecdbd7": ["umd.edu.", 0], + "059bfdf409d63ef4f1136cde00b5a0127dcf113012727552f3c316f992274ab6": ["uschovna.cz.", 0], + "05a18518982c3354fe7909cb6e9590c0ed5ef8b02b98b46c961851f1567dd68a": ["howtogeek.com.", 0], + "05a2d44bcf19b16ea61b4f8f1e3e9dd86e52873f5565f54b810298be5d4bc1ed": ["skillshare.com.", 0], + "05a9f31f4319ce0b5bb0111d13d827dc939fac24aab4329a9476be7dd0f3ba92": ["yourstory.com.", 0], + "05b67484b750bcd52fbe6d7d288692dc7f46aed644efc9776f1c9ab5c1c76f13": ["blockfolio.com.", 0], + "05cc4c6c57397c118600402aefced021c8e007d7a4e3e564e936a9a966c8d37d": ["arba.gov.ar.", 0], + "05cd98fdecc74538182a123f3d91e031833da3e9b0a2558d6652e48bf318a1b2": ["dog.", 1], + "05ce824735b039065b98c36c9bf57508938ccccd573a72ba51f969d498ff8c87": ["yazhouse8.com.", 0], + "05d213ab4c477dfd3de179892f85c35e15f2ba561063ba58ee65a80f036f73fa": ["broker.", 1], + "05d48fd9aafd4b87d9fff5027025e1972b731f39aaf8c7425f7c5f94a2f58062": ["redshelf.com.", 0], + "05d55dd3450cd72943856c0144d785bde489f0a95b2252231f38ac177a4e37ff": ["ucdavis.edu.", 0], + "05e32893889c4d4d24a5a421b5bfb5d51976f89cdf9f30d827c461a8a970d633": ["hongleongconnect.my.", 0], + "05e9102e5a302ce85daf82044cd7916e22a8d6990e3848c6f5b25c5d09f5033e": ["voto.", 1], + "05edcc2c58cf5d4cdcdc8614be0c727ec0c38585c48a8d0c3793fe10352b617b": ["purelovers.com.", 0], + "06028826dc8d32ea89729378adbadffecfb0ba6202f6afe384a41044769bf58d": ["shutterstock.com.", 0], + "060d8f0c4834f785c258d12b5d41d2bcc6c9ba82b2018a7499763ca2fa244a6f": ["ogol.com.br.", 0], + "06139afce65a4a2f84bb7031faf76e26fd4261a0a6f6471eb1cc68bd7e0e9163": ["xn--r8jwklh769h2mc880dk1o431a.com.", 0], + "0614f913cd0a862aa02d2f22102c8bede8b7c2d984f0fa4f14378586b943ec61": ["sportsmole.co.uk.", 0], + "06257818f6cf77b1c92b24d73758ebac701d0bd357ddc76faf34df9a9783a0bc": ["sankuai.com.", 0], + "06267b8c261256f8d5f277ca0be4fec021a307b77912156bbdd24283d785fef3": ["tejaratbank.ir.", 0], + "063210735a5663120cd93c44f993eefbcb02a66a25d1295cf0522cb2c6c3cedc": ["nfuwow.com.", 0], + "0632fea5444371d2e457b164a2ab66ecdc1fb855a183976381ce3befb6b6e5bb": ["desdelinux.net.", 0], + "0636f28827251ed09a8747552845713a425820afa1db267ce05488ccd85a398e": ["lever.co.", 0], + "06384758cae2c37cf7b3bb8847ee0f50cc18cd3a7c0048df19afad46b5369d58": ["pinterest.com.", 0], + "063cfa96a52cd212b8321242a400e725855916ba8a2e2a9e2a72d9484ecaa1f6": ["gamersky.com.", 0], + "063fac3813c5481faca7147c420335d079d04a0fe1ac0cee9e66a20e3addb9a1": ["upsc.gov.in.", 0], + "0648ecd02b1bb2f321a0799b552ef64d66072f7ce7fede37597ec91520e2065b": ["elotrolado.net.", 0], + "06522ad64499c3e4489cd6526d4446bff8885778184c356e006b97d230150562": ["repelisplus.app.", 0], + "0654815e61549c1a9458e37cd2ba4724f8124bc31f99055fb6eaeaf6b4c6b19b": ["vocalremover.org.", 0], + "066ec3f583f0f2a67f9a2f36c745af5e3c26d54a478165bbf229280b2ea824dd": ["ipms247.com.", 0], + "0673ebab9336476cae593426c988a54a048f3473d4df8eac205466597a6116d2": ["informador.mx.", 0], + "067430d7e2e369e07e105ea33ad26874f25e3b9cc8be1f859383098da132a207": ["downsx.org.", 0], + "067c51c54236d67816a63e9920a42fe5ded09212ae38102795c0966ebbb9495f": ["maijia.com.", 0], + "067c9fc67a0c86a8195c1e64bfe1cd56baad84b77c97c5d0420cf57456f6ec09": ["webcam.", 1], + "06829a07f275f8c01f30333e8ab5877adfbc0368af0d02fc15906952c00059c7": ["ccma.cat.", 0], + "068ad467f6ab1e8ba90eaf1debe8bde69a009bee5cccedba644158b35c26a734": ["tecnoblog.net.", 0], + "0690fa26a7c351b2728a8ea13dae5aff377bde554c5f5966ca2e2196d656a029": ["bancoestado.cl.", 0], + "0691147fd466ff3dbe6d6f554163a54659165cf49f22c6509979061880b7cf17": ["convertcase.net.", 0], + "0691cbfc4915baa4ccac704d4e21bfe69b06b1acdce045c6cc7010e28b37c85e": ["calciomatome.net.", 0], + "0692208adbd616abed37f6b29e341e65353e501f9b64ebc5a2024694d9c90aed": ["indiegala.com.", 0], + "06b01ed987999e35bd1d0f9672b49f8f7d54c3d883dde39d41a2b1758405dd18": ["difi.no.", 0], + "06b825520f6b385c69f8aa626faafb1fb3711820b666841f3deabcab567ef3ef": ["tubidy.mobi.", 0], + "06bfd7a6139f0720020b4d245498480684322920113139f36b54ad4a6dadb1fd": ["2ch.hk.", 0], + "06c8924ae3d9864072ce3f6d11c3ae74fa4fcbd2ea7a59862b957d8617658521": ["twimg.com.", 0], + "06dc7b723df7e80e6eb0904adedacc8ed3b906580b7494aa8ee1db155297a2ed": ["biblestudytools.com.", 0], + "06f0673940682be2a2d7b3a5c52ee0553703fefc049a86026e25794989a612d3": ["rlp.de.", 0], + "06f833dd1c0fb2407f572c9d9491f30a6465c4b21c0deb7456480aef7375f903": ["myperfectresume.com.", 0], + "06fb2c309c60a7fd524a9ab804c71286e1915c7dd74d0524fc51241ebb5757e9": ["rarbgaccess.org.", 0], + "070147f47de45781fecdf41977d7da04115dba8d9ac6ee09aac57afd6f2800bd": ["brainly.com.", 0], + "07021a0cd0c65815a972dcf819d8800c7fcacd7e12c033fc097317010e5b57de": ["jobcan.jp.", 0], + "07049724af6c3a3b0e529debf2ca021652971cb7b758f1e5ff316c4f6bd53259": ["nalog.ru.", 0], + "0706b9e6541f29b617b73a9d757d192ed975c6c4afad21488078417baaffef0a": ["cloudfront.net.", 0], + "07197d3738c210df30d14cf0c019703b22871e07dcdcd299c4ecf7a351e62af1": ["lianjia.com.", 0], + "0719dd59dac5ac0e13ad9c59e71c9d6bb34d7ac39c1dd5f5eb69922cb2ff3e74": ["indianvisaonline.gov.in.", 0], + "0723e4a87376260c7ea14a03748e5e384425bc761d23a5809da95cc2ad22784d": ["fieldglass.net.", 0], + "0729b6de06cbc0f96b9d8dcb958de3c12ad5ce79664f86cd1d99de63f42e2d75": ["itorrents.org.", 0], + "072c0c595fa80b508f8230eb631608fc6cafd97690ba333234cd6f10cda89795": ["bsi.ir.", 0], + "07388c42991f3b2ad6aa20df41ea7680df34ba664140f7dc1550c9cf5e0a6f28": ["photos.", 1], + "07390252696c94a930b8daf5d692a88a77b98048f223d9a1d722dcdccfc9ca3c": ["louisvuitton.cn.", 0], + "0743a067701f0dd261a26cf47002de1de30a7d187074b7973837d679bf6e6223": ["hec.gov.pk.", 0], + "07471cf9706fbc1ee648e83bbb34a8268a1c534725e921e913c61440ec258d16": ["bizjournals.com.", 0], + "074785a37bc031a55169b12add0a39d166384e3020857c39c8e5ef950d366d7c": ["similarweb.com.", 0], + "0748d4e1cb8cc47a65fbde1f825016c3ac1bd2cc7332518c45a6b7fea37d7543": ["organic.", 1], + "07519fe207119f2f95a15d92331338f4b4da2bb413f008100b27798bc41042d3": ["exhentai.org.", 0], + "07523da482226d94f01fbceb5c94a5bb02b152f4fe6ac16c7ac497a3b1a9f175": ["azbyka.ru.", 0], + "0753937388fa55062e23c5e0cb59eac822016c2b5b97b43110e0cea02af6b45f": ["18183.com.", 0], + "076285d383238feb3749b6f6f17e61c9b936f4aabdbaf6a7ca83c583bebdad3b": ["ip138.com.", 0], + "076d05903aa2b6f0cebff10ada2e9ca2c23580a15ef877d557694b86f8a0617e": ["nsfas.org.za.", 0], + "077a2917c822ada52f211d83b55f335ef4de3395cabc2592c69860a49b158a31": ["foodandwine.com.", 0], + "077df9b35a4464d8a6f0916a7b9538068c00339a5dac321c99ad4783ea6161bc": ["chinacourt.org.", 0], + "0785670a249112088530455d87ab5f900c08a61d50f65e6d2c5207f0b448b178": ["iranhost.com.", 0], + "078c20015387e37475df427a08471d96c4f26f0be996e8b7eca1e6b7dc818966": ["centurylink.net.", 0], + "079ef99f567d4dd6e4c4e7ab45599fd1c974e12f9a7cf66941610af7b2f4e07e": ["tsuisoku.com.", 0], + "07a814fbb3bbc29955c87e781323e02f37f4ad3c30610696823339df4ac1fce3": ["britishgas.co.uk.", 0], + "07ae16c82daf6a431acbe6293de1467d9e6cbbc335b4b8d54e21409dd12bb02e": ["job592.com.", 0], + "07b367d3fb72f0bbbf1c5b64d8f5d0c97fbdf20f51724ee14a73023a1ff2e600": ["incometaxindia.gov.in.", 0], + "07b5808450f5f1fbe5a1f2102f05cbb13e05182175a7eba4e9d3362f547690b2": ["nme.com.", 0], + "07b6f09d1ba4d6d268b1744c0fafcba96907f8f0a9375b6e703b33e010d71529": ["1688.com.", 0], + "07b98037964fabf402ac9af5bfcfd2f44375fec304ea525f44795a272f4f0c6d": ["eurovision.", 1], + "07bcd1aad3f0f40614d93d0c7f35334bcda5229858aa7846bc3b38d600552d64": ["mta.info.", 0], + "07c7a27925c23e6f17da2597df9aa6fa2e008e0668d770bbbcfd49260aa89b99": ["cults3d.com.", 0], + "07c909cd53847125312c9b48a00e05f0fb70f8e7316b62fe7c2207fb9f8d0d8f": ["vocaroo.com.", 0], + "07cb656394850fcd5aeb8e29853cc100109921852188369f380e8e785a93ad87": ["mailerlite.com.", 0], + "07cc0a037ec98fc61c84eeac006c1015bbfa359d0f7d7c5803dc8660e789726c": ["ssc.nic.in.", 0], + "07d10ca629ae4a489b3875a7a43cd444dccd8e59f3cb30d6e2e67126cd566c51": ["leparisien.fr.", 0], + "07d75822d56c0fd276b9bb034fff4edbb2b806fab9719be18ab160e1078a81a0": ["zenithbank.com.", 0], + "07da38a8c059e8c31ae8ed4845dc3ebb2ed0a3a10f3afe112fe706c16c941142": ["tlauncher.org.", 0], + "07dc76b2bfc5b1e54689a7b75d5d78d2eac97524d5d3236bb76f0f120752a7f0": ["sanook.com.", 0], + "07e8f5d00e8a0034b01eb4bb6e9fa179756860870289bd9f070c654828e5b3ef": ["downmagaz.net.", 0], + "07ebed8b0c2dcadfa149e35fcfa6ae8dff3b982c509e62212c5ce04dbc7e1acc": ["animeseries.so.", 0], + "07fe3f64551d8a25f28e2cb4e459be294684d08f5bea840c4f7445e3f0f15f9d": ["elwatannews.com.", 0], + "080447cdba7fac3cccf8f9933e14cbfc818c11237ead145902a294b511e89192": ["auspost.", 1], + "08158c58f4f2945a8a8965f9056ce1b62c8e633d208634e9394ff882c1f945d4": ["nexves.com.", 0], + "081926ce16386aaa3a6e21722aa778621b4a8e6bf57af2d8ea680e21f45a55c7": ["imgflip.com.", 0], + "08195fe30efad41aeedf1acacfc25efa4e572a3f1f4ad264e70b7b98f171deea": ["playvids.com.", 0], + "082a8c0a68b9f6e52a09f82d657331335e3bf5d67882c29189673512c29b123f": ["taylorfrancis.com.", 0], + "082c725d298c5af1dcbd42f2e326e15697d5b1e6d832ae71b2845f4839b17754": ["xueqiu.com.", 0], + "0831847c2dafbbf59d01c76ba9a23a6d765c93022513404f13f3d2272c333555": ["france.tv.", 0], + "08327dce69b5645660d8d373b6d8a27d11f4ee616e7ae78293b933c894328202": ["site123.com.", 0], + "0833db9d666735be8038c24bf6067907bd6049fe746d711b8fba0d54118e5d12": ["boo.", 1], + "08386de40fe30b3b3c47fd299ab6ca58c3cb35b27877da2402842ddfe4c72299": ["sakshi.com.", 0], + "083a81c62d087d49a2ab70f43702e1f192b427f7fbae1916730d4cca61a2243d": ["eiga.com.", 0], + "0848a666683fd6665f7040225f1f298511025c19884b0d2774fb6870de9cd12e": ["larazon.es.", 0], + "084e60c1898ad412b04682f89f0101a125a5dfde52502cdae7ebb31d1fab5df4": ["ktvu.com.", 0], + "08551b8e11422f01e6fe5f5660e4e8e67562d5a46a4ae869239fb063422f3597": ["mapquest.com.", 0], + "0856ea09ce0dc5b6abb6fd41c84b22f3aa5f68a16b13aaa400084fb7305ade6e": ["revistagq.com.", 0], + "0857d97842bb861bb28b28e87070fb5a728e6ae9ebf83b82f8b36866258e9b48": ["dealabs.com.", 0], + "085848e96d64ef8a13943168e325beefb8671dbe42dbb7b1033a765e2f349acf": ["msg91.com.", 0], + "085f214998e51e5ab684bc58700102d860202df43b7f1ef999f462ed344ff1fc": ["ckplayer.vip.", 0], + "086972342412eace68021bd4e38401e375dc34977543e9c2a9af690bcf991ef7": ["tfaforms.net.", 0], + "086bf89f06cb16dd41684efd4add3501fbaeccaee2b4bc2acc4cb4545f7f539b": ["urbandictionary.com.", 0], + "086c626e42f776881d4d65858d659cf6d216eedba20e2a3e517325c7d920bc7e": ["baixaki.com.br.", 0], + "087429ec2ec592ee3894c0d893b1d8983362cfd37a7876929fae6565c4edee78": ["pramerica.", 1], + "087c64584db8fd085c28eb142336bb0d7834d0a4e28302ec90f9d152771c86a7": ["tecmint.com.", 0], + "0897afa304927c286d2028e29169e53c6c8ebfdc9ddd20836631ed170cce6f7b": ["managebuilding.com.", 0], + "089ae207472b572efc58a55df85c5bb41fb50fcd62bbf9bfd9e18fa7380056f4": ["dokumen.tips.", 0], + "089c71e01398b30f78e0a1e81aaba8145b0d7248c90677de1c09866db818b4c0": ["rotowire.com.", 0], + "08a21cca1028a010b4687657cb1e4faceda6249e3e406d666af1c74904f46833": ["med.", 1], + "08a4f1b843a1a58e23a8200edb3a5052e1083bedeac5630e70b7848c1cb045a2": ["aps.org.", 0], + "08a5341465bddb438dfb5cd1eb0153df3f0f99a7ba72b6f6b7b82469debe7494": ["shiksha.", 1], + "08ba005185cda2709b3f1607436fbd1bd8e21926873e95345d970c376f2e4f05": ["lotto.", 1], + "08c3ce3eae817e0bf936dca822b6af8f32ddabb40586d6b715ab6a24e136e73d": ["immo.", 1], + "08d67ebe04e8d5506f4ee56d644a3c97774080c8a4fb1c2b78fea76660fdbda3": ["netsuite.com.", 0], + "08e319365e16a59699e8d1336f1655bde3dc94de079da21b124ae015902405dd": ["index-education.net.", 0], + "08e319431d3170a8fe2e99b5416b14a503be9bff8e5b8008f05740a4a7012a04": ["iran-tejarat.com.", 0], + "08e6abf501d9f8d87d4dea8e2954979640d235303aafc7d78c528841c145dd7b": ["theatlantic.com.", 0], + "08f3b69567b9d73463a4b0d9c0588e05491c2520b70397f9e66f83e9ef1b6600": ["chunyuyisheng.com.", 0], + "08f5975cc800fb716899317781cbc826eafe5521999a9c443059bdc57a7c5df7": ["vtube.to.", 0], + "08f6e2e732e097ed9fbda3d7272d01501f2428361163f3f1f7cf1e8be247e8da": ["et8.org.", 0], + "090694591a973b7b023963b572494814c80d42083e822240567fa75424ef2b53": ["theoutnet.com.", 0], + "090a99e3ed3e9eb3ccdf8bc32f3ce9888b0f79af6021eb788de9e0769ce67ea4": ["lastsecond.ir.", 0], + "090d90f472a3bef44e357b4569d380dc16db46bbd24847307a7ba2066bf46fdf": ["bacaoo.com.", 0], + "090e6f1c7acb58d0b2e712b1706b055cffa41e4a67188cd98325e7877c73be49": ["collinsdictionary.com.", 0], + "0913fd6f964306da46155d3264c864ab32c8187bfecde1791494674022ed52c1": ["soundgasm.net.", 0], + "091fbfc534d1738d9778ae247595124ce8d518e27c2ee0c41aead0864ff3d749": ["flightio.com.", 0], + "09256714734ef998ac71d7dd29409359469db09a36530c9519ecde641a97c37b": ["scopus.com.", 0], + "09279ef72087375d8984593292ef0812570dff06f2e374196bf48369bbd0641a": ["khamenei.ir.", 0], + "0930e68726caaa392158dd96a37a1daa116c4294e58356202ca08ade0f1ea651": ["12up.com.", 0], + "0936902c19aa863de5faeb6aa8ea05405e6363f2eb231cee82326b98f8911c48": ["smart-lab.ru.", 0], + "093aedfb922eee2a9d2e35ed05c35b93c159f7f8cca1e3f7192c2cdea289aaa8": ["at.", 1], + "093ff7b212c720bad70217227e3dbde028cde4d7ce266f577070ad2b50336c8f": ["grabcad.com.", 0], + "094850a67b84671fa1caef5f38ca122ca7687a892ca13723759af2a4399eab9f": ["xn--pssy2u.", 1], + "0948f3542630cb240b8630f3fc9d89347ded2348f5b61b67437a61af04f82056": ["xn--9dbq2a.", 1], + "0950e283913804bbf7fa715bbc774fecb6f6e3ef33cbc8be027971cf4b2c6b79": ["no.", 1], + "095443195ebcd64f134461aa04d797d4fe2d2f33c33bd4a688b0b1558e62c080": ["lunapic.com.", 0], + "0956dd482b320852664f3805f596a52ae820cfa8af9d95b7233a2c434bf37e77": ["samsclub.", 1], + "097131d44c02ac02c95efa96b54c7a80aaaabecd4ab3162def3268e3769b0f97": ["mangadex.org.", 0], + "097e7760df09284c8531b48ca8217a1edbe96d6f8baae6009e7258d370451d8f": ["swagbucks.com.", 0], + "0980cc2bbe71bb9892da6e51e8e4dbb037d193ea76a4027bad69a8419ffd35b9": ["lequipe.fr.", 0], + "098583059d6e6322fc68d698d89376d2e13ef6f15c86d5e38a91eca7f87ecd5c": ["artstation.com.", 0], + "0985f0918cc3a9eacc563f6295d44e95e25d61f6aa193e4bdab199957537be0f": ["paysera.com.", 0], + "0995fa02f6e49fadfd738c667fcad6d2bab2ef3fa61d70f5a8969da61e84a6cc": ["copyscape.com.", 0], + "09b038c34349f7604af5db7f1d65487bb9d568d57fe90c444997c1c4f0d6630d": ["policybazaar.com.", 0], + "09b5178f9e1dfdec8d168c5dda73726734e03fadcce07aae9cf3849d1c1bfbf4": ["cuchd.in.", 0], + "09b7eac0f95394b3ee308bc79795b7d60753d9d147575b8277d0af3cb45de899": ["pahe.li.", 0], + "09b86ed088ba77667634682730f8fee35576eaa6eea1d619f7c8c033ceb72cea": ["bedbathandbeyond.com.", 0], + "09ba1b04af013af9dc71ca40ba04ca927a14ff8631acda4db037fbcdb88b7902": ["tukuppt.com.", 0], + "09bbff467275658f4c6f31c69d0ccc17314daca6e593c365b29e53dac09fe258": ["internet-start.net.", 0], + "09c309f7531495d0a89b121031e52d66b9ceff866d2f75c381c7b5efb0b42ec4": ["irs.gov.", 0], + "09cd761443eed41e3e911053f8125542773b12103243cf493110205d80d73d05": ["stboy.net.", 0], + "09d2506eb2f304c5f2a28c752e4a3d9c11dcdf435f6eae9c5a22f73f95211d5a": ["grancursosonline.com.br.", 0], + "09d27aae4b84d959f8c7119e85c876e714e0cc31d0cfdaeaf1c5ce1fbc7f9bb2": ["infoseek.co.jp.", 0], + "09d54ef5eb48fccff0aa060c171faca936e829f172364789daefbfbd21e3a4dc": ["gamivo.com.", 0], + "09deeaefa467639b95e62b44b05a177fc70cad8b42f8ee9e61e0127a557067ff": ["landr.com.", 0], + "09e35802d86ffbffba32ad025c12f57a072f7a464214fb29d0d765d9efb96109": ["muckrack.com.", 0], + "09e4b088be91f74ba7ca1f3a0bea17aa5bbc5a69c77f56fd485d6d1ced575931": ["tel.", 1], + "0a2272005ff4f359552cea2a65e0deafa51057aa43f3b85ee55a045f27eb5fd7": ["chordify.net.", 0], + "0a3a38092a9910a94f8f6b38943d9f5fdb42a1e244d008feafb4a8d86ee9c297": ["heavy-r.com.", 0], + "0a3c4112bb82077174d271ac99d4ba72598f2ae160043c2ca8681102db0879ef": ["parscoders.com.", 0], + "0a3dea759aa8b15e46ba00abba031f42162ff55088a04bcce6d0c7a6e4b1b88c": ["filmix.ac.", 0], + "0a4503b7d341798282f5d857c9f6720fb58c94aeeaa65d928e21089fde819cf0": ["watchwrestlingup.live.", 0], + "0a46a20ccc653a97eb693fbcf4ae1ac3c1955efbaeed8ba4642a66a5ae1094a3": ["castbox.fm.", 0], + "0a4a692f5a2b332e859cfebb766e6509e62cd0c72149af4567c64bc4dc3f3be2": ["linuxquestions.org.", 0], + "0a4bebb07275816f7e977af79fe9f76461bd2c7e9b094f0b0991c30debd87bee": ["johnlewis.com.", 0], + "0a4db91b96c83bef1fd83aa0ea1dec052ba4b822a6ef3e0485e28d469aed3814": ["dagospia.com.", 0], + "0a5291b5239a5f651453eb1889e81c84c12a80b8d6b474a77fedcf5c35ed465d": ["brokensilenze.net.", 0], + "0a562c4e22bdee0b36449149aac8c488f7ad0a932c6eb7a35f5709a0cdd638c0": ["elisa.fi.", 0], + "0a5694abaf0d5981456c20f4ade33499d6b0359cf806d8a08a0643bba80c6cfe": ["soliq.uz.", 0], + "0a583aa712ecec0cef10c6b2dee1ed537e640bc13658e85297696cb0a73f1a72": ["tupaki.com.", 0], + "0a5f1a7cb87726746b13510c04901ba133cbf047ee94992176f2d93dc26a6d01": ["sainsburys.co.uk.", 0], + "0a6af67ccd57b9f559441ed999cadabe6b1d2066cfb93f6a6647699de25b32fe": ["bridgestone.", 1], + "0a6d3d26dd59b8ea43375512d14e0967b4bd7175cc71359630b0ad03ba64c2e6": ["soccer24.com.", 0], + "0a6d955355f50c6f363d5a8233bf9e1e05d4a1ce90d1b7fd5c9fa9b863f78dc5": ["ssrn.com.", 0], + "0a70f8282371cbf64ecf888574d1c82254b18a7c945e67746f36cdae3abae346": ["ott.", 1], + "0a71d7e1cc49a4ab5549407623f91d04f0844560e32dc51ebd029a6b2a3bbfea": ["gadgets360.com.", 0], + "0a7891d1fd51328d506005a47f8dba125393bc8ec6f15992c672a5ad23aea5b0": ["alfabank.ru.", 0], + "0a7feb74bca91c927ac51e00e714ff79b3a47f95ee708a58baa4b5946b160e11": ["redditmedia.com.", 0], + "0a7ff7eb3f508d5adc8b91bbb4e4aaf2f15926052f4ef173c5ec6b3f633c5254": ["delivery.", 1], + "0a808687de3563b0e4bc0935eaff443c88f8427a36cba4ffa1aaeacd5de0756a": ["fresenius.", 1], + "0a857c16f4e0245ddb5876e7c91fc98e4b2809bb79666431d91fb7f96abb7768": ["eet-china.com.", 0], + "0a88349a9f0eec1726b92c20a669b2c674a7bd46caa6fb1cce7eefc587e0e875": ["downdetector.com.", 0], + "0a9dcf577951f0c0b0bd38306412606f5422e5f35a2799024d1e6f0fc87be23c": ["irctc.co.in.", 0], + "0aa349fa4e7a75afef649ae5459e07563475e7e6d43ac85e9cd8e10ce7b650b8": ["ebsco.com.", 0], + "0aa44d8b1308bda69b13459922c09c4a83ae1ad1adb36cc6430bad6ae3feb8f8": ["ludashi.com.", 0], + "0ab0d2d1a2040a5b39528ae666b7d9c4c0f2367960c6b978e125755b49307c2e": ["porngem.com.", 0], + "0ab3dcc8662d0d222085b56243f10ce66ee1f5249f541af052537af1bc58af65": ["bajajallianz.com.", 0], + "0ac34b8ce5cdceafd517f1e049d052de285d06b0ce6d0b1f0ed99a6c701cf188": ["sudoku.com.", 0], + "0acf1841058c5c8e8adeb3a8720705574fdc3c5fb43eeea4527598e49b64bd90": ["epizy.com.", 0], + "0ad429b2c81bda08959618433879af9577f2589684fc53cc82f18203b987c48d": ["windowsazure.com.", 0], + "0ad83ba93ac9a5001c32c416e76ec00e80a38bb7f0c5173ccbb4be12fd0c391a": ["netvasco.com.br.", 0], + "0adcb1c63bef4094159e864670e457f27fce13621d05543164fdd5c5a98d814e": ["unina.it.", 0], + "0add0ac1f754ade6c6716e3de3a199d614b4fa7d295e15fac0a323a6c24716f3": ["everydayhealth.com.", 0], + "0ae40c82ca742c838f314864f2915cef0ca35ce46fbe3904aa92281013e3b72f": ["plex.tv.", 0], + "0af39297ff1c7afd3065182fa370c89950966b3ec0a0fbd7760f59948c103b52": ["mihanwp.com.", 0], + "0af45ae1f7c4360e7a43146326e42938bb14221709c7946ec056e2e77858ce1f": ["twittervideodownloader.com.", 0], + "0af52ca89f39fb69499cff6948dc69129c3d6d41d0c0c9beebe74e951e1acb34": ["guru.", 1], + "0af5c1912e567142ef75e7aea5413848abf673854cee7383cadd95c68f1e17e1": ["fiu.edu.", 0], + "0af6da198995e570658cd5e847b485d260c923471f647ff8b3953e21b45f4a6b": ["thetrainline.com.", 0], + "0af7fbf1216d2bab84ef4b2352695e81c9904c37831234759175d5560f7b9227": ["edu22.info.", 0], + "0afbb72a87aa2488737063aeb3ee177ff77ae77f7dc5f9269eeeba766c322129": ["18comic.vip.", 0], + "0b005b1f6a13d4e8955becb32209832231739e0c46d822af33fb5c80fba76f84": ["eltiempo.com.", 0], + "0b01c155b9d005083e77b0ab83aee1edf3b2ea3e34cab43e9b0585ea4bdf8aa0": ["yapikredi.com.tr.", 0], + "0b115dccd90f870454acdfca2d75c71f978803e5e335ea7a0d7f6eefdc7d6768": ["po-kaki-to.com.", 0], + "0b13dfcc00ea8ecb4d130256024fb575b3907e836912ffe4f98d66c09fbefb8e": ["bncollege.com.", 0], + "0b170b0c5f58707d22992329c5c6c0a2cd60123226d91822363be42ef56452f6": ["buhidoh.net.", 0], + "0b2106258528f620b8d2de6d9112247bd7eeb31e19dd9fe6b4135aee1028048a": ["pay.", 1], + "0b227a243db0a8537c4d48dd74212a00ce95ae6fad0d4650467dc39d3baf7c24": ["360doc.com.", 0], + "0b2665ea07e3dd8beae47b713a1acc8b23fce17cee47fe7f894797ead89f2ca3": ["springeropen.com.", 0], + "0b2997b34104be70af599976ef83fd1c46c0a4a3f5fc0f0ddae6399356ac9d5a": ["iha.com.tr.", 0], + "0b2b760047825b4c78b0f4fc1b9a01c36c297ffa419043643ae9472383bba086": ["industrybuying.com.", 0], + "0b2f02f907bb1375652854cc8f566c6014226f6a74508c37de4e8a8712b4aad7": ["zimuku.org.", 0], + "0b2fce0545b45cdda5d7bac2f6fa10b0a8bc53b96ff1ddb6b81b5104be5ef7fe": ["shop-apotheke.com.", 0], + "0b366a129377873e9fc093e138389b8e342065c91ccabbc790dfb7a6c7d6779e": ["phoenixads.net.", 0], + "0b375cecd9c8d6f0cb226346e8d15018538ded0be5e37b97206bf520f6c642c3": ["gama.ir.", 0], + "0b3afeeca757ec2acda7c202a02ecb5f600b37b36653b71b2c93aaef7c768a4b": ["wendangwang.com.", 0], + "0b3f460047c6d438438e966512be812ebf2428a17a5ab83832a77f1b622c4d6e": ["pdfescape.com.", 0], + "0b54c85436392c77682275d42256fe3cfb0d5331c7ad103c9c7ea03ee27f3974": ["pro.", 1], + "0b58858ca03b7768054f7c2464b48379d95a3200bca2b8e0f16a5063f26ab2df": ["wunderground.com.", 0], + "0b5b403efe951369e6270914cafccef886f6c9dfbcfe3483362a7d3ab6c6cb26": ["spreaker.com.", 0], + "0b60fed831aae2506dbfe8a08e4b049ec24d88ee302aa2780f2f8b82ea32bdc5": ["videohive.net.", 0], + "0b628aacd433e2306e4a08014968e2f2bf47469b9cfbc2d43f9b743429194449": ["opencart.com.", 0], + "0b641fe9d98f285d908f4aaef1f163c335cf7f1abf7da09052bcfa1ea3aa45e4": ["premiumbeat.com.", 0], + "0b66ad8b30fc114c1bc0cf8c74c2f7fb6880c12c985271f6feeaf55a692c3c2b": ["panerabread.com.", 0], + "0b66f34a539684a244192982aefb5b78ed26e9a7011842084a7a4c9004f0f7ff": ["jining.gov.cn.", 0], + "0b6e52a273d2cd79904cdacd8e18668163d39f35bedc1be4864da7701a6c6cc1": ["expensify.com.", 0], + "0b6f305d129e3c2597c8a8153528a08d031e2a120d7a4cef1baae1a4c064c135": ["huawei.com.", 0], + "0b6fb5ff94cc17bb052f2a4419bc7b486d8225a27e0e27f925ce02610b8e9c52": ["zhutix.com.", 0], + "0b78b50d2c1a81f9aa09566cbd5df169aa365133402c2af1aa5043191aeae1f8": ["ixigo.com.", 0], + "0b7f0fbcdbcc4782b8932183e32a244385e20c1564f4f28cd96aa56902031688": ["jpopsuki.eu.", 0], + "0b81b92afc345a9a15819bb26ec5f0dd7a638a1d7ef66fd156fdb34f76c7be9b": ["exportersindia.com.", 0], + "0b8b4652afaefc32af018873de66e98ca8815d9766d949788cdbb4873548ad8f": ["cimbclicks.com.my.", 0], + "0b91553392c827e84724343a990a2b54ed075a0fc80f853b19961edeac712dc8": ["verivox.de.", 0], + "0b981d51b12192a92403d9509e5963fc26c6ed271d18850d357ae58965e826ca": ["omni7.jp.", 0], + "0ba29d8404792b30fcc8de0ca21edc3e834ad23aacf878fe03745ad1e61ff3f8": ["hacg.mom.", 0], + "0ba2f7e315f55a69b8ad92f60fd282e23b16fe13392df6705ef896fd092f7cc9": ["shipstation.com.", 0], + "0baa27fa540238619f9023dc081ae873c9844a0427b29dbf993955d2c62394e9": ["getadblock.com.", 0], + "0bb08d72dd0e50d28615b6986c4d8e379cc356d46b772ca65c69573b2e2aa4ea": ["alarabiya.net.", 0], + "0bb25cfe1848a965694f74bc8be384399ef09e2f4f8da81e0291e7f8815256a1": ["baku.ws.", 0], + "0bb437ec97cff69dd671ded5a5f962b02d526952cf963651e34e425703c612bb": ["force.com.", 0], + "0bb48f323c946ea2f82a7b453e054e7ade05ee031f0ac399ebee4f0079719ee7": ["greythr.com.", 0], + "0bb6db2bb8247d2084f1139b24cb6504aecf2337a7ecdbe18d56f30832e30a4e": ["cpanel.net.", 0], + "0bbae4ac21b31512019195ff1b1a9c0912dd787c70648a4145dc50af41013cce": ["blue-tomato.com.", 0], + "0bbf55e5ea592c3d3cc2bd8a2e8e6a354ed859f3e75676cc1b9e6bb74939ffd5": ["inc.", 1], + "0bca370656f61ed66044245c89211d43d9e9a8b7e324f9f69bcd689b53aa1f34": ["cdc.gov.", 0], + "0bccebe2854ca4f07e13aa0cbed8c06be4add76fd3f20fc2d2df1169af4bedef": ["theprint.in.", 0], + "0bcd090389c7bedb834260484a242198cf5c34787bb3d7c4385dcf4ca9e8a811": ["adultwork.com.", 0], + "0bda99c33467bee0b0c8519fb13671bcd416509e1c4dd65787ad85d485621473": ["olhardigital.com.br.", 0], + "0bdd811253dcd4ed620a36995060994dd5d84f4656a24a3c5daf1b7b271c6fca": ["guancha.cn.", 0], + "0bdf0601ed3c7fec7676f4cd23c8364bdb5daa9f1b306f8127cc79f62d59e63b": ["cpp.edu.", 0], + "0be181678175157d350ac775d697d4b49c63d5a0533515bcd34d3864c92b1a23": ["letyshops.com.", 0], + "0be54a32cf4b2be2190857f7647dc7ee49a02854bb54413951aedb877d271d5d": ["co188.com.", 0], + "0be57e276a352f99db4ef936841f8022351d13052aea2dbbd21f82c7c77d9953": ["otsuka.", 1], + "0be6ca473d1b29f4f0be6aa1b627084a95f8634bd1dbc45fd201e70533143df1": ["qvc.com.", 0], + "0be7d95937e5a3c387e20faecb7a2c8ac2050e73e1652a53a9efae9658544be1": ["icons8.com.", 0], + "0be8b32790ef53d6373c4f16a4b3ec1b6e357b3caaa877c72f403e5b64d64481": ["tasvirezendegi.com.", 0], + "0bf05685d3f37edc581b3a79b9ceec437009c0027f8fac5f6a47c6063196cbe6": ["my-personaltrainer.it.", 0], + "0bff317a79e3c919e9ac6c32d92fd0fa8a36a3e7ec0f7834437c5c11e28da969": ["delhivery.com.", 0], + "0c0872c0951df6fd99fc8211b94b4ca758db4d4ba5df7f549cff32de1aef3e31": ["dynadot.com.", 0], + "0c0d2d8f2d0d16aa58d1450b9ff78034fb9feb65bc818229ecf3b3eeba21506d": ["skype.", 1], + "0c140f1d6fe2fd6fc77464b849c02c96f693c3cb6c180b12cc8e5eadf253989c": ["mondialrelay.fr.", 0], + "0c1e7a6a950cea0c33421085aa5ff978493162c6693e3e6caa2c73cf1e08c0d3": ["support.", 1], + "0c2336d7a4dd3b1f2930b51b3ca65c3503c4b18e0e4e73260089444828efeb61": ["spotrac.com.", 0], + "0c2d578d67e3355315256cd9febf236a970108e36e5352464e394214f09541c5": ["17house.com.", 0], + "0c38fac4e85930a74efc58800294d3dc6514887789bed1dabf94aff4b06c8d8c": ["abudhabi.", 1], + "0c40c793bb09d8ef6800f5d630fca56ab1328692cbbd942e4b55d6fc548a1638": ["contrib.com.", 0], + "0c43355e174a58d4ba15369efb6c4aa0d0f440cc72cb5a6eaf6f692419ec4838": ["youcontrol.com.ua.", 0], + "0c4784cd4d479ae7dfed28b2753cbb1db4ae3dda503c2b81192376e511189ecf": ["lezhin.com.", 0], + "0c512b630c88225fc36ec1358dd3d9f40e7a7c7e315db346fa47f7f3c7677f4a": ["pcolle.com.", 0], + "0c517112c3302400d5b3d601e4e4e428e44cb7975ebcad0491f88f951c1d244c": ["cntraveler.com.", 0], + "0c53ff3c4131f9152fd31bc31d40bf9f7c6a3ce26184bc134c2fe2f9a6151aff": ["game.", 1], + "0c567b0dc7abbd1cff9c63e79d0b1680318712e7533297ffe4db662473b35b4a": ["systemrequirementslab.com.", 0], + "0c5d32173065f0c182b95b0622aab18bb5997dce60cdb0319a6b7df77b7d7a33": ["fontanka.ru.", 0], + "0c674a98d419c8faacfdbac04f880945bd7249b768197f56688ec29521465e44": ["documize.com.", 0], + "0c708acc7924a7c043bc3718759f6395d9ef3933b69ec7f3f7305ae9e8f8c4ac": ["americanfamily.", 1], + "0c77de1f134a6a576daf7e3747fb511fe24e503e79ffe9ab7b418355f2117597": ["wikitree.co.kr.", 0], + "0c788235335dcff16f49e8edca6d5a7766a10d9c098daace1683cbb02b94e1d2": ["rozmusic.com.", 0], + "0c794320d1e604fc566da854207a0c667c4fe6c525a768b823e2645fc2bfe4e3": ["tf1.fr.", 0], + "0c7f52352e773b6cda601184031d12057331cee649f59b6c289cdf8da7e5b8c4": ["pinduoduo.com.", 0], + "0c848ae6c1ecaa878effbc40acfd2aa1b4174f12d9837fb842c5a08a9a52e324": ["lanxess.", 1], + "0c84a4a09c5b4e635216e7ca527885decb5ab2e0f85347b85122fe0a9db2952b": ["legalzoom.com.", 0], + "0c8b9b5d5421056a5f7ecb9e980d99075e464fe83b99d7bbc1910ca926e8e5b5": ["incestflix.com.", 0], + "0c932c197a4028ae7a4bfd87470f61d05f8dc9481f67300c9564d896ce0f26e2": ["boozt.com.", 0], + "0c9339989af678c23890b14703336c52fef1d31d2e4d62aa0ed356c554e5a669": ["xn--jvr189m.", 1], + "0c9c58d8164ac8b005bbae337340ddda6b5939ca18786519a08de00faaf9cfc3": ["rbi.org.in.", 0], + "0ca14572e84973efbfd04d5cfd862bf4780c4588471441a5b34b0d8b63a9af2c": ["ibk.co.kr.", 0], + "0ca5919f5a62c0a557f74ec1088e529199ae6650c871fdd156923cd933d7cf42": ["seslisozluk.net.", 0], + "0cadcb1fd1f8abff2de96cfbf84f729b9b942580675a84a4418317efcebb8559": ["link.", 1], + "0cb0376339d54424e69d62bf3793ad4bea2b932e94e3e13415c824fce3620c6b": ["unt.edu.", 0], + "0cb77567d6584b538e8867b8b8746aa0397b5bc78f7bce25f25372b36c6da6c3": ["ssllabs.com.", 0], + "0cc0b06eab8afc05144d02d3f6d22cf6928e767b20f1581ff211e8aad84e77bc": ["realtor.", 1], + "0cc6af18ba19a81f6698b04459385e807ed53ee9260723fa5d9be6ab738a46be": ["capital.", 1], + "0ccb2907935fde1bc0515c334b0f613626a158c2c0ad17f406ed6b0fea35805b": ["moa.gov.cn.", 0], + "0cd23711a840a2c250e103f6c2e1fff9e2516261b352f3b56378274d2cf739d2": ["outsideonline.com.", 0], + "0cd6ef6a6e57ffef5fd25d8f74f5b42a24f859e15776ea71db1b93c213f90f34": ["firestone.", 1], + "0cdc77657800f579add15e9376e1c955e71a0690490ad5019f09b0c8b9fb12f2": ["reneelab.com.cn.", 0], + "0cdeabc9610eedeef3bb593cbab5c39898bd0bf457a7187cbd040cb7ea08cb6c": ["openathens.net.", 0], + "0ce2d84da3ade261e74f0c5637f71abf6d9c89005f8b047bdedcfffc6854d783": ["polyv.cn.", 0], + "0ce8911ab0734336696366d2b0f380d82f15728cf209e4f25bab0e335d7e8b0a": ["shrm.org.", 0], + "0ce9dd839e92bde63c4baa4ac8162bef265df2ad5629aa0a474fa8684b3daed6": ["mdbootstrap.com.", 0], + "0cedcbba22785a29818533b438d8b5d5515dfc45d7cebaebd26a137d0916401d": ["koeln.", 1], + "0cf255b24b42821e914ff1aad97a60fae5a58c327feab50930fa2629f6d7856b": ["bms.", 1], + "0cf6130c648aa033a88ff751623aa92f6305979e41b401f50a2918b30e99fc33": ["cinecalidad.run.", 0], + "0cfbfe0e26eb3f2b872d4c87fbe6b4cb195e54d7e3b9f4c3a68a587f3efe0172": ["yangkeduo.com.", 0], + "0d03b576986b1d1818958474fd60dcff23f48671a612be180ae630e11b0e31c5": ["nexus.", 1], + "0d09bdbdbd960228c11fc035321b53d987a58c242324d018c6efd38e459fb7c5": ["uaeh.edu.mx.", 0], + "0d1bb5d10651ffefcce5e899832ed22c3ed4a23e6be468b71baa95218f19dbda": ["ncaa.com.", 0], + "0d1dd84b059aa9c059841c39c59f0d4ad7a30f3f200a2629c3bb1315d82ba427": ["polycount.com.", 0], + "0d1f7585f813de081bba9b845601063c11d977a07a5a107f5090b0db90017d34": ["njupt.edu.cn.", 0], + "0d260b83207a568f70dbf95a3a7a800048f2f7be3d3607ba37e8aea5421df255": ["tribunnews.com.", 0], + "0d2629ab1f19b30557147ba053b520f08dbb273b3a101d21047ba8e6e8d03367": ["kabum.com.br.", 0], + "0d275e80f29bdf7af496a23a4187a88222f22760aedab2cf8dd36a7150c43548": ["nikonimglib.com.", 0], + "0d2be173eaa28401df5d94ad7b4d385c8942404d2ce918f33f3d455580489c23": ["nikkan-gendai.com.", 0], + "0d2bf182625bab908eb1c13c3fe9571bed66bd949fc1d87a61b5c26d7a98fdf6": ["moovitapp.com.", 0], + "0d2cda42e68e0f3f2804f8a491ee08014b2fad74a83a48e0b9b2b53d0f216adb": ["theverge.com.", 0], + "0d2d2169187f29de5132ccc3855bbd280ca558caa0aad04a76ba1b5d61cb2741": ["zarahome.com.", 0], + "0d39f1aba9db8ca8cf32bddb2275da8d21bc9afbcb5dacf2e7847985e6740769": ["bilibili.com.", 0], + "0d3e8a85475c857578edc4ba439a0d349a8fa74e5b787c760af00eba70b62dc9": ["yummyanime.tv.", 0], + "0d3f7a0d5f46a529a6fbfec74f2e257b442f10a2750a5eeb153fd53242ca56fa": ["bongacams.com.", 0], + "0d449189c19255448401cb73232fd2f443cf7be85b1b9a43ab4226bb8e45df8f": ["teradata.com.", 0], + "0d4de8ef9ae7c97aed3bcc3a126ce2be4644526a661393c5b806cead2eccf216": ["plus.", 1], + "0d4e2b0ada97d44181feca22fbe44ca6cb74f360aaf9c58423424ac6b8b06703": ["imagefap.com.", 0], + "0d4eea38fa2a981e1efa32009efde5dbc65d3ee3887cae45341501092ee2077b": ["35photo.pro.", 0], + "0d5f00668de2490cd8ed1b59590e76baaa5a46e24d5dff840d8e493846c35f58": ["acloud.guru.", 0], + "0d65c4ca5e1da5eeef9f82ba355026d216a80176913d69054fcd521b53ef2b47": ["questdiagnostics.com.", 0], + "0d67fb96f7082e277cc1fc2b194635d3acea95afbc4e970cc8173bd76d4e9477": ["drivethrurpg.com.", 0], + "0d6c3895296ae6b8f85b3262f819e770d17b7e82848acdc7b3bed61502352109": ["manongjc.com.", 0], + "0d6e3a4d867448bf67a50683c9b84ff1bddf2c3ccbead91f9f03faaea68aaf9c": ["catho.com.br.", 0], + "0d6e79c96be15d097599f0945991a0b57f0dda9640dee53d217c6c60ae1f890d": ["tianditu.gov.cn.", 0], + "0d73dba3398b6eac3ae1b17c852434f927b03d0a2ad1be3d204bbbe1e6c0f73a": ["voiranime.com.", 0], + "0d93369328bca23c3cb20914fc8accc92f177235def3d32c4b1d5113f3a03fa3": ["cms.gov.", 0], + "0d97e593fbbc069577cc5c583d2e7e487b4635d73d36ecb11160f52c58dc3915": ["mobi.", 1], + "0d9bb5e241b4bd0019dde22de4a1f22ea7740c73b9f41850575302f73411c337": ["hjwzw.com.", 0], + "0db7ddc91bfd42285cba5473590b4445fbaa0e82a98cd315e796119899e37db2": ["nottingham.ac.uk.", 0], + "0db86b409c97535ff5915b3d11d778db8d15586f50980a6d964730c6bff59514": ["myjobmag.com.", 0], + "0dc091ae665215a0210d596091cdc7d84d1c2e1e24c3edf318e729ca5c623d9f": ["arabic-keyboard.org.", 0], + "0dc6da94b26fc4eee276b93046d89c1819f67ed519ee910b45a7bb72362675e4": ["hqcollect.net.", 0], + "0dc9b7abd78c7af68e9480b328e0bf30aa37d1a756a4f076f0c20674703bb589": ["0xxx.ws.", 0], + "0dcba6ad9087cc4618ed48d158eeff08542072bdc464b9dcdb6dc6a6231a8569": ["gallup.", 1], + "0dcda68b9375751b906280cdd6bb42aab18155fe1c23f5da57ca1d0be7c8bec8": ["topuniversities.com.", 0], + "0dd4db03d7c67ddaa48fa2f5cf75c692fa4b4dbdeae86f80817524bf61dfbffb": ["pizzahut.com.", 0], + "0dd695060016a04d251149f16accd1584b61ed1e2ade83401e5b4815a2d2ac4b": ["asianetnews.com.", 0], + "0dd926af187356a140f59a6ee086e2168ec8427d8e2b2986b652547efb6a7e5b": ["washington.edu.", 0], + "0ddb5339e67d7ebb59cd0a21b6aa8cb466d664826c0f9d99c2d09b00811301c9": ["malavida.com.", 0], + "0ddf215dd0ffaa3647b2dc1e74e08812f91e86386f0c28eaac16d5468b7784e2": ["rosserial.be.", 0], + "0dee18e06c3707d0c344a0068f05820dab56fbbc28e79796be366ff15e7fc715": ["bravoteens.com.", 0], + "0df745d93376aa12cb4d163784a3fbbd9801dbea5dd9afbc2318b5e00557baad": ["mapbox.com.", 0], + "0df9f2dd583b346c38d26fcf4de0b072c7e324d1733303e85644fedb8856b5e9": ["ondemandkorea.com.", 0], + "0dfbef3ea14bbfefe9346ec79aa8de41036a5cc801b640ee710b9fb0faa792a9": ["buymeacoffee.com.", 0], + "0e03db061e9b427c348c3c7c5f5efb56d8521361294a81f2297b1bc86de1b1a3": ["assistentus.ru.", 0], + "0e0614ed3254fa739ab1b0a63eb6dc5a64f284b72a9fee2f6fb5864a75d59a1e": ["sparkfun.com.", 0], + "0e091cc74eb1d5438f2019a68751a7a381d1569be28ccd3c06474fe9f42dd0da": ["kinokrad.cc.", 0], + "0e0b66ce6fe27f13bc37f134e6b25c036188122353306662013660ce3adc9558": ["muji.com.", 0], + "0e0e798ad4e241ddd6cb172d5aa88b6b15ac107bde5ef5c958e9699239bcdbc9": ["kfh.", 1], + "0e16bbe9095a0c04e5edfd777a24b76c34492dc6d1630086435c807b267a0cc7": ["style.", 1], + "0e202ca0b2eeb0db8929efb231317c4d4b0130e4a4f7627a5075815d759df93a": ["fuq.com.", 0], + "0e23000836fac429a3fb03d9f03e91525125f9b825264333f8a85b8c0d64da41": ["applytojob.com.", 0], + "0e2e869fc12e209088d45355437e80a1e12637b8c04868ac3ef8d7dc2c4fc641": ["doctoralia.com.br.", 0], + "0e3587f8c8e3a49ad338a910ab614e91d7a0e0765e1e355a37acd87cdd5f55d6": ["taoguba.com.cn.", 0], + "0e377091520839f39290021250d4b4f4756a067d185243cb6be2d8b0ed4f3ca7": ["netacad.com.", 0], + "0e3929a356a0b467a2a98efc351988557d0e3360ee5bb6a42b222a44c3c3a8c4": ["business2community.com.", 0], + "0e3c3a5f35938bda080c10b68870f03f8f7b79243f437d2b78a2ef4b887dcec0": ["cashnet.com.", 0], + "0e42186ce676e4a83a1abafd1b5224e39bd2230949698fd0d7af3f25b0dd885c": ["iskur.gov.tr.", 0], + "0e509299ac26e89c7e869d80d1eba03e880b07b166803fa76651bf4dad22c88e": ["wanmei.com.", 0], + "0e5c6fcf9762d786a1147fda49cb4018db956f1c3f03f9cd5f05a6ec262bc1f4": ["camshowdownload.com.", 0], + "0e6322bdb95de66a55f72ef285746b8205ba0a228dff65036a673e6882522278": ["exchange.", 1], + "0e646cfdcc6a1430040e3b5418d99808bf9dc9a44be54d9d2896b3467b17b8b7": ["niusnews.com.", 0], + "0e660466fab2b3309a546c0e5d858f1e03a019cb25068ef1c5e0c5ab4f1a37b6": ["3movs.com.", 0], + "0e67f7b095a3097eb8efa4a9e219748fd023ed3b99f0e08c754b5a8386b5d140": ["online.", 1], + "0e7dadb005871cfa21dd1fbcc431fd25960063d45655ac6c821e141fde4db4c1": ["iafd.com.", 0], + "0e7efbc326356dcf48439f484b9457dd06ca93b38138828742eb2fe91e2c21d6": ["masstamilan.dev.", 0], + "0e91aff3c4880e1be7d60b0f771ff8ba91e47fa0d343fc19fe1b23e8589a4af7": ["ogone.com.", 0], + "0e9b1d0c7470f53f38df61322370d68bab1d97885d2ecfb47037d48ea106a158": ["imf.org.", 0], + "0e9bec2eab407ba612a3ca368753ea379783e21ad8f0cf5cd000f99669cd58ec": ["cfainstitute.org.", 0], + "0e9cb5a50c5f24e867da5c86d65a1a2fd324f425157a97c2311e113719a8da58": ["cathaypacific.com.", 0], + "0e9f5dbdedf2a0242c86196819689c9e596609ae21f792f9127415f2d1b2175c": ["aritzia.com.", 0], + "0e9fae36fbe2e1f684b8d7a89bcd3fc825cea507637d2c2b665bd9043c12b47b": ["a2zinc.net.", 0], + "0ea4c77ef3e41e61e471062943bd6b9b0769a1a1823e3ec891dea0b8a821e0d0": ["cfa.", 1], + "0ea4d176015cfd09898e475dd0b39e50683452b5e87797c2b520601dd123c1f1": ["hotcopper.com.au.", 0], + "0ea79b5d1cd7d3507ef7d3e748af5038524f711070c5c369761be33114a6d7af": ["transfernow.net.", 0], + "0eb18ac3b0d6efe98344b83793eb5cd4d734a1bed2bfb13973e8266f95c80daa": ["peta2.jp.", 0], + "0eb2c207860e635df4e3f58d3714ab69f517ff60a1bb5230aef6381a0f9f3d83": ["koreadaily.com.", 0], + "0eb4679893a5480b1a875979e9ddaca90afee24c53406616492076b9b2cab32c": ["mdzol.com.", 0], + "0eba328285007c5e51c89cff33771e6ae9d7dc0b06fcc4fea3e1f80b519204ce": ["wallhaven.cc.", 0], + "0ebd0481e220d6527af3144044b7e7083f17c15126b0342bd0e2d1e0dc779080": ["cdmx.gob.mx.", 0], + "0ec7a8a235479ed7bc3648687e512dd77c555bc942041ede811e8cfbe5f9adaf": ["able.", 1], + "0ec7f6b141a43538adf00b99cd5956ae42b3b0e8a4fab7d56f480f81e37da623": ["mikuclub.win.", 0], + "0edb2d01859bb0edc9c129159d2776530f3da64d7e4152a7e77e7dae07f45427": ["indiegogo.com.", 0], + "0edc424e0c9a1b737cbf13951cb1528f8508b4dab732c8d50f0882191955309c": ["oabsp.org.br.", 0], + "0eead4e8ce6c3aed1b0240aff526ece6966423f530470f8a1bce218a5d9fad96": ["celebritymoviearchive.com.", 0], + "0f03ae162190c211a60818afc33fffb1eb5c908b20cd09c2f3dfc4b8db2e7615": ["vana.", 1], + "0f0aa6a7bfeea87f950124f665dd1a0a17d6e20be8ae0d19a317c9006f7e7e29": ["musavat.com.", 0], + "0f0c07b46d908423d31f289bd39ac8d57a9c1c82b049c54e7fffcb6b10a09281": ["china.com.", 0], + "0f15a99c55e080059d2e10d341915043d3a06d69670a16dd446a1c18b24a4145": ["sportinfo.az.", 0], + "0f1c4a65d1d603a6901f5ea46675ca8214894c1f3f0fcfda80e20e5edff4232a": ["garden.", 1], + "0f259f2deb62b999b34dbd5deb085d73347bf245b4c20a588d11eabf51ab32f9": ["yad2.co.il.", 0], + "0f266eb3d62462e0db2f64c1e950b52be562a59273b047acd16ee24baa4845ed": ["adevarul.ro.", 0], + "0f2773fa9a68651a5b2c81fffad57420e0a189a75ba9b136352136be67db8888": ["darksi.de.", 2], + "0f311afd0d9da13618418e394abcfaca782bbe64e8bfdb5288a2302553d51f79": ["ac-versailles.fr.", 0], + "0f3a3baede3eabbdc020f96ba9a952bae0fa91c2e23c095be159061c3d6c5ced": ["torrent4you.org.", 0], + "0f3f3caf641f3a02c2fd1724cb0d4cd73795efbb64a4bdcf6bfec23a1bfedfb3": ["megafon.ru.", 0], + "0f435aaa33c7663f7272110082edac8dd8055e146bee16aee98cf9c8a052ffb2": ["jora.com.", 0], + "0f449cf2473a20820f46de3287d10e6eda121708bdcd5dce423bd5e4a396bff0": ["firefox.com.cn.", 0], + "0f49fb47e40e982f8b7041bfe3f7702f0aaa23e1df10f3ab7f14cad18da6fe8e": ["ca.", 1], + "0f52d06906a9a3f3765b8d296af9a2beb7d332191739abd2b1bd19e95e8da6a9": ["saglik.gov.tr.", 0], + "0f671528c403aa19c0094d5e8bf80d031ee40f1a5f2de98f0cbea03057db8420": ["jin115.com.", 0], + "0f6781b92ca2b0f31a66b3ddea544be4fdd011b195659514e2ce05874ef8bc1c": ["igg-games.com.", 0], + "0f6e1f64ef42674de6715339db5f5242b54ac6bc70d182290711224ad7e81f3d": ["jp.", 1], + "0f72d127bc89c195147e307e7f4756d33bef8e7eca73bb15771edb90bc9d6841": ["capitalizemytitle.com.", 0], + "0f7852d91956c778618b0483cc0943316ec164dc27fac1bf09c03de2615f1269": ["eci.gov.in.", 0], + "0f78ed5e58aa05e6c3f9f4857960afb88ab02ef1f36b14244506863548db8b2b": ["icann.org.", 0], + "0f7968f376c485f0e7a53843dc5f343b58691e1edb403c3aaf343d3d0529fb2d": ["voyeurhit.com.", 0], + "0f7c9a090da0b2045e2fabb922c7124a9e77572a4461f5b4dd0567ebacc00c02": ["s24.com.", 0], + "0f8492d8f97e6486202ffdd6be17b64353585339ae8411eed195f6f8887dc25d": ["care.", 1], + "0f86f91d8a646449b2b2f30488bad0c67b1fb897bc982b20d0aa0fd4d1562ea5": ["duckduckgo.com.", 0], + "0f8d99c8df207d5c8bf295eb195f9a3ee6a97b6e1f5694a89539e452499491cd": ["prezi.com.", 0], + "0f9f7adca7c9117b480345fd42b4d4fa040558033307188b19bdf5f174f9f24b": ["voidtools.com.", 0], + "0fa3b38486fbc86227b2a751728ee34116df0fb56b316b484f54906054d13ffa": ["towardsdatascience.com.", 0], + "0fa3d79f9a67b49068c6d14b909f7a1bb50c7991c7b5aaca06a3bd219f4cc702": ["socialblade.com.", 0], + "0fa8e0deca521e0c3a884c87d1f80a5339bfd5329ef3855522a58e46b99b18f1": ["kwork.ru.", 0], + "0fac50e7281e9b4baf5f0fb37b082708f76e77731bc49b78c4818f98f015727c": ["jpg2pdf.com.", 0], + "0fafb2c79dc2c7505e089d210b02d8a05f3ec09ded96f9d8d2c9a73fea8ddbef": ["snow-forecast.com.", 0], + "0fb12480052272bb07f553875a616fd6338135ffdfa0b9a10cf601e4987816a7": ["19kala.com.", 0], + "0fb522b4f878fe5f74ab401f6e814ee080cc1964f11f3dc5cb2b28c8b801e698": ["expedia.com.", 0], + "0fbadce46cf10be252dd9bd8e286fbcce7c901fd822b071aca884c96272732da": ["lefigaro.fr.", 0], + "0fbe68069c030e4b25295c03a2b521fdb179923cede12b3d35da43c6e5de9251": ["ucv.edu.pe.", 0], + "0fc155ed1e8acfabb88fe262221ce8ee2b5d82fe679511ab2311ce014ee3b654": ["genk.vn.", 0], + "0fc2e955a2629b967edcf9c94d4b1f772d6db717186cedc73dc9eb294c99ab7e": ["akbank.com.", 0], + "0fc921a0b80eab82df069ac107e25bc50e4283fe95ee857cbafeb86882d9b6ae": ["wgu.edu.", 0], + "0fca78073a44531768089d87a10c98cb979ad23f51097b743005dd4d228491b7": ["zhiwutong.com.", 0], + "0fcc8109d852c66ad43bc19dd21798d830e8fe276e88c35b509a27bc85b7e2d8": ["pigu.lt.", 0], + "0fd015400b02ba1187012146c5e2d92fdf622b5826a79cac4c4f24d3d3c3ce62": ["happy-porn.com.", 0], + "0fda18db0d0c355f9638a13a77edbc2afa878ded5c38a69bd973afdac46e6b7b": ["it1352.com.", 0], + "0fe2caec24ad0869bacce69a93cad20673549dfb34ac1d9005a27a6ba2a4e9f3": ["plejada.pl.", 0], + "0fe37a1f8578fef193bcba2e0a134b4d632fdf0b5b1045136917c4469529c2fa": ["floorplanner.com.", 0], + "0feb877ba6c45779c00db3a5c9fc55ff441a8de53a068463b46623310d17d6ef": ["jeuxvideo.com.", 0], + "0fed0a2528d549402bb2959d1aaa874cdfd4586e572b33a61e45c32a70ce7545": ["harborfreight.com.", 0], + "0fed9caa4c8a249483fc6b19218c0f2fe986d65ef9ed912351a1a9a3c673c608": ["upstox.com.", 0], + "0ff1d9d3faff41548855fb4bf937a41a45a56538a1c63be509c04180d10954a3": ["croxyproxy.com.", 0], + "0ff65394c8eb810812a74f527c06207ffbbb1a44cb45f722c89e27db63bed8cc": ["flir.", 1], + "0ff9880eeab29da3c818a5a2f13dd54ef883166ca7ecea8c056dc50022d7fa37": ["tuazar.com.", 0], + "0ffba2787026c0921be7636b783933189d370a0a833e613e632b5a671b1ef343": ["oxu.az.", 0], + "0fff8cbfa3796f4ac587a845893290ea8571c18ed75b25347d0a991238b36f0f": ["indiatvnews.com.", 0], + "10042062ca7f8f983e1d7c82099f9f14b393e56dbcc04b61a1e76840049f409b": ["w3cschool.cn.", 0], + "10059c133b27cf766c986204bc8b62a49e047bc7dbb76ede3d2fe0856c3825a3": ["1999.co.jp.", 0], + "10089a69ac2010bbe475a2ddde0168e89b963c9819e471e6a82bccce9a618db1": ["thomsonreuters.com.", 0], + "102270d5e1af5f3dc7854a07a4a1f011d3e347faf6a4a76be7e5de007e339c58": ["osym.gov.tr.", 0], + "102491b41b4ad99bfa010dd7a45d0d58af62be15978417946c186859bc395078": ["cbe.org.eg.", 0], + "102d4e01aaeb30c4fffa43f8ddea69427a87a9fed820d012b1c51b9bf346d5c7": ["utwente.nl.", 0], + "10374efa311ab4f65367c2cd18b569fd1bf4d161c535d52b952871e9bed278e2": ["robertsspaceindustries.com.", 0], + "103b5632875eb2d9cd7af412ecdbc99d79c4df9c58a3139bb761e0463b603a6c": ["bajajfinserv.in.", 0], + "103c5b046d1723c71a3f708e7bdfba7b7be36bb68485c3dbd6a87b78aaa3c645": ["yts.mx.", 0], + "104097b03bafc7506588e5b2d82d9b2e7f4eee25bcd6f188383ac069a0477760": ["gallo.", 1], + "10414145323772df86d67f55a07a80e989ba7d893f8fa9a79031b2d7000ecdb9": ["security.", 1], + "1051154fcc8158c91d23c9fda8e215b002bdb8c1ceeaf3e8131c47fdaf7e1d1e": ["powerthesaurus.org.", 0], + "10587b4919cc624541854d4a996792673078985586c15f6c9ab342b1dec80315": ["playgm.cn.", 0], + "1058ba00b0535a7ae54dcf82d0970a77dd66973a31596ebb510cd8f49bbc32ac": ["dcfever.com.", 0], + "105cc58773b2798716ec76bea388b218412060e7207443cd67b3bc8426977a4a": ["1mg.com.", 0], + "106275828700b490d33a5252a1256cfe5904bd789167074c7867d4b5922d3afe": ["airvistara.com.", 0], + "106bd99e18aa145fb777a1897c7647b87f27cbd665ce9bcc445937a2caf06db8": ["ugm.ac.id.", 0], + "1076d4caccb14af260e54f4ee8d9e5ff33f3e05f25571a951d4ea0709634f382": ["ice.", 1], + "107a6800373e51747c47fe6dfc31f43891d300b88aa12fadca6042654b6e31e5": ["wankoz.com.", 0], + "107fdb96d174d252ba4c4af1c43ae4c0982c6ac0512f639d4b6aed92cf63bbc1": ["hot.", 1], + "10819a4304389240d0607d01c2acb9319dedf2757a8d955b01549b4127d7e7c7": ["byjus.com.", 0], + "10844734d2302a82d23e2d2c1f47c2fa896e86dd5cfaa2173b45ee12b16efb75": ["uefa.com.", 0], + "108ad4f98fe3b40495b81251c0ae5a5df6f7c6b606f06f216f0f2e2cd08f7afa": ["seranking.com.", 0], + "108cb89303f8f340070d04a67e2ee5f65619e94fe2b103cc13f6969fea303583": ["realtek.com.", 0], + "1097cfd00635d61108c0d4c68b44efdd17e667948e5d048280bac0f01610f06d": ["cafeastrology.com.", 0], + "109831fa3cd67bc6eacf3b78e46011afb9b8aafbd639d374cc00c82b05993740": ["narvar.com.", 0], + "109deaacbb2680a128aedf2bb31d5fb2733af28ca9c54ddcfc1eaea3d36d60ff": ["seoclerks.com.", 0], + "10a246160c857bb1f8191ed0b43c3589ca1413d25bdf992dcfd09aeb7f9c2dad": ["settrade.com.", 0], + "10a43dff8fb703f7c535ea21fe11f44d38f821ce3058f8596bb4c501693cb2f3": ["babypips.com.", 0], + "10b1c8fa01a3addec39f15fbf2d713492a32ed204b56c552e0945a9a880c4e08": ["kau.edu.sa.", 0], + "10b429c6e476b09297f168e00d6d66a18f3928e7d92012e1d7802cd0c0bc9244": ["jbhifi.com.au.", 0], + "10c06c407f1a86d2ffe1593e6edcfc2e3b5929c6ff21ad6b98576ee5ae8b35a0": ["rozee.pk.", 0], + "10ce8986b741a075fd429869408fa680861306e122fab868a745a5734807acd5": ["51test.net.", 0], + "10d45346e4be9aaee1b81a97b6e76ea79be4471c574119a7e15073535835f372": ["sparebank1.no.", 0], + "10d5f5e3701e1aacb68a742eba341bb8bf2a09302b62f5ced7d1e40f2d7510ea": ["kun.uz.", 0], + "10d825a354d59cd201eaa30aed4116935fc1caa2f5a6ca46b55002b6c980904c": ["takefile.link.", 0], + "10db7c73ccb1ba442f5b36ea2e97928e7e7e0d767d273e0c8516b238aa35889d": ["thatav.net.", 0], + "10dec6d6e71a3ba7b15b561067e16dc20aa4a5e944ec05749ebdb601ce2cd03c": ["aniflix.cc.", 0], + "10dfde72702dfd175641ca3aebcbcbecaf310bc27a3252738975283095362d4f": ["pcworld.com.", 0], + "10e6d71e4782fc654ff7dae877b46d179166fbdef1e0c5c7f99c0b63a1cbd29a": ["infolinks.com.", 0], + "10ea9961f1d1e30fa6e87e777b4252c3e94e0c8e984684da26ee17a43a248f82": ["litres.ru.", 0], + "10ed05ebce3a9252c38cfbc4496cc2f34c1aac911208fe265691bedae9fc7e98": ["xnxx.com.", 0], + "10ed657bdd5e13f3906f8f6ad9c013962376925cbffc35ceefbbdfd917423cc7": ["webinarjam.com.", 0], + "10f71643988636227a7c74e36acd4b8e743cd55f75e38185d67e8d9aa135eb8e": ["poro.gg.", 0], + "10fa90b7bd3ad4e7b3625a63f43665c060b63cb64623b434c3467919482c3257": ["bochk.com.", 0], + "10fc6da9156fab33984a27cbd3fe3289390ead699af4946bcbb13607e319a6e6": ["yeshen.com.", 0], + "11036eabfc3a2ebb1ecad0ad3c2e900c42f59ac3b8bd0b0a9313d335b7f19ed8": ["kanald.com.tr.", 0], + "1104befadc2a6388cb8e309b4fde014b9668bd5ae7a0f2acec0478826e73c37a": ["tgstat.com.", 0], + "110da76f0a9787d5c5627fdd24504050d4e43f55dcc3403a241052de92bfd462": ["justdial.com.", 0], + "1112849573b4988b1d12100884fb4debf052b1297073d19d5597c8cc146f02ed": ["url.rw.", 0], + "1114b333b31265f79422922e5f87da2a108e73f30f35413441961a80d53de90c": ["sinonim.org.", 0], + "1119e2501892caf874595226a25a05d9f8aea7580854e7717854b8d8e2bc5f89": ["fcinter1908.it.", 0], + "111dd8b2d00354455d519f7b1f5cf4906df0ee2d4c0ae60b556d70d074b199de": ["cnblogs.com.", 0], + "1129f28ab97e4ab809b83b17c62093103ec05f6df95a277cb93f01818c10cb07": ["hackmd.io.", 0], + "1132ac59d684a802c74743deab0ceee4eeaea5435dd203f62b521730066efd92": ["barclaycard.", 1], + "1136db42701ce259208937853a62451360bfd873a130ae8a909d24af77d6b6e8": ["tensorflow.org.", 0], + "11380874503a4a7972c607eb7cf951c8981a746d72831de31c01e5c075ee5253": ["abc7.com.", 0], + "11420140aae27528c3e56ff8b26d9ebdeef188f5fd61bb4133eb0efe7decff79": ["dunya.com.", 0], + "114a621b42a1d572bed227a288181ec28048301887caaa800b9153e8e905aa24": ["calpoly.edu.", 0], + "116b1e28a5d69c855011a4448bc4675ac27d15fcab4f4553d17c5e6b9c50ea1e": ["alimama.com.", 0], + "11708454af1327de1750e5cee27cb8cf646f3a5903b80d8d34429723cf7508c9": ["yasdl.com.", 0], + "117c76a26a1dacd97757704e3f9197fdd8011aa699b25c28d8f62cbce1161627": ["smartcat.com.", 0], + "117d66d178163236d9b66a507399fe36e6f90e668b7fadfd378fbbd99bd14699": ["mofcom.gov.cn.", 0], + "11801b260b5c7ff8f71d810fa27af3b3c957b65913797d2a4854b31d23aa235d": ["agora.io.", 0], + "119063bd6e9f037982a141f4e99b74a2717595b677b4b37e0a6dfb8ea30310b5": ["py.", 1], + "119b94345a436841114f0fde8f4213fb80c1623e5300919f699f2310c7c807a3": ["sozcu.com.tr.", 0], + "119c92ec304bde7e73de0fcdb1a6b9b2beb6e03151c21ec8c1a325a472eb3436": ["formula1.com.", 0], + "11bd64af6597e7914b37a5df2415e51de83b94d2c4f81cbf1e4674e98f874d2f": ["c-and-a.com.", 0], + "11bd9844d79726e1ccb823e7a9a5f5be8b5b7503860cab6ec1f6e6db584b2db1": ["lifehacker.com.", 0], + "11c89b7394d4d0cf923aa618a5239243428d304b36be5ab19edc764cd8a1c21a": ["howtopronounce.com.", 0], + "11d03b6d5323dfbd7530747872f17b22248c0557a0485066c5994d29b0224228": ["kuaishou.com.", 0], + "11d67b5e028dca2c8454235316d9036853040e65214ae20a848235c4255df920": ["the-qrcode-generator.com.", 0], + "11d7e982537f4e5fe30d1773ab6bb3a0c6a56a1e7020b7896d494019735d63de": ["marmiton.org.", 0], + "11d958d30028888c36717d0c89d5f37b3dff7ad28ddab67f1d7808a4016e9b0f": ["ebaumsworld.com.", 0], + "11db41281057f0747a60015cc962b5e7d18f64c44f5bf46d156a0daef51f6c7b": ["r18.best.", 0], + "11e1e2e6407c57e501d0cb242020fd88a82a78800cfc50e1968098c833520190": ["pogo.com.", 0], + "11e7407a7210a495f7592e6339130fc5ee3119c61317fe747dca06355d8f5aaa": ["yjc.ir.", 0], + "11ec312a9c4159e476ee7d7ff54e7b59664e79c72c9ad5b401a5fa3d51c32c45": ["css-tricks.com.", 0], + "11ed39aeaa62f13108e27c7dac66354d576209cf05dd7eddac65baa3d9646b0a": ["cnodejs.org.", 0], + "11f2835969e58cd34ebf28058ffcce0d271eee1f5ee9f885f70adbd4615a49ed": ["rbcroyalbank.com.", 0], + "11f7122b98da54c5e75014e28ee83977d0a41e9c0f66ed1f1556dd9d0c879ef4": ["clutchpoints.com.", 0], + "11f72611303cab31cd28dee38569ab615be454b551926dc5bac18f1840d51c2b": ["ncdot.gov.", 0], + "11f7b7caa5f7964c89645f2808a34c184f21588e0abf96a8fde743d422d527ca": ["bostik.", 1], + "120078f45df28cbb4f9f4f7933db4e2b65ac09102d00028a1c3bd822924474a7": ["pdf2png.com.", 0], + "12039dfcd7059612828037df8034c6775f978a8f9bec91d30a0c812a2a01b632": ["xn--mgba3a4f16a.", 1], + "120fba70c1e69131d8b4e7cce1d393431bcb4068926b969b31473e39db45be59": ["itpub.net.", 0], + "1217123556ac6e58249c833aa5a95971189f9dc4078f48e904432374872bf3e1": ["utis.edu.az.", 0], + "121dab9214792f201143e90c3df1aab3d2849f26bde17441363eb80907431d36": ["betsson.com.", 0], + "1223c92c089ae381c686c3f3753ddc3cbf31937198266c211ffd6fb9a9e9d600": ["slidescarnival.com.", 0], + "12256bd7bcef66f2d88b3995701dcd9b04de034ddf23c37d5f7083cd5ab8928c": ["leha.com.", 0], + "122782742fde21dd22f0e3d984f634f93d5e24889a98c240b10ced1fa41c9257": ["alodokter.com.", 0], + "122acad04a1f794fd646c5d13d205662894a9078adf9c7c730cdef084ea20a44": ["fgv.br.", 0], + "1235b91075ff532f3abf0d3f37255488a1ce8c8a9d865dfc5a8fd65630ba9348": ["managertoday.com.tw.", 0], + "123c771431889781709685e64701ea8f0fba87c324e375fff23640d7cc038e0f": ["cplusplus.com.", 0], + "123d1c101c6633f056d09119f34921b414f65262d730e62c45495d08a46170bb": ["filimo.com.", 0], + "123f28d8ec6b3f323d84e486c2810af7da1e1eaeb060cf75fc435001c9ce09c1": ["wtc.", 1], + "12411cb2769e5520d42bd5a2c40fedb5e5f0801507893f322fec7ddf9186cff1": ["ncu.edu.cn.", 0], + "12421ca996201ac12f5dffd30beb658ee37be45aa5cdb51dea965b2c052a95bb": ["mzadqatar.com.", 0], + "1244b3dc139b4cb01fa53fabbd0ab1e5ea055a1066bc259508da57c98372d817": ["peeplink.in.", 0], + "1254fa4aecb6550a612d6144e62bb4b37584024c9050d262f48ec10ae887b5a5": ["massagerepublic.com.", 0], + "1256a3ef7039b83c64874e8a6336c9e6c773697fb2a53410fbd11f730a88b457": ["biz.", 1], + "125d9e72c5f9e697f44ffdcfb3d0758fa63f49d24a73ff0144271961f7f20e32": ["paytabs.com.", 0], + "125f5602c4ec019a6d981b7ea1b51540931025a1427394d560dda80996cdfbe4": ["rpi.edu.", 0], + "1261d1e6e17e2e5346c26f757133700cb166cda563e26ef35314ec1ea8f3db12": ["eldorado.ru.", 0], + "126d15647f2265227672d5f342a2bb9d61d41f789661cb1ffe7c6c8967190942": ["yenicaggazetesi.com.tr.", 0], + "12775ef763c9d76328ade744b2cb61a92c4dfb67770e95ada2d6f4c772ae6b32": ["mattslater.co.", 0], + "1297c3d2e9314442f8f615d637f63ba2913f707d31210d50aeefe9d048dfeed7": ["webmoney.ru.", 0], + "1299dcdd8b42b9958fd503088dc2e29aa1df34a558dd9dfc4a0c7e9484fe7955": ["kuruma-news.jp.", 0], + "129a3271b474330bca162a453d59a95f3f80a67bb5eb7c79eb0c41b26fbfca36": ["trovaprezzi.it.", 0], + "12ac5e2d9a4a0c917c1f1bed9500ed5d8810fcf0a37e05b3fb1cad13c2e0ba9c": ["yellowpages.com.", 0], + "12ad5be46b6bbf4b8981c7993000c1368d99fec807e752d961e931a3413bc04b": ["societe.com.", 0], + "12ae35df1906e869e611c5bd2b7d071aea1a17ba2bf0fbeedfe1e82caf40de7f": ["rai.it.", 0], + "12b6222577b2a79d5dc89635002c27e04ea1bcfb21597c9bc321d4c2cf9a2702": ["banorte.com.", 0], + "12b81ebe6f196bbca198e75bf0f60547d31518371618bc3a5458e0e9998f4cf0": ["ondisk.co.kr.", 0], + "12c488447b457547524ef978d926796ddce5485e3e06f1c0dc5ad65c3cb461ee": ["spiceworks.com.", 0], + "12c57376a01b13f9f8131be4b549a2c9871f4597030d3879b0fb9441a5461e24": ["kar.nic.in.", 0], + "12c8290f006da2043d9965cc3bf0ce0d03126c9bcdac023b74d7a27835055f88": ["futwiz.com.", 0], + "12d5a2aeff8fd3e9026b20f75abaeee79b3da559cdcd9aeb61528d2eb1376c78": ["1tv.ru.", 0], + "12d61b136b6d7da529c531801f8bccd542cbefb2aa036f0e1aaa750b3a1e1c62": ["swiggy.com.", 0], + "12d7ea67d8c0fc2c6cadbeef6d75714b3ffb3a00be1631551eae2ca02fe4a827": ["firstcry.com.", 0], + "12d890f58b3ae7aa437cd4fb0c4315481df30c9a57266e868389fd6a038de97a": ["lkong.com.", 0], + "12da508011d3abe7431c1c6e2ae5ecfe370a672a067d1e71c474498fbfe72f54": ["cngold.org.", 0], + "12e41cb4e57f33d7358790fea2741e3ce04eb6784317cbddffb93ac6e207afc6": ["hochi.news.", 0], + "12e8f7e6912b87010b95e7bafde4d4ac8868768c43f711daffb579f42e975ce7": ["xn--fhbei.", 1], + "12e9dcd53f10d9ccd096cc896b990a2067c70397275dc6df1a259719348d7217": ["frontier.", 1], + "12f7493c35a80e634fb791559eb1ebe35e6cac381166447171524283b32d9365": ["fptshop.com.vn.", 0], + "13067ea74c804f22d54c918a79a9e19eda378d456e0cf7cd5c4b09c634af8c32": ["ucoz.com.", 0], + "130c5d1d675d3d2eee289cc5237223be7a4bcb4e1aef8e660ef38fdc9a7b0fcd": ["codecademy.com.", 0], + "131e64e769da522b7f8b4e0f78f3718f4bf10ae5031a62cac6948c4cf253f0c7": ["gdut.edu.cn.", 0], + "132015a9bf1e0a970d81c32abdecef94b0609b5328fbd100641bf09329f7a16f": ["carwale.com.", 0], + "13281be96bbe77de7b691383780b416de89a36389912c1752f8b42f30f65b960": ["cal.", 1], + "132c21fdfa768023cdbcfb1cbc9e9a15d3f6a045b960ce7648dfbc3a4c75ea7e": ["yaodaojiao.com.", 0], + "133a35078e9226d08aba692d93ae0b264d3f8a41dc17377da1e473747d788a21": ["wrike.com.", 0], + "133a6ccc11995983d8a17013d0f34eee4bce648d1a9865898a99b5ec65585e88": ["4travel.jp.", 0], + "133cdf0c6a564a058397cbac103a7504e9a68d275900df5acfb351cf1e6555a5": ["solvusoft.com.", 0], + "133ecf013514715a431b0cfe3487edf3975ef34110d58f6b2a89c19a20516aba": ["linktr.ee.", 0], + "133fd1fa6447bcf834f6c03b35a8bb1eee0f884629c27f60a13f1ccf6658c8ee": ["fanlink.to.", 0], + "134b43b6a4fc162510bfb704d13df0d2ce00db4ee93c8b8d426e47b80da838b0": ["whattoexpect.com.", 0], + "134e29e9f1df2fc9a1cd2c7cc87aea049351ac6863d7dbfe78271388fd98604e": ["doramatv.live.", 0], + "135013a40dca08c36b1757120ad66baf90d405c1b2951298616f1be1b725c8f9": ["popdaily.com.tw.", 0], + "1350ee6d771b827054d4c5a302b2a1d334e6b54cfc09ef57763f542b7f33b825": ["har.com.", 0], + "1353049fa29756e87de39a8eb98f2598014c81d79753752bb616353d9802bd1d": ["alberta.ca.", 0], + "135b2d45338d01bd19015ce710e3d594cc25886a32333578613862135d9753dd": ["mixdrop.co.", 0], + "135de3a229cb9820031cdb5ae623c1c125fc74d6e8bf2efc03200467b8230ffe": ["dunlop.", 1], + "136d989ad807afcf1031f10423b41903ea54fae1caad6acf3f3a43a252871ed1": ["uchicago.edu.", 0], + "136d9f77451f58273d42ff40a74c0b2f9702ceea42eda58be70e4a9c93f5a8e8": ["payscale.com.", 0], + "136dee70e10993eb6e659ef590ea3b0dd1912a1936c38f00e9ff878b7988fd9a": ["wuzzuf.net.", 0], + "136eda94d155ed6301d23a35a30b46e1e8816582096619c2b33d3359ccc7ecfa": ["qidian.com.", 0], + "1371f89c97c46b8f2540136214720780e77a26a8834728e4e3a94c56d798aa59": ["truelancer.com.", 0], + "1373bed770ef76d59bf2affeee9f58519fec453edfc3eef713a8cb1f5ee849ad": ["gstatic.com.", 0], + "1377d1976a15770ea115afe0784f96e15eed627b3152bf9acd9ff3efd2ef8770": ["tspsc.gov.in.", 0], + "137b2ffba8b010f7f2d1906fd75e0fcbc82725d9a8d57bf33eaed239b2383c5a": ["ppomppu.co.kr.", 0], + "137bec86ffc07ed575ce7dc231230e2428ae2600faa8520a213345b4d00bdaed": ["myhcl.com.", 0], + "137ed8d245ebb711875286ddc57ac34b2687c042b5fff5eac0977e13145088df": ["scrolller.com.", 0], + "13a0fef843f8c50f04ac2d9a526883a442f4557e0ce6fd68be753c16bb2c685e": ["ac-illust.com.", 0], + "13b16195d0c3ccdb8bcc26ee6d2d3f2268560c852176ee4fd95e4b4a85fd0495": ["streamja.com.", 0], + "13b46b2f4891a7ca87270ba8bf8b3492bc084019727a5471ef47293a5aa220d3": ["aw.", 1], + "13bac5352dd2bed3af06d29fb0ed7e8d3f03ab56e8ec8ac8336c46fa788f3e39": ["beer.", 1], + "13c6a51ea1b7434d65ab260fe74b27836b53a3d3779d48382b11fd352b8c38f4": ["zazzle.com.", 0], + "13c86d6681d8d548d229bde0bc45869fbddbc49779700b120478b8354cda6cec": ["codelist.cc.", 0], + "13d7bd404915e8160f15f2ba4ad5f5feedbc8535349240b4361a078396c737b5": ["ajira.go.tz.", 0], + "13dba7e5027f79131abc8424041efebd67737895840fcea6f3febc501f53ad01": ["nidw.gov.bd.", 0], + "13e4019bfaf390976f14d25aa5b7e439fd151a2e59d86fbb65d60b728f487d9a": ["pozdravok.com.", 0], + "13e9b6752557952465b5bb689d4cf9cb2f58de9cf53834697b7be9467999eaef": ["adda247.com.", 0], + "13f96fae28a0b1648b8c3529aa5b53f04a2551dbaa48c6194e60c54b3a0409b2": ["picresize.com.", 0], + "13ffa5bd461f4fb1f42842a71e09c6e158651ac7b03d8334d4e0c3d049ca52f0": ["zju.edu.cn.", 0], + "140152e93e3d40b7473e186894c72d825190ad4bbc824dc2987f6c1f96974b53": ["xiuren.biz.", 0], + "140684cafc1bdbca29d5f04420a17dd7a30d930ff47148c0b0cf001fae2ea302": ["unicode-table.com.", 0], + "14071903a37131be2ecd66756e4d9d60c931624512ae2bc329737429d61216e5": ["guziyuan.cn.", 0], + "140785a98ad85e9207a215bde82bae69dbfef7c83daa3e0e2aace20683d13408": ["iqilu.com.", 0], + "141885c01db3139fd99d12123d60111eb179a742ba46c0f467a0f7c7aa474dc9": ["cuevana3.ai.", 0], + "141904d038617024c8eb53a6486515dcc6fffb3361c7a83151d78af4b0198e1c": ["lasalle.", 1], + "142d0afcd4e17d050066f34f939ad91f3535d124a82b9f20ba3e08b06698db7a": ["vivareal.com.br.", 0], + "142e885334c8f40554a88d8dd4e2a0b2a1a1c6284d0abe2bab17b4529f5bdcb4": ["sfera.az.", 0], + "1435c5f78fda94ad79eff79262667c06914c83c89b23a25ace2c71c53faa7be7": ["edrawsoft.cn.", 0], + "14363a16040112e1efe4b1b91251311d65ce42b1da187732a6ff738b3d61211a": ["jandan.net.", 0], + "1442bbe11cf346b444a6b67b7cedcfb32fe9b4b993312f1004c15f982abd733d": ["interno.gov.it.", 0], + "144cd7c244f0c53d786f2ff80bca2bada64ab20f90f7741dec5a14569452d801": ["nts.go.kr.", 0], + "144daf3fb3db1985559b6ec053005df57f371aff890bd020814603bdeb3bfdea": ["samayam.com.", 0], + "1460861ee85686bdaba5a1318e1b8523cacc72ef4f9cb10e516130a60fbe53ef": ["procore.com.", 0], + "14641ce62ef358b89d2661deb661b59688b8e1bb6d3d2e9d96b44d7b748b8010": ["ecollege.com.", 0], + "146a486916e9b49793f279447e573f532f9cb4667ed2683c4ee49ae1727771f0": ["slideplayer.com.", 0], + "146d53ed93251bd7700e22678863974d5b38ea0eb547379cf1e5177fa5fcf974": ["javfinder.sb.", 0], + "14797cdb5af32a9a2607c21b018738486175e1621a5d19212d946970add7329a": ["mazochina.com.", 0], + "14816330f87e25296e8134df7fdc4136b061c930220e8a446a15a941dc7e4880": ["rome2rio.com.", 0], + "14838b1aa0cee9fdc76dda26ce954bb45356af3015738d1f08e6752d0b265e78": ["hnext.jp.", 0], + "1496679903310fd129e9f27ed9fe210c1cdefe27701079e68f00d3269a1b1a35": ["tencent.com.", 0], + "14a2f00d2519bdf772916fa019434d13bae5b1d8f28c60de05c9d066f752722e": ["fow.kr.", 0], + "14a6a7f7c2c8c2c981b2f4ef981b229802fd935654e5f7022c18330994b02d81": ["lv.", 1], + "14a80141ee6072c0851ec1db5efcc5b966c8a267eb909632e29498df5b03112d": ["avclub.com.", 0], + "14c04d749588071262032fd61df9028bdafed3ad6dd0ac6ec682522ce3b40d78": ["litnet.com.", 0], + "14c5df94f6b5fc44af98cb8febcc64a4abfed784d7c535667fccba2067a886c8": ["varsitytutors.com.", 0], + "14cd4ba6e1ced010569d1d90006e47ccd3df02904aa60c14294fff86c56183fc": ["designbundles.net.", 0], + "14ce7607bea281330e7ec1b5eacac7f8cdc8b184e86ebef72a110e27add8d7a5": ["scitation.org.", 0], + "14d1989724faaa619e3c32e59c442a47c7d6c117aca84515c7b19e702b29e792": ["autohotkey.com.", 0], + "14d449af030a806821f1672a49c1eb3f30b662362dd3825b57b4254f1d0a680d": ["ikarishintou.com.", 0], + "14d551848c86dd8c2584880c53b787527f28874e1958490ebe3638487b284b79": ["kaufen.", 1], + "14da4592ce27de9f4c6c20941fde1951ec2f1c60a379a928e88b408d7521479c": ["moodle.org.", 0], + "14db24411900a069a04e1835f0f8f963ebb93902f2d17f4552309201ebf32367": ["bonappetit.com.", 0], + "14e6903613ae6ea3bbe3bf73f6b43103dd5a5bac7d458e9162decaafd467c3a5": ["mikrotik.com.", 0], + "14ebf00160d57e6b7192d725acb090f999e88bb38f89e943930f8576f0cc93ac": ["ndtv.com.", 0], + "14ed04a4bee3519629cdbf12094abce5df90e6c2e55c26d6eff3cf671d5a6f01": ["titan007.com.", 0], + "14f6298371caa20331326da270f4c2ede17c707ead0cfaf9edbdfc65ef6dd299": ["ableton.com.", 0], + "14f7e0829c1cd98449cf45ffba6306eabf5c2534c931cec66024c73df2bf80b5": ["rodeo.", 1], + "14fff631519e23e930f1ef42c6849444792b65891eaf50e99bd1fec438f7f40e": ["txtnovel.pro.", 0], + "15019a94f01b52f92e3aad4ed6b14745b887931348f4390cab9813a9b46e2ce1": ["qr-code-generator.com.", 0], + "150c030c88c33c7d31ea349b2cc5cff15bfab5a02892ae7a41cce6340e34ee74": ["apache.org.", 0], + "150ce50d5dd376be2e10fc537c8a605b6f4a3357612f95a2757100f60a6b756b": ["phd.", 1], + "1510f8fc17b8d9bb0fb8c603569ef85c7fb888bd4d83a44ae468a8176dfbfc80": ["iwank.tv.", 0], + "1513f44016b725c505fa7e5159b34360842aaa8c1dd7ad776b2c0464ffacac2c": ["cgtrader.com.", 0], + "151493ad94e31161857850d99f5b34a59a1568a2527eba98540ad3cf1533f677": ["cashify.in.", 0], + "151ceaee3d104f8923362ab015914c92bb3f206fbaad4761c3530add1be0b2c5": ["kotobank.jp.", 0], + "1525d79978b614ed4352632c100753bddb416dd97e334ee42f069845dd0e6bd7": ["animefillerlist.com.", 0], + "153206725a72d52c80378b67a8c73bc06a7ce690603ccba4a657d76f0549f1d1": ["xn--80ao21a.", 1], + "153a0ba66b361632cf84ce8b2a60766c75afb47ce560c9a8e29543ae228b66c4": ["afterpay.com.", 0], + "153c20874fb9bd3cfe88166ff189dde94b87fc7f0bf5ca398482370c548e8765": ["chanel.", 1], + "153d168ebcbf76dbb1cbdb812d747f89e297d5c59a131a8e498d329332ab2f1c": ["npci.org.in.", 0], + "153d2a648ea74a72eba3c8848e75789bca4245a2dc38d9cd19a765f1e3472d85": ["rallydev.com.", 0], + "15403deef0208414efb8fb58d767477df8cc2e4276f7aa6aece921c03923421a": ["instapaper.com.", 0], + "15491cce9225df8ee0824de82b3124144546d398222cf43d2d4e0279c6a99d6c": ["expireddomains.net.", 0], + "154bff652ea388ffe2f877615f7bdea0b2041e547f81c3039a05b14f8d21d801": ["thumbzilla.com.", 0], + "154c84e308ccdf4a23a6be241cbf1da99fd8ccdd4060295e9ed4fa7da7648587": ["islamweb.net.", 0], + "154fe1d9aa6550c1eb8da6a98044d4ebd788a40178e311ea948dcda6fe15666f": ["london.", 1], + "1556c687746a2c57912c07dde9272e6d2471f329ec8af48c4a6a18a00ede710c": ["green.", 1], + "15587f79392cae2b54dc1f3cd210e35f0b485cc02bd14615dea7e82b3ead1154": ["barstoolsports.com.", 0], + "15685d2df867b0c53271b46cdea6215c9cac50d7abe39d7a80fc20288d99964b": ["pfizer.", 1], + "156acb8ff91c9952340ad36be7dc7e67c8283e7eea65932102e87cebc75455ea": ["cvshealth.com.", 0], + "156c15ae63acac52ee5b118ec19a18d69ed3d69ceb41c899d6a4f0f444677929": ["e-bankofbaku.com.", 0], + "1570acc9f3bdec0c6faea6d2a81e30144f0a68558f00e936b62efea5b706852b": ["acm.org.", 0], + "15710906a4efd2d10911496ecb666ba1a7874fc7c623c8200a6cacd2d2c53e1a": ["pond5.com.", 0], + "1572c8bb234f040bb7e183805128c56a323176facdf666f3b6bb4bf72969cc3a": ["invitro.ru.", 0], + "157f6b1ae49db652bbb6e447492f454a1d57981609dbce1e0e64a7e41ef2a106": ["ximalaya.com.", 0], + "158001fa56aed605bf486cf59dc698491027dd79a2e5b6735384ffde4a8000f1": ["michaels.com.", 0], + "1580be1f72d8e14cb2d3850eac0b4e311ef1aa758a8023e9ee9abd7d5272591b": ["qwant.com.", 0], + "1587435951f008e68bfa3461b9fa280c85c14776f366b8459908566160b2849e": ["melonstube.com.", 0], + "158a44d235dd94edd7dfae9a35938d7be29224e9cbb72b6c99e1918b8707ea87": ["khabaronline.ir.", 0], + "1594ecbe9abf31c9ac923ac391e17091736243d5845fa39d10d08c1c58012428": ["mockupworld.co.", 0], + "1596128d25e625b99d8cd53e63922ee99a88872b9c4e5e942d3cf7a838bf4f1f": ["evernote.com.", 0], + "15a127d231b9518e06c285825714a9a73b983a50c8404821a60d9c31eff9917a": ["frogans.", 1], + "15a6d8198f2d9e402dae091f7965ff8f165e58e709c7599c19d1d0c9332c2a24": ["ocn.ne.jp.", 0], + "15a7af668aa652ccb71ea77010576cb76cf06fb191f9435950e7337b6aa9f09c": ["okinawa.", 1], + "15ab7743baeab5d3d39e2915c4c19e6c64677afe674ed6490ffee69b6518a752": ["arabianbusiness.com.", 0], + "15b618c7e5fe33020aa64a2783f6dad8d378500efb1b4073d3680b401f67ca99": ["cpta.com.cn.", 0], + "15bdcd4f398b253d51927f8bfab0eee436d2d018dd411f6d3b808af85c38a86c": ["mysynchrony.com.", 0], + "15c2058f69c9137ce6b3facd0c943c1bc308929aee0a824023213677d0974c3f": ["manchestereveningnews.co.uk.", 0], + "15c988a1220b07506bce435aeea38b0a3af4063ac8cefd162a3506a684ba557a": ["read.", 1], + "15cddbf4a494ee1aabc1fad1e99f297635beb76465a882b66cdeb6d8722def91": ["teamup.com.", 0], + "15d575926162b9e663a291a98e8474c5e842c0d801816840dd092326b4c7f31c": ["manmanbuy.com.", 0], + "15dc08a6fd12437f218bf6e661f3c43c313ebe27e111db3e6e8bfda2d14b44cf": ["jinshuju.net.", 0], + "15ef5a21d683e07c152c07155f657ff6f6fcd171ec879573df598e43ac6ac318": ["mbc.net.", 0], + "15fdcdb0853a7fe9b7419c61a6a5b64af4ab6bc1e1d1ac9ba2bd8f7ed26d5e0c": ["vatanbilgisayar.com.", 0], + "16018b6a49d91edda4fd1fa188ca82c2b28d2edbd729623727bd9df0a77ef4d1": ["gasengi.com.", 0], + "1606c832c2ff00a0787208290edc82a77c94c0e264acd7fa7e51a1f97613d3f9": ["elcinema.com.", 0], + "160a62771f5a5b75981849f9034bb10a2143e439c0b9db086211aa92b0bc3955": ["nesaporn.com.", 0], + "160b11fb452a071b34cf2780a6ca823b6789e9a93d021e7667243a4795fe3cfe": ["zcool.com.cn.", 0], + "16119e09705135c8ff19b569a47f31fd27e9fe199ac474ac57d92fc2e5649206": ["sciencing.com.", 0], + "16158f6d63357ef3f29cc4af1cae73c953c6dca7e1786f2315c88d9f360af303": ["cerist.dz.", 0], + "1617fa85a330231a9bb686fd179adad91452379702e8d03b7bb1af706b279bf4": ["eplus.jp.", 0], + "1619526a34af358e2eb86f5df74005fc170ffcb466aa374e176c9c119565fa8a": ["hollywoodreporter.com.", 0], + "161d9272f38ba56b9e6be70ea065b1a22ca848b3562af920dd7fa0dbf4ad3e91": ["osaka.", 1], + "16252b8f2366fbd645c3cb146ced8d81080b96ceac900cae793c0c019527ef56": ["getnews.jp.", 0], + "16253c09d8d7d33c72de0e70e1c40dc44d5f5c00bb2e75fee51304afa40810d9": ["radiofarda.com.", 0], + "16277451527e4de47d1457c8c4bf5d89ec3231110b8b3f0a63bd1c38a2bcbc9d": ["hdblog.me.", 0], + "162793f2254ba232d9ae565ba5b142026e9ec81c9da8aa6e6f2a883e9eac36fe": ["sanet.st.", 0], + "162a4618fe87fb42539a0ef19efef387db3680e30bb6123a25e606e276069d8e": ["bjtu.edu.cn.", 0], + "1635f8201f6fe0f022225e57020cee574e3c751ea831b0930c7138e54822363e": ["techsmith.com.", 0], + "16377cdc707125326b567779157a44955dbcc0e427441b8e11a476e9049200bc": ["ucm.es.", 0], + "163ee08a9d13fc149315cfcc8822984f981932d491ec77fcbd64dc3c85317d44": ["notebookcheck.net.", 0], + "164491dbfc95f62a0d09951a1f7bbea9394f6066d1d86e6a524c4e5691d5c903": ["xn--mgbayh7gpa.", 1], + "1644a47b6bf7f1b21cf0e85195840a8bf348652466523b94485d2e02bc974523": ["znds.com.", 0], + "1650e2b65c4ed2cfe69d6a8d1e9295c67e513f3f2569604c9b04d93992b3f801": ["testout.com.", 0], + "1651d9daa63e2ae5c47df0940ad13c3524f6f684699803bbc34fc74113fe1b9f": ["shimo.im.", 0], + "1656908005c3a3f415b5cc496f146e9dfe2b2bc5101fbd2cd09ef22dd9093c88": ["betaseries.com.", 0], + "16576325136dbd61c3dd83b5f1accb5de2e692ef2913477e0edfe8ba5c378571": ["francetvinfo.fr.", 0], + "1658d7c12a0900667208cf3147276774cbfdbad7be1264db58e40aaa0f68d898": ["liuliushe.net.", 0], + "165d0a44acfa92f1a63c224dfa9a5a3b7eee7ca1c115cadad6978b7272a935bb": ["manheim.com.", 0], + "165dd86e1dd82f080f63348e8a8cba346fcbcfd0b6251b9d670f127ed51a84d5": ["virginia.gov.", 0], + "165e907dd00d33e7d97ed0c5de9be06882910fbf4c9eaaf8a901e67996990427": ["tdk.", 1], + "1666b6302f73d25186ea8e638b06aeb4c2ac4da61425150a56324beb7af31dee": ["treasurydirect.gov.", 0], + "1674290cc4ccca4488ee6853f9b64ca3f44e927437fc812db775d7e76d132046": ["nvshq.org.", 0], + "167c30ffae4daaf059441663e184f5e495da9426975ee0c04b0fb5e0786d04ae": ["skyrock.com.", 0], + "168255b0d1e988b168a3fa00c893db49898b8d447ac149a613486b5db8649f08": ["3cx.com.", 0], + "168385e86ce5a7088e6772ba1e50b0e308161172ef2419cf35d5ef206612e523": ["processon.com.", 0], + "168da1bc83e342631500d2d674098446c427ba36bb0474e7a612dfce260e2e48": ["xn--5su34j936bgsg.", 1], + "16962699d3b35bac31a2be1c90d21648011b10346148540e7a62b545bd480db2": ["erobanach.com.", 0], + "16982b639782bfac3a0ef0f072f97988eb6d704b13b96951717c99d09da59f10": ["albumoftheyear.org.", 0], + "16a44e3d2bc8a4c34a7593d6d282a1f5a03d2baf8376172557cdbc4c7cb632e2": ["contabo.com.", 0], + "16adaaa06b2d995ec579df72ab18fdd385746af141b0617dbf910f43fd0b84cc": ["visualstudio.com.", 0], + "16b439f6ca9f96034e1c122cc0bc247a54f049418ced39f7bdde7b5edbdb81df": ["stoloto.ru.", 0], + "16b8ed75c973ffaf5baae5517bfedcaefc7dbab3c4216711d5085c9c562e7fc3": ["snssdk.com.", 0], + "16bc85661118b652f2a469dbd288cf312283ef5d9d18e9012c53e63b206d6d04": ["swinburne.edu.au.", 0], + "16bed8c1b490fdbb5526429edeeeb50a91f7253528f3d301ebded8244e9ee009": ["samsung.", 1], + "16bf0ba1c05924d951ead7d9be69a7407f2999f8cd92a5741d83377f2a86c3b2": ["abbott.", 1], + "16c22154cdc59e7e9ddec4da98014acecd72b0b1dbaed1b37c34f059a7aa69d4": ["angelbroking.com.", 0], + "16c37cda8bef68ce2fc9e7f9f459ff49570c7b7b7f06e2378572d294afa9300e": ["resmigazete.gov.tr.", 0], + "16c3d02c9c6934a3838cdd4cf3cdd928320e30922be24838c23ddc6fd4412f05": ["singtel.com.", 0], + "16cb60634759cbcc256f35615fc3194efdac57306cbac8267012592d7f87d076": ["sdo.com.", 0], + "16dd93d666130e5f4f7d3f7672a5522b934979e07ad83a0c9e594ba45c41469d": ["pstatic.net.", 0], + "16ddaaf1ccdc10e5f9a1b73d7e193794e66df9040af79fa88e335d9d843f207b": ["heraldweekly.com.", 0], + "16deaceeff1ae24d033072c0db9f484d01f674b877f981e765ee91d8393d95ea": ["flipsnack.com.", 0], + "16ec42badf5dbdc78fadf401c6d2185cb1b5d82f049ac51d6f9a5c765ed0b920": ["anser.ne.jp.", 0], + "16f2b0e32f78ada88035f3af9a91aa051f503e61085c91cc832ed350c839b0f6": ["ycombinator.com.", 0], + "1700f3231c4d8d429727fcf7b712d21ee9f6c43720d4e4222075a19cc8752fc4": ["nozomi.la.", 0], + "1705f3536e6d9f0e859b2679ca2690e3bc5a8aca87ab8f7f9e058c0fdd8dfdb2": ["mangalivre.net.", 0], + "170735cfdb046e32e361a9a3773c40ca9934c494c9a78777a948963ae94d5040": ["dim-rs.com.", 0], + "17160502b9f5308656a4061d846971fff03edcb1380b1d553abf600a51e746a0": ["best.", 1], + "1717d710cb3897963f0f267baf2f3d6a2c1e607b206364ed26d503c16041f199": ["hiphop.", 1], + "171abfd80686a9da72903f36f6b86d5f105b32b4a100cde1384e5606fa82f73c": ["3dsecure.az.", 0], + "171c97063a11dfc0de4274e0265f28006c651e1e26b4911cf9c6f08d4837e057": ["suzuki.", 1], + "1721135003daf8444b27f0d2f76749f3538278deae00ebec4adbaca5197fca7b": ["paperform.co.", 0], + "1723b303a383d2905da4cc638a6c930428477e2ec78ac38833129b1c3c7ac82a": ["aarth.net.", 0], + "17252967a06b50cacf335f55572748a468cad57bfe7fd689a2cffd1785309eb5": ["weatherspark.com.", 0], + "172810d320914b2aeac42c841cbb8bb7431f952aa8ce6835dd86b77438900233": ["fund.", 1], + "172aa299c19e26c37bbde994d7c22c13fc1d62d90f29f2d132a3eb83905a8f92": ["insure.", 1], + "17335da119ad87616e7e2f00cce0a312f13fab95d6a91bf44cb11e35ef93d800": ["trt.net.tr.", 0], + "17338afef0a01ab5c84c85107557353a602e2c9bfb3610d5febc4fe11aa8e066": ["pdx.edu.", 0], + "174470ff75ac71ef01f529fb3cae9e79595c1a90c21606e7111f868a6f28869d": ["jiyoujia.com.", 0], + "174568879432fbe871c86cd8bb52a2b49cf97a94a43341b590faaae9a719ee2f": ["xiaoe-tech.com.", 0], + "1747b203432f3ae3021b3251195a3362c301f9e8983307fefa5ced5e204959b9": ["ligazakon.net.", 0], + "1766288622c1b7d24a9e8e4c75098c8a5c4f953fdd19a1915f5bfb9ac8060750": ["xpaja.net.", 0], + "176638362407af7b426dde3fb37758e1424aef8e63db790703454cfffd981a55": ["els-cdn.com.", 0], + "176a73a3b92fa94b7cc8bfca3df4cbc6e99fe975a82fc8a5604506e957a92573": ["team-bhp.com.", 0], + "1780c10f964b9a312cca335cbf3d3bbba6cb454f457affaa102a2bd9844077ae": ["themarker.com.", 0], + "1782ca260b116c2360821078875e766e03ba1cdb547381416e2fabe1c495804b": ["help.", 1], + "178aea07e9fa0f1d52e254e63778ab688a68538b935b381281ff9699af8f03aa": ["adguard.com.", 0], + "179985054ffebc1d59aa235c22e3cbd84056f1674dc77f408dd7865d6da3c2c9": ["mydrivers.com.", 0], + "17aa574e244102ed8297d0d4b53babf3e0fb2fac3ad45392ab71141c205a08b2": ["unicef.org.", 0], + "17af174cebfd41b1b210586c37e8cfd9a335aea5c1e840d6a0280b8953df09cf": ["docplayer.net.", 0], + "17b1aaa9289745d9ca620fe23f281f2733fbd7274d4cf378745765792de49453": ["cnas.dz.", 0], + "17bdc1cf3dd27d8f24063c52ac9dee4d3d11e3104b23623e944de01b11e8d3a8": ["sxsoft.com.", 0], + "17c0286f06f93ac5220c293ece1578aa81c534eb7e39170df7a2c848bea0e9c6": ["auto-doc.fr.", 0], + "17c4a55abfcdcb269a83901bb37de5a69fc07dc75c6720a1333fa44467e6e1dc": ["timesofisrael.com.", 0], + "17c4e67b98cec45d6a11ac6f98be9c36068f932942c19b138c3d150264c32966": ["mymodernmet.com.", 0], + "17c8aad6e256c55f5f9d11c052b7c8f7d7efe2f8b573f856cee7eda287d6238f": ["todorelatos.com.", 0], + "17cbf0dea47f8b3ac486eec0da0e31a858768653194026a00434306a69e35802": ["payhip.com.", 0], + "17d466e6c0d83ca34fe4195bfd9c9f7954065931bb98a9b2a96fdf45edfd0d0e": ["lingojam.com.", 0], + "17d919641d9bdf8004651d666864814057d511930eea93e24cb226ce2812e1d2": ["testberichte.de.", 0], + "17dd13cfc95f25f823877d06583cd0d39bf099647c921ad9c944ac2deb2414e5": ["samehadaku.win.", 0], + "17de03cf902b0a90a61f2aae23b4e59e23f670aaa327f6718d632ffc0fb34b11": ["naszemiasto.pl.", 0], + "17ed24dbaf246d559853927d39400f3fb056617b27ef3eba0c4f161d2ef8a1dd": ["blockstack.com.", 2], + "17ef157db4598ba30e1441a6d807d2bff1d22ca1d0046e7fab619b4d33626501": ["superuser.com.", 0], + "17f024c0c2ad22226d966bd2ae4d13c5a6dea3aff89e55e3c46fb99757737830": ["avianca.", 1], + "17f6652d314536ffcefe84f94438acfb450e334756425366393b357d23cf97d1": ["zamzar.com.", 0], + "17fb58c3a49349b7ddc046b521ecdc8006c747ebe47fbf4b3d50e882bd688546": ["prusa3d.com.", 0], + "17fbcd6335590b90112d1252c74039fe26b5bed169a6e2d11ce378de84558bb4": ["awesomescreenshot.com.", 0], + "17fc3e2b2d47b5731d14842d5e5382119251599aa1798c781ea6d6c9086ed6c2": ["aig.", 1], + "17fc8f7d8ba9db4abef0793d0f1de15465a4f271e39c2b544ecd8558dafda3e1": ["eco.", 1], + "17fddf2be11467cfbc138ab670ec08419f77bb258b69c4f158b87d4b59d2caf2": ["collegedekho.com.", 0], + "1801d7db6cfe26bf011e468434aecd2df3297bf8666d6bda2e2891af64bb1375": ["theguardian.com.", 0], + "18057f16b6d93e54448c89413837ff07e020367c0ee654e9ec1938538643c153": ["y2mate.com.", 0], + "180e3e6a46efa070761341261594d5552cb29261158feb417a5d56ac27d3888f": ["biligame.com.", 0], + "180e9d277b08de3b43f4d66bb1bda55d877bed9004609f86b237f0ce6e61075e": ["sinaimg.cn.", 0], + "1811616987fb960ee2c2263fa31d5d8888279ec34e9c4c613b6750d1b211ca24": ["cbse.gov.in.", 0], + "18161123bd63406e32d7d078b00106b4de1373e50f3f2bd53e6565a7deb631d6": ["tilda.cc.", 0], + "182029521783d5e112e07be56ba6b170b334ce3eaa04279c33835ae2985ad337": ["amazon.", 1], + "18226157081d4edea691e80913dabad67b7de21cc6d419ac55524bfec0d6627c": ["rich.", 1], + "1831658e0a16fb579400e4830dc8a8054fbdb29d659224e9fe139ff1ddfaa15b": ["khronos.org.", 0], + "1832a969fffcacb1abcbd0923d1fd60320d86614c1607712ace64392dedf558d": ["xn--qcka1pmc.", 1], + "1836e66b858dc6aacbe7a2f4b98ba92f2639d1abb49da085f45cac5b8507c6bb": ["thespruce.com.", 0], + "1837adf0a936404205182b68b603d30dff29baa0c1c711d872dd06a704c0f8cc": ["tires.", 1], + "183c199b077f17617896c9271ca27299730d3cef722eda4e0ae996c0e9662be3": ["matome-plus.com.", 0], + "184021b213e4e90863c99ee17313dd0215e234b0633bbbc014845e85d8a90093": ["shotcut.org.", 0], + "184c9d623a069176da3c5313a50cdd20a8a818620dba03b59a588b2459dcb5aa": ["keywordseverywhere.com.", 0], + "184de0e1a731e97b24cc2c8adfb94aaf1307324cd2336882b7e90120ac0d1497": ["thomann.de.", 0], + "184e9f7a493757cc7492ce9385ec58a8c51418ed4bf4da621a44fc7df255f869": ["agakhan.", 1], + "1852000476da2d56a70a44e5280852c2e0c548c51126c0b785af7f840f504273": ["metservice.com.", 0], + "1853d830bb945de99790ed13d0462dc4b0bc62f49bedacd3fd6f729627df539d": ["shell.", 1], + "185819f23c4eb04c4ca5344618947ee299cb58d965066e1c48129aa9b55d4ee5": ["rqb.ir.", 0], + "1858e6fbaca1661a35cbd1889077e953a9a945e93d7d7389bb4b88bf72c75485": ["mssqltips.com.", 0], + "185b74c41a53388dee908ae8b7d6c8d8c614b7a7d29a4d0d07cb6d7adb8f46fb": ["funshop.co.kr.", 0], + "18692f112c783bd6a3df299c5e07086afe12085e961e18505580707be6d45b14": ["telcel.com.", 0], + "1878b6a861280adc396b3b3de723bbd5827d72e91b98caa91118d6783c1883ab": ["unionbankonline.co.in.", 0], + "187c8f3e0c718bd562b6a6e03738989c5b0b12e5b84af56ddb43d8f226e92edd": ["vg.", 1], + "187f512d7478246e45964130975bd24bfe087de077885af0fdc4e6d6e49211bd": ["selectminds.com.", 0], + "18a040c424a585c39441dfbdaa5d3bd9dc06600a5971dee91b89b9cfd123dc4a": ["64365.com.", 0], + "18a1ca75a0bde62fcd645e755cb6bd8001684c5506ebb8d1b371940b1ca3947d": ["imgsrc.ru.", 0], + "18a440e738fe3f2c62ebece487bc8de519c35f60f04405703b38bcae4c56e9ed": ["autotask.net.", 0], + "18a78630a3b2bea70923e4b9c9c5921b4b604fb2664fec55c9709457b9c57e39": ["adminlte.io.", 0], + "18a8d334d3ba94fad6198d7f4e691862625963b0da9dc539506e6b5ac6b1eb62": ["roketdizi.pw.", 0], + "18aa34f2f3719086dc61181d23da85acfb2abda249929f66a6d126d6abebdeb4": ["betway.co.za.", 0], + "18aefaad115922583c490c15a2a5329d917d5016c6038793a24aae42c60ffb68": ["bank.", 1], + "18bdc2c695fd5c0eca751c6c4a0f3ab03aa3eb902c13483f1bc66d5c8f7dd5cf": ["propublica.org.", 0], + "18be7e9a05f8d9da6c1e234c1a2541381df8743a61545cd004c408970979755b": ["cloudflare.com.", 0], + "18bea3847493546aef666cab4328b12007d4c7460780f367172766d379b1252a": ["followmyhealth.com.", 0], + "18c0ac53f622c01f377164696d071551cef42226350ccc5c02bc610a8dd4b8e8": ["cuny.edu.", 0], + "18c1a4a77cc947bc53e78ee53a0c405d223df1583fbd4b39e01547cb7d89559e": ["ziperto.com.", 0], + "18c24e52f0e57157e7d36f133398d5261feac2af6fe6a898a3575450a93f6a41": ["photobucket.com.", 0], + "18c45229b90f41f65f961a43d8d3c2a5678a74c8896a699a94aeff6ca0fc3ca8": ["gap.", 1], + "18ca06a9f40849bd1050304377a3178357bef8391d68a7d4790de58cd68f90b0": ["vudu.com.", 0], + "18cd4fd3d4fb78d468cf0ad67efc33571427e78db010b9284e66e174cf88b8ab": ["tirerack.com.", 0], + "18ce6c63241ef9c27796092fa3290db3aff4eead4b8d2d72e30f3222ec7a7157": ["xn--clchc0ea0b2g2a9gcd.", 1], + "18d0ce9c123c067987ed847ee10aa9ec820aa0369f00c26f5c594743e60be27d": ["dtdjzx.gov.cn.", 0], + "18d1eaeb707ffad616a530a7653887a6fbcaa1bfa8acc80a887f5ff9924c7e3f": ["eastday.com.", 0], + "18d5dec9b4c3e468963cab93b0e5a9b61fb645757a1234f653f6f706b9757f08": ["abuseipdb.com.", 0], + "18d7cf15fc3a95ac7180cc0388d7eac18b7ed016b6d18f327c955a40be401b2f": ["dayforcehcm.com.", 0], + "18db60854a5c5d02d32fe1c112b295a2c00188c9ebf62b1bc4ebd948c7e93bae": ["almosafer.com.", 0], + "18e147d974e33c1e3fed18e9bec763664edec4de70adc5bad78e62fb9baaf937": ["malaysiakini.com.", 0], + "18e4a839595d5e6348e940382bfd351cc643cdfaf9c9eea597d3a49e0341f289": ["netgear.com.", 0], + "18ed2383d867020e66749fb7df385f502f608b22bf62339553b3d851a0e632da": ["college.", 1], + "18eeac34db38b354287a477aea5abc6ea4aa25cb25af3a2128329ab84f1a12c2": ["huxiu.com.", 0], + "18f92f8d31a86098505c55bafebda201fed92d612ef818c268b48117370447c1": ["applealmond.com.", 0], + "18fd99407e52dd0da63395481ce0f50d87edec4d2ad933edd7c7bc8fb22b8bee": ["boombo.biz.", 0], + "190daef0d7fb939bb23c9d267d17ed8cd6f81a19c3e4a310af020bdbb5d8d46a": ["hiworks.com.", 0], + "1915d94da281882509e0c72eaab1d6801189ecb3e554efc3ee0d25cbf3ff5374": ["gamebanana.com.", 0], + "191a74e4831fe5c85ad245256e5865aa4e4f08fe80e7a081030dfe6d734f9271": ["mreader.co.", 0], + "1921945d742d957cde71059576830e881ff703fb933ff123ddeee9072670eefc": ["csulb.edu.", 0], + "193460da97da3546e2ca84ab6117e5a13f2784b2b955fec8b965dc0b727c48bd": ["digitalcameraworld.com.", 0], + "193e9379e770ad5b1ff6864597ba1ad3934fc2b7d43f8f25b1809c1ce92d41c6": ["fda.gov.", 0], + "1943cae207e7c425ea8f0a1f53e456901996c38204a75def1909afcece960f65": ["zw.", 1], + "194daebf00ed196e4cf8626023edb2c76ab3a5341f106f9203179c4ae815a4ae": ["umang.gov.in.", 0], + "1952bb5e8e9fe3a159355d540b433259feedb770691116df7875b06a4a4bf93f": ["msnbc.com.", 0], + "195377ef66d4b148dc5013014f8803c931f4502bf75110552aac6bbb5f8338ff": ["xn--xhq521b.", 1], + "195935ca03738cf29f49280d339bb0072224476d5b7c679d5fd179e7c1a44492": ["filmvilag.me.", 0], + "196a6f6bed3ebfade692974a5d0e052937e9202a5376e979395cd22eaf56d17c": ["couponbirds.com.", 0], + "196b51f2177835b51e9c84d8ab166ec18599f271e4bf5c9c6fb2e8935941f1fc": ["beenverified.com.", 0], + "196f54df9fdbd08f3f9daea6056969c12074e6153979d5acdd10833e54fe4087": ["alukah.net.", 0], + "1971c54bdc6e11d325bd68f62e4988a33e1b0c54c9ec667550f012bb50300264": ["capgemini.com.", 0], + "19792176817106a96c7b540dc461a63779a68f72977f6b3f18f2f4e680c964fd": ["sportingnews.com.", 0], + "19796fe0fbe1c068e02dc9b77c3227df338dde356018e6d00dd6264106bb8b59": ["idnes.cz.", 0], + "197b2f28b5fc4d8dd17bffb7cbb349b08f9f2f4fc47c384c8247549a2e526025": ["eba.gov.tr.", 0], + "197f48f106cbdcd098265fd941d8aee4c6d8c6cb0b207dbbd611574169715e51": ["jetbrains.com.", 0], + "19811cd862299ccbc9f5061d6eab8c4ac2d0c04c3c5753261e77048147c4e4cd": ["media-amazon.com.", 0], + "198b563c35eb59a226f231446b76adfc53a1f3fe563728443980539c12a867b9": ["qunar.com.", 0], + "198c33cc397b6268e734b7d62bff14b3707c6bfddc32b4c20491c6bd95568ebb": ["jetpunk.com.", 0], + "199195a96f7504506b67aa448f6e13f0f5914b8c49cb749e9081c8c561a3a4e4": ["occ.com.mx.", 0], + "1999129a02c9e1dca803220fb51635c616653871e07f1835ed7aed0694944046": ["hardverapro.hu.", 0], + "19a1057ab3c4d0cf9b65e555eaa258750455491b2ba56587f9327a4b3161dab9": ["oclc.org.", 0], + "19a19ea541ce4aeb36354941fcdd7b48ba209597256c85af05af1d489485f10f": ["airchina.com.cn.", 0], + "19a1f0409061b0d3453d0a35952c179b2b4e222b6f8b2806ac54b185a929b07d": ["progameguides.com.", 0], + "19a2f9d07762befdb7bf2ab2603ebea7223efdb569bdcf070834e2ee0c8c4484": ["baiku-sokuho.info.", 0], + "19a314484aee6a493c7251fe6f1d35fd9f3b50c2b691f70d4afdbf0185c67485": ["spokeo.com.", 0], + "19a92359b0abc3a8ba42cb9d244e1c8850756c5e72e1930d9e54d4a5289fbaae": ["4ege.ru.", 0], + "19a95326d68c0088beb93fc4ff676663286bbb25acb61205203142283c4bc0bd": ["dramacool9.co.", 0], + "19abf12f10d359b65583a0eef2d25680734246aad9a536740ba5bbbb791a8b2c": ["betterteam.com.", 0], + "19afe295f78abb7a88c8776754d42727f2a2e4269426bbbd86d666022a187ab1": ["livemint.com.", 0], + "19b38b4c9d0bf6ec88c09b58529d9c5031421ec7fef1bf7fab691bbf605474c9": ["xn--mk1bu44c.", 1], + "19bc978c14180f471e6dc0c3784255edfe182c8a12959292a97a499c3c51f31d": ["tagesschau.de.", 0], + "19bdbf23e96621b75f6145f6f418f6dafec6ce9b595a5364779dc4f000638a80": ["meishij.net.", 0], + "19bf8c7a538252e20a010e7ab047a37f7f35bd478c2d1de6439ceb8b76379efb": ["ultimatix.net.", 0], + "19c27cc1e3c49f942036a054a96b5436d0e2b7092b5518abb01bf033119deaa7": ["townwork.net.", 0], + "19c340bf8f1dee7f3cab07fc99492b7b21d5ef6418f67f4a5368b211dab401b0": ["betterhelp.com.", 0], + "19c7c67ff17050f616c77e21c1070c411d552ad07b45e0ea841459403eb5ca64": ["diy.", 1], + "19d56adf787a09b41bfbac5c9142ac5797eca6b91e3d0c6f5f5559bf83fbbf59": ["vietcombank.com.vn.", 0], + "19d95e8cf7c5e733246b7c56fd462380987f4c018a576bf5381d865eaf0baabc": ["thepaper.cn.", 0], + "19dae09f74764d66a5c66a2da4f3ce09b5bd8c4b5f4dea99c01b07905f45ab0a": ["winbank.gr.", 0], + "19ecf0dd5c1ee071292df6a530abbe74d897d2b94d562eacf474677e5ff4f2f4": ["360docs.net.", 0], + "19ef936d1e0a883d404fa723083d53b81abb4b7467a9c796353168971c4cf05a": ["imyfone.com.", 0], + "19f9a4a39d0710a6ef460de163d2be63b1cccf9100cb07cee73ef6819f346954": ["jpmchase.com.", 0], + "1a04645a62b0c3dcf895bf19c03c5464b500c1ec75417dbfc5c56f907b572ca4": ["like.", 1], + "1a05d52aa270c10e27889827e7c17ff7a7946d8cdeaa035c56ed732b9f8529b3": ["shareae.com.", 0], + "1a07210c859c54cb55494f861f88778d6a42757440a233b60f492babd9d719b8": ["tubebdsm.com.", 0], + "1a1017ad56fa65d544c1c053e6a6714fcc54a18747c0e0ed686d45c8dcfa2ad1": ["techspot.com.", 0], + "1a11dd72d2cc23ce5eb2c5dedbdca9aba33fcb107ad3be7fe9f0524a3d2e491f": ["element14.com.", 0], + "1a2a45c7a6bb6d5c331fae556c2b6cff17bdb7c12f69d462767a5d3e417f46e8": ["24smi.info.", 0], + "1a2edb8675901fe268916246b105fe25794b0d2777287367c34c0a06885b36ac": ["portoseguro.com.br.", 0], + "1a31e9851653632f932033d5b673d18373e26c8114fe815b74e21c5bbc8709b7": ["diva-portal.org.", 0], + "1a400c42ef225bbd40f7708e24dab75633bb4ed54b39c570720024903b481d72": ["aci.it.", 0], + "1a41b7d757f83a8310999c510b8f546eab3b6b9fbed554a8879778397f4ba8a1": ["movieffm.net.", 0], + "1a45d292628380e896de610bcfd17357136977d3fd78d199f4641bc5d4f8b821": ["mq.", 1], + "1a4bcf9b06f5ce009191cf9e0c9e931d6a70414f52c55482e1bd1f4939ffc7b0": ["leawo.org.", 0], + "1a54d4482ef7690df0b4e7ea444d50606a7f9b3b18bfee161d88609168b5eddb": ["fastspring.com.", 0], + "1a57ae88f6e3fc5f681be86ac5d4418109fc97cc1993fe4c1ba66999e9755c5a": ["presseportal.de.", 0], + "1a580c4a75716538d56d5c09983be5edcd4791a0737b5be1dbc0b15cc725d7d6": ["nyc.", 1], + "1a65b562c467edb6cf06f38ba5cdf5210962f3e1722f8c005d1398c90022214c": ["avval.ir.", 0], + "1a666099359c1761b0d506b5604c511763384ce16b9af60e09f2d4c982842e7e": ["uwccb.com.tw.", 0], + "1a67fc91ae4800e9d604f76c0240c2196045cc18165bd0d128d9dae7e9bc3d17": ["eksisozluk.com.", 0], + "1a729357d6f20377f9dac67b9bd28343db566d02ffb74e7b3272183774a2ff1f": ["banksepah.ir.", 0], + "1a78852533e812ef16cdc63cef8f560ef0b99b17d529d54991102da081371ad8": ["ubc.ca.", 0], + "1a8102e2421a67130c4892a8ff87d0172d9b631f07572186b193011cdfb468be": ["pcgamer.com.", 0], + "1a81c592e0a8bd5b634c8b0ac4ea2dceac2d27888862cedba96a5787b9dbba12": ["chuangkit.com.", 0], + "1a820ca530145b638c2307ca69131998bebff80510aad7578f969b88fa8febc4": ["gog.com.", 0], + "1a843feabd5dbfa5be958a7d27239e440ba253925bec0a66fa91d645bbde53fa": ["missyusa.com.", 0], + "1a86c0e501787f0f2d5ac10775a1f88d10ced76fed1e944affd1b37f0cbeab40": ["javmix.tv.", 0], + "1a8aab2efcb9282d3e64143205c04392388ac4681e4eef1142438873297f6cc1": ["bankofbaku.com.", 0], + "1a8b6225542cdbf91babc72138ded87de45af54bcd2806eb4f4147c6c0522177": ["edurev.in.", 0], + "1a8c1eaa80cad31bd73375ee45e4271b1c01be18c01512279d5aa6fbc69528dc": ["owncloud.com.", 0], + "1a8cef6af9243820e3562ec9557611e11da4c36f5b89d3405a9f05770c7be39c": ["neea.edu.cn.", 0], + "1a97d3278b5e687ab532258484d126cd3994749239b78460a5e3ac147736e25a": ["eonet.jp.", 0], + "1aa863c5b2ff20a320c71fb5599fd0d46e049488d1d705f03efb1b48cab2f36f": ["scourt.go.kr.", 0], + "1aad7229f12a53679d1d66df7ff0598f2f8cdc2143be291fadc92fbff8826356": ["qmul.ac.uk.", 0], + "1ab535bfe413a96b953213a8d13ce0378feaf73c2ea4241cde645f84f8f3cfbc": ["whoscored.com.", 0], + "1ab8aea39e2b155399930676e6b2d4d43dd8325d5d753af4cfd1f5f1d327d2c5": ["xn--6qq986b3xl.", 1], + "1abb38365340a2cb11f8f6b0add5ded7841a1275dabae1d304d73a54ef8125b7": ["slt.lk.", 0], + "1ac3782bcbebd246154cd6cf5776bbec0bf1b38e97e26fdcf0cc9883486ebcec": ["semana.com.", 0], + "1ace95d451b134cde758245e35eddd88942a9888831437f92de043f2a161af39": ["figsoku.net.", 0], + "1ad586d989c61b5cf0f2de492165f37ecca5e7d5e20939d1ee3f4756db7fd617": ["filecrypt.co.", 0], + "1ad5d009e6ad4ce1502c6436ad70bdf10cf8aa5eb31a4cb11ddbec63eacea394": ["mapcarta.com.", 0], + "1ad9f5ec71f53f43e142789a4b2b8f7177d136421386a9f91085a58f3df17abf": ["splunk.com.", 0], + "1adb794e6196958d9c49a455fded2b11e60c361a4fa46ba8c627b6e5353d3411": ["exe.io.", 0], + "1ade60f379e7a7549c0e9f523420c26af2fcee06e9ad3fd38f3dcd4070df8636": ["azureedge.net.", 0], + "1ae5985970ab9a998253f4d5bcabb60e2fb9a8523f8bc753dd0eea56b65c9120": ["toasttab.com.", 0], + "1aed99ddd0b3137c223197d10d9b07fede7e594b540993860a516d495c39f000": ["alternativeto.net.", 0], + "1af2e4b9ae1af06d23e1fea297d7ab1bc37b8d8b19fa12bd7602a2187c008598": ["podio.com.", 0], + "1af7fd5f4faaaa2c4402dd8e3432876becd1a1dac54503ecbd232ba1eacee919": ["photopea.com.", 0], + "1af935f4856d30485934f1d18edbd0acc98e53a2cfdfe2e7474de5700e5f86e4": ["liveadexchanger.com.", 0], + "1b0008e6e91dddcd851ab4c933e17bf53248ba3c63def95f13ae83cde3be10a5": ["convertio.co.", 0], + "1b0dabefcabc7ab0a4e984551cfafa48b1615182323ed364465528dfdd6fc08a": ["cashkaro.com.", 0], + "1b10ff0bbcbff662678185593f11fbbd5e2962390c0d9520f247ac93303f2a91": ["popin.cc.", 0], + "1b1207d1988a26062d32df7c114f3ea4bed2d3b074f103b3bf69bc5c8c162890": ["rutracker.org.", 0], + "1b1223216b84298270e61920aa0f4ece018c314e540712bb88093c9b9008b33f": ["cssscript.com.", 0], + "1b147ccb0d05b3435d803d55b863a82650c9ce3219582553225ea5fc94fe4392": ["mercadolivre.com.br.", 0], + "1b169711f4c5239b9b2dbb8c91c49dfeea87e0b4cb5450d5d5672269fc1afbf2": ["patagonia.com.", 0], + "1b21bf1a3e90ea63c758ce915e7cdcd8d991ff484258c0b9f407867310138a26": ["eclinicalweb.com.", 0], + "1b23e59ef9b70ec2d6e623bc22e39e79339f3efb20c6ee3b6807082c3a176ba4": ["265.com.", 0], + "1b29283ab735a619a39b1ef7096e26f54414ec5b873f1966257bfceaff7145ef": ["ruangguru.com.", 0], + "1b2af688f21cc7d86473d88c26a1a710c03ef105359e121de8a9809f88320703": ["eventbrite.com.", 0], + "1b2be580f1e73aae301b46dc291f7f4d1df6bb1c893b52871e81fd91f26e4451": ["unitychain.net.", 0], + "1b36b1bcf8a819a5a846251efccb27147d55ed0274ff9c570deffe76d86565e9": ["njmu.edu.cn.", 0], + "1b483bdfc14eea72bdc73ceff981c568db40e63fa579983a10179b8c8e1c7266": ["lobstertube.com.", 0], + "1b55e39e5b51fef5aef477b25f2913cfedad44f6af4ad69593ce1748ed3253fc": ["frontlineeducation.com.", 0], + "1b66c2d797787b8cc434ddd32972971653d257987d68521dc913cb78d3830205": ["organism.ai.", 0], + "1b6ca1d8250fffd04ef0495ba939b0f45a2cb500265674af19c0ecb729a50ecf": ["anotepad.com.", 0], + "1b7083482943f7a319a6930c28ecdc51c227c734967dc7f32edb01d631635278": ["digital.", 1], + "1b74c889d3f57900be2b3bfa7544b731f16873f3412a6b6cb2d6e860c18baeb7": ["nptel.ac.in.", 0], + "1b7b959042457ccb9644110796a575f8ef42699721d9a5ae19a87a41f2046fdb": ["aruodas.lt.", 0], + "1b8b2c368b02d504f20ab6f5139d27caa1d4e36cc9ce2152fa274e6934fdbe12": ["sk2ch.net.", 0], + "1b91f2d2be4fd4cbfc2f704876292b15852ac151af548a39c08a540ec4868ec3": ["uoa.gr.", 0], + "1ba72be823dd7cd193252bf7908efb741d29ff3074e2a6710b586a3737c86856": ["videotron.com.", 0], + "1ba9d4c975ab7b7f34cfdad527272951a07e1a9d8e514f69cd466c815abd71ad": ["16personalities.com.", 0], + "1baad3150695c1a612d2a49db16430dbd3f721c8f014098489107ea29671b162": ["wynk.in.", 0], + "1bac1dfe4c2f4e41aea09d5cfccfecc995fed3715c433d0a6f9eaddb4b112e3b": ["nasdaq.com.", 0], + "1bbe785577db997a394d5b4555eec9159cb51f235aec07514872d2d436c6e985": ["ao.", 1], + "1bd704dc535d51dd5d77f005750cd75741ee64fa34402e0136832dca34ea2e4d": ["syurabahazard.com.", 0], + "1be2bcd3bfcfcbdbd8fd0a204df50dd7b892a5c457e24205a2b969546936855b": ["okjike.com.", 0], + "1be82afc5513860fd1f56cdd18987a09a3d07bb049a14ceb5872c659b448a707": ["anjuke.com.", 0], + "1be82c1cd91f37b4ac9976451699a10f36d61f9dc506cc94fdb0ec7e81759a50": ["starhit.ru.", 0], + "1bf2043137d9fd07745b4f840a3dee2f540c1053a17506b1df25d3ba4caae6db": ["bombuj.si.", 0], + "1bf5a98cd800f4ca766d8aa46936bb80a6c4ad9a1fa8cfa67a7f469026fa3e2b": ["tvn24.pl.", 0], + "1bfa34711581ac2f1ac3a15ef02a34cbc1402bea2017fd45785ada319cccaa85": ["ps.", 1], + "1bfe8703b7c8b14974273a0566656eb90b13fc5eb162b5d4be6fce258de91cd7": ["tokstok.com.br.", 0], + "1bfff54a4185957254c2fbc4bad607578754de43617f9b36034687e029332b28": ["brainberries.co.", 0], + "1c017e40bbe74c85532e3a509970984e9e42dcd47c0ca672cf5a487e0169d7f8": ["fudan.edu.cn.", 0], + "1c04abbb7dbbbfe4a891872abe2ba648a0ea76a28f139d1c88dbabd3563c66b9": ["bsnl.co.in.", 0], + "1c06022e2537d76e1ae2e04a58c47f925d24835f2112d6c9e7483a3ae4bd6d6c": ["juejin.cn.", 0], + "1c0aba88b5c69c993607bcc105799a66e877f04329ce869edac5355ff20ca2c3": ["posta.com.tr.", 0], + "1c0dbb7ae437838824449207951316efc53602875f46aae610b8ed4e2b9a3907": ["lamoda.ru.", 0], + "1c119b39f9ea70d1da049e85903c4e28702ea5a51f6099eefdf9846183f5dc7a": ["wyzant.com.", 0], + "1c14d707b1647417ecdd7ad32f1837c909a31f714f803582a8773fc5335b0013": ["bnpparibas.", 1], + "1c1544268266a9f1e6b8c81c8379cd053e89e65cc5706349051eb227c3dc5714": ["liquidweb.com.", 0], + "1c1676590e30c9d4694a941b6aa961723d1a8195e08839ca54d9b7e5b717bd1c": ["youm7.com.", 0], + "1c293418fc6668d00a76020904e4140226e58bc68562d0a13e101f9037994712": ["npr.org.", 0], + "1c2bc0e44e6f600f983d58f8793787e5221279d629197e694d41d6a8c10fc564": ["software.", 1], + "1c2bdad8ce07f0ae1ab6a7261739d311b25f0c770eae9b1209c9abcf0cac57da": ["freedownloadmanager.org.", 0], + "1c3a3d23859d8c4366c900593d1df8742a149ffa5a676311caead87866714e23": ["adultdvdempire.com.", 0], + "1c428020817fa08c84f073a27aae6fb43ff9519f3b8336b5a9a1e2e89dfa668c": ["analyticsindiamag.com.", 0], + "1c466d91f24bad4698a5ea7b29eccb42b91890e52351b90017253c9ff4440c31": ["gamesfull.app.", 0], + "1c5131808c1982d46f98a93a082308c700d937a66171dc06626fb7e66342c52c": ["standardbank.co.za.", 0], + "1c522067033f1def1aef73ff0e4d899d246beba700e702d49e15d1697ee76907": ["viajes.", 1], + "1c5255f7e80c36adbee86286094ef91db247e9eda222c21a8ffaa5d46b0a7cd7": ["sat.gob.mx.", 0], + "1c56ee63415c1a3d3e51ca9bc9aed43cdb8ef41caa8a3947523a39e8f12b5dcb": ["windowszj.net.", 0], + "1c5d8fa843aa05af2f11e0a1b982379b8adad0e09f881d557f625434d6a486e2": ["sena.edu.co.", 0], + "1c65e26e23944da5caa7be0deb6f7740cea19ca42f6323083b332652dc5ccb9a": ["tes.com.", 0], + "1c68c1df505a0ac09006b7e1497767e4abfc9e117158e9a58fce016ed5e43448": ["noip.com.", 0], + "1c6b1a0b57e6d6ac12d37ee16ff15d8ecaaa8a7cd6ff8417969bef1720580f17": ["screenrant.com.", 0], + "1c6e59c909a2f99fc091de13bc52fc8a448ff55e056bfcd62deea56bd5575dcc": ["alibaba.", 1], + "1c7133a5a6e8eb60474e5b619e6d0f1e39e8b99cd6b97a993fa4068fa051b42e": ["alza.cz.", 0], + "1c7e7ad372d92fe2d547a468fb05fbaa816718e2cdb7d65f2fc9a526be235db1": ["edebe.com.", 0], + "1c84100640bd1c594f886f7f71d6fccf7c963e5da21804117cc29c0ecee1e3ca": ["rubiconproject.com.", 0], + "1c84d471bb5c49060a934be53385ad1fe8be6ac8cb266c906d2a840fbc3f4bbf": ["quotev.com.", 0], + "1c88f36c864c0123356d949987a1813102731a02090e2bd7057be402281c476a": ["quickconnect.cn.", 0], + "1c8e29c30f69de7ed2e4cc76f1899fc8231cbad09dd9c7178dedc619b1737931": ["xcite.com.", 0], + "1c94c88f6a36090536bd144f8aea7215f7e74fdecbabbd16b8bd0b349c39df86": ["observer.", 1], + "1c985bdd6bcfe17992397e31411690b260260ddb1dcf0b2285c0eaac89c2438c": ["getfireshot.com.", 0], + "1ca96616a6f2d92b1ef7c27f14ba2a6847d1e4e0bdbbbe7d108b23c3bc7a9556": ["tnebnet.org.", 0], + "1cb9a85329e87346e849e77a6ef1c9b04a0be5aa8b796af3c6f489735f67a141": ["betcity.ru.", 0], + "1cbc55454f426e61b09d45c2ce47006a6a401861b5514fa6275c474ce53d1349": ["hasil.gov.my.", 0], + "1cc18c062c7efecb777dd95ca3c3d89ff5d1e59b32fff8d66ac9053c1d657ec0": ["docdownloader.com.", 0], + "1cc3a93ef07b2cc8671cef6b2c24e3b404e55d8f2b80467515518f3b211e1281": ["newspicks.com.", 0], + "1cd389226cd9c96d16e9dbc0c717f4eb15e56d20d1a052e97bad4bfa5c97722b": ["azerisport.com.", 0], + "1cddadc6511b10a4e39c2226549e82765d6ac645762828f172df23e151ecbd22": ["gomlab.com.", 0], + "1ce30cbec5620d680e6ea89936bb89e01ce779f49de92e67393e03e532867cd5": ["ingatlan.com.", 0], + "1cebb622adab18c2e5d23b01a3f4eb8a1a7f17ecd5513aed32689e196eb4593e": ["antena3.com.", 0], + "1ceff2729d819a76262b3456efa5fed92197b22bae14e188e353530cf862d40c": ["cutt.ly.", 0], + "1cf30f5132da4b623f1122a006a8103ddccd2f7118a5871b89b2078ca099766f": ["musaned.com.sa.", 0], + "1cf5429efdef1fe3f834d4a5ebe0242f8011bf498f141cc1517b5420689e889a": ["wikimapia.org.", 0], + "1cf72b13741395a0a6205c90aa3f761b3a285ee6ab84de6812a3bc37161e24b6": ["breitbart.com.", 0], + "1cfcc08d4d1c7fd6546bec69c61d283900fe31c5b52824f85524ecc94c15ad77": ["pgr21.com.", 0], + "1d006aebba7db6ff8ace90de40cdf5054ee031ddb9263ba8f37adecd7dccd939": ["deutsche-rentenversicherung.de.", 0], + "1d00f839bf2f5d70abbbdafaa0895eebc2b30fb82a4498ed33657c6ac8cef417": ["kwsp.gov.my.", 0], + "1d095fff73ac448e69bbd6c0967584d51236b12175cc7894dcaf7b3c5b10fc0e": ["animeidhentai.com.", 0], + "1d0c81f134a7088bc887c2a512de97970a83c74ad3db4e6ebcd8c32c3683f378": ["52pojie.cn.", 0], + "1d0e248b0916d60a9b380cbd56ea54333dcc7312d310ac2274c7533d2f8b736b": ["taxi.", 1], + "1d1018787434588df60a472083f466c3be5189760ec06840cddcde6c89eab4ea": ["aspsnippets.com.", 0], + "1d259700d0aaef23b4d623fe23a312c1ccad283b5489068a90181d0ec8abdd58": ["learncbse.in.", 0], + "1d276027b2252e89800bad5df4d5115e022d33e32d6a0bf59e04f9408d29f3da": ["pluska.sk.", 0], + "1d2e3c5fda68e0c1ce81a0f584386766b5775a1b897e445c86843229e32f92f0": ["dwarffortresswiki.org.", 0], + "1d2f2ce0bffc56cf5b78d01f45a31b9e53bebed42bd0d3ad749e5e390687e3d2": ["backchina.com.", 0], + "1d3285644170a5b7372d968102553a1e46c08852b6c97b18352f16a5a77f22d1": ["theweathernetwork.com.", 0], + "1d3abe74f389242192d2c05814b41bad17e5e9c5f626fb5220d5be448f49a203": ["kitchen.", 1], + "1d47c0eab617576ebc17e4e7ee868dc5593ab78e3989f2e8959bba36a42ee744": ["myasiantv.cc.", 0], + "1d54aa5c5b2745d078a95cfe3c8af1c9f4ab912b0efa1942c23a0b308878d75a": ["fosshub.com.", 0], + "1d5be2141405a71db40fbbb4a015d3c5c0f12a9e23b47dbeaa19b2910ff43bfe": ["brother-usa.com.", 0], + "1d5f940a8722e53b27e0dbafb91c3b491ad92d02dd6edd156ffdef20191f517a": ["pinkvilla.com.", 0], + "1d67f042385b46b4c4f9498f40bf1f31d42fa97ce51aa0318f237547fd6e0275": ["underhentai.net.", 0], + "1d680b578167e76dbf0ace6df54c974e2654dfe51a812b03668441eea2e60799": ["color-hex.com.", 0], + "1d7149a067df6e21b79decd031af7658d899bb7497a572171cd859a750852304": ["plarium.com.", 0], + "1d715ed61aa3e971583a0b0f89fcf3fb6a163a7628accbb4739412f0b772696a": ["jiyunhudong.com.", 0], + "1d7c964bec863d2673c85bcbe71e63a0c1c94f3a3fdcc66ebc4ceda8de5c230d": ["uitm.edu.my.", 0], + "1d853ef42935b25fba4917f1a2fb98ad23ff3f635a9e0dad45de61a3a7a30275": ["gaia.com.", 0], + "1d9546d9a63660c294cc785f81df114752c9b5aae5749a085d2f32048004fb29": ["distrowatch.com.", 0], + "1d981ca99aa2c92d4fb2b45d6dcc9ff50c019bdf921f05dbed02a0d8c26f14fe": ["helpscoutdocs.com.", 0], + "1d9cc6e0473b43ddb147f5c2aa58b55f92d8984c7895c24cf3a441545307012b": ["popularmechanics.com.", 0], + "1da5164906ed1e0d4bc8f2a68dae692ce170fd35184ec2e0ab2ff55c1a7dd675": ["scbeasy.com.", 0], + "1da8c24dcd42d0fbdcd1097da2b1c0bbc6bd74192bd39aecd39bc9c0a0691332": ["alalam.ir.", 0], + "1da9fae72d87584f0da797886d0fed1a76a15d2d63a3991a34275bd60cf52472": ["saferpay.com.", 0], + "1daa37ff65071078bfb540fdedb372d710cae4024ea50ac3cfeb912de7f78e1b": ["simplepractice.com.", 0], + "1db18478133b43fd15ac603a973e05bddb10512d377c037ecb90f164a0487652": ["eat.", 1], + "1db2f9d665966a956b8a4b3eb0abc9864d1e61895a010d5666af898110a79bb9": ["11klasov.net.", 0], + "1db49b72c45a42622ec3722fd9ccb2498625df9ba3e6e1f56946c0c36c85ac0f": ["br.", 1], + "1db6a0bf5a7b37f4c86ae952f1b349a3f5d802d7d06465feb70df29ce4dc173e": ["lnk.to.", 0], + "1dbc76b87f7df0e3593c8676a6d4fe9f399c98e88ecc4e770e07e1e5adbd2ec0": ["ridibooks.com.", 0], + "1dbf00c50991908e67eb98bd1db4abdd7c08f39743442764b636e8aeca893636": ["atitesting.com.", 0], + "1dc4c68c0e72908ccf55c076f84b030f72fbbd09e5f767530ff9ae9240328fe8": ["fau.edu.", 0], + "1dcab176017770a6af71003e820daab0e7a3edbde9353ac2964cffffb836cf39": ["hobbylobby.com.", 0], + "1dcc6b28466cd422f5c4a464de924f50b5aec4bc2eb634e770d11caa1c9cdab8": ["education.", 1], + "1dce607b49f3c482042712276ead3a282e6578189f4ac7257a1df995f0fef98f": ["auto-swiat.pl.", 0], + "1dd01345abbf8680cde23ef19b20cee84d59703d759ae74266949730fa91e074": ["playblackdesert.com.", 0], + "1dd6ad7ecaf38c295741653d24a190181440fd029e48c388512bf98cdb13eadc": ["nhl.com.", 0], + "1dd85998b902871fe46db693ca46c55adaef5632e1e28473253f68de65206c56": ["restaurant.", 1], + "1ddb94794ecffec937f38d02753cea329c003520be0dea1c35d310acd1f48eef": ["vivo.", 1], + "1ddc8b0c7c35b0a5fb4707c2c334d22ff90d698d776005507cd96df0b394e5ec": ["beamng.com.", 0], + "1ddebdfb153e8a0e85dfa9ccaa9673ec9c9cfc6422458c8d5e1cea93fd4d0282": ["bakecaincontrii.com.", 0], + "1ddfb92497a8fda1c5321cd9d21008f9bde42791306341e96ad77c9317bd5fe7": ["kinder.", 1], + "1de57080c0df59f735e8bb4d04301ffac8f3405ee2c00279c66c33ecb27d2137": ["typepad.com.", 0], + "1dfbfe7b714c22de4fd7075dcecfbd01f0a6a90011e0262677ab14fadb1af48b": ["wolk.com.", 0], + "1e0188dfd9e930ac3a5d6d676e3ca46f9ecf9588a643b6f8bfd246d7d7a1f362": ["skai.gr.", 0], + "1e03b588b28d2143eda2e60c75fbae1253204d72b65062b4246395a216453a48": ["millenium.org.", 0], + "1e0b3409fab702bd2f562d74780f47ee134a8616388ecf78427e205f84d6190d": ["astralship.astraldynamics.co.uk.", 0], + "1e0fddc00884c61b4c7f24618ee64aea173a7ec47b3545254179f002c2882574": ["wibmo.com.", 0], + "1e1ac19006efcd61f0f91bd5b1d15e842a1f69efbc86309efc059716b4b8488c": ["sexhd.pics.", 0], + "1e1ad171acbf5c966c7463b35392553b0ffbbeb0e36adfd4ba29b0ac64ec73aa": ["nitroflare.com.", 0], + "1e1f649465ee447bff235a19dfb0b742b8d678acfaa50a810cbe7557774fcc5b": ["rs-online.com.", 0], + "1e2235cbd045613132baedb7904e4919b0160eec343f2154120238924a759f82": ["edstem.org.", 0], + "1e23c41b8c839b41fcafd199f1102743a89776ed92f2ea103e2a7c82b8160d38": ["usertesting.com.", 0], + "1e280f79cd35f136820a5143ba05e8c961cfdcd0ccd16114c2fba6ccfa85f66b": ["repair.", 1], + "1e2c38afd589dedadc85564b7e8d4316251c19fd2732625127be2bc41c704679": ["streamate.com.", 0], + "1e2d06057ec4ed6cf80213b4a52a0b1867346ef1e6a8fe212bd0123cbc4b2add": ["zhaopin.com.", 0], + "1e3620586aa6c1e13c87dd443a22650ded1637e5940b4c10897be6cb49bb0d1f": ["dongao.com.", 0], + "1e3d9c666cdaf1bd252065e78c54ad05b07b4e0296cd7b1931cd3006f7420fb5": ["spiegel.de.", 0], + "1e4dc5a57fbe8f75ea2b137433d33a72b634929eb63412644626444a9a810ab3": ["memurlar.net.", 0], + "1e5e0682a6c78f4244055380e1866510f6afc3521e73478af413b0cccdc4ac19": ["barclaycardus.com.", 0], + "1e5e78920847902bc831fcab0cbf66689a27cbf090f3bb96ff40d7968e84c966": ["vidal.fr.", 0], + "1e782e79a0e2916dead886b3d02316fc2196c8a2f8439806961e18c51f564e5e": ["savvytime.com.", 0], + "1e7ae38ede6f58864714200d99c50f3d0ec70630a091aacc007b557845d8d96e": ["schule.", 1], + "1e7d5880b425ca0820afa65b9e5a3091034b1015d2d534199887dcdd14c1b610": ["vuejs.org.", 0], + "1e7fbddefff1105ccd7add7abb9e739bc9896473950a863170306d6ec7ea5b11": ["9k9k.com.", 0], + "1e8ad89d251c527d25b598e89d02943935ad934e355ef52c2a8e9893164652a1": ["descomplica.com.br.", 0], + "1e8c7fa9d93b7c0b842517e8f173a6f14b6ee4a370e0d42e2820aed409b8f13b": ["irecommend.ru.", 0], + "1e97b49986c4aade3fb7587f1eec13fc438dbca0ca98e738d656b7aa3fbb58e5": ["elo7.com.br.", 0], + "1e9a90ae2d887eb8871044c5d9a224d83f30a44514fbdf339ffdd1cbd8aa33dc": ["ikyu.com.", 0], + "1e9ad00c4be2f71a6671bb6500febd413b4d3e64af78b07da40e06b0be0a341f": ["mashable.com.", 0], + "1e9d1420157f3d30a02492b0058d9f57f48374ac53947f12439a50901278fbb1": ["qianzhan.com.", 0], + "1e9e08c28847b369127652aef37325eda9a7dc9ad3c698c38b9bc111881ff088": ["nmc.cn.", 0], + "1ea097bbb725a7e26836ba7f7da53732ec513240b50ec1c1762e4ef654e7708d": ["directoalpaladar.com.", 0], + "1ea447296ba95cdcc0d297efe1bbaab0b646028f83b45691f9c075c8370d5616": ["hotline.ua.", 0], + "1ea9cc832cea4bcb569246c1001d15d1843180691ddd755e3ceb013cce081d2e": ["collegenet.com.", 0], + "1eab9e373e9c634e0c0919ac56725a1e3a8c0d2c80d26530202252e0ed747278": ["wps.cn.", 0], + "1eadfceea411aa69f7792856e442f01a7f7c19221a3eb3ac3bf33a104adb27a4": ["zone-telechargement.tech.", 0], + "1eb771ad70bab19b5d009596c4278abb4c3362416743e021336d90c6fd10637f": ["mmo-champion.com.", 0], + "1eb97374c07f4e4f97f3b01ca17934b9a6ca1a040c3d21c93ed4acd563a9ef13": ["dpreview.com.", 0], + "1eba4674b64a4eaa3ebf319bd5ae7df750e49b9631c71d41fc38d33d4b86eb99": ["xn--bck1b9a5dre4c.", 1], + "1ebfb48c3f82e3b13a45cce1f9c96eb9ea7c2b42ba2407d59f5a39e137ac9300": ["notebooksbilliger.de.", 0], + "1ec744cd8b489b1f9b08f6c1ec64ca01341e8743b0ab78ac3a9dee147270ce4a": ["chinairn.com.", 0], + "1edb387595f121a3e61f69b4bc9c811e348fa24bf0b15bacc81eb97d3817165b": ["aigei.com.", 0], + "1ede32f93a85d3ee6e6268fa1bcb6826410a9c5cf8adff566bc0e175fb318c49": ["ovh.", 1], + "1ee6aeb1e5947ba8dc2f85cfc58c0d57d82a044e1ce2f6f7faaae114ae98f1a0": ["ixl.com.", 0], + "1ee78b31377ca17d048916c9ed4df67ae86a3775e9b76492aa80386bc7a97786": ["icbc.", 1], + "1ee8b781da0422f851b3585fc62a3c5b1ecc708007d9197c9b1f4ff02e9bc413": ["guru99.com.", 0], + "1ef19b8919e64041d2f6635ef905b5713aab8b63009899c989a737bbe6d3ed99": ["couponfollow.com.", 0], + "1ef235ee1091ec85200cc2c11fcdb3bfc67b6a9784d8a69101e33b3d8d12c947": ["tuttomercatoweb.com.", 0], + "1ef4ccfacf609e94e02de1259a31cbbf13940d42e8444186bc25cc361030e40c": ["investments.", 1], + "1ef7e61f0c355920e34a698db6c8daf95d311308ad2274f10ec4262ccc55f75d": ["otpbank.hu.", 0], + "1f024ee5cb409270c23e106c26eee420703d9c2488707c2ace869dc76fa65238": ["baomoi.com.", 0], + "1f089a76492711d1c4e164bd7538ac4daa4f29ec3450a828090d32ed8f075e23": ["gloria.tv.", 0], + "1f0a2db8c716566d6f7006e731f023a5d98afbb9f257e106b348bed543e481c6": ["mepsfpx.com.my.", 0], + "1f0fa2dcc0e2650c2027ff27e6f80eb8c2a793c48f72d10b09e76b2f3f1acffb": ["avdbs.com.", 0], + "1f101e501e0738f2b8986d24679ff72025283e713036035e139b4d69e9747d1d": ["tfpdl.se.", 0], + "1f137874cd7f244886d6a6aaa107e398fd5775661058c0e1d233211f7e632af6": ["10jqka.com.cn.", 0], + "1f203cfd9f0a8d7c23f045b7a22710a922854c389fef6394d5e190a7cfd5b656": ["filmibeat.com.", 0], + "1f2ae2aa5f5b7b12d2168a22026507d7b72cb889639a4450f82cc6b26f96bc48": ["nerdwallet.com.", 0], + "1f2c71d2d965d1e173f05bd769a6b7e6419aceb307253c8eaef0dc723e9269d7": ["imgmak.com.", 0], + "1f3ef10e038f1d8967bb4ce6fb73435bf1e8d496a0508b910a7696227235b47a": ["rawstory.com.", 0], + "1f41816e64beb69cb3f462150ff6ac5803ff9c3df37038b1df4103f343e211cd": ["gopro.com.", 0], + "1f47b826243aa3ad0e030724ca887f5b6bb17897c3bb3e5d93702a6ef0f0b3dd": ["moscow.", 1], + "1f52b0552ed9677012bed9aaac2b2124525f405172108cb78b0a1a8a9b22429e": ["niezalezna.pl.", 0], + "1f5dbcd489788d3a75c2171fd4c7e8ac21d004f86c40784b4ae99d7d82e5826b": ["anchorage.com.", 0], + "1f638939c36eab5b78aefab9ff3fd520cc84ba383a3b223761d7b3ec6eae1714": ["navitime.co.jp.", 0], + "1f6d587a37ddec8e80bfdf5bb8d6e72bf5ca05a1b31f6c42865d51965060f343": ["njuskalo.hr.", 0], + "1f74d800670da36c6c0807d44f2a9a0587a4e514bb4f55075667a5a5a4fd1b95": ["jot.", 1], + "1f7562f91f9b2d6ff0f334414953aa36eef61ddda66b12df5243b05c766e7cc4": ["gmanga.org.", 0], + "1f7637ddfd239ec480e7ae209a5942f3dc47321a00cb44c7b456a1f18da98a41": ["talabat.com.", 0], + "1f822be8eb539446816aaa65bb630125c4a6a72a277d5663bf30c8aa8ddc67ec": ["kol7sry.news.", 0], + "1f853161567051010fa537f01064f9711152ed33581483788715ecbd350b2774": ["breuninger.com.", 0], + "1f86f9887a312c6be3fec65ab68c34d94c64bafc6baa2c51efd1d98d21018096": ["kundelik.kz.", 0], + "1f8959271425ee8a5fd914f17dad77f6c43e0d373c41b33845d732496d0c9871": ["chng.com.cn.", 0], + "1f8a504b154eef3997f59a84a8cdc9ccb67201954335ea5ba1727b01843a9518": ["worldstarhiphop.com.", 0], + "1f940131ba16938bf14be1933c893f87c65179d1ede1e118e9732de753f187fe": ["tasnimnews.com.", 0], + "1f971d91c224b328b54631dedd753d91071ad5dae17d40325b62996d7c0a6309": ["nyu.edu.", 0], + "1f9a9bd059270a8163a413e547d557063634b97895c631bef672ad767b39a54c": ["wales.", 1], + "1f9ff1b4234f11716271a69e3904818cd5ac8fa2d93e91e7bd2cdb68e405fa60": ["xbiquge.so.", 0], + "1fabe9b92bc51f8fa47adf60af17db8647a9e3c31c8b283b142e7d481f98525f": ["tradedoubler.com.", 0], + "1fb014a6df4297f24891c127e1e74ec932ab9c3e00abbf08ccd36c25572bc589": ["gnunet.org.", 2], + "1fbf356578cfe3f8b8a4ea07589b9bfc3fd1418d790c788d65706598daa4c824": ["bmc.com.", 0], + "1fc0af2aa739f4aa6819ff98f90ef0bb2aca1ce6099d01316721a60883e8997f": ["mako.co.il.", 0], + "1fc8c0e699da2ca06102fc8dfff86a4702d05c7fe62a3656d3299b57de046acf": ["realsimple.com.", 0], + "1fc9c51524ac39998f30ee90c377d3873755845df89af222accd69402475b0dd": ["cfi.net.cn.", 0], + "1fd86680004867cd65ecd26209f50820c9fd0571afb1fd82542d953268413b5f": ["insurance.", 1], + "1fdfed739847cdce5c3d566c5864ee8292898caaf135c7c9395b8811fe6ac4ff": ["policia.gov.co.", 0], + "1fe421fde61168da2c77bc111fcb01cee035d20612441083eb3a25210f5c633c": ["quebec.", 1], + "1fe865903920960da0da31632ae5cba3e95962156f452eb990cec013d37da4be": ["g4media.ro.", 0], + "1fee3d45d42e1b21790f77af7edf2be8cdf894222784b2b0e150e0b6a6672a5e": ["fortinet.com.", 0], + "1ff0ac971d59dcdb463f2e0b1a270db1fd319c172d6f42b708ccf0dc565af975": ["fbi.gov.", 0], + "1ff68204422c2a670b2abd30aa330b94a0de40a017ad952c2435fcb412ac59fb": ["mangakakalot.com.", 0], + "1fff87f2d0ae5046093613e030b7ec288793373cce55a2706dd63e0843321e69": ["inoreader.com.", 0], + "2002ef3816003a174fead9d9df994d77d1615b21ffde1ab0f36f9a51c87f870f": ["oxylane.com.", 0], + "20086323f15cc8cfc650e27aeec8f4642536040d76044983b2c35bccfb392ca8": ["mangahub.io.", 0], + "20088297ebb03b407eb0b7c8b903611ad392409295d4576604d3731625e7aa48": ["photofunia.com.", 0], + "20092a45c8e7c8a08448fca20bb88420095a62abedd0c9c0ac32d4cd4b779dd9": ["tci.", 1], + "2009fa2bd4b95afc2c6321322b77f8b07b2cff7fe794df06610b0e63bdede90a": ["treccani.it.", 0], + "200b1388381c41158d779adc5530aa0f7631ec49092df6ea5e2f5ce26d7b6888": ["desire2learn.com.", 0], + "200b98c1d022c3a138f832b8f72f6081042d998455802d4471cdc23b3cc13f0a": ["wg-gesucht.de.", 0], + "200bfa956c28611b984c355f81a4e02289294b23b724a2d88811e2e5d96c5f7d": ["pgyer.com.", 0], + "20229ab26b8d610d388a39139405085b6045b65bd6b65087fbe95b6dee4c9dac": ["anoboy.zone.", 0], + "2024edfadb860e0be4a9f37e84277737c6b9b1349459269a7403dc16153063be": ["trafficfactory.biz.", 0], + "2028418a281a9ef95affdf6c18677aed588a14fb4630fb289fd62cd47fa536ce": ["aastocks.com.", 0], + "202a9576e803d74924c7ff85200d246d357e589eb99d9a4aa411354c614996a6": ["netbian.com.", 0], + "203115e6acfd6b9dd43384cbf59db7565c3c3a2ea2d38423846431d88a5d955c": ["sendgrid.com.", 0], + "2032f51b80b764b516318f3b786e270d22aa26415b04ae315b44eadc5fd24a17": ["namesilo.com.", 0], + "203499020539683256c74f1abfbda31dbc604a31b1eb6af5b1b043e578b0f766": ["pconline.com.cn.", 0], + "203ab84b75b509bd57d967ffaa7c75ab3010d854dab915dece84a9b57f04a781": ["depaul.edu.", 0], + "203b5f0c8bab23a10274164543eeb88dc6ca52240b4b186f00069493bdeb861e": ["tractorsupply.com.", 0], + "203cb08e9c584e0e86ae190da956c63c3bad8ed3ad32810ecaccf9e20fe597cf": ["bandisoft.com.", 0], + "203cdd766d0f8d86ddd517ff1eca58519dea0a05cdcbdf2ef662366b245c315e": ["airbnb.com.", 0], + "20507770f32ada99800d9e584fa52da7510748e36ac135dfa7c846bb83f77bbf": ["cw.", 1], + "205811f2f860793b0e1148f6c94ac01771a6ee2735cfa458ec9fae844f7a6d5b": ["hamariweb.com.", 0], + "205e5b3f8e0fae990145810a5032fb273cdd84862b22ff4f174c0f16e5a87eaa": ["sms.ir.", 0], + "205f74e0e56c05db988dec01ef0603f7462d0481b2a5ecd89ba676278351df39": ["gigya.com.", 0], + "206d821446749ebbe3ae28e0542ceefcfe18739c8fbf20cd75e2ebabddc4e86c": ["e-taxes.gov.az.", 0], + "207dee256a640df1affecbeaea212ce0936c6ed7df4aa271ed476ef475182640": ["getuploader.com.", 0], + "207e738dfeae27e40ba3762b27fa1b00250621a6ff1bdd68cbc516291794d4ab": ["babycenter.com.", 0], + "2081b90c20d0b989f20b631e184f3386eab18e0365b676db9a82be32079314e6": ["radio-canada.ca.", 0], + "2090750058a187843de549360a48967c86e465e135a3d75439eb27afbac530db": ["interfax.ru.", 0], + "20967c7fb2ce9b3c4e905e639e0233d2111f90bfebde0804c3f2f2dadff43512": ["cityheaven.net.", 0], + "2096d717fbf530430d477ba1df30620f709a8865e53998d2f3137618485a419f": ["mykajabi.com.", 0], + "20a102a74ec69e51cd4f30b0b0cbbf56fbb2957d6cea767b8d3a514a6dac2149": ["prnewswire.com.", 0], + "20a70fec2e8625fe9a66f02977430ce85c267b8f101759e5595c4d0df8e56592": ["ali213.net.", 0], + "20a997649fce45f6aec19871498926d7d7e5b26b6aa39ab98efe5078710f085d": ["newegg.com.", 0], + "20b47d4f7a558210e61c79022c9a633bf83b9237cece86cb2a1e5e8da57f92de": ["therapynotes.com.", 0], + "20b7e97c538dc0cb5c1e64fa71804d06df2e52d77cee1c2bc18469b28e4bde78": ["dndbeyond.com.", 0], + "20bdc343a1d8eb35afdb91342cf1314ec3db8530257ca41c9e463d81d03a541f": ["pbworks.com.", 0], + "20be21a558af341860b5e005c852e2da82c6a2aa8ceea73799978d778234c6f1": ["elgato.com.", 0], + "20be3e8e3d83814cf65d165eed248978395671615ee6d280b8664665b8e6bc54": ["ads.", 1], + "20c1cbced2a33c4da29eeaceede92e9ab717b06a013a59259af4ee98f9d2a94d": ["n-tv.de.", 0], + "20c94c6433b8ad9e53b847a55f3c6fee7208744bfebb5dcc59694ae838294b85": ["itnext.io.", 0], + "20d4a738af0ea698a041c81c28c6ec2ddcebf332aff0172e46301b5c3414872d": ["onlinetestpad.com.", 0], + "20db02e60c376ec782057951d33ec0a3a155e10271529ec54561a3045305844e": ["base64decode.org.", 0], + "20e09b7496536c45fbe0069f34df668f02e2b9c1fa4f0e94578169e488ccf0f2": ["baiten.cn.", 0], + "20e1a03a942b54fe006cd830d95dc39e1e20a67b12b591d2634f549a731fea35": ["iherb.com.", 0], + "20e3b84ff36a296396eab57bf3d506a258d5dc7d7b61666b13924417cfbf742d": ["animate-onlineshop.jp.", 0], + "20e73b8eaf63ac90fd58fc4c672ec5705fcebfc66cf5fac20d7b4c16629def0a": ["wlu.ca.", 0], + "20f76c921e8b49e019f2382c57fba4d59a28007518363d0aea941850a5fc2099": ["anidb.net.", 0], + "20f7fc8c55045c5ff7c81397d0fdd09a45cab7d0457f1f5473c1d8e44feafdc7": ["palmettostatearmory.com.", 0], + "21150e6e82f515b59c37a45903439f8b8aa4c8ca265fd23cea94b118b3b60a10": ["almalnews.com.", 0], + "2115e71e5e4806f0a98946c4992b088a6d774239b12f7b6b0c2e29f6bd87149a": ["er.", 1], + "21213f9eb37280f19aed5b8474f491135747b8135856a0ad811d33664ea811d3": ["ubi.com.", 0], + "21390bfdb73fbd82f31a3820e0507289ecc34f289d5b3ab3e36e0a653b9f76c1": ["intermarche.com.", 0], + "213976060304d2538edf50af48b81991b89bac5a996fd900f10656582026486a": ["mhrs.gov.tr.", 0], + "213a6846a43c8c35cc5444adee898893884e2722b2ed2f6b9fe9802ecb9ca622": ["telstra.com.au.", 0], + "21477b096bc5585da42f66f1cedcc1f3e07921e49b0f482f69a557049291a32c": ["xn--2scrj9c.", 1], + "214cbc9d78e41033ab64fc9795cc778d57cb857b0f0e8f1b6f693ade5014aa3e": ["serviporno.com.", 0], + "21518f862d75a2f437ae07e4108068a82e1197be3ba360ca7bb5c76b61c2e21c": ["offerup.com.", 0], + "21542c7c0687720f4f12e158382a8e3bc96d21f67751c01d0c95c4912829f118": ["basketball-reference.com.", 0], + "2157f817f2ed2aa45ae862df2c37f6b6ff3e4c7e8184baec67278f71f6cee248": ["birminghammail.co.uk.", 0], + "215da1d34e18b9a930d8815d3cac34c2f7b0bd1f383d33d05233dbf264181a92": ["myfonts.com.", 0], + "215db9307253ea254a2b0210ef57162439aaaa8bee196d41cd2fc21407080041": ["volkswagen.", 1], + "2163df6096f2673d647a14b6f622e78e0c2d31f1dc7010c45f18501f3da0acd1": ["upmusics.com.", 0], + "2167e29b891e19bada23fee16c6feaa7d0d3c7b5548281c9211c7103bf90fc36": ["ushmm.org.", 0], + "21692ff40cf076dfe202ea1f829f86361a3b11a9f34ac7c1acd0d19c0dd73058": ["bdocodex.com.", 0], + "21736b12f56051b5522e6be235b5a7f6e9f14406b607fcf799b0c6070cada2c7": ["fapality.com.", 0], + "217f674f8f19e67b73a56b993b975ae25789e6e09e36dfc816920e7425b78494": ["billdesk.com.", 0], + "218953d73da06181e220841f45cd0a87b36235bbc6014b2068e03096643da6fe": ["toy-people.com.", 0], + "218e6b13bdb2c64f74120a07b0540682f061d1416fb3ffd91cf6044d0c526ce1": ["tori.fi.", 0], + "2193b4ad4f03d8ddfad723271207fe9f3d7073bef2a8f49c2729c6721ceba649": ["stanford.edu.", 0], + "2195ed1ec126ec1de6e53427a550f58520607e9be7bb41f6d6a81ef8b195af93": ["momoniji.com.", 0], + "21a4d040114bd979837d5142b491e9541df5b1f8a723f99b642382c33dfe0c83": ["friday.ru.", 0], + "21b84847cc1290e923705cf8d8484a2d968f4cca0fbc58ee7c9c04eecb483c1f": ["morrisons.com.", 0], + "21b957423c68b7bdd7e5efb585de1d0d1978a5f054416fc9a662175c8f1b263f": ["kenh14.vn.", 0], + "21c3661f809bcdeab5cdd29ced781db5cff41b00d01329def360d03894ba17b5": ["worldometers.info.", 0], + "21c7fe2fed57f5d78cfac80304bf43eea1eecc2b13328ec1aef7731423321d6b": ["htmlcolorcodes.com.", 0], + "21cd538c0bf1b46c4324d964f4a3682eeacee252fd709026993387e42a561c1f": ["synonymer.se.", 0], + "21e5ff0dd51b4383531241085fce240dfbe96eebea2f7e347bbf4478d2ac5c02": ["fineartamerica.com.", 0], + "21e93f1ff7e506ba5753ad961c2edfa1b396bff91dee51cf4cbf3f652715a620": ["ruanyifeng.com.", 0], + "21f498ab0eb4b3aa8e76e74e37fad41e2e841b58d7e965d12f8de778c0689e9f": ["jvid.com.", 0], + "2218376e5be6290a25e9417863f86f23ebce6a32af3e54f670dc68fe097cf0e1": ["ipn.mx.", 0], + "2219b158719035f1650f72e71663a6e98641666df686f8813d4bfb9608ded296": ["funnyjunk.com.", 0], + "22213d9064214bd6a65d695bd32495b09d46f939efbb52627ed39bec157c140c": ["sensortower.com.", 0], + "2221dd27531b92501d07efc7ac2d4e80e3291d3b400a0f1df2b0b19ba7fae170": ["decolar.com.", 0], + "222516b3868dfb76d69e5c668d34c96e17cb137f9b576dec41a0979c1c5561de": ["livesport.cz.", 0], + "22382bbad1dd286198460a84ce62c8f0dbb10e3a6dc78fd3444d18dfaef18436": ["nra.", 1], + "223a8fc0615faf10d1189cee0125885c6bb62d9e0d0db2ccfed7d51503e79b03": ["sktorrent.eu.", 0], + "2242bbb63241df276becb6bd9c270ca59db46ac930519b3e493583b8f81c4cef": ["xunyou.com.", 0], + "22497be66b26e42226f9bac971627adf91f66c7becfe867057d9340d17ce1638": ["ynet.co.il.", 0], + "224b9a6a7410f227f5bcc2a309a4184ebcb3abdd71c30d39e168a7ba33e34041": ["mturk.com.", 0], + "2250fa0b557f93b1d92a26e2ca55cfe2354e416e9d674277784cfb09184b41b0": ["coffee.", 1], + "2252879ffd91a09fb2149e4e30b3d0de0434940f861830e2ab2b514a0b33049c": ["afp.com.", 0], + "225e2e18c2649a111a225de0e045899d7acc32cd47c6b648f0286c4d5e26e4b6": ["attorney.", 1], + "2261b2a8d9440c0588acef128e505a048dee0bce15889e8288e70e6a03f62c85": ["derstandard.at.", 0], + "2264ba1159c26c34091c34a85a4db91aa9c18701d78ddf614687ea112042bdab": ["productions.", 1], + "22686412ba6a352fd13c780e1d01ce2515c705b8d26ef928004a3d48fdf41f80": ["ais.co.th.", 0], + "2274ba71c606c13ac729f57947d9954fb8513b851771498fceabd53556c4b8bc": ["1point3acres.com.", 0], + "22761d7678d6cbd89593affe84787df17db7f941418dfe7990c160d4474a47f2": ["venngage.com.", 0], + "2277af651ad894d9b70d2ce61e087b70914bd55f4d2b4fc5b1e64d6324418b02": ["chicagotribune.com.", 0], + "227b5f70cbc4743b765255c355832347c0bb5a4b83e27fe24b0701cc03e631f9": ["animepahe.ru.", 0], + "22801f3a04e9111e67d4279eb0a6dbf6e6ae2038f8636ce7d86dd48f5f7925fa": ["fontmeme.com.", 0], + "228423ecc026e8f4b5c2b6fdc590a8c914e5f68798bb41697318cdcce09dce3a": ["caranddriver.com.", 0], + "2286b75222887de9f7ffd04d4f5e5abdd627be9524b7e124c1a5aed030b39326": ["mrbilit.com.", 0], + "22875f3a7b2ad5675e9b5e380a784cbaa534cab04542a716eae0aa709ae26a31": ["capella.edu.", 0], + "229070b0865635797700ef062abe7fdf9e669df0877d562f35506b25352a3219": ["pullandbear.com.", 0], + "229163e3c1ea504b511b8216b118fd31d226238c60b9d79418dee8c9e648655e": ["xn--mgbc0a9azcg.", 1], + "2295e66b54c5079a363c421828ff48817b41db7a037c2166a4655080a6f7700f": ["qcloud.com.", 0], + "22961af0e1bc7e2b89a8979c9882b47498b351445d394d6885bd6714469a0dd4": ["property24.com.", 0], + "229864a92e041f485908fb37417bec233698db78f21252d458ca3514be31b630": ["ourworldindata.org.", 0], + "229c16d4da1be2e8a174da5c64567657d3fae764e202f2a52c2fc92bb6f378fa": ["aviasales.ru.", 0], + "22a9f637f68b2188c75aaeb63fc101e2b06124c7f93dc63aa37a2eeafef2ad08": ["thefappeningblog.com.", 0], + "22ddf0f20c4e1775b36e47157228b6b5b92bc9305fd15a5987297f33bd69020d": ["download.", 1], + "22e410467d8bddfcda602a22febcb87346ee1943fcd8dbe424997c925b726c95": ["cima4u.mx.", 0], + "22f056f872f27134360d413a8d3e55832c46858cf18661c37d92f7820a5abf0d": ["wisc.edu.", 0], + "22f15eede183fc3fd4e57107309fe532d827a6db931a6d2a54af182d4df599ff": ["warhammer-community.com.", 0], + "22f57bb039641c7439cc882b8d53f82f38b44503565aa7991a217648e961871d": ["yomiuri.co.jp.", 0], + "230af9d54f5289f690347d8473dcdb4bb16480f171e2f0dce390c8a1e5f23e7e": ["aweber.com.", 0], + "230b709f78b7eb7e6f4fc55df3df72ee4e0c28b58947923be3e247865c017fad": ["colaboraread.com.br.", 0], + "23100a8ebccd167e53ceaeb7c5d74560a8ac05998b6fa4e253be4902e7664df3": ["colfinancial.com.", 0], + "23145b5b1cde893838afb7cc2a9f6a83e9667dffc91d77ac9756e8a309780e4c": ["syosetu.com.", 0], + "231ba7aa1bcca5953d19d30003c19ba939b850b18077353379b932e62180dad5": ["niaogebiji.com.", 0], + "231e48248ef86e5c1fda07083800ad42547ed7f86ad648990daa6f3dca59700c": ["zhixueyun.com.", 0], + "231e5dcfd9916a1b7611e6026e6312df9b3476d82a92f4c60746cf9265266cf6": ["metlife.com.", 0], + "23201d6e467431cfd5d2f8b2c6d0b0378fcdf65665e30af4c12c9a736b3237ae": ["alditalk-kundenbetreuung.de.", 0], + "2325182a623302f18b4bf408a8d0edc0eb719b864d7f39cba5bd067e2e4bb882": ["tikiwiki.tiki.org.", 0], + "232b76361e41124f3db839036a05e75ab57652083b14fb167ce915df1a37f2fc": ["shiprocket.in.", 0], + "23348f5f616b7b9a0e28354ba5916e00328acfcc342c35b14525e63c2e8eda5c": ["nios.ac.in.", 0], + "2336be3fab134bb7fd8a218e8a7194582c25818f642f81452b08a56d7a84ab45": ["wikiwiki.jp.", 0], + "233b446111c587f92d4994a2754edd71e5b2eb7e1cde02a09376edd2dfb9024e": ["golden-farm.biz.", 0], + "234194a979d294964d33f2c8e69a499bc779136944374d347ef74feeeaf12847": ["idol-blog.com.", 0], + "23430fe592deba60fdb85b4af0aeaacb690777df6c6ea36d4ea5fa9235e2c141": ["hhu.edu.cn.", 0], + "23489b7d9f12adf2bd0086c917018e9cdf005e5a838dfa8cef9fc3c58fcd0fe8": ["epravda.com.ua.", 0], + "234971e3c9bba928a23a8a89d22055d578b093109e7b283f193724f006c12400": ["freee.co.jp.", 0], + "23517c09d33c32b4e4660c003d025b255d9ed0b3d2b969aa5ad45a662d60257d": ["company.", 1], + "2356161395dce3e7d01e54925e4c1da72cf02d9fb63e2af14795723b72b621c9": ["watson.de.", 0], + "235c4358254444d5fc50abfca195c1e777215ec2ae752a33184172b3da00f5a4": ["despegar.com.ar.", 0], + "235c67dbf37e877c82225819d0d85d7b54c70de0a5c7724117861ad6b74bb850": ["shahr-bank.ir.", 0], + "23722cf1cccf067627b0e0ca635f5c648213f0cb05a0bddd6ed13b1df72b6846": ["womenshealthmag.com.", 0], + "23749e73ea8744c25afe6d9343a10c73f17b97bbc990a50b3bd0cc6bee323b2a": ["free.", 1], + "23758251a6c4bc182ddf60569c3f13e9fa969a2e62e4a0a75506016b3048fda4": ["weber.", 1], + "2375b8fd564aedcc97dfa0b34d5eb4842728b7d85f6ebc3e1911fb989c30b3ce": ["tienphong.vn.", 0], + "237845f2670c31a3d72ef1693e07bf0b7c9fb55a9d3fd712c05dae6616f614cf": ["jlu.edu.cn.", 0], + "237b6a813dab497a11a13cde47f02183f66b425f153b9ff610b9ca2af290578f": ["lzu.edu.cn.", 0], + "237e7ab1fc3ccc875202b577fe0ba4a4d18375e2ed3dfcdbd2024e19149b9440": ["bikewale.com.", 0], + "23812a868f8a3a829b00bff48ca3349bca59e67470868347447555b0c85e90f0": ["wal-mart.com.", 0], + "2386a06e89062696870f57d2d8e65b09ccefff2a37ae50813b7f88008886bc6b": ["wikiwand.com.", 0], + "238a446591f64527c5892f7079fa66a0b34649d975086c0087898a0dcc059822": ["scielo.br.", 0], + "238a857ede7d1182e6538c243320951fe7dea69b8ef693bf0d52bffc738174dc": ["transaccionesbancolombia.com.", 0], + "238aed51288209a16474e3f56f4cf9a1069a1e780093327c455cd2e682509d85": ["newsbeast.gr.", 0], + "2393e5fb832be8ca32f5732aa460eb3d7a982075aaba959f26489d7caa9f5747": ["nperf.com.", 0], + "2395e9a5c6211d9267396741e5e81c858f3b811c713e896ad1eea676716ef78f": ["lat.", 1], + "239b722046c199b163647136366f31397fecf6e04ab9305e5e19411c64cd5051": ["channel.", 1], + "239c99f41d603f54ebedeed99919af5ef3818c8b99121b9e4012ff114674532e": ["letgo.com.", 0], + "23a0459733828bcdaa89d21415b544f2f094abe166884cd391e51e822c5d57e2": ["zgjm.org.", 0], + "23a1b5b158410da9f14c660c43c3e4ca1045b98b9fcfe6f21ad30cb72db7787c": ["naver.com.", 0], + "23a7ef0bb4b5be2d462db6d23977c4168d70ada55f32ed166510506a0db1d502": ["desertcart.com.", 0], + "23a8df388c77a4290c771cf2e87cc1f569087b99268ce53a0871411f0bca9029": ["xn--45q11c.", 1], + "23abd4bc7ec3231d0d1581e414693ddf87185bbb3fbb4131745c1ec74866f305": ["scol.com.cn.", 0], + "23b31d2a761d43736a913d1f200bba323f0466514e0fc1e645a2bbad90b45336": ["dicionarioinformal.com.br.", 0], + "23b3a1f0835ad0ffa92928f67718301f043bc1e4989f42da0942def90bf6ea60": ["socalgas.com.", 0], + "23b42c369a7560f188a0931c3b5888db1fc8642b5d484c10390a3f441dd41072": ["lofter.com.", 0], + "23ce0d84396476b73d1bd82a048adf3bd952cdc8539bc6a97a2c685c8d9d829a": ["csgoroll.com.", 0], + "23cee59b25f6d52969da9f31159eb559076992a1ab6ce7128eb7e82f5834614f": ["edx.org.", 0], + "23d0b95676e3f450ff71115c0ad80a040d0b4187044ae7573478aa68b09622a9": ["agenziaentrateriscossione.gov.it.", 0], + "23d95fa2b36f0d5c406f088ea94da17eebda05e38bab30fe7a0f72e8caea6000": ["service-now.com.", 0], + "23e58c2f7c4d08d91c9f4bb0950c665115f07fb484d058aeeacddb4b310e6f1b": ["swisscom.ch.", 0], + "23ea069469dd551710e43d31d3cc656ed8ae90b3951a41ce072876f8c38630fa": ["webgains.com.", 0], + "23f191198998c50756dc6959e9a84c2db8cbb272be0af13efa9e129581dcf2f5": ["coupang.com.", 0], + "23f246760660abd1564109a60291a2052ad7ec2967b9881040611ebc5240f75b": ["sariasan.com.", 0], + "23f4f19953dd1ee84040aa9bf5aa32677b49e1fbab8af7fe44f36c647eade63e": ["fromjapan.co.jp.", 0], + "23f6056f74b3f2cce3e898fb16f16a463b521477a90b936304d5494b9637f7a4": ["verywellfamily.com.", 0], + "23f65e7beca69b53ba5ea0df3f248ed9260fc9104f89605924cf06ece382a0fd": ["rutgers.edu.", 0], + "23f6dec8e6497729669ba25e3ec491261b084c750e0ba2b8ca993b727ef3e2ad": ["page.", 1], + "23f8210eac13907733ff3f51c19fac58f48dbafdf9f46eae0ec0ce0949af4d8a": ["secureserver.net.", 0], + "23f8fe9c025fac1e0b695a61d060e60860c76ec5a4476291235d8397680a7f74": ["coppel.com.", 0], + "24036469bf0d978e8c27df9c4066707be32b9b44bd037e9c6815c339e870f35e": ["necta.go.tz.", 0], + "240d0b46a7649e0f9fa4724b4562263a35f7d6625a1781b0c8146342fed0db5c": ["xn--1qqw23a.", 1], + "240f7afeb0a366d004966bad91ebccbddf9ec5f8765e3d87c836fc445f71bff9": ["ign.com.", 0], + "2414173b1f822e1bb9005d351efbea944949f4d28befe1a14dd854fc0670b025": ["tushy.com.", 0], + "2429494076b14ba053afe0f596355d75814f24c59920909eb6057352e04b8ca7": ["pixnet.net.", 0], + "2435b14c9b01089e63d9b352cdf600bd9af9233e9a7f8d97f02bd4bdf570e556": ["azlyrics.com.", 0], + "24384708599c348d6250480eba1d75aeb8fce9c8f6358a61c127b1692fe69fb3": ["picarto.tv.", 0], + "243b17db179a8b49503ecaf7a63d415d5345ba960abebc61d7d21f7ae16bd55d": ["ikano.", 1], + "24447d727e8dacc4826801c565000889fe8e3e443210e37edbd36343352a6c5a": ["pachinkopachisro.com.", 0], + "24466aa24cb25bbfdc9f10962895c2e3f74b04b1633642f072bfde45bf80967a": ["delishkitchen.tv.", 0], + "245501a438c56cfaaa8633956bb55bf3756f1ba2fc8b56c2f7a648b2be467599": ["halifax-online.co.uk.", 0], + "246056e977f9a2e642ceea68c0265ccb07e44be91cd0adc17dce3935a428d160": ["galaxus.ch.", 0], + "246bbb8e66a119195a96d220c31407191d323bc53da068f1b920f7dd60c5ef78": ["kakao.com.", 0], + "246eccb7687f329142b221d611e339f123db45673412de3649c5eb6e829bc83a": ["fotor.com.", 0], + "2470625d02fd8f8e2ba54f09a02c307cd6ee392865b703d7f47155f64b87379e": ["businessofapps.com.", 0], + "2475c4c35c605b2731c9c5994efb90e12f351f093fbb330390b05a5dde78212d": ["track24.ru.", 0], + "2475f2d2bab1fc4909ea2de3fbc180f61e121377abebc91db1acc1b056471067": ["plumbing.", 1], + "247eff70459d72ebad1b665e36faa36fb9efa110480059fada523417198180a2": ["mangahasu.se.", 0], + "24823f5d82f1af6d15edc8ead137ed51146241c00b16839a119ee076f797b4db": ["sandvik.", 1], + "248279d8028c75678fbe80f38cd23f8692a32d254041c8dbc98ae57356ce5839": ["nordvpn.com.", 0], + "2487944d45bf68774ce478f5ff1f3c16582f9106dd09330f30dad0fb8e7939c3": ["redecanais.la.", 0], + "2494c926e745cc5e613c9ed8b95d04fa9d3b18bc6214acdf6ca1bafc219d5454": ["parcelsapp.com.", 0], + "2496bafb8d4df30e79b3d71ee92d97470fbdd784a214e4653c0defa0a8653cdc": ["zeczec.com.", 0], + "24985aa1701e63a93b9950a1ac8be44e72e81efb83a1c4ebabd6744c4a01e31c": ["bb.", 1], + "249bb6f4e003a700cde459e3662afe72ed915aba0e783461b43775130e511cac": ["team.", 1], + "249bf899dd5ade3b75cefc148be336a020e232c8745e80475c639c39b1d3bf4e": ["stellarinfo.com.", 0], + "249c231c85b551eb2b56a2707a506e89850b6c99f97cf82d69adffafb0951f3e": ["uta.edu.", 0], + "24ab5a7ecd41606e5b46fcdf62456dbfa48d94a5ec612f1a9a823be9e4f3eaa3": ["pornpics.com.", 0], + "24ae754dcc0b8b2d1112ff41ebb833c85615a9f9819d327c74acc666f107bc61": ["unacademy.com.", 0], + "24b01647d44a37ed09a6313dfd583d6ead9d91256a92258e42c45103bd012dd9": ["reelgood.com.", 0], + "24b13b773d515cc32aa58359613f29e76eaadec7d18a635cfd9d2fb2a32406e9": ["rfa.org.", 0], + "24b2583c65c188ab24b36f835739d6b21d614cc81500f8f1c8481daedad18656": ["antenam.biz.", 0], + "24be0d820eb214492a4bf694a3db9c4b8b9a415cdf258a4b43b9cd0e3db5ccb5": ["raiplay.it.", 0], + "24be662a0d63b707b0bb8a66dbec3ecac07683e893bc42733e5029a666469fdb": ["ba.", 1], + "24c1705c53d8823b6a471c22e953cd1de37cd3383a1d3eb2f009c7b5c61b6d6a": ["maxpreps.com.", 0], + "24cd1cd026117cf8cd1050dff2042ee6816c38c5b758f13adac80eda0ec8c0b5": ["lenovo.com.cn.", 0], + "24d60ac998decc2ec7b4f9553946bf5c16a1dde30740efcdf03decf1022547fd": ["pex.jp.", 0], + "24d63af7b1bcc14f060d2fa5ffc52be5e6bd16e9da073ebb5a2d6b272acc72d2": ["ucloud.cn.", 0], + "24dd9cf785ed645d239197a4d8a2717d17706ddb05f20ac1732cd21ee01b6245": ["avto.net.", 0], + "24e9a5981b165c631ed8839e5e8ff6165f4e0a82307521a996d011e6f2d79b06": ["99lb.net.", 0], + "24ece85fafddd7a28c6b1d2d18b8da0d8190b96a49af2e20268eef03c9c057ee": ["quimbee.com.", 0], + "24ef3f51c265a582b3f8ea48b07a1fd2f4aca4618a375e05d9309d7884849659": ["sfsu.edu.", 0], + "24f1fa379b51e16f02b867e4a271d76d8f0ffb8248df02b3d0b18d4189fef7d0": ["babbel.com.", 0], + "24f640f84268b6b56215a4bf365671ec2dd8f2c59622252c1478b65329a1fcb3": ["pronews.gr.", 0], + "24f6c5248f844824cb8a27fd381498077165c05329acc065adc6898442e2f26c": ["invoice-generator.com.", 0], + "24f72a38323c7152986ba17df346718104408a32058282dfc5b89fcb19603160": ["myreadingmanga.info.", 0], + "24f8a361e64d9c03b2d5b4398babb0848a5836b3e1c54ce3ec9f065bbcd7847f": ["online-audio-converter.com.", 0], + "24fa2b2b751c04ee36f841b6a7922ba48e280b36bab525ee8c7eb5f1ea7652fb": ["iobit.com.", 0], + "2500ed5a40eda3e0bcb0bc5df71a2e132e67919298684b0c5fc57a2a1abab0d7": ["coupahost.com.", 0], + "2505e380780bcdff3e07ffce805998934198435ab358a7b2e78b99e1034e0305": ["naukrigulf.com.", 0], + "2506102193aa20e6e0dd08ad6a1e4bd559fa042c6efca0b02e8c1a6e2917aeeb": ["samsungcloud.com.", 0], + "2509575bbc4173743c0f7922e26788b9affd15c010afeebf957b787bb7607037": ["channel4.com.", 0], + "250f0f3197f120e7ab515f839a7e0ed3b9015aa97579879f98f6ef9eb7318067": ["nrc.nl.", 0], + "2510b57abcc4ed741d9024c3b50eeb4ea44a0d9916a06436d15b255513a2622a": ["gainesvillecoins.com.", 0], + "2512b4a1d36c52367aff43dcb826ccbb78d06f4301caf1145aafc0930b3aafc9": ["correios.com.br.", 0], + "25243aabb81a3a1b4d30d1d765d8b3891f1c62f428731fb5e915c6814795e19a": ["market.", 1], + "25278f7599cceef2caf1180eb22abbdf6377249b4192d87938bda2e7a678037f": ["clipconverter.cc.", 0], + "252b8167393e72fd8d28b49f0a2fc8b900ca97bb91e2fa69f0061bb942d8ab67": ["skylinewebcams.com.", 0], + "2530eaa32b9db2b117ea3c405440e968a81742dbcd5e8a3424be01d645ddc663": ["amazon-adsystem.com.", 0], + "2532ef79f8f271208cef18b6fc6384317d05f08c2fecb570fe7b90a852e062e7": ["tju.edu.cn.", 0], + "2533cb32f064d70741fe35e6a9a45b029a153c9cd978fb0552a871591ec1cb45": ["alba.co.kr.", 0], + "2536d779d4db3e11f3a1524bb58112d81d478f9ae2235bb3318a6db19f893304": ["purdue.edu.", 0], + "253b9d3b496a2840d1f70b9574898e293fc3d0a923dd12fb0835b251e5774983": ["fr.", 1], + "2543fe25d2761045b72501dbcb344913c2b838ddc506100acbbf165520481f2f": ["chinaunix.net.", 0], + "255161c649b40574839204656006865a8dc5b5572c3e80bbb1084e357a032a33": ["friv.com.", 0], + "25525bd4467cc2d3b73b95003ab9d280544695270648fae4360e2ccbb61e69df": ["jarir.com.", 0], + "2553c002e378d50d915cf7ab5c7d3c68132a71b18b87b0fcf4ffd8910e2d2ac1": ["fao.org.", 0], + "2554ba5fdd99d689c0c52209e8f252671f3c9d7925920b2060e727158659c81c": ["vedomosti.ru.", 0], + "255d3eb4e0d322460d448d0119467060a291617f8b5e3cd5d4d2e82ac00e561f": ["all3dp.com.", 0], + "25655af07478090c37b357077d77fe742603175205f4615dad32bec096f8a8c5": ["ptc.com.", 0], + "25662ef1619c0933ca00d0b0692aa5a7bd7447b6b994f95b3bacbdf764d420a7": ["timesnownews.com.", 0], + "25678f5d4319da139c5dff68572ec71c01aa3a86e52cbe062584a772aa7887d5": ["vfxdownload.net.", 0], + "256bcc83ef1c02b2563d6584bf60c0de7044148d295b891e50d276dc8d7fe25f": ["akiba-online.com.", 0], + "25724bc25a248523ae9fed9f04ea76a8b41d1723bfc03c99ba2272ab04d7c50a": ["rogerebert.com.", 0], + "258d31995f6bc27b9f063a720da8efe43d743e2311502efc4cf621186407a645": ["dantri.com.vn.", 0], + "258ed997964c3ba204747aa9148da83cac6525e42fa3e35658ca23b440991c47": ["unive.it.", 0], + "25990137bdac1a58af4f4b01569857d8133ba5954d6aafaa4c35779bbb689889": ["datezone.com.", 0], + "259ad92300bec98d30dcbd861c129e3d187f31117ea01cdcb7515c621297127e": ["suckhoedoisong.vn.", 0], + "25a1ba40c253e0b1808e148f64207bbea3271c9fb81cb5996d7c215d499d9645": ["delfi.lt.", 0], + "25a7fb799d52b859ebfdbb27e848f35158591190c91803b8e14c74689332229a": ["fyi.", 1], + "25a935f2bfe85c95a325798a5b83835996e221ce0ca303605b3f347a1bccda08": ["engoo.com.", 0], + "25aa54978f2de68fcf34871956843e8d305e07a10eaabe036e9db9f4446d5622": ["catholic.", 1], + "25ab1c03a5bf3abc40f3f7874d201309d731fb80a79616f353476847b160ae26": ["thefa.com.", 0], + "25ace4044b5cefe8d37f7abfbbe3e80872647014f5fb570828e0c8072a14cd5c": ["docs.", 1], + "25b3b6d9bd1c6329fa0d08ed8dc2e78fd9408db1926abb9b74b9e1a07d50d5b8": ["infomoney.com.br.", 0], + "25ba3b270165f6505ccf8b9921c04046dfa32c66cc2359f07af628d2d70fd7a6": ["totaljobs.com.", 0], + "25bb5fb9aa8bd41f8c4ff94d3669f4395bea1a03e8b9052baa769c3c278df57c": ["dll-files.com.", 0], + "25c6e0f28d3cf9c584fa09722e11644317c0e65e20f2fc3d19ed2472ddfd561a": ["activestate.com.", 0], + "25cc168d20fb2ebdab95477e6527b9b16036b8b087d060ecb7a5a79d986630cd": ["hentaitk.net.", 0], + "25d989db165d144e2f91348b30b704a36799b210cad620682f50159a58b2f701": ["1drv.ms.", 0], + "25dcf489ec12174de4e59368a2c156e20904ddcc194833ee82f76be6eec5df01": ["zozo.jp.", 0], + "25dd1af863fd91d4545eca85ddf8b764da62d69841796c7b747b55e2ef2b2dce": ["gaycock4u.com.", 0], + "25e7c4012d65ce7e8fc41952f2f7c6449809659e04a9b0cd8da44abad7f52a3c": ["sketchup.com.", 0], + "25ea4a6db21db42569abdfe0e4960a626bb3ba7631ed45de473b6557654bd9be": ["sura.com.", 0], + "25ef3c060c3b9a6df20d8571839ab903c2ccc52884e3fdea7ae39894389b1710": ["kahoot.it.", 0], + "25f67fdc7426763f30026ae45710ce7ec49c7e7fac8b0a8d209029301cf253b9": ["fanpage.it.", 0], + "25f872d991601702f7a624a894824e8618043711af895b16cf102c8b455006c0": ["zalo.me.", 0], + "260118ec25d122e253324c2fd18d5be83f98d4a654b31c0dd1062db6556067fa": ["png2pdf.com.", 0], + "2601627ccd8db7df5d508585b16793022edfdb819d77c2d404246c9e2e950a6a": ["indeed.com.", 0], + "2605db04f4f0bd9e47e2c87c68fb86c27c8d48a29df4bf9c860a80ef00e85cbf": ["zeebiz.com.", 0], + "26073b6772f397ac50c58234740c511e90fcd815a07266cdfda2d5e42d457648": ["fark.com.", 0], + "260c01a7df28a5c2bfd575cd96942d79477a299ef1d8ed9b7d77434e2b97e91e": ["pecom.ru.", 0], + "260e57243224cc59d2266b9482d7c3ba1fa0a18330e0c1d4b6af1a505208f144": ["ringcentral.com.", 0], + "2610e710b856b6e0f49f610c62dc8a226a02670bcb7725fcd106e74ff3843d3a": ["commonapp.org.", 0], + "2619ef8bdd6b64f186f04d1ad2fb665c066871ebd113879231b3bed7b7a61d85": ["shonenmagazine.com.", 0], + "261a78e2e5091df4ad66eda2425083689153ca9c34dd3c0019492c3217f3cd42": ["anon-v.com.", 0], + "26233e6db49cf56414848e1dc2ff2ae59864816e2183255b5ecfb5bb46372f07": ["ilovepdf.com.", 0], + "2628753cad0d647856db3d6874a7371a77a3941c6d3b5a10454a94b254ef8710": ["hentaimama.io.", 0], + "2628e92979d2ba3d60419b46531f9c22401b592ce5a5cfa14539647b79b2c033": ["gi.", 1], + "2629a79e3bd8be18d4a0b906df9582379646ff8a15a81b1e4ba531a81a7a7039": ["superjob.ru.", 0], + "262ada8549a3796f6f9e750f01cdabc5295ecca15483cb39e9eae22f8e9d991d": ["upbit.com.", 0], + "26325c8d8a10c67403536f743c1b5f73f76afe5a903130e77f8141cfd6b80717": ["lcsc.com.", 0], + "263418d6db47e9f2bab5e781390a8c18e16d715afc18ca5738271ff3c4865492": ["setn.com.", 0], + "2644e865803772f21c0143a612b616396db218436c2465924e81b18fd62234fe": ["golf.", 1], + "26455db94bbea5888239651cd15710d16a00ff0410eb800b9edb7600605a9b73": ["onet.pl.", 0], + "2649857cbe880199d621c395615adf68dbdf8e6d64800ab38543e0eb42368c04": ["igetintopc.com.", 0], + "264a95ab14b91cdd23fddc7dd25a035dccf769fd81c1f6d3478917c68e91c8f4": ["accountants.", 1], + "264d1bdc7804dc72d43cb924efb83f95244e9ca3610fa62645ab4b6120dde26b": ["themeforest.net.", 0], + "26509f27eacd1230bfa5c88dad6f80911c640939e4c59a48859da3f2665c1d13": ["boehringer.", 1], + "2650d0b95612d940c70c107f3eea7104b3521924a036f2d4f61dbf256a183969": ["patrika.com.", 0], + "26518da785e80f450ba7debeaaaba83a814e3e72c1975311f90094ed0a945bb9": ["vacations.", 1], + "26561aca3003ddbedfa1c1eb180ed4b9967d79f30b5f8645ce8aa4e64c8d9a35": ["hao123.com.", 0], + "2658da3d5ddfff599fe0f8d7ea2e773cffd3e435d50f2a9bcc308243ec0aaec9": ["psytests.org.", 0], + "265af5d95ef71353d0f67206e30742e823d7b0110e49c9a53cfda9b16413746d": ["a-z-animals.com.", 0], + "265b43f9e6a020211577b87118332e0d1447c009930fef8468bd487c77d5fcdb": ["trueachievements.com.", 0], + "265e5c9b1e7c30896f4ab272e93ea1970032348ffd7a0dfd25f70bc88a54aadd": ["grammarly.com.", 0], + "266429677c0970d20bd228d9bbb20930d32aeb7b8b3230310b6be6969ce8f9ec": ["straitstimes.com.", 0], + "2671159a6a1bbc5d451f293a0f3f3581bb105cf86686ec51db83f1c6ab1c815a": ["sproutsocial.com.", 0], + "26713416d71f2b9a1d430a4ed43264a4f54e33b5ab61637fdd9817637f1371ab": ["imbc.com.", 0], + "267423d320ec52983c075f23aa3ddabb368073f9029d09f1eb834a50d453f65c": ["baseball-mag.net.", 0], + "267876b826ae867146fbefd8efc6a7525964db6ee37d1a380a4ea8e27c9fa123": ["pornovideoshub.com.", 0], + "2685ee66dc162881fa7dc9b1f41a86262378e7aa51901c6c197dcaa33e7aba60": ["ladbible.com.", 0], + "2687539a73925a791a09e8466bf486f4cb46ed5a9ed0324a7dab21ffc4a35a5e": ["ambetterhealth.com.", 0], + "2689ed1c46df947e794508bef30f75dc4f0a63ab4b331f5b9ec4017248415d31": ["gsp.ro.", 0], + "268daedc808cbd2016d8d3f555372d6e11cb38776f586b7556eb254dde3d1e1c": ["mdundo.com.", 0], + "268dbd00a063cbd1d370621e78d64f7f17285aadd05b4d3813210ca42b8a68ab": ["donews.com.", 0], + "269388115019d1f8b502c562936a545abef069616614ff1d891b985d8f792a52": ["filmisub.com.", 0], + "2699b75cb24992932aceceae479d1c2f2237957f5784940666f26cbd6b44e66e": ["hentai2read.com.", 0], + "269e075b2baea1cd5a7067ee1837252753971c837c6d552658fa72be2e39ecd0": ["davivienda.com.", 0], + "26ae3122d9d6c1b784d75da66562384b3c9d9bc921c1a27d6e7605869f2ab578": ["meteomedia.com.", 0], + "26aeff22efec08500897ed9338cd6e59f69ef24d499efa9d891b335ad450451a": ["wtf.", 1], + "26b519c3cb49aee64207d7c540d4f12f16210f06aec4aa94e46ff735c1418bfc": ["iranecar.com.", 0], + "26b64c8d6af4bec3c7fd7325272ae1773e76b38a72bb607211da2e7278f550d4": ["kmong.com.", 0], + "26bf2ddfa5d3853b762a6a152cc8fd653a8e01b95d1fbc8a6bf0a6a2d55d639d": ["pc-koubou.jp.", 0], + "26c2e39000f82cf0f5041ea368f585717b705385c35e6827de85eb33f8642fd0": ["tubegalore.com.", 0], + "26caa45475bce982d0da9283c983afda472800d1f310209b36f531acff02136b": ["annunci69.it.", 0], + "26cc0b6cba01788c593c09696af03be7ec789249846b857e9374469807576c2f": ["huke88.com.", 0], + "26ceccc810df7ff479bd6dac7201987ee80dd242b9622cf8293cb0279cbd7717": ["pentapostagma.gr.", 0], + "26d086b7fcd135a025a197a78053737b730fe0184dd2c14a98bb332ce0af2cf9": ["wadiz.kr.", 0], + "26d31de514f31e686b37d93899a8d3598b8a8e5cfa48e7088ad4bc26b64e2b0f": ["j-cast.com.", 0], + "26e33ce5e4300c36bdc8479b87f373914e27fa97c40a0d0e9e0559c25552329d": ["aliyun.com.", 0], + "26e4ba5f2d662c7b0d43e813abab6415063dbed14336a2ac921a298d5f74c090": ["planetofhotels.com.", 0], + "26e8309bfaccd2763b0914c06874c4920133ff86b1453b8e0eb8ba519c291398": ["endclothing.com.", 0], + "26f063aeaf0c3aa8cc337c37ea349a1e0dd33fc65f839fa5d8a3041cc18aea97": ["insta360.com.", 0], + "26f326e2d8131581ffcd949e75d4440071f18a38d26af1a241d780b64b39a4c7": ["dcloud.net.cn.", 0], + "26f7c6fadd9ca5228de7d438d840d00ac6ad6f0eb8e12d9f49a97a3facfab0c1": ["aconvert.com.", 0], + "26f9d85267bbcbe30953b6710e54f84c9fc61a2d430e34b9b095759317cab2b8": ["livestreamfails.com.", 0], + "270154f2370ec7e8bc4cc4e36f5fac602110f8a87e535a16876104124f0f8bf5": ["centrelink.gov.au.", 0], + "270aefb288fab8434c2ba2f296b855bb543e20187150140cf5392ead39765c76": ["dadeschools.net.", 0], + "27142edcacc083fa6eabec73fff247a86c5b813092081322f36df1c9278d6570": ["tabnak.ir.", 0], + "27212b3a73b7ba5acdd67332b57812a64086eda66e0dc3f7d56f78ad5bf9f291": ["pelis-online.me.", 0], + "2721d5267208b846b1ab89915565cd5ca05b71a4c63f80a166fec0926d4cb2fa": ["samsungsvc.co.kr.", 0], + "2725a71b98bd9af4f0dcb1bef9a7458bbe85eea3a19084278ca69f35f5ebe6b1": ["anime-sharing.com.", 0], + "272f922498548995b3dd71eb67332ccea3d80d60604f9b03c8378b7f7a2bd8e6": ["focusschoolsoftware.com.", 0], + "2739063f3344722e6fcde60c51a76cd3b8353712e595888d41643ad78bdfa04d": ["unistra.fr.", 0], + "273d7ed7b817f351494f3f71a4fbba61a6f8163f62ce5100e10a649f40b64944": ["juspay.in.", 0], + "273ed470a77ad7cc57633e88359af4431e1612ee41568887f470ac1c4d3e7a74": ["hotmail.", 1], + "2744a5d039cc2dc8a3c1057095e1829e012f3cd199d3854a944ba9f7ad03316d": ["justanswer.com.", 0], + "27483e0b47fbf9bbcda0a6fd25bbd202bc14e4b1ba60bda98a4ab2a180e900fa": ["91mobiles.com.", 0], + "274e5efe3ddc0d204102aa233e98fe6935fa135f7e0ea3ffc6390f4a88174d3a": ["dangdang.com.", 0], + "275006dc43cae42691a5e155625a76512465aa1f727d565b4d5bcfa9c95d379e": ["forex.", 1], + "27518115577b050cb835063705099c82c8a9dbad59b329c6aad76b750d69ca0c": ["mrvideospornogratis.xxx.", 0], + "2754c3291f04fa49c0ea97a7ace9217e6491fe66d30f615e6a53075ccff84318": ["hanime.tv.", 0], + "2770bb86083415abe7d165ebc1108adf0f56c4d6f3a0c2d6bf0f80aaf431f846": ["dhgate.com.", 0], + "2770fc86537683a47aafb68fa9ae420b4fddee00595155adbb7db5d6ff27a02e": ["xn--3hcrj9c.", 1], + "2778b7de0856098a5a085b1d302885b3642a9b61336d5810d850f6c49870808e": ["migu.cn.", 0], + "277b50705aac2859b75af96bd7c62e9e9a885a73ee47d430247d9afa016fe0bc": ["anige-sokuhouvip.com.", 0], + "277fa848e714c1fa61b2339613951295c4d1e5b328de803202c95fc4d715b561": ["computer.", 1], + "27800a696b99372de3eef7b70aad143b17d4fc2e4424b3c282ce191b9b0d3ab1": ["gsshop.com.", 0], + "2785f69dcb7927cf902fbf8bfa22d2af945a1b21ad7e32b4800a63dbd2014f28": ["denik.cz.", 0], + "27861a4b966fc34b482be1eaf8d6dc8067b673701fc0e65ea3104f4ef409f7cd": ["fo.", 1], + "2786419358f50add6a8ae427686a51b20238741886de581b240c276b87d77934": ["jimdofree.com.", 0], + "2787981c40fdbe04c43040fded6c10386059550a5a4988017270ca93eb82303a": ["tsyndicate.com.", 0], + "278b00c1f69aa8a85f7a0cf210b0d5734593014bc0fd87c7fb92b4dcea9c8e9d": ["regmovies.com.", 0], + "278b3b5fd3925d6575f7f0b0697ade37a8f8f739b2d0f3ba5e0f2a42158a9fe0": ["8591.com.tw.", 0], + "278e651b9105d3e9bba4b0aeed8b676d7c43748c9b98c1f5841e1e8b1fe27d53": ["uvic.ca.", 0], + "2791d5de1c2b1eb3e084824a80335962581a13acc3ecf4618ae8ba10fd6fb51c": ["brazzers.com.", 0], + "279ee64ad2d4a9f77eabc5e1b5cd90566e2a078b4bc141d171cd95c0a855b756": ["flexmls.com.", 0], + "279f3167940781c646367658a3afd9f471250ca26443b61f388e7fa2062a8e92": ["justeasy.cn.", 0], + "27a8f22abe5a45cd19491921806ca9a78a52553a36cacdb4a69abfee93ca9a85": ["omocoro.jp.", 0], + "27ac503cbc90c781252fd98e2fa8a8fad6757d7fede289622a0ce4934a2603a2": ["free-power-point-templates.com.", 0], + "27ade937e945a699ac2616e90dfdd40e38aabe9e863d7df6549f8442426a1db3": ["uptodate.com.", 0], + "27b476800d26c4b181d1752cdd76e197ab1906796309857a3e4bbddcda8616f4": ["psbank.ru.", 0], + "27b4ef3d8b753da9f0a9a88d8d89aecb6a7417342d51a34f029d99d555b8373e": ["aruba.it.", 0], + "27c874eee7fc8551400d6349d4ddafcbc89db52ad710afc1a3a5b1fb1a826072": ["syf.com.", 0], + "27cb559beb22fd4173ddcad5a520e5da43f861b46c7cdf1085a30d4561359bab": ["rusvesna.su.", 0], + "27cc9cff4b1286cbe7bcbf30a1a8bf209dcc440f58ca6dc217434b7fe9aa54c1": ["ebanx.com.", 0], + "27d334fb2081f9277fe20d94694b6e8b93ff5e46ba6bd9622da75bcc57be2e3a": ["health.", 1], + "27de832a399afd2aae75b0f0078e542cbe8a135fed3469f220b5d8a01bcc6917": ["habr.com.", 0], + "27ea406265996bfa06a3640c068cc3523097774c44132167b4589f7e7e052000": ["austincc.edu.", 0], + "27f299a0f4bf1584ff0b6513c6b1ec4fc59a3c3ce2b36c2901df41ec16b2cecf": ["tonicmovies.com.", 0], + "27fa10d836b5402d4ad7e972607c9225bd4638af7904529410cb2be69083c31e": ["easyhindityping.com.", 0], + "27fac61aaa6d43d2515a0446c9fe227179c0b107e03ee0ead75ab2f0aeb8ee64": ["bd.", 1], + "27fbf63251bae8520968cb73196948e4ba20be35d2499eb3f1353e3f8753ab49": ["xn--eckvdtc9d.", 1], + "27fd97a5a5b1a4eba7f5e5a08c6a1033b0067b2ecd1ce7d4e7060a32b59a2277": ["bank-maskan.ir.", 0], + "280ac0fd2d8009882212657c876ae5309c711640563ecedfdca2ef77ea205f1c": ["thesimsresource.com.", 0], + "2810a76fe011950a1a047883be16aa9f5668c0057bc695566b690a19c59ec619": ["useexplore.com.", 0], + "28167ed08a33ed7cdc3af8282e522ebfb1c8d35c04136db8aaecc8e9bdcdccd1": ["lagou.com.", 0], + "281689a3676495502badc85d470d814f8824e70636d02dbde3722397dcd9baed": ["gtn9.com.", 0], + "281898a2dd09d237bd3730d748da272843c3aff3fb02c0dc27bd48b3bd23f241": ["twse.com.tw.", 0], + "282774be2d6f6ff2d7eca664b2548ce29151d8e986b60160d4cbf059cb73df7d": ["23andme.com.", 0], + "282e2ff2ed0f27cd75ad75f6b5088605eb6d290981f2fc9c016e68194caad05a": ["ufmg.br.", 0], + "283096c03f86c6e96e6631781ee408122dbeca3999d22e0ad95fea5016a5b0c2": ["haiwainet.cn.", 0], + "28365dafbe9ce3bb06c8e7980b3b93668e3c2d9ed25804d83891f798e8ff9fbd": ["claro.com.br.", 0], + "283ba070ef663607a47e97d0c1ca3e23799d4d031122d2ced29f3c9d887c1577": ["airtm.com.", 0], + "283fe77dcbe9f7e357874ad89195ac477e10a8471fe9e3bb9809bad953f24ff9": ["md.", 1], + "284021dbed4f558529078c4974f038752d42d5e34a8e4f0abbd9743a0cc78147": ["reliance.", 1], + "2845ae463dad67902076daddf7a8b401dba91da336d9b52938c8cd0b80af1db4": ["4horlover.com.", 0], + "2853f610080e61565399d98a9692fe5d67293613d89405fbc7c791ab6f8357cd": ["chrono24.com.", 0], + "286879473258655ecd1d27ca0cc9ad267991074f3d0843a0fc888d275e80069e": ["kaina24.lt.", 0], + "286e079be64f90a27632c7ef54cd7ed7ce30fc4cdf2ef7519bcc62d5d998ae46": ["neilpatel.com.", 0], + "287af19b870993d25fed6629b90050fcc5cfbda9873b22dc6804b06d2f73f932": ["fomos.kr.", 0], + "287b47083b907c6d2b0a686bd3b2dda510aeb9477f4791e821b58196a7ed7027": ["shinhancard.com.", 0], + "287be2f3cd4c55018db5006b221c1dcd65a406f99fcf47f66e274ee06bb8cf71": ["compresspng.com.", 0], + "28835020da7f53ca988c11b4c79b21ca240b59eb820d567597314139550cf094": ["avsforum.com.", 0], + "2888ddb764fc56f8c451ee7ecaeed193353bbfe53c6a6c03f911102026890bae": ["buhgalteria.ru.", 0], + "288ca76ad879cdfd2af5a578cf706f9fac5a21530b97097501bd487b53691300": ["pornbox.com.", 0], + "288d71ef41857f6a656db0814ad51b62fe75774ce123c7066068b686c6771135": ["xn--mgbca7dzdo.", 1], + "288d9ff355db350982dce0414001fb6f734a44e8dabe57d7191ca7af08bef8a9": ["partocrs.com.", 0], + "288de766200bd4dfd2fb07380c44d5a33eb1b458bf8003fff83461e4b8c0e138": ["btvnovinite.bg.", 0], + "28975c64b82f54e4cd982e4ddf2b985665425184f5bb5f76faf6d8732e642ac9": ["viu.com.", 0], + "289ba3df84834a371f94f53c57fae112c2e9a4a1709a2aa75ac705baf62fde5e": ["melbourne.", 1], + "289cc6a8207de66cc97b9b116e5de7bc6a0bebd395b3498c7cf80d78a06469e3": ["gb.", 1], + "289d22b2ed1f130332de624c18c236d0fdc1d9a7a33d86d88b23fe7e31d468d7": ["cdsoso.cc.", 0], + "289d28994968f1505de656463d7840fbd54543952b19f13e2cf7c6770db89b3f": ["160.com.", 0], + "28a617a953286e316efbe45f4c261163505c00c898a283a0cd1263d4a7b23b2b": ["nike.", 1], + "28b4fd86c375d56193de2bba2d4342a4b477d94e4d481f9ebd08fcb9ca00f7ee": ["e-gov.az.", 0], + "28b7bb3dfedfd1964e510cb76567b0468ca30b37946157fca7d74dbe3fa39e77": ["yalla-shoot.io.", 0], + "28d2a9fe2850971fd95a4c0f0c8ea9ecb7c7f58ad8dab6a5ec63e628e4cbd526": ["xmrc.com.cn.", 0], + "28d4ff1bdcefd289776d42b5e8bb31ada8ef926fe1d44c9c52946933372702c6": ["scikit-learn.org.", 0], + "28e31df7cd5e051d08d7d8f2563c34f499a58c19d2e6171ba65b480450b45108": ["meinestadt.de.", 0], + "28f4b809aee70396d5ed2838e3eb9ea4afe5825fe2d48a347b581670a386ebad": ["cdromance.com.", 0], + "28f9a468303aaeb8cf6604f3527e46a8e0fb0a62dd08497d29762634a239772b": ["techradar.com.", 0], + "28ff68c92c5b0b6544c9d527468a09419acd8beac20b5e05c23f909ad1c6bf30": ["pravda.com.ua.", 0], + "290480d90cb9418e1ef1dcff880d63d3ab254ce9e036fa771b5c0832a5f23328": ["samcart.com.", 0], + "2904a37a38ed55bb7378c9cf7b2434ce9a45fda8613be590d2a6be403d46bd32": ["sportbox.ru.", 0], + "29068a1df3089b3209ebf2d9fadce3e73cd00f646cdf283b2bfff505af5463a1": ["unir.net.", 0], + "290889b27c6d67cfc6a25c27aa474d8dbf5eb90aa559501b7f1a18406101d712": ["irishtimes.com.", 0], + "290e4452b436259a904f0007bac70145e92265312a45e1e33a6d05abb75d4332": ["wannonce.com.", 0], + "290f0ab57b5c8d513fa154125ed4d0c4ebf106a9a5a9042fff71c33d86a1fc33": ["virustotal.com.", 0], + "29169be08c105d870123982cf44de7f04b3f00c6967afb06f569f6b543cd6403": ["gallery.", 1], + "291962761a1c1591aed74101735fb9a64cab667432f6d82144836e1f6743d8b0": ["mangakatana.com.", 0], + "292e7e3183d8f5e1673022f999acbb779690a9b9f26fae4d7448697a4b7c7a52": ["ayumilove.net.", 0], + "294591658805bdd2afcbcc2bc2ab39ddb746d0cfd0c7e22b71fa82cfd7ff3873": ["teletalk.com.bd.", 0], + "29593a26d1c3b505d4876449326df3614eb612f812e1850d1bdfdedd2072eaa6": ["song.", 1], + "295ff255cdc65728c23901ea35e3ced8601b046735cfc8180aa442119879e088": ["exdynsrv.com.", 0], + "2969cf99e35814f22f0dad245024f86319e87202eb118a35c5b5c0aaef7c4d50": ["hrazens.com.", 0], + "297f275227d58bee3b0a7e48ef331be4f517fa1d4943cbf4bf72a49cf21bf00d": ["porntrex.com.", 0], + "298a23c8279b770563193d639d74ecd96be96b0ca48f5d07b7c5c22e07199fb5": ["ctrip.com.", 0], + "298cdf38e3d2f211f8f039c1cf0133de1038c60d9314157822fe00a6feb5165a": ["sapo.pt.", 0], + "2997bc8b34ec21ba38f1b09d6b14806d79b4749a470694b561ea4ea7b299e901": ["bankofchina.com.", 0], + "29a095af9c65ec7c83ac56da448202063d31de57ef1affe9bd90cc9759a98478": ["kcp.co.kr.", 0], + "29a835c6b9a12ebdce74584ba1ba12f60dfd6aff427a76d1395f039c118e23a3": ["jobvision.ir.", 0], + "29b01f904e848288c72cb39b26a265e6bcdda978c9c98cd2842eea2b295894d9": ["yaklass.ru.", 0], + "29c33816ab8f3a5052770006ad3fe032fc11b46d03390cb8c61b97bf685b6830": ["cnbeta.com.tw.", 0], + "29cbafa70c20da130d093690b68f93db7a86e6786234ff53de0c23daa10b1d69": ["bbvanet.com.mx.", 0], + "29d40318295d31835faf9958f2bd42b91da4e66ba354d6d4cd7d48b8cb1ef3c5": ["bike.", 1], + "29d5763c0897d797e2364afc3e2829f5d4871b304fda89e16187a1864c4cb1e3": ["gbiz.", 1], + "29da4bc78ca84a1b725a7e70d0433e670372ca40e4408f355a6fe828ad86073a": ["blogimg.jp.", 0], + "29dbaeee900e08d4f7621b6b602cb4c0f7c898bdcb70fa55db1b79d2430a5254": ["turnitinuk.com.", 0], + "29de9af0470cc8795c256ef01e64d89889063cbd2c817482e4660ab40c36854b": ["bfmtv.com.", 0], + "29e16b5471a5a19e5bd287cb2890a3edd1350dd241a1a26f699ebb25d59e65ec": ["msftconnecttest.com.", 0], + "29e638db6e54f80171d6a902f0d5304b4403561ce3171e8e25e0dbbc69444bc7": ["hermes.", 1], + "29f381d15445c6003186a03d65812442572453f2df26aacbd91ec5070d5ac198": ["ecsi.net.", 0], + "29f4546528bca89d77daae0f8af5a05fb338ee8db1b7d03d49cb09ae2a9b952a": ["twitcasting.tv.", 0], + "2a011037cee03ffdbd630a400ee16f65a76930e1aa39dce73feb27f478c9c1a4": ["tsite.jp.", 0], + "2a01ab81aafd424ad02b842059a1b013cbaae8e9ffe87cc083287351bf08b1f9": ["minnano-av.com.", 0], + "2a0343e4796256b47eba6e477a546d43f5558546322d047f87cb23aef4802627": ["twinfinite.net.", 0], + "2a0386bcf1ca149ae188bcc1aa05745c3f8dc2f963677f14cd63fab51dd84201": ["club.", 1], + "2a0480d3e2e2da734ea00188ea25810782928da0f1fb794ce64d5073721e02f0": ["disq.us.", 0], + "2a09f97c4f5cc6843182adcd9cf4898e1dfb374b3f855416654a15a5c81ca231": ["singlelogin.me.", 0], + "2a0c5ca6a35aabc995dcd307346f4d2ec95b9fea901d0c6df115b8700974e731": ["redbust.com.", 0], + "2a1213c24698e46f164fc4faec9da62f27d0733f9f481b43217f04936e2e8947": ["westlaw.com.", 0], + "2a132e2bcd5e7f2147c158ed1898abfeab7fb897f1d3a19b7327b8e42787d7ac": ["academia.edu.", 0], + "2a1896f64c1ef397743b2b9a87d8685b790ee4afbc3cde1d0f4e7ee837afbae5": ["replicon.com.", 0], + "2a19071ec264f4d4c4714c615ca3a2580675f29c2910699d479f29c48949a34e": ["huaweicloud.com.", 0], + "2a19358bcf62854b390db7a09c22969bb89cfcc2bc436f6453a4c09a2eed854a": ["danawa.com.", 0], + "2a222a11289301ae1164a5e8bf50783cd8c28360b8b64df081387508be9ed3c0": ["bitbucket.org.", 0], + "2a2dbbf3d8609095ecc18558815c158e5ac913f9f6f9d4ecb05c2aa0b0f69519": ["criteo.com.", 0], + "2a2fdbeeb7b2156fcf0d62e128dcb78a5f0483091ba3b058f9d9746857513f47": ["cr.", 1], + "2a33a1b1731e2d59d92d74ca6b357987a349d71a120b81bfeb20004b62cdb18c": ["bf.", 1], + "2a344996e16a9995af6d69e75be7936473ac9afd987b207fbe408c6181870064": ["ba24.ir.", 0], + "2a35326083f095cd81fc03d20b8893401d5b472d7afa63e678b917ce05ae7337": ["cnitpm.com.", 0], + "2a4204017f5fa6494d80ae3407b8c10ab17107be92ac3ea8f3b27ce2605637e0": ["interia.pl.", 0], + "2a43a40a2dc3cd0c9f06829b4fbbd28ad70c566f42cac536b7665daa9870e9fa": ["asia.", 1], + "2a4bbc303dd840fb8d623639c527a0ef507a95c1b020adc09c7e6ae8db1ef89b": ["bunjang.co.kr.", 0], + "2a4e7546f839549f170c47660dcf48cd58da2b39fb13582829aac07e4460cf98": ["azertag.az.", 0], + "2a5a5112b2a2754a7272c1462f3320af25864532c9dacd8a80e41a9f9c8bbee7": ["cyou.", 1], + "2a5c54a216c2a30f962bc54e4d9bc12bd4bb49f9f9706950fbc03d961906394e": ["privateinternetaccess.com.", 0], + "2a5f1f4448af88e418006f70fe8cc148ab0873867f9fe664fb2ef054800f5486": ["crland.cn.", 0], + "2a6a517ec4ae6f296da9d9f82ebd614703d84aab7339582922f8ce329f1432ee": ["gamedog.cn.", 0], + "2a72a0dffd0bd674b7dbfab9ea39ddbb5a9282716979222a255f63dd1c0aa80a": ["tlscontact.com.", 0], + "2a76f38cb4d878781ab6d1b1ae0076818f183c2f0fbab57a8310d3a47902ab94": ["speedtest.net.", 0], + "2a85634ad3770e872cdc1d728c4afe741b22283943960bdd340c22a8f0ac150d": ["11467.com.", 0], + "2a8943ae8653fd8b4c85190b2114e4f09ae3a655f28b3a32b4e90f3a28246813": ["alahlionline.com.", 0], + "2a8c686b477b3c6f24b01307cc0add87ca6777f074c4d4bc39608d6f335730d5": ["directory.", 1], + "2a9719af4ee85e8222d1a229f097ccd5a7398a8f14a562c36cfb377968a60716": ["uchi.ru.", 0], + "2a9d8bb42c2eeb2ded5bffa72f77fc1bdcac14b3ee5f75ee84c7510a98877702": ["uni-muenster.de.", 0], + "2a9e542d18f4e30e5b418b0ee28225f0fae21287aa6fdb9188aa251a2460ce9d": ["tinyurl.com.", 0], + "2a9f5af29338947d2f55cf948b4cb0ebbf7daa02bce1a98b1a130a3d915ec74c": ["k12.com.", 0], + "2aa28fdb46351a0d8cb1722c62843d00fd7faba899fe1f52392c26a76172bfb0": ["freeomovie.to.", 0], + "2aa57a7e3ca7214ac57b64487a543204513726d2afd565701f74d7b8a01f43f5": ["gs.", 1], + "2aa8339c96751910fcd988bda64b759673979e1caa070538df85ebf75f38e3e6": ["winzip.com.", 0], + "2aaf636b5a5381c50017e3aebb4dd28621ad9f1b45528d34a8a03296ad570aaf": ["whut.edu.cn.", 0], + "2aaf945d86ea6974c9bda4c3dd6bba6f0e86f8c3df5a951d8f399676aefa1a30": ["asu.edu.", 0], + "2ab4ffb2dd83f7d42dc5a96c51dd24e0cacb67977f2a23eeb94e9a5d8eeb7a8b": ["xmind.cn.", 0], + "2ab7abdf84a631e9325bc40c604879beb6d508b5ebcecb95f76cd9c978b0ec0d": ["gamersclub.com.br.", 0], + "2ab857af3ad2a18e3b6a01362ee1031eadc714252ac2c2830313427d3e7cb5ff": ["gfycat.com.", 0], + "2ab94630167771b09b6669227c42243df24827bb8ca17e2c01f114b5c5ffd6fa": ["payeer.com.", 0], + "2ad419a5ad5b64426b670fdf21f398421c4f893eb555c44c102af027a2b8f34d": ["lordfilm.lu.", 0], + "2ad853e63df24ae588c916961e4f4028c3dfe725fef6fd6d51b6e46a141c6c9e": ["talahost.com.", 0], + "2adb8e6671bbd0ff5059300dbc236bb94828e61afe3fa16d1e5a8a1e050a2398": ["jsdelivr.net.", 0], + "2ae06595ed461e892f45d9bbdc5c7341e26402c1df6c7ad338b7f3284ef55ac9": ["diyifanwen.com.", 0], + "2ae384d77f7b729cbfe91026836598beba5d821770efc1402dfe4c64dcd2d6db": ["iqoption.com.", 0], + "2ae664d93631b0cbda355915b2e7fd385d3a27be2b45cfc6c120a079fd48d800": ["tubepleasure.com.", 0], + "2aedf4b0c5f4973bc43ee467ba5a96fba2aa282b136b2231d302b18e5c2f595c": ["xinshangmeng.com.", 0], + "2af2a989c33cc145fc26be0a1718a485b56fb57e8db6fc36526fb2cee362532d": ["cpubenchmark.net.", 0], + "2af7842e9380632f309d331fdb574b02106d2c99d7678cab8421516cfe1b96dc": ["uyap.gov.tr.", 0], + "2af987e1932e8eb16243582984d7ee62d40eeb56bb2afb4914347c842692ff7e": ["webtoons.com.", 0], + "2afc7ee7a52800f96692ddf8495c97270f4056e700ebb32d9774072fd1a9a4f9": ["rada.gov.ua.", 0], + "2b0143bf45f5b8213339a08db848127b7057791784bc75387e9cebcbd57426e2": ["custhelp.com.", 0], + "2b0447358a639e645bc5fc4f5b370ec72ed55849e76480086478a7d4b7a8b73c": ["loreal.com.", 0], + "2b094d5aec9a94ef8bf1d6302cd3c6988f5345470d76f652b7c2a7261b4a5e4e": ["avito.ru.", 0], + "2b1829752a3aa7dbf164f80bb902faa43259c07871fb64f82ddcd06fb56d000e": ["leagueoflegends.com.", 0], + "2b27f084a553f4419cca335790faa1f6c9baa4146a3bb833d3eead9040ab659f": ["matchesfashion.com.", 0], + "2b40e816df5f7492b8268a3127c0913406cac0ff4a73da911d663bc525b2aaaa": ["yougov.com.", 0], + "2b4349a7b792f7bc2ff653368ce65a468fd5f4cea05c9ee141f995e8b5065bca": ["achieve3000.com.", 0], + "2b4dbb58dddb8abb433f7880f0db3c391d33d0e3dc8a05abbf7b8d54f85dce15": ["boc.cn.", 0], + "2b574114d63c0c7cb1ae57af308535bb87a7b012f44f3b674437c7d3748e86b3": ["booklive.jp.", 0], + "2b5d87c460415c692d064df89731b156b0556e25d51bf2d72e58a93ecd8a7620": ["ovagames.com.", 0], + "2b6300cb7281580b996aca28ae4eafb4344743c8197b5898d8bb6637a4e18b8e": ["coolinet.net.", 0], + "2b66b98eee262487c40571cedfc92d950ccf9292e417fdc3d56bcf8bfbb1097c": ["51yuansu.com.", 0], + "2b678006200024569deec50849778753368fda192ec5390a6830639352479d76": ["gyakorikerdesek.hu.", 0], + "2b726fbfef171036c25bafa3b9d2c57168946c51d5aba12a165ac408b41760b7": ["gold.", 1], + "2b75e87f49472e39116ebef831cd8c65477f2190f6e0b37f9d0a5070e3cfa845": ["karar.com.", 0], + "2b784cc9234d7b3f884e13874b269b1fa4e08cfa572dd0c5c0a53da4e72b252a": ["anandabazar.com.", 0], + "2b78d56a2b0749e0b8d2f00ad2cb790b8bec7cbed059ab498bf0cd5f2b3df7d8": ["transbank.cl.", 0], + "2b86442251db65234b50436782fa35f95efeee76afa048b3d4160a5cb4d2efce": ["zoomit.ir.", 0], + "2b8e3084519cb40702b1db15a90a8a2edf95a49d101f966581d5e41750760c25": ["correoargentino.com.ar.", 0], + "2b8e4765b0d4dabec2c8fcdfaf02400e047c934f32f14b9d871f96565313581e": ["freenet.de.", 0], + "2b94e14717f7b4a20308f635ca4a6b63d76e06273a8a11ddba41f0c87ce10e30": ["lanacion.com.ar.", 0], + "2b98de4259d8e981aa8ad9700de44a222b38df94495b4fc877ed31723f76d8af": ["qnbfinansbank.com.", 0], + "2bb1d1e52b42d511b7be25cba9ea8a5e1a4615c2450597522f26d8c77d4fb40c": ["uconn.edu.", 0], + "2bc0da193c94e58dc7a1b1a996bff561f4481b730b71669bdc227fadea820c9f": ["bewakoof.com.", 0], + "2bc1c3586bc442977516b7838c429be6bfd77f85c2bde23e2104ff37d0fbd177": ["5118.com.", 0], + "2bc66aa4af9af3e089ec143626dcd1d87393a46855caa6ca1a683bfe1d710ef3": ["openssl.org.", 0], + "2bcf7a66b00c3b24af9f4884771adf0aba8ca091f14673887d7beaea62f7f1b6": ["obozrevatel.com.", 0], + "2bd9d5f8feea7679925624eab9223670d1f5b0fa298280a25593e3e98ac5fb88": ["runescape.wiki.", 0], + "2be28c17d033b0e900487f837f4baa80716750e3a6325188b252dee09883642a": ["588ku.com.", 0], + "2be4b0d54371d6173ac012ced8550bd08d8b0fa448341a51a3acd4105cc751e0": ["prokerala.com.", 0], + "2be9c0b8709c4fd386a581b9a85f42b462bc383a4c1663bffe239a22988944f5": ["td.", 1], + "2bf6a7302450accd00728dc6aa3dd72967bf4c64cb9507c11eeedb4aa22d7d16": ["usda.gov.", 0], + "2bf716bda14fe3fd04757970fc0dd6e6b37d86cf824735846fffb9046f613577": ["windscribe.com.", 0], + "2bffa6c16eb82cbf1d0b67f511b22594aa5bb14c6351629780191cea93191b26": ["exit.torproject.com.", 2], + "2c00384f41b082dc449b7654535250e5a5c9d2c62aa2f084bb7cb7c0bceb28bc": ["tec-it.com.", 0], + "2c0705f1a03ad398207e5fbe5fd8a6ba8962bf76d4ac876a0bd119608cd1dd50": ["pceva.com.cn.", 0], + "2c08cfd9679b185fbf8cd2ca74bd5ea8db9e30750e387bd8482bc9dd4157aa42": ["sndcdn.com.", 0], + "2c0b1c752e58e90a8db959463f4cff76b96b989c7c6b9712617bdc0aec6609cf": ["discordapp.com.", 0], + "2c0ec2ac5c3f7a1188444b71001d1d94639277ef8fa3388564b077c2dc1c66ee": ["ameba.jp.", 0], + "2c1ecae10339499330738a0b42fd532f6f8effb74a84cd5bdd2184f803018c04": ["yeniakit.com.tr.", 0], + "2c2146837dc14905864467700506d940cc2a897a4d99b3209aebfe4a5014cda8": ["nycenet.edu.", 0], + "2c261dde01c8bbacfe8f7a70440cddff15310274f358fb01dc449c6c91c055f1": ["sberbank.ru.", 0], + "2c280e774e390b5e3e941109d7446b56ea4fd60fceed78aaf3a7e7df837e7c25": ["javdoe.to.", 0], + "2c284e49cabdb61b3d0b620f6476f98310dca3a7d302c19ec429f30f3f76000b": ["my-subs.co.", 0], + "2c328f784c43bc88b0ed264c7ffbc1e76bfd22d44b7697dabdc182dec5f2dccf": ["cpz.to.", 0], + "2c340a11b3531895657594e0317b19ab5843e809ea3b190e00c533028d242f50": ["estadao.com.br.", 0], + "2c35ef6e30ca53ab0fa53ea26611a2d94f37eb8979fcc56d12c9e9570814d09e": ["fireflycloud.net.", 0], + "2c373c65ba116044424603c92554b0de0c76cfdf7977611c740579e6e5b66921": ["highporn.net.", 0], + "2c3c96f699dfaf6f6f71f0c182f7b02fa2ce400613eeb65f2009fa93a933d006": ["userupload.net.", 0], + "2c3fd50331bee2697b1d7717db6287cd281a02d411003a6e9541fbcb601fe92d": ["bitsrc.io.", 0], + "2c44d26f06b4a52aa221e98f03ddeff81bf38d9ff8cc7e876b4efe8ee5c50264": ["0daydown.com.", 0], + "2c4cdfd0ee9c9ae07c15f7cbc40f8cf3adff04dee63d268e546bf86cd8112a07": ["hdfcbank.", 1], + "2c593b48651a031145d2a5d464bab3382b232fdefe8f0aef96adb8e169b73e05": ["menards.com.", 0], + "2c5f64fca7533952bc2e33b0e1912afff78c778264d1aec0dbaa2e8e16dd76fb": ["ilfattoquotidiano.it.", 0], + "2c64cb7179499a5696c5bf92cb5daecbad214c3e7fe5e31f1ea04689f362e401": ["mindbodygreen.com.", 0], + "2c651470575ad1ae2eb5b6cc2aeeb1070f1ddd67336b5811a19cb6b4d18a05ed": ["servipag.com.", 0], + "2c65710b794b02a340737b00756ef7a519a36113c5b154a21f9e2031eba0271f": ["carmax.com.", 0], + "2c72e4c7e7ebe8c644518387a7b715c753c5d6fd26dd08295dbc74f19a52a87a": ["bpi.ir.", 0], + "2c7ef5020f931a064f0889e95bdb2528029c2097d2b4369f4baf81bb5697e297": ["kubernetes.io.", 0], + "2c825a3864a49e3ac644f16559921749c7466e4248f5f60be6f03565304e0667": ["truepeoplesearch.com.", 0], + "2c860d6b006d0276969ba0f7d9cfc942e2d9bdd741708ec04f4825f5a627c6b0": ["doda.jp.", 0], + "2c8bbcf76a73970c3f9919d47a231e9f72dc851b0972de6a9ac0fc45662fe39d": ["newchic.com.", 0], + "2c8ddc7b68671d14b81bb92788bf1625d6b09139723a11b93ebe2b3540b566db": ["kinopoisk.ru.", 0], + "2c9a39f710dae50d8f6afcbb0fbf8dd42211629980e3d467c4e87ca365fde0a8": ["mrud.ir.", 0], + "2ca66c456f075ed9aee3b0cd72b9389aef0befd17e52622b70b048b13050c887": ["day.", 1], + "2cabf3a10a161b2c803a5e341a40425855ca644e8c5f85bbf97ef309dfc688df": ["kancloud.cn.", 0], + "2cac527cf3b6cc169e76bedda07fdf6f7cb19583d5d5a89c014c733aa35f31e7": ["fontspace.com.", 0], + "2cafaf91ba027e5175c0c0afa81b2036e737379b433444cf8c1e8b7769162694": ["hokudai.ac.jp.", 0], + "2cb169e3239c186a5402239d804820b1b0abbea921ad1306171b3ffa028e3e31": ["croma.com.", 0], + "2cb1ce6dc54dde74df7a0875db75171fb9fe5733c0bae797f8b11975798817d1": ["renderforest.com.", 0], + "2cb29b285fd9f84fe9126d33e7db1893e3e0dea3338b44e1bc9bb9f7a4cdbc0b": ["lastampa.it.", 0], + "2cb508aff684c19b01d1bf5de4add1ace24a246cca945f74dce8422874bb7955": ["goo-net.com.", 0], + "2cb57588a24181a0b4de6bc13919782066c44dd51ce2378dddbf09aee3658f5c": ["cinemagia.ro.", 0], + "2cb8f08a90472e32c05d43f06b74980d8b246d5f89c8147c1f9294c90b254fb3": ["businessnewsdaily.com.", 0], + "2cbadec822bb714e5ab99f8842e5950b52bf610bc333686fdcc4f46d78c69b67": ["bloomingdales.com.", 0], + "2cbd0c4793f448e2e52b68a264acb23cc5c5e3dabe0cd179a039596fcfa00e4e": ["51vv.com.", 0], + "2cce09ecf3c99d0c7997997ff35005f025c531b107a741932ba60d112b046b37": ["postbank.de.", 0], + "2cd3c77860076b8afa0412135075b1967c989304aeff9ce98151cc4c643a7c24": ["uptobox.com.", 0], + "2cd4f03451265b7585ad3ad4deea09c9262f3416f4caef90256b6049fe2b2961": ["dupont.", 1], + "2cd5c6ac71bcdadb651b76eb75c825eb62d47977ec3808b6dbb70dbf9111bad5": ["digistore24-app.com.", 0], + "2cd71b39fd5faa2105948f2b861348f71cace345716bfe98a6a2b21fc7ccf5d9": ["liuxue86.com.", 0], + "2ce87ad5a3c48d82256828b2a1962a66457522e6fa65238ca07bed88e75c0dbd": ["gay.", 1], + "2ce8f3501458001af3d3f8422168ef3731f0d4b487a66849244be2f2d7abcd52": ["entertainment14.net.", 0], + "2cf49ac331ffce3384e1a74c1cd40176821581f242407a3cf976177a7867ea1c": ["portafolio.co.", 0], + "2cf50639297b77edef2c0cb13c026a78f3006a09f3fb4eb0d18102e59da3ad0a": ["gpp.az.", 0], + "2cf6c5d9d3ecdaf7a2f84d12279a34b63ba21ab50a9c4d3dab20f5033801a0a6": ["wa-k12.net.", 0], + "2d0502cfdb1b148c4d088cf56a4a4b1cd7dd1e0eece9c2d7552992dcbddd5889": ["dotabuff.com.", 0], + "2d0611c741d127235233e11c7c557a2347bb6ecae4552dcdbc71b91177381654": ["exeter.ac.uk.", 0], + "2d134789b27502e584e3a224a653b456e7cff31081bacee0a6812b6758383a7d": ["bitdefender.com.", 0], + "2d2451cd49b1bb8aa869289f13b01bf46845869aa23bc1e2e92393b0a6c8ddb4": ["expediapartnercentral.com.", 0], + "2d28301bc0132ae4591da47876dd6b08945a3c5b9d4a3b7e27f1682414c8b5d6": ["law.", 1], + "2d2e6decc1644de7ae9309607e930288fa5656daa8d0e7f05283ab4214ddfe05": ["lotpc.com.", 0], + "2d34d2a7ef3e7c2ca1601b8c822b11fe85e0e679a8e47626dac387ce22b124d7": ["eaton.com.", 0], + "2d389f6526f91e57c1f9c7fdf4f51570f51dbd7f19d676981633c0a4dca54552": ["goojara.to.", 0], + "2d44f28bcf877a96945578d78d9197bec2773f737eecea95a59e37b88e690543": ["jobsora.com.", 0], + "2d4560bfa943733e4bda49023a5338d559a894fb39c0e4a7533d9fd35362dd3c": ["powvideo.net.", 0], + "2d45e78feb2cb6c742255a60d052d766a3edc907e4bc806f5920a1314b119b5b": ["semanticscholar.org.", 0], + "2d48062b458056e76d396e26542f8f24e915289b5d36e1aeeb07aff69fa139a2": ["mp4upload.com.", 0], + "2d59e9473c464d46a7d39ec3489d5def6d1a3fb8c88d15f087191b78141a6c2b": ["bouyguestelecom.fr.", 0], + "2d5a69982b467c19b763eee71f72e84fa194382dc638f1625765ea31798dfff2": ["typingclub.com.", 0], + "2d5c0758027bf0460f477e73aa5ffca23ca71a94d4381984d6b99047b1ab619c": ["smsaexpress.com.", 0], + "2d5e823a629b73a96a0c4a5e4fe1460edc6cf7567be9ef1e5743a2d8df5ee8ed": ["yinfans.me.", 0], + "2d640094b38dad29e6163c208fdfe6e9d8466817c95be98cd9837a8e48d8df29": ["aut.ac.ir.", 0], + "2d69aaf14083ac1932dc0cbf06c7b8ca96c56b4832582f8ad506d7911b5d5dab": ["anu.edu.au.", 0], + "2d6d6e029cbf8a9764d1cf07c7cb46f0d5a7c7c9189c2a0563a8d25fa9b96736": ["hdtime.org.", 0], + "2d75ff602ec8d5a8354355fd595457251c4f2b7067cfee9710750a5b61c45287": ["indianbank.in.", 0], + "2d81babf97eb7551fb044f3f444c5f4177b60c793d776c3b684fd049459999e5": ["gogoanimes.org.", 0], + "2d81c93404322b6c1635a71a1d0705415b4dfc42ad278a19eb75086a6051058e": ["yumpu.com.", 0], + "2d877fb79951e1eb3f12055e7cf21488384c0243b772a88fcc010e6cc66663d2": ["n1info.rs.", 0], + "2d8a7859aa91e1a74452de8eea0d46d6da626e1b83fd0bd21f59cc1e90878a1e": ["governmentjobs.com.", 0], + "2d8bb0d7b96a966274c9f5f634b9cfd4148930c1007f911750bf11442983f500": ["farhang.gov.ir.", 0], + "2d91b97f1733e9be733c8b9173f9078ac09b50ac8205ffe8b4ca1fbf46650c81": ["lbry.io.", 0], + "2d9cc5dadd1f6c1d75b014200b98dcc66c8418a683b65597a4237f2830b7397f": ["viva.", 1], + "2da68df3d7bf37601168c3b1d42774ba6e3e0eaac2975fd186b65041dd61813e": ["omiai-dakimakura.com.", 0], + "2dacff08aa5f2dc08ea7b71f3c48cddd7dd080b10b9171677b9e153c45aaebfb": ["adnkronos.com.", 0], + "2dad1a7d8f5f67f791926d1ef3cc4792e9f639b663b5a776d34db59271630bfd": ["tp-link.com.", 0], + "2dc2250ff25a933d17745475abd863b61a7da761e43281a6dcc67949031634ea": ["coursehero.com.", 0], + "2dc3fbdb0e0b1bca25a7a7873b9f53e46346a28468aae3974e24f51bbe77c0eb": ["loveread.ec.", 0], + "2dc6db80ec4775f1e090584c11fe30389b136b193cb58bab0f1cee5253354913": ["zoro.to.", 0], + "2dd83f25c2a23fece5d41641a9a036efe495a755749d6828606a2096f5da274b": ["liaisoncas.com.", 0], + "2de3161129a55529a901fdf71d556385224d1c5c9d24f0a59fa0614af525bd1b": ["mikecrm.com.", 0], + "2de5ac7f5b0157f6aec5aaf5e837033bb66a45e4386e9578e7e8edf77b3bb965": ["uta-net.com.", 0], + "2de6c75f0fab78aea868227854dcb3107358d9f323f0f673a947cc271e03d116": ["klerk.ru.", 0], + "2df5da7e23ada9550dd0c7c8fbbad6437a362a1a3735f60ae0e07c2e0530f240": ["tapd.cn.", 0], + "2df625eaf30aad1c617853997c4b4627b13e637ec8199a21fba39d8fbe973c2c": ["typeracer.com.", 0], + "2df8910df14e9de4fbc29b50e483d42e92f10744d7118e2e057dd10c29c5ba9b": ["myfreecams.com.", 0], + "2dfbaf403964c23315d94c3a0279dad3acc1db8692d43441f3cd7aa95678ecc5": ["samplefocus.com.", 0], + "2e078b97bc98fa7801b7db5559f62ce2327a71439798ea78fef89ae46caf3090": ["yapo.cl.", 0], + "2e09c0cfe2338870344657fb2f3a1eec6c8f20a24783f732429a916fa9094a89": ["ntvspor.net.", 0], + "2e0ddd4041230f0dcf7d195521ab892c35cfa22c8d33db1d72c901b7181881ca": ["motamem.org.", 0], + "2e13273aaa6448380a36e2db4b222a7baab4f998fa49948ae5fd9be8dcde0f5a": ["mailtrack.io.", 0], + "2e199501d94a0acf7ff9e3d524a0fcd1a77b528090e023ea219e0d9d3b61c1e5": ["sample-cube.com.", 0], + "2e21c8e728e3cab3cdf87448396827f577be5d331663b8185afb61031172b0ef": ["investopedia.com.", 0], + "2e25270a94424f0dfd48cd99cae0e158f7e6ee38a9296ab2465cd2ac1caf73a6": ["flippingbook.com.", 0], + "2e2a6ca9fd8491ce5c86ead121d8b96fa22f3b4d2d57f94c2599afd2a1ccbac0": ["fresherslive.com.", 0], + "2e2e3ff993fbc0767a7501052bd4872f90bc79095bb172e9cff19c048c653932": ["maif.", 1], + "2e2ef55adc162127f758b2024782ea29a5044fe2ddec90c9aac636aa926bf8e7": ["studopedia.ru.", 0], + "2e48a319c8e0284bdc6db33dae9e4376ea9867e643a9ce53137c7a7006259fb5": ["fxiaoke.com.", 0], + "2e63a1b91c7b525fc57cb76ef6f7f269ebd49e9e58eaf9149adc3dae3ea2e649": ["xsrv.jp.", 0], + "2e68791665a450f402a68e0e971ad9bb45b38f04a5189989c240e7a0aeebce1f": ["ulozto.net.", 0], + "2e6caa9d421a3cdd2ec5cb04744da856d043c48f8a6de45a04d748f23049c04c": ["yenisafak.com.", 0], + "2e81a6bf213c6c984600c5b702d65f9ef18beaebd827b667eddd34d0408198c9": ["txrjy.com.", 0], + "2e81c663d65d3551f8d239b0f1bfd2c1f0b2de5ce69ca86a52fd58d69bec23aa": ["bseindia.com.", 0], + "2e86080ec5b2dbc52facd8c0a8c25b30d078aa15973a770cfa00b3dc0972ce98": ["megastudy.net.", 0], + "2e876ab89058f15ac1a851c8e596e53a5edb5da9f454efd0a8747bb7c27b1dcc": ["sba.gov.", 0], + "2e876e81139916358e940a05476144d6fdc0a7e73de1134b040445b001e0a69c": ["auto.", 1], + "2e8bada9d3b76047bd8ba3428c077bbe9afded3f8db260655ed3bf9a3f145fb2": ["shesfreaky.com.", 0], + "2e94f6ba9c9e4b34eb350741272ff44d61adcf98f55bfee6c74ae3666c9ed0f8": ["uspto.gov.", 0], + "2e99fb0630405394a2ec0316a56999b2416752b69654f492f5679b9c0567c74f": ["myself-bbs.com.", 0], + "2e9d6032256931b7a77da49fd107d90cc79c1c713a813a9dadcf2a72ee2df2f5": ["sudact.ru.", 0], + "2ea0ca4cc599b8cd21792ea4b6000674939437c71051607c22ad3325759b0978": ["discord.com.", 0], + "2ea69292f868a9bbf0a9ee798cac616774a12bdc1d0ecbdd8b9cf55b64980bb3": ["nju.edu.cn.", 0], + "2eac9798c5ff89245d90d0f0c3c3bbfcf18678a741e37fcc7feb8dceac12bc8f": ["starz.com.", 0], + "2eaef841a5b2a399a71382b5bf98da31501c57f774bce80ceee2d43cc03b0511": ["cinemaplus.az.", 0], + "2ebb2569e8069ac6dd2ea7b7214ba6ca74fb6f38cc244c18b89b62c2ee3f2f58": ["chefkoch.de.", 0], + "2ebed140ecd316fa738b92259b935ee24d39a131e66f632c237f635c74696939": ["699pic.com.", 0], + "2ec144c3972adb52b1ab6d80a4cdecca3b227faee585e51fb2b62367d7311298": ["district0x.io.", 0], + "2ec7651f42f854a6100b7d454a0ecdc698f4f197912c3f89d6081d1106fd82bf": ["cbre.", 1], + "2ecd40bf90ddbbd5375f3d0cf0edf86c028c5edb237df50be1f8aea8ad8abb82": ["tradesns.com.", 0], + "2ecdf51b72b51ba2f7f8894f67fddb6fba7b42e3904ff0ef30019f1a342b91a0": ["gamewith.jp.", 0], + "2ed01fdd867365652be8b0f6aa57facf6a806ec59bc730aa02d347da0bf3aedd": ["prodigygame.com.", 0], + "2ed4b24c682c4a11a9ad55985c93fccc20ccedc95adc1bc9f6a9c86931efcec8": ["korrespondent.net.", 0], + "2edde41718d33b38922960a3f046dea288eace644d8a5c72b3e56b31203f36b2": ["ura.news.", 0], + "2ede0ae3299d7417842642c8f626c4f31466d19b29c008d8f63db04c83f8163f": ["kddi.", 1], + "2ee395bb799cbdbe21dd5a15efa691d86e4a3515e83168c38141ca9a20aeacd6": ["iwencai.com.", 0], + "2eeb7459e7172927f6e36a130dff2705d5831a397d8e35c6707349e38509ea2a": ["engadget.com.", 0], + "2ef54b47a494b88b3132c65fb1f10c336b39c34422b2bd9c28716a5d17735464": ["sofmap.com.", 0], + "2ef626d2c6d44173fb95b7dfa0b0b63f35725f30187bc4b94be810a05c2aa4ed": ["wikisource.org.", 0], + "2ef8ba0c18c6684f9af932e19a81ca416ee225c1438aef66930fd1fef9bb7dd1": ["clubmed.", 1], + "2effceae9e5b8efad9e9e465c37b026a3327f0bcc841522bf462994660b36eaa": ["mxhichina.com.", 0], + "2f00cd9547dc00d1a700103fce9ba8442809c74f24ce25c33b622e749aac2137": ["digisport.ro.", 0], + "2f0271e8e522151d627eb2e55791b7414a7ce27edbd2cee3b95a508dae82a220": ["hornbach.de.", 0], + "2f107c4d240d558cea73d3b2136392503544c03b5db4baf3ebce525003672249": ["takarakuji-official.jp.", 0], + "2f15e3ee8979901a2708bbe0e2866ee2917f236db16a8d19dc90bc8c4cc559fb": ["faloo.com.", 0], + "2f17291d9e975ccb64bc16194730bbf4b9e28f14e710e895cb44466e953523f0": ["zendesk.com.", 0], + "2f1d42ac8781c05de93cf575628142ff590334196ebfde3d7300b8ae64cf22c4": ["moppy.jp.", 0], + "2f1ea6d3cdd04df320b0b791469d982084e52a7061a4f28cfe4f9ffa343b801c": ["game8.jp.", 0], + "2f1f86405ebcd7fc1c59b15bc35a716f2a066cd25bdc42d2dd31b41ba27bcef1": ["ohsu.edu.", 0], + "2f24d399f82a765a416a191f5db17b97842c468841909bd344c31a1296563e81": ["isna.ir.", 0], + "2f32fc5064006c4bc2a0def649af8d9f7147d7a95f1ad9464f47b58f761848d6": ["hdrezka.co.", 0], + "2f438dbb7f9f23e674f4bae7ada5bad81f70532582d00fbfbc44bcf1a251103c": ["ec.", 1], + "2f4c4240fcca356a868ac6529fd0978f2e11dab36bc3069772908fea5387748b": ["xn--mgbt3dhd.", 1], + "2f597c86aaee2a5b74632b8bf9056d2fbfef663ab6f7abdfa0f14d26bc8d7a7a": ["animehay.pro.", 0], + "2f677b80ab6c467976167071b916ea835da1baa6589db4441d801955050c15b1": ["imazing.com.", 0], + "2f6d289021c6f6692e5b33bce165c231d556e2283a9da1b096a6c35936df4412": ["gt.", 1], + "2f6fcfbba1b03cb3ac538d390a6d2c67bc26fec5bb709aab426bd4bbd752114c": ["yoox.com.", 0], + "2f70d006651df8f3b7ffe02bebe6f29885d9e35f05f86dec6121e681dd3e37d7": ["xn--efvy88h.", 1], + "2f74db4c36e225ce970764042692c127a7f6ccc267e6fa2d8144c59a6d8948d2": ["zdnet.com.", 0], + "2f78bc21888984688bd35a9610e7b2f4d95a2ac6587b2ccc7c184ec9c46b040f": ["maa.plus.", 0], + "2f79df694d85e0cf7f1374c17a9da91aa14f5b5fb1f5d4ad8bf241de3b84b146": ["xiumi.us.", 0], + "2f7b8037ba3e694a69fcf067665fffafdfb8b99f113680b3192409560c5b65eb": ["sverigesradio.se.", 0], + "2f81cda43e86322d78a0fe94e5110b21d88b1cc6dde27d0a8f56c805142fa112": ["ht.", 1], + "2f81dc7b61b69cf9024f8dc46ceff49b4da7436c2328bdb0b875a733a7cc2821": ["scrapbox.io.", 0], + "2f90fe35f22ea770bba3662dd9a22e99abc398e48fcd3caf91ab6308f2a99922": ["business-standard.com.", 0], + "2f9555fbc508c679ba78b265477a00e12c862dbb0db37e2d1e666ca0eba3f131": ["razlozhi.ru.", 0], + "2fa104b5cbc5c544d7a1a6a46cff2b654f84035635023b5da9b08d75c5869afc": ["mct.gov.cn.", 0], + "2fa34a48dcd836fcf23b9b6cca45538649396e726514ddfeb5ae4a5de79d7c48": ["uiiiuiii.com.", 0], + "2fa3603ea46c9610ec1cf5b43ea549624eab4fa6d34ed8d489fcbd837726cf91": ["schmidt.", 1], + "2fada89a13136befdb55c14b648708def23300b6caecda5f9e98fee43e423fb0": ["xn--nyqy26a.", 1], + "2fb377d7cfc02521469beebae7ad179a4b30876b0ef5c243ff8cba9f84a25387": ["uscreditcardguide.com.", 0], + "2fbbf31db8a9acd7769ed8489ac264994954eb8b1a7af35e5c6cb29f4e4c9774": ["macaro-ni.jp.", 0], + "2fc67c2fae00ba331abcd62a12eb8cb959fbccba97885a4259ca53c98247263f": ["technolife.ir.", 0], + "2fcb47d140562721e0fc9f3740db701002a27eb8f4d8b66b14594f833ddf5953": ["mydealz.de.", 0], + "2fd39c97888a93b194ba2612443329f9e57c7c25312fcaec039f83590f5e8a7e": ["la-croix.com.", 0], + "2fd55ed238a30bc3a0d871d2c04d6aa78e26b138dc46b98d197988a795dbd81d": ["iplogger.com.", 0], + "2fd8962db347614e93dcb79f495c455942db22505f19b5aa7f7a29b0d9a2d95b": ["mawdoo3.com.", 0], + "2fde657d183e37decc337d88e010c90b7508f4ad2000555e020b72f55a482c59": ["dw.com.", 0], + "2fdfab8830f9e81d56a22aedeee5c4a309fe15b894077402f75adb5afcf79384": ["finanzen.net.", 0], + "2fe297150f3a07c2b12945ec2fa4e65f80492e155aa4ad23cf88ca7e4b57d805": ["tvguia.es.", 0], + "2fe30cdb2bde0c7e2f62b0046d2c57e096e88c7fc20ec38485b3e682a819c576": ["exceedlms.com.", 0], + "2fea44ed48e1c9236492282fdfbeaf6aff7da54fb413c0fe21e50c7c2e775bd0": ["distractify.com.", 0], + "2ff4aa6f1f4792f9d163f32f77f21e52ed3a58104644127cae2101360b509a69": ["moshimo.com.", 0], + "2ff77f1eeea93d5e6c88b29827dd4eac7adc6c24ab05f6fdc33933b1c4ccb834": ["userscloud.com.", 0], + "3001cdf73a67f7c3dd18dfe64774d42b4f8fd70aeb093258ee151fc597c95141": ["twistedporn.com.", 0], + "30031dc6e68aad4507315af3c58bb86a188f13f82176d5367f57938b2820f2d6": ["ma.", 1], + "30080d931752be5ede05ea5dee9256004406de528c514711f5ca0a9b5f55781d": ["xcafe.com.", 0], + "3016cd06fd958975dee60f9f76a3813f71203252cbdb6729a0aef213edb2e78e": ["softwaretestinghelp.com.", 0], + "301e6e1b82e7df73775b95cb084dc65b5cc498dc26b885dafd630a08e04222fa": ["cyberleninka.ru.", 0], + "302b905120b4e493d4272c1cc9a01f444a0a7d67451e4f27e93e2330bb764a00": ["cy.", 1], + "302ff0f75cf776c587a8ccd72e56e13f6b0d9aab86000aa7e05b6bebb937d5b8": ["flightradar24.com.", 0], + "304072b1efd440a30e3a3738cc50ea938d35455b11a5a85b3f4ca59c4fc45f46": ["pioneer.", 1], + "3040eabc20c794b2809f972cddce7c29d84e4877d80aef79f4f390e40890c117": ["paci.gov.kw.", 0], + "3043b98205893202d8abcc807c09c32499c35d9c8f89e217514ba7c5e384fd0b": ["showtime.", 1], + "304bec5ea030b0b2bef569cc8182a39f216e9052ff9baafbf8754c10348e60a1": ["vistaprint.com.", 0], + "304e2c13d3c54780eed33abf0b214d1ed4eeca7f621be5bd7276d468e46af05f": ["clickme.net.", 0], + "304ee88c489ead8e09ea026089c3126591715044c7f910e61e7a4ce4115c7928": ["rarbgto.org.", 0], + "305157d896d27c26c195cccd2ac80d26be5f8e6d7b3f4f0232f6e8a2a95f5509": ["cibil.com.", 0], + "3051e3ff0d2d4d2ef9c7bd563976460d0e51c51af8cc436c5848403a4cfb71a3": ["ieltsidpindia.com.", 0], + "305bc60426ae95b2013fdb2c9c01a4e844aa0cd37415d0dca6b5bfa1f4bafe2b": ["asahi.com.", 0], + "305e0308f9835038fd469641dfa7d14510411c38130c9584186ad620b0215110": ["bjs.com.", 0], + "3062f66fed67c7b537d4ea699851dd6bb5ad70e9ff6f90e5432a169e5fe1608c": ["gamcore.com.", 0], + "306d1ec17d130b7433c524b19209585ea2d77df830663e8cc3e77208eebdd737": ["safe.", 1], + "307a12491b13f1102626982291e913c9c543b9fd2fb6ed989d7bbc7af2a3b599": ["gooood.cn.", 0], + "307a129dde58881d44abc29572403383743140bc1f558d565d70beabfa6059a3": ["frontiersin.org.", 0], + "307bcfae9de9ee52182241cc41e9caf0abe52c7cff702d345e81678b4f3961ca": ["komus.ru.", 0], + "307bd983da9db8250eeb8b203fbc5625a9383b45097d901fa08c747dcc24bb26": ["dangbei.com.", 0], + "308fbf7b94ae5d43ac3bbc5fe6a4fd0279c45c491b6d4117abefd0ce76161b8b": ["firestorage.jp.", 0], + "3098eb5958a038617c5a5f2d76cdec5468569f057bcfeef1c82ca21c25fabf27": ["largehdtube.com.", 0], + "309a97267a8f1122a27da92c98b590487a7e82b7d48a483bc803f8e695ae7380": ["training.", 1], + "30a74cc5cf249c0c4c89c7cb381ae6222ae326fdef0c50828ca61be07ce626ac": ["portaldasfinancas.gov.pt.", 0], + "30ae986a6918610fc9fba3e14927069c869475da965b47024131a49656038b4c": ["thehartford.com.", 0], + "30b120628a16e750f11508ecaba0bf5bf1329c284cd8c3b1e65da3af94a83ad3": ["jsfiddle.net.", 0], + "30b34400305022b129bb09f2a55f31714bb6c4faac826306fd03100e35ef30af": ["solitr.com.", 0], + "30b6f813e7287457ab7134fa84ec68ba9adc85120a98d8ce64560a5d7e1f4218": ["secretbenefits.com.", 0], + "30be1e4c712d42a37f97f237e63bb5f4014260d3366272cf80d54eb028b7e913": ["greatergood.com.", 0], + "30c2970564efc5dfd4cea77f78770c563d412e587db9bbe9b7f897ef649db2de": ["cccs.edu.", 0], + "30d152c455f6a4d008d52d565e4029701ef1c93ccd999dfba1ca7c360e7f73e3": ["medlineplus.gov.", 0], + "30d64fa840332e56f29366cd00c4a00e79c05c0a0e9cd4a7daf4017defca6a80": ["echoroukonline.com.", 0], + "30e8d17d0ffb38fae9075d1d500b4908562c4073a36b925202e699f50c6e4782": ["stonybrook.edu.", 0], + "30ed2601ac968acb5db7bcf77ec998897628c7b990bcf1c6d84f30d1ca0eaa0d": ["red.", 1], + "30f5708b5df5c0c7ec7cd93986c3284d750e074a7071300174a899317e3e4d5e": ["voot.com.", 0], + "30f67fcca272aeaf993375bb599eea4697035acb5b9bbed4c3fa9ee4fe90de3d": ["gba.gob.ar.", 0], + "30f7f1b2b575db594e5a257c06cc1fd8dd830dd204bb3d8d7ddfd8fae1e42030": ["amtrak.com.", 0], + "30fb0781d17ebf0818fc0f41ffe15af6ffac9f6c904f7a1a06f6e73b26ba7fde": ["realitykings.com.", 0], + "30fed9a81702a14b6f37a03a56765e091b576c8adfa71030a67e82d82e039284": ["tripletiedout.com.", 0], + "31018bbce5534ea31abd58716900c3f0f25ee61e7e347a1ec06047b62bb4c170": ["hirunews.lk.", 0], + "310458f8f11f75f57d4f7ac5eb1b0a22c3d766f76d218ce32d75994aa9a9684b": ["multporn.net.", 0], + "311a4878120a7c1c90eaa533aa776eaf48afb6056888c5c599fcac127b242989": ["acesso.gov.br.", 0], + "311cecad32ca478364761a6c5f12269e4c073123959393a6715177543d9d26af": ["aies.cn.", 0], + "311cf6d8a16b92548732de6505e50e5c4a2a9c6ebb8f05bcb03cd6ab1f81046e": ["vipergirls.to.", 0], + "312011a81aec7a6f388cba12bffd186b60f3293c3e5ce531cd00e1d5d0bdd878": ["gametop.com.", 0], + "31241ed15681159f2867cb2b6a0580f3fb339ac435d702f0cbbea7be86483cf2": ["bhg.com.", 0], + "3124ebb49187eb2f2a5dd4049e795711fc88eb291c331bc110bc918733669b0d": ["webex.com.", 0], + "313591fe646c32ff422bdc47b9a5f41124bb7989a7c9e6140fae8d639effe2da": ["kpn.", 1], + "3135c7dc57ad2e805e8a97c3ddef917791b491f428eb9b69d8fedb686a0db73f": ["nsdl.com.", 0], + "3140d31a9f088389dda05440236cd59d16d56125b03dcc3a939b90a1cfb94ee7": ["kknews.cc.", 0], + "31486e8cc49a13464e320e9bea05b2feaa3738fd41922449317e6f4ef80f3e1f": ["healthsafe-id.com.", 0], + "3148e102946e9f8c9ffe4b946ebfcb332d56a072f95df12daf93b5b1166d3585": ["gisoom.com.", 0], + "3149b5a8345394c561bc1f7c580f6bd7e628e96e70d3768d95560524552e2bdc": ["basalam.com.", 0], + "314daa3a8efc862b7806cf8ed9b3011ed694bbe5e8a36124a52251f734c56655": ["heroku.com.", 0], + "314efd8baa678d5a01e29db62dfe9dbb7db7f43c8789a49a2870a2a332ee7a1c": ["u-tokyo.ac.jp.", 0], + "3151be9ffda907e50fa3f6d1833d583b8f4a595812842c0baa20174f01625d2e": ["pornve.com.", 0], + "31534df405f5ecaafe53f3fbcf116a6d56831583bb2b2d3a50db3165003a1fc7": ["celine.com.", 0], + "31618a23b983fe7db184d9b4c7e0c7c0f29c2f8b64e9ea48bc275b1b9a40ba04": ["racingpost.com.", 0], + "316233084cfeb9b9d0c0e60125daacaeaed32eb22a63f97ac23a515c3c6746e7": ["japanhub.net.", 0], + "3162d577b9cf969de6da1f56e2f3b3d1fd7432dcf8318fa7f4a47116e922562c": ["freelogodesign.org.", 0], + "3167631af0a74e3cfff6d67136e1c96d7f157ba11d6e472a96db1509a3eb7f27": ["bloomberght.com.", 0], + "31676cd0c539b12a62cf32ca5a6b3507620d175a6b9671f7472fcdc89e493b77": ["capitecbank.co.za.", 0], + "31748dd447e66f2df1bb5160da8b44cb80e1505beffd4a461b898ae5ee612b2f": ["realpython.com.", 0], + "317db70dd4756a6fd0beff83c0179ba0d136b9c4aa4db49d4ca7968ae09f9209": ["agefans.top.", 0], + "318039f921a2474a808e6663239847eff2b425b97a52c76ec250b89bd5ab2d20": ["strawpoll.com.", 0], + "31833285725069a7781569aadcc3099d7b01dc9359a30ccc81df8490a781d1bd": ["hitta.se.", 0], + "319d85f3fa5c1ee79e83e6d88aae39d930222a481626ea7ecf8507c513db12fb": ["radaris.com.", 0], + "31a35ffc2f6dada906c88636a1bb513c7cde65fd65334959ecf23bb7ce07de34": ["postnl.nl.", 0], + "31a61d2d3a7dcba6ac678279b9d4f4e2ee1fb29c31da14644b2b9e48d942f568": ["mtr.", 1], + "31a89a169e8235162e8b1d800512300069dfa0339aee0fd669c297ee76ab294f": ["kcg.gov.tw.", 0], + "31ac49ee266ae4e8c56b1134b3bbc5098a42acf5e05a1b0dcc7a75583b25dfde": ["flagstar.com.", 0], + "31ae00cbc25ff372844fc29ca2eef8c45b1c1e2adb9778fd998d2042f2b4811f": ["design.", 1], + "31ae8d395225c45c8f947070fe4df143452e3bbb65dcd805f3ddd9a61b8ef16d": ["gmu.edu.", 0], + "31af17b7adadd11bd7235e5bcb497f7fd6510278efdbb74e2b7eb3cb7f99e24e": ["chollometro.com.", 0], + "31c1b4cce5e17f84036f9a31bd0fc7c01c2b30a6994fd8b4f46ab8354e3e2e28": ["afreecatv.com.", 0], + "31c442301b5abf1781cfc0760ffd8bae5b8ae60448a7e23d853e022cf5815f0d": ["fotosklad.ru.", 0], + "31c465524a6da3c47f33de5d4aaf2634afffc7c90881701c905251cb00eab17d": ["bestgore.fun.", 0], + "31c588e41fa4953af6ac95f55bb22de95269e2d2b447b33b41a961d07ee82545": ["webstaurantstore.com.", 0], + "31d21e27e9590e236938222ce15d07c5e0caf03f8bd3ba07d6be3c74df0b3ccd": ["minfin.com.ua.", 0], + "31d7d6f6666a4e31e8f1e2006c8d93763fb64425f1e62d0c30e735db11409cd5": ["kuku.lu.", 0], + "31df860d121cb9be44529e8c0cae30e08c3645347b7ee4fde29a41f4fe0ca665": ["meitu.com.", 0], + "31dfa8c76eefdaf23f696d15c38075aa4f7ea1496f75b5a15fbc8ea908a7416c": ["indiankanoon.org.", 0], + "31e0b80e4640a4b23b58f7d47693196953d38230e12475c973f2e4be410c1cd8": ["58corp.com.", 0], + "31e7866d18cf18f277997b73abf99df3f102f754ddacbdc3598b4fba5d8bc342": ["b17.ru.", 0], + "31e996a9525f194b531124ab5aac0344c5f5228be26e23c6cf03837acb266614": ["ngo.", 1], + "31f2108d128b64892f7d5860463bf2a4f9b95b68eb78c39ffbf45d401cbbb411": ["creativecommons.org.", 0], + "31f2dd9e47dabbf121d3d0ccb3fa95b516d5eb8eb5820aba557c24ea7214eb82": ["sobooks.net.", 0], + "31fe083f2050298190d30f85d5e569113c25905713334316a27328f2428c2eff": ["hackr.io.", 0], + "320910c004553b46708a4bd4dfe69e5ee935552bf05835ac66c9f48b711ff219": ["michelin.com.", 0], + "320db9ff2f09c4c71a4c3e578ae7645b871b4f43c8f6041e63ffe02fd3bff050": ["giphy.com.", 0], + "3210d72762eee9f94cefa4e194d3d39fd71298ef9bffe88ba6f61767a6402d50": ["mcafee.com.", 0], + "321c5a2d1b31a1aaa0c1a9398db8388dfdd1012cc66784e920df62bdadcb74f0": ["rssing.com.", 0], + "321c8dd86fc2ce127d7d1a09ceb9902d8914a61085af0c9b0edec78f5175bb51": ["hr.", 1], + "321e8cd78dc61e3e79f83ebc87b3275f51f70f096a8f054c786cf202b5aac3a0": ["e621.net.", 0], + "32212aea535acdb5926a4cbab81a78e2e68cfd0f77ec1a6d9fddf69809b84cb1": ["pagerduty.com.", 0], + "3226cd36b70e26c207e7ce4208da4d1c333140d065ebddd06e52420673c5bcd8": ["thedailystar.net.", 0], + "3227b184ab79ac6e13e3c1d36f916ea09559686ab64efaefaec4ae844f880c34": ["suny.edu.", 0], + "322aab8007201c7aa5e133a4539aa8a991a0630710c6cf99669ea8a876611d84": ["brightspace.com.", 0], + "3234240431bea58803e7946f4019241483de45b3aa5ae8dd426fb733e2617ae3": ["seatgeek.com.", 0], + "3237ce653ab3fa291642b2bd7064f6d03016235f27903d5d66b388aa445aa755": ["allthefallen.moe.", 0], + "323a9954a54536741447a887de3145f44ed5d20797167d9fce0138b83d06686d": ["veracross.com.", 0], + "323c7435d667ab2cbaf7d336883c2b44ab551e9ba3539ba6ea28dbfeb2ce5003": ["maturetube.com.", 0], + "323cd26653371f4393ff8cfc3f1daf14b6c8454126e530771eb70322c165a047": ["driverscape.com.", 0], + "324089f2081178bf8cb4727562983b937d9726b11a596ec2eb4542f08499412a": ["ci-en.net.", 0], + "3245091e1a9483d98517e75a03f51a323dbba297ca78cc3aa3b7a5690530d032": ["zte.com.cn.", 0], + "326504c49fc2f20e650c080ef5ca8e06b8f648a51ea670a00a8740fae2988aa3": ["xlhs.com.", 0], + "326775ae511b79c46841a31e92ea5031839c97d0ff5018d52afa958c5a04676f": ["tcs.com.", 0], + "32777359a0c5748adbb6304d0419ec69db834bbdcf1f958285a57f74e7f3cc6e": ["homesense.", 1], + "32819572abd93647130652c37f9a63db136f9309dcd233f40f825837f4f1e0e4": ["pdfdrive.com.", 0], + "32857b0f21da9b0ae9e681a53baf4d243bbcf38e774ee6f54da408de04b83e8b": ["macmillandictionary.com.", 0], + "328593d0ae8868f301f958b869fa74474226b820bd82734be5a6646017517520": ["spankbang.com.", 0], + "328e30db5cd1acac1ba77def4807e3fd7902b5b107707ad59c6edb1347e8d9fa": ["auto-data.net.", 0], + "328e4750d9a0be644a398cd47adcd9929bea7bbb380d7a2644d8a61d9b56b612": ["listverse.com.", 0], + "328f28ecd6de8510334bea563d2080de3e1ccb54012ee535840fb1dc1fd7a21d": ["citrix.com.", 0], + "3296ad3d2c122ee536399fe8a28fa1e4a193e1458551bc7e0fcfcde6eecb3121": ["onlinejobs.ph.", 0], + "329a4e6afc6d3f82e9490565809155de4e698ac581b613c525297a0365e9b61c": ["platinmods.com.", 0], + "32a9a51b4cf841b880f07e498638f496cb7bb9fc902c2eece300ffa2b71f05ee": ["bebee.com.", 0], + "32aa7292f6efa96d1440da5f4a14826b62a20b1007b8ec690315f73fa70b82d1": ["onlyinyourstate.com.", 0], + "32b0f4499f12e9aa26a72846a7b26b0f3e23e26d34efaaff378bcd0683642adb": ["vonage.com.", 0], + "32b118cc3057f575743b0e6da2ab5d817133d754926121ce94fd3b6f97871dca": ["vitalsource.com.", 0], + "32b62c02a8e9799d1e23f0b805c89d895486030411fa36525bda21d4538385d6": ["jstv.com.", 0], + "32b8b80143899fb1a4715c8c56dd1aff9c155a735fcc3f99659faf17e695d44a": ["bham.ac.uk.", 0], + "32b996f30e2f9532fea079f3f49d9c1036926a9ad7f206a7dc271606a0403f2b": ["nur.kz.", 0], + "32c460e0f9d085af6e4a8d0b55773c90c0b8008a28acd36c9c6d0e9132d9daa3": ["jazeeraairways.com.", 0], + "32c70570a33688db47fd73a5aaa3d131d322cb51aa45375d488baaa20c43aab9": ["gac-toyota.com.cn.", 0], + "32c7144be29e82d886c25da40a1fcb0c9788b46875c0e06446081be5e2064cb6": ["pontoslivelo.com.br.", 0], + "32cf8a4bf41141882a901237c20b427a75ecae347ea71188dcc5ea5f14916c82": ["shzu.edu.cn.", 0], + "32d279f915e6f1bbcd49db249d49cb322a0b2c94e33fdd94019bc43cc7f7b3bc": ["thenewslens.com.", 0], + "32d2fa7e510af152bb7a976a63da2e01b13e7ff23a2d5a3387a1f988e5cea107": ["carvana.com.", 0], + "32d68295fb878df2024135835cb43e38ce31568c164acbc3f6969f318e67c38e": ["chinapyg.com.", 0], + "32db2dcaf4d8595491699e1d2b4ed5ec2a73bdb34a2bac3429b1d6ac6125359d": ["casino.", 1], + "32e41e76b31e5879ef69a0e08047d6eb5396cae210551879177fa2da4700335d": ["reclameaqui.com.br.", 0], + "32e7303e8c88d458c8481502480d73364eb1b529bc2b1fc9a116f059a330ac83": ["chiphell.com.", 0], + "32ec402f41990d9cfaf7297cd0e0b5ee917eb82a0c14527fb309f45d5858282e": ["askubuntu.com.", 0], + "32ed975d498fcc9ae59b269a427c2e764e5764552c397846e68dedce203dffe7": ["jdpower.com.", 0], + "32f0bb275470567c4bac8abc0cec8cf653f0b7aeaf17a06228c46e7a56bcb669": ["thelastgame.ru.", 0], + "32f1339415b406329604745f9cf9f516a139cf1f88ba29356b41379fd9bdeeae": ["baseball.", 1], + "32f8a785d3a816890ad06f7acc2100ee01a73977e30665c5eefaaeeac6f05600": ["hclips.com.", 0], + "32fda7c4f3ae0a990ef21669ddf88f90bcab01e694c702575912078fc4009972": ["oppo.com.", 0], + "3310ea3b5cb225ed92bf57d28af6bba423072c4279da2bc9b5c2f97ba83d4918": ["tinypng.com.", 0], + "33219b57a90a303543d72ca441c825ca1e4494e3c5c5f5a8709feb39e998eae9": ["infonavit.org.mx.", 0], + "3324579ae493b2dfd7359938accc743ff56e7a96c48af44e76b1d992793ccb1f": ["boe.com.cn.", 0], + "33272a32e3dbacd543f6e9284bd6f49ffd8f7f0355b27ce7037f72e54658cbac": ["yugioh-card.com.", 0], + "332c505038880bd0879569ab41afbe668fbd12b0b4bd268e1f49b33f0d2d60d1": ["global.", 1], + "332e44eb22ff1c2d96ffaa28264e9d4bb6254d36f1572703b91514cfc4937d6f": ["dl-protect.net.", 0], + "3332b532e981c870329276a06f8a8c00e2458e702c978479113469a8b5c95341": ["ua.", 1], + "3332f076875c57948c2891fe15e6e7802e31a9703926902487622ec3a95ee376": ["moengage.com.", 0], + "3338112432db253c55c4dee4415096829d77fc685b9256842b34f299ea8d2a7f": ["lipstickalley.com.", 0], + "33398b5790e008602f54dccd4e62803a7a0094c68caaefbc7c55444155cd291f": ["akamaihd.net.", 0], + "333f44d42e145c14f5a74a24f49d66b8cad3ba8900ef1c844017cf97c02bbf5d": ["servicenow.com.", 0], + "3347a02f1a593d709980c3642925f4ab2224390225d6e1c5ba804bd6d65864df": ["xn--vermgensberatung-pwb.", 1], + "3351408c0a7a4f2f311e816a27892f3abdf23c2f393eee33175b5eae4a3aa1be": ["poppen.de.", 0], + "33580e9b7ae4882dae669e59fabdf375f676e55ce9ab29faa71599b67ac32e2d": ["elespectador.com.", 0], + "335c23dc2f320339277812ad20e11a5ae3a87a5517bd96882e1f9fbfcaf9d588": ["cdw.com.", 0], + "335e8cd3581f0c11c2fa141152924451d454a0c9878ea3337dc4c3117010af1d": ["stirileprotv.ro.", 0], + "3365ca0b44bbe119b9c2dbefd6dff7b316193d5bddcf386899287ae91b3aefe3": ["lbank.info.", 0], + "336acb024d8264d1caa97742d3b6c41938f4dd60cfa16dfd0154ba4649d97d75": ["mypearson.com.", 0], + "336ef6d06b67d4ad8beeb25d281b261fde6de63cfc9ba987c5bef64a311eaac6": ["1stdibs.com.", 0], + "3387e6f627a0e4bf9c2d00f7d38e463a810f4dbdcc5865853c0ed3b5da4f3a2f": ["oregonstate.edu.", 0], + "338d3b84bb68a62cf04bc9f382ab2f609c91a281ce9271654b7e6c42b1f996f6": ["epam.com.", 0], + "338f06f7268a9da6aeb81069caf5995adee8e98ec4dceef0cfa115fd604b1600": ["guazi.com.", 0], + "33919da9f99cd654a8a5d4d4e4b916cbf5f46c262c9e334e9dc69c63ef6271fc": ["asriran.com.", 0], + "33956005dd6e989fd701c05eefdbbe839588931dd75cd9e05515879a44c95c48": ["hesab.az.", 0], + "339980f66e62520fd81800c4a1ee6868aec87342481bc3e2e9973fe7f8d43fa1": ["edureka.co.", 0], + "33a4a0f121ea1f361eae5fa0d08dab25dd89e7f93d02840296c8b07a435d23c6": ["eia.gov.", 0], + "33a600d35c3768fcb895918a06a4ab3cf587f4b2a56f4b1ec8e30fb835f8bb78": ["wbgames.com.", 0], + "33a68ab2c1a515eeb9cda6e10c546e9d398554623040da61d76d87caf122c0b4": ["downloadtwittervideo.com.", 0], + "33a8958cae427451a49feaf46d97376916e33d2cd368cc6dc08adf5fa2828889": ["infineon.com.", 0], + "33a9dac65008d23c40b2783d95772e21b511fa3e9cec6d7fb30565aaa5316676": ["voachinese.com.", 0], + "33adf8872ece2dff326cd525bfa3ad6b3a42674671ef4e1b1272109f933a3d85": ["utorrentgames.pro.", 0], + "33af4c9ad2d99152db357a7564d97d8afeae8d0d07fc280eb0de34a61689f966": ["elsevier.com.", 0], + "33b2d08fa6395b581df9e759ddfec522e46d524744af796cb28ce61c3d7619b0": ["26ks.org.", 0], + "33b5348de396040c19887a8c1373d5235e8eb20c48b3f95f66a9d76e1ecea3a7": ["macmillanlearning.com.", 0], + "33b6d12a65139a22abbd05091e60bfb973d2e3d22e435fb1ede909d8d03cbf50": ["archiveofourown.org.", 0], + "33bb13e5681d3e4d2539ba5faa7686174d03e1515c526aa0887afe07d6c77567": ["luxuretv.com.", 0], + "33be5df2dcafad7cffd4fdb0b6e2737227ee22d757273fd60520917f51e10827": ["telecomhr.com.", 0], + "33c3679a2a1108115a767b5b9512b038dc15c23394738499027dcb00f228425c": ["obilet.com.", 0], + "33c630f9701d2c6492382289945f67d187b30afa9fa7756531be071ceb3656ff": ["bankofthewest.com.", 0], + "33caa384d129ef1aefd7fd1b07d0e1792cb399ce5fd62146ea53243315e14527": ["serpro.gov.br.", 0], + "33cede2277566b2afc123871272113ad284e5eabc6a1e5f9970fdbe6afd07feb": ["sciencealert.com.", 0], + "33d47c3146970cbecead4b04e4ed7914b6d209cd1d4431fd3b80c9520b430f25": ["mobile01.com.", 0], + "33d689a7ad70634724e1d1ed50adfd923cc4713e6aaaa6d2b2d072cd3c78c2b0": ["telangana.gov.in.", 0], + "33d70e8fd305cc019ba6c81402bd34bd09a49061fcae6927957fdcf15382e124": ["coles.com.au.", 0], + "33dbeba1b8d2d2f1684b37367a08b00c95c308069e77946ab9efea3030cc0435": ["rediffmailpro.com.", 0], + "33dbf8312617921c8c106c205c8f5505947cc56fae5846cd52603d4ff5de651d": ["smart.", 1], + "33e9c341fa96f81606a7a1e4cae5881d8e802dd54e0e9b1d2fdb1d3efe720272": ["eyny.com.", 0], + "33ea88d814c9410e6e8840ff0d497b46d8d356c881c0dd28595f611b4d259fca": ["usastaffing.gov.", 0], + "33ec0901b7034cec8973b7746c4eee0ea8d447440cb77a67412b8684c8bf1b39": ["grabon.in.", 0], + "33f29763f2094b0717ca98b5b6948d1308217b1251e87ab9547ac9c768a25180": ["smu.edu.cn.", 0], + "33f8ec02f06db986b6bcfbef58f30119dd5b041c34c4d8d46e3aaaa09d4a702c": ["sexycandidgirls.com.", 0], + "33fb54d7bb456a54aa2c0e18c1fbfc45ba3b4ad9a24801a1fb9be2e703bd888f": ["qualcomm.com.", 0], + "34088482c2e978225236c45ea3c527c41c45dfbea854eb13b3b517ac4f2faccb": ["pictoa.com.", 0], + "3408ab204f6ab6cfb158bba9d967520869473093b469b3980af759e78e99d748": ["madisoft.it.", 0], + "340a4c4e0aa2d3e81b9b2f79c8a28cc20529d25287a595603c86a21837adea76": ["gpoint.co.jp.", 0], + "340c5ccdb72edcfa6d0ebd5f258cf86b51b0550583306c58d74d85cf0f48a1fc": ["unipi.it.", 0], + "34247c1f8777f6afc368f92bde67407071b331b0ab9dc8b4d149905ff4166e6f": ["uerj.br.", 0], + "342ad78949dfa17ace578c7d2080511f11b2487955031752c74942976804e4d5": ["chinaz.com.", 0], + "343cd15ba7badc7c3afefaae2e57f45f49f627f798ecfeda008cc27979f7639e": ["vg247.com.", 0], + "3440cf6f5533c72af777d59d7401fd45d4fc19fbb908874edfd9db45716d4b7e": ["nccu.edu.tw.", 0], + "344352327e5bdac29c546c277f3ee69e7040f1ab04d1bc84bda34812f4b4ead8": ["kickasstorrents.to.", 0], + "344422a50bb18d9f7c16e3503fc40020bffe2d90abd861507337f3e50be0433a": ["vietnamworks.com.", 0], + "3444589bf07f8151b9e2071d967834b6f237466fd220f923b7eb079ca316284c": ["ad.", 1], + "3446944f7e958d90f644db756659e083b29dd773cb0435e5a3211e2602c39a6a": ["worldpay.com.", 0], + "3452d4eeb0401e430352bf93b5f8353094f0dfc82173af0a5e445230c29047e9": ["xn--lgbbat1ad8j.", 1], + "345d0cb0eab435809a8e9b3c8e3592dcfe7fde9e301944532a7d87b9df2e21e5": ["pons.com.", 0], + "345d7ab82c4e014d84032892f3b9b6c7785c10ae0af492f3b049b75da6d39dca": ["globalsources.com.", 0], + "3463ec0cd11c2f2f07639333981a95e855d0e5ca3cfdc2676016182440c83be6": ["rarbgdata.org.", 0], + "3466da3f30bab810f5b7d02d8c57d4b6d908084d7088c15542049dac8e9fb067": ["fans.", 1], + "3467618874d35151714d9f41aaed697dae390e1cc5653a030a57caf668b1f4d8": ["decipherinc.com.", 0], + "3472af0775c3cd6c0615344d4df23c0c9bdd3bd28e18d2cd983e2ece961c08f7": ["wpastra.com.", 0], + "3474cc902f1719166a18d33eec2f7823739e0bce3316321293dc308730c299af": ["winfuture.de.", 0], + "348b9c5641febf91ac3fd6debc1e9312865b17bb316445af381e8e6dba6a950a": ["advanceautoparts.com.", 0], + "348c6553d1b35f68f759ee5e8ba70aafa5328ee7c1920f896804a45c2df1009a": ["uregina.ca.", 0], + "348c67818cc85fbd31fde40de1b1c9487e70add5b7cd2cd46b425489cc189f44": ["infoworld.com.", 0], + "348dcb0f87cdd0d239a35b61e8f9dcb118f9731b73907a01d338dd3c9982a365": ["hackerrank.com.", 0], + "349604f09bc2e73583df56dfd711c71d5594f8c0b38338a82c17aa6038e3e510": ["federalreserve.gov.", 0], + "349a6f18e79c863b4d2582734c9448d04ef7d703bac7e983dd26a83d971c01ca": ["broadway.", 1], + "349db228fe39980733e1e8025ae0b5fe60a21759cfcfd402fa97fa3d5c04e01a": ["sbis.ru.", 0], + "349e3ca4e1c209133a9f9f06d4217785067e21adf39f4dd2827dc05094997617": ["earthlink.net.", 0], + "349e3f7ed1d3a4bdfaf0fe004e66544e5da67ca1d307fbc27b3338c6e53abfdd": ["formulatv.com.", 0], + "34a2a4bbf92ffe1c93232524a3de1d3ebac786e5a1dd8d704f07e4a56053f222": ["ahaber.com.tr.", 0], + "34a8b2985698a0dd81562d0ca572f9641a1ef8a8521f1a9361ef141e284a7fc6": ["skuola.net.", 0], + "34b69fff6de782dc28fdf641edb7aa5fea01893e06a005b4ce0713e1d8d00973": ["lesoir.be.", 0], + "34b798509888613d78bb6264bb2648d17fee7dbc363996f394096ffcf637a111": ["rutube.ru.", 0], + "34b8c864b4d1282fa78e499a00f3875cc76bd46b2393d2bd7f6bf6b48e271cee": ["anihatsu.com.", 0], + "34c89de5edbe6b0c7660626db45bae0b048ec236262dc20088a8f47bd9e66bb8": ["qstheory.cn.", 0], + "34da8d37ef04d6c798eab36d916a10f3ae326c7fa244410894d42e06c9ba1b6d": ["jra.go.jp.", 0], + "34e74c8bc024dee246d59786fa73e7c62b83c91e287e0492fd7d9a52ccf099e0": ["freeimages.com.", 0], + "34f266cf161460c78db1f8e0a118d13bcc80878f64f1a8712f97d82f67486c06": ["topcv.vn.", 0], + "34f6f66592f343a31f628e1c2a45ef6f21903d0d731b7501bcdb529feabe38cc": ["thetv.jp.", 0], + "350136e3fe11bd4d4587c1c3d67434556d3af3195b37a9711d951e3ac9c19825": ["lotte.", 1], + "35084fd1c2e05f0ce5f40dd24d1d37581f4f1eb1f4dae9f9ef78ba2f13acebd9": ["ilsole24ore.com.", 0], + "3513e781541b35e95d065638024bff1d70ff658d987fe40892f3264496c6e78f": ["curiousconcept.com.", 0], + "3516806491af2f279ea220f47c9e18211b0f9fb50ebebebdf141ba8750976a49": ["hdchina.org.", 0], + "351b7028568283a54abac02f5c398c4010326fc968e3d2b9e63d1707ae3afb93": ["usmagazine.com.", 0], + "351b883344773d49bb964dda0c0ef132e8b6985ef1fd92459945398d9729dab9": ["che168.com.", 0], + "35331dbe2f9e291c5f021ee52d9fa18d7188cff6601c340aa7fa44d89c3cde5e": ["joburg.", 1], + "3535b28902d45aad79b230bc2ac5484a492132ad8c206c98f91af294cacd5949": ["shanse8.com.", 0], + "35387f1b00d699aaf79e7cf5aeeec84dc2034bb9f59262c7288b823a69cd5ba4": ["forum.", 1], + "353ebee63dc568a8784fa9334053ba4fe45b6b20bad800dd27269f370a1e7b1d": ["whoer.net.", 0], + "35431eb526f872b7002c324faa29868b745d8a0305dee58ecccaea03ef5cb103": ["larepublica.pe.", 0], + "3549bbf5408f78f8e294928a804771c37f5470c60d98eb0ca5e23a95a4595a31": ["trthaber.com.", 0], + "354b07f1d5ef30c42bfa6f834ca2892a989157d0837a16f40d1048ae9650cd14": ["ygdy8.com.", 0], + "35533833a85484a0a03aa333720063747070d73bb2fad6644716053c8a9fdfb1": ["rakuten-card.co.jp.", 0], + "355495350b5e6845515d84f05b2c057b5b3e12e38e3a279f5791f21983efd944": ["quantrimang.com.", 0], + "356d7f669ad64ff36203f8e0dff935e391c380cda7635eae503808bdcd6cb3b4": ["belta.by.", 0], + "357b98eb191fed182e4868394ad5be9cfb63371b2bbe7621e21660560f346ee4": ["smallseotools.com.", 0], + "359661a18757ab093d2ed79221df478c22b74cc63548d97d9a012e8de2155c52": ["socpublic.com.", 0], + "359f3acafd63fe23de79f597ccdf3645ae9955d060c95977d0641f5dff76f743": ["creative-tim.com.", 0], + "35a00930030ac75b34414396f8c7ab790a6e3bbc7f16b325ff061306c8273d61": ["plurk.com.", 0], + "35ac8b08c803efd94f006198d8e6ef2b4df9311f955076d6ee25b573ae85c046": ["tmdb.org.", 0], + "35aef04d8cd95823bcb32b1a974ef7db8c0420fc898c61c1c7364f25ee10e1a3": ["turo.com.", 0], + "35af8e5601c7d613b3fa08bf81c772a24d0de53e7bc259d7a5cf1ede1a241601": ["mzstatic.com.", 0], + "35aff7eeae309cafc77407342a222ff88521f9bd0c3ac3a47f2350090c012eee": ["ettoday.net.", 0], + "35c2883ffab270fc50d06cf3be1c6304d75146495bf71b8a39896e60f36f827d": ["tcgplayer.com.", 0], + "35cacc35676f7bc9b18069daa0e8b5d3192e8bec83d2560400c818526074e69a": ["jiaoyimao.com.", 0], + "35cf78ae4e494f383ca2ff011481dd59c10ce1dc8b3ab425a34d4111e9cbffe3": ["loldytt.com.", 0], + "35d066015bbc4468338808c3ee99c2eca66b9c90e92579d656f48fce50365f76": ["chomikuj.pl.", 0], + "35d1392056d2dbb13c829ada3359c631557a27cbae98301a39f03c25d1ecf5af": ["dian.gov.co.", 0], + "35d92c24ab3d3d5175228b1adfae0d222960b70ed39174021e87065d7de570b9": ["transunion.com.", 0], + "35dc5356aa6bdb9112b4cf277c019ed80fd3100f8b6ef11daf647ba21af2b1fd": ["wine.", 1], + "35dcd919f2b5fa861e6cf54a68cb79e60535e882f75aabafb43fa17666a9545a": ["miui.com.", 0], + "35df12d2bb7583dea521b0c8767951d7cae84f57911b1d21f2aa7616b6f813a0": ["facebookblueprint.com.", 0], + "35e5ad3c548d1ce7ac7f52fe700e16690ef76aa1bb084a5ce5467d86e9401d04": ["mirtesen.ru.", 0], + "35ef86e4e6402b4014268b069091f88791605249b9b3c30eaca7458aab176e69": ["bostonglobe.com.", 0], + "35ef97633852cc2ecb6a4c1758b5dbb43dd901b585d5b7c8266dec56350e4b17": ["casadellibro.com.", 0], + "35f23fec1c72966740a334226758be901857236f8b1379a9c6a63e12ce73e869": ["protection.", 1], + "35f4c8ff3228a2deed5a93612cede3681f23dd98184509013c739aeb579da5f8": ["mobilism.org.", 0], + "36037069b326975201bd80840879e7b1cff843cef7762723f003f6fcf6b6c75c": ["azquotes.com.", 0], + "36108f71d0e1282c3799ed82eee4129055a2de9e4a3924776d04fd02ee25e39c": ["360kan.com.", 0], + "36180f17e7a125440e62971caccb4ff08fb0b67089673d7215112a5e37604bfb": ["melonbooks.co.jp.", 0], + "361ec29fadd911029b0f6031ef345ce9a6738c5128be5e167cc126df6b74061a": ["maoyan.com.", 0], + "3624a47245e8af60bee15644f5b9dc1bea4f9a32209f24734e86e4429b141f4e": ["wdr.de.", 0], + "3628cb7f7c1b8456f69e02ece3faa576da5ea6a1cc123e396919f09c2ef54e89": ["calciomercato.com.", 0], + "362ab838e729a86364385cdc1be5f41cd4cedb040ca23a4e66a33f8f9d19614f": ["theaustralian.com.au.", 0], + "362dd41a08b654b545d304cbb551c8da3cd655ce43399e7fd46c1606e782c59a": ["redcross.org.", 0], + "363c01e7da1f1456590497c3d7f0452399563d19ddc182d858801ba83b207bbb": ["1001juegos.com.", 0], + "363f03cb0863f1bc7dd51a1cbfa02e9a1e2fc746840386ccc294b93fa7b7e928": ["1c-bitrix.ru.", 0], + "364846722febcc9b0c87466dd394e64b077c22ccefc3b23f3e25802849dfd346": ["zaubacorp.com.", 0], + "3657f4670df70a0016594a938894e3908c498bef305e637722220796b54da31a": ["officeally.com.", 0], + "36598c56ddc276adf3776b108bfd4d34f2b737522c05e1900d8bbbeb8c148c37": ["asos.com.", 0], + "365f94240bffb606262933a15db0551ca45a1874e0e9c53ed29bf2c0b2c196a6": ["fidelity.", 1], + "3665d6c5ac0080079d27b0fc68c07ebc706162617e3df53ae50bd48f63f44e5e": ["exawarosu.net.", 0], + "366b23df14f3ad1766e9f05f4c227ded1ae6fadca152475ef7aecb16734dce49": ["unisa.ac.za.", 0], + "36742176a2bca2f4a1cbc97f753f23f4ecb8196aa439a4b582b5a9194aba2366": ["bih.nic.in.", 0], + "3679489c2bf0401efa7b21ddf81007570e31537e65259d7a38a99fa5e65a4085": ["payfort.com.", 0], + "367bc2543049303315d4121a28f3fb8cd78bcb59dba5265c81367878336ef6f4": ["rp5.ru.", 0], + "367edc8c42db003f59a2e914773a7771b5959a70bcd1c4da2ef5089faa26aacb": ["mofidonline.com.", 0], + "3689c267ba5ec79f921139bd6d25cbf262e45a0b8788dc63d948d743daae7290": ["nobroker.in.", 0], + "368a08190df40d44397f0ece37bd1724269e6936ef7eedde572c46fb16d2ecf0": ["bgoperator.ru.", 0], + "369a1c9163e33808483c101391641e8ef3336a094d86a65c0d14780ca8c7e30c": ["filejoker.net.", 0], + "36b118b38cb9b1b3098b301891f13d11a99ae2b4af4d4e30657d74bb59b8a8f3": ["anonymz.com.", 0], + "36bec7558a717e2f4bffe832a2d771ef17e7f6c4a5d56da44c43b623f3038d33": ["t66y.com.", 0], + "36c1bf7542263bfcfd23387f8c3090e89f08767c4a033e88d7d57d2231141a71": ["mathrubhumi.com.", 0], + "36d232a692b5067874a624ad776f34e8161d221f5a552f8ef84c1adb458958a6": ["myminifactory.com.", 0], + "36d5bd87d00ef02dcfb1c96af3034eb760db0986155203248fc81f4b7748ebf2": ["jsoneditoronline.org.", 0], + "36d6dec66fe637c7e73799e49191d83d55e4b86a19a96be82b2afd2276c58e4c": ["iwan4399.com.", 0], + "36debe73e3b3ccddc403ae7d8bc6aa5ed8e55f4b07413c1246e345a388bd34ad": ["sportsv.net.", 0], + "36e039e47aaf678765ce45267374e121bf29d1c967cb78d765da2219fa8f253e": ["skyscanner.net.", 0], + "36e9541e0e82e0055dd9db109f8bd515fb187aad30f36ec4dfa63232aaf65df8": ["publer.io.", 0], + "36ecb535eb2ce113df7da1eca564db711dac6178d9b2d3fe27bc75584dae46ef": ["resellerclub.com.", 0], + "36fface30f461f5320c2ab714c25c0c6b0a64e516962e6ba9a5a949b93678fc6": ["rosreestr.ru.", 0], + "370834783aeb8fb0f7feb08dee5e1fe0168440c40c2b854c23d06860179a4496": ["huangye88.com.", 0], + "3709de36d4f9c1b0bfe3d269a82293021c1aa4258772e14943cae3374419e615": ["uworld.com.", 0], + "370df705508764cad023410ae78aa11eed658a88bf1ea84afed786ee97f0e293": ["lloydsbank.co.uk.", 0], + "371ebf68bcb4681eddb70bb61e5484ebe47d49031f0027d303f4bfdc4520df2f": ["sd173.com.", 0], + "3722a377d5f6c0c571c366e1f4517379e62224bdfa22fac006a40a58ff42fea7": ["ethiopianairlines.com.", 0], + "3727930d673f08edebfcb1a07a250c7b665adbbb13fb267bb44f47d07c2b1b46": ["colorlib.com.", 0], + "372a4b5268f518f0a84e99d9d34082f3a04cf0efc718a094e579106255dcc587": ["gamespark.jp.", 0], + "37342de9a7cdee63c08af1a68558502bfe039b5114fe1e67420c6cccc2992cff": ["psu.edu.", 0], + "3734e4851fc7853b8a5dd2d8ab574e344dff4dfd57640d985fa01d0da9464079": ["originprotocol.com.", 0], + "373c5097461bc2d20556c70e69a8e96e48216f00c3291bc35ecee7e2d1123fd9": ["worldremit.com.", 0], + "373d99b86605ea0d0bf0e57e692a14b5bb05b63109605a4a491f1481b6bca874": ["esam.ir.", 0], + "373ef90f9e9f4a15e0113b3e8d8ffc24a85d45402c0366e94f7ea6970195aed4": ["bmwusa.com.", 0], + "374710211c3f39b10eec5014ca70ede983cbf5d120932a2a1cfc42c0c8bf3705": ["wccftech.com.", 0], + "374a25cde0c4b78747a497cc055185b292f0e0dcec5ad30bf690558d8d18f60c": ["123moviesfree.net.", 0], + "374f0dc9daf908c6110008c9687e087629c7c8a84ed2f6d75815913a9b72e0ff": ["lufthansa.com.", 0], + "375118b8dd480da2e5132f6d892fc01282f048aedb654c96cac9afc5b6e3d936": ["myftpupload.com.", 0], + "37571bba7c51d7efd0708a87ed0a67ff2567a8603928c25aa468717a46ae842f": ["liveonlineradio.net.", 0], + "37574fa3f28bd82565f29f372c112b7d1350facd779f5f09d4304a607051267b": ["sbnation.com.", 0], + "375be9c3290143c886a58df9e248efaf7467aa0e55797a6caaae908fbce7ec31": ["eteams.cn.", 0], + "3761f6beae4677e69d80db64449cd9c97adac4fff4d979791e18fa0d00397b6a": ["baixarseriesmp4.xyz.", 0], + "37691b124afb8a638198e526c44b0c68b096e61812023ac6da9728d57e9c762e": ["metacritic.com.", 0], + "376fc67a632459cbf9650b0b44b37d9a0090846413f70774cc866bb031c5056a": ["americatv.com.pe.", 0], + "377155c7ec3471bacfc46c345aa268a28772248a78ddd7292d066913932a4210": ["kym-cdn.com.", 0], + "37719070015addbb82a3b339d3f961564418952e887de0a0c9914d092f0713af": ["crunchbase.com.", 0], + "3772e76916196f2a42489167ce203b4c1459eac1b97396380c26d0f0b26e1c65": ["wetransfer.com.", 0], + "377827340af19eeaab2ee34b825f1583647840182efa122d80a2d36adf07aa23": ["cloudinary.com.", 0], + "37852255f54b74f3377883afb0fd765c26ea5cefc88cc14226d268bedd8732c4": ["namethatporn.com.", 0], + "3789f0162f5294db46169a4ea5e5669c9e6fd02aa59072744ac8e2b77569eee6": ["tiket.com.", 0], + "378d7a941c04d96ebb3b3816619151db5a0462d0647b40458e8497cc2c28c7ca": ["picodi.com.", 0], + "3790b97ee8af92530664523487190c4699d83c6a6d826c653b2e343262a254f5": ["virginaustralia.com.", 0], + "3795fe1eb274baaaa692d4a15cbd57493fab3dd9e7a98daa97e01d4e4b31a50c": ["xn--hxt814e.", 1], + "379a2195876ea64fb75da1e2b62076b530cdd6933f5f6359b7a6c38bd9d933f6": ["showroom-live.com.", 0], + "379ca86a38dc9f8ec5b2894c6c97d091c76f8ad17904f3d69f819481425aef68": ["exirbroker.com.", 0], + "37a2be1af99e8968cadaaac4f3e4147a786157fca69ce472bdd692d8c0cc3e70": ["zotero.org.", 0], + "37a3d852fb40db39920bb39ca8d7fc1aaf56fc4c4633f7a0dd8383e80ba8c8e8": ["chacuo.net.", 0], + "37a6d3655b927600d99942b9dad83b0d609809e387966b7ecbcbc3a4829e03af": ["dish.", 1], + "37a6d87a3ce289dba55ee50e53d38639f3401d31baed0c1dc25d80108e141472": ["kinotochka.co.", 0], + "37adad5e467403ac326a951ec6f226fc8b4ff2edca05e20d623c8ea89a7af8e0": ["sixt.com.", 0], + "37b2331130ea54819d184a17acf3fbfdf500d009115e92cbbc1e7ac0242233b5": ["online2pdf.com.", 0], + "37b3f2a59407f353915f5bc44ea0e03575950494aff23faf6e4a3d9e00f1bc30": ["school.", 1], + "37b5527c5d0fc990496addbbd951fd1e36e2dd7feef4841ed84a74f9a9b7b981": ["chsi.com.cn.", 0], + "37c274cb07d3840e46e0a7836a88ae745185220d5cdb4ec95d75b789baa082ad": ["btolat.com.", 0], + "37c445220d8ffec72e06f588c9028d43f648a4929ae256f9f0b189412b435069": ["dygang.cc.", 0], + "37d2ce1abed1008505825d59af18741eb9f613b2e0f4f41c80458924a0aa2807": ["terabyteshop.com.br.", 0], + "37de612254801a874545c71e81f39b1853fd34d25516785a2dbea8b56e83537e": ["mosbatesabz.com.", 0], + "37df2a9c17f8a378dc3d9a73c93a60bfb4cba6bbd5ba64073b279fc7ac91bb26": ["assoass.com.", 0], + "37e5c17ddbd6cad57438f08267781668c651cdc2311a2c8a802ce6f9cfd4edd3": ["jagranjosh.com.", 0], + "37ead21d0400b49980b2b07eee1fc6ba72b42b09e3256f4aa7561d96e42d715c": ["hitc.com.", 0], + "37ecb9887d884e6e07dc169829aa55ebd8ccc4a2b461749ad3af4eb5d5a0aef2": ["360safe.com.", 0], + "37ffc3b20a1af2fcd40dcc99dbd5b6e20b00364826158cfeeb62c09b91ff0a65": ["mediaset.it.", 0], + "38082489ce36e33aa5178b8dabebc49618bff7c5787eea8fa8175a50b9f3ffaa": ["cambridge.org.", 0], + "380b507fc0465a553fa0c9e99fb337df8cb609daf972d94d9848237ba9fbbe73": ["mostaql.com.", 0], + "38183c0dfaf1bfb80da7323a24271888262af39039768f46739a9b476d327280": ["jurnal.edu.az.", 0], + "3838f5d0125d1d23db901e2ea14ed6b7c8647aea7ae5f01e2c0e35a867fc3009": ["nykaa.com.", 0], + "3839379661dfe9c439d7fe968a2652487eaa7367448026a83b9bc80f892c40cb": ["free-spider-solitaire.com.", 0], + "38470f923efc61b1d7b910b8a5bc4f3ca050113685234e747640c3530fc69b28": ["roboform.com.", 0], + "38513db17d62f6cc61807d642f3c656181a8c182ddfc1901c422327f1e6b5a1d": ["picc.com.", 0], + "38526e4dc8afcfa8f023cbc27f3887ddb060bb60f126ee45a374a0f997b3af4c": ["aeromexico.com.", 0], + "38538d925da14af6ea1f893e96f001036440b1e1af53a8f47fe0df09475c7f7c": ["yardbarker.com.", 0], + "3866c2da2248cb282b5084637bfe563ad4af567bab07873e0602b3433cee474f": ["tommy.com.", 0], + "386b7ffbec7ca8f9c3107e7e0ffc6e4e8295801b0d7ab97408c709b86fc790a4": ["ucsd.edu.", 0], + "386ed38f9faf3bfdc4e4a16f855c17a80814cf7bceb49a91094cc5a8c0b41cd4": ["teacherease.com.", 0], + "386f10b52b2ca93d54f7151e4d43ca0c57b12a474e3bafb9a46ea2dd92c666ed": ["movieweb.com.", 0], + "386fc253218d331b1f4268d0a99a96727cb9a1b4e0502787747ffbefc04ada98": ["bobvila.com.", 0], + "386ff1320cb38cbe4ef9c5a1c170119145a4511d9b94aa9df4348909219bad8c": ["googlesyndication.com.", 0], + "3870a56dc410b8f1b116d6410403c3627adf38f8a8920d7ca3a349ccc84da783": ["compucalitv.com.", 0], + "387a9378686e65d3c663df472077fe79d1c44aeb17d832e1994369c92383faa9": ["hotjar.com.", 0], + "388a44173c32a8fb8eb20ce595ecf3a86752e2b32f85b26a92339a071330922c": ["tube188.com.", 0], + "388e5ab751cf6e33e5ed5db697cc0ad516e2d744279fadaf84638a45ab8ce2e3": ["wvu.edu.", 0], + "3891b806ea70ac7ececd99a6df3bf061bbc6426f0e611ccb42934dcf3ec3b644": ["nagoya.", 1], + "3891dbdbe85c31d482d2d0df616b194ea9cce372cecda430acef0a2432e81ce3": ["fangdaijisuanqi.com.", 0], + "38a3d94c62ad87a30fa8c02d3f417e74ac6462663ca3764fba61cc4d57402f7f": ["tv.", 1], + "38a58a6c9f3b3ed167619da89240b33e0608e0deabf0d2818b97b1aed0d1599e": ["ccavenue.com.", 0], + "38a87564c82075085d3d18e9e473a320aae1fe30aa41d8ff11b54fa2b043c809": ["chartbeat.com.", 0], + "38ab760618247a352a6d7e9b42299eb379fed699deb7e9eb36bfd2bc3cd6c8a4": ["artnet.com.", 0], + "38ad82b7ee75fa63a4f5815c87781889e84dc8caf4a56a3d868b1c803dd0bd54": ["jpfiles.net.", 0], + "38aeb5919b3ebfa656907b2a28644c220bc90ffe0ca8d1b6bd0a2dd8f7ee2e6d": ["doverstreetmarket.com.", 0], + "38b6e4e19d85b748e840fd2d79b969f29b16300335329f9cd0e7b58e023e5b19": ["zenfolio.com.", 0], + "38bcd6971566e21f55ae41f451c24b8780fcd85b9e9a72ba23d4f17dc5acd7f2": ["njust.edu.cn.", 0], + "38beec344a467713c190457e50da61fd8c2a1723dbefa219731f76e0f306e6bf": ["meipian.cn.", 0], + "38c257dbb70e2a8b843129a02dc2947ef174939678ad04bad3904bfcc39e9453": ["vtb.ru.", 0], + "38c3fd210bdab15caf2b09bcb9b4569423afe4b1c77446c5ba26c8e982efcc3e": ["woolworths.com.au.", 0], + "38c8143648ed0317248a14ab378761fb0e82162cb756e791a3a34f6f6db054c4": ["numbeo.com.", 0], + "38c9ef190ec583035ef202798910a905b9fbff9786206d5cf400c59a42def8d9": ["jusbrasil.com.br.", 0], + "38cbc6cd167e10d7813a10db53738e05ab6a4f1de6c2e9d01c8447d3f4e4bd51": ["wizzair.com.", 0], + "38cf95f42d099204898ef1cb376c7a5a5754ba539b24ac5c06604a381534177f": ["kocpc.com.tw.", 0], + "38d9bbabb886496a94a8fb6e5aa9800339adf3744337ab26a247ab8ff2a6274d": ["kuwaitairways.com.", 0], + "38da0ab9aee37d28985ed987e14236d09e6c834f9d3c8713c2cdc286029dfb61": ["hoff.ru.", 0], + "38e02077b051867da63f91b08714d94e96e1b535629b7f4e547960c713765467": ["rebrandly.com.", 0], + "38e4f5f89cead8c4fc8315e1503fa0a8357e4ffdc2f3d280bf14e107a4202b70": ["github.com.", 0], + "38edd20d2e8941f6ce5735f941ef077341495698e84bb5cad960dea1bf958932": ["aplitrak.com.", 0], + "38fe79caf787a98c607e672a6824ee0f160d8f20469105528e733893cf7c39cb": ["solidworks.com.", 0], + "39097073541f640ddfb7d3d06744be7c1632109f339ce5feef8ec259c17e4e20": ["minecraftxz.com.", 0], + "390d20ad8d2248a8dc36ce68fb3fde38049a979523787bb6b874b64f6100eac2": ["ae.", 1], + "3910cce0b960992d5bdb71b4fd608f8e7832e85232021216fa13a530b217f20b": ["vrbo.com.", 0], + "39207e5c81db90d66f67035f955519d78acd3e56ac448a9ca0e608e9c8e7909a": ["kurumachannel.com.", 0], + "39228548f508c39e3d8cd173f116c43543e419eb1e9063edf32775237539e9dd": ["segundamano.mx.", 0], + "392e7789943bd5ff29a204f67527ffdbd103415d31f84077b92ef1e6b849be57": ["jobsdb.com.", 0], + "393a4ba00cb4a2c8d68784b25104c197eee03f6f0a505ee765204d71eabee5ba": ["dmca.com.", 0], + "39405f56616e9dd7a3cdeef5b4b954b16880b094a5da18b62d787b870252b78e": ["punjab.gov.pk.", 0], + "39431a6fd53e2046cffef7b9079885eccdcc69d1c593d7fe1b231b5c607b42fc": ["okooo.com.", 0], + "394592d4353a0e3d7f3e33edf172880f18fd998c2f925b6baba9f501d2507707": ["jobz.pk.", 0], + "394c7dc86b2d91f63e59cd0062e190a65b2d1b7f3078a5d8172d5584553b80cc": ["tomtom.com.", 0], + "394d467ff5098d3fcdf66993aa13e4cd007a54309d74a4f817cd566b2e1260d1": ["zerotier.com.", 0], + "394d494dd24ac22cdc9d8c22849e51787351c38d5a861ee97231721afcded7dd": ["lacounty.gov.", 0], + "394f09bd1cc7141586b463da6acb962aa8b1cb786498b950614c53a64db5dcfb": ["skyscrapercity.com.", 0], + "395cf9b8e17a4f4f0d8e5223a62ed5a060c82669cd61a88b443331a7f192939f": ["unj.ac.id.", 0], + "395f1a9c53d54053cc569f0c547ffac062dd29286312ec209cbeecec74f36c04": ["yeggi.com.", 0], + "396910d7f25e43eab0ee1ccf8b756e352ed8b53f1ac1a41346759ede580e1c88": ["downyi.com.", 0], + "3969c195d663e90394b9f48bff11e40e9d4f62bf04cf9166653de84db460c841": ["internations.org.", 0], + "396ae8b8d2a11a8ce8db811d6763df1f217e99a079702f0c62cc5d16b5ef50b5": ["kapanlagi.com.", 0], + "397172a6a098c2b4a0fc3e8965628ad1a7041f18a9c0fd72eb57523cbe0cd200": ["bkn.go.id.", 0], + "39732e8ab9fc593479e755afc261f3499b6f8d6fb74f832193dec3b549017f54": ["network.", 1], + "397593b20edbd10fb3f9c9c27e05a15094bdfcf82a696b4b5880efb3db31b517": ["hao6v.cc.", 0], + "3981a89dce6f51c18778694ffdbf192b5981ff80e5a1ae7969310ee869fbd0d1": ["upstore.net.", 0], + "39820b9a38400de8026d86d10f285cead1e1c5957cc4f498e5b1c5d42442c0af": ["theqoo.net.", 0], + "3986f39e4d01ababefb5b8633df969caa1ae1e2b5b16f16c648da1c526d6ca70": ["snapcraft.io.", 0], + "399329620dff839f2bac77e8aaa8ea84fe3b531140c9208f91e83f2668645271": ["marca.com.", 0], + "399766e0d09ca23701daa5ff3d6f502020b20b4c40533aea9beb57536d748e5f": ["indiewire.com.", 0], + "3999d2bfd8c272933b63c250d7fac5afbaaf6ecf1b73149a54b69ade69fd3781": ["edomex.gob.mx.", 0], + "399d5cc1d090341c341472fc3fa21f63b2447bf4e3721f83da8b66fd4c60d9c4": ["tamin.ir.", 0], + "39a06230923632b6ef05d9b7e4749dd2278057bb50fb3578b4998018927e8c6f": ["wikihow.com.", 0], + "39a23d15b2377d49267544a7ae762fe622b9d94551b4cb8b59d603eef292fdb3": ["drupal.org.", 0], + "39aac0d637c99b9f494e3ed48fc318d51d1cce144cfad77b7e3caa8ee53d39f5": ["wixstatic.com.", 0], + "39ae29eedfae452d8eb5f72fca3f407b6d5ba5ae03e637a98e14d70230f5c303": ["fanduel.com.", 0], + "39c5e4b07cbfe09283584137c1c6196d9099a9689bf26d79a465e1f6d0de0ca2": ["pomponik.pl.", 0], + "39c640ee1d449aec9b2674e812dee57a2072ac3cd77f5e66f05dda77a090408e": ["ufc.com.", 0], + "39d4461fe6820634aad57f1b6dc3a4aea46de7614bffef4a0577a6b4c475bc25": ["trademe.co.nz.", 0], + "39d525263e0483f645e6608f0bbe5a8c9e99e24689a5bae1d7adba3b27bce944": ["gobiernodecanarias.org.", 0], + "39d5823e430b7f6cc8c8376c94807abeeef63f0af115f2a7e760fd857a96b75d": ["milannews.it.", 0], + "39da6f485acffd104267221b2475af55950aaa6d7bb0604a5632820fce3d657f": ["telerama.fr.", 0], + "39dfbab664d0327e5ec495f5a7a4931c8026cb139e83b8bfdb89d69d6686f0a3": ["sb24.ir.", 0], + "39e35a1487f5c20c72c4d46810b6b0ded336b594c36113f3870f86b14fbeb8a2": ["ashleyrnadison.com.", 0], + "39ede6b8e88b7f37bbe64ce4d7f76678928776966668ef1cc14e592ca4419da1": ["sgcc.com.cn.", 0], + "39fc24e6a353bf336b44cbcb8fcb1361d6af375ad930558502f98d77ff575f78": ["saashr.com.", 0], + "3a01e4b6838278f77dcc94e01ec0dde0fbe866df309be1bf947ca0098b054f40": ["canarabank.in.", 0], + "3a07f12d6ddbe8b532d9b116db98ef4a828968ea4e3282e7ec8417ead32a4734": ["fubo.tv.", 0], + "3a12d25856e6474b58e3a98308c2163c0a603ece01b0641ff347a3d78b3cad07": ["stylecraze.com.", 0], + "3a17b674d5e62a66b0c93dd4cdc81974574abb2d119f425a84160e02af8bff94": ["xn--rhqv96g.", 1], + "3a1fad9c023c649580824b2322dd2522d9fa577660f9c59bfe6fd4306f91ab40": ["ibaotu.com.", 0], + "3a21cd3d8b4617c8f72de0e6b771cbc5ea5b8718d455a05b941f7ce1017f4c0d": ["videodownloaderultimate.com.", 0], + "3a23eacc4724db8834b7369fdf02e11053742bf5a82a2db8cc9ee0de2e41aa3b": ["image-line.com.", 0], + "3a26adc144d2c382f2bb0518e7861e3f0a368498da5cf2a856c1805c41996649": ["hosting.", 1], + "3a2f554542017a70af1ddfa6a0d3419f7e479f7f59f3028947474e4f18919613": ["mrporter.com.", 0], + "3a30bf7b39678184fd58fed953183b9303170c0e9e61e2ab4ba68a5ea81daa2d": ["mailgun.com.", 0], + "3a310aebcf77b8ddb69d2712ed94242637cabf807fabd82af7bbcdb170d84fc3": ["myworkday.com.", 0], + "3a353efcbee1760e9dc6abcfe4550bf2052dfc720c467e4767e0619de5359064": ["moviepilot.de.", 0], + "3a417f1ca5348763b11941c89ae32e1ebce79fa4ef90aa00845ef237325cfda6": ["xn--9krt00a.", 1], + "3a4483b2b09775346dbc0312f14d1a37c3a0f9ab30b2ddaa99e3c6032320251c": ["chabad.org.", 0], + "3a55d37f67312e59023da1a96851a0e55d2bc6d314ae7600029e271299e5ace1": ["mapion.co.jp.", 0], + "3a56b2900077c6c3a148e34e8e58b5c10f281b29d918cc64804a741f56fa96c9": ["chusan.com.", 0], + "3a6504b4af5a85cee3dffcb261fe05e92af78ab3b032bc8cae0cc84637dffa0b": ["zhaket.com.", 0], + "3a73f6adbce041b6ad8e665f8f3e0a47d7e978b69d4e3978efc3aadd9a19f073": ["isaimini.com.pl.", 0], + "3a74b4d3238e348f5a784552b730203d761c6f302681dbb316d0ea4dd080d2b1": ["watchasian.la.", 0], + "3a7b6ed9786f2b55e1a9be0a4a6a31d9aaf5e9a5149eebdbe95d3dfc92ea5b46": ["konga.com.", 0], + "3a7f13526d6dda1087c1b76b05a3b7b1e5113ce07ef9e384c8f379d324e1674d": ["mrskin.com.", 0], + "3a7f48d115c9ec617ed8d6f6c54058884e6e3041704f6be2ccc4b4ed4f6b1856": ["comsec.", 1], + "3a88c9cc876c08edce3c48fdf97413c10222660120e5bc754ba2e6ac477ea71c": ["press.", 1], + "3a9041cbedd713b27fa3ef9d0c62f2a24f651b229bada7fef94783a6c750fc6f": ["packtpub.com.", 0], + "3a90a73bf00df99bda1e6b7e2a8de365130c447ce1d2bd22b7148154e58e0709": ["twoplayergames.org.", 0], + "3a9644503b93d4c033e384c03e77f0a2bf4dc15ec5c8f58422de109ca7a59b42": ["codes.", 1], + "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532": ["abc.", 1], + "3aa57da628ee0e8047d2c1440cb26b10003c68ea9892282f203a5fd04361b837": ["presearch.com.", 0], + "3aaa8000a1e369ac8cec9f0c68b188f6b22377b6e9c635adb0a6b54eaedd8b81": ["etoland.co.kr.", 0], + "3abca70379cfb2b7e9f797d9dbff73e07a91fd078146095a9924c98795926570": ["pandia.ru.", 0], + "3ac659eec9973a4ceb4a307ef422187ee7c833794085b4f8b0f2394eba0faf8b": ["latino.", 1], + "3ac8b741d86d2c20804a8b7dcf82d449232b6f744e37d9af18155c9b13be0a85": ["xn--d1acj3b.", 1], + "3acf88f83ddc522cbce4ec64dd3741e66f252cdf006b898e5515eef13ca5c058": ["myschoolapp.com.", 0], + "3ad5feebf60c1ef65acc4ba5d75617cd536e4308e62005703139ef9f6ae22ff4": ["steren.com.mx.", 0], + "3ad628f1e91744884ecd66bac666379666fb7efb858de15c5a8d67ee93290f4a": ["caltech.edu.", 0], + "3ae67a3496b21adf49b0524605f2ba2be95b44e1c7035df514fcfa68b48de2cd": ["packagist.org.", 0], + "3aed58e0c0d0714b855dede38eccdba97346de376e9b7ab92772007648c24e99": ["jobvite.com.", 0], + "3aeffb032a51d224814e7c8d876ec433aae4ad44cd6362dc26b6b9cb750c7620": ["kali.org.", 0], + "3af36fb4c1e7e3dd39a44176373f753fec960736ef7e50b02acdc7acb4144e5c": ["coub.com.", 0], + "3af46f9f4923fdcac959829b412b81f4c5a119fcf734c161eb3a903f1a6275f1": ["ege.edu.tr.", 0], + "3af4cf1521dfaf72e1e6f6be914e18d78c57f4829fab793bd49bbb5ee2fcc7df": ["hmly666.cc.", 0], + "3af77a1a6dbd76e996bea4d4d2c3ab852e6945bb003b0cfebcc1b3458c2b7512": ["ontario.ca.", 0], + "3b036a630978d4f721510dbe1eee93081bae2b1c10e7ce27114ca1bbc2b4a126": ["tycsports.com.", 0], + "3b05ac5b472a0a1ede3cfecf54fc8436eec03c97b18ff4dce8557761d2bda459": ["miami.", 1], + "3b13519c757b1231b7e32f93e6516e312d390711814d1bda31cac5ac711ef616": ["fastwork.co.", 0], + "3b2b0cf1804eaf7f28cdb50051962840b7c82e332f6c1d9b27c6b399e747ce0e": ["disp.cc.", 0], + "3b32d97a9eeb118c88356e2fefb45e0e7aaeacc65cc99ffe90c762b00f96e2f5": ["ixigua.com.", 0], + "3b39f54be4c98242b88e4d38be29df4d4847beffa4fddb26c68f4ca65772047e": ["uestc.edu.cn.", 0], + "3b3af6174b0775f13d81845c0613f86c13a80d36082b020c9b6a2d5c63d23ec4": ["efka.gov.gr.", 0], + "3b45e757a33af833469d19576ccaf4334aaf9f5a22cbcac2edbde4a7a8313237": ["bbt.", 1], + "3b51c7d8fb0108abf3cd92bf7acc570450f293627828a323e5e02f21a406dd4f": ["atlasobscura.com.", 0], + "3b533c779c0f503d95136f331fea5a165131cefba4a4d6779a0111a34a8f429f": ["segmentfault.com.", 0], + "3b591cb6be1af5e8b3f985db044d2e434af815b55c5249b6c7bce7c4c8e4f2a9": ["ddnavi.com.", 0], + "3b5924abf524a71d2ebcdda0d7a6e6cc1ef815c62f687df5700b8046f371c91e": ["mercatinomusicale.com.", 0], + "3b5a080305df584e15954b08e9538f1ad25bfec083d0e608428cc7d31d57d45e": ["lacuerda.net.", 0], + "3b6cc1253fa0bfb5cb2f35bd12524b3e2dbe5d01eff1d84288acc2eb4fb844d8": ["linio.com.pe.", 0], + "3b727d3078351fed0e9e5d7909a560d3a8de2db2df08ff2d7b29a1ca49f2ffbb": ["dashlane.com.", 0], + "3b7829667e9c702cc7f4198e966fd215fe31d3dd2a509d9a4fea46c23b833a33": ["epa.gov.", 0], + "3b798f9619f1297e962cedff5f9517f43a9713f4afe8986f210cd9ff26e577d7": ["bj.", 1], + "3b7d474878210aaba5b4bb22e7d546dfa4881deb30f8c3c532418bebc442b6e8": ["italki.com.", 0], + "3b7ef10ee51e0ba527a28b7b333183c666eb65a464130ce063a398dc2ca1c1c9": ["ladepeche.fr.", 0], + "3b812476aee4ddda7d5c3ba83d7c5dc431f29dac68002ae8c107443559e1945c": ["tf.", 1], + "3b83b585dd8934024d43214152dc40b73e929ccd3a69c897ceee15393387c758": ["ipa.go.jp.", 0], + "3b8806894f9eb80045131bf5609d2a62a738cb3fec076bf454469fa2132d07a1": ["pickmypostcode.com.", 0], + "3b8a1f95613d8eed134adec39831d85c2b1d7b998b3008ec149cae647f74e282": ["lassuranceretraite.fr.", 0], + "3b8a826b8f82d18e98ecbb6e0c98eece6817b7a43b3130d6063e55559d90dd2b": ["rio.", 1], + "3b8cc7c656619e4234bfbda9e19c2b2e089794580e6f7a0e93c4ecc6ec99daa2": ["serialssolutions.com.", 0], + "3b9e4106a275e5d5ea2aa17bbc2d20b499d237f7f7067611ecef412d8576199a": ["alloprof.qc.ca.", 0], + "3baf94c8decd66c7b92515bfb76bd34650250d509de292b48c42c4cbc23ed9b6": ["libertatea.ro.", 0], + "3bb9cc4262e4226a7f075f7e42ca1d1b46a527a4edf877eb2b50b76d1c0d75d4": ["bitrix24.ru.", 0], + "3bbb7dfa41062cfa7887323c319a1520ff362aab5bfac84ef908575cecc24432": ["localbitcoins.com.", 0], + "3bc82ac56a29f791a9b6652838884a90013e8ba7c85bc7061cff5584ae55428c": ["nimbusweb.me.", 0], + "3bcc1a5145b8181e775e2958786015f86fe8afc62dcb202ac07a2e5b972100ba": ["digilocker.gov.in.", 0], + "3bd8998408863224b9d3a4a3e65b6bf0a261ebe04a9b918fed456ac119b3149f": ["computrabajo.com.", 0], + "3bd97ce2635dfa271da47eac863e52dad3d4ffbf07c765a542e2a7306f6e92a9": ["doviz.com.", 0], + "3be1b90f87c16f6d19aed56192ad0f2735ec0ab0aa77061a044c213706ae7c7e": ["george.", 1], + "3be6d292f10ebce28deaa2e3648b23fedde53f8e012b94d37b82aac3dea447b2": ["aescripts.com.", 0], + "3be86f3279f118ee3aae80a04095cb604d353c0f1e981917daf480d47ee438f5": ["bancointer.com.br.", 0], + "3bec5f91ed410941a60a76a2d6dbdc151db7be55b3a75501ba389370fd6add56": ["gsxt.gov.cn.", 0], + "3c0417ba2e62cf658591f97a103852e50b9e94ad228c06caeac51d8bbbb9e269": ["stockcharts.com.", 0], + "3c0b780b2017d036514d8aa987e5c1f93686fd48a0ee22a73fa50c36f406aaf8": ["celebmafia.com.", 0], + "3c0bfed230cf67dca337b3c1349108c0b8cb3b93fcd3995846c0072e626e7272": ["eprice.com.tw.", 0], + "3c105be8d5fd208cb69dd617f636566128f4af9e6a6243b77d1f0899543d7dc9": ["ixbt.com.", 0], + "3c12173ddc65e1b9490b4de1c37e1b324a3e03ad80e292503e5933e3ab2f336f": ["ligonier.org.", 0], + "3c13c493b863dda15c81a39bacc35bb37f463f3b11aadf5af0c5027496e95c25": ["pichau.com.br.", 0], + "3c1c5ba5f26eda54fc6555a4ef6597e596389634adcb0bf3a87dc045a4460b7e": ["play.", 1], + "3c1cd479e7447db0e3d072eacaaf66920daaf159341faf5da23fcb1fd8eed4b8": ["telewebion.com.", 0], + "3c1f4ccf67902ad77c1d7de993f02980ecbfbd52fd2b28239daa1012ffabd72b": ["mu.", 1], + "3c255775632b05b1194107f9ac8b40f9d498720c70536a3f90be2686b31d1b67": ["orange.", 1], + "3c2673b58e13a34f1eef556551dd89ce7339820f50de46791e93379c03437add": ["nsportal.ru.", 0], + "3c2902eea9b20228185dd7998e0b69e6ffa1a856f10f3b2e8bb53a4985d24768": ["tigo.com.co.", 0], + "3c29e3bdcca5b26a161c7407557cff15c485183cb94bf17f5a51188babdb58ba": ["ng.", 1], + "3c32a849a529eaa1dd08a046e9601788ece9b4678f49d2bca19e9df4ab513535": ["xn--j6w193g.", 1], + "3c39bf9b0a53a71508b9c3263659d3ffa2a1e304f1e402b6479b1fb4f1ae6b84": ["whatsapp.com.", 0], + "3c3bb4eaeec94cc28a220591e0e73c7087044913fae8aebd5c4c912bfb17791f": ["cloudconvert.com.", 0], + "3c43828673a5c30a4428281a1e4556e6904525b3812a6ad9e01f0d63b14fedd6": ["ajinomoto.co.jp.", 0], + "3c463fd5aaa2f8d13b81447630c774836299a6a9d3e359a3c1acdfc3275c51f1": ["ifanr.com.", 0], + "3c46e5981feab0a8fb5d14d6a51cdb0a62a77fb569c804ec5ce0ec773224b8ef": ["cfd.", 1], + "3c4785b5fb8a7723b5864d4527ec262b11c8e2334968202e5638d9a8ac07f837": ["ludwig.guru.", 0], + "3c495748a87360620ebc626774f5e6e88e96b21dccc244e295bba216adcd8ede": ["pandora.com.", 0], + "3c54cbb26820a1256c82e0bcabf12c681fe06c1f838522d52dd33f7b92581fb0": ["kyoto-u.ac.jp.", 0], + "3c593b6969acb49b4756d1d00cd3dc89c16115c296b3d29694c90cf5c6603d93": ["stradivarius.com.", 0], + "3c5f03e425d9ecb1d62487f8f1db8f28192ed5f82a02b7f46af0635492eb57fb": ["poshmark.com.", 0], + "3c63d43daef55e325fe1e887a2a54f5fd1b31fc7b05c1801de89c25e96ed3106": ["lpsg.com.", 0], + "3c6501663170d05548c31bc6a53f62404644943af67e3f2f220d33e1b71c3d3c": ["homes.", 1], + "3c65a2e3180a0f15fc9806678012693be68449d8fd6b4b2d87b50707cac880c4": ["arzdigital.com.", 0], + "3c6c3ca2127c6375788e2fe3a54eb57cd3e92be1c4123035a8e7661a6f087c2e": ["kapwing.com.", 0], + "3c7498e798204b63490d315c8a9b8142b486514af748be8c77086881b0ac0671": ["infosys.com.", 0], + "3c7527294ebab382f62dd8c808a4733b3f0f9b87f0b9c7c63c13db8100ca642b": ["biomart.cn.", 0], + "3c7a3913e414c9397c8dc142f323a21efc8c34a27ed616a46337ad39cd6ab6cc": ["jmu.edu.cn.", 0], + "3c7bae952a0b2e5b93202f14b02c53a18099dadac22745976ca403fbcfd4cddb": ["doctolib.fr.", 0], + "3c838bb038bbe23be9edd94acaee3f86353b963360dceec4ca3a4489cdc9d393": ["bkm.com.tr.", 0], + "3c8526eb9f342b9e9ec112a0e26d4ca292242b2206cf4a2d6e8a12c349752b61": ["xiaobaipan.com.", 0], + "3c8637afb197ce8d6667e2f514ea667b0ffdbca30f9af9c0bdd9d7bf26f0e318": ["sponichi.co.jp.", 0], + "3c93a13e8014838f7c20a85b4470d07ee6e44a1e5f4213539030c3d5977f0e59": ["cointelegraph.com.", 0], + "3c96f23089eb98802f850e9268644b1b6739c4be07b385ef9998b11ff9ee9224": ["nejm.org.", 0], + "3ca20d0cc6adf5dd83f1c0daf84e5c8b863caf7fb91048b0f737e6a1137c1a48": ["biomedcentral.com.", 0], + "3ca57162073c73cbfcfcb18547c2a6f55aa58414fe28b952edc2e4414e07a5ad": ["webflow.com.", 0], + "3ca71aea9894e6b9f562a6d2afad0eda0c3e2b9ffe16633bb6f02596633c804c": ["llbean.com.", 0], + "3ca8450cd7e45456fc42e777df5449372029c0dc5a37c5f38075432a03c1ee4a": ["rarbg.to.", 0], + "3ca97cdfb59ebb3dbf4403914441111a05dc592b8199e888eb6d461ba9fc7832": ["badoo.com.", 0], + "3cb02881b3db9331cc0dad6b32f0c1172694e48b10ad77c80c64d70d6b2148f0": ["irantvto.ir.", 0], + "3cb1f2981b9e3299359832fe32a6441474a914690559beb0eea6ab74d026c914": ["akakce.com.", 0], + "3cc1bd5d0b6f788c75d6064030e7f499e4a0303e1c7d4b179745ddfe8fe3d419": ["coreldraw.com.", 0], + "3cc953667c81ab39697988f2c3e501944fec79804c0181499d1ed3d0776c3605": ["manuscriptcentral.com.", 0], + "3cd2a84e230ef4729115b3cfccbe09a2343bda83862be79f6c5edec3cc6b050c": ["zjzwfw.gov.cn.", 0], + "3cdb7ae442bd28062662ec5a5e35408478ddf4621123cbbadad9346394021c5f": ["gatech.edu.", 0], + "3ce08f0055d3913405a958b806083ea085f317379f2d084432b0a7bd92d7e741": ["empregos.com.br.", 0], + "3ce5b2c33c8b9bf3bd90d3512f4fa0cd09e84687660e4675a92ce49975c04d05": ["gigafile.nu.", 0], + "3cfa00bd4bdf8baf31030ce6aaace68199fa3738fac2e7159a80c6d5ca1d2ee4": ["4tube.com.", 0], + "3d08cc7c27c591a3d0dfb24c050c4e7dc51da8c2b6623fd2805d99ab5585d40b": ["fontsgeek.com.", 0], + "3d0fb7a85f2f9e8a748eb7ac94a7a3033b98d5e5adade71d200cabe1945cce0f": ["19216811.uno.", 0], + "3d1280dc779a4e1f0af910500c3ec4ca8a98556631addc8d4d20dc9536bfd053": ["chinapornmovie.com.", 0], + "3d34b07f47fa3ef554db7cc4116e389ca80e75ddc7fd96ef5f60e89b588bdda4": ["pbs.org.", 0], + "3d47ca96b84e411a03585356d85cbdce16e6945caf5c8933670cd25f59d139ec": ["disney.com.", 0], + "3d4a522ac1acbb72eed7150bf3bf74d1787b6660f4803943c2d5cd97cf264f8a": ["appcast.io.", 0], + "3d4aaeb24d4c21b8f08b4da96ad1b0c71cc93ddcf0cb2c4d53bae92596c1023e": ["pdf2go.com.", 0], + "3d52870222210cb6e2601f6c782cdb22c47593f5de77d925f7072eada01a489e": ["tv5monde.com.", 0], + "3d52a2c13dd96b4143dafef69476b62c658b969204f4e6fbd6b06e5d50988c29": ["tvbs.com.tw.", 0], + "3d56fc3a032d0a8af1959ff4c97f6e547bf95139664d990311083e65eed5da50": ["decentral.ca.", 0], + "3d5f3dd53620739b5800159839257bfba25efebe582643f7f9c103615fbd597f": ["sangfor.com.cn.", 0], + "3d6d5630180fb73c407dfeff8f8b9c8668f849caf2e86514e1d364c68291a95d": ["desuarchive.org.", 0], + "3d7fc1bea1bea766e74dbd615b8ff871bc9447a09dd1b66ec6788c7b980e41d8": ["intersport.fr.", 0], + "3d81872c408f92f6f284c60891af807d04c3fccc3f1505aae6c5622e94028267": ["hdslb.com.", 0], + "3d82afefab3fef4890135d15deed8bb0e25994bb61a2201a2cdbe05cdddabad5": ["invoicehome.com.", 0], + "3d836e242170af2972bd94be5220fe289e1fd59b822e4f210b3de50896fc7f33": ["4gamers.com.tw.", 0], + "3d85eecebb570f76ea8e69bafa89a75b427bfce7fe9f07ad1fdedfadefe5c2fb": ["bsu.edu.cn.", 0], + "3d8b2015bb833bee81032a32a874e6690f21fa3306fd32ef09980e89e94befba": ["peing.net.", 0], + "3d8c23cedfd9570a7e7677db00d751d8675bb41e3496f205f5b3c53ce10d20d7": ["teambition.com.", 0], + "3d9031cce57437093d1ad4a887fa321e3a0a5ada33ae9d980fa5c29cfce8d829": ["ldoceonline.com.", 0], + "3d990c28d3da06585fed9db75e0201b5d2cced28cb039e6f58a2eacf179cde65": ["armtek.ru.", 0], + "3da036cd016d2902dfea86181b5f453c2a8a31ded07712842584603a0e7cc511": ["next.", 1], + "3da0f8ebf372dea906d171947524c8c739ac0e0c76e02a8eddb99d6417417305": ["hikrobotics.com.", 0], + "3db37028eb5f4ab7445ff0720613dfde840d983edfc06f4e959bd176bf3f4f6f": ["is.", 1], + "3db9d8d8807e0c5d917c377d461ed6f7c6d676379670bd1ae138423a47aa799d": ["onlinemictest.com.", 0], + "3dbfdaef98735e876d984a88bc3c2c8894ed96b8b006c973faa99ce6c477083a": ["actu.fr.", 0], + "3dc32cb019c15ba5db80c71f9a32fad989e7a219f98a8e2c7e6035c2facf2e73": ["singaporeair.com.", 0], + "3dc6ae9c0325b0554d5aacdf396a51caf85f9692fd7e769490d9a2249f98ed97": ["xn--b4w605ferd.", 1], + "3dcd5c4a3be59c2d8d758f256f89e20dd086210c4e3ef722a15d1bf5bc6677e2": ["extendoffice.com.", 0], + "3dcd68b6c10f4f13bd2f08c692c096caba5c163e972500eca550a872a72fc8b7": ["hunliji.com.", 0], + "3dd5e292b4f179d1123fc2a0689ec46430d69758ea26e43b2507375095456a1f": ["fastmail.com.", 0], + "3dd8ea99dbfbc49921432e6469ec347de80dc668041a3345d50b459544a5fa62": ["vietjetair.com.", 0], + "3de127a5b275d45dfbf7f56bb1e659ebe1928e96a101c3ae6855feb2d5712eed": ["parcel2go.com.", 0], + "3dea2c886b946a1f62e408bcfd732d9b6ec0595aec21d954fea22541db55e8fb": ["yachts.", 1], + "3debcc8cdcac1e16d0259ec078ae37b3ae25b28464f27e8d46db47ff18e7f499": ["vipsister23.com.", 0], + "3dee189f47c95141d24503b193fe63babd9d226e7e7462e16cbe18121a4f30e7": ["memphis.edu.", 0], + "3def8cf2b4e0fb97512bfa0322227f27dc1cce7f239d9cc37c215c23879913be": ["feebee.com.tw.", 0], + "3df2e389b4aa9bc608b1805b005a66f25a6123cd3441606ac2689c99cd5df5e5": ["lybrate.com.", 0], + "3df3c15d43905d23183e057e25651ad61509ef83e9ac0b468d556c42b44fb74b": ["xoyo.com.", 0], + "3df913b24d2de78e3067a51709d1ec84dbf6ea351f01eb6b96e02f05255cc1f8": ["cleverfiles.com.", 0], + "3dfa35af37b396095c3168ac6cac382b27ae002273a8c8bf0b09cae6e6fcf373": ["liverpool.com.mx.", 0], + "3e00a5d163d376eca8d44e97b9bd80753708eea5fdc719524f54f1afa2826cd9": ["umbc.edu.", 0], + "3e02c0b92c7251ef94e13ccce51fb33066f817d6fe41c26fdf2144ca4731f641": ["t-mobile.com.", 0], + "3e0707bce7abf6068b6767130d906379ad9b90b36d30919ffcb8d6e7cb40c8fd": ["sigmaaldrich.com.", 0], + "3e0859ecf201a03ac476382e379b7bf2c82640d84c14a37ea88201debd2300d4": ["dallasnews.com.", 0], + "3e0988a2fd079e68181446431283f1db9bfcce4612a65cf87ab3c1e83f725952": ["managebac.com.", 0], + "3e1171826ec58661bf585f56fe8693c3fcbe8cf50e6619213000f5c2b18613fd": ["lastpass.com.", 0], + "3e1374484325d4049291308193f9389ff3eca9ec0f9eeb00ba9fd62eb74453d7": ["igi-global.com.", 0], + "3e20bcca1c084566c3c55d2f4153123edd32d90dc51f2fd614aa5958df767d10": ["landrover.", 1], + "3e21d2f3dc2c2f694907d946197be4341aa68f6dfed4944f12d56cbf774f15d1": ["kinogo.biz.", 0], + "3e220e1a605c3f911dec9147bee6d37c2fe92bf22db4d14ef6ee6340ddfb1004": ["britannica.com.", 0], + "3e3231fa5b8fdfa714b7a64a4a2e2b9266e74b294e2a3006e268f64bb23dd749": ["inboxdollars.com.", 0], + "3e37241d8d1902825aacdd98625cfaf8e870b5f10ef4a7fe2303be69160b1b2e": ["xn--o3cw4h.", 1], + "3e43d41519c9807d6e4ddfd23b4ca76f85221c3f6c8868a4f8e89eefe0758101": ["siol.net.", 0], + "3e44c480024df499c5a3ca006bd52a0ca78571ee8d7dab74d0f7176b1f0e582a": ["jezebel.com.", 0], + "3e46c572281a7d76770c40a7cd54843150850dc1b02a36a9285706546b76fe33": ["shoutmeloud.com.", 0], + "3e49070576725213a7c7524150e54f435778306cebe501fffaab6f028cb6b409": ["cv-library.co.uk.", 0], + "3e492237b82ebd8c38e27d96341e929dea5e0da89d6e77cb302b8ae94b229ee1": ["pornotorrent.com.br.", 0], + "3e55a759202297802bbdf3a29c34fcdd9950e0d2e1c701fc36b8ae8cbda726c1": ["wayne.edu.", 0], + "3e58ff806abff95ba65bf556a0ab423b46d12e6e66ed4ef463f3e4e215e584a2": ["crowdin.com.", 0], + "3e62d347e71c22d62b623acde85ffc263f65846b34e9bf30637be226c19e0900": ["theonion.com.", 0], + "3e6866a8e63da1a641dda64673c337d8b414131dcf533b62efbe2a4cc8dc2823": ["pornktube.tv.", 0], + "3e696a02c38dcf35e59fb6fbab9c0f4fa3936bc27bffb68c469d1e47feea7106": ["vegas.", 1], + "3e85a5c1635dd10da5f18b761f29b764394b65eb7114dec7fa0dba73509dd041": ["truecar.com.", 0], + "3e888fd41c20a0782509a3bcb8a89b52a594bd070a7a6264055f4e1e22e5473f": ["macromill.com.", 0], + "3e8a639a342dde9b816e134936298d7e4ef8ec26beb46e20c981865df3c1eb56": ["pfrf.ru.", 0], + "3e8d33cd30111fad7d5f0f220fbfafa9ea5c55a412916560a2998f4e9ab5c6da": ["sjsu.edu.", 0], + "3e93ebdbf17bc3eaa219b605abeb7997c12ca1e7ce8de92706cdd213bcb09c8b": ["etopaz.az.", 0], + "3e9716053ae9e9cbfa6026e0b50e13b7addf52981ad4021b06c93f27f0f85b54": ["oup.com.", 0], + "3e997fe851db78ac40f783c9966334be47185a1e6bb222eaafb8c4afcf69d4a5": ["etisalat.ae.", 0], + "3ea3293af9261c5c970c9d6231073ed6188e5102d07de54eb7046043d8990b6d": ["propertyfinder.ae.", 0], + "3eac74c073c41b09a36d24ef904d7ae5eecc966473e629e9ca2834fab0437319": ["hxnews.com.", 0], + "3eaedcca91f878ec365e4310de0f7b5f2e8437f07c4b2f8559ad18dd266f9767": ["sex.", 1], + "3eb6b1fbaceb68ad92b5e416261184f20bc1bf13f719b5d8298011879a44bcdc": ["donga.com.", 0], + "3ecd1443bf68424733ed74c38465aa01df512fa303a4677202dff5a9f6259a23": ["man.", 1], + "3ece7133117c3482cd3a467184ea68578a2dad9322c9553f93829efd336a1c27": ["smartrecruiters.com.", 0], + "3ed4a0568ac7f8a19de5538648fa15a754b4f450c40d40796b5ef917bb841e44": ["classpass.com.", 0], + "3eda72e760f94ff0b386133649d471f2512959a3970fe8ed906f14e42f58e75e": ["nyaa.si.", 0], + "3edaab809fbafd28b858226499d194517b28a3a7a73a9b95d630eb84085d2d79": ["remita.net.", 0], + "3ee51a0daad642ffb28ea5838c537fa64b313ef6435c36166fe0f38ad18f52c9": ["uk.", 1], + "3ee69ccd85173802848ea68df99980b5e49a6732e1c89c65c57de7f90a25d4db": ["manhuaren.com.", 0], + "3ee6bb762f91ff948807f3b1fb37912d4dfe4d0e7c80dd308867b479aee80fc9": ["southcn.com.", 0], + "3ee91d30658b94ad215927e537e4505d2a66cac5ddca84ea7b6700ebdcd994ec": ["logmeininc.com.", 0], + "3eeb0b75c9da066d899602f3a430b022e1ad1719eb3cbdf227692b4498f8b701": ["ruten.com.tw.", 0], + "3ef95b62cd081106263960bb0d65fe0c8a260e6ac594893d94a785404be4d44d": ["soccerdigestweb.com.", 0], + "3eff9554b0b281541d8b3ba210890fd336a7b5a8c76d89275da0886821e89cbf": ["takvim.com.tr.", 0], + "3f02d4de2fd0aa714fb87d27f87728feb0af92237dbce8f087c6cdff51b09cdf": ["kongfz.com.", 0], + "3f08dbb6fb3dd95aac2c7d032407a9f42d8a335d54be691569cbc5cc57e6ddb4": ["activecampaign.com.", 0], + "3f1615dcabb2e9da3e20792e077e2e7ef045ee96b1b03fd5bf5bf8191226ceb0": ["asia2tv.cn.", 0], + "3f2583f12fa3b99e1c43ebc8450c64b05f019c1a9759498bc3f669efa5e8f7cd": ["cwl.gov.cn.", 0], + "3f32f7d26d2857c57579121db24407fa60604c3c929f5a12fece78755053579d": ["property.", 1], + "3f3a979ea639b74d6aef7b4b4ac8614415dfd2eef8bc252f5b78e4424d6f5896": ["qobuz.com.", 0], + "3f3e1eb24e79477d47d7e4b52b5cb98a609012a1fea7669b4c16301604331612": ["placeit.net.", 0], + "3f5cb9b590816ae1b0a3a2367fd182ede63aa38f169a4fb2a98af307ea19b370": ["eldiario.es.", 0], + "3f6f25e995ada49d7f1ecf0bdda3b1297f1bd6cb9cc34b5df8beb788f50b0a6a": ["uline.com.", 0], + "3f7a4fb747b7b27d8a77584259d13098b90bbda882802de7e307dc5b82e9c4f8": ["oyunfor.com.", 0], + "3f84d71a66af9321e1265ee05217793dd79d54f76d55033c8f45986fc2d17685": ["etender.gov.az.", 0], + "3f860566a0558502a92a5f85ca73e82331a0ecef01b71716151bdc49e1f3e6ce": ["wenxuecity.com.", 0], + "3f864a15df530ae802f9b0c884e9fdf00c9f9ef46f696cc10aab38ede9df7f2f": ["csas.cz.", 0], + "3f8912591966bcc75c5ac9076a0801edbd07b2ef989517fb8692c22b77343810": ["wine-searcher.com.", 0], + "3f8df7e474ae8924358b0f5b0c3ad69489bf26e5d27bd478a9549dd624f318f4": ["xuexila.com.", 0], + "3f97c04df25d8930efe4d25f589e551adabc2128411d4ceca8f1e5fb95a25039": ["buy.", 1], + "3f9b76e0bf800dec8c053907682049b264c735f629ed9a9e58771e631e78467a": ["mikocon.com.", 0], + "3fabc2b5007bd75b3727e53604944f92a123db5630816a230f560b9d6a959e4a": ["kanasoku.info.", 0], + "3fbcaca66836c2cbb311ec01ac969f13c5b74393749bc284552a68a33c62afe7": ["pikabu.ru.", 0], + "3fc0ef74a053e5a6253424622c380f2c3a985476ee461d371f15beea4d7b6348": ["doyo.cn.", 0], + "3fc56b191a5cb719953328e5a4ce93bfd5b61affc1fd3f5b1cff00d7921f3d3b": ["4cdn.org.", 0], + "3fd277b58aa28872512c8528a11f8a8166de0224e81a18c9a901c677013ff9e0": ["batdongsan.com.vn.", 0], + "3fe13ad9339023b3ea61e2cc8766320e18076ec496564939c628f52284ad7f1d": ["careerpower.in.", 0], + "3fea3802f5bc998535f6e6bacf1cd8011eb0437a136fba532d50014a0f6d2706": ["netzwelt.de.", 0], + "3fed19e4ea73055bcc432c94f9aaa1f919386255e162e371fc47d8d80150fdf0": ["realestate.", 1], + "3fef3f12e147e0e96691f39f66d5ab8650052e411e40a82cbb702dc33bbc0a69": ["aeon.co.jp.", 0], + "3ffd1530b4fca02d4d3a203510cb13a92975c16ad369c202013e6b918cc983df": ["dytt8.net.", 0], + "3ffe6b69f6049191aed888253f38ee839f21c32b6986ed8a2882e5cde164f3f0": ["jr-central.co.jp.", 0], + "4002a12977042efe3b1ece8a485e6f077508b7c417d278d926cc6d4953aeb8fc": ["nydailynews.com.", 0], + "4004592f363e2a6d0addac2156ed1318a7df6ce32b66bc9d07fc260760aeb15c": ["aircanada.com.", 0], + "400cca34ef191951f341b7b056680878693f617c3b6800712468f910dd668b04": ["deezer.com.", 0], + "400f2f318766dcb4797f86b91c1570cdbbf62b09d2ae5a62bddabd06374d4e95": ["vwr.com.", 0], + "400f2f7c4f67b85f73ca053d4ef01c7c0426154aff48cabf68607c695e2d86d2": ["goszakup.gov.kz.", 0], + "40135c4fa4e05076eef6c53280073e7ff055aa317759590ebad1545b6ce746de": ["simsdom.com.", 0], + "40181952074f1fb5b64344aac6638bd8236e2cded5fa8695a393ca861b99c1c1": ["redbubble.com.", 0], + "4029edab9240c8fb5d033ac78988a6a0078198f6374d9a6fd772956126cd0b02": ["genbeta.com.", 0], + "40336b4b29be13c231096dfb3ca2a101447a91a9174cfbb78e8358fda1321f13": ["femefun.com.", 0], + "4036a2caf717a2be8b0d55e012e9617098526603bbacc59f84d972b7d9086b92": ["tjupt.org.", 0], + "4037486902186930f026705d760b735f4ab5a699f9972f6cd5772b30e3e0ab5c": ["ruiwen.com.", 0], + "403940e4be5de78a82e7bbbc64a671dfee711f1709ad881a9ba7ea1adf4dbaa7": ["unm.edu.", 0], + "4039e5519169a0e5103e9a534387e82afec18a0005bfb32453ebc81123b1a4c4": ["hitfile.net.", 0], + "403cca00a8bedd2b9bde655990d58d3be0882efee0a7b76311e1553d377e41f7": ["cliffsnotes.com.", 0], + "403e8ff16a21de260722648060a1a8d475313f74445693ec2b9817c29ba53529": ["xn--rovu88b.", 1], + "4040fded38855ecf9d0164e9124f58f617c2d904de0ff5e352a710c43253b947": ["nubiles.net.", 0], + "4042ad5f879d63916f5bed752602e731a7f1018e69f315c8306176d73db4e769": ["cabelas.com.", 0], + "40439dab129f5f0a693547ad3e23673599ad3df7073fe4ac4fc21f8d19fc8bf5": ["novipnoad.com.", 0], + "404847120028006e9dc2f0d86565469e690774dc622d83fab3be056db7d928ee": ["iracing.com.", 0], + "4049cca15696d0decfec6b890e6d72c6924ef8a4a7623d6e6d1ebc60837f33ae": ["cloudera.com.", 0], + "404c366312b5c05c313046937556d36520d91532ff028c1e885c103d401edfc0": ["fotka.com.", 0], + "404c9ffb0a1a6a1540b47d43995d44a1a07f4989433d6c080a4b9ce0454d4f80": ["ctt.pt.", 0], + "404fb06ee188f31c86a4454d3f28827fc9d6cddc7988ccd801e8052a53acaeed": ["eastmoney.com.", 0], + "405a3c2b1330fffa1e7126fde930a374c8e667fa423845e8bf7ff570d433092d": ["egrp365.org.", 0], + "4062ef4a21cbf3e5a42b1cb5d892cf4ca78abae4e39146d0355ed8c419c84842": ["hjenglish.com.", 0], + "406c799af560d8f2426e2251c62062b9fcc3cf3d389282f087b9be64dfaf0f67": ["winbet.bg.", 0], + "4072016b0932704e1ccaa2b94fa98746d76e2a709c9627657cc178b35ea03f1f": ["definicion.de.", 0], + "40775db8a2d6feb4358d7c97bb94eab4a6a27b48c531d2f6a2dd1ed067b3e2e7": ["kvk.nl.", 0], + "407af29f7afa8a74fd0a8807672bfc4a81c2b22350bad3b5673e2be88bf33cd9": ["persee.fr.", 0], + "40839b2a182fed6db12f651a4b9b0c0de7d116637df8d17abed4c65cfa7251df": ["recruitee.com.", 0], + "4089fb7c73bbe979b8f370d7958f0a83ee34b3dfa5b9e3d716ba9889d1f31cb8": ["whatmobile.com.pk.", 0], + "408d52dc688bd00f35b19e913aaf2b2a61f0ce4184e5175a535141190af3f1b7": ["purewow.com.", 0], + "408e8633d1f2e1ce1405c11f8c790a85108615bf2bc2d4f5d5ccf5efc8de6de1": ["userbenchmark.com.", 0], + "40927314de92c5543548b81a5a45e839e72b2ce0467bcc1506781b0048315756": ["matomecup.com.", 0], + "4095c80c44cf454d601d9df97e61658697308787ed62de6836b2ac562ca5e758": ["lgbt.", 1], + "40a22e0af67dedbfac2ac779a4890438367eb9591810539bea15f7f41910068d": ["xn--6frz82g.", 1], + "40a7cb1675eb24aa6e8a422c69ab8d0cae02ca612920f3a761d532a0c1b2efdf": ["sca.", 1], + "40ac8f7c435ccdc06714f887289782bb3ee78db6e67d5e699d5ee3118f51d2fa": ["co.", 1], + "40b3c0893080679d906d6afaf569956609df46d3354eaa35da9bb25c3c6d4e70": ["wetteronline.de.", 0], + "40b88f15c54c058492d27904d46f04f2086d8e9a3003dd85b925aa9b6344d9c8": ["oculus.com.", 0], + "40b996f59ba6819e7205a568d36de7cb828479b026cf374ced3a4139fcf5ad39": ["vergleich.org.", 0], + "40c5e775b5c895bbf0cadfe4bf6aa8e20fd8ca47c29aa4d05ad67339959b2ea9": ["hsbianma.com.", 0], + "40cce980395dacb3e7b96a941579db45e744bc675bdf32bacb102fe4819f1d71": ["pcstore.com.tw.", 0], + "40d8545f985b7c50f5761894f3b1afaff9927fac55d6af261a784a23d00bad59": ["blogfa.com.", 0], + "40d91f5b04974b2baac07c608785153775784004eb4d3254ee77701d4d64acd9": ["medu.ir.", 0], + "40e0dcab939550f3a2f0fe6c99abcceca72c7b00c2f65f85571ab5ad001837c8": ["acmicpc.net.", 0], + "40e2a1ff59e65f21e5bf98bb122b13af9a3385837f061a7d91be1dda4792fab1": ["kartaslov.ru.", 0], + "40e348a47624b1dbb195404890264c57287c712eb2b8d6ebc0209ccc1a3e41de": ["haaretz.co.il.", 0], + "40f8a9b4474908e8ee8c96c43a051c36aeaf710a5c9faac9b0e4843506c8b2b8": ["kerrylogistics.", 1], + "40f92356622031f4d0adcde85dd3a564299c29ae03eac175939ffd890e2fe8cb": ["sapsf.com.", 0], + "40f99e15f73ccc53804295015f66fd330f8d9efd34ffe76a73152be683eaa191": ["nmims.edu.", 0], + "4104101486debbe2fcaebe6b6f455c79650980cb0c9bc5eae0b4b5ec99063ff8": ["cqmmgo.com.", 0], + "410a3ccebb2055445c2f4d0d999448c29b98f889036684549af38baa178f4b05": ["ficbook.net.", 0], + "410ae90dd86a2ce5f2c97ea6317735bf1af9ef6d77b92020a64b8f008659f2c7": ["ssaa.ir.", 0], + "4122d08bcd9643cba84317ad7d32b463ef26d28b9b81ea7754ab4420358dac35": ["businesstech.co.za.", 0], + "412fe16530c842ed66a291e9d8407c72608c8a853cf979c4697164c028bc0b3e": ["celebritycruises.com.", 0], + "4133efb8f36d66ed1103a8ca820a43be512cd9cab17f1a04f1abddd53ad3447b": ["officeplus.cn.", 0], + "4135d31f8e0940ac3b9ecdbbcef87eb165c8356f81d643bf49e6f6c16abe969d": ["django-rest-framework.org.", 0], + "413aec98b8260c3389b892ed075c823358c65c010435fc9fc33d0ffe238e168c": ["katcr.to.", 0], + "4141cbc531468aab5e9b8a87fdd45c253fcd232008206fa382d2fd748d3f9071": ["modiauto.com.cn.", 0], + "4147b45d200f3cd477379d9f61e14a4db116b110fcc76464b1c5794262304452": ["sagepay.com.", 0], + "415011859f6da8d62ae3c1666d68be7b5108c74b79191b639bbed79b3aac08bb": ["fly.", 1], + "4153f8932a152a475710d546a4f8879bcc6d55fd79e32ed5014d7926b4e2c216": ["pge.com.", 0], + "415b5fc222da4338e757f5c761ee516a68921e572aa979b60c8f1fca2eeb5358": ["dng65.com.", 0], + "4162fd40070b41c13ab3c2935efabc904e351aacaef46d44d9a793e21afdce7b": ["litmos.com.", 0], + "41636486daf210a9ae0e72a8433b4cabb8d7dec53bb0b14aaa4cd5e595372927": ["sandvikcoromant.", 1], + "416b58ff33f613e9d8cb7bff2b06b6e345317e111ac50244d789da437b50e772": ["livestrong.com.", 0], + "4172015253d9cea82ca6797626ccbed3ffc943ecb8995651d71b4e84596371ee": ["biccamera.com.", 0], + "4175f51b523bef1bc2a929bb8f6568d054d8077adcab1debbf89aa7ead51f91c": ["listcrawler.eu.", 0], + "4175f6657952590809c45c439f4829a4aaffb30571de847543a797417eb5b991": ["qualtrics.com.", 0], + "4178881843464e965f9a229bffdce5b8d023f57560af36b9fc133884f06b7ec6": ["caesars.com.", 0], + "417b11d1a28c9c61d8f652fdd40ab85e4ce865b451b0dcfc3a2b3d7f3d3d09ce": ["ups.", 1], + "4193c06bab78a453ac8a91393f220258533ebe1132803db5e13d86cd38e3a20e": ["sportdog.gr.", 0], + "4195d89e44f84d7b9350be4afbd0abba4440619cd537cf11b6643fb94727b4ba": ["etrade.com.", 0], + "41a1d77988ba885e78b33c336a7768784535da1c1a771a3ea322cd2a20542a45": ["looper.com.", 0], + "41a4388283dad147d483db7cde45ddf3784178ae0d14920ed3065d376a1c559f": ["xn--mgba3a3ejt.", 1], + "41a8a85160eec82fd9f7e7c9bd2179d6a27cc56e9ce12ec41b96f1526edb6cb1": ["lww.com.", 0], + "41ac58b05c58fa05c3934530ff4231ad888baa72c13c29e3b44c0310cc86eb7f": ["uu-gg.org.", 0], + "41b8d2210198c19eae73533b64edcb34f3611a16b1d18d72ec2b2128e626fee3": ["freeones.com.", 0], + "41bb482a689d530dd2ceaecb41f8b97d3685658d51200be31d33afc4c842e9ab": ["victoriassecret.com.", 0], + "41cd84a80e4cafb4b6ef564d37cd70f25a9ab7dfedd17ea5df663ccfb979b060": ["thredup.com.", 0], + "41cdbb766f85818b8fbb1c9e67f5d2155454330297cb6e427a9d0bf96a064b4b": ["1ppt.com.", 0], + "41ce1f48c316bca8e5ab1ea7a56b3d5ffa5875f2ffcc86ad125a6534d678f4af": ["kronos.net.", 0], + "41cf47c9d5297b8f224b89efc1647f33b1d4813770eafad5b47cfe86fc9e3f6d": ["sh.", 1], + "41d1ae7d931f75608ca9872042abebb7dc208c348eebf5941d104af1ab3fb594": ["letterboxd.com.", 0], + "41d43eeed8c04c66b3fb9406d7e56581f9121718a4356a8a704f733f1dc8d5aa": ["albawabhnews.com.", 0], + "41db3ce6ee1bf429cb9194977c1e6f405234a363156c5e6439e924bae6683801": ["chess-results.com.", 0], + "41df52dd11dedfb48284c135b117a37c156fc39bf85eb901cbb7779df246cea0": ["rzd.ru.", 0], + "41e472f6c0fb4a37a67038d37e8061cbed9de4a5e99b25f8c7411c764b856fa2": ["cronica.com.ar.", 0], + "41f5c3e7cd9cbd8b1025b5fe70ae7d0fde168e0169929eddefe04f92e197ab31": ["caribbeancom.com.", 0], + "41fe858d8aa98985bf75acb19032a9c708f31b5b8344bf3aea5f0620b35323fe": ["alstom.", 1], + "41ffbf585aca866f80c8bc4e55a6453a127e0b6ff62ca92a6d3d38b3314f1f98": ["binomo.com.", 0], + "42011a47d9019b5d19f72f32237abe28e87b741c17833341454f46e5de764360": ["xuite.net.", 0], + "4205b857c5c722ad89d9f32cea90718730e76938ac95f9f8859e41e79e8a1de4": ["icloud-content.com.", 0], + "42096a8c82a32e7694dc1cd5ade38dc34ac56d6179c73771f7d83a917ff67447": ["cd.", 1], + "420baf620e3fcd9b3715b42b92506e9304d56e02d3a103499a3a292560cb66b2": ["world.", 1], + "420f4235e1700b4207fb2d1320fbd930e6a2b819f5a212350c69ab07f37cbfea": ["2ch-matome.net.", 0], + "4228877c564a3e8088874f8d8b24c03dde7a642b746a6c29b02ea682fbbb71d4": ["filmfreeway.com.", 0], + "422f777e5a276f02953324c7e81100e29ee3b3390f477400dd8c4a0de4a641a5": ["getchu.com.", 0], + "4233099ce193a2bc464afb6cd234c1ff5037c1d66124b55f679536b459c75282": ["bcy.net.", 0], + "42452c6919ebcc40dd790583754614da6c93bc6d38adf2c00194d24784df2d95": ["gobankingrates.com.", 0], + "424b8ec283ab54f2db798a97726dc240840d76db61befd9010dec45b4fe3df4e": ["wmtransfer.com.", 0], + "425bae54cf3a677338148cbd8bbbc293b9dcd15d8a32d403c8136dde1d2f1c65": ["xn--ses554g.", 1], + "425ccfc2160923eda0c7343b8e048ed073dcac05297d87918d5cf0528babd3b5": ["payproglobal.com.", 0], + "42685243d30450f03b0ccd71e3f4a14396ca2220941dcc1697d07a9dfb9802ab": ["xn--90ais.", 1], + "426ec539208869e8fd5a92f8e5c81087577f129a93076ee44bd5d7fb35ec49a2": ["nbg.gr.", 0], + "4273e5a57b609fc34d09df880c6eff35760168744709adb3b0cad0c04f5940d7": ["quizlet.com.", 0], + "427e7074e455fe98828eb1c228ae7efc33d2d8fb60a1ed21f2c61e05dd8afb1c": ["ing.", 1], + "427f165c077c7ff2d74d85b116c016dd14cb9f1304c07c41330a92c151533f48": ["csod.com.", 0], + "42826c1153eda67f6a7c83811504ac8af28be4b1a0d86ef25ffeab561bc82292": ["omelete.com.br.", 0], + "428a206229c184fe6233c06291b577a6ef5839307c5a940e9d23b53f66424f98": ["digitalmarketinginstitute.com.", 0], + "42a990655bffe188c9823a2f914641a32dcbb1b28e8586bd29af291db7dcd4e8": ["apple.", 1], + "42a9a3936fe143ff30b315f8b5c881d99b7f84fee76884383637b181e32f8a6a": ["flightaware.com.", 0], + "42ae5142be84229d0231c99539e7389a33f705b1bd6f7d1d855fe51ed9d2328a": ["extraspace.", 1], + "42b34328ea36912ec5f5745442d37e13b4284c27e5ff378bde997ae67665b923": ["choicehotels.com.", 0], + "42b6b4390bb817471d71d4d831f19f1d1a0f2dbb3d0b34c7dff856076936d2e4": ["spitogatos.gr.", 0], + "42b7c5bdf94e0d1b9c423384aa28785724a6e73777a8ad215d8a85843e58f712": ["rackcdn.com.", 0], + "42bdb8eedf241bc39c268e9ba16b3530c31a3448ee6fd9383e9068b63e43abef": ["eveonline.com.", 0], + "42c65504cee4303f4a390ec4bc727bd102983945ab968ef7c94c3c452a64828c": ["paychex.com.", 0], + "42cb16b6b184df0ed9967ef8a81e4295b31c862690f64071dd9a051b7094b748": ["kakuyomu.jp.", 0], + "42d2ea5017ba3da8c1c0a0402569876fd174e96471a75e17d0e1aad2628224a6": ["censys.io.", 0], + "42d8fa045114da3fdc57642b2648f0eb7604767331c203287de7c328760b4484": ["szlcsc.com.", 0], + "42e272cb6baad004912542a51d0153df75987fbe50ddaf8cf256477ccd7c0e4f": ["subscene.com.", 0], + "42e4f89cb1b6f96ad10e696be2c37593bd8c4fe690ae75b32f89937c0a5bf269": ["nhentai.net.", 0], + "42ea0b806cd32234e03db074992b35e5414bb633673673bcbbcb968bd312d24d": ["flypgs.com.", 0], + "42fd63e2ff4792f8ccd1fb2ec392a0da74f74c0b15707fd0b892d886f9dbbb3e": ["voyage.", 1], + "430621ffb9f24cd956cc0466079a44355031f80f3e61823f8206f8f78f71ced8": ["forbes.com.", 0], + "430f3b22559acf58f9cf3a0310ee350d9713f141861ff2dde82a820b5fd96584": ["unionesarda.it.", 0], + "43181374a8ad88e70681dbd866d47ed4829f03245312b1285cd0c3f501e93b1c": ["khanacademy.org.", 0], + "431a52295aa4ca8652d8b01adcfd295bd5594b28d441f807a7c4a676ae29666d": ["toshiba.", 1], + "432747487f31796be4f1b333b99283caefe482fb29e293a2cede34e6fb93da28": ["priceoye.pk.", 0], + "4328d537d0d00f7adcf5efdad8806823c7bad090e83c6069070d879b49b9b7dc": ["imvu.com.", 0], + "432b0f2f99715ba5f17c405c3cf7ed9ce3dd206c5514072ccf640bbf723684ca": ["thestar.com.my.", 0], + "43302101816427426572db844b59900510ad640c5893f64f02ab579d464b65f2": ["chemistwarehouse.com.au.", 0], + "4335df434994a64b43011532d48496140168eecfb833535679d1916ca3261969": ["spicejet.com.", 0], + "4337c10a9f7d17a83660663ba497cae896d8f84ec1f14190a9793253960eeb22": ["slideteam.net.", 0], + "433c517696707c9db4b2bdfc1bdaddd3aabc84c50a93c1a5374b03d178d8c45f": ["sunbiz.org.", 0], + "435130b8a87b8168b05cc4641a0514ecd761fe684feaab3e987e0e2d5730ea3d": ["poste.it.", 0], + "435e2a98137693095612532a9be9df6f07b764d4511fd54f8410cf3e117c92eb": ["bingo.", 1], + "4360e72a220be8bbc3c47df535cb99f77dec86d7de7b596357570642875fb8ca": ["shabdkosh.com.", 0], + "43678efba901981081751aafea3319c9b1d758c92053467b430fbb59ff265142": ["xataka.com.", 0], + "43705b14cc993385d76f60d7ce0db0c4fe4cb0302ce22d381cb7bd3f98a50ff0": ["tellygossips.net.", 0], + "43735f64c6e9e96d20569a49932cb59fa75f3e792e0bdd717b806253198e0518": ["prtimes.jp.", 0], + "43763cfb97e981b96922daf0b17d97919191fbb4176b90f53f18d3d04d3eaa08": ["partners.", 1], + "437c881995521c3c3e15af361eaef34f12df7d95672a3a3fa744a2651d0f5a34": ["ets2.lt.", 0], + "437ea9bc5ad75dfadcbbade7c73c0b971a66d8f63c34bc7743d86d5005da781c": ["topcashback.co.uk.", 0], + "43802d33c87d6bcf2c7c8b586661c4e62081db1ca4c14511c5781a9deed892ba": ["koton.com.", 0], + "4383f93d62a8d262ed26a0f0bdcea7574a348003d083c6cb33e45b6722cfad32": ["tumbex.com.", 0], + "43849b0f398b3690bd89381d995a7be1ababbba67510e08721d7dacbb5f0d1b9": ["behance.net.", 0], + "43851aa9f0d3b15dc919abe957fecfa6d43a280c4115fb5c51703ae34caa0a47": ["lawyer.", 1], + "4386ee86ab7ccfa1f6eb15d975b5e4bf5de820b7332bf53dae5d9faf1924f1b8": ["nc.", 1], + "4387fef14e0d09c729ad3d0e272f08677d877bbc0cb6c0e8ea9be7519d90b00f": ["ancensored.com.", 0], + "4388d5f7d47643de7d260a5cbc24bc7e1bb5a08184dd85e2a9b857a56d8f10d4": ["technologyreview.com.", 0], + "4388e714d3863ed6ad86ce21df256722e5c4ef927d827a6bef9678ce062efa89": ["burberry.com.", 0], + "438bd07e60eafdff399772998f65f18eabb9748b782bc2d5a6229f7ab11b5347": ["timeanddate.com.", 0], + "43980b25c5f6850e8623db19e9f6edf6b45ff2dc21222613f0012c872803b978": ["drivepedia.com.", 0], + "43a3e11640a2abbebaf84ca5f38efeae436b1d44fd369631e7cf870d8ee12db8": ["noticiasaominuto.com.", 0], + "43a439a0c6f1de3f520ec2aa77fea45a40aa645e804bb7aa85276f1ba1904f25": ["xn--80adxhks.", 1], + "43af7342ecc878dd8d386969f81c8ad1064a24f689335937623818abc0c57752": ["datacamp.com.", 0], + "43b52f3893cffcae4093222d85fb8cdef8d9f50dc775151f7877767802e61a6d": ["blogdumoderateur.com.", 0], + "43b962da15e7796f40bc00bf8e58046a295efdec1cc686f0286e0ad34bf2e992": ["tedk12.com.", 0], + "43bdb2ce01cb1ff03963c5f993f717f18232db255400d6755ee9a7c211f9e34f": ["recipetineats.com.", 0], + "43c4d7c7fc6d8df67b8c66d6d0521a4d9bd006978f3adc963b7d33f440292870": ["dafont.com.", 0], + "43c73685a37da734516bd3378ea785a58f35a2b395f5b0e91a5c967fd1b6ecf6": ["haqqin.az.", 0], + "43c806f5d780ae1b4baffa7e1a823ad2a6388956bbe85feb358f115a88d9688c": ["blackedraw.com.", 0], + "43d176f3e52bbee5799b6728f09a9ab6701f24f51eaf15ce40864d655037fa14": ["tuya.com.", 0], + "43d3be64468f6e79844bb0b815a45304e26db908c8ebedf4db551dcacf20a64c": ["szn.cz.", 0], + "43d7f917c09ede0a660bf0e5b706b0b5b88769d5e111c230f08d8643e9d72a7a": ["vmall.com.", 0], + "43de0ab081d2313aa3cdd4a8a97898fee4df7ad01b6064e429b633c3f70a953a": ["warframe.com.", 0], + "43e03817d2a682bdc92cf2af60245c245c9ac92b6d2e86e0365482aa7a9273ee": ["alaneesqatar.qa.", 0], + "43e072530cdf5fef09d8bb74b205f3100238978d0b46e07f59601f8a8e0c6250": ["yaraon-blog.com.", 0], + "43e0fdc4d72b3623846b512dae94becd76318efdb6a5e07bc6dd30db695c08cc": ["serverdata.net.", 0], + "43e2c1ce6dc46c678a2cd10a8be7f3b43c741627ffeb20d5536e315ba8913c11": ["uesp.net.", 0], + "43e7e35fcc65c1e9c50f2add2a0ba3dd012ec1972f141084aac7b2e82ba317f1": ["ccleaner.com.", 0], + "43ebb0d606d73df65dc1d13412096caaaa3bd47e88b4138532e67c237f91856b": ["win-rar.com.", 0], + "43ec4825256a4a55d23161fd29f87646e5ee55a03c98c6336c704b7805282091": ["hotukdeals.com.", 0], + "43edadcd8acfb2c9759e7d35e18297d8a461d580641fd51709c40a8e1d07cc02": ["hotmovies.com.", 0], + "43ee45680aa6417146f25b5c3bff0bf0f9cb7c0d7eee0c9b18562914bce2da61": ["nps.gov.", 0], + "43f54a1abf1b0d948b8c0c7fca8f5ccde413c63aa55328fc45a1c6919c5a2be4": ["carview.co.jp.", 0], + "44017652a1f2ba9ce376ee23b5e520f863e4bcc533b93296b3c4a138bdddcd6d": ["ichacha.net.", 0], + "44088f57b472c20edd15a5cc71e9d4798f406d5f082a8e366091a07690cac1c5": ["stoiximan.gr.", 0], + "4412871cb9ce27f7809d125e4056e17384d4c9b494d2f14a677cd07651586ac1": ["thinkific.com.", 0], + "4414500d8b1cfd3e07a9282288eca5c99d944bc410844a1db00fa8a06bc189b5": ["koreatimes.co.kr.", 0], + "44171618e9c12ad799106538669b495ea85cbd20de5c5dc571c21fab0f35cff8": ["otto.de.", 0], + "441958710aed2fa231311e2177b5b7e62359797ec0ee92b3d9f886833c6680b4": ["banglarbhumi.gov.in.", 0], + "441e255788e14e640a18b68e64114c129227e0644c499790130ef60946ca66e6": ["whatsmydns.net.", 0], + "441f541757d2f0d2f26f715b7d75ad1ded6598e649601739ae65160db8940886": ["prisjakt.no.", 0], + "4426d049c77422c854ecd7ed0ee4179746da2ac6a1924b31a9e5dc443a42bce6": ["guanyierp.com.", 0], + "442f3a95f900dbf9ec4389ad7c33c0c6b7b0c1bc2fdfe6fddd29cfc4261c6403": ["silverchair.com.", 0], + "442fbb273326d2397d15db655dc52323a44b57fe8c581e7a83c703ea65783ae5": ["anthropologie.com.", 0], + "44371898c6406a57fce2dab3bd447587bda4e3011512b52220195d706abcada3": ["streamkiste.tv.", 0], + "443b8259116f02f82f62e5562aabe4724331bff9777e42e3db9108199c86e44f": ["livenation.com.", 0], + "444219659a140abf486e6ed21f0665ee0368b2aa94b1805cb9193920ed14f582": ["webmd.com.", 0], + "44427c8e0fffb104149388ce603fc2c27c5b5dc33b97ca92584d779eba8eef9b": ["acehardware.com.", 0], + "444b0ba435ee7a3a09921936128194a1a04754c45c3b6f0af3e07257a2d9baea": ["bandsintown.com.", 0], + "444b47f894cdc5b489412eabbcca4f6e77b75579e142dd11a3c6845368cc5cde": ["screener.in.", 0], + "4451115c842bdd90931a040e0ef9a0c7bc4cd0368968a24a2ad0d0808a2de121": ["flyscoot.com.", 0], + "4451f570c0c236be7ca1013f52a90f6dea03955aa9aec1a68b0632fe3fbf426a": ["undocs.org.", 0], + "445362408020e4455fd210a7ce22db3260e1dde4e959ec131abe7f7e4808f23b": ["sm.", 1], + "4456bdc3632499634c2bf0de04618d9e8124b08a1a467020cbc827ef7cee649b": ["ele.me.", 0], + "445c28c414ef109ba9e3ef1c4fc8b820e85e884a9d360bc7e8725dfd2b9639e2": ["maastrichtuniversity.nl.", 0], + "4461362461e049f2f5bce7ba599d29de3cdc17da9cf1e8cb4e489a75247b1a6e": ["redstone.", 1], + "446355ddb33201ef57888223cd24e8e4091acdd596e001886e87e158779ed339": ["gazetawroclawska.pl.", 0], + "4473c0335209cf5a03cea6c2aef2f2a5b3e964db81ad3794f516b6d8312a0a04": ["final.", 1], + "4475619b1fc842831f9af645b268fcd49b20113060f97b9fc49355a69bd0413a": ["craigslist.org.", 0], + "4482fb940321349b9665377c784afc6e70e19ce2883d5b5a74ee902b5366dcaa": ["ciudad.com.ar.", 0], + "4484b7065132f5c858315ce852f62c7d0bac031e0b63a847dc1e6422a80188b5": ["jisho.org.", 0], + "4493db7afd015eadc9f92a7c4ef9c2718c54ef4d1e9876813deb45573cb60f37": ["planbook.com.", 0], + "449759889ef62519f49f7ace14992ab22f0ca837f2d8515a7e7843621295529c": ["ytmp3.nu.", 0], + "44a068251c7e0781a04557942402ab55ec231105d7b3af14a79e47ca8987a949": ["mirrormedia.mg.", 0], + "44a7aed89d908216f114652ba1a8966f9bf2adcb2ec5268183541ba905cb6d54": ["hangikredi.com.", 0], + "44a7b1c5c3f9ac6d3f057ec73ebe1326607810104c5a60e3f3e6e6a54a991993": ["ocsc.go.th.", 0], + "44ad7e915bc986a0eccf78077dcde3f907e67e52cda8c5debe7e212c3189c592": ["ssc-cr.org.", 0], + "44b267a914ffde4448ad33bc0778f5c7f6c89602eecd20c2c8ac6fbdd05fb17d": ["adblockplus.org.", 0], + "44b93bd8e5039cb0dad9c400d25a6613dfe5f92402825010abce32b082fec909": ["xn--imr513n.", 1], + "44bd3d1512174a035fc2f1f056262ab6bdacea7c84d778a5f17e65c9abc7ec24": ["kmail-lists.com.", 0], + "44be58448e45469348eb60389356b16e6495d457365625db0f6e8cdabbde5cc4": ["cheapflights.com.", 0], + "44c3cd3d833c2cbf806b0cd4a33ed875c50ae793732672de0a007dc030b3a657": ["racaty.io.", 0], + "44c43be0488bce361320b518018693b0cff62cbead97b6dd793e32e0b83c89cd": ["downcc.com.", 0], + "44c486b30ef3fed0d78003ca3a57b87f83e5909cd31d20940f15259395c940ad": ["hypixel.net.", 0], + "44c56c8668061e44893bc464f9b372eb945766df5682f84dcdcfdb984456995b": ["qiita.com.", 0], + "44cbef4f2cdc4eb6224e2b3666deba374a292e40385889f435750edcdf6ebf11": ["shangc.net.", 0], + "44cec6c556c9ce3bdb163e656ed25c9d22a1443e648d964a9089f3e0df055584": ["adliran.ir.", 0], + "44d34271db400f61b91f929a7edb7286b32d123477896bccab0ef4b9da179dbd": ["foxford.ru.", 0], + "44d5174820df8da1f983e61c06958a6931b55055063efd80e9b0a97d1e3c309a": ["olayangroup.", 1], + "44da08f4c233c667d84358ce46f96cb754162948af9af9d226495342635d2325": ["stepik.org.", 0], + "44e3a46a37a50a51b124286e7b077976baee958ec6a59509f992fa482a92f6ca": ["torontomls.net.", 0], + "44e474df781582fe4f454c907f808a44ef5fdec5aae352b1d0a7809f4f0283fc": ["browserstack.com.", 0], + "44e5300cb9a69f2cbe0802a026d9b1ea8eda5b7fc1b266b5a4580ee960772032": ["wantgoo.com.", 0], + "45085d0f808649cc3013565d4026bf26dba6f490558cecafe52fade222fe85eb": ["spa.", 1], + "4508e8cfedf749ddf0da966f73c390db5f0b08f6d8b79c96b3c68f4b73574887": ["screencastify.com.", 0], + "45099ee7eb96d44fba1f8d876add12b2c969a1ef5230e3ec75ef8e91ad378f45": ["olevod.com.", 0], + "4509db957c24d6152966f224eaee5523e71e8f57c19ef35efe5f41964be2786b": ["trimble.com.", 0], + "450c3a680b930ed5b30c317fab536346334976a53a72089dc34316aa8cd0af23": ["tanishq.co.in.", 0], + "4514e9e8fe32b41f3112289acec7d9717fd7805e1560f9ac468ee7907042c7b9": ["tatarstan.ru.", 0], + "4519d9fc4e458e58de829441e4ff72842c80093600ef59c89c43e476d8ab9145": ["sinonimos.com.br.", 0], + "4519f62d8396b792505c233b09defcbd8e5c5356fab4b06c6ea699e9742ccdb5": ["3h3.com.", 0], + "452892c37322c9e1cb574b496dd39f9c9c05118258472bcbfff37cd2e7699deb": ["clubic.com.", 0], + "452f3a926d7823d314195aa7ffe8a5599db3e036a01a6dafa20740385b30ebbc": ["babnet.net.", 0], + "453dba8d32dbbb8c3d2706df368dbd7cf5df426ded9f2e1c9af12e9d539baf3c": ["jobinja.ir.", 0], + "454c82fb57328eeb66cb2cef9c3a2af79d9ba5ae32cc99e49d410093159033e7": ["gwu.edu.", 0], + "4560b1627969125576ddac47c2530d39c84bc95728a875b088ebe1df7911014f": ["fkw.com.", 0], + "4563d65049c793ee4c21f6378437dc798cec0c7c3e50f046ce444c64953c8ec2": ["halkbank.com.tr.", 0], + "456417f5b7a36ce0fdd6cd0f108d9f0263ddf46e06933849a40cf6d541f73c90": ["biqle.ru.", 0], + "4565054528b63e5efd535d7465041b75741a14ec8be2bea3fc13689ad9a85822": ["maxthon.cn.", 0], + "45688376e250174ff4748682ef78bc7d26fc21326cf6f896b7826d427e749519": ["uab.cat.", 0], + "456cf3aa3d1a7c22c97afcc4de05e4fa3770f4e2bfc8820776ab7e7ef13b6f53": ["euronews.com.", 0], + "457076c38dcdaa61af5c2e4c1bcd9adfa5f1df8049faaba745a9c155ae36700f": ["4chan.org.", 0], + "45737c55e58123b7c74803149a9d4e9c430b9ada64751e52baff8f9d7224190e": ["pitt.edu.", 0], + "4573909228f7007d417552520d9260a0f742985e8dacbc43e7a342904235b6ac": ["hatena.ne.jp.", 0], + "4578ab75fa341e127eabfde68f265d6b989215cceb9272a4221dad21b4709c74": ["pub.", 1], + "457c46ea0257abe0b5b38ba426b759f2ab49e8a51855858faa2e4164bba2fe0d": ["wechat.com.", 0], + "45814ef9754a979f4c102d1111a56307bf0e8df902b6b2ba3b7a28cf1827c9ce": ["lawtime.cn.", 0], + "458ab0cd1e2884a1ac20380e5ba52573e7dde74ca78a64336103fa63b146cd9c": ["jetzt.", 1], + "458bbdfbacee8144fabff9b96078c6f26b22717e617fe438ce0c0d0db1d0697f": ["iflytek.com.", 0], + "458c0b77a602bf63f45eff77d8f802dd6900428360b23f5713b432cd83fd5114": ["downloadha.com.", 0], + "4590b1a4fc8cb98b0a993092f6dca0dfcfba361bd7d99a46626d23f6b1481262": ["scmp.com.", 0], + "45912e00795b0eed2cabf32850daf9ce9703f108c67f6c16566a560c1df3be9b": ["symfony.com.", 0], + "4598448106895e024aabeb52def6953eca4fb7cb4fa1d7bc8c201eb60f1c1897": ["techworm.net.", 0], + "45a1678b527eefcf2de323ed6c5defff376631c8d800ccbd85b2f719ddfa4a09": ["itau.", 1], + "45a332918d84ab072f1b13a5c43aa9af9447ad7894faa069c6971ff8b9a9ed3c": ["flaticon.com.", 0], + "45a847c4082a1fcd98504afa37836307c0eae85fe42d7fffdb6980a53270c758": ["haus.", 1], + "45b33397402d12188e7e2e1079d0346f6c781d5d0982759b45433ab6db64f79a": ["filmyzilla.com.ro.", 0], + "45c1e24d5a5000b2f9525bc55c9c8dd5a491a809b16a5464065cb7392f4c2296": ["discount.", 1], + "45cfddcd44ddf11c06e9a7d7cdc1c86b7db2dc6fec4808c4b98f6b1751cf388f": ["aminoapps.com.", 0], + "45dee478e7c1e8568651531dfb749de2a05044763e36b20decd7218025045bb6": ["uic.edu.", 0], + "45dff12aca509045507f4f9aa27bccb87286ebd674029e24fa31475865552aee": ["santeplusmag.com.", 0], + "45e3868cc74dbf810a26a3a652b8f89e4d5b5d1e7fce412bcc979cfb390109e8": ["mrdeepfakes.com.", 0], + "45e6ce310854fa9643ce06ec4ce216cf099faad94ca2fec8f87222543998bf7a": ["mediaget.com.", 0], + "45e75950cf7893d91f67eb8b923ed9b76719ba6924069a4cc91df8e892d8c53f": ["qrz.com.", 0], + "45e81df52c44351e23fa19940bdf21c6e606431fb4762f9d69595823077a202a": ["balady.gov.sa.", 0], + "45ea817fceaafff23b6efac04bf14b2e12280247123f2aeb9206bbba9c66ef3d": ["action-media.ru.", 0], + "45eb2fefeea236162c3fa4310b80356c088ec098756cc7bda2ddeda57a53dcf8": ["rtings.com.", 0], + "45f28feca950fe454cebda5c9363278c94c2f6a8aa50f6de1e0b9c4d62a8d8c3": ["kumparan.com.", 0], + "45f62f911dffa1948f70a383dd09641c4a99b5780ec5dcb9d341f571299c15ec": ["buscape.com.br.", 0], + "45f638215ff75c6e92e08dac7f2b425b89b30958d00aad66fc752752207e53ec": ["cityu.edu.hk.", 0], + "45fb6a5b2f07eaa468b8fb9f010d6bba6cb3da33025e89fbb136b675c7131081": ["eventim.com.br.", 0], + "45fe834035e0795a093ac1b4d22cc2a109657c37796b324f876d4151fff70769": ["warwick.ac.uk.", 0], + "46007dd794601ced1d5e88cfe9c3f74c6a14ca7e64b683855c007b0d8cbcbbae": ["xn--gk3at1e.", 1], + "460d520dd7dc8a612dd22958b182e97bfe733aa554096a41a4fb3c86ec4ee74f": ["youx.xxx.", 0], + "460f6e95d23968790e9fe271b7d58777d62c2d86e07c4ae25482b728ccfa1233": ["babelio.com.", 0], + "4618304f1c00f7896f8d533b05ca34696a12b45c79ec8bef256e6267d40d330a": ["80s.tw.", 0], + "461f53f2d6a13493f978f851617b5e84c4be525ac2b04a7d51045e06b6d17815": ["grownome.com.", 0], + "4621a81a77b04116ecb9d05cd176c8a921a1a68931adcc8a120e14f5567215ae": ["xn--g2xx48c.", 1], + "4624a79cbcf8f8c84c0b8b4876087d42200e15c8b0b81d6dd491e0ccfaa18d1b": ["ucsf.edu.", 0], + "462692435194423788193eb719b097b304ce6eee7b489e8dcade8ad559d80536": ["anxia.com.", 0], + "462948e8cf1f0477b1f52f5dbe7d8b9750682db4f4fad410c02a104998562efe": ["luminpdf.com.", 0], + "462b22ac98404a0490fead1c85c3804239af8d62af20910b09f50fb06713be7a": ["sucursalelectronica.com.", 0], + "463bc964d05bd70aa42999b0e8c09d8137ed4fa0466f9a8323926969c1833349": ["rts.ch.", 0], + "4649ed8da9352bfc2222425813fd2e384b27513502ac488c0ce1e70fe7004e1c": ["smartprix.com.", 0], + "464bc5d9305c7dcf9a54df3aed9ca5fc649fe531a838fd1bccbaf0563f8e7898": ["bankofamerica.com.", 0], + "4651188ff37b9cb55e95f3facb3d32f016fe90c97074c12957b71503c9667aad": ["asbe-bokhar.com.", 0], + "465454a3afd372451c6297f3940b7b0ee84b3499736f3d12f927bfac6c676542": ["unotv.com.", 0], + "465cfc069f69cca52ea1b8448adc3c9c2ab4473f9cfc19a26fb72650ad255436": ["starfall.com.", 0], + "466db27a374c41844226e86b815ec98ec159d34b9429a3da0378ba7dcd5ad766": ["okwave.jp.", 0], + "466f62cfb6da92504901abf23ba84f0e93685500a29de7497a9a540b61d42ff1": ["flooranddecor.com.", 0], + "4671475fe2c5b4756524f8656be6138ae76a184782270c90b9203ca5e3de44ec": ["cv.", 1], + "467ac822ac84ab533746f9768ae5030fab1fdef343aa9f82c052865fdeb18b9c": ["clickworker.com.", 0], + "4687f369b65d6c8f4bcba14a80a155759324098a75c7f67015b870d22ea80b99": ["pixilart.com.", 0], + "4689a1d912abdcbb7e63254e8afbc7b83d919717bcf3f64639797592da0f2054": ["netcombo.com.br.", 0], + "4689d94448a8ffbd96b0c47322224fccdb665491d434679f1b36521ba10ce00d": ["maxifoot.fr.", 0], + "46ab9d8fcf9b387d3b592d804b8c28a08055f819ac7bf7abacd73081dc21f932": ["answerthepublic.com.", 0], + "46ac0288aaffdffdacb93d469e6af9eeaccaa004c904f212c5599af59450900c": ["avdanyuwiki.com.", 0], + "46ac9a65d28f523378ad8c5d867c31af27e29a18e1ee6c3c3fe79c33bf87afcf": ["blueshieldca.com.", 0], + "46af6a172d081593618bcd8c7ad0bf03b4ae147433a95031cda76ab18545fd43": ["adultempire.com.", 0], + "46b553e0526a043d2c6ae4ee72026c61722d43f44b69185d377b2d1685c9c1f7": ["rule34hentai.net.", 0], + "46b5b9961916be22226da792a301f0854195a8521be91ed2634c7d150b4dd042": ["homeplus.co.kr.", 0], + "46b8c7bf75df320d70aaa65480aa46b0addc6f7ea6dd33aa6df8409ff0769ac4": ["xxxcollections.net.", 0], + "46bf40368cc1e104eac61b97b91f22e877cfa388da4c68e97a6b650703ff4d92": ["mdr.de.", 0], + "46c5db44d5f7bd83e20a557ba094fceec46c29616ff9707f97719538f2004ef6": ["sarkariresult.com.", 0], + "46ccd3ef1f4a8277ff0429e8d20eaea01d16d7c26aec2741343a116828ec68fb": ["targetconnect.net.", 0], + "46dc68ff31ec6f7c3767d62633df1b66ea82a147b17fa28e6393339ddc83aa2c": ["calstate.edu.", 0], + "46df10cf1088383f3f72b06e3fe866306d70e96e1ff800db6fe14f0d0fa909e5": ["pixhost.to.", 0], + "46e0336a1b66d5b013ec520b62be2aacef467244e3eace703be2c37728c2985a": ["videa.hu.", 0], + "46e4d034cb9f65b8dd9ed2685a9ef060ea4e3ca6981ae93dae2638d4ee47b2e9": ["ducati.com.", 0], + "46e5fba68eb074d94d381c93d1676a23123ea458438bd7c8e0841f024c709bc0": ["getyarn.io.", 0], + "46e60cfa6bdf833c5d438105e4008b4a8f6f293ab2a4fb8c23b26d2b59f1df22": ["dateas.com.", 0], + "46efae9139a1b2283eae298822f016e52f9c4f77f05c649712ea6ded5ef0d29f": ["kim.", 1], + "46fa17f1c5e9b0255b24952b2279a103e48e1b277983d96a3a1a3d5a33857176": ["icu.", 1], + "46fc9bc08eb0259122b80ffaec22cf9045d05e221e6d63399cd16d7349f82bba": ["euskadi.eus.", 0], + "4705f720b499b9c23dc4a236309a1fc3c26786c8f6fad07a7efc947668ba80ec": ["xn--mgbbh1a71e.", 1], + "4707196b22054788dd1f05a16efb1ff54ed2ddbcd338d4bfc650e72e1829f694": ["kp.", 1], + "4716c1ebef1157ebf3670da579d6bcd021ca2daeae0db2903c127dd355f289dc": ["smartadserver.com.", 0], + "471890486293fb8633db674fc1f4eee36c940847722349955bf91b92645badfd": ["agip.gob.ar.", 0], + "471ca289a707902cd4c388fe55bb7cdd3359d8a8ac586cc382662aebc0f9ad9a": ["news.", 1], + "471fc4e16c8d6890f62a005740295479fbdc39dd7708a07868a54e985fa58799": ["oikotie.fi.", 0], + "4721759b0dfc6ea32f1654ac6f1e1c364f5bed7993faec4a33162d6ea2f1ece6": ["rexroth.", 1], + "472303dd7e68987f162ea7f854b0fd089271d58bd0119bb2d705054d9351ca3e": ["medscape.com.", 0], + "4725646419c5c45579d7503d2ad1581e064ae4142e43daff2f6eb0183607fa84": ["opticsplanet.com.", 0], + "472783964d09f5bb863fc67c18595c4a3a7a0409b42d82e2cd7331de996c359e": ["buyiju.com.", 0], + "472819dc755973461069e2e21776bd8ff193ca8c720ea5d25b64856bcf99602e": ["immonet.de.", 0], + "4738e91e50e3a6e8dfcb14bea810fa52c6c08853c71057267c7a78ef69ee0647": ["daftsex.net.", 0], + "473b678c23d9c147d5b2c57d96635dbfb0f3fdc026fac9a3b2517f2bde20d03e": ["gutefrage.net.", 0], + "4745882de9850b8379cca209706e1f545082437d2ea45cc4ad2befcc32e4c14a": ["avgle.com.", 0], + "4746455a1b319a8d8c1e471bd3b6121441d2d7c8b80bb0d3bd561e97a0fed2be": ["discover.", 1], + "474c82fa813c25ef9d760bfaecd43b33cec13e941608371ead4a32d9befc58cd": ["guozhivip.com.", 0], + "474f9a721b43523cf8053ce7b23c19c54b41be8f2730317ba08a581949108a32": ["streamelements.com.", 0], + "47519d671db1bd6ea813ad0139689ba122801b9ee8f5f02ea8d409575f312ea7": ["sandai.net.", 0], + "47533eb0a6f9c7efc2374960cf8a299f64828aca6e592359ea54146fcdf198ab": ["tubesafari.com.", 0], + "4755968ce819deccbc794426fffaa963605be21c0e9b147b30b4ff5d184d35c3": ["cgd.pt.", 0], + "4757ac2407053ab1d6f4a0875c1d34f092807aaa3dc5e41137e725d1c8b5e236": ["statarea.com.", 0], + "475bc5503f1b69939ef5c2adf03cd5ec4c3fe73021952c175019ab4ac9f245a9": ["lplfinancial.", 1], + "476f6ef9eba24e0253500fceda2bc31bcf5d5aee8962e2788ff6396bf8d7a493": ["keepa.com.", 0], + "477751da3d526381f69b57fed0f9eeec152395421f4a748ce4851b8c119f6e5d": ["ivfree.me.", 0], + "477e90b927ea43db838c77b7b3fd1e471de87710deae4757e53f46189d510c7f": ["centauro.com.br.", 0], + "478ac7f7db088393e893321cc89f339de72e91026a9a8a5d7370e2572b163323": ["broadcom.com.", 0], + "479661f3c3812d1a9bc776818307376f29c768c1675f663b047bbcae2361ccd0": ["top10.com.", 0], + "4798005146a6b32db1731017ebb4bae5fdfeb743075b3e5ee5881ceb0f7a7d8b": ["mastersportal.com.", 0], + "479c7d41ad7507c93d9db2b908a1b38f890ba7fe8da5cad743b44bfd154c4da8": ["marketing.", 1], + "47a0e94ecd95cfb697a3a99a453b1a450c92c9df1d633ac487e126b546e2e2ba": ["ccb.com.", 0], + "47aedc2e527083c0ff1b6676c21cc84780c6df8acfc8dd9406e2e8f4efd64abe": ["vgtimes.ru.", 0], + "47b3ee6ec7826926f723cc4e8c907c9e71baeb879e454842108344b5480245fc": ["onedio.com.", 0], + "47b42b332656db90e25b2a3da80faf7139beedf454eb8dc17025655c43b2b876": ["potterybarn.com.", 0], + "47b8854ec6e7fa1b7365b389d34f5127518dcd93835e331fba5e4594d431e169": ["icicilombard.com.", 0], + "47bf03e73a7325a03fc36607f1aac31fa2b3bc6085c61661c6ff81db064fa5de": ["ustc.edu.cn.", 0], + "47bfd2d8d3af5ee55a2f9bd398ac2cabb8f996298c19a306cd27ded7a68c2af3": ["tvu.ac.ir.", 0], + "47c71795b02437c892fd5aaf014c5421e8c3ea4c07eb26ece1713acefb98b855": ["udemy.com.", 0], + "47c7ed3432395dd24af234627d1e071cb429000c0f35f65213cac4b451f26577": ["wkinfo.com.cn.", 0], + "47cdcd6204a805f5bcfeac75ab01896c08cfaeca3505e3420d49f40a544ef4a9": ["youzu.com.", 0], + "47d127723ea99127d3c5f494ec3a30e349f4df5bb8835a9670a6f08a382cecba": ["esri.com.", 0], + "47d1922b6fd2c82e4c020dcf3c77d3d75d5030a2b89e4dfcacaf07374e51896e": ["makro.co.za.", 0], + "47d1d403bf6b83dc2297333092f4ecad6450fc8ce5ef2c2610841e5f92753784": ["builderall.com.", 0], + "47df4e926ba5006ad85e7af7ee1346fd945e237b1fee4ebcb1a3de83023eb3c4": ["pokemonshowdown.com.", 0], + "47df6fc87f0e05e3424515e10496648070a6c1106d48acbb356c27d081d65f28": ["coindera.com.", 0], + "47dff5ca16fb5b2622cb390e4f7cfde15217a64b5003fbc3d0063098ee86d3dc": ["personare.com.br.", 0], + "47e0b1e2bbd86b997ad71ce5acaf39c22829ea5abbf4d46f216db614c6440632": ["xn--s9brj9c.", 1], + "47e24eb273b27d43454bd9d3c7a66929fd9cfceca7dc8aeb07ef0d6a184a5dd8": ["oecd-ilibrary.org.", 0], + "47f09370977c8c6b934e01edd6183b9c7b3fdbc23d22607dbc3992f08800d704": ["onlinewebfonts.com.", 0], + "47f0ba7ba1f363c8d5c628d6088fc92903e97b1633a6bbc63bde5a26e8d38130": ["beinsports.com.tr.", 0], + "47f49431c9f08f1bb6da364014c4d96c5fd5cd69211416257137177a670bf567": ["gosuslugi.ru.", 0], + "47f5e5df34fd1c9cff411f79d51ff7e85311b9fb3a49730b334e1e04fa4fb62d": ["neoseeker.com.", 0], + "47fa9917f82544fc91c90196656e150e24eb8a1702449f244c6456489e69246a": ["clarivate.com.", 0], + "47fe3f3c39eac525454801a121e305bb0f6bc0ded41710a2059ddcd05d9e507e": ["almasryalyoum.com.", 0], + "480291d05544a816d38f8c4c0ed9466cc79d6632ac85390a26343be0f5ee4e19": ["syncfusion.com.", 0], + "48052ec410cc873d79e23d97b56cb78028542cc8c0793c259374c8666d1894aa": ["radiopotok.ru.", 0], + "480a502c963d688ab540ccdab9a2c03f895d419c8dafaa240a1c52f2de240aa5": ["dealer.", 1], + "480c9789d5634f6bd1b0c2e2008e67d6d638b5e49211b8d18ff703120c8c3f93": ["tweakers.net.", 0], + "4813f0d3c7e4806a7d4fa9727398492dd16dbac1a4d354117d6017c460a0af5d": ["thecinemaholic.com.", 0], + "4815649569a70969cd82dd90ae6e0cd53c370bf6e9a61802c8d24f2ebf4cd367": ["muffingroup.com.", 0], + "4817e47c642ac387c203e4245f324db8a339fbd5b485966eae0acfb75d9ef7bc": ["playstationtrophies.org.", 0], + "4822e2fc5ff9491bee4fd74e07524163064775418fbc9b60979c55c0ae5afcdf": ["brusheezy.com.", 0], + "482c437e1ff9eadd642da6665a54587b031638e2011cd3e32af3e04e366ec7c4": ["wed.", 1], + "4833294207baf44978d45592be4a2698451896d426327dd35e4e6f8ea1df8b4c": ["hitachi.", 1], + "483669cea821f8aec0ab48430fdd6f107b148b0ea1ef649a961f9a4558bcd18c": ["democrat.", 1], + "483c271605c8155059e0a1b41c222f62a83c603239d8faddaddd28dbbf46a893": ["rosszlanyok.hu.", 0], + "4847dabcc70098bd44b93dc3602edc8ddab198fe4269a8f0f93d3aca8cd49e7d": ["avanza.se.", 0], + "484b603bb3854c5233c595ea6a085f85fd69b3a5f227564fed2036d956126441": ["ixxx.com.", 0], + "484b9aa4b1cb929641ae0e7c9c52e937891dd6284742a5c650f55658bcade3d3": ["tournamentsoftware.com.", 0], + "48564a638f2a51096789d728e94761f847f9d3791319dd6812722052fe45ca54": ["accupass.com.", 0], + "48569276afff031254c6277e1b95d464edfb9e68e971b1e140821dd5a1ec4272": ["sebrae.com.br.", 0], + "4856fd702bce612fcee4227f6296515a18622a6a5f90c79f6a5c7a576ea29abf": ["offcn.com.", 0], + "4857046f6352781b36fc2b1a28a0b943fb4bc81f560e63dfc8d96ecf2ac3502c": ["jianshu.com.", 0], + "485db9527a6e4c1dd01aa69fc60e70c7c16f7582127aad9e2c578ed883a90562": ["dufile.com.", 0], + "486001140df989fb28a82b394a57cdb3e49e340ed028ace26aaa74de0adfd0e0": ["bio.", 1], + "487154f404769fc0f17cdd93ad4f7fe2887c61b6176c87032ec5309dd07edd28": ["amz123.com.", 0], + "487d7fb90492f4f98b849cf674f28a327229639fe6b0a0689eda34443c48793e": ["zhulong.com.", 0], + "487ee1d39d4acaa2a6c897b227226d8e0dd76092b2cdd3874739cd224e9457f7": ["tubitv.com.", 0], + "48804a2fd4a0edacd567ce45d0d76842eb38c0ec1f816ac411c7519b69a0570d": ["la7.it.", 0], + "4882c8f69bded67f3f2561ea702a35874bc3b43b6a62a66f20ddc9135b397a2e": ["omega.", 1], + "4882d63ceb18eff2937becd6339620bd12710edb15dc2da6cb7d26770a7c63bb": ["proboards.com.", 0], + "4883f435af7745f6a9334419643b6a934d18f98ff80134c5aae069ba838add19": ["bankalbilad.com.", 0], + "48863200b838b0e0e3f77f0a9a80698e3c38d0979598fcee494bfbda7a18caa8": ["vidio.com.", 0], + "488939bc8737d355c5301d84f0514d6b0a84e4c771e7bf18a29a15387c212cc4": ["airav.wiki.", 0], + "488d9550f4c491d4c9707868b14652b580f8220c9075e4fbfe325eeb7e27b06e": ["avm.de.", 0], + "4896fc1a7fbec78b9a907aab0784606d7023b3928767e37e97c8e043ce1fc7ce": ["grocery.", 1], + "489855eb2616739bb317f887b943e5f8624ea32d48d4034338e78480d4c66973": ["onlinepornhub.net.", 0], + "4899d56abb061eeed81389e8ad6adeb72b899c333d54bf7f559160a4ff5bf857": ["repubblica.it.", 0], + "48a23a0b8c33eb3371c7d17b2c637794ebfe01a47a76a7e1619e45c6de6bc78f": ["thesun.co.uk.", 0], + "48ac6661edf4dd07195e2e2e314419570c701ac620161819562a588f9706465f": ["mariowiki.com.", 0], + "48adafedae286bd233f0c8c0ad6a55ed40142f12e5082f319f4f6df235ed11a9": ["amway.com.", 0], + "48b5b6e287bcf922e567a05d2a231104d991afbbd9131a3058f8dc7585bfdb71": ["dochub.com.", 0], + "48b5f98a3287873c277c82da66efd8695ca3a17f00aaaede702b221467a66b39": ["businessweekly.com.tw.", 0], + "48b6b39834f70a304d7b4eb75f4fed974659e61ee3bea8bdef8e6c4a79312ebf": ["xnxx-cdn.com.", 0], + "48b906ede1e8f80ce203c3e2f214f9e297fbecc562443b569d23ca97a510485f": ["mo.", 1], + "48bd7a4fe8ff1b2806e025cc4128bc410a38c36afd4309db0b784d498e65ea10": ["bbva.", 1], + "48be9f08aa08b4a77021e5f4a6ae3e00b2e82510292b854e851348a9f04f3640": ["jpn.org.", 0], + "48c22ccc1ec58af9d818eed198958359b6792fb5cb5cba18ca6cd759a3729744": ["yaplakal.com.", 0], + "48cb75f3684ea3eea9d652bc026f9f02e8c63119692d485db3355f05217b9801": ["uni-hamburg.de.", 0], + "48d121346b1c8cd439ba377c43f91f6b1eae02b4f0ad068659fae9db1986cf5d": ["isbank.com.tr.", 0], + "48d43f768993e8b83786bdbaed8cdd4c8662220a551c5972c42b6c706f47249c": ["jetblue.com.", 0], + "48e0f09a2637cafbc2866d5ee2543c15939fd760f18a32eb53c960a1ffde8002": ["redlink.com.ar.", 0], + "48ed34114817c71d92b650a6342d3f2c62ee1735c7cf065af918fe8609d7c2e9": ["plaisio.gr.", 0], + "49007d72d3e2144c964fbffa995bd781349dfb1a24a329a8f19bac9748b19ff2": ["tochka.com.", 0], + "490374f97449d1de7bbb1676a46aec972d80a67afcbe7047a2632cc1abb217f1": ["sport-express.ru.", 0], + "490b30cdc3195ed59efc01eb919b8fb9022cff7cece11c42e08b9679fc4bd47a": ["korean.go.kr.", 0], + "491653fa2d1fb4ae60899a0b97091845a72e3890428a4d5ebc96857eb4bf6fa7": ["nowtv.", 1], + "491773215a1f66a6ed130278acaa9d5c1f7991d6b8aa7265786ebe37fad06a4f": ["mtsu.edu.", 0], + "49182fa1ae4da64e09e1d18c9ba79e4b21a5093e98837372fb42c1054408c2bb": ["jprs.", 1], + "491c6b9447a638eed123b2be1318ce8a306d6d311f5ea1e19c0ff007536cd05a": ["kiwibank.co.nz.", 0], + "492669106cf09faa7fe1ad36e95d54fa3838c7682984c53204c32c09133998db": ["hpconnected.com.", 0], + "4926ca2d76773c53789b0e0f24ffd43232da682d2432c7e133d57c68101a9401": ["gosi.gov.sa.", 0], + "49293037bedcda139cf00839dbea9ced7159b3be163082235999a57b268c18ac": ["jupitered.com.", 0], + "49333dcfcf3c5cc77dea8f260f40e6e23c7d7b472f10d25d0bf7fd8078564c7b": ["jcpenney.com.", 0], + "4937d34cc600e3111075c1d505e5426a204281f510e87f213c60b9da3a88fd59": ["helpshift.com.", 0], + "493956093893740cb71338af9a81b9274e6d68c884fe15ea47a0bdf9af0f0194": ["tvs.", 1], + "49495b557b4abbe34312d52ff2f8d61dd2b7b2cbdb71c288813aec34fcdddad0": ["porn7.net.", 0], + "494be267e803dc05cc06ae70262750676613fb25df4861429bdc3dd3b18e01fa": ["tnaflix.com.", 0], + "494cd47fe362a42fdee5607e0bebdd69f4ba27a2a986ca5c8a2bdda908121181": ["filmpertutti.tips.", 0], + "494f4b65ab1f7e5c408e294a9823d2fcb62c5fe3a5940b77d2c1ecdf8199ffd5": ["ejbca.primekey.se.", 0], + "4953596129b62a4f2d0d16d2f3cdaf38e8d460e788c9eb26baf4be78022f3279": ["ifttt.com.", 0], + "49556cdf6c8058cec41d6e50e5177c0d71dd008b1dbd342fb63179405e83def7": ["epost.go.kr.", 0], + "495eddaff6c82ae93d42f0d0ea69c2af9cdee7a98658712f2aba021d429f7f72": ["hqwx.com.", 0], + "4965b7335a0c5d7494f3271a221bd3f3510a285de10cc6c864c1f84c1ed6bbdc": ["akamaized.net.", 0], + "4967091d89222f79ddade4f500c67c3844f7495d432a83972709e33ed9975daf": ["yatu.tv.", 0], + "496c057a2e47cfb322c6b1b3e04eb721f6e2ed505c1ffc1d7641b419ad05f765": ["khabarfarsi.com.", 0], + "49783d7c928d66e593c6f0f286e3985099734263946c91848bc0876928da687b": ["monash.", 1], + "497efa57f64311d2ef3d2bdf32c4831f1d87505650ece42b565f87884b2ae0a0": ["accesstrade.vn.", 0], + "49818b02a0bdaa92eb8e2be283bfbd143c24a8f7e319a2c1242487c9fe29f8f2": ["aryion.com.", 0], + "4988b3f14a2a30bc4ec3443d933f0af76f6f2cf9905a8d52fb0ae0b327775c53": ["kinoafisha.info.", 0], + "498dcd486e31d82c71ea49ad6c225564f77a7b25d058d1282586bed61435cde3": ["dospara.co.jp.", 0], + "49913adb2334cafbed8bafe6957a8bab112b79887443acf65db6cf236ad8ae23": ["wondercv.com.", 0], + "499467243e66498190ae69e07bdabec7820cc6aac26857072ada22eea71f030f": ["ddooo.com.", 0], + "499781ae08e24417ccfe088056d47181840f1e4a35f34b2516a165530a2e2826": ["dollargeneral.com.", 0], + "499dc634684be993acd5d101697b8fed6211dbf0837b1062007ab1efc4094abf": ["simplilearn.com.", 0], + "499dd52cfcee641c0a5aa7cf15bfb0ad0fdd20721b9917157e56761b1e25ab71": ["cn.", 1], + "499e69c5726bcae15d796e3560e0df42b3e4dfe18248f739d53fb3aa6dcd7d43": ["piraeusbank.gr.", 0], + "49a3524c00eb37f56a65babe5d15a44d9091f17d08dcd53a3b79f46c401d48d7": ["allevents.in.", 0], + "49a4a4b6d53562368543a4549b1aa01ecae0a2eb061a835f52f9a19d8ece1448": ["ollo.", 1], + "49ae7286fa06e0e1d477869f14163377e5bdf92e1e84a45ec79cc6588015d35f": ["hncb.com.tw.", 0], + "49cf03290bf220f3d3e4909c225791008e4c757d6ac96ed5d29c97560fadd202": ["mtu.edu.", 0], + "49d020e7cd7a8c2b777e70222ff6d978f8608fbe6e51de069def08f64cccc9e8": ["antpedia.com.", 0], + "49d4aaec143f690da729469459f1f9e10aedce6562f072b14b0d9a727efd7c83": ["rsc.org.", 0], + "49d72d29853883c2a23f34e7a62a8b0e818eeb55bbf42960951c1fcf66c171a6": ["ppe.pl.", 0], + "49d9cea0e108fd7c1de6adeff09d508f004fe096c88d523cc13c2df91760d234": ["deutschepost.de.", 0], + "49e59f0aeb80de5a9c86276639c117055c2fed2769560b732f156ba26e27c7cd": ["itellyou.cn.", 0], + "49e903085b517af534d18eb0871bbf8814d7bb318b23f838151dec163904e681": ["3xplanet.com.", 0], + "49e94b34f90a33fb1e6e3c434aaa59a878272ef42e9782e3d4785313ca213d8d": ["telegraaf.nl.", 0], + "49e9a27bd3d32f0ffba0e1ab3731bd15758d6389b4321ac33d3b1d004bdc62c9": ["h3c.com.", 0], + "49eda93083f0bcfe119f3864cfbc3b125f4ce9598c8c51b0a3df8f71ed0e7b35": ["2-d.jp.", 0], + "49ef1de5a2cfc9704bfef61d1d81203aa351cc3e0d41e323e4fb3053271370da": ["coinmill.com.", 0], + "49f80621c60fe92208e68a9936021516fc3987b31f91299a544679fb2df7b1b6": ["efukt.com.", 0], + "4a0387db4dad1ce7d21fd1042fa58daa6d0989577b0a1301e0ce82779bab6e1c": ["pornhubpremium.com.", 0], + "4a07d152f0f273bc5056131aa03ed1ed444a7958df8f3b98b92e6e2f70163dd6": ["ine.mx.", 0], + "4a088df649e66ebcdb577eccdb76901b196e748bddfe64e1531817d635a8799d": ["ticktick.com.", 0], + "4a0ef9d15aa1deab74ee06a9b998f6207cedc4569c4e5bf7f74b2efd11c7341a": ["sharewareonsale.com.", 0], + "4a17cd83b9a1bbf4998b3f5315f032afc826592a04b57a964b1bf5bc8d83568a": ["talktalk.co.uk.", 0], + "4a1da550bc1cc73de6a7671c662a9b7167fa14b1bda474da5d273c657fd8c899": ["symbaloo.com.", 0], + "4a24a76812bae51d9b25b26985e5b19519374b2f3327bc3dbef83595ff674278": ["lodynet.ink.", 0], + "4a2750eae1f01daa23557e8ffe2cfd299f4952b65d7275ee556d1cca872c46b6": ["518.com.tw.", 0], + "4a34647d38d0eff9513f6024a3496f21cfa5c6c5a35cc56061a8a18b2a70673a": ["chaoxing.com.", 0], + "4a3810a2e4f1f153d7532ffeabd468eb6fb244676c42bcf97b6b5dd89661069c": ["gprocurement.go.th.", 0], + "4a438f57be40db9770c6aad0f7969ef1940a961a88da4e407b7c476fc57c7b01": ["bw.", 1], + "4a4e29ca08681ec5e3ad525021084a9fe4062d1d39881f2c7ca7123d083ae899": ["ahu.edu.cn.", 0], + "4a5536f636cb79ccecac4684ef3b5dc1e796a2b70fd50992c34255a2d9b7e2ec": ["bathandbodyworks.com.", 0], + "4a5a97def71c4239aba40298038b9589e5572b88ec37e4ac0080370912b22c8c": ["fpo.xxx.", 0], + "4a60498269e2f2ddbe45f1ec2b204a1899e5535e4da694c69c016763420d8aa0": ["worldofsolitaire.com.", 0], + "4a608d5a9bcb2a646acd9aa5c95d30302a1a21a80bf2ac550449314177295675": ["51fangan.com.", 0], + "4a67292c7403baaf6150ab9a83eb1af56295cfd203ac86502f7891626fd72de8": ["webteb.com.", 0], + "4a8c682816a32cea2406b02f317cc21690c5413b37fc862c7549a0a3ddca9192": ["ruc.edu.cn.", 0], + "4a8e5e93e87b25a106517710070a0296529d953fdcbc9354cd31cc7310210461": ["haberler.com.", 0], + "4a8e8a382feb11a5ba95573b029c36d48cc0405503693273518d96bb26145bac": ["adbtc.top.", 0], + "4a8ebad421aad1b82ac06cbaba73b6d8f4dc8c46c06145976640d1863beb8139": ["allacronyms.com.", 0], + "4a915ba3b005091612556fc5e5170c3b03b5e6446b5e7ea908bf1f976e3d9fd2": ["projectzomboid.com.", 0], + "4a94fffb17254b80ba72e3a13988cfc1962f85f8c737dbf3de6cfe3b38ed2eb3": ["gcu.edu.", 0], + "4a998ad3ba0454a6958de8034ca920456a2d7556ec65ab1d68d760230cd535a3": ["720yun.com.", 0], + "4a9c2f7af95160f51168e161b6b9a3c7edaec57baa1ddf893e1b1643746fbd7f": ["energy.", 1], + "4aa0d0c1b92b15e4635cd2df6cfb042c1f28aecea7db2c35ee7ea9f27b5bccda": ["familysearch.org.", 0], + "4aa10e2270eee09996b50941376122714833e9b504292548c6bd9f9885cc38d5": ["black.", 1], + "4aa8fddc762e9fba81999b76e9654bbcdf8cf020b980e4e972ec20f36b9f1ec5": ["thefreedictionary.com.", 0], + "4ab18aa44e45c3028c3d847644cde8a1520f22f7f28bf7043b171af973c226fb": ["nxlog.org.", 0], + "4ab5966fcfda53bdc6d4dcade591aa50026e9ded05b5fdb4ec441354e900559d": ["aif.ru.", 0], + "4ab78c3184248deaecc7f241d3bd0b1871909c5b1efde51e26cffe95d5837983": ["minecraft-inside.ru.", 0], + "4abd288d50738c56e487781086d4e1c256aba23a3bd4d090a767a8a2c3c1c8dd": ["formstack.com.", 0], + "4ac4647d0f855ebb9b221398fbdabd3ca45ac41ad29a1656b8309cb0a7c90d4c": ["wiktionary.org.", 0], + "4ac5735cade247579bdfe114a83f8a6edee383abdf6fb2ce957e177b96e3b970": ["morganstanleyclientserv.com.", 0], + "4ac6a12a3c33a86367fb01df95d8f482410119e828e0f06e7cfcefb8c8a17c1d": ["itworld.co.kr.", 0], + "4ac91a0915b1f9b6e1dd55f5fac87627b7fcceeaf3b37bd25bc2551e54c539a7": ["roku.com.", 0], + "4ad1d161fb7733e870a7f902007bd4342f83d90f15a62524ed330ecba3c5d150": ["fire.", 1], + "4ad21b1f3a9e715e02547f8e0d8bac5822a886f1dbba71a3518f60572b16f00f": ["2345.com.", 0], + "4ad4969d045b6e65a82c670c4e83ebace4f763b90817ccfecea973ceba815aa3": ["enza.fun.", 0], + "4ad812dabbd3e5fe5ce41c06167501b42a94953e3b9b13efb75fa985522185eb": ["casa.", 1], + "4add82e39441fa63770dbee2415fbae122fddcc3d12f3f59a546ce0ca59b36b8": ["edomexico.gob.mx.", 0], + "4ade000dafc7dbc65bcb4677c5f17993475d9c3446de685a7da96a220d155a5b": ["citationmachine.net.", 0], + "4ae6d3632c9acaccaaf0bfbfee620a9dcc8f0b8d0780d57e026b1fad2cf46cf3": ["oscaro.com.", 0], + "4af4cb73b286ef792470df7889d50d606ad8517fda96b82c799616c9618a769f": ["scihub.net.cn.", 0], + "4af71785aa3b039dcaa1751b722772b9d177fb21e9e5751addf9cfac5804c1fb": ["remax.com.", 0], + "4afa581f9e8e604b5c4be180b6c34d67bb52c753765748911f8af0f150bdeb17": ["20min.ch.", 0], + "4afad0b95e313e2a896003a18524569ec483a39f5015247783170c494d669137": ["terra.com.br.", 0], + "4afeccad61582880a6ac7d6ebd2159204b01c8a29e36a12586b2b08c3a20268d": ["colegialasdeverdad.com.", 0], + "4b023b92e4af7aa256c8814cd8213eebbb6dbd7cb36637c4561bb7eaa12058ac": ["anime-kage.eu.", 0], + "4b08d806ef4ee44a125317de1108745050d766f7ec2a77a13b056485342a3d6f": ["bizcommunity.com.", 0], + "4b08de29af3ca122f225f4f6f91e928dd48fccf67213029a468ae923cb283b95": ["tz.", 1], + "4b0fd7fa8fb737ae655112b8503bd929fad9d7f3e0bb621ed99ec64192e680d6": ["cardmarket.com.", 0], + "4b18d4a8707f8224230ab9d042dac1b9cbe8663e833d4c54525ab23317082e85": ["iau.ir.", 0], + "4b2083f9cb7aec4731b887a5c1a5abccefce94def6c6b78ab05deccadaee4f6e": ["e-health.gov.az.", 0], + "4b23324da2e681118e60a16e2ea6aaaafef1a7588ad0fee5bde429da8574500c": ["anyporn.com.", 0], + "4b2733a7653b0b874211d7280aa0b983927922bd580f3fb0e442a58e6d041f80": ["naukri.com.", 0], + "4b2e9841f77740734845aedea0fff6145cd31887797bf03cb9c14c8ec11fb3c1": ["kuaiji.com.", 0], + "4b319666c017a8e66c7bcd549c07badac52180ddcacd270b387b307b04636a79": ["pchome.com.tw.", 0], + "4b3cdfda85c576e43c848d43fdf8e901d8d02553fec8ee56289d10b8dc47d997": ["eth.ens.domains.", 2], + "4b3edabe590b4a24b8285ddeec690fe42796fc0dd7cd393f8876109032868d20": ["postimg.cc.", 0], + "4b421141977bb02b223fe893ea05c3a6f18bd8910a26b42119600d3bef634a31": ["iitb.ac.in.", 0], + "4b437003361bef9d5ba7e5999273466d02f9b280b0de1750e08942ea9b9f856f": ["kz.", 1], + "4b4f53f8d29ee5305a48d360e03cf622ecbc0c300588ed9e63b50499561f875d": ["delta.", 1], + "4b5a50844f715fdde1f0a369111624d46e2e2516f50e5f44de79478872a8c51b": ["ethereum.org.", 0], + "4b5a834ea20d2b739352f70068b747c9416bb173b583f412396be82ca635a0b5": ["depositphotos.com.", 0], + "4b5ad32b6bb64ef01906e9fd7bfc1019b7ca1931906ff070415fddc52ccb9c18": ["netonnet.se.", 0], + "4b63acdad78ef57f0df42e3f28f5082e153f65f73df45b0c511b7e7f504d5b86": ["taylorswift.com.", 0], + "4b65f0aa2e065ed4ca32351ee313288b66423fbb121379f38915066b754bf7e0": ["freevideo.cz.", 0], + "4b6b9875fd49a1ca419fcaf900bd03525178f6503b67171e8b0c1d88034baa16": ["bigc.edu.cn.", 0], + "4b6e366352f33ab5b278145ab997b4e49911bcd6776f7148808afe95e7c451b8": ["turboimagehost.com.", 0], + "4b73819e146bbbbe0f1afe2341c70166dba35a2f6d789297ea963bed91780f48": ["1905.com.", 0], + "4b73c771d69f29efbc7689b57d23d3cad8349b4857b76c7c4e71a2b142e4b917": ["amaporn.com.", 0], + "4b79bdf58e8b7586980c63c616f9baddcf2c2276323399589b988acb235fe819": ["rambler.ru.", 0], + "4b7a1d21c7ddd28c2265cfd7495d2a397eb061b5548e22736d8fca4770607638": ["shopstyle.com.", 0], + "4b7b18a4b89019c00267da200444b2d37971ed5cb6515c47412dc6fa009ea2a5": ["daum.net.", 0], + "4b7bd4786e617be8e2a007fed5b5a56da26c7f4f15e058f4e838e37703ede4b9": ["logrocket.com.", 0], + "4b8085288060fa5aaade061f002d865e7b6fd323852b0cb9cac5c1cd6efc636e": ["girlsaskguys.com.", 0], + "4b84df5427478889ff43f402dc2cb713530e7ec1528351a6224f95e22d2ec6b6": ["mlstatic.com.", 0], + "4b87625362eca55580979f8cc40ddf8930e54a6635a28c865933aa7e74e8dd33": ["uniprot.org.", 0], + "4b925e4e8e2ca2e7cf72d6ae8c340aa3ee6b976eb2d1d3f1f1b252f20e2ce304": ["soccer-king.jp.", 0], + "4b94cf81e45b4418c70db01ede4c397268d684c21e5ac15e3576a0abb5a9a092": ["thisoldhouse.com.", 0], + "4b97acc1b5bcc7f7cee1cd3053b5fe39dc0711ce4d27e3c51a3890cffbabe3f2": ["ponisha.ir.", 0], + "4b9c7140745948a8cdf0bf45b0bd721625ace3afb810f68ba0d4f1f9638522bc": ["gq.", 1], + "4b9c85ce596737fd2ee56aa860a67316d3109c148b9326386f53fa5a2e317a71": ["freenom.com.", 0], + "4b9e6ad2b46205bd73ab4ef180297872815d114f54bfb6db399129355a226bfb": ["jobdiva.com.", 0], + "4bac3ef7c7f585fe3c9b15e3e7eb5e9a371e93def8c417a1a711ac6308d78a29": ["podbean.com.", 0], + "4bae02cdc3692653ff116dd1591fc55adb854113b7358852951a94363fba79c2": ["asurso.ru.", 0], + "4bbac297f8dbd0fcf835054897187624021ab5602e0c69539a6f1296881eaf58": ["fontawesome.com.", 0], + "4bbb506e049117005b6beb06fce260d8aebaff2d4a0de2d4a211343bb3ad8a8d": ["cakephp.org.", 0], + "4bce3fc988b6bfc9f962df73b9e1071017f2b608f70cceae4a6660e99eb01800": ["hinative.com.", 0], + "4bd55900a6ac213d183463b70664be60ba344d73befad841832afc8a7e0924b1": ["astrosage.com.", 0], + "4be2a9be35a5da48aee26aed9be9f148973e52893d1fc914a806c895936031f6": ["yupoo.com.", 0], + "4beb0cb1aae596144fd2311118142827b1ca626dbce5f88cb21082206eef222e": ["movavi.com.", 0], + "4c016079471f6f468f3473652984bfc86b9adbd08ee3e0f5a08567fc1b9488cc": ["aarp.", 1], + "4c03a771455e84e7077c61cf271c7e8c1a397abfb11e2b2ee9eb6abd80c5734f": ["yorku.ca.", 0], + "4c07f04ee7a85f798eda4466a6c0a8ae1827e899d32f968d515fce22b4e2c552": ["luxe.", 1], + "4c0e757b0288091b93793d121a445929915fada1d2690b5567d7f7969609ebb8": ["holdings.", 1], + "4c1f47e9b818c6602de0006971f27fd54c41c486a4b5086556682f8b89128bbb": ["basspro.com.", 0], + "4c22c5a79f89cd25745976c7887cc8e02e3377718526c686d73b225fe284bfb2": ["foreca.fi.", 0], + "4c2801a9f36fa7600d4548fc0b12c2a9c2086e8022e1334ae793ae7e1f0cc44d": ["adobelogin.com.", 0], + "4c409a63e6627b36138ab4e0921c1a78aa73fb33fee801902898b9f70da586ea": ["uscourts.gov.", 0], + "4c4948b4474075a194fb937af17b474aeb6d88810a1afb496af72d18d48d659a": ["tsargrad.tv.", 0], + "4c58c2297c11f6a22f808056c6e00ec6ea53f4897231fb7832c9b972b4130473": ["iranestekhdam.ir.", 0], + "4c59b054dc82524d0dcd1c69d84012b938e1bf020a13f4a31ae97f3c1bd51cf9": ["seriesonline.gg.", 0], + "4c5a8c64d760a0883d511c6ecfdf84d1208c46e669812c77cddacebd9cfb43f2": ["beebom.com.", 0], + "4c5aa493b0ec156aae77a0aa03c91854b7583563c30947cd0ac5190b67b46950": ["cardinalcommerce.com.", 0], + "4c653c97e9be6fb2ee5e5ca6850e7c8ad9c0d5e04d03ca5a9b3c6757f96a2552": ["minitool.com.", 0], + "4c704952075be246522b74cd8efcd030c6a6ded86da0277ef96ac0ff6f7426e1": ["filmarks.com.", 0], + "4c71d998b009f689f82903de10cc301d4f22559a44e8beeb394bee2db01dc017": ["gaytorrent.ru.", 0], + "4c72295d6d456605e07cd9bc8b322b57427e9969a682e434dd5950db222374aa": ["tlife.gr.", 0], + "4c78bdde9d5c1bdddb3c666411fd4b7e459cc876b6996da26497cdbad1c61425": ["jupyter.org.", 0], + "4c82a19004653260e83f718ae25bc2dc4532fa512c32b3aa3f733f7ee924e4ab": ["thisvid.com.", 0], + "4c8330bee332ecba9dbe025a57df829bc1e2a90aa2a5f748ca80705f24e7c0c3": ["iconfont.cn.", 0], + "4c8680872a385ef8498255be84590f9ab6af630a0d87470394b3c0e0be33fa1d": ["90min.com.", 0], + "4c8d22b3d689b8ad9240aa2d63f04e31fc11135ffda54554e68ce278d639d880": ["indianexpress.com.", 0], + "4c8dca7459c26ffff9607b6a6983611618f463cab960be2f7997d3dbd4495204": ["bytedance.net.", 0], + "4c8f61affee2acca6664b9cd11eefd740df4b3f94cf2b9936e5f374cfad04588": ["superprof.es.", 0], + "4c9a283fb85368d454530d5d828b990a0b16f0cddd5102aaf420f54b4150eb7f": ["razorpay.com.", 0], + "4c9ad43a50bdff36357061928aacfa08ba7861e349632b4b45bcd0740a88a11c": ["xanimeporn.com.", 0], + "4c9f2a2775d4e43fc68f1b03f372cabbab17958421054589e743ad8c5c5a7956": ["pingidentity.com.", 0], + "4ca6f9085c5b9c3f9237b7b35a631a9eff0b65eb29224d391f71769de810e83f": ["ventures.", 1], + "4cad48598705eceef0969b910b9cef8f6ed147e8cf38665983d5ac84cc66e814": ["imovelweb.com.br.", 0], + "4cb58313d315d6a87d3fa178efb60a381f809a9c850424e0b5eaecf28dfd7859": ["befunky.com.", 0], + "4cb7e31f68d8f7d00e331294802875e84af38c0e09c787685558de4799b09230": ["therealreal.com.", 0], + "4cbb013adac863e684c7d3455fdd1f725bb90afe33d92d73258f610f339d98af": ["homecenter.com.co.", 0], + "4cbc7926de50fe1d1a9f7376aa499cde25b4b667a4489b73547c80b9d29c1c3f": ["busbud.com.", 0], + "4ccfd2ac42b872ec77e632ef5dd4b599df9918431754e31156936ac81d3a3f86": ["qut.edu.au.", 0], + "4cd7386d226475c8f70e141963a45faca0348a0220038cc939fbe864813ddb04": ["kanopy.com.", 0], + "4cdba6331c8627703ef7cee7a307df8978cd30b6b9657ecdabe716c7de8fb1d0": ["passthepopcorn.me.", 0], + "4cec3bb382b805a6cd01ad23f2231127678813c9e35b32e1127848ac8e0c6c4a": ["latercera.com.", 0], + "4cf0dee31bcb09768624eda81ef44eeecfe226d029981eb72d05e7d6437a1ba5": ["buzz.", 1], + "4cf6b9f3808f3911b4621d60eec1398495d513f4d2db777f7cbadec8bcf0b579": ["nhi.gov.tw.", 0], + "4cf89431aa98e4d4f1083fc84f8c3c37960df081bcf822ea0763fccb127854d6": ["tribuna.com.", 0], + "4cf8b6a6852243bced754726e19fb521c530053cbaf1a790a2d102122a515d01": ["voanews.com.", 0], + "4d13711d5d390b175ae66f2feaaecc38e5799b6030c6f055cdbc31c3e203bff8": ["xn--xkc2al3hye2a.", 1], + "4d170fb42ffdbf6106af28e37c5ee2bfabdf96013a345add7850692831d1bd5a": ["consulting.", 1], + "4d1fd508d48dc427b03633f829fcc84134ba8269f08a0646bf249d8f28144705": ["xn--80asehdb.", 1], + "4d20c68aed7615371e32525b7f96281dda1d07a122f2ee77b47965b95fa6fb77": ["enaea.edu.cn.", 0], + "4d2cbb18ba91b95ee27c8952539c93301f4a9ac0c775c80bf87b76cd3fda16b1": ["bn.", 1], + "4d2daceb860804c9bc79dfd1e2d286007410e8c8292af0070e7a700dbb1ee311": ["st.", 1], + "4d40af7072bbecf3df33394f9193fc78e7a8310b9ddfdb8fcfa1ab1d91b552e5": ["hostlove.com.", 0], + "4d410a252c26f343e54b44fcae1d55dcc8956e29934c07f401591a67dafdb236": ["stream.", 1], + "4d41df9493be9da154995bbc592ba3b813903f8e4badab19900394472d2f5da4": ["lianlianpay.com.", 0], + "4d431604191407223715393beae412eae2db667f4aaeaec79a57108edc8b7767": ["playstation.", 1], + "4d4893a3471acdb00d3a6d4138170007a87bd073f5ffed80b87745ade3990966": ["ricoh.", 1], + "4d4ba77a5265639a5b2f69e0032582dbdb9bcfeece2caa90b4c5d5b7932685ca": ["rustih.ru.", 0], + "4d5466b989241220ba475f1c1d6d111293fdbcca2502b5bc5d2fe2ced705eceb": ["telus.com.", 0], + "4d5867957811f29d990478bb54211b67ef6455804e812da91bdaa3886ec1aad0": ["displate.com.", 0], + "4d5a024e6fd768d5e7aa2647fdf3c23318e3403112753204424f25522bde4209": ["liveinternet.ru.", 0], + "4d5e6c38f4f9acfaf21c226b7f989653ffc858d6d285028d56701f2efdba82c3": ["tonyrobbins.com.", 0], + "4d5eba05d0f90ad9a8bb951c680354401b271fd102e8e401ac47aa1d57ceb9da": ["wikidocs.net.", 0], + "4d6b1bc0a02fa936117deac8db2b07d6ef992a660b9e3f044a354753ac691c3e": ["equipment.", 1], + "4d6d108b9b3318d65882bbeab8b8f4477295c8fbb7f3b73d02ab5cbd833d0f54": ["meb.gov.tr.", 0], + "4d6ec8c49349cc0815ca8cb9867310e56270571ce5586baea1a62d65faf2a040": ["asana.com.", 0], + "4d720adba9a121e372ed9cb0c5da8807dc13b1a424e2778bda86fba112121efb": ["mongodb.com.", 0], + "4d73cca8e27b3fe78f36e7f4f7e32a40119d8dd0ac31a3804ded83740c6a53a4": ["beatport.com.", 0], + "4d808160b48a2c2a049f0648778d2f55c19d0a1d6d9bec59721d0953f0ece76a": ["pcsx2.net.", 0], + "4d82e06028e865820fa29c0b37d0948a46cf4546631a8e86fa7b7c83cc8aecb8": ["accessbankplc.com.", 0], + "4d84139c3e24d6827de259d07a8e2c32e114a70e2bd9f40ac26e61167bbdd441": ["vpngate.net.", 0], + "4d87b2bac379bc4da0c5f7e7b6283e6ff42057109912ec5c0e2c392d78a0b891": ["plos.org.", 0], + "4d896e8704dcf113cfe89cabafc69d8a39ab3900689d665d8d9d449e0dba03f5": ["rarelust.com.", 0], + "4d8a15f92428674eec395f95029e46ef4bb622b8e7e1a5e972b68274ec48672a": ["126.com.", 0], + "4d8ba632fc9d6333c2f1963db05bcf802c4044036c1e07631d1ded4f2df55d80": ["kuwo.cn.", 0], + "4d90214a98e663b45e7ddae27c7aa58e6a01052b3e42e5ac9379b675b96bc1e7": ["mendoza.gov.ar.", 0], + "4da4841a37c8ebd31d83e70261e2572f1e3c88d17db521ecd4a49f2c249172a0": ["flipgrid.com.", 0], + "4da91d47a834cac8637d8b6bacf2ff4f23cbcf0c9113538ad6181b20b099179a": ["franceconnect.gouv.fr.", 0], + "4db3707c62b26dc9ffd074cbc6a6a0faaf2286abea40872e8aa7a1b650b25425": ["beeg.com.", 0], + "4db401aa058864a61666fa62f3152de276ad4ff72743cfa1291cc575e5ff56ed": ["freemalaysiatoday.com.", 0], + "4dbd50e76746b3446a9513c1e872d54941cc3acb8cd2b4befdc8fa21aa58df2d": ["kennesaw.edu.", 0], + "4dbfb5c465ea74d310aa863282c8710b56aef14628534c6693b6ac7ce4c608fa": ["int.", 1], + "4dc8f2a446606c5a987166184875dd6de763e6055eb3d018c7de20e510959210": ["hcbbs.com.", 0], + "4dce7c4ba0ad2f6331d94435d5fe9d4c5cda89b18feca029309652c948b96033": ["hentaiz.in.", 0], + "4de681a9826809f7b60ed67e937459c7305b4ac37208e7319b2492eadf0ca593": ["gaana.com.", 0], + "4def9c3a0f7de437e906f03d13ecc8b850452f5d8d1c98dab7760b83efcce154": ["aiosearch.com.", 0], + "4df701c5fd5a37b80efcc394601ac85942d992e34b45cf54c2f8bb71c7e84fce": ["bale.ai.", 0], + "4dfabae1b56778e7cc0fedc53ed67ce96bd873d7cca0c308332a963eb85e952c": ["tiendamia.com.", 0], + "4dfc40881e11451941efd3a995637fe0cfa704eec9fc3d1f3e911396d671e085": ["miraheze.org.", 0], + "4e11bb5ba922aab983bd0f6224f076245a38e6f636105d6e211e16985fc123a7": ["ctee.com.tw.", 0], + "4e12487f17e20682cf6f336632aa283c4b117c55cc5eacba91ccd6c4af869e6e": ["linuxprobe.com.", 0], + "4e15e607179e7cef39140673594f465ad08a4be98a164b38361eec321a799158": ["baskino.me.", 0], + "4e179a910bcb440c932c11f6cdfaaacc6cfebe692794e32692e639089e7caa85": ["pwc.", 1], + "4e17ec1093b6bb7f7b2d522131d9ec414b7257607a252bc1a9b0eb7d6faf5676": ["kde.org.", 0], + "4e19e99a552cf30b10fd90f34ad2dbee7db68bdce956a7b8b10563582fd8de62": ["lesbian8.com.", 0], + "4e1def5d6d91fe82fd7aa0d0ed8467701e998aa894a3b24312fb223332dd62ed": ["tvtc.gov.sa.", 0], + "4e23b8e0af7f0e97f9a6ccd667bb8eb72c55b358dc113f7bdcb4508f8adebe67": ["save.", 1], + "4e253edc8699cfb78d17e6fecd561b12a517116d0fa7d95fffcf64e8b0c86ba5": ["hsoub.com.", 0], + "4e2ff306970d517c3bf59e09f3f96e07b9f8475b2417d06de8137be56cc4c9e2": ["hackernoon.com.", 0], + "4e3df70c82a3150edbffd46d9cc7250a5e5b86da8ec1cf223b54f8d84f903a78": ["chinamobile.com.", 0], + "4e4832ab9e4c74d3c21ba5f10f90d38a3fc5fec957c40a612c158801c033bc33": ["dating.", 1], + "4e487516169e86dd4c5c0b4f663edc0996ac06df5d15302c83764f7fc5201ea1": ["fpsbchina.cn.", 0], + "4e79786590258027d87d32a43cd1896b470c4347956f5fa0eecacb71965d2f6e": ["docebosaas.com.", 0], + "4e7a8f26b2e2d400419d776757c05e54d5e51bfeb8e3a4c0e6a0699679e1333e": ["peekvids.com.", 0], + "4e88a9d9477e3f6d764605340c46777e3b62ed957c0d3555b227758e4bb3355d": ["edgenuity.com.", 0], + "4e8bdc424ad49a09c744785576522ce7364fe69751edef6d131259837808d012": ["musinsa.com.", 0], + "4e98a7c084220b723343ff42e74787324eecc7b728ac7f174f320ab61e792649": ["btschool.club.", 0], + "4e9c730e77200ac03dc472c187e760d3657e5999c4a9608004855428da54f875": ["urbanladder.com.", 0], + "4e9f653921c127570fc57ffe0411d847f5a39616db09b33c237054824fde6ae2": ["motionarray.com.", 0], + "4ea450af0e01a7a329ff0748a8e6df4c5b772850b869371586f208c99c19092b": ["gfxcamp.com.", 0], + "4eb1b99e9ec53701f127f17ddca76534c403190df8b774eef262a8db23fdf06e": ["topcoder.com.", 0], + "4eb5e56e076fd46b19dcb525faac3411195add17e1281f71869891bc796daf4e": ["denfaminicogamer.jp.", 0], + "4ebb4c5c0871d1a8a801396aa791ab3e6ebb3ce6276d40ecfa1140e6a596cc7f": ["cathaybk.com.tw.", 0], + "4ebc2b1578721be55bd5b88cfaa8f892b70c3625b3c8ccc7397f97f5af8e2d2b": ["slideshare.net.", 0], + "4ec05b272093f350ec59a4cb7348e984da44d029d54531c72d26652750212ac6": ["myporn.club.", 0], + "4ec2d49640cd939cd21e507a1431c4973b801f0d3cdc9e48a92089ec2828ece5": ["yamaha.com.", 0], + "4ec667bec55c7ebd1e920baf91a2143c829744035fc4414941371cfe441505c7": ["loupan.com.", 0], + "4ec6ca1f24a46aa1e471332cb97fed8362d5bdcd7503d8c5f735b4de5ffe3fef": ["pcone.com.tw.", 0], + "4ed06d6042520d2b4f1474de508b70dd9eb820175d20046e453ed9c091202a65": ["byu.edu.", 0], + "4ee288028a6cc9cb08386d840aa34a03eaae546f3273de5d129f859b4b55899e": ["prntscr.com.", 0], + "4ee4d598f94d485a99be9ba9625496db5cbe9a275c944f7f4f872114b6f955fc": ["ssl-images-amazon.com.", 0], + "4ee6e5b5e0a6bac06d1ef5137e24eb5e73502cdc3de5d683c47a15900172ab2d": ["warriorplus.com.", 0], + "4ee9cc091ba3d499f476973f193ae8ddfba3235d883d920f39ed06d8bef3cb1c": ["mitre.org.", 0], + "4eeb2cc854e8bf31f92b0f0eef4201cc2d61d9780e57b0ba8e6a68fefcb68add": ["stj.jus.br.", 0], + "4ef6543ccc40c88a350aff0ae2601d994af28513b92fadddcbbaabb75cb1dd72": ["joinhandshake.com.", 0], + "4efab0ce2bc6cf6062dae8784bcd2a4bedf9a39d40849d14600f61e5f4592593": ["overheid.nl.", 0], + "4f00abc9cad3b916f8d9a72abe8e994a6236c60b668d247d043f34a6fa8dea2d": ["kcl.ac.uk.", 0], + "4f064a79179d74bf24dc70488ce43c01afde7d3ba4a02a7d42f015fb9834c5eb": ["ulaval.ca.", 0], + "4f0986ca61ade2e70cddb739335633fde8a1419ac25f184ed2fbe854effcd4fc": ["dcard.tw.", 0], + "4f0ebd341a4e678300672225abc0be2b73c9c2cbcdea3f354f688f1e1a4fe0f4": ["sega.jp.", 0], + "4f1687f4b79f23a3cecf81539ce5b4f570c4d5583b22436422a373f478bc8419": ["insperity.com.", 0], + "4f1bb48ab045229a61423dfb2c292b3896832814ffd87f59e05f31dd3945722f": ["uct.ac.za.", 0], + "4f1fecd05c286e56dcf446a730f96f5e95e51ccc6e07ba9de763ef14db3b8e12": ["outlookindia.com.", 0], + "4f2564044be359fbc5ed4ed3b31936e1485bae400e218ff37e2f76c9f9f69721": ["egov.kz.", 0], + "4f25d2b8a7d931c805792f30ce63823460f8e7e8969bbbf8e87a5b21d722d34e": ["jreast.co.jp.", 0], + "4f2ec7f04cc6452e487c326020124f80d7debca9d237d8ad979021a2f86c15b6": ["imagebam.com.", 0], + "4f2ff5223d54cc73d9a1996b75c0ade1ba98e88ee93f5ad8bec3f322523ac582": ["wholefoodsmarket.com.", 0], + "4f35b28507e7d9e944718397650054f4ffd520aa012ce9ad70c2e38dd7220974": ["man7.org.", 0], + "4f3d04b70f2fbacef2f84b2dc998286743a78ff8a4bc54bfa686065671d9be9d": ["tgju.org.", 0], + "4f3e0f21828a4527b407138ad0fa3ecfb4cd25ab93b023ad57b39e718f340e27": ["living.", 1], + "4f44766186f64e2e269b21325594b6a20aae2f4c5fa38c101685637015a3dfa9": ["tasteofhome.com.", 0], + "4f52a4a087e2a1db1eb0b54dd4fbf23a14b9b3caa0fc76035b47a43be55a056c": ["fullerton.edu.", 0], + "4f535d7227b63bb607c606116bc6c5934eeaeb276ae7530a9fce6618af200c6d": ["openenglish.com.", 0], + "4f5487f628dca62ab0857f949f8bbe47d6b14db14637455935199460b8c75339": ["georgetown.edu.", 0], + "4f5bc32f0aa2da22548da812d97948b2c69fb11a1a2abb91665b34f42c3299cd": ["traidsoft.net.", 0], + "4f5c12b5e9d09ea5d44e585bb4484b8f43156bc895f2091c21e91d1211cc106d": ["isoftstone.com.", 0], + "4f5daf81d2237c258d5c62e10878558d720a5904e4c97b0cb0760ad90c1dbbb8": ["beforward.jp.", 0], + "4f5de7d570cdd6496b7f4fe5b96fcdb6a21eb4f826b9e2818890abb2cc253610": ["p-world.co.jp.", 0], + "4f6c019d1aeccea165305f5927bab3aefcb31bd3862259bd184b7eb8dc7243c0": ["typemoon.net.", 0], + "4f6da21a98c41886837708f12b98895e9b866a1dfb498f6feb1515bb630552ed": ["sg.", 1], + "4f75702b77b0861c3c1ada758abf0abeeb80d78530a5ea11f67f981881e89ab3": ["anime-planet.com.", 0], + "4f7780a5fd43b395d2645ca65a740bbfd6a844c1da865bbb731555ab95956655": ["mobafire.com.", 0], + "4f79661418c2f761008ce50be1183f7de8b11b0bb9473bacca8219c62ed2474e": ["kotlinlang.org.", 0], + "4f7b34f9680030ead0e102210f93ae5bce4e7cfb80e60c453aaec8bcbc2a44a7": ["ascelibrary.org.", 0], + "4f81367b4eee421460917a67a2d63ac7b24511ef9cb1a1bf62faba5ebdab576a": ["eposcard.co.jp.", 0], + "4f84904579cd587a10d82393cab40f58fec838555aae399a3a01bb77f88602ba": ["trudvsem.ru.", 0], + "4f984c599b48ee6fa3fe74c487ccb9da205aa4d75c69e2193ac06116ed282ea1": ["alphatv.gr.", 0], + "4fa090cad365974197547326c73780699ee4286205cbf734b06d7260bdb1eedc": ["geogebra.org.", 0], + "4fa12681c74b9b70466bca9505dcdfba9d504a4afcb1dbcd6d5f6ec31f644498": ["rabbitmq.com.", 0], + "4fa354b52a4b12d2698c52f7af1040d086a53947d03454dbb25ace036a5086eb": ["heureka.cz.", 0], + "4fa90663eadfed04448158677a0a82d92be449552b871c50d47ada41c78b28f3": ["linuxconfig.org.", 0], + "4fb5880905cdeb2689265440e3447117450f1f11058269b250c5784f77bdb423": ["yuntsg.com.", 0], + "4fb642c9b19fb33b90b155b9e1fde4123451995caed59fc407f7bca89baaaccd": ["newgrounds.com.", 0], + "4fb9349d0828a49e5af07d27419555eab3795d5e3ab8da303cb68d54a2169ac6": ["2chan.net.", 0], + "4fc02bd6273796766b6810bc0e8ff6ec1890e76bff01fb5332c13114b1d198a2": ["skipthegames.com.", 0], + "4fc0593abe96d2086c91774822a771e25114dfda21b83864f392c6037cecf744": ["buzzfeed.com.", 0], + "4fc1a623e22289b5772887f08db204a1a9d12bd3cd4c5eda12f8631c329ce298": ["bollymoviereviewz.com.", 0], + "4fc328548a61fd23ea1c093af0a55041d2129fc04175fcac1b77d090f3bd5406": ["chartink.com.", 0], + "4fc60355c3403243765f30acec7389db2c2a2d20ef129678f873142e130ed7ce": ["myjobhelper.com.", 0], + "4fc6e7a4bd840e41ac285a1faa021a38b1331e5e1ae1c23b4ac20b21d7cee0b8": ["inschool.fi.", 0], + "4fcccdca9287a2698bdfe4979fc11406f6fb6fe47e8ad97518fd1bed3ceff164": ["evite.com.", 0], + "4fd31efe123f9c23b686b03db956d8429b27860fa5d4ef13c4295d7746edb888": ["tj.", 1], + "4fd943de5a991be324f8a31a2860610a13f804a951c2a864dcb4eccbff971ce8": ["trueplookpanya.com.", 0], + "4fe59b6421e6830279e3e103f832b635f0a7639afc9e722da8ff257823f58b25": ["habitaclia.com.", 0], + "4fe8e1c984609cc485b698af8e670727c6efb797d31906e61d4c080b9e4c5996": ["metrosport.gr.", 0], + "4fefb528c69f569f006743e562cb0ce72da4f1a475a6122a1d9e9c470cde3e42": ["deliveroo.co.uk.", 0], + "4ffe43140d2bb5df57f05d95c721f70128a2afed2e9b0b7a2bfb8cbb690eb2bf": ["addoncrop.com.", 0], + "4fff7460db5d69979e7a710d92028e50da8391bb236ab2ca8b1e7d18d0177b2d": ["chotot.com.", 0], + "5000cee56a7471528245a862f2406a8662dccfafba752fee6af9eb0aaf1bb06e": ["min2win.ru.", 0], + "5003f6221613e475f2cc63c3f6c62d151335de4971976811c4d7bd4ab9c4991d": ["xitongzhijia.net.", 0], + "5005fa7f28c89bb5bc2eb8605faa1a40fbdf62be047c09cd3329dab0c7136049": ["paxful.com.", 0], + "50080e1a768ced04dc769032027b37d8ce7c87af4f24836cd24717c089a8590c": ["ucr.edu.", 0], + "500d9f174f9cdddcfe5bd115fdf071027bf1971b15835370426b4e625394c655": ["autokult.pl.", 0], + "501bab97b651402b9935975c11ccf7874fa0cfefcfc01b4f1390c1313478823e": ["psychologytoday.com.", 0], + "50317262ab2ada6ab07a189c7cf0439110b202f7acc178cc629a4a187fd6fa20": ["tauron.pl.", 0], + "503643dff8847a4416b421de2710407c41d3906e5f90efb316b0344bbca1aedf": ["e-rasaneh.ir.", 0], + "50487216ac610f13d9b1217095aee35725bd92495db529534f3c2dedf5a91f82": ["gamerant.com.", 0], + "50489fbd78683d4bc77cbf028b351902c040f4bae078bb4d4ce745e2edf60300": ["aliyuncs.com.", 0], + "5055d2db2fcc03d12370de0df6846088d6648093ce873da10216eaa197adf0b3": ["toroporno.com.", 0], + "505cc330a098467b216f7687834ec4a939d8580405b04a61219904da647849b0": ["metric-conversions.org.", 0], + "505e090a267fd9f657edc68e08f57591344a5bb5dee3457c7313baede7f17e4a": ["sse.com.cn.", 0], + "505e827f2429e6a26dad6274993ced81965a6dd0ded5c9baed5babd9d22c1f71": ["pandownload.net.", 0], + "50632134bc0c27ec1d25fd3735ae1ac4246143b545c047825cb2045643878672": ["udacity.com.", 0], + "5063877158487e3938abf6b16c661b5b2dbcd0980360df12eb811f8614412cc1": ["nikkan-spa.jp.", 0], + "506d6bc31ec497002e94a05d622adbf7f77d7001f6391b1491d627100842b4e4": ["ilbe.com.", 0], + "50716704b81d62ed8ca1dd67d04607c4ebb80467280e3e5897cea16e9221ab03": ["narrativ.com.", 0], + "508c399ff64d184956d5abbf7ad7f363e3f04d49a6a52f3e0c5698976fa705ce": ["ranepa.ru.", 0], + "509565f4a0513507bfe7683b610b537b72713ae23e11a08205e0d699ba4cf7b3": ["mihanwebhost.com.", 0], + "5097456f2bdfeb4b3a3fdf9c4319cae6488dd21e7e34527eb9f912c28aff633e": ["ibb.co.", 0], + "509a4bb28df7214c551097a2f838cab4fd423a1526d7d3488d56c4b25098aed4": ["plesk.com.", 0], + "50af42ad52e521bababfac99897b6ef11b4b9416d75840fd4f23e77dcf4180c1": ["auslogics.com.", 0], + "50bfb03895034e2fbbff0a24f030b907eddbad809b0937394b165f4ef02826cf": ["wow.", 1], + "50bfeaf947b36defb1ee2e39b6d963da5ea7df846c518b601427acdc89594913": ["meituan.com.", 0], + "50c6fee032d6b562705d6a7902fa7afb7c7ba56cf08c2344ad978d2547dc2def": ["lidovky.cz.", 0], + "50c7927f446b8fba6b702a05268d567b704d1c530fcde1fac146b8f9045a344e": ["fakt.pl.", 0], + "50c79bda4f4de479e5d6560efe29108f5dfdf82aec1086d29c774e16dde774db": ["zkey.gplv3.com.", 2], + "50ce90614ae0fb8eac2603d3dcacc5abb86d13c65e188d9a8d5acd660a57421d": ["minutouno.com.", 0], + "50cf4dd6276336bb971c25895d8e4eea04a08b5115448466c886cb6defe50536": ["submittable.com.", 0], + "50d30cc6e747e1f22be21e0bf7968275b87451873a6418adbd784701fff80adb": ["renfe.com.", 0], + "50db1bcf8509f720d2e3e0e01dce5e87b74f78b5e9600cffe3fa1d79bb74a1a0": ["pixieset.com.", 0], + "50e4f3897027931b26e79d9e37fd3046cf1ed80ae09d9e6b4443c6e933a2435a": ["aetna.", 1], + "50f21bd786f5bbd305a8634acc907143416b418b0b1d8ea245bb8b3c18b25dec": ["data18.com.", 0], + "50fac29ed8a64181c584a2cd26375030c385a82d91809c82c7d21e4edd95d553": ["comic-walker.com.", 0], + "50fdf29e32ced240157ed3a4e66381a6ba0a2cb63b9747343a6b7c7808c220cd": ["whatismyipaddress.com.", 0], + "50ffb9b4392e43b5705ef6fcf7dae3745168b7db01d94ca53d059cf60702eb06": ["78dm.net.", 0], + "510b2e189a1bff297690fbab01cc8dca8b8cf0e52066e56f6992bca96199cfd4": ["anilibria.tv.", 0], + "510f2876ae1ca18a87859bef5ba7f7aa7d7d2b1ca81126682e5f5d13b6bd8254": ["pearsonpte.com.", 0], + "510f39ba6e8f8df9f3b1357894f67890dacfb49c7e02367c1092a47f9574c7b7": ["testbook.com.", 0], + "510fb02417d071afcd9d53301669d3cc2ded7aa6f9f33e67dd3ec07ec768e8a5": ["unitconverters.net.", 0], + "51118a0df94d7cffaa6e54818611292ef07193d6c36fbca611e276ebff4c5330": ["lonelyplanet.com.", 0], + "511f11f328fb47a82ff0efc32f33edf0c15097f49b3e4a96e3d40605a3921634": ["uninter.com.", 0], + "512315705afae35bac6fe5df7cf6f57cb8c4428c4e674b91783fc1c295ccc8a0": ["jumpw.com.", 0], + "5123a502e6ef94a2b342a414fe339d4fe72e835c0de6ff430370fbbac67b09a8": ["service-public.fr.", 0], + "5124afacdc56516aa9da33d34f957af026f6256a1af66475614ad4cefbde339b": ["docomo.ne.jp.", 0], + "512939ec58c57531400a40a49bb682396a9086ddda04a406b07c5d0caea9099c": ["55bbs.com.", 0], + "51358b3a1b4c115929e82c0868bdbea32eb6a94d461dbee60dc4c933848d3ec0": ["freemeteo.gr.", 0], + "5137082c8a7d6e36780acd9ef44fb9a6a0602cdb87a350f8f2d782f2867e7860": ["emex.ru.", 0], + "513b3aab1f2df6a091272ad8b7e19edd196cb5b864e2a46430160ff0f146d10e": ["phonearena.com.", 0], + "5149e168cec7a4c0476d34e136142c6d4d6a23bb5858a482ce14780b18ce5033": ["clarin.com.", 0], + "514d1caeddc2f307e4b493dfdab5f6f88f876efa56e1db1db556945744ce4869": ["anydesk.com.", 0], + "5153ffad647994ea5177e382235c098ea8084857148bda6ed71c3638f5f7a46e": ["when2meet.com.", 0], + "51560784a6e9f884f7608761a46d301164cdb61036fd874131e0e556f6422819": ["wpforms.com.", 0], + "51560a8d2399a573fc6726ba7af570ac72f7cb0ec309657c94e65cff772c9f70": ["allstate.", 1], + "5157cb67812969ba65e01b6de3abbe06fa5b34409ea03782e77ebdf875fc6ea5": ["futbol24.com.", 0], + "515e6887237c9eceaf5429ed360bdf76054138e64ac330a149807470d4fd1271": ["illinois.edu.", 0], + "516a31922315fec597ddb2e322bf1ff2ef88cba4bbd11853664794ec13eea469": ["timecode.ir.", 0], + "516a84105b456f1acc4525e2c4508766b309b6f6097898319945e8eb3dd1da62": ["javfull.net.", 0], + "516d8fe491aa74aed4547c24c7cceed405c661d2c3597b3769ede9822caa4ef4": ["town.", 1], + "516e3fb87be5a4b8f6b853694e189e7969ff87b87beda371265f52832463615f": ["elconfidencial.com.", 0], + "51731e3331031460d159cee9db19cbe0e32177e60e28dee1c4a5b3eab3ad3e6c": ["filehorse.com.", 0], + "5178f18cab25a840a9819bcdc0f1066560472f434cd793e183fd9f252b790a82": ["norton.", 1], + "517ae251b7f26788b7269005df43d9fd04f3638dbe5d3124874c1f6ec40cf4c4": ["ctbcbank.com.", 0], + "51825ac9805c0e4de8de263b4761faa885eb8bbb57a5ab1cc368c268542a22b2": ["publicisgroupe.net.", 0], + "5185581ed188b58934f677b83a24222d76833ad7205fa89e2f44dec6cca43295": ["yuque.com.", 0], + "51920b1babaeb890ff232c6360fb37f479a0f54ae360278ff740c17a23a180bf": ["buzzsprout.com.", 0], + "519256bd2ab0912a382d397e568afb255843fea3abe4390855c52854005ebf90": ["zappos.", 1], + "519cae96ef200fd4ebe66d277d598fb1ac8fb18f65b59d72b2e0f94ef1f0e170": ["yourdictionary.com.", 0], + "519dcddec964403545e6473f2de7a79e0433fa94c2305b68ac08828ad44376f2": ["cuantocabron.com.", 0], + "51a8b1280164c3d8a967a2327b5fd884754ccc5c2332c7fd6ac439c6e0b85bdf": ["massimodutti.com.", 0], + "51ac93e90d9117432a58ac0fe3b1f5cc2346016a565d857f49455f60defa037c": ["nissanusa.com.", 0], + "51afebf8e5e368fd3488458f42d028ad8c4ad385e84a906e775c7c152509c63a": ["tomshardware.com.", 0], + "51b51a744a6e1ac5fe76a32ed36ba677edc1c61867ae18b1e81ea2817ada4b5d": ["fk.", 1], + "51bcad722e4af68c06f1123b1db821bb7838bb45b6b5bde420f12583800a5945": ["snapchat.com.", 0], + "51cb9663818c35d8f94aef1e73d6ccb21b03d2f0d5d8642b347d083b02b4419c": ["dennikn.sk.", 0], + "51d56a0d3810556179799ea31876580c2bf3fefff52ea751119f21c0682d75e3": ["zakon.kz.", 0], + "51d66c1a2616e64515176067b4004e95c05769b8f8bed330c562e92eec63986d": ["sbu.ac.ir.", 0], + "51d91e5be60b508b160f628ff59aca904d439e03816880f3ea4a79c3e38272a0": ["cqvip.com.", 0], + "51db25ca751ea54e8d5d0e99b084a940f8a3115107d05d8e20820e1832a186b5": ["call.", 1], + "51db7be05652bc13581acca735f774e4a93f82626c72f09fa6dcb2d4d1ab155d": ["jia.com.", 0], + "51dd18a421f45b120b107b8f2ec1ce419281071e51bddc7089433804d767358f": ["ford.", 1], + "51e1541826af6873efa3b449caa6b2bd34d6fb4c8feab35375de80e98aa4d029": ["gva.es.", 0], + "51faa97a5d16a7b8b16d4e80e835b3f28245fd909b338dfbecc3069f29874f6b": ["americafirst.com.", 0], + "52110e31db7d89bd72adcee4b51cd43a82b46dfc787ed16ad5366d93d480ceb9": ["salon.", 1], + "52149241508afa66d2ff0a12f73b406438b94439df397f46f69c58bd7ec1968e": ["animesonline.cc.", 0], + "521726d40cb06c97f4e7b7a81a594318318deb758239137cd2dd0024c862915f": ["bookwalker.jp.", 0], + "521a79fa52116b13609939aaf14bf7da5fa19adc124975528921ff20fbd72841": ["revcontent.com.", 0], + "521bbdc4c6dd4fb009ae6912198f62c9e39df0be9c0751f26269ec0f3280c391": ["cdek.ru.", 0], + "522e401cafd80b778f5e9bd9e37a91aafcaf022f89ba1a90e49171e4ffcbb287": ["westpac.com.au.", 0], + "523300e93dad78e6de6f58f28e276b9dd2d4ed2be1655d3225ddec2f389c2589": ["taobao.", 1], + "523ab65a4a794f1baa8e8af6ec9d9f68b3e6bfccf5f13516b643efd0278c9ea5": ["15min.lt.", 0], + "524679a7461cbd78fb38b42b4fa8dbfc5ddfef82f9f1633c705161b90e46b9d0": ["subhd.tv.", 0], + "52494dc480df21e6977990632df8b6679da4b34094737cd43088d2ecb7c02d3e": ["bangbrosnetwork.com.", 0], + "524adab823af5da8196b03130bec55e4f6ed4c2761ff3b1d580e2cdd4c103144": ["speedrun.com.", 0], + "524d1762d6294694c1b164619e125617c7609217b7a2ea2d5f3fb03fb1de9a07": ["seriouseats.com.", 0], + "5257300d05183cc4d2fa0f3252489a0b2fa9bf06ff5459f7baf8064e6e6a2219": ["rockauto.com.", 0], + "525ce500322a0f4c91070eb73829b9d96b2e70d964905fa88c8b20ea573029ea": ["twitter.com.", 0], + "5272883c3b3a046fce0cc8d32634f2fdd0c1382df173ea4c13fa76d297bcd0c9": ["programme-tv.net.", 0], + "5274ab63accb553a0240ad70e32a4d56cdfa0fe931109b930df172e7eca87ea5": ["mockplus.cn.", 0], + "527aa26714194751e04c0a7082d54f2a668cb00dddc54798f9ec59aca17a005d": ["eromazofu.com.", 0], + "527bb23d5aac1d52570b79477cde8f73fa32eb8c908644b93d55927d90a138f2": ["fimfiction.net.", 0], + "527c7cf2f977b4d29e84a90a33a0003802fc3c2ef4abbf7e14fa5a3241126bb1": ["sncf.", 1], + "5284c0b32f6b1316499be2d6c3d0d880b29da873e16ee48bc89b62d99a1de402": ["twinkl.co.uk.", 0], + "5284c27f474b8325a487307c5a18bfc8f72430f3135ad1828adb110a8f716107": ["shikimori.one.", 0], + "52862e5d8b0cd184401027eaf0321fac8d15ac95bc2f602750f69546aa2971ff": ["praxi.", 1], + "528808cc3b7e2a0ae3c75a11726f7fb81846d31431336c6abb963ba192161942": ["wikipedia.org.", 0], + "528a3604d8bac629c6d355d316f9eb9d0807e15e1d985a69e76aec6e4597f1a0": ["bosch.", 1], + "52943c1428c08faf1c4462006d934915241b3669106a195b27d1ba84f40f4568": ["lxax.com.", 0], + "5294749a365faafdc2fd6aad34a16529e739fc35b143562f572558717e3f1533": ["shopbop.com.", 0], + "529fdc30a07b7b1829173af3ea2555d0552a4b8576166eaad9ba2ac8a0c37226": ["promo.", 1], + "52a858dae7333933b1bbd37838d5d677dcebbdb43dd9d834ca489a108bc14221": ["theregister.com.", 0], + "52b2c3e11964824357f33f30aa0e77678617256518cc659f9d12c6d2a37b00d4": ["sznews.com.", 0], + "52b456cfd998b004eada2b5c776d36ba94c46b68a1e7fd99bc43c3916bcb04c8": ["slb.com.", 0], + "52bb64e6cc32979acfb303bf722a97f71f5e5c153c16ceaacf6457f07c38e454": ["ravelry.com.", 0], + "52bbdda7c9a65b8bfefbc8b191a03a2a4f6e565f9527c2f39a5f5105110c1ba9": ["igroutka.ru.", 0], + "52bcacc156cd282c8439cf8916d697f0ff395ab5fd5458c04471caabd03f270a": ["nubilefilms.com.", 0], + "52c53d961c34b5ed66ab847c0a6d58847c0835a8012ace55f970dceccc80ee8b": ["58.com.", 0], + "52c92dd91a90b3ccda51263fd464f3f234c4dce31db699fd1954491af1d1d3fe": ["news-postseven.com.", 0], + "52cac0237b72c100ba48840ebb40620396de6b71ff052e99f52821aa3d5d0971": ["indiamart.com.", 0], + "52cf30e7ce0baa84f8372419565d0a2f437599cceb5791bc0a804bddf20c9d29": ["snu.ac.kr.", 0], + "52d25b2806331296157a2fb5412f83ba70b20f260c92347f66d9853c8878e076": ["somethingawful.com.", 0], + "52d678fde730dea87328af9f67a05753713449934d3344cc0835d13d2116636a": ["jigsawplanet.com.", 0], + "52d9355dfb5a441eb6c9570a96b4b9e7fd0ebdd35fdd0366aed1f7aaad8d2d4c": ["tmz.com.", 0], + "52da54864cd48c0231c911fcf350d9a6589cd10020be2db696a4eb254ee758b6": ["biggo.com.tw.", 0], + "52dd65c8cd3cb9ac2833d3a9c7aaa265adb10cce9a8fb33213345d03ef1ce43a": ["zzzfun.com.", 0], + "52e10095f6af5c5dc5a7497a2cd83b3caffc227e98816020449069f9ebedbf39": ["tku.edu.tw.", 0], + "52f160f384e1e253e3ec6a4ac7dc987294fe412dd26e7d9cf0b59588e93cb872": ["angellikefire.com.", 0], + "52f2679cac7bb99122df5c6370410aaacf168b743bc712aa9e0903e63a46f9de": ["hnonline.sk.", 0], + "5304bf8ee4f6f39a3565a642b0033224332e30cd66819e3f96b565d2fcd139b7": ["naughtyblog.org.", 0], + "5317a32e088344720c9d77e596e1920463e4f2570f2e51eb797778061ed11f51": ["catering.", 1], + "531de1833bc6f06ee586633c3c4d113f0ccafa3a2e5b2c2651b92466ca3ca829": ["apnetv.to.", 0], + "532939829d60aa8ef610df2b8b3cdd05bb1fbdade58dc2646ae67b2dbc8f1b74": ["vit.ac.in.", 0], + "5329977457df5c8046d60c7a5536035490a55d5e5608d4da4b0e7d19def6ac2a": ["gfk.com.", 0], + "5330eff48b819660f3a62d9dc731dd16ef229b44461c7bac8d86d5bdc971ea12": ["book.", 1], + "533c0e692e43f4ade6970229d502494c8838ef308af250234980662021501278": ["yamaxun.", 1], + "533ffd0aff9f95072a3a97fa16dadee88d634a6531f94b965f98bbeaa882f5fd": ["dmzj.com.", 0], + "53548e1df0ad8f0065d71bf9de11c5735b53f50e954318186fea1a28bf100194": ["miwifi.com.", 0], + "535b678984a405e6e29a771d1ff456cb0a6691c9f497fdb1da6c447742e567a2": ["kurzy.cz.", 0], + "5363974655413f68e5e7225850d9aea0a947d8ba0ce8d0079729400a66102208": ["ldmnq.com.", 0], + "536d2d9b29b6db45bd893968a92f78a1be45193b47cebd1ff81844c0e6b1028a": ["fcps.edu.", 0], + "53752f6aa6f2feeaa4ee524590aeffec1fdf30ea1655278242c81ef921b3d9a4": ["xn--tiq49xqyj.", 1], + "5377d90d2c3e2448f80b745a5f90cb9e45cef852beda88a26bf16fb3ada5fad0": ["alaskaair.com.", 0], + "537b80c392983bc8bdebeaf18f971b3859e4bef011e910a0020a3985314d2a14": ["guide.", 1], + "53864472ab9f796c73a8266cdbeef31974a1ddfd192f80929fb5d45f60f89a7f": ["cic.gc.ca.", 0], + "538b3feda074e57ec2256cb1346773cd1831021bcc623d071b8572762151767c": ["maxsys.ai.", 0], + "539766e7ed6ec912b07dd5adbc02d66cf2db5ca648584830e55f0e4f60a39214": ["viabcp.com.", 0], + "53990cc0b9a23ee596e5ba7f77b6f6358dd569bc0be4edfb082fe2e605f61265": ["wme.", 1], + "53a2bb8f3effe19b3ca5f11d5d3951f7a978248317497a8f84995d37160143d1": ["moslenta.ru.", 0], + "53a515202903dcb441b10a6e62ca99f5630ad37c6874628104ddb31125353fb5": ["pinggu.org.", 0], + "53ac6d85ca9349b5183343544af7ae99aeb00d41061010e7853079061ce27859": ["ponta.jp.", 0], + "53ac8ccf69372f15b67bc11ae883b1db06306ab92294a13b87f9a92f6c5b4edd": ["elegantthemes.com.", 0], + "53b253ec18b89c6c8164d7b18abf96f5a915d026daae90c3ac27230d84e19021": ["onliner.by.", 0], + "53b85df964a36eb6baa21e7c35c7d549f611737c3a2560220fa12859e5e841cc": ["tvboxnow.com.", 0], + "53ba6aaca84463174e37457b1e4bebdc84580ddc71a5e1deafbc43e1e782c1ff": ["cybozu.com.", 0], + "53bceed5133d0ce1aea590dc1de064baf82926a139f9234dff431394fc6e8dde": ["wanggou.", 1], + "53bddcee417cc16883656046faa3dca6851e0beecadb798c973ab4e31121c571": ["carwow.co.uk.", 0], + "53c7ce31ec4aef6685bf13572dbb32ae8e6f2c58d7f5c4b4f5022b799652821f": ["gent.", 1], + "53ca3eb7cd5edd456a2101835e8fdea7732b2c6eeb1a18f9bb67e9a220fe3157": ["netmeds.com.", 0], + "53cee425685cda99aef95eb2865bd429b5d8585eefd3b692834a92006185c31e": ["estate.", 1], + "53d287b0121bb52b9885aeb609be30e49c2763eb19920daef48b8bd9ac94452f": ["schaeffler.", 1], + "53dd9bd1785f0da021a256a709e956e07531d80d7406e4967191a3b2a37a53e7": ["windowsreport.com.", 0], + "53e40eca70ed86745c436f071d4c04a9c61c1ed15e1bd2a5e23c2a1c6da16797": ["mercola.com.", 0], + "53ed1fa5efe50b590f3c08609967b48c049850ee810d6f477aee89e0ef862a2a": ["rahavard365.com.", 0], + "53f69a3075542250940e5d539c3d00a32551e326da6b889986f699e2063b27ec": ["geely.com.", 0], + "540330196796dea16a93875b66fdec437e78f85d0e6bee104c693f5fae111f2e": ["livejasmin.com.", 0], + "5406e6ce76d101780f2d595f696473384294cd6d997a990f53e3a425660708cd": ["soccerbar.cc.", 0], + "54104b34455d649f3797207df3d733059e9fe5084d5bad8c7ceffcaf47d550b8": ["pcbeta.com.", 0], + "5415dfa3f9befe490c60ea69d56653d28c85401837aaacdaf0ad72f5de8d29e6": ["ijinshan.com.", 0], + "541cebb24784b8b5731bdad5cb17b02fb3ca5e0018330854324ad904fd51d6bd": ["paheal.net.", 0], + "541d7640669bfaca2a16e10d2cf7e3134580280ad71a0224fbe373352501d67e": ["igap.net.", 0], + "5426bc5f21cc438684d7cb5b636e971f4f51e67690ae07fa9c0ddc06674d9a68": ["thaiware.com.", 0], + "542b65cf8511cbd9c40c28bb6d968a07d3ff6231d946053997e9e8294d481ee5": ["wolframalpha.com.", 0], + "542bcbc53e7e07fc8442212ff9190f175f5420309da328db311de237600ddbae": ["alibabacloud.com.", 0], + "542c0714cd6e5cc1dffae2f9d2eef0871950747d115dd604e2876a148bfe1dd7": ["vseinstrumenti.ru.", 0], + "542db6e3f5c2626741f8bcd1aa1683f50c7c3b26bba658055614d9361c915fc0": ["paypal.com.", 0], + "5430c58a24aa88e6423fd6f5f67f0875287789f9892bb5cff4a8e1ae2d8b36a7": ["unc.edu.", 0], + "543afc9a4dc3206771da6435fadfdb17be30f051d5131ef3231faa3c2317a42f": ["mhcampus.com.", 0], + "543c17a2d02e71df20670ec95e5c0da879622fd8268bc3d4859d531cee7fcfda": ["assam.gov.in.", 0], + "54560b92067be1ad7c4f2963043acfb7357943df5770c18bf9c7bdf1429ed50c": ["xcar.com.cn.", 0], + "5457f7b144c6d8fc1d6c6cc8615d7e9714eed433b35d11fa8699d8526611ed81": ["irasutoya.com.", 0], + "5458cdea029e72d21ed3eeaa0ac382f2070f962474a0da83a37450ad95b7ca89": ["williams-sonoma.com.", 0], + "545c166388111a674a23f9949d3a9ad94131d10a76d35ec421a97e5fb7c66727": ["lolesports.com.", 0], + "545d601378f2e76501850c1546ffd7b9be4812cbc36692168566919769a94d0c": ["cheap.", 1], + "545f66841770584924c21b402b6e41f461d242581400f2ec92abe92b15dc2a07": ["dartmouth.edu.", 0], + "54633d7ee5a680700247f3270fe21028010ae9b83b1e641fed6db1948da07e94": ["diffchecker.com.", 0], + "54667b7818a4e440c0bf8829a64b4eb67e1ac5140856d73e3330a8be1ff7a41e": ["moda.", 1], + "548eae8025a0e3e3a9ebe0ea7931fb76450aaeac3b5a94e0adc94b2724eb2ff0": ["dubai.", 1], + "549664bb037012dd0cc690889cb5700dc78875f8cbaf5242be7b9e40ce9c04de": ["2checkout.com.", 0], + "54a18f2b4253b2283d4ac73cd0ec23a30f674d0b36d586eff3de90f355c2b3d7": ["xyz.", 1], + "54a5ff7a828466aa65d27493e90c2d0235322b995c837d76d7d74fad34cdf816": ["hpstore.cn.", 0], + "54b141640807d38c87433ce22873798cd36ab0040feef5f6256d72f0260bd73b": ["tubitak.gov.tr.", 0], + "54b3fa1e065d26ba35f4930e90616ccd2ae83a0a3a7de14c5fff21faf5b9e38c": ["fujitsu.", 1], + "54b7a3a6b8a8b9595e090a0e301798b8138526f9e8f8b22691103217b5463fcb": ["hudl.com.", 0], + "54b8129b6a9344b88d1d619cce3e4715a75d24315f1a4a858e9d4a946d78f5ee": ["soumu.go.jp.", 0], + "54be4e0d2b5aa51ef8c4ac762b3f1e4a5ea0d4970770af28158bf371ad05793d": ["nukistream.com.", 0], + "54c77d2dbc3543d2baf12dade6a620aeb80da2d13aa9d632b7080d7cf2ef76cf": ["gtaforums.com.", 0], + "54cd2c88b7ef64594ff3c6ee772c5718d58b73f98ef7e4e3f5284d09462c7e34": ["oregonlive.com.", 0], + "54d8f2c9fea0b1c98ff899770604faf12b2cd2630829cb00119569f63b241e87": ["stada.", 1], + "54dbfe4a62f55027dddaeb6ad6ccb21a00e2df0f1c78071780cc4265bc8529dc": ["javseen.tv.", 0], + "54ee71114ae46dcaf305bf0e97096b83f0e4a2f0186857ddb78c0575995e2f2f": ["citizensadvice.org.uk.", 0], + "54f69eb778fe20b1bf458d5b8443d4dc1855ab4a7237e718761e912d3d17c725": ["osha.gov.", 0], + "54f836e6d050e2647a94067d6efd38cf1650b1115f0c4949660429e3b128825b": ["livesoccertv.com.", 0], + "54fac9662870e38569b1fab915e5ff612317e5b54b80b24191549553e4db7224": ["presse-citron.net.", 0], + "54fad95d827d7eb8698646218585c2672e47dd3b1418c7c701b97636003d70ab": ["immi.gov.au.", 0], + "54feadb9b9621807368b71ac004059ea234edb8528b88afea1c21118fe716375": ["freepeople.com.", 0], + "550df9198c745e7bbac72fba171725e33639764a443079751d275c6a1872dfd0": ["xmoviesforyou.com.", 0], + "5510d646c825c85a41f2dd56f7effd3ed7de5d33e364108f39980c10048731d4": ["escavador.com.", 0], + "552308dfb5664635c9502b5e10929be340a8604778137f21a5ae33c011b00b6c": ["kontur.ru.", 0], + "552689ec0fe597953deb7efd19c026c0325ebc131aeaa62cb1d0072fbd8f3b3c": ["luxury.", 1], + "5526e5a238c1a238ab22697f16925e8441577517f4e8cbb5f1cc58868ce329f1": ["cfe.mx.", 0], + "552f240dca478a4fb0ff25bd1d56984adeb6b27532f8e399744bd4c6459b5b67": ["tecmundo.com.br.", 0], + "5533002cdca1f9aae032a68a2fe3a09695a438491c3d8981cdc6b79a4e5033fa": ["displayspecifications.com.", 0], + "553898e8ff5f44623bc1afa3b2fb9b481ebc82815d7b03ddca201eccf8495d26": ["famousbirthdays.com.", 0], + "5546659b25eb0b522690e29a43f19328eb8975e872413ca3aa40c6f6a2a0f52f": ["comunitaria.com.", 0], + "5547982cd0956a4d1721bcfb40ce785c9b3b361976dc72ff1f65bca2944c8d75": ["macpaw.com.", 0], + "5547e3779c13c4b71be9d82a895233d7fd6c9957f3e79387a29fc7a20df2967e": ["travelchannel.com.", 0], + "554f0609740e54658ab7cbe7ef4cdb0bf56d0fca70bb92557f15ec1e99a5016c": ["ikang.com.", 0], + "5553d70a72ad39a5b71ce3ca0d3395f892eccf7dd95ac38bc310ff9f3d71d5ad": ["nzxt.com.", 0], + "555da76030f64a49934f86b1801539f92865ad0999df626420cb5b9d889750ad": ["honto.jp.", 0], + "55613049e4b7b48bab4e0b712d1764ead79bd56f9b4a482d553bf4ad74963429": ["femdom-pov.me.", 0], + "556219ea13635f9035730a404c0086de64c69484dc824512b8126b5273552de8": ["libgen.rs.", 0], + "5562e8e367f39b76e36316f9b516e3a457ea4264eb0bb7a8f7855e0e2c7b8376": ["tfl.gov.uk.", 0], + "556bb7a20dd9231050fb48e7580d37ce657d9bd7f5c5d155d17140f6d547a756": ["longgame.co.", 0], + "556eec17d79a07ba3584ea0d3ecf43d1e3dde6b879dc527190fbc0c71d601974": ["faz.net.", 0], + "557a36f2890c2431309340c97032b8fb01d2fe6c614d882b2b1334060f0d3a5b": ["iitm.ac.in.", 0], + "557d95234d2b0fb7174f3befba10ef0c34555930e72a10a4a4d1c85696eadd9b": ["post.", 1], + "558544ec6b41e2ab9e66280d7ba0c259f97fd989da0ccefbdcaceeaf0fbe24fc": ["im.", 1], + "5586ca2e66dbf532dc6e6237f8626f074cb1205a8a6d34b84fe9d702a515ad5d": ["gu-global.com.", 0], + "55996a3b82d44ff519d89654b55eeb316b715f589cae1bd82c1866fa92e68750": ["paisabazaar.com.", 0], + "55a2b97a0a1eddc695455c55ae629f7f166faa60916e4744fd3c8e0987c2535c": ["unibet.com.", 0], + "55a5955e73e630a40eef07dfc0253cd4f5a01387e712b97e1bded501fa1a96c1": ["gnavi.co.jp.", 0], + "55aad0b954a361b85c8f5abc811e4a1876e89167dc7bf1d44c4b517a9d5d988f": ["bestcarweb.jp.", 0], + "55b244351bfd9e234a104f6d17c59ea9d44d2549750b28f6de5b559f76031ef5": ["digiskills.pk.", 0], + "55c19b4862b9376dba3adf7db736bc550d6836f058dac001646aa5249ffa13b2": ["senecalearning.com.", 0], + "55c38a02787f242e1ef8d12df2b6338f8a358cb666cfdf5d8faa80452a63ac8b": ["whattomine.com.", 0], + "55c74d91b33faf0b0b6c776289369c55623542c6e58284b8fdf3b5c2ac8b39f2": ["ucl.ac.uk.", 0], + "55cc327c85de92dec5fc06972da19da8472a925373927bb716d540767fa93cf2": ["topito.com.", 0], + "55e39c20bfbff890314f7f67deb47058129d5fe1f6a9d5881d5cf51e2e425b8c": ["orcid.org.", 0], + "55eb0baa0410f12de59045606c2b102ed2695a7c142e1965f6d364d7ecf8865b": ["xidian.edu.cn.", 0], + "55ed445f8d3ca1f670a39950082dd2140b5c8d4323df88504c7edc8206625638": ["abogado.", 1], + "55fcf73a918231cccfc43b04510a12fbd40caab3187e87e27a47f5f351004013": ["gamerevolution.com.", 0], + "55fe8a74bc1fd05511dcc0bad4162a421c2419add4d42c0c8c705b7212d1bb35": ["orgasmatrix.com.", 0], + "56052bd14e2469fc6c3ad8b66ebf475dc25174710104a85e0b917f75e47137c1": ["clevertap.com.", 0], + "5611782d159a719be9ce361cef8b208c6bd69bf9d06a4ffd473de586b866d9d9": ["hbo.", 1], + "56144d7b02e387b26c6fa33e82460fd59a57db596d69de9a20416c345a6b7b52": ["liqucn.com.", 0], + "5614c1df4927cd79e92a99b85b73385c91de2e6910a9a7af43071277e6cc97e8": ["animeshow.tv.", 0], + "562704d0e37e2142bb731c955b3944c633f54bf52d9fd46da7552dce61b0a28a": ["literotica.com.", 0], + "562f7516d02905aee69b2c8038472d21ee2a2a5059b23f2cab4199f900d1155e": ["etihad.com.", 0], + "5631330812d4b4ceb41e1ff55b2d222025a72a137066644d62437ebfb149a1f3": ["tax.", 1], + "5633ab2c89adf577ba42b790d46d1f1fd5dd61a43836e62b27708b4dec241ce7": ["37signals.com.", 0], + "5636c42c979ff01ba70d83cf9d3cdd4bd337960a65ac2cddf2dd1460517d0cd6": ["experian.com.", 0], + "563da8d2911f10011f83edc17318018646dbce6df32c2f94cf1d955818042b17": ["eurosport.fr.", 0], + "563eed3c4ee3b26efcbe15ab561670c1558abad0def6c18768358d76af3a5aa7": ["ehr.com.", 0], + "56411b9f94e55e87da011ccc2f036c478b053dfbb61fc56f0120feb93e5f5701": ["apartmenttherapy.com.", 0], + "5645c2c660badc76c2d16bf23ebf5021df8c7f17d8f8702a8d338545dd2fab81": ["coches.net.", 0], + "56469f0f167ffb46e7592ccf0a356f2206c4ea9d10dcb2b45097fe1b0468491a": ["xn--fjq720a.", 1], + "565874ed68d49b91a08cd3b89e17b3c017d92ffbcc0af68373192e8db600bcfa": ["fmprc.gov.cn.", 0], + "565dacad27d3d8bda1686eabee7b8c0ef9adfcec6ec56dc8eb7315f455577e2a": ["aquarelle.", 1], + "5664c4c967e32004cc732f9560da948dd4d15a55fd84203c99223f43997173c1": ["munpia.com.", 0], + "5668b077532230eb25a0927f2ade335585ba0d7851ddb25ec488a24a6b942374": ["gartner.com.", 0], + "566c45f697004813b526bbfd0a24f86d055c2474fd8c0d3ae4055c1a21fa846c": ["paho.org.", 0], + "566cd38a8d0a03ddab8efde46646a0b67fb5635731c333750811b351d6980c74": ["aizhan.com.", 0], + "5674f510ce549ddeeae2ffaa18357e357019d37193eb38167d66dc5bbb9d0cab": ["chittorgarh.com.", 0], + "56777e050555b7bc62ccbbbc68b4d4c715651cb59f24130b2d2ac0b759d342a9": ["stockx.com.", 0], + "56782ee0df2914de9f3528b0af9d7057b7010cb5c6a474743309cc61e67c8522": ["futurelearn.com.", 0], + "5684347ea5be47f2015f8680a3e70b800dc59642762768376f257b6fa532e1a6": ["shopee.tw.", 0], + "568ee71ce1e771d22828713f2cfb7af0e80cecf8a15e63e3ac274bd6c6653cae": ["marshmallow-qa.com.", 0], + "56a34a575bb52e7680c50496466d3004271282270d436c9fd5ce1ad7891b430c": ["senukai.lt.", 0], + "56ad14b75664149339124208f1c1b7f2308312a0f8dda76e56bb646cd501145e": ["fangcloud.com.", 0], + "56ad6e00a576884475748b4f29d94a29aaf0a1ba53bc25a7b5e61969bf484ce4": ["zbiornik.com.", 0], + "56afc4ee00959c226b8d1eaad75d0edaefcbe51b21b5ff6c2c4fdb61fd277766": ["vanguard.", 1], + "56b25716556b13c7f7f6c84eb8b3afe8f98b9f00f1d9e163709b26a9917d231e": ["xn--mgbbh1a.", 1], + "56bae567d73d533b26bf59b7a4e732059c3ec338c324bb7b489f85925eda9b18": ["bzh.", 1], + "56bc18101ad743edb4b49cb1bc265094d699cb51e89ae8c0517ade43c924b6a9": ["gandi.net.", 0], + "56be82715d6cb13181e3d82827e46d605877dad6a2c191c941d49650c8b2911b": ["weir.", 1], + "56d6f8f3509a6420e697940be63b781625a4c7608d32eb0c8be04c352fb09cf2": ["stubhub.com.", 0], + "56d9c4d988ff0aea3b1752b64b647191f7bc718482ceb7ef748d41c0438bfa08": ["kuleuven.be.", 0], + "56dc038fe5e52a3e4f46c7c4f66b3782d2175d3f73c6287060cb600f83f5626f": ["proxysite.com.", 0], + "56de0c0ae7b0ed63c556fc441d7b9c942bed6f3b6c560e2557d5464746769f60": ["japscan.me.", 0], + "56e803982668d389ec47cdc12253d5af4ef57b97ebcf02440563392d7c66d47e": ["jiegeng.com.", 0], + "56e96fbc82717a94c6a46a77d909cf0591ca3ceebd49160c824016e526a978ac": ["plus500.com.", 0], + "56f0718b3333488f3f4f78c1c4031746eb1121c9c03a34c02d2b8f893d2d869a": ["dowjones.com.", 0], + "56f4cd0e8d6652c9ee02e08b143310e0d03aace464b022cc9651e4e737851739": ["mfcad.com.", 0], + "56fa06ffc3424f6233f86802f4e20fd1a3a3c653dac3f060b411bd89ba8091d4": ["girlschannel.net.", 0], + "572a285703471ade56f7c9a5bdf8bf76356dc7db806db55f87bc7f2c660014e7": ["naghshealmas.com.", 0], + "572e835575e1ca95a8077d043c8dc9bf428b6006e251b924ec36fa5af75c4a40": ["lawinsider.com.", 0], + "57367eeb571b4fff0b004284caed6ad287c22aadc5254aef66f8bbb01ab7a91f": ["elibrary.ru.", 0], + "573733810cdb55b61922bf44982c5458fd113975ba389946a4da29c13bf499f7": ["dstv.com.", 0], + "575b76aac54293f006593b6df3ee1fd8b972f59efd8bff964ba524d113c1c998": ["k7computing.com.", 0], + "575b7a795fd576200dfa70bd7d002aa16452c348db2321e86e8251ab8ddd1952": ["usask.ca.", 0], + "575f7cc693544f1c9ce9318874119820b5293b992bb8afb12ad3634a4eaa5755": ["as.", 1], + "576c44cc8c59bb03ab20b3c600c6eb348b1d02b6be0a01b30acef09d55822c9b": ["foundationapi.com.", 0], + "5771b9c2472f3baa0be5c027781cf0a2961c4e778867c1a711c77128f88e6a87": ["iop.org.", 0], + "5783fdccc8ea7e36ecfcee6bc300f35675a8bd56066c0c034439ea17ffea7938": ["tutsplus.com.", 0], + "5789ee6e20de9e82a66166317d46610df91a35d9355bcd0322396c57dd60100d": ["dribbble.com.", 0], + "57942f2f328acf0c5dfa83359689e496ba307c69055ffb8551c24e0cb99d9fbc": ["kusonime.com.", 0], + "57964ff2e44f5900c882ef44cd0a758efea4ea98180656209ffb1def6c03b3d5": ["ccm.net.", 0], + "579d45c44ce7d5e00f4ad1e9a6cbce0bc0964ef8036eda2ac924c5794670c8ad": ["businesstoday.com.tw.", 0], + "579fd2f5303fc673865a1259ab07a13c55b708ceeee1efc23d54c56c92b92b82": ["sport24.gr.", 0], + "57a03ea94648748c56570aa3151eb8ea411b13cacfe24d7d6523003dd24635f0": ["sgk.gov.tr.", 0], + "57a10a4d43141fe0dfe03551e42a8c3a2e594d58d957ab85bf222226c822fc08": ["worldanvil.com.", 0], + "57a1617a1eef12db985a06d7eab7c4b7970f2ee15112047147ca58a8dc30acd0": ["5eplay.com.", 0], + "57b1141c21d4bf8fa7eb209aca4ac3c995e0cae4856948e68e75cf596042970b": ["sporcle.com.", 0], + "57b5ccea52a795e30e025edeea709e889b307ffc0ae2ad3354b9e6adc7893113": ["bankbazaar.com.", 0], + "57b604d15ea19f903ad1f2ab5122f1764731e59da23fb69291126ca5854b78ea": ["ifixit.com.", 0], + "57b71e45fa38c5d9d16953fd174ed0f11bad7df93223ce8a60f009057876f01f": ["enterprises.", 1], + "57c7ed11b503f3361967bebeaad4f4e5052d982859200f73e5636da9b657a00d": ["dailycaller.com.", 0], + "57d33e19777fbe3db68720451770d3b15b9a672dad12fa63c1a41d17712a73df": ["mediotiempo.com.", 0], + "57dac64d3a70bf4a81e3783f78eec63c4396bdcc740f7b2220a2302407ecb2c2": ["fio.cz.", 0], + "57eaa5e5b429d3dd0ae3bb7a3120d7d8199cfb29e06f5ccd97125425842fe783": ["peopleperhour.com.", 0], + "57f58449c54c42d3d5cb59fbb79c09f2b43fd8fd34e7709d78bfbccf99b3399f": ["tu-dortmund.de.", 0], + "57f74778a645a6f5455c8d2fae1bef24e1c2b7ac2bb8a50774e894d9d3555427": ["kurashiru.com.", 0], + "57f8bb051872d6a07a0fd7ac013523a0dec30b7645bd3bb3205fb3c7a744652c": ["slovenskenovice.si.", 0], + "57f8c9a49c8a02cfbee48073c693dbc7044bf160401204765a515db7f5f365ea": ["cnews.fr.", 0], + "5801f2e3f9b13f1ba81f64f1014087cea1a83751819cb3f3ee7a9521c1069a7b": ["griffith.edu.au.", 0], + "580523214bc88b5b365406f831d3e2f47956fd1903e593f2276b700a566715c0": ["trackingmore.com.", 0], + "58144e42addd0815450c9d7df877a14641bb496a9e573b2a31abfdf0c6928573": ["rolex.com.", 0], + "58147184da34c45a89d5c2ec987ef1456b2704136569e1e5bba477ca941104fa": ["prada.com.", 0], + "58182769f5c1e515ef72ec3f46c970b6b7d8ba412c966c2ea360537e3035874f": ["rest.", 1], + "581949b39fa447c67ee8a08f827bbeefc5a68837421156c80cb61bf6367f962d": ["porn.", 1], + "581abc8aa815ec0e0368b5f88de7b98f98fe326031d2fa7871aa81ec30e180cb": ["spotify.com.", 0], + "581b299c0d805da4904fd76e356b9703375170206640b924eb2340b08c07e016": ["scotiabank.com.", 0], + "581de8c27e403471ed354854185ba1ba96c29ea67d559f303b8ce35fa263eaad": ["novelupdates.com.", 0], + "581e6627d681a878e7bad6e7e668982476dcbffb59edb7e8ab355c0d90a0a247": ["ytn.co.kr.", 0], + "582245b12925df337aa927c8fb7fe642809253dc4f17f0b5718fd8b9c23357b6": ["iflscience.com.", 0], + "5822f2399eedc6db4964b5a36a66bce48972470acba11dde848d8fd93c928ba6": ["sinabank.ir.", 0], + "582af04790433098ecf8b63f1670e4a9986a64efe448a11b1c7587ee112143ca": ["zeplin.io.", 0], + "5832a85dbd0e44d8986726b52caf82bebcf25ed6925ef35a322f29911413228c": ["fap18.net.", 0], + "5838a2d7e4775d9ef6b2318fdbb8ed01fee4e88b318a0d6f322f139aa96545bf": ["remitly.com.", 0], + "583f7adb4f102d74d3d0c5dbe590f7400fca4b8695c1e7ddfcc63da08831dc15": ["cisco.", 1], + "5844b7da772709ac1ab6777cf42ad8fd5b7424c0f0cc986fa6d4c7412fab7f14": ["audiomack.com.", 0], + "58452d2074862b8f4b43ffbb1384027c75e2a5d9bac2d3d332ee13a1869278de": ["upgrad.com.", 0], + "584a7a6fc56c02f059d353343fd867012e80d656b15a1ddce21857f7706a5011": ["meteociel.fr.", 0], + "585dec4cc7b6ea20091be68a134eba3bba37c46f6690d3335ad6e78108e886d4": ["uy.", 1], + "586fb2ac2b867bfd977eb46a68624b127d23d226b870d447ae1ce56d0fb9d5fa": ["brainpop.com.", 0], + "5870135461a408eeabc5e3824ccc233d4147d9cfb957eb25bef4beb632e1c2ca": ["raidbots.com.", 0], + "5879f5e2e502c1c5ef36ef1cff4080bc4d02666957678e294930ff2d00d119d0": ["acer.com.", 0], + "587d2ffaedd96754d787874721667bd90965cbb07a7a353167da6ed0bd3e01aa": ["lundbeck.", 1], + "587d9fcdaeacce2044b56927e8d7725a1f4594793c05372298e2815737d8ae46": ["penfed.org.", 0], + "58818de82e34fe16ed6fa3e25cabfdde558076daec3816e78f0f1ab99b14b74c": ["lpl.", 1], + "5882bae6257b781d28cd597c9b6369cf8a438cae8241b8b3b0e7df906365b0a5": ["fontke.com.", 0], + "5883579f5a7898e8b14f05502c0fe2c6ad0254fec941bf8d6368db8460656cf2": ["eetop.cn.", 0], + "5883b019b57eb54558aada99fa73afb3162960702dd0f39b926a2d761057740b": ["med66.com.", 0], + "58861dcfac092334fcb068547bbdafcc45d592963c4e582b74ee3777dff1864f": ["payu.in.", 0], + "588808f4d56dc7ecc3419e895634a093ce8906a7037f0a849ec6396d7af79aaa": ["totalbattle.com.", 0], + "588ca4f18c5c2ec349f6f2d5edeb235f0129a13f21c005b22fe8ec834cf69680": ["uptostream.com.", 0], + "588e2af963c21076a9c0bd437be8390a48857ba62ba771665fbfb2c7921efb2b": ["musiciansfriend.com.", 0], + "5898192302aa2cfc39b251386ebda7eaf1cf580d5aa880a1d281dab9b44b5d03": ["tenonedesign.com.", 0], + "5898cc6ee60e812be4c564ed7832ba1495fcd73e2260401a1b5b8c0f9d2c2ac3": ["rocketmortgage.com.", 0], + "589cb4a98e38e1c10cb9cab796a7e40787e5579c27c8fd16bdfad0d5cfb151df": ["magazineluiza.com.br.", 0], + "589e8810cd0f63102b2c0173a354e66c3bb3f0fa3e847f7be903495e9eb6dab4": ["lsu.edu.", 0], + "589fe06c79af1d74e233ec170e1a7d1bb378e3f627cd3205573307a71e29ec86": ["home-assistant.io.", 0], + "58a78ca5d5bbdc4540a5f8ea90150da710e3cf9f0be4e55bda6819d196c45a58": ["harveynorman.com.au.", 0], + "58a99debe7be15f74bc9e3c80c90a545eaf492a0ab199da537fc29e4e7ab6468": ["sokmil.com.", 0], + "58ae5ee88a7d29ca727a94c352ada4150866f1079563731dd57c5099a2a95c0c": ["business.", 1], + "58afe4d7d3ac1163379d2efb881710409f19d9150896a67c75e815c0c6c1f3ff": ["mh.", 1], + "58b72261ebf43dd1262b2c0259053b5b70cd84f38958565188e5c5ce678e4a27": ["skidrowreloaded.com.", 0], + "58b81a9c55a676921604377ebc4ad2fb7673a17a6ee1115314d36b83d6d46ba9": ["jobteaser.com.", 0], + "58bbee8fd34999dfa4721c28d25e3373be3a85bb50738b08195e97bb48f9726e": ["airasia.com.", 0], + "58cbd08a4acc2215d2762002d2ef6e2033e7c509a01531c8703d5211f4e410d2": ["fxstreet.com.", 0], + "58cc5aa280439207059ea7ba362efe5adfea5d1600147b6800ca247dac6899b0": ["hpe.com.", 0], + "58cce9d42a0eb600774807c69c503f627cf8d413a00adc591885796000a66c8f": ["hygall.com.", 0], + "58cd589d70953c3d6945fd8f566cf588ad0393c8d3d7a2bdfe7daa99c8d8f765": ["check-host.net.", 0], + "58d9808317bc7e52332e009478926712e9f323dc82d4508bff35b33d13e90504": ["pib.gov.in.", 0], + "58e45375d1d211cebb34d2cffa6f7c910397f17d3d9c4b263616a05e4507beb9": ["bisnis.com.", 0], + "58e4cea1939b4909cefcb0588dc038ff59a72b201c641f94846d8d94ff624d01": ["myorderbox.com.", 0], + "58e54bd72f1f6950d8cc0474a5223d50463b1623a04834df8bf93f18eb2d64bc": ["pagibigfundservices.com.", 0], + "58ea6ffd762e8662981ffa5a235abed4e4ebeb9009d5adc5b385156e89c7634a": ["paradoxwikis.com.", 0], + "58ebf5b162daa61fdf58f72308c6e785494114d62f0378d73f104f059994729f": ["freebuf.com.", 0], + "58f0e162c098f199b93af56fc36f2c4f4bf749b9b650fb742b9b4ec1a88642b4": ["ipfs.io.", 0], + "58f69a79ac6bd2d79738eb31f8f27739bf8c2a6e13fdd0a7941150e284811b3d": ["list-manage.com.", 0], + "590215814031ce01328a6ac03da056c47b1466f8eee13973ca595a0d681963b7": ["ebitsu.net.", 0], + "591ff28c57cbc79577f80accff9c7210ccee7ab94329fe576a634adfa22342bf": ["fitbit.com.", 0], + "592e8bd63a1c06d5f7ac1167e663a104c0eb7b7def2781ec55d1303aa430db52": ["emailsrvr.com.", 0], + "592e93eba60b36382b51a9250ee3806c82f262fc54409ceaf33429994b7e2675": ["themoneyconverter.com.", 0], + "592fe4c64e98f2dcb0f5c4311888219989b20e032036218b42fd4cfe56d617e2": ["onlinesbi.sbi.", 0], + "5933981c16a60dc9f36bcdd3f1bc323204ec6fb2f2b0cdb2da9885f0968d9c2c": ["gameskinny.com.", 0], + "5944b148d4af226b5a3de99bb8983865f6e906e487900c5498c35cddaba2bdfd": ["significados.com.br.", 0], + "594a6b8901a2437c14d7c55fcc1a9f8591f056c74702d4b4751a46d21fc44660": ["games-workshop.com.", 0], + "594de85b028bd2cb7ca06bd8ff104a08e0d8e2fac3804b98d4af4fcb83376e2b": ["hackaday.com.", 0], + "594ec5b6a931731abc3f69d352d2707c2a7103793ed5e3b381d00bbb72df8715": ["topazlabs.com.", 0], + "59526f5107e210a4e323a3542b3ca0df8b871d23bd108666763d8ceb020219e2": ["fm.", 1], + "59572a07c8b3b897440d75304be6afd00cc56eecb036b6b5890678dfaadd881a": ["statista.com.", 0], + "5959d040e7ce6ce347d10be58eab7ea6e6a978be82c373969abd850e242cb7b4": ["fifa.com.", 0], + "5962df3ca9b99a194bb465e3768307689d73d1606e67c4cfe39c57cba2846181": ["taz.de.", 0], + "596682b40f1d9edafea9a8aadfb2fa790a57eac0710742cf92b7838f35e4ec71": ["justwatch.com.", 0], + "5969f357419ae5b4772b2302b3b7a807ecd47d5c28231118852acb358a8e6620": ["feng.com.", 0], + "5976e94685f2d95aa79d1c2243e548950d72973723da7ca58cd460fc90581b44": ["icims.com.", 0], + "5978e14d90130cbf35bc8c21c4e081bc9fc2ad79a84b9214e2812ad8e1122606": ["surgery.", 1], + "598cf8d80d69f32d8ebf7a0e59d12c6025e740013a928b07e25b1134b1fd0862": ["linternaute.com.", 0], + "598d1539fef34740afd520f3b2d930bb52a7f813c5092ad6a41130ed1f6bab04": ["westminster.ac.uk.", 0], + "599a9a6487727916143cb975572a217aedad6b3fd4e226cd0c422b51e5f1b999": ["cleartrip.com.", 0], + "59ab6035cd43648e5d023c99985da918b5461ededc1a1f7b9f8d388f844d615c": ["faucetcrypto.com.", 0], + "59acdd0501644278fc7daf2ee9f007c78d84be9fa12a86f1a892bfac6d7c841d": ["faith.", 1], + "59af1cc6db7fe9f9cb75e1aa1691475c515c708dd04f4166ce25731e4a506afc": ["grantcardone.com.", 0], + "59afe49179c1b7c66227dea2bb1d40072953416a7c943cfed5ee28932b72326f": ["yjbys.com.", 0], + "59b05286bbfa0313a3092c12d10d99a251f835a698ff13ca29b53c86281189c4": ["beytoote.com.", 0], + "59bcdd3f05bac4883dc3009d91f6214f9f277846c9f9bddecb18562f154decf6": ["mountsinai.org.", 0], + "59be1849c4b6b9142f1f6724edc8021c9c201db0f88454b7ca8a5641fa64b76a": ["autocentrum.pl.", 0], + "59bfd541d4b28494ee21b615f9b7e056f7750ce215b863d3657072a6974cb362": ["gls-group.eu.", 0], + "59c44c5610fa9ac62fb0c529ebfd1f0b5c64dbc9f28ffcad9fd002e491aa1884": ["uptimerobot.com.", 0], + "59ca65f327e35b9f2ca95072965de9dc17434b1b6d88f4f5694bf2baa1ef6242": ["chinapost.com.cn.", 0], + "59cc08a56f4121cf80b859378ca8667e7677b036514efa7dd9b50e44f2467b68": ["prisguiden.no.", 0], + "59cd352fa8d52e1dbdd8dc7bf50f0786c55ef7bad2966871f22407355f4b204b": ["prnt.sc.", 0], + "59dd2d3236ecf8d841d5a1c951af200b3bd8c9b6c2d64113287883b511b8daab": ["nbcnews.com.", 0], + "59f659c7c1e59d3f1dff72e57525414120310623cfb41622449dcef16c718ed4": ["sportal.bg.", 0], + "59f8d95dd5d76788ccd47c3026aa74b2a2d2af0045b842cc9b74d73a1d9bce5a": ["interbank.pe.", 0], + "59fc6fce26e8c842af66d28fa2a27fd41c47055c5159e5f21e33284f02384616": ["netbk.co.jp.", 0], + "5a012b974a51163c9ea15f543e5fc6ed670b1eccf6c4664c856a309f568785d1": ["greetingsisland.com.", 0], + "5a0751b3e9026485f623456a86ac1bbc320d68f68e0f2ed64bebf7ac3f1bd754": ["sonystyle.com.cn.", 0], + "5a0c89748a6141937fd993d0c6a05343dd07f38719d0cd3ca8b962b8bdde3eec": ["lc.", 1], + "5a1f947bca404551ee2484108202cb7ec7c316c6dbe4c692c0c150d90c43a5e4": ["worldpopulationreview.com.", 0], + "5a2c8adfcf411c0255b8d0c920aff65342321f32fc73d11fd011453a495adb28": ["speedpay.com.", 0], + "5a2f5756ca5f706d47635b50d7cee5b36acf4689b73f6247eba3bb6e14d0d95b": ["nairaland.com.", 0], + "5a33055cadcdfdf22e2b7c7e6dafc006874d9e3112d7d1a347070395fca4da25": ["iheima.com.", 0], + "5a3d2c58e5a52b6cf884ba2c1a22955ce2fb27e92752626cb00de56cee91ee74": ["linestep.net.", 0], + "5a49922c1a9126b8c6b307071ae0e91f4faa60cbf7a503923394b80da94ec170": ["lubimyczytac.pl.", 0], + "5a4d8c3502106dced8676d80186659a5a377cf2583570f45e4e05cb0698ea0c2": ["umich.edu.", 0], + "5a4e88094ddd7064772a3241e28102c0b233fdb5c51702f52f281bedb400981c": ["usp.br.", 0], + "5a5465f6df19a0ca447f37cb5a2f3a18e7872487ab3a5e41a84108389e1634b3": ["chordtela.com.", 0], + "5a59edf5e7a3ed4163ef89867f64d62d2fcc1365615b565f2ebc9884a0878937": ["avery.com.", 0], + "5a5da900435ee82def7b2b9bec4036a258d2d2fb403b0c23b796fb65114397b3": ["raiffeisen.ru.", 0], + "5a5e4bb7f56d0e3b55babfa6362e7c69b2722c568f4e3c03a3fe4aee32c25a72": ["gtbank.com.", 0], + "5a5eedae87d232ca6ed2499c1d0b980b75e05928871314bf6933859d64ce5c55": ["ptt.cc.", 0], + "5a65870701651cada52d0836e69b5b9e71c6ad2af491a7804d5265e063eb64c0": ["rarlab.com.", 0], + "5a6bec680687465eafd728204ca545e36c03b39e7ee841f712940b150cdb462c": ["primewire.mx.", 0], + "5a74a04cac0a232afdcdbe42e62945d11e0dd24d1903aa08577a5dd904dbb241": ["ssense.com.", 0], + "5a790ec85a01953cd87d93b9a25598991e233dbb3244d42119e3faf4b8e70390": ["doterra.com.", 0], + "5a85410f1a94d406587113b5e057eb3c007659483ef244c27b504d4361070854": ["rogers.", 1], + "5a86b12e0416118c69fe7f8de779bbd1c25eac04f1bec417293b68540e37e85c": ["zad-academy.com.", 0], + "5a8d95f44a8614c01a2acec4deb6fd14aa965f49a70c2a792ffb1a007f0499e0": ["redd.it.", 0], + "5a96ec486be71dd1646d0224420d95e65212920f1fc89647e515b8b66f9e2564": ["urdupoint.com.", 0], + "5a9bf825535ac5c538073c8d39fb549828328da052c1c8203f1d76544b11c739": ["editorialmanager.com.", 0], + "5aa1aba8794341d42d78d7b0af0b134dbd3aff7cef5bb19889424d2a072a78a8": ["51credit.com.", 0], + "5aa3debf66f9fd36cf20e97b9afbf5a0f45302362401ec32706ac98451aaedce": ["tipaxco.com.", 0], + "5aa7f91ce43ba0b261a8aca5743055eaa322a13911adfa70a72896f6bf9aa878": ["navercorp.com.", 0], + "5aac5c8744080c02b3a5f0119f3bc30af07989f8c4b2f9898351b1fce5f33f6a": ["emory.edu.", 0], + "5aac9975a3776f1febc2997eeb34f7e371734e2781cefe690e0c763aa867aba6": ["markt.de.", 0], + "5aad70952d0fa5156f76c2d19f08704bde8ba2df3fee4915b454ddb7966149da": ["rlsnet.ru.", 0], + "5ab15ec482437d72f95f06a6b9796636cb871397502a76bfed14ae85c7b6dfdf": ["cars.", 1], + "5ab5278123b0e1cb7736bd9e992da0d9caf960c4735967a997a15b0915c234ab": ["zola.com.", 0], + "5abada8a1d3c3167e5c231ab5d8843ba5fde0d3900c175ac0495ddc38551dd3c": ["flvcd.com.", 0], + "5abc0e4e6d0fbfb460cd8e8fcad6f0cd254f5240c10f93636a147f78d3cd110e": ["workspaceair.com.", 0], + "5ac7879131f92a9a4c8d5b040eba8d7a1f370122b32a080bfad55f1981c00b97": ["aco.", 1], + "5ac9c13f0fa244a7c70d7ff5b283ce141909cff1b5da6a7b0a31361764b00dfb": ["infomaniak.com.", 0], + "5acde36825ba5074d339d55c15cf283a5a9e4846dc0f2e70afc2812545d3ae88": ["camscanner.com.", 0], + "5ad7bde425cff22a660d5dbc12813690b13e4f48559d154add9443f6046dbdfc": ["conflictnations.com.", 0], + "5ada69fa74656a4d6ce58d3186380e874a5a44cbba57fea5540329203f913d01": ["500px.com.", 0], + "5add885b1b80cb2b7a0d3323cf281f878c6528ca9eaea1315f918b9bc916d120": ["tiendanube.com.", 0], + "5af08af9ad40babb820804aff8934d1746c15ad0e3177ebcd40c8416d81daa6a": ["adlsa.gov.qa.", 0], + "5af68bd970a8d4f168b9bee28cbe2ef372d62ec165083a8cf86c16795613743b": ["arcamax.com.", 0], + "5b0ad3cdc41f9641f1654391e95b475c1b65d83ab6e8c1528f16b24ddbedc815": ["dto.jp.", 0], + "5b0ef8937f1b9f854b97a08f7af5c84fabe6ff33b6cd81d79a4a1bd859f6c9c1": ["bpjsketenagakerjaan.go.id.", 0], + "5b1031b1c1cb6415a289bafd01bc12a5831a34caef86b35b736985cb29c804b2": ["paloma.getpaloma.com.", 0], + "5b1172aa8fdc8526e4846d067c8fd7a84e3dae4b7184a376dd6252dca0a5609e": ["zooskoolvideos.com.", 0], + "5b11e4b17eadb84fdcde836ec7f267484847276cb4692d6baae3d2e7f1a460dd": ["xn--y9a3aq.", 1], + "5b1591a4c7740f76231e21176dd92916b57f0f4bddad55e1e71ef0969fb56f60": ["helsinki.", 1], + "5b15d216e65c857f6449a0d4de3274663273d4737ba1cc1125d0407c10ba3623": ["ardmediathek.de.", 0], + "5b170fbea23420e0a78729bde6e5ae2a3a68a9d947c4c7611e3a10021d6f52e6": ["s3waas.gov.in.", 0], + "5b17b2b5265598dec17b542875ced48d980b2a60f3ae8689d32fb5549ed7abcd": ["ignou.ac.in.", 0], + "5b1bc2e9067cc64f4e9f8655bc2faceee7e6248da6749ad966003931d3bad38b": ["cafef.vn.", 0], + "5b1dd7740b80dbc976aa4ca2d8bcd45fd2942adaa821297280e425b08af78a7f": ["sussex.ac.uk.", 0], + "5b22358c7ad4b0ac1f38b8974686f24b51f0537319d7878d0b4ed995dba09dc5": ["labirint.ru.", 0], + "5b22a8ae428691e7c756a39d2dd46916d2447718acf4acfb957a056cb7c273c6": ["watchseries.id.", 0], + "5b25c186a47e98f0afb9b861409d1d6ecfb37a51da67d289fbf58ac2923af372": ["asus.com.", 0], + "5b27cc128983232cf92e28a87b7e4d82f07f6f47c9aa9c87a8b966be355eb54e": ["techbang.com.", 0], + "5b322df40fa8a02bbfa0273d4e25ce25e358fa746e5b7361cb924275e31b579b": ["pttweb.cc.", 0], + "5b35e0c6b7471c0f7d6e29b495d4e158a263abcbfbe9446f7cb51364b74266f8": ["uisdc.com.", 0], + "5b3b225e86331f24a71bf5539714c4d31d78906c52537f75de2c60646a9c0507": ["btl.gov.il.", 0], + "5b3d1ad9e17d62a48e6a6ea8b93996000838cf6a33bd4dff7c738f0d250e1fea": ["ciceksepeti.com.", 0], + "5b41e731aa614a7e061d26dc0b4d3b76a9a1d70b552207f6f32a130cfc018d8f": ["dabur.", 1], + "5b42de84e7aac57078ee40322c1f702f0199475ec229d71717b2098ef8b7ebed": ["ebserh.gov.br.", 0], + "5b431e555261202765844da8c2044975639303bf713c635a4d11a04be527795d": ["adam4adam.com.", 0], + "5b4548bf50de62e3bf9596769d46726dc28df14c433e1c4579c186828220f969": ["shadowsocks.au.", 0], + "5b49abab5c64e6cafd6e02477c294bfe02d530d65fcd6451a1a73ba9203028f4": ["xxxbunker.com.", 0], + "5b4bc285bfc8a445cff48b6001af72c4f8c3b39a7a1a91837e37ebd805b8ddfd": ["glavbukh.ru.", 0], + "5b509370903ed6b1815021b4280cd620cae93daa7a167115ba1b2a397e486dbe": ["amd.com.", 0], + "5b56939725a1c693bce06fc098fec812ba83f5d3eb53861656944a9ea935af7d": ["traffichaus.com.", 0], + "5b5973dc5c3ca2a5e89a59af141c7b353bacec1e617502848830633ccd021ff4": ["lumenlearning.com.", 0], + "5b64bd2d3503159862cd46dacf9be5fc9f774ed940c47807113c227b9cb6ddbf": ["doordash.com.", 0], + "5b6efc4fa5de1c02e579901b5e3b730e80d9599fd40b5684454bf1d4a09adcef": ["pypi.org.", 0], + "5b712663aed11e644b6643ac34be6d8704c60211cd56714e0644266d810b5522": ["tennis.", 1], + "5b74060f2bc9e74131e5608758ca33f11ad0f984dbac24ca463cb81102894caf": ["halktv.com.tr.", 0], + "5b74269716b1da11c5515202e97de634cecce4039d435cd309c4a1795f2506ac": ["lexus.", 1], + "5b751ab70277a8c63818418d46a08af89cb02eb491e87029c0d97b965f46c939": ["caliente.mx.", 0], + "5b79a89da75539f2c10c41b963a0aeb37776ca87c6148ce6ceaecc0117131b41": ["physio.", 1], + "5b7a4d1c7a7d4919fa4b53d4bd690e83bd561112a0b3baf492bd4e6b6276c819": ["cna.com.tw.", 0], + "5b86b781010469614a8c8e70e6f645c215b700598434e30e0f3733a2e9796ec1": ["backmarket.com.", 0], + "5b86ca855c5cdd7db599f599f86c3d92a85b2f27e163dbf9e35d321314fafbb3": ["travelocity.com.", 0], + "5b8853973e8348f136cb5827a919b72597463286915881d62b444f306944d9e2": ["tnt-online.ru.", 0], + "5b8cc9a7145d71762eafe92b4b148479d169d37fd021083bd92f9a0a597de1c2": ["hyundai.", 1], + "5b91d9226e6b19a7adb7e47cf00ed6061e609068999075c2de4b5cab7336154c": ["newindianexpress.com.", 0], + "5b936c0b60f980b9de0a7a71feaac422f0ee2bcc58afc95c046c11d196322d80": ["expressjs.com.", 0], + "5b952eebcb088b0b7cf8806f190fb03dcfeba13439d197ec9b45f682796aff99": ["ferrari.", 1], + "5b971afe7a3f1f4fcf9cdba58215c6b38006986681ccb57bcd69052a69467645": ["blockbuster.", 1], + "5ba727d298306863fe0e931fba0572d05360b9e546399ff288761386b93eddec": ["malwarebytes.com.", 0], + "5ba841ee2f1adda1af6464dda7bb5b26ef8c6aa035b759b056121b88b889d0ea": ["queue-it.net.", 0], + "5bad575e831db9824b4612a30e42ec76e22db9663abd21524b43067557a79bea": ["cloudways.com.", 0], + "5baf4968477583c678e360aec32f2339a063f7457c27a5984a65d7d682aa0c03": ["relaxbanking.it.", 0], + "5bb38e6a721258a37490368bf5ce02038a06c96d996524d0a09b342d64d57c0b": ["baizhan.net.", 0], + "5bb59b7d1334a05be4d37309a1f3e74860b741181f6a24acf81974cd2c282ea4": ["netteller.com.", 0], + "5bb65a71885e20acf4bcaeabbfd7d9221e045b559313462464ae238a66197b35": ["julesjordan.com.", 0], + "5bb7250ca49decb55a3163db773f703db82684b6841109759fd0ab39e5f98e63": ["moeni.net.", 0], + "5bb7460badc21a333b5a587f33ae24ff66dd15ff981a29ba5e6c62003fb103fb": ["jma.go.jp.", 0], + "5bb7c612567c98dd72ebc5e56c5f8c16b1ca4293964738047177632cc777534c": ["elementor.com.", 0], + "5bbb8d4561f9a9e99627a972057282852664fb408b77761bf6b5e3dc93487213": ["shoptime.com.br.", 0], + "5bbc5c31ac51594a7e9518982af6cff25028bd02b9c5a19a461883ade68e4e85": ["bbc.", 1], + "5bbf8827d13f77e6341bc7044a8d4b00fbe62866fbd8d3e28e55ae55af621c70": ["shinhan.com.", 0], + "5bcd407ddfe433dc8565cf08df32d99ce4e54c9626b67a8363841f88cfa1b7cc": ["marketwatch.com.", 0], + "5bcd6455ce3b317123114219991a9ac73be7f79d23b9c81b38712658971d6be9": ["footmercato.net.", 0], + "5bda1bd3fde8a4d6b48f747a451afdc7994ef175b7bce48ae4ae2adcf43aee80": ["eroprofile.com.", 0], + "5bed4226e7a85f0d5475f61fca985d158e67801f769f707c368c0b65bc903732": ["tistory.com.", 0], + "5bf28287363222461c022ba6c322cef068f0a1a5de5fd90ef6a569dda9d51aab": ["avon.ru.", 0], + "5bf522d81beeaa059081324e3c5d96422d0ca9e582c3223cd578948349aa1e9d": ["anitube.site.", 0], + "5bf974372af1012c4339714789ee13e1fcfa894c54999c260c3d5b6c3d537763": ["tu-berlin.de.", 0], + "5bfa7dd54ec83ca59210e387976075a21403175e47b5774aa6604d6bf51e5c72": ["french-stream.gg.", 0], + "5bfe32549640babde2a99f165d705cb599fa3b11f103ebbaec722968939826b0": ["pmcaff.com.", 0], + "5bffd225f74c694026f7a20dd58ded2d3fa2bad9752622171202467787a2af4c": ["pbskids.org.", 0], + "5c0827ef459e1c171434de72b2f76f0a9a821c7e287039b8eb7b2b5136b7a4c6": ["malltail.com.", 0], + "5c162b15a8d67d314ba81151207554d06c5b29597a8471dcc9c56614d902c642": ["je.", 1], + "5c1b7404d34d7a6497a762165fb1d8158c04ee0f9233b6d8b33ea91180a7a78d": ["hamshahrionline.ir.", 0], + "5c1cf16b10b2f4c76d294331bbe75774f565b0f1017f690655ccdf76b1d3dd8d": ["xn--vermgensberater-ctb.", 1], + "5c36f4b41bf583f38470d65a481826f0a54e90171e664ef30e1c804c010499cd": ["limetorrents.lol.", 0], + "5c3961a5d2d4e8565f64701eb6cb11e984009044b7ae3f8111f3ed49058de6ee": ["usm.my.", 0], + "5c3ca7be33f298fcc185b622fa491c8ae82c9756fcd31a70500ce5a46120e84e": ["cc.", 1], + "5c3d265e10507ac1999754daa5859af645ed67e2f8cf6bd65f062b1d4df7e5b0": ["nrk.no.", 0], + "5c3d5c588e68d5ae1bf200c0782d12884c939f3af6eb4f04a5061e895ab2723a": ["pln.co.id.", 0], + "5c42ed5dc5a4b05bb1a8c9778f151bd6ed25e118fce3871152152d8dcfa64f95": ["beforeitsnews.com.", 0], + "5c43f62e9e9be544c779dafcb8ccf7730cbb576de9f314f9eba2ba1d9422cd75": ["hashnode.com.", 0], + "5c49ee9ff99556d0c4d765e43baa90b651add0e1d2a4821536f5a801e969a924": ["chemicalbook.com.", 0], + "5c51ed5748d5c12e3aa8a0620bae4c6085401487552d02e5c2233e63d395b451": ["nau.edu.", 0], + "5c530e0a54fd517858dbdae8480770b9f74d708a0bf96a3fc89bfaf49fd8c412": ["upsconline.nic.in.", 0], + "5c5be2f9697979e3f6db71630add6d80120ceb9e7f929d34bf17cf375f0e066f": ["shindanmaker.com.", 0], + "5c5f94298dbab7efc1ef99b21fa86600ffb9d25551ac1a5ed578ce2ceba20c51": ["blueletterbible.org.", 0], + "5c6e1cc08eb0ad4988c5308f361f40cca02386ca907bf6f8ef2f087e5f711fe0": ["lapatilla.com.", 0], + "5c8a7d8a5ce9bcb417d19f8e5f6d0a7a9a56bb3b28fd7d3badf5521ed018d0dd": ["yurticikargo.com.", 0], + "5c8e3804f145ab7d482c15063b26b785f0a6e0975ba71b0971f52a81f457a7a0": ["yallakora.com.", 0], + "5c8f45d86a3463bd418e53a234fdf193bc73a71bbb8ea7e313abf2868c45f4df": ["meaww.com.", 0], + "5c8f7921b523160c63cb7850796e9990cf629b6d8f7ac34295bde40bf0307d58": ["cra-arc.gc.ca.", 0], + "5c93499279faea76e769676b80eb4d8a18208f8a400d57d54cd5ddd53a46ed60": ["rockcontent.com.", 0], + "5c940e38fd9bc4e967e2f013cc7be9aeec015fd68b4562ad02d26008c31c32e5": ["mintmobile.com.", 0], + "5c94796169ddf86c0c3774de127586a85e127f772b208b5c1d0bd87409d9ddea": ["freetp.org.", 0], + "5c9c8d233713a98d62534848d0f13ede1282d8fc488a2de709af3cb9c73bb1c3": ["nia.gov.cn.", 0], + "5c9cf0d39e5765a9a4ae753b30a67bc116e812a515bd35aba54b42d2bf1dc97d": ["sheshaft.com.", 0], + "5c9e482ebbe1eb4cc004245df7e9324869f11702569ce525b4e2031898d90af8": ["freefrontend.com.", 0], + "5ca50b06be31750289916ce72dbd7e109be9aec3c1bdc0af80412dc2a468a43c": ["bitnami.com.", 0], + "5ca7e9823103d0c6729d359c0a25664c0a8083e18ed893727158ea2ecf2f66a8": ["bankmellat.ir.", 0], + "5cb39dd3c25e35531d5e1e4d631a3b59598c09b21a6a7707a58cf4f984635945": ["amap.com.", 0], + "5cb4a384ec6825744f8a496288faeab2a56a34f63bb1ff59849383bcb98105a5": ["eporner.com.", 0], + "5cc0d8206a1a6b680b3aabc3598c41d06fc2f0637801e0cb399968a53ffcc546": ["teads.tv.", 0], + "5cc1c4a2ff182d0ddd564072d0323aef17a75ddb459b9aa5bfa5ac5b57f58bae": ["store.", 1], + "5cc79badf19d7fa923f338f41aeb4137b32caa0c0dce262f80faab8151f8bed7": ["alura.com.br.", 0], + "5cce6c502c2b70da8ba1d857c8b83da4b87cf136414f4aae36dd167e5ef57b2d": ["sarkariresults.org.in.", 0], + "5cdafd3d06adf141da7a608e3d11f8e03c5de3b1919ffbeecd5432201873b0a7": ["xn--ogbpf8fl.", 1], + "5cdb6e3f8d0ec7e226ce2981d48475fe58f3003db7bcaf72fc728980fabb9763": ["deepdreamgenerator.com.", 0], + "5ce87e0f9143cb13f9799be4efa3cbc61bacdb2d85ee20101bbccc060fe48fe1": ["rta.ae.", 0], + "5cedf1200a9d3bafae39a30fd49ebf052925ad14064b7ac083581fb3aea5cce1": ["doujinantena.top.", 0], + "5cef0cc9964ded67659efb464d758667b11c403d66909ea0f33f9444e6aba6ce": ["clipchamp.com.", 0], + "5cf12cb294e453874ce91b5d388891d57666e28fac00a5ee7c794bd3582c2461": ["construction.", 1], + "5cf163acf0e80ad8a9d6c4535088c1a03cd94638fed610dde83d540966c8804e": ["mixlr.com.", 0], + "5cf979b45c305533f666adac46df1254b3c684ea86258e0eb393a8211b7298e2": ["christmas.", 1], + "5cfdd4aa6cd45ad93c7433616cdf34238144667528127e1a22913c7f2c028c94": ["nameberry.com.", 0], + "5d055cb0da5216b69a3a09795a4647f7789c194533ee651bdf50475c7bdfb86c": ["clothing.", 1], + "5d087aaf87c9ee2058f217f4110f224477e6f8ba400ffa2aedcec07f0e0f2653": ["newatlas.com.", 0], + "5d134fb1fe333ab3cef97a56b4c612389648f70ec6a1a0db85311c6faf9aa855": ["banamex.", 1], + "5d14e3ec947512c699e58c482a3052a501258e785d7095735c7b2b523ecbab8c": ["casio.com.", 0], + "5d202a0d4e0c487a4bee12699a731be1ef3aa5401a5a24e357fc3712da2ee8d6": ["versicherung.", 1], + "5d251d08a7b258f4148ea7df75be673d0916aa38fa6aef720d156abdf9184b67": ["skidrowcodex.net.", 0], + "5d39e090642d6b732c2072a71cd71e339d9152a61f679b7dec2a93e9c4192de3": ["thebay.com.", 0], + "5d3b2518bdf31062bd06bd642e8f2a2ca94c92de6487201c3c59a2da6de1c57a": ["starmerx.com.", 0], + "5d44367f25b21213128644a43a94b9b6148c7fd5d239e2b0cfd563aea52c6409": ["merriam-webster.com.", 0], + "5d45ea1f5e49bdb9bf208b0af85b370837f0e6ed27f60c25731d4f9757e2e727": ["cumonprintedpics.com.", 0], + "5d4a51c514a5dbbb56d6d653ab2d1348eec44896f3831301d963746514042bd0": ["jorudan.co.jp.", 0], + "5d577a4242f863a3063eea9edf13881c9c94b87d7d28816a0ec71520079db964": ["gratis.", 1], + "5d5f100b9dfd20094b69f97b8079eadca66cf3d56276013632f9ca8c9cd7b6b0": ["supercoloring.com.", 0], + "5d63ab9bdc2ba11a153458d4113d043e2ead9ed545c7a6b83fc4849916d00324": ["xn--42c2d9a.", 1], + "5d63bb3cdbc17be214c682e482489a756e1305220b44a1bb02474d00f9e32784": ["stackexchange.com.", 0], + "5d66d5876c7ec0ea51155847765269463e19d48f8e9a7054921482342e9a7e11": ["carsales.com.au.", 0], + "5d6778950142a903da4d5b26007ae8000294dcc53abc89e67e2de6b9287946d1": ["total.", 1], + "5d67aa27b80145b9cf233ffcc77d23ff404a10ef5848a6a36a3df79b6bc1a308": ["xn--mgbaam7a8h.", 1], + "5d69eff851160931646627ec61b30b5bdb94f6cf730124c3f0fb00f56499b1f1": ["allmusic.com.", 0], + "5d76d14297d5086b1a4a67a97c0597bf4d03ac1733ae81f2d7a983c888644ce0": ["naurok.com.ua.", 0], + "5d7ae40d69e70c7e5751385cdd39359ce2efb2c1dc84a511feb760d3ebf4ff6e": ["xn--mgbpl2fh.", 1], + "5d7b7d66c32d1deb4d67f6dc3a704f9fd2c49fd1b5c77a46f17bd56df274aa84": ["digikey.cn.", 0], + "5d7b98adbf6a71f94dfe65e4b791413b2701ad525c54abadc53d28f4b87cc4e7": ["voobly.com.", 0], + "5d896842c54dcd950b75b4c4bc155c8a9d8c012f3ac20eecceefba84b0a34dff": ["totalexpress.com.br.", 0], + "5d89c530c202257fec9fe6066e1d3d1bda5ae03d5545f75b0db92496ea9cf0de": ["hlj.com.", 0], + "5d923f903d09c33e31452c5ff35cc368221f90dca46bc85b178b508b666079a6": ["neustar.", 1], + "5d93a31427384c503bcb9605a1f6bb05c0188916f00df4ecead7fc9ae254f3d3": ["vitacost.com.", 0], + "5d94944c609ce02c095c2dd7faf571c092bab4727ba2483020ea70d815b700d6": ["film-like.com.", 0], + "5da3dbe1f0dba3a7d8eb2a7ddf501809363431d9fd23e9d171fc48ea3244935b": ["blogtruyen.vn.", 0], + "5da71546765b24c18061ebfc44782d3c0da1f6a1fdcaff95df7f78637a8a65b7": ["sucuri.net.", 0], + "5da9b51a901aadbea06dae641b744b47f5fe1596b9cd1cede50d058e51773965": ["16888.com.", 0], + "5db266cfdbe8d3f763420d58c3d42d6579d39e242497c08e8ccf2f4f51adbe48": ["mubu.com.", 0], + "5db42c3739b014adc66a9c0c98223d33b0c35fbf85d386f9764e04b02f47cbbe": ["proz.com.", 0], + "5db7c3b6f3353560b3e0e526bcd9dd2a0509e0d09852c0efff5f58d5b80e4d6b": ["mixamo.com.", 0], + "5dcac7c48ae45dd914e5d027c59c3ebecaf3b9e1ecedd604feec444baa1e3c9e": ["pushsquare.com.", 0], + "5dd4dafd0b0626a03f282cac959dbef44b52afbcbe15c60a882986963b533fc1": ["bolha.com.", 0], + "5dd957b733b44c6ca4dd20aedd3776bde71c0939d78b2db3836acbc245bd3583": ["tablesgenerator.com.", 0], + "5de009b6b0463915b2dc4ecb275ac565ee1fff8889ac58ddb6023818b2639875": ["katestube.com.", 0], + "5df07abafcafbcf4745eb5e04d269709304a7617fa5ad345c7bf965ea220802a": ["alohatube.com.", 0], + "5df13bd9bb4f856a901b852221318494eceaec9af151559ea3fee26e8d35f3bf": ["takealot.com.", 0], + "5df393d8c4fbcdf10b8bc7701df4e9bf4c5593ea648649fbe8036a6b857524f1": ["pcbaby.com.cn.", 0], + "5df8a07596244d0ff2aa642aa9327f592d017d588f4a1a1fbf85952270398375": ["gradesaver.com.", 0], + "5df8b94e668bf99e48ec02483b2cc916a598b99872f1803eba67360477818ba8": ["walkerland.com.tw.", 0], + "5e147e88dbaac1083fa92c09cd31d49eed689f1108dd6d987c52bdc7c0edfeec": ["hccs.edu.", 0], + "5e1ab8389ee33fdda2383707e4875072ff2d5584d44a65e71556806d54b4128c": ["midilibre.fr.", 0], + "5e1b16e99d9a17a0388bd482bc37a017daf2d9820d5b9f61754c0cda92075e11": ["pixiv.net.", 0], + "5e1ceed94df6161c4e837341134cb278acd7a35901ae9dcc97641116a0920f9f": ["tiancity.com.", 0], + "5e24a06b64027ccfa2af1aa37526dc246cc824d9bdeccc0597638c01367d3c26": ["eitaa.com.", 0], + "5e26fb3ee9c4ef58277ea36c6c98112a5e22ba0a3aee6a671302d1f9aaa031ed": ["brother.", 1], + "5e29489423a9a4210120ef88fe2201a8aecd2de11010d7bd434beee2d1c40740": ["worldjournal.com.", 0], + "5e338921151d4bdd1f45dd519885fcad288003d0d0443f65d4c7f611e9fae741": ["smartbear.com.", 0], + "5e359db781d93eecb28001a32514773514c43b5ff2d6e81db1e2c04207d0a041": ["mgstage.com.", 0], + "5e35bfc4431575523556ca3a3c6d40e438ce2ff1063b133db2b3b7094bb7446f": ["imerat.ir.", 0], + "5e52ed8329feaa85545ecd2ee44c72899d6875b0c1f25e0f5d1bd003984f001e": ["indiapost.gov.in.", 0], + "5e58af5f13a20a50b96ef1747bacde8100b99e7ac08b8bd3e40dd96fcd78af8d": ["express-scripts.com.", 0], + "5e5cb942cc55564462d23fd993c9c7feb7772be779c7fb0a04350aa823f5d15c": ["engineering.", 1], + "5e5df5ecd13a54ced290d0bec4d0c66734dbc353688a1c0583f2bce9e16921b0": ["edsby.com.", 0], + "5e6c4450866c45009d5945c86dc1e77f40573eb1210b71d6eb259af08d1ce150": ["dillards.com.", 0], + "5e79616e76c702f15d7d540e2e706ea508bc1e36b775be2a7f256166c03f639d": ["dnaindia.com.", 0], + "5e7d9e3a843fae663a3000257898918040608c92995f51734c45677ee15a43c1": ["4399.com.", 0], + "5e864a392afa02f91fd8de1fbaa5d6574cb55cb85f3f258e8c492a0d3c4535ed": ["samandehi.ir.", 0], + "5e8705ebac76fb301eae44929f783e754082b192eefdee1f3a447ebc6c2844ef": ["webmotors.com.br.", 0], + "5e8b0144014788b6467f5fefac4873aaec15455ff5f146d68771ef9aa8908536": ["gidonline.io.", 0], + "5e970a54205f129f53a8143d24d00c9425ba40af3cee32f9e87c9670e7785235": ["nymag.com.", 0], + "5e9a4ff8b8603dd25e48bb515409fe8a60e26c77f4015891284e9c78b3757d46": ["teepublic.com.", 0], + "5e9a534696ab7bd7673104be5a49bb3756b14a0a1d9e7c37468ffe454d595f3c": ["pressreader.com.", 0], + "5e9ad1357f10c8c0eb83ec7cf2889aea2059baddcbb6f15ca52e64b1d66166cd": ["artlist.io.", 0], + "5ead59f1851603e648589188c426c5e00c51ed2bda011fb45fc8f29f0bd355a9": ["xxcb.cn.", 0], + "5eb37d507d45018865606f0dd42fce3162862ed21ad2d5d363c4c63db8122554": ["express.", 1], + "5eb5e8526e8d77005862da945a3d17b2dd62a72420475e6e0304f039568b832c": ["antiplagiat.ru.", 0], + "5eb8e4dfbba9477dff70643e5710c6f26aba10bd7d5340188275a56ed139d315": ["ensembl.org.", 0], + "5ec1cdcccfdbbd86d3e1378304362de533c9589b9cb7224fda0f6d0ba1cf502b": ["meliuz.com.br.", 0], + "5ec1fa1ba1afa14c152012509618c8c07530d0ae07098ed83c85afbf0bad9ede": ["educity.cn.", 0], + "5ed32a72fff1ef54db972530189eb95d6bfc32445d1ac6bfdf6d7cf2d4dfbd60": ["inven.co.kr.", 0], + "5ee6d04774f9375432d552f3c766e3ac95d18ab61b2af619daf082732174f641": ["icon-icons.com.", 0], + "5eeb04b3ac0a60be8619095319e300c9a7ec6bb93d7ec3b3e0d1f62d67055fc5": ["oecd.org.", 0], + "5eeb1524d554fb443c8dd70e2b6e922d3dcf80a2e4a8f5279ca0f6ff970eefee": ["comicbook.com.", 0], + "5eeba97aea42755a013c71b7f409240de6ce4c2ef0a0ce85e70e3f21744c4044": ["filestore.app.", 0], + "5eed69f140ff1f5105af048903c802be084c078afd0553f5db5de23151b0f7a3": ["asic.gov.au.", 0], + "5efbc1097dd8288c10a91d47d43ce70a6d90d9a44218db7e681550c91e2095cd": ["exblog.jp.", 0], + "5f009d5274dbfed9151c6779c13dac75202cefe4706d5732b4f81e43a84fcd3c": ["retty.me.", 0], + "5f07b2869b7cac4d315807166e2d8917fe068e483468267a7021123077320098": ["dnp.", 1], + "5f0a48cd3e1385d1687fb9eb026be281bb2b8c0898cb08d77dbb5a49e5e7038a": ["xn--mxtq1m.", 1], + "5f0ac06391ac3054754cedb841b9204bed3a3bae89759437fce159694c907dd1": ["aero.", 1], + "5f0d64f9d48847f95b9840d7191192e2af442eebd4a8f923fe343d7e52e709d7": ["hindawi.com.", 0], + "5f0f934ccde09f93c094f70b870919e066a554f1fff99718c4e2a22b77da5575": ["wonporn.com.", 0], + "5f1389d35bbe3c9e2d938b425d9802b447381783ad7944e67cc1f06adeea558e": ["calcalist.co.il.", 0], + "5f1732bee51071070888838cbd736457caaceb78393277db6b4c2ecc656fea2a": ["eways.ir.", 0], + "5f1a62762770f47a1bbf58a8e2c4e838524b941481d39d61414cf2c347a04a01": ["huyosoku.com.", 0], + "5f2494b9c28964d5d36613126926554afe17737a8e259180faa42f9a34f974be": ["humblebundle.com.", 0], + "5f28b4eb2c555036f0abad93121b2b2ab7e1ef0c722ac565bf3b745b445ff2fd": ["hkt.", 1], + "5f35e23a0840ad5133290bcc0d53ab00531b248f421a3405117258ea158309f7": ["saude.gov.br.", 0], + "5f384a9f8f4afd07b291b407a24368732a204a7881eb53bd04bd06ebcf4a7f69": ["enuri.com.", 0], + "5f38d8887030f97c24243fc29beb597ce20e1a8f005fe1f2cef5964d17786851": ["charity.", 1], + "5f4318a37c9fcacacab441578350d736b2376bed879a5962a12d2e0673a138f3": ["suda.edu.cn.", 0], + "5f4b8684bd434c69be6ef4ca99cb04068a66c5da1e0b273fd6f07847405818ec": ["mortgage.", 1], + "5f4d53c48e4c6cfcc7236c16557b145c2f3fbfc872ffbfe280772449aa32505a": ["xn--5tzm5g.", 1], + "5f57274fef542db38ddc7519351503fd43e99e7558638e2d926755ff9e1aeae7": ["mercadolibre.com.ar.", 0], + "5f5a17c01335469806cbe3ac3ae4a396f9385e9baa16cbe5412cd11b03c50d38": ["app.", 1], + "5f5b0be20f0a1c602f03ae735304681afa841deb8baf84d25e27fea707a64ea5": ["woot.com.", 0], + "5f69f4bda2e6eec6b055ca22d5838d538c13154166aae3e3cc8b994b332f2291": ["thuvienphapluat.vn.", 0], + "5f6aeddf64dd99521dc647ceb74e319a71114e8a3925d23df1299b6e2698f484": ["lalafo.az.", 0], + "5f6b218f26f36389489ee4b457e86c97a7c691902608c5fe8e5c9a3f5b89f7c2": ["3dmark.com.", 0], + "5f76a3309348e8bcf4f8a9c61a61d5fdf4f973f518fba9b000b1ec8db5255c08": ["otonarisoku.com.", 0], + "5f7c1e899c5d8c2b6dee5e0605cf26024bb8263b2a26684f250dd5cc8f92ef2c": ["party.", 1], + "5f8996f3f213f9fc7b32e8dbe8d33fc40be2e3bfbe7736fd3700a02a3f90c337": ["cbn.", 1], + "5f8cd564fa2bb01f80788dff38c97a78ae714708b6a2a7322576a24b2427693d": ["wallapop.com.", 0], + "5f90cca177d3f450a01cb4ff5cb17c3dd172832a95e2549a0a4e90e3c9cbb8ca": ["careerbuilder.com.", 0], + "5f9170232aef46c20229509332b9495316954a3a11dba57b76cd4ed8d78bc34e": ["gameforge.com.", 0], + "5f952a143ed83f674ce15725be9e16d251112cceb74f1058b211386c05f1b074": ["afr.com.", 0], + "5f95e9f2bb1716c871264843de23448f02566ee2f3ddf3d3c30866ef4797e4bd": ["calculator.net.", 0], + "5f963bc8433701041fe15c9033cc7efcd67ff19a8e4ef70e7afa96042890e014": ["wfonts.com.", 0], + "5f968b5164bf785872fc07a38d77556170fdb3326d87449e1885bbc834d25219": ["gtaboom.com.", 0], + "5f96e0e7da6b826a0bc2e36ba57fe7fab9c9449abd47ed198688b983c7caede9": ["winehq.org.", 0], + "5fa62d7b11cd51c00fadd2b83f6dc1ff3c7ca5d8339d1da5e9b8bff550e465c2": ["num.uk.", 0], + "5fabd60a0b65069ee8d6928d9364d47038b0eaf4f05cbaef67551117fe2ca44c": ["mgmresorts.com.", 0], + "5fad20b79e2864a9166a27d9958bd5c89dcbb48f9d023db76b31c8f0bd797d38": ["weforum.org.", 0], + "5fb54fc8641d67e1afcad097c26bb61fb80356aeca05c6d5d77644cfa99ce7b2": ["civilica.com.", 0], + "5fb708fd282165f3a55c9581087a5f6b70106bbe35d5df53891742ef59503728": ["ahram.org.eg.", 0], + "5fb94b23b5857e59de094e847061810aa1d826d9d32b37024bec7abc1c2991ed": ["bankislam.biz.", 0], + "5fbc4a8a064784f6536f2b59690c2e3da606dc9a421e27d126630bfd60ffb28a": ["otoy.com.", 0], + "5fbc4ebff06b80fff458dbec137b47f985910ede695fe0d0003b256db6760ad6": ["linksynergy.com.", 0], + "5fc96907020d40c0d85c586cab72cdd23e098e5b27c4475417736d214df3ef7a": ["centennialcollege.ca.", 0], + "5fd1d5676c057389f9b74e34fb65a1a713dc1c65ce236b0c3a5025556dfab62c": ["erome.com.", 0], + "5ff47d98e447bf8f7d260a9dbe573d1a3d6483d212fd74ad6e13465b13c6bf41": ["animeyt.es.", 0], + "5ff67a21afb383d1c80d52ed61ee91c9211bc85a99028c2e08e1a12a0089b38b": ["onvista.de.", 0], + "5ff6bd9883b7de33a4b2b66afb673c789ac96203a5fb083735faeca22ecee8a0": ["woda.com.", 0], + "6001bf1624905532dea18829fba636afb2a711c76cedd9e4883829733430c59c": ["instamojo.com.", 0], + "6003cc59496e32ded78cf0cbb2c6c3fa17ed01a5a5279b37f60ee8e79291e1b1": ["kununu.com.", 0], + "6004c70bb91b64546e46e0bea767796697e7aff2c38fb3ea1606c4d398dc63d4": ["irancell.ir.", 0], + "600ad6d858f0b88af86c2d216f79d8985f3e084f2a6e88d49e705d7b7fe4cfcb": ["surveyrouter.com.", 0], + "6014ccb759597c40fd7a8382f64e1c9cd3d343e2a9c12e8be99fcac1bb48029a": ["bluradio.com.", 0], + "6018a14ffbb78a4247d6f97c559b17eaab1c1d87f7bac2a395fe767a67979330": ["ltn.com.tw.", 0], + "601b4493952a528b5b5e950f6aff5fdf8893e84a9444a19d0fb39efbe885c095": ["cdb.com.cn.", 0], + "601c1f29541278b2e0294bf5d7e944bb37aef481968331e2c8c7e87995dc68c0": ["marja.az.", 0], + "601dd048a5d2feb77a3420573c03b556144b7f00f99c0e0ef127e3ff6ee4b62c": ["photoacompanhantes.com.", 0], + "60218e9fd772eb34c7d15f13921acf5fca1ec31424b4f1d629950e0cdf83b337": ["camwhores.tv.", 0], + "6024970c68b3969ef58f15e0efd739bfb5f782813e77b5c43293a2ad9d3fbd0c": ["compare.", 1], + "602a0028baf19ae5e371504f546ddf8ea0100ff1b5e77644a4c02ec36c1fc0b3": ["fazenda.gov.br.", 0], + "6036c9045084683a49c1d663dbe7c047d9925211c586000438e86a38dc5023ca": ["solarmovie.pe.", 0], + "603ac05a6cba84c304da1f7815f010bda0cde5707e23734123b1c9db2afb80b6": ["pohl.", 1], + "603b5af091c8bd694677c57e157ad6fe4958342f6cffeb2b4946a20cdc8d105a": ["oh100.com.", 0], + "60455a325c9873ae76462904e924e51a1c8b5b21ab0c8098a21c7e0b088240e1": ["willolabs.com.", 0], + "604cdc6c0a286b033a733fbc4bbd08b03063d44033cbe2ad6516c54e62ee28e5": ["thenews.com.pk.", 0], + "605a5d19fd6c7733a8c4a75034dc013f57c5d2bd86a4efe7adab454110cf930e": ["liveperson.net.", 0], + "605f79043bf887151a7e359c4f76926f6398848a81f7743ce97481a25dbb6482": ["coconala.com.", 0], + "6065313cc21b7933de016eeceb171d7c2be1245e1e8c26520687108f28b213cf": ["nbe.com.eg.", 0], + "607299a8e2dae8544a11e203299aa7476309747803db45dae64e3c2983446754": ["hbx.com.", 0], + "60751813c4e0008e69d8e5f46f34b971d237605de5738f408132db87e2763a30": ["prosport.ro.", 0], + "607c59db584191d8594c155c64f817aee1b4f808c6612bd633d53fac0ec0622d": ["kijiji.ca.", 0], + "608b1182acc80904e64b89c2a127b08c391ab724bca08f16ee26ed3cbc020666": ["autodoc.ru.", 0], + "608be294694a722a185228647dcc669c4f6b765fa20b905adaeb9c5df75b5dfd": ["ytstv.me.", 0], + "608dc2450d82bf1531e87d8141ae68893fcf5759ba3acaecf2ff13939e03644c": ["yayoi-kk.co.jp.", 0], + "608f8d4f3141897cf4809425dd1c3817b2607188f413c3f186c918cac0c9d094": ["jizzbunker.com.", 0], + "60992cf3b99658e7f5c74e0eedf77fbfc847df5bd2eacab07745e7d172ef65e6": ["rip.", 1], + "60992fef713b48f68b350112706bb10c16d71904b7e7db3113ba001b413dd29e": ["xrares.com.", 0], + "609e3d7e94521d0e32936f5d92ba7a5cf5eff07da9a986851fec6e276d3f2cae": ["addmefast.com.", 0], + "60a1f8f7a63588d6f635b18914e3653e763a724261d045b32f4f97c6b5e60580": ["askul.co.jp.", 0], + "60a79908d1197036b439f690949543a5c1859dfeba64c109bb41e84809476bf6": ["douguo.com.", 0], + "60afb0a4f41d540c51c91cd66abb6255b837c51baf150afd22877d1210c04870": ["auth.gr.", 0], + "60b31f9d30b510b98070caafd48182a9cf0106bc464fcd9e6b8097b3360178f8": ["naijanews.com.", 0], + "60b7aa118fc024204d79bb65a9741bea5f0e1e0f377c330e5500712f1983fdef": ["makeuseof.com.", 0], + "60b905238fe8acd45252d0e6c9da254c41888580ef8a859e9deb04716dec6dcb": ["meistertask.com.", 0], + "60b90f8545aa1f46ec7a14d219498fe57e8048e41c9c707b7b7dac0cb7089664": ["gossiplankanews.com.", 0], + "60bd61f05564bb9d30f9e4a6a1af815af0e3d721430e808bdab9f34234e90d37": ["abola.pt.", 0], + "60c42b40f83444a778e376084d1393ec4a114353421d0351c5f1f1343e2851c1": ["nextdirect.", 1], + "60d0b713914bd923b5be6f4ab7fda9737de7a7f2abace618d2d43f76aaa5cdeb": ["toppr.com.", 0], + "60dbd83512dfb3975d39045fec904cc1ef860ec97c338f361079e942a942f700": ["comptia.org.", 0], + "60df638c132567f7eae7a4384669111963e6163760adc908e6719f5f9c269638": ["albiononline.com.", 0], + "60f7c34ef70b4c332225efd85a84529bdbe9a0e1db579700f9f337aa87e7d715": ["transcribeme.com.", 0], + "60f7f897bdc606e3a347ef2bbd7eb6b6ddec9a8db2a683631bf5f7e6ab014c36": ["mobile.", 1], + "60ff503e9b7d6a5a518382a4ebb48225e5994d08e4a94773aa2382111d262095": ["channelmyanmar.org.", 0], + "610263f13adac61ddd143c3243fd6f160468b6116ec66281e797cde0301ce1aa": ["youneedabudget.com.", 0], + "6103fe1255314bff5861ac3a126677100875e390a5b5b8ac99777f857735d321": ["boingboing.net.", 0], + "610a614aeefb4b9d7c402147fb0e7284c95e371e3ab49ace91bebeb3001fef12": ["publi24.ro.", 0], + "610bcdf9f50140e7c3470b8180235669c885bff211e8d031bc196ed50939d788": ["helium10.com.", 0], + "6118f8b93903fe483cb9eb6d62aac19d4e972f2ef9fcdfe5b23a45291aa9d2f0": ["gal.", 1], + "61215d4d9f39472ba434d6b4e6319e64114985613915f66fda5854440bdb085b": ["laptopmag.com.", 0], + "612c01f8f3ae8022bc692c9a6e181d2de48538001d4d76241fb46ec7a46a9e1c": ["nbc.com.", 0], + "612dfbfed64098f83708aaedba79688901d25e49b25e36063d7a8fd9ec1aabfd": ["closermag.fr.", 0], + "612eb971444ce6aab3a4e570638169b2cb7ba2749f4293c01ab8116f3a6cfd17": ["zakupki.gov.ru.", 0], + "6132a544995b5759cb174844ef92d92998140e00c9f9c009b4888db3e1256454": ["dingtalk.com.", 0], + "61352e5619cfb581967bdd7ef62f7ca2aa58620b796dc1a8c72a24f1055c1f56": ["japantimes.co.jp.", 0], + "613895f07e9ba721bb5a51f785940d0d6071148a8259de3c56f4cc91f556721e": ["borna.news.", 0], + "613a97350a6a4ce5fec30cc9661d734a9d2ee1509f1e2abc2913f2c9e5f17a0b": ["alldatasheet.com.", 0], + "613fdda50a4fcc2010e9876d77dada20e0dea55bdf0ba7784f8ac3a565ac22ff": ["tamildhool.net.", 0], + "614076ce045d0a7938b507e3c3fa95704042b3429306bfd12f51d57976b66781": ["digid.nl.", 0], + "61506d061bb2e2760c645430eb67b30a0bfd1bd1ad3adfa5d0587762789c1a16": ["screencast-o-matic.com.", 0], + "6155b447b24ecbd68b8fa1762082d1c8867cfb72422fac0d52e9c628ef635a57": ["lemonde.fr.", 0], + "6158ac2837cbe6770ab8d5f73b33fd10ddd4be185daf15c6bcaefb78d347a735": ["prensalibre.com.", 0], + "615ea3cbc1c090e42e27d54dfa802ba9aed46da00e40a1d19291ee66d6ecc236": ["afip.gob.ar.", 0], + "6161a65ebdcee17f37923876adc155c192576687ca596426d8c050523fe9fa3a": ["huffpost.com.", 0], + "61625f6fef4007c056e45251d89597e420fc1b7c5264297d0cd8dbf7ed78c0ee": ["yt.", 1], + "6170118fe3ed057b462d170e398457fd7a4f51c26cf84c97dd086e8fbe02da8b": ["gezginler.net.", 0], + "6171a7944a5cf63c6c752182e54ce019a632cfe4fed55cfe878d3615c91d8f2e": ["thehindubusinessline.com.", 0], + "617c3fa3167ff1efa44788163ce035fe7181485c98ed4650c99439c1399342bc": ["pccw.", 1], + "618f97ecf7650512414666480e9338da241f56f007bd42ef5a6f5677d1d765eb": ["sitepoint.com.", 0], + "6195cbe300c2b0004ef083032546e48d68384f40f7507daab07ddfeef1c1707d": ["blic.rs.", 0], + "619f1ebd0e89f111e24d406dd3e562d6e670fcd1c790e1c73f6ac797c80ead55": ["dfas.mil.", 0], + "61a3258f87c0ea796dad724907d7fe2b4d652c2b4bcfca31e83c392f9b2c4396": ["chipdip.ru.", 0], + "61a60d2053dbedd0b49f5a7a1a319827b7494a00204c90559e296ff015b917eb": ["soompi.com.", 0], + "61a7317d1d04f84852e37e598e2671bb8f832e9bd2ca170e9baa81319c68e7d8": ["weworkremotely.com.", 0], + "61b2e49bdf8161999c5e31b2c4589a5793cd73593ff93eb3c4ac0d31ed74abb2": ["economipedia.com.", 0], + "61b6b47484050dea62366871a811970fd179f636162f29353c0439e7a09878fc": ["eldestapeweb.com.", 0], + "61ba1d05cecdab04492639c6989d87c20926e3080e7c1cd9b874a749246746f0": ["ap7am.com.", 0], + "61bd4718d1b4828dd27f3723507cc987d7bad16da2402823d0fbc4470768dbde": ["all-nationz.com.", 0], + "61ca5a9662d0cc46e697c494aeba400a2ca59002e7f58f214ca21b5244629237": ["kmust.edu.cn.", 0], + "61d1a7d98787f73e3b10520b07dbf99c999f2f2bf60bfb46ade50b0c951fe1b6": ["assamcareer.com.", 0], + "61d7d38a4e38e34760a5d1a0b202467094ac869f8f9427a3b4841ec9f172a74a": ["moddb.com.", 0], + "61deb8dcaee4e681d498ebdc2622511785101ff2b8088564c92cf33dd71b8573": ["kia.", 1], + "61e1e6fa5fbbd815523fe294b9fc9e7976f70decee101b8ca86c7b2a2254639d": ["azcentral.com.", 0], + "61e2c310dd889e5b342cb40d9ed54886b96ce7f78f8bd30445540665c0555523": ["boy18tube.com.", 0], + "61f40da3cb535c46d6c7b07a4b2e89a443b3ee9db94d191794c2316a2f9fbf9e": ["000webhostapp.com.", 0], + "61fc1cfddae6a91d64c0c909d62986819fc5e5e2c56facd102c9fa3274aaed7e": ["seznam.cz.", 0], + "61fdfab15846c04bcecb84d0192fa83f91b0a0a1ede8aa3d87b36cadc4822015": ["sabbnet.com.", 0], + "6204781d779f6d6a03b56d424604be87371c47abc909d51e803c13c9eb42cc8c": ["slickdeals.net.", 0], + "620598242fbc990b6f6209265ffb5d4e87bd70b9d667f1b5305bbbd0f77f1d23": ["sejda.com.", 0], + "620cd2181ebb5f6e489211bfcbe203e046b433fe9c7b70767620c0c22ad22ec8": ["instructure.com.", 0], + "62128b45e12eb80688fe7eb13de549a5c1b81099f665f6e4877a62a7442ea735": ["meezanbank.com.", 0], + "622152c5163ff271d5cc2fd78a10e8b94088d72c129c2d748644e58b209b78e7": ["hvideos.net.", 0], + "62236515b66eaf4d97b3dfbb1304652d478633c765d19d629257a17df24825e7": ["curseforge.com.", 0], + "6223ff21f45e4fd566964db4ba214f005d55330ccb4dd9c11a713a44ec3094e9": ["hackstore.re.", 0], + "622539e725eda63a23f8da466ce7af0ff293ae3a10082912e940eb3348f84a65": ["fararu.com.", 0], + "62295282dc83c47d95d0abb9f790f69e1441a7794f66aee87a736d0c2998a933": ["spot.", 1], + "622b681bd2e969a3d758b380cc425a8b19dcd141782991259435e9782a2e8122": ["salesforce.com.", 0], + "623294e1fc3ba48be2e98e9010064cc615dd35712a0a912ac95171ac4a5192ec": ["bikroy.com.", 0], + "623b193a306ed37efdafb1a6bd3836698ed3c9650e09043e9b8484a4d0bec5c9": ["bupa.com.au.", 0], + "623b3d842c43053e31d4b5c91a32cecb6bd1b31a0ff2e2de6f97fc0c5fc37ad2": ["otakomu.jp.", 0], + "623d6ca0de8101ea2bfa98647c25fac5efdcfa515c936e76899d304a17df3ff0": ["carhartt.com.", 0], + "62459406218ec3deb8cbc2d386dc048db7966fbb99a7cf00571c228fb2510002": ["ukzn.ac.za.", 0], + "624b7c154d827c7befa1462504e3c916498d8320e9f9c8554e5c61bcb005fa25": ["jwplayer.com.", 0], + "625a6c79497f6e45e0551cea321e131c905090390c6a3e4fcd5fa847a0c984b1": ["weblio.jp.", 0], + "625f2d2972c0ed59e9b35abb685687bef8d347f835fe725cee6183d53ae219fc": ["aust.cf.", 0], + "6272c77c44916add27f11512b5a9474ca0a82ba19fa59575fb08e080ca5c0bf5": ["vote.", 1], + "628890aae1fd380209162142e0d2c47692b3e674b9ea766e6ad23c5d57f95fb1": ["slideserve.com.", 0], + "628e64314c619e5fe0f29e9e97a7357d62c812e626e12473332029a87abe5799": ["36kr.com.", 0], + "6292be73bdfdc4ea12bdf3018c8c553d3022b37601bb2b19153c8804bdf8da15": ["google.", 1], + "6293a6a95b36f1eda011603208f350f27de093a208e97d498ced4cdc625fabbd": ["philasd.org.", 0], + "6295917d27fb21254aed2ea64427cd27f7ce5782eb4c4527c64c6b3bf466dce8": ["xn--mgb9awbf.", 1], + "629ba80edcd499b3219f47e1a11ee17f1a476be41fa6a85b02ff16896d2e6296": ["sui.io.", 0], + "62a952e5506eba094224e65e0c5c4a4fcdbe2db19d44ba29307f1efc944b71d6": ["wykop.pl.", 0], + "62acfce16bbe0c2cf90499d5f047fb889cdfe9e9c47b5ecd2bf715b410f92dea": ["nexi.it.", 0], + "62bc32c8441858ca0c57872628fc1eb6fbad36836796c3ae6516065ad5d9a6e9": ["wealthsimple.com.", 0], + "62c7fe0cb418154971c6aaa644b8fcf8d54514f964a48f1a07ae5f1184da2419": ["rdstation.com.", 0], + "62d1fef4ee3e9dd3026ad6e238f7479b1076e7697aa6556b0167be74d70c9fc4": ["9game.cn.", 0], + "62df4d6a9889744a28f351168852245b026038cf193f0bcee652e4d9b5712e78": ["sener.", 1], + "62e8439ca707160230edebe5136c3319e4ab2d15736d59a2b4b9ced3b9c02e40": ["550909.com.", 0], + "62ee6fc0f4af2302e2a993b6cd2ccf696086609e801559be3e3feb4ec6088126": ["busuu.com.", 0], + "62f3341fd83029c97865dae3e02407387796f9c90915e9c5970d1c1700c7bebb": ["kitco.com.", 0], + "62f454bf5cf5eee66c51d7f93b5b7426adc4e98a1d368b51a31566fd55ffb3f2": ["giaohangtietkiem.vn.", 0], + "62f9449c3be11ecbc6ddd0ff464404f18523a911942ad47c001afd0bd3d53e50": ["evat.ir.", 0], + "62ff7d64bb99f9f388c067cc108dad3ba17e35a5aacd910f68830b4cf191139a": ["panelook.cn.", 0], + "6301cee1683570cef92e42e53915e2244fe37e9ee0a46ebf583d2c949c7a44e8": ["caixa.gov.br.", 0], + "6306413c474afdb2e615a2bbfbf01057a49e24401bc82265a8138ec8bf22f91c": ["tworld.co.kr.", 0], + "63134abc98507885766c16737ae14122c9d0bb231fb41b114f6f06343cc316ec": ["nih.gov.", 0], + "631728073ee320da7d0e79f6948427c982073e10faafabd2c535b4275b237d5e": ["picofile.com.", 0], + "6319031481a3dc522985d1a523b4b273f9af5d08d3a62d51ad056a28749a5d43": ["xinnet.com.", 0], + "63194ad12e83f5b1a0d4643debaa3f32b7a19ea3a910cefd5fcc0d3c20990241": ["to10.gr.", 0], + "631c1e260758dc12b590b2f1a30faefe7de687135800c6cb67934a7ec059d7a4": ["avature.net.", 0], + "631d211775c87935e2140c0d17ba6c0e0666c4398fc666e9e63ed226f9aa22c6": ["tplogin.cn.", 0], + "632e3515b2c1aab5f7b6c75b89371a8e3ed115f7732d0b7f91dd50d497b2a32e": ["koreanair.com.", 0], + "633032935cecbc00040256e8951f91fcb4a6f0a763c595d07b390cbd58bd94d1": ["maxon.net.", 0], + "633ace0d0da1104d0449793fe4ac2d506eeeb82dd8133982e6d5cf92a4a885df": ["xn--p1acf.", 1], + "63455e6406e58412036a72df2b29ce88d71db6ca2adb74831206894c0c963117": ["rbc.ru.", 0], + "6353c67760c800b3ffbe6f46544ca8142c2608f197f601d21d160bde4894ab4a": ["workupload.com.", 0], + "6359d1c6c77c4b133a55ecd1f2f7f5c05c97c07a69bb792808b8e0ff9e526485": ["ar15.com.", 0], + "635f9aa351e44d67fbed96d8976929b8f7cd9c488dd0b670d0906e5d447f0db9": ["cyberport.de.", 0], + "636191fc4d77bb8f68f7a03a6b5dff41b089df700f21bb55e06adc7b10286845": ["stripchat.com.", 0], + "6361c59c814609b0aeb54d810619b34eeb65e037b5e430dfdf1551366287206c": ["pastelink.net.", 0], + "636339010e069c0db2af48e40b2dfcb2da7584ac89829407f4f3df45099aaf45": ["drexel.edu.", 0], + "636b971194ab98d498f95a7b23573bce246956e94992c19ca67118c359210166": ["cadenaser.com.", 0], + "636d1553fa69bdd58a9e9c4fd739545d4a35c35742f4c87760c4a03f7a78cc5d": ["netology.ru.", 0], + "637b95288b1d82d69826baec5df2e6599736d118b1a2177687b672e806144d88": ["kickstarter.com.", 0], + "637c055bcd117efff643d8d8ef4bfa144415c52cf4a0ad5889018dc4a1e4eef4": ["castorama.fr.", 0], + "638993567df3fcb139e9b66f8e8ddf1313ab0ca5fcebb2c721e864722d400baf": ["harpersbazaar.com.", 0], + "6391b059ab427eec095ff2ff833c1f5293862feaf7352bc21155bb5669a2a629": ["pcloud.com.", 0], + "639834aac9cbdf89482f5b413b96af1d5304c8dc8b7467ee974dac17bec98bc8": ["matomeantena.com.", 0], + "639d650fd87870af2cac27577daab2e25077d02b159f39a95ff3702b74ef0c2a": ["laracasts.com.", 0], + "63a2115e0ac2c43ce8829b3f66a8a171aff49cfea30027aae54a5288a525fca0": ["blogdepelis.io.", 0], + "63a840be53439165ed59965860b042b6821868878a8fc21715ae5c286c6a3fc6": ["channelstv.com.", 0], + "63ad425ff85ea213ba20aa5a2e9c0d37bda51ba50e096152a8b2aa71d97bf62a": ["barclaysus.com.", 0], + "63b06e0f3f2774428685e7b9dc3fc2e859b3dedb38ea0b7a18af379b61210f24": ["olemiss.edu.", 0], + "63b81a3a5528de4eaf39231e7d228e91d367a43b90ad4b42aff4d46b111943bc": ["forum2x2.ru.", 0], + "63b8338920cbd3e52420b014d580c0d3f55563c5115558a08cd7b1c133dd50b4": ["joann.com.", 0], + "63bd8af5ec7339f560b5f2c14e7463ba74fcc2ed24be6be9ce25688a777a1773": ["cuisinella.", 1], + "63c792385302f58c6e5af911c8e00e8a119df55d3c35451690b986214e11757a": ["faselhd.club.", 0], + "63c8d213c209bb1b23877ef0eff88984ec078367c91e7c1cdbd2c57d60c868df": ["reimu.net.", 0], + "63ccd3c1b94b6df9d41edc2d0074a9de68204531880e7cabf2760c699763601a": ["thenounproject.com.", 0], + "63ccf3e60dc6516361dbf82735abe682bafe5a9018b5e22af56d66b4284ea67b": ["ilibrary.ru.", 0], + "63ce73039a7e7fc394f669163b536a2b06322ab6da353c6f749f9e6e202d0771": ["sedaily.com.", 0], + "63cef02a0cf2df08fd9da95894daf436987e90319ccd5b96019f669440412c76": ["citizensbank.com.", 0], + "63d1e2af22cd914116d3535892d872a888f375115780dc2773aef0997aa0422a": ["taleo.net.", 0], + "63d58c891ccccfb1cef46cc3ad8ce910d3972cd69bce24b12af38e3aae4c9aa0": ["pdfcandy.com.", 0], + "63dd3f7f5510a897c64a61753b1f87c8d7ab5dd099c517b5b0ecce2ee3606c64": ["tu-dresden.de.", 0], + "63e84cb00bf30c8ab737dc34a11d3add7282eab8259cdacc43204d49cdcb3e4e": ["cleaning.", 1], + "63e9309d25b30c558814922f62f6c607226c4fde85ef927d5a096501e9efc41d": ["milanuncios.com.", 0], + "63ec6d9d7129c0fae41d2c1b0ead6d588953c436940b2686bbbd68a2f983de9f": ["uni-heidelberg.de.", 0], + "63f2db60c1822e1d0d7d79ba9989e6ef76f81acdda79103692dfb8333ed39a32": ["sv.", 1], + "63f369256855fa6f51f954c997ef782779e834b189d2e67306454c36e1f1361b": ["eadaily.com.", 0], + "63f42b6769f87a0d0048990031b92975d5b79b89a805da6769bb5179c848cf98": ["zapimoveis.com.br.", 0], + "63f74dd0afe49b4695603a518b90436351dcb1e0dce50218111b71f1e91d0491": ["atv.com.tr.", 0], + "63f7681d4e29b521efdc09d1e337c1dff503de1ee020b4e3dcc9e77bab6bea07": ["hao245.com.", 0], + "63fbd240d9a085b7186f729c4420ca1e78cd6305880d48c36543e20bc34fde70": ["chipotle.com.", 0], + "6401102fbb34d8f0eed114f5a7387274c82c2054142374523d5aaac3006921ee": ["georgia.gov.", 0], + "6401e97f00d7e0cd845dcd2871022414bc32ede5bb3b73b62455bfb2eb7a9742": ["100sp.ru.", 0], + "6409337203a5cb42bad7930b669ec790ff20c95c357b047655801952da619e48": ["lichess.org.", 0], + "64098bd288259c9a3eee0fb644b6805fc5652772b150c88324b8c06c7155b150": ["lexilogos.com.", 0], + "640c3ffe10a9a7a28263c08b12dcab784dfbab34921c782968c01591295ddedf": ["scjn.gob.mx.", 0], + "642b4c4f63024f4bb29a01e2bac541450e990657fbd937d2adb32ef646d7190d": ["suomi.fi.", 0], + "64323eee5cd353a1584a572edba323190b0fdce06166aa9e13f91d76eb587b75": ["yokohama.", 1], + "6437d2537bd0954e79d2b00df99c4f52549018cfe597d0381decae8cd09d2bff": ["fap-nation.com.", 0], + "643ba262dd61def7e749a79eb44af6b69f11f084bc25d0de8032d5786763fe4e": ["parsian-bank.ir.", 0], + "643ddbe367ef526fcbc4301a9170962f1e80a6af3b954cd3c195783eb4377e19": ["globenewswire.com.", 0], + "6443a9cd777b1b49dab54aa885e289ddcf2dd9a86bb6ae3d89669800a95a58d0": ["4px.com.", 0], + "644c56f64a5bd608ba08c75d4fdff1bcb9a2277cec2132965bce290ff0dfb2d9": ["cyol.com.", 0], + "644fc05169a420e8125f68f4a10cda2ee5d9a718ee2fb42496a7dfe4a2707aaf": ["asan.gov.az.", 0], + "6450029302f869928f737f571ae8d54971b8ccdc568bea36ebb911622636c72d": ["fullhdfilmizlesene.pw.", 0], + "645887b0a70f5d552920a0b182dca75bf5867c5d99ef8c24fb22f52d158bf358": ["pia.jp.", 0], + "645b78239167a652fb5f3a9371f9992df80ae3e6e6e5ec30cc4df26de9338ce6": ["coinmarketcap.com.", 0], + "645bcbd65595298a217c0a8aef49a67adbde74a12d84f18016c3e8b2d6924ba2": ["duelingnexus.com.", 0], + "6468d9c63383be2c502cf3358d6c9602eaecaf7e1912cec3e695342412ae40e0": ["hubspotemail.net.", 0], + "6472411f4049a2b4f66aedf0ada7f78d4b24aa30cad3bb7f10cf97a57baf4e4d": ["casetify.com.", 0], + "6478ebc5747c71adbebd9f4327c38566cbecd3c4eca15ff5d9dc2793b03e74c0": ["ipindiaonline.gov.in.", 0], + "647ae3ab5bc842370b4210228cdefb08d9be305ff97451825d3c90f0fd613607": ["playtech.ro.", 0], + "648730c694610a71db799bcd0f04a98d6e84896201acba1e3c352a0cb18e114a": ["159i.com.", 0], + "648bd610785f3beb64809fe6e890056d764ff5efb551cdfd0b0b227c248b8316": ["weightwatchers.com.", 0], + "649244693205b79bfd978371f0f227c4c8d1ef506cb0440ecc955df12bc732c7": ["worldofwarships.eu.", 0], + "6496ed5ffe20c2c6548d8b0e9786d30c891dbe1ba143858c32aebeadb2b2c8f2": ["abchina.com.", 0], + "649d54fdfb72f2c884020b012f2cf41694bc0b14c8761b1076ccebedddc8eca8": ["blender.org.", 0], + "649e1e3d6dfc1a1eebe1ead63c3a112982866dc50c960a2b85d04722879acff5": ["sophos.com.", 0], + "649e4d8c1ddb96fc4a690c231ceded7e5702b2877fc60fdac37121cde726a45c": ["simplyhired.com.", 0], + "649e57c0c95dd3bb576c153a2e4b12a17efbef53b1852afdb16f51394fd7234e": ["megogo.net.", 0], + "64a3f14c4bfb9d9e4acc7591c93df30ff4641f0015731977f9d72234cbd6c580": ["elle.com.", 0], + "64a551440bc02626b448376fdc6c4476b77b6d9485b05255d8297eecabac174b": ["niceoppai.net.", 0], + "64a616c6b1e0d79e1925032c8f489a0cc23c583cb4b06bc52d3cf474685a8a59": ["gm.", 1], + "64ab0aa8fc67e4cb4211fc43888965680be2009a545f200aba90085e9909fa68": ["daemon-tools.cc.", 0], + "64ac3be19302cc9528b4db6138a9d75f1b7ac8a08367d265b5b132dfadf6dd1a": ["vumoo.to.", 0], + "64ad0a3f25241c19b37cad85bb271d347d201e9ec674226f01fb408116461730": ["cvent.com.", 0], + "64b45869a926510a8b7150cfbe716bc5ba8feb78ba0b7dea8ac8d75f9ad2ba27": ["brainiaccommerce.com.", 0], + "64b6db43a7c9a48be83aec36624b649fd4a333bf1853cb4eaf824c7c06c33a37": ["onshape.com.", 0], + "64bcc075d1564de428c547cf648868196dea0e9ec170f97a5407aeeff0170aea": ["zhcw.com.", 0], + "64bcc5b9b9700f60610832c2c582845fd0b254d5690be8297868cba601408906": ["arabysexy.mobi.", 0], + "64bcdfcf5b268aed136ecf74762e248717b9aaae6479456eb3cdc82bf007a0ea": ["sfu.ca.", 0], + "64dfcc2eed9bde7bc8baacc986e72bd56066caa21d144dffadab066c8937a2c1": ["largus.fr.", 0], + "64e856b3400bca94d4b6a8397cc6799492d6fbdd134f252ffd2270ce91e299c9": ["babylonbee.com.", 0], + "64e99108f73ce434afc3f92bb08e17d771667476dfbc008430b67ec11dcd3c41": ["3bmeteo.com.", 0], + "64ee5d50f5b1b969cdc3bb7c9cc9d0c485a1629eb540814c664c7a1705678e13": ["e-cology.com.cn.", 0], + "64f01fac2d0eb83c7fd12eee97c480ab905bc259c6a63f3443392aebec97492f": ["logitechg.com.cn.", 0], + "64f465086c821a83f1ea15ddebca50003bccb09c5043171f6ccd1b3ef4401b32": ["jugem.jp.", 0], + "64f7c064a280f02b66f3e78b061f94133450a44bd5105b6c2306c02eeafcb642": ["almrsal.com.", 0], + "64f7ce5c64d6d1472d72c2656e9100129f10013084b3655a48ad1a4cfcfb493c": ["mondo.rs.", 0], + "64f8b74387947b59b27ac6ee87bdd2fd3131dcd4d27d8d676a5e1e7429746dc0": ["wezhan.cn.", 0], + "65096a61b5a9adf1b77d784b92397034f4bf9bae22a530b8902024bc43b6557f": ["af.", 1], + "6509f7249203d84dbeda72b93ddcb20a479fcc689c76a24258b7e92d511ee66d": ["fj.", 1], + "650d8275a1b86fb7459776c1398cf5ff2132beddb0d3f1e4c4a31866760802ff": ["finam.ru.", 0], + "6510f1f8822badc4fa17f1eaf4fa3d0b802d3058a6586936045d143662a52234": ["opposhop.cn.", 0], + "6516d079be39d2c974de403fa055e4a7757e91e55307c0cabbf3f37afa7ef081": ["tarh.ir.", 0], + "651708da0997177b89fcf9dcb70f93b032acbf3ef9fc45a091c96f425da9cc27": ["sodocs.net.", 0], + "6529181972a419ba181e8ca6e712280c28917ef2f5c7771d7e03a4fea26464e2": ["thepeninsulaqatar.com.", 0], + "6529b49fc997c1380a51d47e8a71e14f4a5691731d480418098216db6f536014": ["noredink.com.", 0], + "652d87bb0c4cfad335444375934fd39f09e4fef8b2f34a8b485b18b94ebe92b3": ["tipsport.cz.", 0], + "652f58649592b9840348c822c7d7161f70ba9bdd0aa52d3848175d05d8e5682d": ["coolrom.com.au.", 0], + "6530dfb805cde7319a94fa2bdb5017cfb32496debeef5e46d7c3cd3c88636ff0": ["ebook3000.com.", 0], + "65392b6bf48e3f26e701a6bd2f9fe500b9d968a0e1523e53e4760aa43a223014": ["unsplash.com.", 0], + "653cbb46aee56fe54707c528d9fa4d4a2fa017bb18d31b567d9fb059b1b6225b": ["newasp.com.", 0], + "654572c2737cfb081e2aeb36a3c13a7454ef654d003679a9c98050474c3766d6": ["taskulu.com.", 0], + "6550ecc7be5d62e9d92b7b08cd856a7a07c491354dee0366a4d04251d5c84362": ["yotpo.com.", 0], + "656507205027f08d93ba8ea0e742d7a0b75cb4d5293e8ebae7a52f5fb17256a8": ["vivastreet.co.uk.", 0], + "6574f416b90a6285564e92c21c78f8e279ead5a61943c9878673a549bbffed52": ["imss.gob.mx.", 0], + "659a1f11a4cd5cb70d9ad0c10715c89426cc0bcd9de0cee196830d3ee4825e9d": ["okezone.com.", 0], + "65a72c2c14c6c2a6f8463979ecae08f04da27a53240a2029f1d69da8024a64b8": ["wikibooks.org.", 0], + "65aa23fac50b83670df0992c935d9d34c6416783833667e1e71411c1bfe77631": ["ivoox.com.", 0], + "65abec82f8d29797849d14d59bf23708ce9bdd0741873ce49c9f35f1a742f284": ["talentlms.com.", 0], + "65ad4454f36894516bbc98ce03e62d15e9d03ccb68c070ec2ef6036344518082": ["site24x7.com.", 0], + "65ad6eb9608350c8f25cc57c5ad84b041c6def11516149fa5f6960d7d152d3cd": ["drivemusic.me.", 0], + "65b4026b04cb737ad200d8dd6562282173364a1fe3903fec93467cd5aff53172": ["enotes.com.", 0], + "65b44256a2655c93f4f62088903079da291ff24d04f0dc896a0a7c8aea6acb8f": ["dailyfx.com.", 0], + "65bf36f9301c851a4e4b2fa183c6762b27e74dbad56d4c34abc4cb0c18afef92": ["82cook.com.", 0], + "65c06740cdfdc5bb6d673fd6eece360684099cfe25893946677329a0a7f5359c": ["oriflame.com.", 0], + "65cc60c4ca61914ad099ff3d57c2a6b7abfc718d2db916b70e47804ad8862f1b": ["iiilab.com.", 0], + "65d9552665a4573c1ce826da0035ae21c6f59c353e669cf6b3711b7e2c667ac4": ["ebay.com.", 0], + "65e1ff2aa04648f9bec3826194cdae36c9da0532760fcf061c8bc598a7ae9c0e": ["endnote.com.", 0], + "65e42e75d7352b725363eafb433c8a145a5855022f471fc404f53c17a39bba51": ["ebanking-services.com.", 0], + "65e84ddc1d7a6695e600013873e6217a12275a49ce662d5ccf268280679de3ba": ["onelogin.com.", 0], + "65efc4f7ba385a84c11f218251fbf3f7d7ca21a26d24b139f792ffc1744eea03": ["hdfilmcehennemi.life.", 0], + "65f22e4d88b9422c421c0faa177bdc793acee4056266343270d7cf0af1c0c320": ["myheritage.com.", 0], + "65f2a1e414d29ba57e2f38f3c62ab136f4181fdd3c6ff2f8927a9374bfd3a058": ["applyweb.com.", 0], + "65f6d13a6b1c5bcd2c350784c4a81a68b5ae0a321f0bafdd2de4df4978cca078": ["maine.edu.", 0], + "65fb1484d301ba6578f9f7b5491ba833362ba8c7642d2774ecf53b7cfd21a6bf": ["btech.com.", 0], + "65fee52afca7186ec21b23356b00141d017b8cb82f4f9a7cc8ba12c9dcd33690": ["sofascore.com.", 0], + "65ff9fab214fa12ba4e3d5863012381f21fb938bad797f488a8450d8cfe649f6": ["nas.gov.qa.", 0], + "65ffbd4304b78f1ddea6bb2d8d5af3a9b03a6666ff5893fdea2d9ee3740b7513": ["szu.edu.cn.", 0], + "660222d63735f7da60d75ed04bda678aa2688ae374c798eb05fe9f8e82d0788b": ["longdo.com.", 0], + "6603ba2939264af05f283c81cafda52e781ae898619a11ec1ca1171969537262": ["dota2.ru.", 0], + "6607a648168afab45e71c9beec533c82c9099a2cc2eb05161bd7cb36db4d8db8": ["smartisan.com.", 0], + "660bac091fc61dd9d93c1d204617caf17550b493e981dfa523b448d3f7fd0af6": ["dagbladet.no.", 0], + "660c95949581a64c66c724f9e88850a82cb80bda163322e9d6e9fc8f86420aa5": ["gks.ru.", 0], + "661411594ab61906b27a305a63d62ca23d6d3a645633072a63256040abd69850": ["keepersecurity.com.", 0], + "66163dea41ef2b297e98c4448f9f92285e6d89999d60cacd8293097d0b2d4662": ["mpb.com.", 0], + "662375348b3cfecdafb1cc88fd4b26c84946087e3e85f40740065abf07587927": ["hankookilbo.com.", 0], + "662751f1a494dfc98441dd0f578bac99a0f71de2d34a189412360ee0eff82bd4": ["ymmfa.com.", 0], + "662c2f54e23711b455fe58668f1ebc48f67a6c3a45c87e9767eaff6bfbe81c40": ["redstate.com.", 0], + "663412403b26b17ca899a9d43589e806f85b4e96620f957a71b85b0fbc825f1b": ["wenjuan.com.", 0], + "663642d5f8d222f6d72e8a4cbf28f6f8ea39176d2bace393ae61ff9a54149ae0": ["blutv.com.", 0], + "66434e9461f10c0d561e9910f5fe7d12fce60bce1fe6944ea76e6e2c209d5733": ["qihoo.net.", 0], + "664d017f4560f10136e5d16b97fb4184e6316e9152158785a05d6c0360dfdc8c": ["openvpn.net.", 0], + "665b3f32dcb321aa06ce5010ad9e9abb83d265e7e6dbc33b2fbbbfdbca0b8359": ["sa.", 1], + "666325b21c5855f8089c7e3e68d0025861a72278d87f99f118e02b1dd6427d99": ["auction.", 1], + "6663650b9f6c3232b38d5c0deada49ccbd26a8cb69f0b36fbedcac2837e31403": ["mastercard.com.", 0], + "666902344ee211b93572cfc2dc7e5ffd1d8927395b6a15dae4340713dfcacae1": ["hqporner.com.", 0], + "666b2c2b231a4a9889829fab1c29c972d4d99699592040b46becf7821f67ff82": ["volvocars.com.", 0], + "666c3595491272555e7e0b8627e100a07951f3fee6fb300874a239b56349a2a1": ["tours.", 1], + "667177fdf7d1a1acfa25c4b9933914389889025d5cbc0eea8426a8c6b94d66ae": ["velocitycdn.com.", 0], + "6679b4a095f3a938160b76d294189b4662c990bff6e111a3e0d7f5383e4d1c3c": ["europepmc.org.", 0], + "668072935e5315d1a180d38c5583eda21cf5754f604265f6cfd281185702366d": ["ewaybillgst.gov.in.", 0], + "6689b8a5ca23ea88b0f43ed40f6e6f5d0893810d6a1bb71005a0af366a28741a": ["olymptrade.com.", 0], + "66908caaf265a18ada9143df2d2e5ae70d12cc12ac27a26555bf5b63fe196eb5": ["study.", 1], + "6694587fd91038140ccfceb95d4bb15d7952e57d651496ebc27aad4e974f9488": ["ouedkniss.com.", 0], + "6696e24f1ab8648d8cbe6f5ced9791cf3502f7c3c6cb6af332b7073c361fa81e": ["dvdfab.cn.", 0], + "66970901c16a652cbd5f5c226631b937850144bf4ad144e6dc2e927b5b549923": ["eghrmis.gov.my.", 0], + "669ac8549f765fd2b72edd75341d309ca5c29ac0f54a8232f3a4cd1893d8929d": ["garmin.com.", 0], + "66aa19b7b89f5cf17982f188855c264689cc484a5b3b62ee561c5f2d09d7cdb2": ["sanfoundry.com.", 0], + "66af208d47a0130be74709f29a9ca77251c33fde13f37fc15fef323bb4c3a43a": ["transfery.info.", 0], + "66b620e41df697ec7bc28906fb334f2ea14ed64d2286674b50bbd2ebfdd7901a": ["churchcenter.com.", 0], + "66bb4a02b0a5275c9d2703db86b913c294218c00257f0d0446c1db734d7c8534": ["onstove.com.", 0], + "66bc32f9218b52c8ef3c2a8c1f5213fe3fc46b526696b8921d841c7f316cb492": ["betfred.com.", 0], + "66c8e2de6a5dac46e45385c9515b1e5b386d9770c94f9f68a50f41c1fb07623b": ["anybunny.org.", 0], + "66ce9d34b61fc59a2d2582a44707245441dcc4548773b2d33482404f585a7f6a": ["codal.ir.", 0], + "66cfe06e5011928bb56e5bfe7ca65bdb43b81980309c56456f68b91a4be607bc": ["iastate.edu.", 0], + "66d0cf0ef760c2ce5e983e7403afa25cdb24e100716c11091fc4b4ffbae03dad": ["cdiscount.com.", 0], + "66d20496f97b3cc0a6ec0c78b854f41985b623718d1009cc295d383566207dd6": ["asiamiles.com.", 0], + "66da6dc0fe0425085e24b22b76a7ff0e3d116da745ff2ac9c11365410acb41b7": ["meltwater.com.", 0], + "66de388d852a6291626ee37b557d30f4e1ef1ea0f6bb80391a7bf892bf8cff1c": ["juicyads.com.", 0], + "66e3e97ce782f28a601767f8db60f6d7712257dd6dd53e53a6ea60fc506f8265": ["esnai.com.", 0], + "66e78650f8042238b566d1179fd63a1952510c7a91028df5f6aa8ea34eb92373": ["zigwheels.com.", 0], + "66f713b569a5784d5c1d843debfbc613cae43c761ec5abd375dd2755d047ae03": ["ruobr.ru.", 0], + "67034e3057a8d743f2a3f95ee1452bd3d2270eafadc11248d3d10334929a7c0a": ["fotogramas.es.", 0], + "670cc9b96c868f4941e1a6c6948476d6ab347440f6bf84de7a03fab58de18861": ["naturalreaders.com.", 0], + "6714ae5276785b80156386db868f1b46ad1e85cf4c156bb47b9a84825c2d7b04": ["matterport.com.", 0], + "671ad42042fb1022125e8a1683b16342627f5cb821218f5a42e43d7e8a87bdbf": ["bangumi.tv.", 0], + "671ae94e7c27a7160a4fd1db5b0981c0e255e7bdc84bfe201dd928d97fd17e91": ["kerala.gov.in.", 0], + "671e26b6c61cc61408eabb7ad9863e5799222d7ca7e534908c781f5e8af69951": ["theatre.", 1], + "67290bf3b51d55157930f16ed3ccb8eea0902ff5defa2a1ce6d1b5e87856b493": ["lifo.gr.", 0], + "6729b31c257ecef0e92840b00339a8ab252266331272a0875e2d02112caeb967": ["kakakumag.com.", 0], + "673036a95cba29084b29b7f326d62546e0ee81a8fd62cc48d2befaca119f572f": ["kanobu.ru.", 0], + "6733be20ec0d716092bca9709be4bdca3eb5d4af1d24559190847a734950ee25": ["mixpanel.com.", 0], + "67348a12d6110f6dc9e9a2cc3f583a62e22365618fedc4f305a2c7ff59f07208": ["1337x.to.", 0], + "674a7c45d087927eed448672234679c89950f352f2b0f65dec966334e9b04b22": ["buttsmithy.com.", 0], + "674dc856381acc501401467460d20bd2eaa1232c1974573339058399a6fd8cc1": ["vectorstock.com.", 0], + "674e0b5d22ebe65715cabe691e44e509adb5f6212f5022985b492205ebd7b38c": ["sdna.gr.", 0], + "674f974fce4257a8e31aaab8076e845d0ad381750441b55df68ed437da98cce4": ["siemens.com.", 0], + "6757f8291a30a8e9dcd5ebc16c6a83149da14ebcfc9c1532b38788479845c36a": ["westernunion.com.", 0], + "67596728ff4d9a7d1e9c90062b6d62d7d622f848f7fd4e5d67bf4a1bbe6ff810": ["onesignal.com.", 0], + "67631736be01f99f79fd786aca224b2eae07d1ea51373530bf38e37efe97e8fc": ["lenskart.com.", 0], + "676471edad5b2ebcac5f00bf520366adf329b08d44906a7701a001d5f4d41931": ["tiscali.it.", 0], + "6770a19e568e3b388aa5f7397ae5b1b82672004f8a72a50136352c8dfb961439": ["aznude.com.", 0], + "67726b9353748cfd143d5d41a38f7f77f0d0da2029f3a00dcf94b9ec9e25d7c7": ["hepsiburada.com.", 0], + "6775f993b7c6687296338c71c1a8c1cec94f54b97cdf77253c42b30f17beb37c": ["digg.com.", 0], + "6778f2f6a2ca6991d82631c2b3d3c3e11bd7d572767ae9349bfb34c06cfeafc9": ["licdn.com.", 0], + "677ddcbcd8d2be2fe36ea09d6202714eb44003e0b7bfa10b657f6ffa38b06ccd": ["geekflare.com.", 0], + "6787109fb9961e613ef2a859ec7e8d3ae1f30d918992511350f4bbc9667c4426": ["scribd.com.", 0], + "678afab247eb60fbc26146aa81e620a795832cc5f3832dde50e4fc078eaf49ae": ["betfair.com.", 0], + "678efc42f3d45a0ac90954e962f294d07c3559ed63510464ae5611756ef47fff": ["myntra.com.", 0], + "67a0f4660b3ebe48e1fb314ce9c3c61336ca354a5d97bc822e17a31832964808": ["milli.az.", 0], + "67a2525e8d793e9763e0475b1c58c2fd59699b9d56052b9e50bac7516b31c0a3": ["toys.", 1], + "67a9cccb48e2c8bef1787f95c4ec3f881270b6f0dd02f34439886bc9f356ba1e": ["gayforit.eu.", 0], + "67aa5384b677a50316b4737f9695e9a9c50b366249d7c097014cc003e3f268a9": ["1000mg.jp.", 0], + "67b514b0a474e00e83afe26fc3d1640b65a9a5825efc6627cf125db75258b1d9": ["technion.ac.il.", 0], + "67ba3557e9bc784e71575f350ac186f5d756dcbb7b245453d2a6baf2cbdc7979": ["top.", 1], + "67bf6b03671ae908a56f73d4bb1fc5c4691159e159c4f0f9d48f27727b292fae": ["serenesforest.net.", 0], + "67c10fef16daa6fb0cff2add39cf817bf4418474849b2bb1c166fa419f0663ab": ["pokemon.com.", 0], + "67c34a7ee86624939991d71ab8c777dc277c939e24f6df8f4a766ccb51aa4225": ["listindiario.com.", 0], + "67c51f1bac96b55b204c78e318e9e7278acd01e1891b8c1f8b4f02adc716ea85": ["sld.cu.", 0], + "67cba34769288494c59333a12fd3d222e68cd2334aca1617756b95b13c4ddae9": ["tucao.cam.", 0], + "67ccd708bad2363797eb606ccd69d13532a06a75f5926a75ecde26d118c3362e": ["kaspersky.com.", 0], + "67d27e69073c9ba1f5a3d5e8ac6da6a0a52b820ffc63cc2459a56729d68eeeab": ["brookings.edu.", 0], + "67d295c73725f18ef71cf01c0d55e8c4e14f48e5335891702c290f56bdce68fb": ["zacks.com.", 0], + "67d61e594877be9ea4b8c5e5f9297208e150a350dc592ff1b8053a1dd02ff95f": ["hpcl.co.in.", 0], + "67d951a21162844d6c127453c960ead844507250b3edade3d6d9f9d8e4e0a554": ["streeteasy.com.", 0], + "67dc9ada0057a9fcc8aedf622e403d365deeb1e523e10ef1195491f7ac478b48": ["shafaqna.com.", 0], + "67ddd071345483aaed887a4d50e711189063cfcf801595f3c833e6fee63d3a60": ["thestreet.com.", 0], + "67e01794692a78e1fcb5abbbb06c15824d2555d07b06c4c61bdb5b8d39cb39b8": ["mybookie.ag.", 0], + "67e35042dbc3c5da5a5e8b88cd3248592ef9059e17efd679079a23d7d62aa051": ["qpon.", 1], + "67e7226a3a3afb3e1867715d27f121535aaa0eb856e2000e939e18dd0f6d061d": ["paystack.com.", 0], + "67ed12d21130e392566b829b3cbf23f69d2fb5630adf1fa2a29ea73c677e18b0": ["satu.kz.", 0], + "67ef450f940ed565e2edbc8510d87078389d74d1b81f591638be1fd38b770bb4": ["unilad.com.", 0], + "67f1187c649987c88a10904b128a9a5a1b51f48f969f74bcc02c0c74bda5da20": ["googlegroups.com.", 0], + "67f1f135c2c1da1ad5575e816b76c5223f2690f8f6f55d394d95dd3217486b90": ["jiyingw.vip.", 0], + "67f4649ee599167571f897872d081595aa2ce73759334b125c96886c361e748a": ["git-scm.com.", 0], + "67fa2c811acaf1f8de07c76b2f2b30f90baee670277914760965c3b93f512498": ["currys.co.uk.", 0], + "680198019538db01f46a49b8b8ca36431e5516ff9df2dd6956a13ca01ea0ef39": ["audio-joiner.com.", 0], + "6804047cadb805b04e86c5d0cea8089e3445c5d20546275b6fa511d1d1b985ad": ["easeus.com.", 0], + "680ade3c32ccfc17ccf2db69155aa2554529a0239964b774d1526d5637427077": ["tsa.gov.", 0], + "680c7e637b0416b8481866fe347fd0bd0b85b64995320d5005df3bad7e91ef10": ["canlitv.center.", 0], + "681871db73f8a8c78a3c8fe102d925b7605ca135f63023a2494ab4b4b6865fca": ["telam.com.ar.", 0], + "682373d67182680e2bd3da2847f32fd21790208cde74e060858e44167e12d4d1": ["dailytrust.com.", 0], + "68251cc8d5acad739989e197fc3cd30578480e97fd8194e0cb5bc925f2419970": ["vesti.az.", 0], + "68295c3b542be3a8c9fd1e25cb8e6add9446d061b3a509bf2a6edca24c768d95": ["smi2.ru.", 0], + "68326c1fce99bf167b80a89fb8f4dfa888859870837c84cfddf4bef3b3158770": ["hopkinsmedicine.org.", 0], + "6837a1adb054bafa46b533c92a2710a05853d6805b38c7aed10779ff6e83c52e": ["youbianku.com.", 0], + "68456da9aa966f6ee15eedf531c8f9219e1656eb1e1dbe27fd68a8f6e84d767e": ["mbank.pl.", 0], + "6845f2639f336cb4ec3fc227df1a416a0d2b9184d5050512737366ac7727bdee": ["journaldesfemmes.fr.", 0], + "6846468498dccf3b525a7a9dda5992bd56fe072b9aaae2c991bb227b75bb9e2a": ["bancopatagonia.com.ar.", 0], + "68470ce08b05db0093730fa7c58df1c7d9033758e79dcb33f0d4e60e6b62956c": ["motorcycles.", 1], + "684dd26888a668b92bcc443d63cea6e8fa67efb217e377bdbaf6a300c112df59": ["idrive.com.", 0], + "68517db74361d95672b7e02ad6443a562b2fdd0252d844ee720728e8df8ba4b1": ["hu.", 1], + "6851dd9f8fc8a044b63c0fadfc300d0e4b0f5720a7d6f27271b1c0c405feb2d0": ["aol.", 1], + "68580ff696605f00741197240ca73469bbc9cc1a2a65bfafe024c4b040ce00b7": ["girogate.de.", 0], + "68581372af425b3243bfde322cff5bb81c2c1d873449f77e9cdeea8c5be113b4": ["so.", 1], + "68595974b5da0a78415c103f917b2f9b707fac32ff3c677cf7a4b5c68f8a9571": ["tvnet.lv.", 0], + "6859e7582517b0af5e9f4b237ac1426600164da8ff0a792167595a0761b59821": ["compari.ro.", 0], + "68600199273402ec73679d17808c844f76ea421986bb8eeae56ebf8467135e01": ["admitad.com.", 0], + "68632fbdbf52812351e421541ca4eb186563360725a74bf98d639fe51c8b47f9": ["imtranslator.net.", 0], + "68641ce1fe719e7e686ddd66e4f49a3707a13cc4ebc1fde9de6a6d7b3722f94d": ["iloveimg.com.", 0], + "68694fd0fa07d27314b9cca325493dfeeaa9f121bbf8a7bed7d9dd3956819f49": ["mindvalley.com.", 0], + "687640ece87f23b108c941b74f93281460827af95f7faca1841540eecca0e588": ["eleconomista.es.", 0], + "6881988dfdfe77b2312f092457b94c4b48c8a7e8badb3fb67384c1fb8ee883c1": ["videoconverterfactory.com.", 0], + "6881ad30cab9ec43d264637ce0ddd9383e1c6aeb772d87dc9f93c1dcbc7a1cae": ["utep.edu.", 0], + "688384b5dab80fcb8e898a37bf55a89cba045e162d5a6515a66c500191ab6f46": ["pornleech.ch.", 0], + "68867e4aec1a2b2c546eca0e4b6a09c2fa450ae6e1d36410d53e65eba4e3d175": ["svd.se.", 0], + "688a02d6c92d306fa0e1f0c00433a5e3cfc8231c5668a086467471527611588e": ["narkive.com.", 0], + "688ed435a42df650d55c42f76f1e53bdba7a4682ee321bf7ae7ef878257234e0": ["qa.", 1], + "6891a32a48994285f910a95916d0ca7d47e66f378059f29a26f57afdf6ff8493": ["yatra.com.", 0], + "6895c77e1f747bb5d2ab0402dcfe248d742d70f1c6b5dba490a2024961b805f9": ["i2p.geti2p.net.", 2], + "689c1fde587049954897e83f523fe34d4ceab2070b4667d8dfbc8d5ab2c74c87": ["duoyi.com.", 0], + "68a1cb77da5cf2975b1ad935ac06b708f7a1d8a292163784dd302765cf1e88f1": ["ss.", 1], + "68aa2fec3bd4a55338a94c7d1f3de86423a87ca8c4c77e28f17ccc83a37c59b8": ["yunzhijia.com.", 0], + "68aea9687d2ff0ebb9a5b3ff70ea2d4233897421c59e2c50c22155e9e19948ab": ["toyota.", 1], + "68b08a923df667ea099b52f32d6503dfd9a30401dae62b47d8adef73f5f36433": ["eyebuydirect.com.", 0], + "68b143048597eb1807068f8719402cd9a10b4cded4278cfbb0ceecb86059d949": ["xn--55qx5d.", 1], + "68b618e50d51202f05ae233b39bde6e15bb58bd35cc4e24044497b8bbc8f3242": ["bentley.", 1], + "68bd9151dae7f75175ce4495824bc9a4d3215b77bedb2e980e0b0650baccdd2e": ["sextvx.com.", 0], + "68c2361d8c3f0e048f8824c1fa958075ca166da69698fe6e14bdbb225e86ee65": ["clien.net.", 0], + "68c5d369cfbcb246fb1e849ccc7ebc508d43886869f9ff359e6c95ced35c84ab": ["cbp.gov.", 0], + "68c5d8b7278906d1b4b9e375ca779c89a92bceb574f381d5333732f1b2e5a4a5": ["seattletimes.com.", 0], + "68c6f815d7590aabf7fd37322cb520630e220ddbeb0a7a15b88035df5581ac79": ["xn--w4r85el8fhu5dnra.", 1], + "68c838d7c76a92baa18945a76a4b267d1dd6e3893ac150502ef4d3c7c465ad78": ["coinex.com.", 0], + "68d283e14269be20781d7dac6c99c15039d9f6802c9238029da540833b8ddffc": ["knoxwallet.tokensoft.io.", 0], + "68d55a5fca7e0c54980413a4653f483d74dcdbcaf2276dd5c86d77d8fa1b45e2": ["forgeofempires.com.", 0], + "68d7176dd5d5993a8b9dd68e058cac8bbd09a6c35f9cae902dcc1938e2c1a149": ["lifestyle.", 1], + "68d983cd7358408d0bd2f1610f8e9fcb977a5e246e361791bc6c2978f78c4391": ["ren.", 1], + "68e39cc385db65e4f4e493cd0499e73131939d79adb3221137dd0b84cc8eb717": ["hirkereso.hu.", 0], + "68fe432ae69ebecf97b2b8f811ff550f98d8a2ba25b1ab9cda6728cac83132fb": ["championat.com.", 0], + "6900aecc542bdc9c525efb137667dc4a2e4be6c944bace75bea3003421821116": ["zbozi.cz.", 0], + "690e135b09464c2dd112547e5927b576d2a1c5d642c5a33029fed3e095e919e6": ["bet9ja.com.", 0], + "6911f28f0aa089f7c0a07ce7c2da1a5b8c14f7c3786f7572ca128bc381a39dd8": ["sgx.com.", 0], + "6913426a18b7a2b8e1e56901ce29f4cb4591ac0a6b16293bf89a9996979bb8c0": ["author.", 1], + "691bf21fe1f450d6ed0955dc80773f81d49af6059240fd0c2cfec5a3b9de812d": ["agendaweb.org.", 0], + "6923cdb43bb470f0b5de3e25dfadbbcabcc9823d27ee32f9cfacf1abc556db42": ["donanimhaber.com.", 0], + "6926449fbcd5fa5118bafa8a82c33b855a6c06a349bda9c486309b0ac7db0466": ["haysex.net.", 0], + "692d39aa2bf1f2774dd5aae8a33e28730dc2c2268a42cbb87151c245619d091b": ["tejaratnews.com.", 0], + "692d4af683ba646ef5f13511f96534729ab7cf02484d976749aa77cdcf6f4f64": ["depor.com.", 0], + "692dbc7838b961e6add38d65a964ad43a8ce42e27720ff89e2ca06cfc47f7090": ["gaode.com.", 0], + "69315a92e84df988ccc8dc17bd37192c94b78d4c63b93c3b5d56c1c77e3489b9": ["baylor.edu.", 0], + "693461c0ea453b25a358e42e300a561ecfc898ff2ca1dc0ad4945062c0083988": ["campograndenews.com.br.", 0], + "6935e2b0b3bc2d3322a54d95b88273763088edd737f9ec54f9c1b6f94542d51d": ["baixing.com.", 0], + "694393cf0f0996cf6b141b552734bb497dbb6d1da8d3dbc58527d66dc02970a3": ["money.", 1], + "694459ed4e49c2958dc34a2350a0c97074192af05f01ef5ede7b5c34bacd3d35": ["karger.com.", 0], + "6946666e0cdeaba73f9887bff0e0b444c60434e73d4e1f811c8c866aac7374c5": ["steamgames.com.", 0], + "6948ff3be32a63d8db2e0e92d42592c5f21a82375f05be7d8b449ec18241d70b": ["proprofs.com.", 0], + "694ab16d72f31b2a94586f0dd59dab16a6ce17296cd230642fb663b50612e5a8": ["verisign.", 1], + "694d9f75f142090463c35fac29b6b5168b605fb4f681f69f05d9c4c95e0ebb9e": ["gencat.cat.", 0], + "694f9fd6505120b5e36a30246f22d2039b90943f6b41f618308a0e413e33e629": ["studocu.com.", 0], + "695342093c758a3f2926c1ff9c3efbf789e464739b7d6fd6f7a44650c31f7613": ["serving-sys.com.", 0], + "695359bf431e1a1798bb72f6d2d11a10ee2008fef5a33b9f1512a47ecccabd73": ["pinkoi.com.", 0], + "6956445133525e227c98e23f53a9e367b1d9d1e4670cf943f2a717ea2d67f479": ["nintendo.com.", 0], + "695825070577938703b09e9f8def0c16653c03960d25214a82e317c7684f1709": ["reddit.com.", 0], + "6958ed216633dff8b46854f219a0830fe06c6057223313eea3891c3a909db898": ["appinn.com.", 0], + "6960639f73458aaaa18c4f21b13a9b8f2c7e7fe16956b23bda397390d2ce8098": ["fedex.", 1], + "6962ea942d798420f6d2a5d8020c05ffd20a5059020272740a7a34b37bb56cc1": ["clickmeeting.com.", 0], + "6969e3041899ef5f04c9afc2592b2417b789821a044dc296e36f9b6a5075ab50": ["patria.org.ve.", 0], + "69770c2445e9cf4fd63e9991eef5a90a970703607299e07b24a4dd628b1395d1": ["igraal.com.", 0], + "6981af54d6eba2c30e636fa5a5d4aaa915c840c5d14b1fe0bdba7175dfae92f2": ["onlineconverter.com.", 0], + "69871563edb391450d66f6f73695c3a705e8ca239bdb01f605c974c3ac95ff74": ["chinahrt.com.", 0], + "698dbd6b27bd1a54cdc6320cba1350e2b59b00ff7338faef3ce80403be323c97": ["fujitv.co.jp.", 0], + "6998964705286d3eff376f08fb336d259fd94324781b62bbf3d1ac5b9a9680d2": ["questrade.com.", 0], + "699b4cf0eb4b11b4eba59ad88c22afd44acf0bfc2bfa68f430c88fff9afdda36": ["zoznam.sk.", 0], + "69a2a435f44fc08ce23b52ae51ddd96fcdf8281d7917d4968c534077d916f2ae": ["fssai.gov.in.", 0], + "69a64361f4065e0325f5af2613369a46d844ae0c12516a198ba34ce2f165f0ba": ["mothership.sg.", 0], + "69b139de9ee007a635aaaecde2613c8323018d453eccca4e6cf400269b173e24": ["clipstudio.net.", 0], + "69b3676f414ca6bbb303ea50baa28d3f2e48fd0747028f4afe2fce7a0258fdf7": ["6789.com.", 0], + "69b5efaa6bffea77de641c9bf7eb4e81c08bfe389ddf55fbcbd10cb024e44aef": ["chron.com.", 0], + "69bb80e191f5a07c149dde54f5bcaddf31271ff68fc31a9281056c4cbb24c094": ["icicibank.com.", 0], + "69bb92e56481a323bc19984913b5ea5fabdece585e6d00402f2484a57790ac49": ["vb-audio.com.", 0], + "69bcc43f0fc990c61bbc7d2ddc1e3a24335257b0d0df61eed2cc1f422575c5aa": ["n11.com.", 0], + "69bd933df97504d8203a86992b08e56d3a988fa9b91e4f6eaea6801ec5555fe2": ["popsugar.com.", 0], + "69c425fbc2a751b21d5072258b21fa81694adc361c0b9f5ef2d32cb1a8cf63f8": ["upornia.com.", 0], + "69c87300daf556502b00c8832245b6cdffe0076316b5c1f090e4d24a55d61038": ["escience.cn.", 0], + "69cfb1d17b56093f2ab1041a05ba6cb12d51abe17a454cbbc1e3136f6db452ea": ["amarujala.com.", 0], + "69cfe9aec725a82be0c67ff82d395de86ee5a2e479bac6f56cad633e04ca2a19": ["getcomics.info.", 0], + "69dcd5bf4aa15e0901cc3988f536ee28a4c10aaecdc535e4a0084479b66eb948": ["csfd.cz.", 0], + "69dd21294d84a5bbc349ff217a258a79fbc6825da9e785646d9e694e11cddb5d": ["bmj.com.", 0], + "69e5f01c5155db4cf065c56aa0369fb524497591b3be10d1acf34863581205c2": ["gocomics.com.", 0], + "69ea8b2ed01797530a9f99b19e146f3fe2ef44f18cabd14ba5f3b8db71a4931e": ["justjared.com.", 0], + "69ed57e91e918f32f0a34e7f9e7f100827beedebd14dd60e180739c73dbb7519": ["advertcn.com.", 0], + "69f06fa4f7807572ab2fb24a658b535a57ef984b8ac7372c7ea9f2a1f1107d0f": ["corriere.it.", 0], + "69f45b9afe828ee2d8df338399c73cae6332836bda20d6de3f38c31522cbc5a0": ["eltrecetv.com.ar.", 0], + "69f8645dc0a2d3fa86fcb366e0d082736e6848813df0632ff4ffb571caad6c33": ["cbs.", 1], + "69f9b9eff7c40c523dc946f97709035dbca45fc6950986dd2177f512996b0cb3": ["citilink.ru.", 0], + "6a0309a9ebe951abb0a60e0b0bbe114499b5b8e44664fe1f756d64157f906e22": ["respondus.com.", 0], + "6a061c0225442162fea7d1a281e6ae73edcba66d00498a64b0317ba2aa62767a": ["apkgk.com.", 0], + "6a06dad5caf2ad770a03a3b88c8b09b987fcdd341d7176c1f8eb113f1b17eef3": ["xn--nqv7fs00ema.", 1], + "6a07621345531a008017051bfda008a671f906c10c54c3bbdca62a2128d06deb": ["seekingalpha.com.", 0], + "6a08499af0724c5b85dc1568c3bb811c7e8f329208c67b202507190ae3d802fe": ["atube.sex.", 0], + "6a149a5511c10fd204dcc5c662ffb0bdce403900b3b4c5b2665c840b93bd926f": ["dnspod.cn.", 0], + "6a1b04b5a2b208d1eeb81fd761350cfbe65f8234befc4537741221894d171d89": ["wunderweib.de.", 0], + "6a1ebda879957a7d80ebfccc1383162dc09e7063752ded4426ca23316eca4178": ["vlaanderen.", 1], + "6a346a5fb9ac49966867d4235e3884949deff474cee1ee1be9651f2cb87a5ccb": ["hdzog.com.", 0], + "6a37c9ebbc232bcd09a15091bc10415af5fac582630b20f56b9e071d852346d8": ["you.", 1], + "6a401bf2c7c71a2b3d27dc689bfe270d958610d3abc52d01eff2cec1e5d4887b": ["intodns.com.", 0], + "6a468ca5efd2b3e258a694494f3d0d1e293ca2b15a74324ddfd2ae603d60aa11": ["greasyfork.org.", 0], + "6a501adfbd69437c81f15175c4e88fc280fb68778b35a9565d3a85274e291a9b": ["eztv.re.", 0], + "6a59fc030a4c3c996839097469073fdfdc160d16e338c327e8882f17f2f60aa1": ["uni-lj.si.", 0], + "6a5fb8b7f23a72cf1946fc15262151d657d862cfc0fe60ce1b1d706d2f26bdc4": ["turkishairlines.com.", 0], + "6a656d8b6d0c2cc443557bc06647e116edeb75825be7b9f1b43ddad148f9c93e": ["shisu.edu.cn.", 0], + "6a7281ff1bad5c36d6b4ced64350b3f94897ea2e4af5d4141b3bb714afe0d3e5": ["dlsite.com.", 0], + "6a7289f7e602dee351cb68fcb6683f215c16172722346c7718d4ef53d30c6add": ["estacio.br.", 0], + "6a7ccb0805a8af5fc575d879b54a90aa90a91bac8d10ecb0c4908b351c917769": ["fortnitetracker.com.", 0], + "6a829fe3b7f54034c0db7cb05a637af6bf365e5715dc4648e223ed49454e8025": ["shabakaty.com.", 0], + "6a867f537a810098f0ffb7027ee3ee8e0f02d12aecea8ae2b4c040e32f1fe3bd": ["moph.go.th.", 0], + "6a895fd5793f98d3c8743841861e17bdbccbbccd1a3e081f6195b5937d5cfdf7": ["epfl.ch.", 0], + "6a8989af590d6ffefa1fe873326033c70de7c9091d346948dc543b36856071f6": ["boxofficemojo.com.", 0], + "6a9c0ebef028c83b7b30bcc64114eab8f37cb176e0a0f07b1e4d5267a5de39b4": ["tdameritrade.com.", 0], + "6a9d1f414db7b171cdbadbfcc7dbb86e329dbf6e96c2ec30977545b5f6145d23": ["castillalamancha.es.", 0], + "6aa3673bc23f01ea3502c1ccc02dd13f3fbef0c224ea377b836f5c40d5b43f1c": ["fileinfo.com.", 0], + "6aa5b65ee70e203acf723cfb85aa1d4aacf888389bd7380f55d8bb31601d8723": ["sharjah.ac.ae.", 0], + "6aa8848f4553b88f214ebeb09fbb93db7fe63dc8396c841f857ed397e84f57cf": ["anses.gob.ar.", 0], + "6aac085a743ea1ef60e22e070a29b5785e1f8097ee21f8415fa6c9253569d22d": ["bethesda.net.", 0], + "6aac13d6b259883d47e6fbd91bb3db55935d4693bb87115c413b9b0005bb9671": ["rocketreach.co.", 0], + "6ab3a501c051cf0231db163559e0524c4de43dc8f7682b4a16006ac8b25e625a": ["wegame.com.cn.", 0], + "6abdfdbbc1f7aa05335feee285c937ca2f5324a3c3bee3116d79f3b6460c5275": ["alphacoders.com.", 0], + "6adaaba994f740b94ed300c600fa5772ded6c725af84f55b25c3bf96a4ee2ba3": ["lavanguardia.com.", 0], + "6ae751dea412be7c987a47888a00222dbedf5b510cf6ce20546b1d2d48a8a65c": ["waaw.to.", 0], + "6af0d4832569014be5452410d2eecab04fcbecd57f87a0f3abadef33b6b367fa": ["cloudwaysapps.com.", 0], + "6afe203cca8d3db45a7ed17ad1e4cee0328a053b9ecd7dd46504e6f193e8c490": ["electoralsearch.in.", 0], + "6b03a306444b521235268aef668559385cd81418274addcb18e565898eca42e1": ["zyro.com.", 0], + "6b11133a9afd5ffe1be6a44f1cb401dc5cc16551d25357bb8fcfb58731e7ae37": ["mizbanfa.net.", 0], + "6b19e1322ff3fa3f4afa48f7f1e2411541c10a0bd4352329aa496d6ac94fc3ea": ["glass.", 1], + "6b1b646c8e4617476125590f1037b940e544925c5f387e8bc8536204c2de6b2a": ["es.", 1], + "6b1c8713a7a5b7083e0b5ee10143ef57c97e1844290ca97960f3e9ac3221b1f9": ["immowelt.de.", 0], + "6b203d3f09924daf80bba7b5df03fcee47be5bfde720ed29ecf4aa350555291c": ["gripe.", 1], + "6b26a2e5d7f5652492c09cd97aa159ee6817a83f693b47dff7648d5f35923beb": ["bol.com.", 0], + "6b2d5a18c76d12f48226c630f42ab3e0e44d715f1d0ee1dec1a4a1f22db4183d": ["amung.us.", 0], + "6b2ed5e79f98e5c0468edb1456cd48ce0c575d5aaa2c8f9e529a3e77d840eb68": ["moneysavingexpert.com.", 0], + "6b3085c5dfd16b5031efe92b8b0d0dcd2fc27a23c95045ada650445ad2e54d0f": ["viettel.vn.", 0], + "6b4823a84cdc59129db8b3bd7b3a2a0b939dd7aff0ffffb6db382073fb94f177": ["eroterest.net.", 0], + "6b4c98cc8cef94bcca0c06b1dac617fe2d782c76b4ac624280c01ffafb96b0c7": ["tv-asahi.co.jp.", 0], + "6b4cb398b9e3e6535d9a3d63e920dda7fe0cd635f50e2ffa484bc1ad2902d5dd": ["motor1.com.", 0], + "6b54fa596f5594d6977792fed429450c1c218eac70b6b08c774404864c8b5bd7": ["geniusdexchange.com.", 0], + "6b65faaf56f3059b452c591fed92d69fa0896f6c0c8cba5b72ab2fb5839f7b48": ["elderscrollsonline.com.", 0], + "6b691280f28432edcaf69656a1a44885a7284dbbef870153a2187bd98a5f8d46": ["gamek.vn.", 0], + "6b69a01e0c73ae7c9266be5c41bd0cc2c347dbe95e486343639e1039ce6a8984": ["bridgebase.com.", 0], + "6b6c4d6b53902c556d4d49ac3fdbad906d5ae262ec17b956b1f735d2f7f0be6f": ["pcsoft.com.cn.", 0], + "6b7b8a59e517d79334067858e3378d562eaedc1c9d264d9a3340d2bbebeed46e": ["saksfifthavenue.com.", 0], + "6b7ba2a1078c5b10ce9f88a3e187361ad4720b02957f3d2dc158a3fcb8538f27": ["eff.com.", 2], + "6b80f790bbc3a70d3c9448cc79203e858e73dc67ec3d13e4ed392ae6a748fd60": ["datadoghq.com.", 0], + "6b864b741d2173c928410a9e122122ecfd99c67bc81f884641a4e8ee1a15520f": ["rmz.cr.", 0], + "6b8bc197c6a2325cba3f930976b7e29e803b80594ab4a59f5d1f8d90ad1a7895": ["kufar.by.", 0], + "6ba0e162d955f029946312b9ab2ecb7107e93ce3198773c01dbcbf7fa482ecc9": ["fnac.com.", 0], + "6ba40ca7b439804afe5e35f8d3815c9cc1661feba19ffe6efd0711170c7dd0e1": ["zdf.de.", 0], + "6ba50caef73fab47a7b5f99dca35c54bce6531f1e47340d0987ca422ac635396": ["lexoffice.de.", 0], + "6ba9a864b903d132dcbf0c09330eabbadbf6dc1f55d15cf998b9fb4371c69000": ["railyatri.in.", 0], + "6baed0f5661d573fe43f289d82bb3028ad723f81d70c04e88b2d8a1c0840d130": ["scholarships.", 1], + "6bb0d4a004a7b259acdd6982a1b4acf8b300e65d4fde501aa8b5650721a64d27": ["dm5.com.", 0], + "6bb3679268441dda273f76d76661633e693a4654b4fa4ea895d0f23ca64ec6e7": ["4dsply.com.", 0], + "6bb4c8e14fe4dc77a7a27a5d75c181cffa632c0c2907086c0f67fb9a55016b96": ["tech.", 1], + "6bc8f88cd48198db54cace5f291a8e233181d06da77c3b9a45c39e9470503b5a": ["mjusticia.gob.es.", 0], + "6bc998d5c87179c7cdceded10d38aaa4553f6a968385a5acff78acb075fd0d5d": ["absher.sa.", 0], + "6bcbebdc75ec84dedf3453335783ac240bfa5491d00f28af05dd1ccda9bd3195": ["bcit.ca.", 0], + "6bd2de78058786fa8e92f74d24ca7d80a0ee8f5cc5528965367ce79bd4baafea": ["webmail-seguro.com.br.", 0], + "6bd389c3ba27370bc08d0d42736e7bcaa5e246d3c51ce012bbfb604e4a7174b4": ["goodyear.", 1], + "6bd49e5b905ca4618052467b7304e78c9da300a90ae9bf9909196e543aa4c12c": ["ilive.cn.", 0], + "6be26028a6a6243c7fb455102ba2356732f98faa6b4c2c16bc741fd040fe93de": ["sodapdf.com.", 0], + "6be8042aa753532b3860f38895d5ff79fdc2efe6362f0b7eba74c64d02620fcf": ["tvron.net.", 0], + "6bf8c6e405a3291afc4a1280a50d4d4a058b4af3a649f1c3570d9799cd30ed73": ["talk.", 1], + "6bfb36a069133dd059c69fa659e4008eaa4a00da25309eb93609841b997069fd": ["ttu.edu.", 0], + "6c0a4f6a9305eb51be962eb1524f228e8b708e3a87fc16134e8205dc6684a99b": ["postcron.com.", 0], + "6c0a5c21ba9c769aa3448773b4bbfc78224de1b0c33ce86dd001fc3efd5c6da0": ["arbitr.ru.", 0], + "6c19b568b1b58683bfad233e23fa3d29fae2d8ac7dce28b50fbc62974384700b": ["newbalance.com.", 0], + "6c1a62d67f82b60c75e1cead89c35dcda14036e1a25e96578b6c490bdef7bea2": ["forgecdn.net.", 0], + "6c1b72c884d776f602bea10568e412a62b530b279143796a3badfc3cfcb0e26b": ["grubhub.com.", 0], + "6c20b08fb1fdd57d64e2483a0cfe8375b0dd05225bf58a1ebfac133b221ccd4e": ["cmjornal.pt.", 0], + "6c25d122549515c2e3e42c97935686ddb9661fecbed8e56b57d24bf13a80d275": ["ladok.se.", 0], + "6c2bd8eadd7d7f5dd4bc462e680f9bebb51b948d481e2823a0463282d8a5cd42": ["lancaster.", 1], + "6c2e9fffd1df9fa307d20eb48102cfb4abe353fc7be698f74d9a2eacbfd60306": ["liveabout.com.", 0], + "6c406f928028fc041790af31db53f3750eda36790a8c88753e1dc0f3b1e3ae75": ["miamiherald.com.", 0], + "6c48ee39e3b98a5a3bbbe7f1eb65aadf7db2182adbb7ccf6fd653b3ee3d47370": ["ki.", 1], + "6c49f751bbd113caa197fe4edf67b6153b13603fc37f21399190d7922bd85561": ["sex8.cc.", 0], + "6c54bc18d74040e944e9303bc2cbd930174e3bbbe48843e5facabf3ccd1d21ac": ["countryliving.com.", 0], + "6c6292e9eaea4f5c2c478d2fd8898ce6a99f501ba80240e693b84a6c3249799f": ["kuaikanmanhua.com.", 0], + "6c629a965e04757a9b9d99b6f5cfaf4d36df11cb88961788085ca756bbd92897": ["hobbyconsolas.com.", 0], + "6c64e98cf6078cc61ecde78b5ee3a6487b6d77eb82fe7cef02076baead5e5b39": ["it.", 1], + "6c66c48720d50c69eb4ecb62dacfc1f8346991d6af6344f0b812f346c9e2e99a": ["ambito.com.", 0], + "6c6d2dba90e1c5d151a2412a8b39318faf1e95f66d93d2e2ce3236578d8249d6": ["21ic.com.", 0], + "6c73a721ca5cc0b034d41a13bc471255a5c6c5fb5d5490a45d6f21216971e769": ["cruisecritic.com.", 0], + "6c7d390d7d950857a7976ebe2cd97d878ba895e135fc2a18b4832f35e4022dc5": ["epicentrk.ua.", 0], + "6c8376b633193f9ffc20178a92a054dd17d3173becfd72f824d8908abf0ccc5b": ["topwar.ru.", 0], + "6c849e2c74540f472eb99def5888a289badcb14e498e5202f08adc44275c43d4": ["setmore.com.", 0], + "6c85dd024f4b1bd8aab0439ca0c09cfeb23c1840fc1199b7abada3b92492ad0b": ["lepida.it.", 0], + "6c88699512ba8a350be5fb765f623d6f74699bbefc9bd83c1ccdffe0c60a5e81": ["mjtd.com.", 0], + "6c8ad5d1e962c127a4b45fc7759a1445f9176c83a7e7a6ca5ed0ceb86121d7e3": ["yourtango.com.", 0], + "6c8cbc917e49f84253f746e867b39086c1eff6761335dcbe1e8ae498d3482c1d": ["bignox.com.", 0], + "6c9faa9025b7047a3501c7964dc01e7c17b794bcf86b5ee57f826bbe758ed8e9": ["xn--zfr164b.", 1], + "6ca18107ff2b26d607288f33b3b3871495b5cfdafc537ba04acd27ef6daeaa3f": ["manutd.com.", 0], + "6ca42321bf85488c6e7ad4316a9c4dc2e667ced373d397751e01a18725d241a1": ["biguz.net.", 0], + "6ca888fdbf584e78ac4d68c03a3314f76c0fb20de629bf5e57f79af02bc701f1": ["bidcenter.com.cn.", 0], + "6ca8dcd6352f7d85d18b144c2147443430258e109b58943983a541941c4fd249": ["send-anywhere.com.", 0], + "6ca96e01ea29955fdcf88e26a2bdc11278d63ccbd0ccc818acc8b1c7b2ae6f43": ["cambly.com.", 0], + "6caaeb93092b176daafd22f1cef66a59ed94d46b2fa5264c8ade3586f305c298": ["supleks.jp.", 0], + "6cb205c13fc383ce85216481719158c5692c7fbff3d8fd565e62505ce80a07ed": ["bvsalud.org.", 0], + "6cb30617ebf2a2bb635e0d0c5cd3c47c8da0dae2f3828d7a67087df426dcf518": ["gamefactory.jp.", 0], + "6cb7d77fa6355dafa559f19f09f88cd353fb1a6483d49cb63e7941b67fd0a3b8": ["desmos.com.", 0], + "6cb96dea84fa10837276a3ce49433e846cfc61a34ce004e9b9c7a123ac62e7e7": ["vk.com.", 0], + "6cc881beaaf0bd3e128b18d68186bbf21ebd6ce9ece80548ef8d056edceafc5f": ["auchan.fr.", 0], + "6cca408ef7d7e898236ebc581f2861a2360599bf7179f3ca84eca79d063f313d": ["pchouse.com.cn.", 0], + "6ccfca73dd616d6eefb599b83103f10a0e94f8f3b72c7f5236365958c59f80ee": ["meteo.gr.", 0], + "6cd02e70f0640687f2b04903e2f01b695750fc3ead87ed917b35ea5c8823b2b3": ["sn.", 1], + "6cd1953d8fcdefc687fde9f445b21669d863b3c48a5850979255fff1aff3cc71": ["online-stopwatch.com.", 0], + "6cd38ad54ac3c3fefd3817be7e101afec0dac734e2b7c67b3244e8af7724e3ed": ["cmd5.com.", 0], + "6cd7e4db8b2b7288ff7ab31a24657332f60c8d66f79f45fd1e111e11700ef85c": ["renater.fr.", 0], + "6cdb6d938e5420645ba513603382f1f20f8afe523ab5ec3a945b803eab5a013a": ["pipefy.com.", 0], + "6ce13c176af89c90a0fc8fe791a382481b2615c6348bf826b1de7a62c0447289": ["office-converter.com.", 0], + "6ce2e08835f28d34bbc6ba9c50ca4ebef1d6f14588898889cbb3d781378b5556": ["apartments.", 1], + "6ce543265d49fd44f68d43906761b8151134d6af06c7f11c24bd57457d0867a9": ["pearsoncmg.com.", 0], + "6ce59b46a81630a33f9bdcb12033f60a0aa202562379a25634be2fb3346e23e7": ["miit.gov.cn.", 0], + "6ce5cf1f75724618e2a6d94f84ff8c48ba79ccb2af667f369f12401391b00d65": ["medicinenet.com.", 0], + "6cf456ba22b391388cd6ba1c7f69f68ec4bc36fb97303530d452d94d50c2ed20": ["fanfox.net.", 0], + "6cfbe3aac2001f001bc04ee01c20a479801986e04e9c0f484b064d9d9bbc96ed": ["mrmad.com.tw.", 0], + "6d053e762cb4d17e76f84376414e378286b52c56f8591883fde4e66d726562c8": ["jobstreet.co.id.", 0], + "6d16d7e8d90cd5e03e31c6f523cbc4ddae658c7931df171941554e741d4c5c7e": ["shoes.", 1], + "6d188ef4571f9e9c72e1a2394aa760cbd350a864a18e15f33e59e88f14e98a51": ["walkerplus.com.", 0], + "6d1fccfef00360158a2944136a96a7897d70b2c40861527dc10fa807ff4d6b90": ["institute.", 1], + "6d212f1b0e7bc5018394d2138fca33d3c9d3398e4e52cb0e42751d00dc4f6563": ["mgm.gov.tr.", 0], + "6d3f2e5c10bc204c5f2b0be4ada1c80872c5ccadb9eae16345de2e2b6d01bee6": ["astro.com.", 0], + "6d3ffff0e455a6817f5a743005f5c66a13dda345314bebce4e6ad2383c29766a": ["winscp.net.", 0], + "6d509e51083bab25e31bd43e74f9135a58d520cfa5b29a8579b606013f5431a6": ["cmbchina.com.", 0], + "6d53bd0998430a31f76f6a7714d7d25c66e9036fdf9c3080223dc535d9ade708": ["iqdb.org.", 0], + "6d5505965163831d814f381c14e4ba5d0e61be0b7e28b0ef1682fa3983f822f6": ["oberlo.com.", 0], + "6d5cf0637cc01f4e5e9d9b312ffcb0dc20de9c141da025fead49752616eab8f5": ["motorsport.com.", 0], + "6d64f695d21d46a9860bcc7d8b55be91083d03542b7ebc883848556469e8ab81": ["escapefromtarkov.com.", 0], + "6d6913383443027a4031fee140aad19ace7ef3445f3f05e5ab66dbbafb6e1fc2": ["getpocket.com.", 0], + "6d6a16363439123c0580f4ba0894c2d8d1430c3c33b7377d123a5b41d9978960": ["stackblitz.com.", 0], + "6d6b0d4f1712f0732a0240301489381a3bd385f4edaeb8e0a852d9aca1cdc6a4": ["helpscout.net.", 0], + "6d7252c3659ec522f28b63ea5e5e8fbbe8109c809e0698f4ced64d55a8d12bb6": ["easyhits4u.com.", 0], + "6d7549e31210f4fc245b820e70f1922bc16b8d56718ffa341d7ae613186804d9": ["w3school.com.cn.", 0], + "6d772507709488e802a24a687847d10710ae1c7d6e48c4b6190f909c4175174c": ["xkw.com.", 0], + "6d7eb13645fa900444e02ad9e90b449c1d6bcebcd4ba0e88fcaf8e23d61cf000": ["verizon.com.", 0], + "6d82c4e57cf2a9ae72b768b3ec4555023bcb313df028a07efc106060724bd51c": ["willhaben.at.", 0], + "6d86144a67a25a7d954dbb4566a1f6f463cc5218d130da2a772fc6685c1269d7": ["bershka.com.", 0], + "6d8e2b20c4bfc59936f3313547c747e5e681fdfeacaf35e111fc7bb32cb31b45": ["vidyard.com.", 0], + "6d940824e35c3de7118ebe65d110ad1145382253dc2bbbe9388a7595c252495f": ["youporn.com.", 0], + "6d9c4d84857ab2d557e4e7fbd66df8641b1c898547c11ac1020a0d8661af4b11": ["tvtime.com.", 0], + "6daa11b6b24d925cb6f45286620a9c5319b28731aea8b0c846cf02b5a40353a0": ["woodside.", 1], + "6dab1380fd07b4e18d24958af28fdba1ff5f58a08a00fb5aa47a2c9e8120d015": ["farm.", 1], + "6daf297b3ade60c03f1ac3360dfbde578fbe4fab9291b89ccf82770754a32f2b": ["greek-movies.com.", 0], + "6db4cec51761bc4d397478e54a91033a3409d8973a5e252b6229ebb8e47ec1c6": ["xn--czrs0t.", 1], + "6db6181162c041992efdab06ad99dff89e178e9678f1db941d6cb43e917730d4": ["cash.", 1], + "6db8152bcda165d2438ec28bf214c0fe01c956e964ab9e8424f96aa40f6c55b0": ["cognitoforms.com.", 0], + "6db9891bd13b51646ab3cb4b2e0313200b0b7c283e19f651abde4b6fb1dddde4": ["sketchfab.com.", 0], + "6db9a14e7f7b53d213cc884afdc276a60da9c7cb02a2bc20186e1461c9fd6e08": ["teknosa.com.", 0], + "6dbc757623c12c0b65770b5764284b1e4608535e4629fc33bc35dec48bc312bc": ["utoronto.ca.", 0], + "6dc0920cabfe0ca8a222a63fcfc7b72f5c6564e10116935bf994719a3f9b5e89": ["fidibo.com.", 0], + "6dc4bd77f21425ac048f47f1a2ded349d22c11edc172c8f0c215b158f4c49d00": ["eros.com.", 0], + "6de1ef62d6057715d2244ac6d5918fe77f2016818ff22fb3e0c09fdfda8aa65d": ["canon.", 1], + "6de5915b79c7ff31abc7143ebf8c0e0f009cd1b1d8218bff6f67c7bd224bb5e1": ["constantcontact.com.", 0], + "6def3f0e302f636bed9ac40bbe0453b2f4a6aa0c75f31dbd8fb4ea5e9e76f39d": ["jobs2careers.com.", 0], + "6df3a770e5bdbb745d7a98ca9cc2817a5885d908e59cf965e3a9cdc2c22994a5": ["studentbeans.com.", 0], + "6df7ba156af24a3eedbd3f553290a20f42f9cedfedee13fb83aca0f32601b7bc": ["b92.net.", 0], + "6dfe182d86facc56f436eb02a598da778574d3d5d11aeeafa8e63fbbb2eff157": ["serverfault.com.", 0], + "6e0c2a22a9bb8f1c081b66f463ab25528d6884303d1317e29b2fb3c89c768398": ["stltoday.com.", 0], + "6e17c42a94d634f006ce1dabbc6e1b6a134954b8229611ec6e7115fc924db4ac": ["edu-74.ru.", 0], + "6e19ea0c97daa848ec53e791c2cc32f2c4f0aec65ebbad2be8f255ce48adc1c3": ["builtwith.com.", 0], + "6e1d7ce0747621f371199f0a0ad35e2e7ab7cc141f64bebffea52f42f54e9c06": ["mcdonline.nic.in.", 0], + "6e24681b4bbd83db889ba89209a21b43f78c722f08f9c3b9e750d4c31128d2ac": ["yinxiang.com.", 0], + "6e291f6878176fdf2ddbabe2f3f7ee00110cf15462ebc84db9c0560a170ae290": ["xn--90a3ac.", 1], + "6e2de35292bf7acb905c6f0993fa014c58f10a5f76ecfa4b358a30c5ede1d3af": ["iesdouyin.com.", 0], + "6e36543785407c5dfe6124bca2898b13a72b5232870ea7122f01b22e1ace5ffe": ["sankakucomplex.com.", 0], + "6e3f1e9fe66123aaa117093b9d89f924c5dee8316e34b681fab6fa8c266a4cff": ["pornwhite.com.", 0], + "6e40b0bc5fe569bb4f7d98f8b77be1857330d8e1c3e346ca47dde753cb9c787e": ["phncdn.com.", 0], + "6e450fbf90fa5a94d64e2c8a221c91289319009f7b8e39caa601c9e2caad4beb": ["fon.bet.", 0], + "6e50b059ce92579a0c771c1e85efe8daa043883abe3cb206a2cde98830e11b2b": ["islam.az.", 0], + "6e5534b440f971f284811aba8b5785e52765e03a2826d96bb15ac84a0658314b": ["uio.no.", 0], + "6e5bfa33dc62e060c986aba9cd962b9f418cbc5fd84fe8cec6b6d4b2c2f75ece": ["xn--p1ai.", 1], + "6e612a5c45868da338dfd880c86e90d2eccb340631b4c4f4e412834673c553d4": ["taipei.", 1], + "6e66ce63b216970b9481f3ea5885be70f6066cb0a657dd283271609c37d8bdaf": ["blinkist.com.", 0], + "6e6a989dc688ecd9538778fa7b6c08d86abc40c1bfc85ce2e5c78c46e8f67b4f": ["shueisha.co.jp.", 0], + "6e74d206c6051b26b1591b78891e233ff42dd440aaf521ee6de603f06a252119": ["petapixel.com.", 0], + "6e75253e02c780b1ff6ee3b39f9c136f496125abbc092664f70b86d79717bc3f": ["winwin.com.", 0], + "6e8c968ccc35f3dfc9f87f1abba4b2efdd3b55b77fc6f2a50739d691a836d03d": ["xn--mgbab2bd.", 1], + "6e952b0c0cd27d40bc1b473673f16af2dda17148ffb4e5bbebcfc7a751607cb4": ["utexas.edu.", 0], + "6e9ac5824239870e9876dd028fe4279a6dd7cbccdd853cdde550701273dc19dc": ["xerox.", 1], + "6e9db763c3b4e97cd03c50825ba995cf55f5846fd0536053cd36e796b6db763b": ["shmu.sk.", 0], + "6e9fb17c56cc16a465ea6f1d6baf85fbe876c7f5751ef6464884e240f4e3583f": ["dhbvn.org.in.", 0], + "6ea03c9fda932c9fe8cb40dce307570e89a03fba18dae4556415049d3250f8f3": ["nissan.", 1], + "6ea24d504341b49aeb9e0257df88036306af1614a5a7d3086d9d05fb14af09ba": ["noelshack.com.", 0], + "6ea9f7fab6ca09bf87298fc3344345efa1b1a98999befb85241c3adb19bcd981": ["ie.", 1], + "6eab2d79a42a6ad3e6cea4992e5cb41c07fab4254848985373534c2c19f796b6": ["aje.com.", 0], + "6ebcc07cb67a21e96e2405d498a73533e353e3291d020f2032d92f45e5230600": ["ssa.gov.", 0], + "6ebf22cbf8982772debe1ea4bb2aaa775b9e06671c76acb8ab792e56e37b94c5": ["freepdfconvert.com.", 0], + "6ec7f62df6ef8dfa1cb51b6540ca9c815f3e0a26854bafd6dbc9340f9ae58f50": ["72to.ru.", 0], + "6ecac82f07255b2f4e652a670b4a15bfb3f2e76f99af45a48f999f61dc5e4083": ["ewn.co.za.", 0], + "6ed505698f3da7816e5ccad9d154eb169eae47d6045ff63ac6b175f55d716031": ["foxporns.com.", 0], + "6ed54207526fe4bc192b00697445939b37fd6f7b98475e191c960a404e8f4902": ["hangseng.com.", 0], + "6ed977c59c5946595c1819f8eda2b704c4283637479ace6d13ce520f4b5ad501": ["duolingo.com.", 0], + "6eddf02bd9dc1c32e47daff2517a3139c308e250cbc110b741bc9db31dcb5512": ["car-part.com.", 0], + "6ede83cef94edc1d89b562127645198fe129fdf42d6ac85257d1e5185b6f798c": ["de.", 1], + "6ee43a0588934971f2d3037e75fd7cdaffb581b9d49e0df9a1cbe15793028362": ["argos.co.uk.", 0], + "6ef6a1900b627a7c3aa7417cc47f99b0a1e6dd931bdd2d8995af3c87a6ffec71": ["toomics.com.", 0], + "6ef8e9fc7e7d7f1900638e77a1bb66674135cc1927db15605e9a96bd0bcc8bf2": ["hao4k.cn.", 0], + "6f0422ce7128aaf131cf55bcd75e1782946635517c2341ff56155b1867a93def": ["coveredca.com.", 0], + "6f0960f76ab80e431e3c73f87405a4798e8dd6dfe49557b858e7036b7cbd2417": ["kbb.com.", 0], + "6f0deb3d87c864fce24dfb2e257205e56ccf7b235783573a51f41abbc5543b69": ["celebjihad.com.", 0], + "6f11b9008b202df561bf9ba9bd101452ac59f6423438f0ee04718d0dbb9ceb9c": ["tamo.lt.", 0], + "6f19d0a549bd424c7bbb77a702a4901e2f67c0545096dca2248a3bb9fd0ff600": ["bg.", 1], + "6f1d80d0cef283fd8446415e63d7c4fc8e781e01129a7e51e8a62a08d192b324": ["nhk.", 1], + "6f1ffb9040e725b1acc5e5aac3ac3036c839d28d4d5fcc7e47258d0112951419": ["nch.com.au.", 0], + "6f21c8c1cf5ef6f839333afdda2ead51031acb96211880b884b8594e7c2259c5": ["icloud.com.", 0], + "6f2e5eef1687fba0b5fb78e34277e9db02378f71a7521a0350182d6b1facc54f": ["woocommerce.com.", 0], + "6f3312673682ad0cbce2d5b3fcf1f07617e199b037bf126ca62b8b6b58dd55ab": ["negisoku.com.", 0], + "6f4441e8f5e69e5932526c421be820edbc31c3b3a12d6f2cbb42a85b56910a86": ["g-portal.com.", 0], + "6f4fa9bdaa24fb2fa12c881836282a6ad20eb6110f251f289f7b0dcb8f00c8b5": ["toyhou.se.", 0], + "6f576f6038b94f8443f2347823039ffce9f3b4cae297256077b67393f3e3e070": ["xvideos.com.", 0], + "6f62a404ba4604e2d69e13db29af056b0ad962d0c7d01d936970e8913d4dcabf": ["mightytext.net.", 0], + "6f67d19fa89d7636e2f939495f5d06de5f71509e230a2d55dfd65dff2ecebe2f": ["riotpixels.com.", 0], + "6f6c4f054cc36f477a19742de35e614cb61f06ee8061154d05d36a5b57d1a605": ["ryukyu.", 1], + "6f70ad79c74d0d83f5bafe16e8cccd673ec58815f72b74bc23a56862edf02751": ["businessinsider.com.", 0], + "6f70f27e13fc073a2541cd1e8b38ba9dbd5ec6de7bfeb24328534c417697381f": ["one.", 1], + "6f72eab83bad3a2bdd08cb2f281492e540176d2051409d0498dd0324f3a91a5b": ["commonhealth.com.tw.", 0], + "6f85389b989a2b70ec49549671793ea7581548ba9aa9ffd0d553f8012f641204": ["vanityfair.com.", 0], + "6f93bb4fab60acd9f25afad3871eb16e6ea2a5448825110ed3d652b3cf9df0f6": ["vividseats.com.", 0], + "6fb0e322814f87c98c0d5f02531fa952d51f0f5b65a6c61f6d5f197d6d747981": ["telemagazyn.pl.", 0], + "6fb9652e6e388150394b52ab838a790d9f6bc36c55d061ea3bce0282f664b794": ["soloby.ru.", 0], + "6fba7efcfd169f8ac59b209ccd672823b9993d4693b7a293bf4c0a3566854142": ["thejigsawpuzzles.com.", 0], + "6fbf2f867f8c1f8c5f28eb72d38ddf641b3cd5c27664fe5c95d6cf3dd10cd639": ["korea.kr.", 0], + "6fc04457ae59a9392bc007e3fb825219f97d77937c2515ad299493c79ae10654": ["zomato.com.", 0], + "6fc38770bd5b2ef614307bcfed3a2097a3e3a8a72fbb4cd34d0336ef4666015c": ["tokyotosho.info.", 0], + "6fcb12009696f7e1b397a367471716898e32d8be34a18ea7d16c9c81dd581db6": ["baza-knig.ru.", 0], + "6fd6da7101d9f7361fbb16e05d965d24f57f58ac01b76af3476fbe172f104de0": ["xn--mix891f.", 1], + "6fd6fde0d1dc00295bb1b1f114d62d0d76ba3a38bf1ed765afc5567bd2366e49": ["vox.com.", 0], + "6fe44cfb3803cc8a0ed2f68b5aaec86c9ebe852bd52135b3dc529a1152664390": ["domaintools.com.", 0], + "6fe574df558e9d242aac0999a8fc6323df635b7b6d8839e91d1a4da5a23da67b": ["egybest.mx.", 0], + "6fe9846df8183d04870a4cff9814c8048d14fa34033914963f148e98d5735cb8": ["zippyshare.com.", 0], + "6fe98c37728e34b1eec451975658646b0b9d16292c4cfe73c5ae68a2b4258f9f": ["megapornfreehd.com.", 0], + "6fea4c8f27da8f1d2c1f7d82bc81e19ac72e83400b22694952ea3fd1445a28bb": ["darukade.com.", 0], + "6ff86cbeb533672318543a284ab2af472012979c6bbd20b8c27ccefca9f92b26": ["prothomalo.com.", 0], + "7010155b4c0a75d9261f17fcc42aee55683fdfd61447768c268a9fed2d353cff": ["mitsubishi.", 1], + "70130a889e7bac77adb6655c86418b68b6758c62b77b571c2f14a3140f63e630": ["fastpeoplesearch.com.", 0], + "7014dc73e5471558c206213d19c9bcd758af0d17882fe279a94d29a10d1f3920": ["storiesig.info.", 0], + "7015f8e58147b03158ef7e841fff39a733b888fc35cdfdc9e089eccb79098030": ["staseraintv.com.", 0], + "701955d17a5f126466286942477edaa927dc29d298313d2a4a0d9b142fdd5149": ["couchsurfing.com.", 0], + "701af5f5911ae0f89dd19187d5aaecdb9a353a4139c8aa1307acd764287787f1": ["flibusta.is.", 0], + "7025e0c9de5642f29778b5c9972bfc19c17707566bc34f34eac1b1663a62a975": ["opensubtitles.org.", 0], + "702a0da82ba78e1d3be3d58ebdc88521a920ff623234b50728f8f3cb6e4c7a40": ["arabdict.com.", 0], + "702b409307739ad1bce3fedd14dab440c13cbbf72dc05b114ae76a0272027ec4": ["digminecraft.com.", 0], + "70328a3b5b05deaf894ff24129e6a57dea03dbd5aa5278050a06f8acde73f7b1": ["debian.net.", 2], + "70351791b57bbeb2cb6d27d9b7fc06f6aad8c78064f400ec47552ae2248b85ec": ["gameflip.com.", 0], + "7037e1194efda8cd985267bbfbb2113ca2c85563a565acda7a3227be632c052f": ["sepe.gob.es.", 0], + "703a677d04912c4da25b15c62ab784bb48bafae87ec083fce90d6b8bbdae49c9": ["haberturk.com.", 0], + "703bf7ce76681aa740901e1e688f92b0612763d2323648e0a929ffde9089d449": ["plus28.com.", 0], + "703f21a5509f748bcd95ff2fedfb72738cce52596b725ce1a405cf930b2e1d80": ["ygosu.com.", 0], + "70439c879a55af104264c0cf7091f7c9fbcb886b6153f68b0de62f01a76af73e": ["meduza.io.", 0], + "70490d7d38ac94d1c1e4c499f2fcb54177774085bca77431178f1f9a089dca20": ["princeton.edu.", 0], + "704a7c540de188d0c0c2127697b85cd950135c0ba49410a04334c571db908cf8": ["the-3rd.net.", 0], + "704af762e6b36fff74d85ecb7057f5e42b541629f91131235a124fd9be2c2b12": ["tudou.com.", 0], + "70551e929ac10ddfa60ee51dbe278dc5a601ca3c5bc7ed49f7f310e98f07e9eb": ["elkjop.no.", 0], + "705b4d6c198f121a937143c53adb799d15570a8751f67e69a4dfd861fdd1cbdd": ["adcell.com.", 0], + "705db4a6186fb256d6c663d42d8ac58180d37ae6db896fb675368d399ee93405": ["oliveyoung.co.kr.", 0], + "7063ce44929d3976b90e4e6d54a57af0cbe248860736b9c726901654d4291bfa": ["dotomi.com.", 0], + "707328f0fdebcafd6a1001ed5e35671891d947985b50a5a9f39874aed4ee2348": ["bioon.com.", 0], + "7075115e38077c6cafbfdd4e95e06c2d9a7e661f2605ee4e4e7ed5f10b759a76": ["searchengineland.com.", 0], + "70792cd14c1eed80b38663e4f845ddf330626d530b23af3dd088c72ab36c3511": ["kingston.ac.uk.", 0], + "7087a97045252d6fc0668417e05c23d9d876a0ea3fd9c6cdcf41fcf1d47729ba": ["idealo.de.", 0], + "708b9b26fb9b6631b2428a0f7d2dcb7d9d03b5eecd585e140edcdd7252d55c81": ["graphics.", 1], + "7090a4b73a5fcd7620aa1b1ab250434c73fbe599ec0209fd6e315339bd3bc5d3": ["stan.com.au.", 0], + "7096d8d16dbea9bc371a6eb7f01ab9ee10ef10e1162f3258e117ce7d1b92f3d7": ["zenmate.com.", 0], + "7099e14faf70b4c11ea3655b6eed2e86f2fe554266b84d8a206814c97862e1dc": ["mcgraw-hill.com.", 0], + "709e6b2388b630617b1aa4daf8ac14ccd17fec32558646ca7b1c660bc2054497": ["educamos.com.", 0], + "709efe92e7e02314f19456adaf84560e2b76e5b99c071c2e557c2d8cc689ed8c": ["divyabhaskar.co.in.", 0], + "709f08697255df3cc83daed0bea90c50d45697df3f5842fdb1534e725515c982": ["8maple.biz.", 0], + "70a8c9ce5ea13844c2454e7d05afacf3600d8af0794088c778dc403f4e854397": ["gotowebinar.com.", 0], + "70ae18478e2bf06a7e4efd3359bf9097f30a3c0e8f0d626d93698c0b3232205d": ["fapvid.com.", 0], + "70b0634738c4f01eb302b6404cd950003a83b1ae7d3789fcea680a6585da845a": ["kalerkantho.com.", 0], + "70bd313e73817b4983679aef329ce531cf097a39e9cda02ff6015ea14ed70699": ["fwdcdn.com.", 0], + "70be1d4707f205702433e27c1ecded70cc3164dec77bbaff7b8a893449b8ffd2": ["jisilu.cn.", 0], + "70be2d19120a54c6daba8f8a39209e068e85226b41e2783466bd370808490dc7": ["pearltrees.com.", 0], + "70c8202eff8e1ce5801d170a12c4e64923e44668e4939040d93015e8a9d64fce": ["chase.", 1], + "70c96326c39963ad7ba158d08e25f88fef34f22a1328096049c93e4043ed3c09": ["bonprix.de.", 0], + "70cafd91fc3c10f899fa129c9ca96d4698079bc4a01373d2efe0c9af0d518263": ["lojadomecanico.com.br.", 0], + "70d316b4e883ec6dad162aa03bc1145ea2a3b6942480837840f78fbc3d8ba2a0": ["savefrom.net.", 0], + "70da822009680ee2068afe19817b4295c34cfbc3e99f90e55c837159b625fe1d": ["zdic.net.", 0], + "70dbfffb0aeb3a62569787a8edfd59ee52e9ed916eb42ea9a7ba416f6c0a1aab": ["myclassboard.com.", 0], + "70dc2aca61b17e2cd13f38d43bdeda42a3cbdf9495ca8116b28e3fa945e87339": ["dygod.net.", 0], + "70e791d2f4885acb881d5128a13b662147d347505fb1b0eed7555c9915734107": ["autopiter.ru.", 0], + "70e7ad26024132144ba5cf015a4d1dd725e7292513f875f1e160b61c18fcf1f6": ["select.", 1], + "70ef01e5ff4b553a7ea40e4c75daba0cacd441e7da656447ad1a3aa78b5fe22b": ["bobaedream.co.kr.", 0], + "7101b68f97c09e6a1a925e868ef29f488d93d553a693224ccb9b839a2f533f5e": ["cpic.com.", 0], + "710b504899740c44d0d6ef69d8b8d7d386156687566dca14b7ba239325820f91": ["notationcapital.com.", 0], + "710bd741028d8fe271d01b43e854f5769b3c11676b69a2daa7cf7942aa202cfe": ["bancoprovincia.com.ar.", 0], + "7121c26f83f7da15da648a8cf4efc22b9fad5992f32c2561aea6059e973903ea": ["baccredomatic.com.", 0], + "7124dc274bea08226097e497efc1364044b2edb90fcd7b004aabe040bc9cc610": ["redbus.in.", 0], + "7125547ebe4e01080403af1b8288d8d67893f1f18d75547c22427ee217684cb8": ["chocolatey.org.", 0], + "7129ef38b611adae010bc81c3cc6e3c8df1a3ebc70bf8ad0c2b76aa5b05e919d": ["maxmilhas.com.br.", 0], + "712cc1d1ac31a138f948d707bf7ed7dd61238413f69cf101275c2c52b03a3330": ["yopmail.com.", 0], + "712e91cae014c7a7691ed8f9de434e3b73b1d48fe009663f19d2adb2f60a81b5": ["logi.com.", 0], + "713b64e91714d7ac721c1dd1c9048c12b00408de1d040a82c2aec9e1d8bc80ba": ["dongtaiwang.com.", 0], + "714103b5b0b14563a672b584975900debe2d72c05923238c111b6d8704f68e6e": ["javhd.ws.", 0], + "714aa4a0de785ca7c36433a166c7b22a256ce078c811cadd732bb1ce4e65b90c": ["yesstyle.com.", 0], + "7159b8505e0dcd8f26307d7936dd4c2150b527bea15a54468345fa6e57038a7b": ["bniconnectglobal.com.", 0], + "7160eb4747c8051352c9dbc8f7a4a949d5f6952775e6c56b2fe5065effcb7fd2": ["lrepacks.net.", 0], + "7162be8fa001531881d70890a148aad759f1a8378bab72104b460a4cf929657c": ["paidverts.com.", 0], + "716d83cfb7cacdf64420e8ac664b4698c2bef617af1f9e6cfab0e5adffcbb700": ["smogon.com.", 0], + "716e5068b4eb0c533b3b0d44bc01c173b47a66be646cc6a11a86bae96359b387": ["pinetools.com.", 0], + "7171b2c21a0d2b284c4c3e0cfef7bf3ad334643d54c16390bcaec73b883aa00f": ["infranken.de.", 0], + "71725ecaa5c0231c075f2796973ed7aee12705ddb81bb9432c42287a7ce60bf0": ["banquemisr.com.", 0], + "7174b31f6f59aa4fc74302757b7a3278cc5c62c9235ec3a557acca2e8265bfb0": ["industries.", 1], + "71767c4ce5c8e606c20709177c6fa35e8ea4baf3aadbe140070392f7931eeaa1": ["interac.ca.", 0], + "717fc4cfba74d50128affd0837dd6e5328e1e1a0df290464cd1b2435f6a2e83f": ["mapnwea.org.", 0], + "718553cdb58bd56c02539a8199c26e884db7cae8da930f242d6b6ebd660a320f": ["ustraveldocs.com.", 0], + "71899399a3a6a9ff10b4de84268120ea70d8633730a5fb78725853fc4d127f54": ["gymshark.com.", 0], + "718f014545e12c0fe3e39bddc810dfbc60817d9a7a6cfaa1b6558e5e77d58779": ["jornada.com.mx.", 0], + "71935c7f4f5f2e79a532babda8675219cbec2a44763074505991c8651e73bc4f": ["dosya.co.", 0], + "7195b7b7e9a91e908f417d2197c56432e9193bb8c6db3744ac328d0e473dce72": ["doubleclick.net.", 0], + "71983a917dba481d498ff34c868a89be147c94f009917d6903ea8116b4c89558": ["allabout.co.jp.", 0], + "71996c80a41bec9315f510b0fdb38a67c62e420baca250bd31d09d6d5608657e": ["autovit.ro.", 0], + "719d336d5a573b79f35f194a1f2a9e1716da0f20381a32facb2536b270854a74": ["wwnorton.com.", 0], + "71a8d01848c59c9f8cdbde6784e704a81acf750df45c6a5ac0123026db1685b1": ["imufu.cn.", 0], + "71b50e27e9a2d86c34544bee579d6d32546bc75bcc650b813046fe5d4dea0ebc": ["healthcare.", 1], + "71b58aede90f42b66dce90c938e7b22907c0b3d60366ce83d1ca694e75149c89": ["blikk.hu.", 0], + "71bce6bf43b7d1908a456e903f45d15dd44a2dd003044c634bfe30fba9076b10": ["12371.cn.", 0], + "71c8baac92f5c0e10636f090dbc865949fc29f276dad6c21868c03860fb5dd3f": ["ethz.ch.", 0], + "71c91247a79bc24f42294fa03a5c598d2c73a78aff3bba1bd53bbae9fe8c630c": ["sberbank-ast.ru.", 0], + "71cf5b2afa92683b63cef6d19d4870db39ebbc367173a3bbcf50aac7cec4d56d": ["letpub.com.cn.", 0], + "71d1b6c4a5f3e11496787257433b12f667d64cfff5c2726491a9f19142a74cec": ["cbsnews.com.", 0], + "71d20d4be5f3af868a3b9cb7b51c67598599325df1efe880edaef24103b51bb0": ["aixifan.com.", 0], + "71d235e090e430a60ec8e0d69db2ab971f18b93eeeb0c2f9a8d300aa6cc73053": ["virgilio.it.", 0], + "71d272699c80385133c67ebef83fa831f6ee11fad8a7f7d5d7a40c1d1ddc4520": ["federalbank.co.in.", 0], + "71de408e767190104ae243a979acff320c9c7e9322aed1942e088d06656a4a36": ["upv.es.", 0], + "71df2008cca92a37d38907eb08e88aaa181a140cdc27199cfbcc189e12e979c4": ["cgbchina.com.cn.", 0], + "71e041a5b650ea1dfa75608aa059d820279e0f944ea0355f8e4280a9404c5e87": ["ero-video.net.", 0], + "71e5e2839cbd0b7956e7ac7df679427866d28d71193c91731d05728abef22ad3": ["dlocal.com.", 0], + "71e7f5ce4a479aeda9416df80a33a4f4f6b7baa3822a155b14f67600dbe9f6fb": ["vietnamnet.vn.", 0], + "71ea4ddb0b3c11d725e2d341b4c70aeda6426f4d9218afa45edfd9afa6878c31": ["eepw.com.cn.", 0], + "71eb4dd9a032f920bec5af9e7d989f7b98f2da67759c7a3b9a15440e5e60315c": ["primevideo.com.", 0], + "7201fa475a3ff46771532c7e04e06635a32ebba47ba8788fc54f1dac8ac9a383": ["hesabfa.com.", 0], + "7204303fa6ff35a8a5cd7b2bcfe0f59780cedeb85d12a664c70db13c9e64155f": ["bayt.com.", 0], + "7206535c66e1b4185ab803551548539eff5768a64dd67d5ca532896d19a73aa6": ["mailchimp.com.", 0], + "720b736e5e1a6335fac05eec6525289723f97ca310a22e68556b3df989fcfea8": ["spreadshirt.com.", 0], + "72194434ff16de977a5819c4fa3ef04ccd22a8ad1dddee3cbe076e3efbfcf258": ["zaixian-fanyi.com.", 0], + "721c77b013373d8ee208d07f558daeb103ab4f9caf5de7fe23828858b3ea728b": ["singpass.gov.sg.", 0], + "7222d883a633cb5c0054faf555fa4b71482df449d91382b2f99196ae6bb254d1": ["hbomax.com.", 0], + "722459304edf50e459fd9f483973768d8cc338c4d9ffeb82945f4151f5d25f30": ["jobscan.co.", 0], + "722e4202920b631bfc74ce7b7de5c02d964ec5a1de726ae49a48e7c0931e347f": ["momjunction.com.", 0], + "723689c186f2b00c277def7056e9c7f245fcb6c73ef8a692ca96ce49adc539ae": ["dpd.de.", 0], + "723dd03c33d59fa8766bfbd96563ef49653547540d7481b47caa4b36b0f04110": ["ci.", 1], + "723e7b858c5fc217fc1bbc34d06b5172efdb4af3c96c81effb3ed51b6caeb4bf": ["edublogs.org.", 0], + "7241ad55c102cf4481196d7805fbb9c133a8ed67ea051f10029c9596fcbbd84d": ["dtu.dk.", 0], + "724204e6b576f792e402b8944fc70716b25a9c3022710fc254585298176cec9b": ["simplyrecipes.com.", 0], + "7247fad435840840e70d5a83315c0ba2c402407002740b05a6e05e895aec7fde": ["lepoint.fr.", 0], + "724fc55102968fd63871bc93ec15d08ca5d81e2ce19c36c277be7f7c33c19753": ["climatempo.com.br.", 0], + "725679ea80e2c412b5a5925bd49243ee3344b5d9f94b32abfac955bb5fbecf46": ["cengage.com.", 0], + "725764ee9c30aa8c26d058ef824748644323a0480ab30c8879c2387728a4a873": ["nsw.gov.au.", 0], + "725a0adbe3d6f30b5222407537485fbfa236472e3e91371bc87e7f238b9bd3fb": ["undp.org.", 0], + "725db35facd9c8f6fa5557472dda01fdd2b10c621e15417bf364379a3cea2a79": ["uniondht.org.", 0], + "725e85f823b47dc21be2d3977eb27d2178180476dbd0d95e52aec925882a0151": ["gettyimages.com.", 0], + "725f85e7ed842ef0df7f543789f5a20720a8e0c36bc28154a92a5f25d945f2f6": ["americanthinker.com.", 0], + "7261b9ff823b95e54a21df2ae6d69386843f151f67d52c88017881b2d56c0291": ["strayer.edu.", 0], + "7265d94f4aa13d4fad49cdd976f6453ed6c1ad4e322ef50acd381c5141a8dac5": ["caixin.com.", 0], + "7267504f56c74f4d1f224fe6630daa63b80db36b9227bcbcbd9d1484dd1c1d58": ["ros.org.", 0], + "7267a5841037a04733cdcec89259b80080d2077f093dc591b25850de60be4346": ["httpcn.com.", 0], + "726f3261a8dbde6237680ae4e52e306858dfa4cd1bad00c45490248337d8b220": ["elperiodico.com.", 0], + "7278e0543426091114bbae5d5deb945845500793cd6965fe6fc23445383d2e9b": ["pucp.edu.pe.", 0], + "728c47d42c27e14e4147cb42c10c864d975d26fdc12eecf6540cbcd0288ef344": ["baitoru.com.", 0], + "729101355dc25ccddc1df773de19c578045ca26af8436d5b9565e9baf4dcf77b": ["cameraprive.com.", 0], + "729303665bf77919e24c7e73f5b149f91b9e4d82d3080367ef32a7833801fa30": ["tofler.in.", 0], + "7295eb0881f23346a07950840f366323d92d0e136bb166f80fdcee221db8e463": ["bongda.com.vn.", 0], + "7298b09393f1424c46be0eb1535285e5365a3ecad88dd88b2fddb1e5fd378c38": ["redflagdeals.com.", 0], + "729e5648c1355f67befa061a343e3578c43913b136b68ffc87cde5f0069383d0": ["gamestar.de.", 0], + "72a09e770c5f825d681e5d64673fcf47c6f6800b0aa3d119de205a41c5474617": ["junglescout.com.", 0], + "72a0ea5e4ad7382694a2f7c6dff773324cd5a672ee7f38d2e3ee707e9015ef94": ["jamiiforums.com.", 0], + "72a55752d89620153bd9f2cf2a6884e5fd545ac1ecbf67b0096e844afbe7f1d1": ["cartoonmad.cc.", 0], + "72a6960078471a11a97f480bcb6de37bf6d8ce3f80f9eccb4c58bd24126ffb08": ["openwrt.org.", 0], + "72a9189b6fffd4137396458bbbdc8d815a809a27d5eca91f5f5c2b327fd23e54": ["rajasthan.gov.in.", 0], + "72b57950d69249373fc3ebf08421c4a9f76b54d27f5b49b84fc351228f717b3c": ["appsumo.com.", 0], + "72bcd06adb1c3cfb37c0cf0b7a45ed76e86873a6a26d9de337ca9565875c75f1": ["medonet.pl.", 0], + "72c187f1d49c5d777d394e17fb3e4723021f470eecd71c73ff12a9ef14b9da86": ["mudasure.com.", 0], + "72d2a266302575329d1bde70cbdd6099ad7209b32ce33ff47697b28afefc3059": ["hotmart.com.", 0], + "72db75688e4c45e1febcc11abf1a8880eba515579eb50ec703c16ab4a4169155": ["cimalight.vip.", 0], + "72e237ec10f2b29da1e14068aec11f0eaa54ca5003aa7d0a94a6a09abcd9820a": ["colpatria.com.co.", 0], + "72e875b2d7a1938a1d4502361b39acab8235b1a879ca334b7738e5880aab31b6": ["nico.", 1], + "72eb5788d56aba0e3db3d54c4aad45cbe21a918f05708595a172c58308b4c3ba": ["1001freefonts.com.", 0], + "72ed779d52edd86176e6613434fbb6b57a9a2c5843e8065f54b3aa2a87959f78": ["xn--fpcrj9c3d.", 1], + "72fb6efe8b38cd13a9dff64f6b6a2f76390c6c2db13af4e5d0596eefbb632d42": ["hibid.com.", 0], + "73007d8f5925d1f1f03a61e4ea8580affafb1c76aa52e4bf96c750766f47ad48": ["premierleague.com.", 0], + "73031467fd76fb9a5efa2c79bc27e290ea98776495782b5626ef688650a6ed64": ["teamblind.com.", 0], + "7323b9ed0b679fac57112266771592ffdcd82893ce96e1fef259620ae1c9d647": ["baeldung.com.", 0], + "732fc54ec84d7ced9599a6eca7485a1be40868edc378353d7da1fe1a8da665ac": ["tattoo.", 1], + "73314788f363c02dd77bfd999127d3986ecd274ee71000b11db8791a269301e0": ["escort-advisor.com.", 0], + "73403a111c13e9e00d62b490487789366c4f27b221f28d330ebbbf1dcbe401d0": ["shopping.", 1], + "7345e20c9b989ef73906750fca2f6c53d61314c0c9cfac34a5182636b58c9398": ["webaslan.com.", 0], + "735ac88e4e35c20b10dadf4a3fcf74100f5d05396db74d559abcf740c93cb3ff": ["androidcentral.com.", 0], + "7360e8ec930c834fc18c39b8481e11b5264345198d4676dffaac20d15815acd4": ["tokyo.", 1], + "73617ac579d7b1bb05dd56d104d7c614184e839dae280a6301cdebb1dc73d0f2": ["ally.", 1], + "73619355c546dcf469258002a07c6e26c20c2f44ce47cf18fba67ed3fc9de6de": ["nationalgeographic.com.", 0], + "7361c79d7c817921720b70a4b44f44941f9adcfb20e5a664b89ffe41f37b9a4f": ["gfbzb.gov.cn.", 0], + "73725c9a05aa2a466942950169a154c37cb4d4d7e63c75f29a8b983459e32b05": ["imgur.com.", 0], + "7374cf934fb13010de64d695bf72d3a9b171282cf2032f624b6b65c0dbdd5073": ["kat.am.", 0], + "737cf8f476992965d24cde5cc2341914d0adbaa36944eff87266e49a40d82055": ["hwupgrade.it.", 0], + "738627e0ed379538801b0b4aeb7b3e3ed26958da1d77e93cff510ef61c8be75e": ["xiaomi.com.", 0], + "7388782d3ac9203053e814241f65ff8d9f14468c448ef4d83003389b6470e1d1": ["metmuseum.org.", 0], + "738a180bb2e663fd5cf8999c5eb01514c72e92c37b48abb548201f8c70a4eaa2": ["leroymerlin.ru.", 0], + "738e7153b15cd99d2b84a61c454833d465ab62780fb8b49f25b8678e14fefbb4": ["compuzone.co.kr.", 0], + "739258400f77ff484415b946d664334a8beaa7d985932b1ebbf4a205da7b80fc": ["trezor.io.", 0], + "7393101585da8b986d38a1beb0e36871fb070a9a532bffa587ae4922087a47c8": ["mydowndown.com.", 0], + "739579abf848415a9ceac8f84482012eabd58f59875dd48112c9e35b709f01af": ["kotak.com.", 0], + "739aeeb9d604bb03918bfff518c1aec64213bd1157866afa7a215b1040bf50d0": ["amctheatres.com.", 0], + "739d8606fafe87470d9942e37f50a584f26ce1ac3b5c1cab3da913e8b3ec839a": ["techrepublic.com.", 0], + "73a5b095d1a7b064b01673c4f5e0ee28781e70949e62df9c5dca770d648cc4f7": ["xn--wgbl6a.", 1], + "73ac5e7f518540cfc5bf3b27b5fa11ef7b8d0c8963bd1b5488e0c761d0b25cb5": ["emalls.ir.", 0], + "73b8f56a7dc93bc9e4162c283221d53fb36767b246f3e22d85c34da2b7b2f4ac": ["zerozero.pt.", 0], + "73bc350dcab419de471cdbebb6365ef2c959b4b6f0ad6cb94200d108a4602f2e": ["manulife.ca.", 0], + "73bef423908a252f4e979445c77932a514417ce5ef7d3593a9872561aafe54ed": ["kinox.to.", 0], + "73c691e584e249cc3b3918e6efe5960056f57c944bb9696aa2d7e28acc9c46d4": ["wmzhe.com.", 0], + "73deff561031cfa25fc45715bf04afc85a9d9463a4645b8e8ee108d12a66edbd": ["ktla.com.", 0], + "73e1218432010ab8f554ceb4a215b567184bd8f5882327a8be42fce08484f214": ["aldi-sued.de.", 0], + "73e5ac1c3ef4562855160a145ce67a9aacc6e415907db8eaffa33d728d87c93d": ["yifysubtitles.org.", 0], + "73e6619f8ddf28def495637eb19d1fbacf79a01bb7dbac2f2c671649d26089ca": ["benq.com.", 0], + "73eef9af400915ff69c34cbea16dfbf54d35c8bdb886740e4a2ac89d77034829": ["nudecollect.com.", 0], + "73f023def481761983ddffe6ff524d326f4cb6441fa23795da5bb8752fb0d47e": ["hypestat.com.", 0], + "73f8f8594e1d0861c70746d3b95e12d2858e8c91e7346c9b6a8cb4202382b361": ["citizensbankonline.com.", 0], + "73ff01eba289fc32c70fff47e5e421cfbdd57235880c5d7090697ea2c7695d3a": ["merkur.de.", 0], + "7405d64ff11f631e64b044c35e0029d72d460a4df13435fa4580afe523b61438": ["dailyhunt.in.", 0], + "741ce4f4591213889eb60cd315f33dfc4e8197c5b9b91a8b4aa236b4456dbec6": ["gumtree.com.", 0], + "7420569e7f8fdc1fc80b66629c49ba6f4195a3449984292feed58613f483158a": ["3ddd.ru.", 0], + "742b9fb75e2f72b12078f262b9c92c61bed12a344d96ada5dddde0bc38db4af8": ["spontaneous.niftylettuce.com.", 0], + "7430401445fd833c901e9592506f7b365c3e6acf79f8c40a9deef1722872dee8": ["porzo.com.", 0], + "74319511fcdefd7296d4851dab964869d123566dc3b9d574233fa3921bed5bd6": ["capetown.", 1], + "7447ce9246da1dbc00647e265a3abef013dc9f817f20d5bcd4682b783762e751": ["cymru.", 1], + "7449191f60d159da03654b024cb168fdf8667dd5b68f3ae392cbf20b63be804c": ["uci.edu.", 0], + "745083de0633a1c3af9985a06cf082fed07f70a4c4325767c3f8b53f75ad87e7": ["penzu.com.", 0], + "74536a853c3c566939fce0aab3a695136a8c5b9ff8a804991f8dc8d6e3a2d13e": ["easybytez.com.", 0], + "74567ff8ba08688b6bdad30102016e3b00dccb4bc4728a8bfa839a412d7e6339": ["quikr.com.", 0], + "7458acfe4fa5ffa1d096a869e3742d0f816cf15dd2eaa0f196938ca0cf5128a6": ["profinance.ru.", 0], + "7458f824c5a2b85cb0e23b2c8d1db31f828062aab3d6a9fff245c9e226f13d7c": ["electrek.co.", 0], + "74593e9470a139c13b2de3178310de9fda507067c318e183825b4f05fb021633": ["mia.gov.az.", 0], + "74740d0d6d78d0951c2d55d2912cfef31525cabd5df40ea00efe0921925abbd7": ["tgd.kr.", 0], + "74750d9f10fd657e6db208c76ad7066bcffd62308e2e88ca84904b67efe8a309": ["trustpilot.com.", 0], + "747614ef5a9fabc54bf418a6f662f53aeae4d6ba50be465395bca6f73994e5e4": ["click.", 1], + "74767ed128c347b0cc9256deb72f388482130723f9ba144633fcd451c1dfd219": ["atu.ac.ir.", 0], + "747ebaa991c02475c66ec5f785c92120f0418b0dc63f1f9b24fe956cbef405bf": ["prime.", 1], + "7496fe65daba52ac2394e13e890653221240a1ce3d9948266e167cee59592278": ["yimuhe.com.", 0], + "7498d1149fdc801ada7ec1c033d0a4c03d310c176698f4747048a50ac6893d1c": ["docsity.com.", 0], + "7499220f70bd071ed35fb10340197c8bc80c9866f59be6675bffa7192d03b6ac": ["mymovies.it.", 0], + "749a36040b6f3992cdfd94c76ee2c01bbc0b58d9b64675c8cb61af920cb6a7d0": ["saarland.", 1], + "749d34caefe2b6f108835e97fa2ba8fc952256eb5b6c65d3e1f99f0b55a8b48c": ["meteofrance.com.", 0], + "749d4a300703d766d50a32c177875147c94df0eaec8de64de73ecfc6b5253137": ["silvergames.com.", 0], + "74a077c4e9a8ce4b76aa45cd3b5fed54b91e7c257ca0ebdfd8948fcb530e04a4": ["grainger.", 1], + "74a0a9741508c2928e23e286dda909017c7108b4728cfe91a9dd9f2ac809e940": ["5paisa.com.", 0], + "74a697b0bc3da3629171d568fec890383168face65386c2097516e928424e377": ["bangkokbank.com.", 0], + "74aa625f4079b2198e776f48cf15dfa76c7fc45b894ac4a38a612fbf09591b72": ["keenthemes.com.", 0], + "74ae84bb403bb02abe4e6fb32fefd9a7934dc1fdee7ab42e5d1a804a8a8397ba": ["vanderbilt.edu.", 0], + "74b75ec9955c156e6f2d2913eb3655e112a87733cb979c0e673e2bf338c81f4a": ["andhrajyothy.com.", 0], + "74bb8ffe43c39de37097ec2a3f231d8a31d749b5b1b9707ba34615dc544affae": ["macx.net.", 0], + "74bd832a59bf7c64d0baa0db346f1f0476c0df5d7d98c594470ecdc79bdcee87": ["a16zcrypto.com.", 0], + "74bf73bdb10489222869d01af15bb33035bb5d76d7897692b00029d5ae192e71": ["imrworldwide.com.", 0], + "74c0cca50d0a7b55554947a765610c4c4f480b8d6811abb7ab8c2961f5925738": ["fakku.net.", 0], + "74c5b6cf37e9903a6e052e17b28d54df15c0670b4c1249c0cb3e5fc2ad7258ee": ["bbs-tw.com.", 0], + "74ca94282c1d9103173768a6e4efe647b4abe4bf210d5ae5de7d1aa95ac32877": ["socrative.com.", 0], + "74cbb5bd7cf74f7d6ea2f1c6d9cf2e6df8e215b809ab6797789d7e7d2188e812": ["namethatpornstar.com.", 0], + "74d4a346f8a5a55a1b6ee4a336efe767ce689f8e5b28e5f29470fe42aae3a7ae": ["barchart.com.", 0], + "74dbe1c5c922c3195ff948a76f484c9dd24591efa29fc16a9e00a01216db4cb9": ["iporntv.net.", 0], + "74e2aaf76a3d405e97e477205693eb9ccd719000d34ef0c675098c4e46c4f575": ["porn4days.biz.", 0], + "74f0b3d3a8555795dca12165b6fc34b101988ed53c2d69001338f228480aacde": ["sogou.com.", 0], + "74f0c8d03f3f23be00f37b50fbb3a6efe8f8b92f77d0844d54e1c3187ad6173e": ["libaclub.com.", 0], + "74fbad1230dbc1060814588abfa87530dd8b720c979d241693a7ad117dd1ebde": ["eatingwell.com.", 0], + "750e78f4e53e3f5b9dfa2cdca91fea17e9a625fc882f67bc1915edcd3eee862c": ["usphonebook.com.", 0], + "7513e13eda86a43f9a60f261cc2d6299a335ce1e1f244f03231435e853f60f2a": ["animeheaven.ru.", 0], + "7515f7729ebeaaf1f91a7d4fb9035f0313472d2ad1adb763b3fbb61315a24879": ["directindustry.com.", 0], + "751e12d68875e87fb8e1af08ca850be83311ca9e33835f40e84a238d6397c689": ["dji.com.", 0], + "75291c522036c2e6c68bbf74b40104b38124d11b8fb582cf27fa405b86030d12": ["edwardjones.com.", 0], + "754df15c1a0e63e90ca2c4f60bdc9ed05020f44eb4e1b6315a4b923d4bacfadc": ["mosaiquefm.net.", 0], + "7553c62027aa2a991e65cae5817ed244e4e8a17da15f4c065e5877e732de1cb0": ["pornzog.com.", 0], + "75595b7d7ac078aac9c0f433449c44a9e85c21c92556251c8ee2904762b5ed13": ["nesine.com.", 0], + "7559913b38cb2a9701a8aaf431114a37eee2a1d40cbeb215b3c5e82049ffc68f": ["hk.", 1], + "7569aa76ef0ab50a8f6550a3020d98eaf60623bc2a8e1d0dc6391a71a8c1d4d9": ["tips.", 1], + "75711163477d32a2a17ebb3d71f3e52be8949c0a47f72a08ab9afc128927dd2b": ["vin.", 1], + "758150535935091bc40e293f2c3cc6ed32872f0001f2c637b4c80be9f17c800e": ["ytimg.com.", 0], + "758161c89008ba7c8beeece14ad0b81dd0761873210c127343611304aa6149a4": ["2chblog.jp.", 0], + "759616e7fdcf7dba951015485ca7661a9c6f062a6f830c5f06ecd099ab079476": ["avbebe.com.", 0], + "759b6fb54394d7c1bd54bd0ce128ddea960782e6eb5e3467c0b3c138e1d57cbb": ["pars.", 1], + "759bbe8cb77c482c24a6f0ca176f4bc736bdc808cb4991f899f062974861414f": ["progressivedirect.com.", 0], + "75a300c0108639669bb0c8bc86432a269ac9be56129648452c371632cf5aa0cf": ["dxsbb.com.", 0], + "75a5681f2bca854f9081e1be60e1fd10bbecdf9617abc70e501b27d5ca357e07": ["t24.com.tr.", 0], + "75a64e8c80efa1d02c5fcfca972c620b37600e774433fc02d0b35e59dfdf93aa": ["europeantour.com.", 0], + "75a68db197b50133364b53bf5d3a5250f595ec5a82c6a56f94ee8c5fde5320d2": ["parivahan.gov.in.", 0], + "75a79242039877059530b38bdb7124ce9f6249fce876ef75ace60b5f999f1d03": ["bgm.tv.", 0], + "75aa6ce3b69d1c2e4f910905063e4c96b139eb2fc9d762a4757d2b7bc4ecd271": ["sci-hub.ru.", 0], + "75aa740d7cc16d00a6381c727d773d91f063a1b9edcdace535d6a8e552a4c6b3": ["ksl.com.", 0], + "75b29364c8889bd1b96697479c9b531daa66f62537a302fcf53bd082784c7554": ["sporx.com.", 0], + "75b2e2fb08f971d29aa8fe782a2db1f033a4595bb25c63d95459c09def71309a": ["africa.", 1], + "75b489a25c35df8cd4a283d9cfa62afeb95d206647fc6140714bdab349952661": ["chinatax.gov.cn.", 0], + "75b67fe4f2a19b9b616e68e674bf5fdb6cc13ff15d0841c502d0e35cf7653321": ["hellporno.com.", 0], + "75bd51a194f52e0f8f26344c29821b4ba21488db601f06111ea67258c9dc9535": ["intel.com.", 0], + "75bf15c7add694f4756776bf146a35089fca73c45ac597f7757bd4ee337d6cb2": ["feedback.", 1], + "75c106a6e5949ef0be525c5ff3a23463798fe458d24a061a9340d727c385ce19": ["bootcss.com.", 0], + "75cd14c987a29333feef97df5ca5c73da6548243d639706b536df14cd50b8532": ["bestsecret.com.", 0], + "75d4728c6d5a9bb00d18fba8a32b28832c6ea258c1228e23a29ef7f91ac331fb": ["kiddle.co.", 0], + "75dbb5d571e4def48314e6bacbfa170f1dbc2094ac2c9a9129bbe81c0b4f254f": ["pixlr.com.", 0], + "75e015432b0735f202cf32bcc3b483aadb2a11d5642683f8bc1c218d236020db": ["manga-zip.is.", 0], + "75e3a919c374727172b063de3a03eff6922f53a0faa6b5c484859a32aad87c27": ["thegatewaypundit.com.", 0], + "75e46dd4c83e87576f89be2d469364186322072538dfa63825cf47a3ee7e6847": ["pcgameshardware.de.", 0], + "75e5364454fd3178301c090e2bcf75ecc7d6d4ecb805fe8e6a039cca367315dd": ["ajio.com.", 0], + "75e67486dff8ae9e13d9c7037e47b7cdff5a451080af6cc59fac34187fa0edb2": ["atwiki.jp.", 0], + "75e8aadd5f4d88e270b158ea15585e6ef32c3aa4463e8b96519cb522af084c0c": ["urfu.ru.", 0], + "75eb0a97c4ee115c513918bd24e7c5af936e9d3eb573ab844ccbdfff59d951f2": ["mixi.jp.", 0], + "75f84cabbb365990c9ee1029d88f06baeb477b68e5ee64c08f7c41623d557071": ["kfupm.edu.sa.", 0], + "76003cda06cd8ddf53f45d4a9c17962139b0e38df3015bb87581ab9b916d8bb0": ["freejobalert.com.", 0], + "7600a2428a56ff95a07a27ee3e5839539a1c85e8a9762b90083b1d231995a713": ["imageshack.com.", 0], + "76037e3361ec46252b629cd1b54f5bafaccc2a977c90ed3877d3bba815567d6f": ["fakings.com.", 0], + "76054d26ccea27b9f4ad7cd2a05000599caf2a8f1c071bc956567866fe04f81a": ["mytaboo.net.", 0], + "7608ac9d5c8c05d134b5cdf9c8a51d02e4791d25b64b10613624125e01b16486": ["webhostbox.net.", 0], + "7608ee157edd807f33af6123212973bce2a67b9552762c70c8c0cc5b2b7acd0f": ["citibankonline.com.", 0], + "760c0891184840e688b3bfa030d27ac202f662a6f641ac0c425bca45c5703539": ["restream.io.", 0], + "760d774cdb3798e60d09bf9b89bd3b0b81bf6d863bb6296ad54e70806f119c68": ["livetv.sx.", 0], + "7616345ba47203134e925a49de05c3c6908e5df2123dc226fda2dbaca5d2c392": ["dclk.", 1], + "761818cd4c653757d2e695a94cab97987c4d8a7d2e4a72d380e6cbad166b27d2": ["qoo10.jp.", 0], + "761a97e1bec5b4e09fb379bbe228fa6340a1fba214ab366551a77ef6acb62462": ["chinatimes.com.", 0], + "763107649b1d5478941a39764aa190ad37117a66598ab800071aa854580a2e6b": ["filmstarts.de.", 0], + "7631dd95a8e55e02b1362f3c383d0d7f0b3ea432a66ff5d45bc4d65fafd4dd8c": ["zzap.ru.", 0], + "76365bf9727e53f5cbf95fe7cc9f1c981f194cd7029e34f3db755d6808c45b64": ["bladehq.com.", 0], + "7638148ebbd4135cca27804ac802f50e9bc3bb3cf3106c6ab4a7c0168fe916c8": ["halifax.co.uk.", 0], + "764c2d09f34987d35891dc2fadea6b221610cf11beb4c3cd252c858813e92297": ["iaai.com.", 0], + "766cadf902d11e7847a6d35092c1cc64584bedd91991288f58530159500fd604": ["news18.com.", 0], + "766e4f22ccff9afbdb5b60c510f37a56e0c2a94c33b7b076abd57428d3c3a448": ["xn--gecrj9c.", 1], + "7673fa1e741b837a41ddcb32150927b4d6058f44d221bb1cda0f73b5a7ecf697": ["novoresume.com.", 0], + "7676fc4328dc91724db5ffc55a3754e0c8b0a38cb39dd714886992f0568f3788": ["mylust.com.", 0], + "767b520382518707c0c27380d95de72912b9a6d8d3b41cc6372eebcaec1a992e": ["wordhippo.com.", 0], + "7680c41f1ffb13a29f7c82ac59acf817832f58e7c27fb9f89455c5f43a94c51e": ["baseball-reference.com.", 0], + "7695f099257d359190f17aec0f6a97f52d6bd7a4230695b7cfc28481bdbd43cc": ["vertex42.com.", 0], + "769e40eb7292c2b8230e0069c1152f71df53ae26c0d7c0226eb5efa40fb721f8": ["ledauphine.com.", 0], + "76a8a919346fb51d1b702c24b6bc0de88fe22d31de6465357fbb862278d84e69": ["mixbook.com.", 0], + "76a9dc978b32358e69c3b76fe4c0f76d852b0890f2d11b11315934039234679f": ["groupme.com.", 0], + "76ab2c522adc09bd2bc7518337af7108483e8d526774f4a103e1ff8819881bfe": ["ruliweb.com.", 0], + "76adbcb014296691816e414e409168983e60b377f00b7392de0a24aec61f4523": ["familyporn.tv.", 0], + "76b3ed1bfbc3a5dc204e23a91d4bcc0f4ccf3c46edc8d95b0fa264cf628610f5": ["uncrate.com.", 0], + "76bbf66333dde04c720a1e40c6f5d0a3795d6817c2fd2327ecbcd072138466f1": ["probiller.com.", 0], + "76be0f93e762e8c0027c8b21876967244ea010e5589f0afe44b8e3c61a99b76e": ["cnnindonesia.com.", 0], + "76c45f2a931cf7babe133d3716409830504473d71a71fb7a99e71b485e52e3fa": ["box.", 1], + "76c5f9d39ca2beabec6578a4abcf1bc1e37fc45223ab4595a6185303316ee777": ["aternos.org.", 0], + "76d0328bd1655b013e30fd0247dfe19f2c2a8a6de07cb3046df2c8de2f72e0e4": ["xn--h2brj9c.", 1], + "76d3bc41c9f588f7fcd0d5bf4718f8f84b1c41b20882703100b9eb9413807c01": ["foo.", 1], + "76d673fe3d01b4c62aa665b972260db81363b296bd8755961c45e2b82c917974": ["epolice.ir.", 0], + "76ed682f085634172f57ae9e553b569a5689276b66b450550e895a2e79bb3ab4": ["itmedia.co.jp.", 0], + "76ef5899f47af430b2d3387cd374cef0ff78dd1b7d3f700659bbca212b59222a": ["popcash.net.", 0], + "76f9558e6d6aa2b987c7180cf7fb5ea33c611872ac1a56db78a90a1477a9f725": ["cfake.com.", 0], + "76fa7ad63a85e74f3ba0b1b529265e83d3b7bdd03fef147914245f90a66eddc4": ["senasofiaplus.edu.co.", 0], + "76faa02e1fc4cb1c1cfb72b2ae5f82d183a194c5e21bf632c624c404c487fc2f": ["xiaoman.cn.", 0], + "76fbf6977627fa0b3b48dd366f111c023003ae6c101ff6b5cf11ce7366247ded": ["gamestop.com.", 0], + "76fe68464b3d48ada5fee8962ebb51a29d1a52dd9fcd2e8273bf49536f2015b7": ["wiwo.de.", 0], + "76fea17bbfce691ecae09db968db682e262346ad2f91e3c1bcf3834bc4e8c3e0": ["pn.", 1], + "76ff6b05407d497d98e597bc1244463274f914098fb8902a2a73aa8157078a7d": ["moysklad.ru.", 0], + "77050583c52760387151ce7bde1a4e6026b99c136b8148cc5da536b38ed9e237": ["getresponse.com.", 0], + "770989a7841007025f2f69f043851adfe1904babb4691931cf966cce1489caaa": ["carscoops.com.", 0], + "77270577ec9d0a89789c960bb74b2426f25b9080e5a2c7d3d7544a9d3f79f964": ["kavenegar.com.", 0], + "772beda754459559a4951dbbb4a17102f9a4cd3481f01c8c27ed0b47392c319c": ["mcdonalds.com.", 0], + "773018e5e153d6bc7e37edaab29bb8bc4cd7c74cd67db0d04ba6b7edd0ed6651": ["hupun.com.", 0], + "77315dbb087513ce2b9ed45a1eb1681e5cf6e9b5ccf6fa76601957a4d6f5d40f": ["babepedia.com.", 0], + "77350f9457b4e1164eaf0f456ca125a1d53a9c417860c5a14e4f7d3a75f60ec0": ["movistar.com.mx.", 0], + "7742e7d6f747596c2642993b57a52adc7b692836ed032d4206a5c1173155de2a": ["fenbi.com.", 0], + "774ab94efc26bf095a315f3f7524de023cd97f7d1cef5b8aecf8da4d5fd82f65": ["markets.", 1], + "77584f0cdb7939f12b0691597693675680082d7f024a1eebf4ab383c44bc544e": ["flyfrontier.com.", 0], + "77587d56118fb12e55b59c0ba1e8e471f6fdbf381177d6b1feb38d8a35544e4d": ["hizliresim.com.", 0], + "775a857f5e15cbf4f70a51e0673709ae728ae47700cd2846b52a20b35ae49d0d": ["oldmanemu.net.", 0], + "775bcb31e23966cfd9d4d9fcc38e425de13f931785753fbc53b4cc55c0b15de3": ["tvspielfilm.de.", 0], + "77673371ff81f2e439b61cd4b908477bcb348dbc70f05d21ee73266b8c9e9240": ["livechart.me.", 0], + "776b83639626731bb612e947330b20ff2976832e04a339cb3105a60b28555e6e": ["sars.gov.za.", 0], + "776c4c96d6d48c0d2ca6d2ef242dc0aa06d6f19f0270ba3c15dc38d49feaa923": ["gcolle.net.", 0], + "776d5ecffbb96a4c9c58bc2dff68bd4dc877ed2313ea8554699f70c1ee18161a": ["ameli.fr.", 0], + "776d715b073b18a4229d7ea5a65f043dfe1bfd2f98cea0d45d61fd0bfbf672e2": ["cmu.edu.", 0], + "7777624d1b36a57701b2213192f8fb371b093122aa6c76441fbcf9d654c37236": ["mangalib.me.", 0], + "777cfe2fc1dd6885ca78690bcf1e3f9cde7f468eaf3574f6c18687d7908c2a29": ["kg.", 1], + "77910cdfe0966f450abeb1602db18bcd82d1f06181f8343e6cef698ecca5eb27": ["abplus.ir.", 0], + "779407800858eaa741cba47cc8840379c3a2ffd4902ab114b930ad5ea247b6d2": ["arcgis.com.", 0], + "7795326b7cfde88fb643bff0f49381bfd626979b5c434e0e168ddd6c5c394552": ["52poke.com.", 0], + "7797230d305d2f3ec6fa91214a2ca54886779f46ab785fbf297b8de93fb3ce7e": ["salesloft.com.", 0], + "77a2b569236cb907ab76d897d6f505fdc961b7bdeb969b292c30cc03fd34e155": ["jeep.", 1], + "77aca85255f2f6967a851fe73500cb989517459d49bf1af05a207827c230e816": ["wotpack.ru.", 0], + "77acea18a90d10c4ad8b2971dbad82d4d32829877ae678e13877462467f098fd": ["twitch.tv.", 0], + "77acfb2837c2b8488be6ff777b9c0aee3872867d488d4d4234d6db06aea9bf1f": ["oxfordlearnersdictionaries.com.", 0], + "77aef7d8ce3716cfb101a59f3b472d58b6c75aa4dfe7e859856a708c81952fbc": ["autoscout24.de.", 0], + "77b1de349de8db35a749b94258579be5fa49af2c074981f7180cbdd865c17621": ["weibo.", 1], + "77b871abaef7aa2db9ca475c6108fe57f5b2a176e9e24d41c71b2d0b034f102f": ["gmx.", 1], + "77bcfe9f1f2dad5044be849b3ab0bbf8645a95ee278ce3415a96c2d7e2d638b9": ["purevpn.com.", 0], + "77bf2e11bf6b8c42f6f1d40147f1f67cf7b98cdd55167a17d9cdf659337f29f4": ["teespring.com.", 0], + "77c0882091571914ab412a79597327c46f3f5711d4324e1e8079b9ff3ac3b83a": ["westelm.com.", 0], + "77c52ad5365403c71786563b4df411986aeca0b54ea0e927271212b29431658b": ["amazonaws.com.", 0], + "77c6443b5afae09b450bd81eea060b56a3e9a134f6adda833c35fb44cf28fe67": ["bahesab.ir.", 0], + "77c72f9c7bef09d96c51b395112a9fbc80e48431113f6050fc4fbd80d7a65b2a": ["xn--fzc2c9e2c.", 1], + "77cf6d05f89e92db67f9f1056457f5202b252ef89582d3a4d9187cdb94a100ef": ["aramco.", 1], + "77d44c5d6762c592224b13f998e65e6ea40b296f12a2b5a4c94647c10636cf9e": ["csiro.au.", 0], + "77d5893704efea381ee0432c67331aa9bd365bab8106de3f4afc77c65605bff4": ["oreilly.com.", 0], + "77e169cbab9fcbb3fb622ea8c136b605cde2ecdc012c81ca10efddd2f3d4ec99": ["zameen.com.", 0], + "77e7a27b42e704a8bb0c970c514de041aa79485ea824447cc5d291d8976c3c81": ["yourlust.com.", 0], + "77e8aae5fbab30b48f63e2b9cd9dd3bb3784ef2edb54399a449a5b146cf9188d": ["whosampled.com.", 0], + "77ea610de860ae66f662220e9d2d9b4fde696714fd59f87b5947b2cb6bbcd8b6": ["cleveland.com.", 0], + "77f7cd48e5980ef7c697f16c1092078e181ed83377d76b4591acd2b30572f712": ["11st.co.kr.", 0], + "77f9a9fb751d91a2b86acb7dabd3ec2a3b7a2238484067e3c80ba4df44389353": ["kabutan.jp.", 0], + "77fb71abf6d95a54fe7f5300942cc3bd9457b42e18811214084e6695d5579873": ["emofid.com.", 0], + "77ffd2992022d72ecf229cca64515e694ae179dc2310615d3cac45946b6e6606": ["digikala.com.", 0], + "7807b115192ea53795d119c7723158cb6684701a0a0244edff58fd1fda364f41": ["centrepointstores.com.", 0], + "780c4776a0a1d0dbe0d841883121f3fd48bb51e0769b0f264ed1cc7a793fce50": ["virgool.io.", 0], + "781745a220f346ab830d7e5d876aec58c08d67cc808607e08e9b7204627da4ca": ["tineye.com.", 0], + "781f24da45e3f660e10c31991be4b7e5d83ccb3949dc7c6098e5148927b9b8bf": ["pdf24.org.", 0], + "782467b015093705e9c343ffcb545def2a0fb0049a6f741fb751e6c70f002bc4": ["sahibinden.com.", 0], + "782778fe4154521a2c69fb293e97776b70766bf974ec5d5b11908fb0455c9639": ["e-qanun.az.", 0], + "782be16e8cd3e7728bbd42997e1fa4dc13b5864d2fe31c9bbd6ef50f79fb34de": ["nsfc.gov.cn.", 0], + "78361d3913668f02dc9108cb872c1782fb0b2ec14c6352cb74a0eb63622e9cdf": ["storage.", 1], + "783d1ba42681ac51d97c359449b9c3b0c32a9261fbea71f399cbcd01577c742f": ["shopback.com.tw.", 0], + "78448b0cf9a88880a56cc88205d116cd712ebfc2ad8b73df4f885aee3fe2f1f8": ["asb.by.", 0], + "7847d9d475029b221737897fd9182708bd70d5f1fe585c58d259ab78f91f27a8": ["okazii.ro.", 0], + "784a4bcfacc2422f12a7239613af54685b4cb434c560a1d4be945606052ce41e": ["rpcs3.net.", 0], + "784c8f217f438469945c99723c754b4d61d8fc870f3dbab9a5747557101c2751": ["hdhome.org.", 0], + "7850c9eb2de7096473a64cecf3cafd30f090dce31ef67f68603b63c3fdc8a7d4": ["fmovies.to.", 0], + "7851aa414edcc1b147adb58b3c4bebc64b5950290c1656445cd419530745fbef": ["sina.", 1], + "78570b6034f34801084edf34f8c7e99d1215e569a0310681b19705a65248f09f": ["porno365.rest.", 0], + "786aab10b3d8ba2958cab87ff718ea203bd871bd63c6450b233ba6bc20af2378": ["tvmao.com.", 0], + "7877e2466667ba96977886cb241c5b607030d5a1eb2873a45107746b0cc19161": ["bitcomet.com.", 0], + "788134f2f8b771844891381d0393d096508a46976663b1f72b6dfc4920e1236d": ["templatemonster.com.", 0], + "7883e367ef04afd8fd585f0b9edc7577a8b9f928cc15b26ad185d5359a4f7a5b": ["mangools.com.", 0], + "788a0e8fa7ee5f7169e3bb0b623a16b44075afa2fe465d907f41ce2e624ee523": ["muzkult.ru.", 0], + "788ace84e81142999643fd74c2311e7e5e682cf8bdeddd17420b84b1a86415bf": ["mandarake.co.jp.", 0], + "78906c64a7ba30d618099660cc11a7b2fc58098a5f6a81dda4af60b0a1129a51": ["newschool.edu.", 0], + "789504a68aba98b2e8cdc2e9dbbd9b3a23c67e6a5435d27ede80bc89ad7cd9f3": ["kathimerini.gr.", 0], + "789a99967e3541f7eab94bdc72b8646cf358ec9d54a16bbfcc723506d97c532e": ["coolblue.nl.", 0], + "789ca04289399d134fbe7229514ac6fe6fce0fa67eb6f15ad1b794c439045760": ["sitejabber.com.", 0], + "789fb108e4d33d70e3128f6c9f83cd01799a29526bf6ca6d0e1d36a2e89a8bb2": ["mosenergosbyt.ru.", 0], + "78a598ca23b59073dd18888029d2decee5c9db0dadebb497c9a80254e67faede": ["theknot.com.", 0], + "78a6bdeda29926af37639fd01e51a1641f6e44a908005e6c1c3ee2c1235d3f1d": ["rapidgator.net.", 0], + "78ae8734d31c332dc590071fade5e3f23acaa056a75b03d7dcd4becfdcc76f39": ["techadvisor.com.", 0], + "78b66f31e8561c2e3dc8b5ab8456710b471189958ee195d720e20d409d9298ee": ["coinspot.com.au.", 0], + "78b8bfb681492e484cbd55cbba40dd00bff0149f1af9c504500942286b711d9c": ["befonts.com.", 0], + "78be851411e96deb5ef9f162d97762709072ed84a99f8f2308b845829b0ed489": ["arubanetworks.com.", 0], + "78cca776a8480e8e065741396990b28322a4fd730548f88f5bd585d7e595f4dd": ["church.", 1], + "78d09f3adcb6675f1ff5c80ec3f95868f1caa38c444d2d48b62b0cb2dd7321cc": ["swiss.", 1], + "78d6cc8faacbeb0de17465cc7239b411123c6c67a95467db5ba969b4f967c1a9": ["rhbgroup.com.", 0], + "78deb9c8a4932bc976889b9231c5f229ff9eb83e7f19a6021f38013218ec1b53": ["jetstar.com.", 0], + "78f3cac124699e5672da218fdcbb1d3ada9cb5361157d762151de26096680649": ["talksport.com.", 0], + "78fe2e17b8fd65a938d922d0c6763f24b594dfcfec600fef6c40ee92ae0b0200": ["bose.com.", 0], + "790285d9690e98ca5a41124100c6e81e5db7e62876d3ff6075e49bb1b3d8ff70": ["vndirect.com.vn.", 0], + "79068fc9f7906297153df0a7ace3d9b1b7be4a27a7f66db339d70d338b7f3ea7": ["aliway.com.", 0], + "790d6cb4ce23e71024b747cc84a50fc004bda12eb3e4a3e96a0c7fafa7f22a61": ["poki.com.", 0], + "791033c993383be05ea9242e02c2eca4428151e4b3db3b4fa37720e2dd32c880": ["bund.de.", 0], + "7911aa791a2a896cd215f0057da689d67e8ae3094ec55de1039850321438e4e0": ["seat.", 1], + "791256452405f793d0519d75272ddfdc7cf4d1ac3e48018a7e40c80d13b1a3f8": ["libertycity.net.", 0], + "7914864bb3a142d09fc1078cd428154300739d9646af58d136d8e070a3ce14e6": ["norisoku.com.", 0], + "7914fb7cdb2b0d54469112e1002171231a0b763e302705842e7697f4fc356357": ["usbank.com.", 0], + "791d6636e059e6478292ec7a11781a7400a4011ffc72cc1036f7601f76d9f62b": ["yes24.com.", 0], + "7923193e4190222b2fce8bbf1336d04368cff0637d13d0bd11a89ad721aeb5c2": ["voya.com.", 0], + "7925d94bcf303afbe561c9abf6080efcd9e23f53b6656dbe2cd02a42f9437bf3": ["wyzc.com.", 0], + "792f457b1ebeb011452518f2f5825678efb040e2b25f01a6c640361b96f39a76": ["tubepornclassic.com.", 0], + "793095a53db457758c888efe4469739646df59f60dd7f82c1d59ac5b99073f65": ["iol.pt.", 0], + "7932265c0dad171463a3508f3a32d1554f672599482139780c63948a96ecbb7e": ["manhuagui.com.", 0], + "793526393d0eb35a5d0d51bd57f6f78de640be7ead6543985745ee8fa08f92e2": ["yahoo.", 1], + "79469d9f83ac8cc1bc2616f44fc733f6650a09754de4a065dad3a8a276527403": ["myabsorb.com.", 0], + "7947751d5abac02c13aa962b0436cc63f249a5793ab4d0e47dc1e5864fe1198e": ["simply-hentai.com.", 0], + "794a310839e13c1f010c717c1392153ef733c825d9cc6e5cb953f4fc8349e319": ["nmac.to.", 0], + "794d20be277255e08477b743cca8a97bf8b36594ad7b98f97a0ea22db429c16c": ["tagesspiegel.de.", 0], + "794d731f07d9e36c99b7b7461ccc9ea31e4abb139b16025861257b95fca84ad6": ["fss.or.kr.", 0], + "794f965c51b5b6ec6f024be245450c317fc5a6722273baf6b2ae6b485686bde0": ["lizhi.io.", 0], + "795373497c7bf86872d012423148156a87def044d3675256bb02775e16f443fd": ["nab.", 1], + "795963a604caf8fe7d849a76eec7cd4d51b0b17c551977469caef00781fc5038": ["read01.com.", 0], + "796fc608f2e1208ae0d45df6ebe93905ca855982950596a118650f68129198a8": ["sucks.", 1], + "7970a29919cf4ee6e298b00d04e999da2ebfdb5edddf44593ab014506c2a570c": ["codingame.com.", 0], + "79777f0203c8dd90b5ed3597b69eec5150c29e52e54f9ddc4ce73f4edabddb39": ["bowlroll.net.", 0], + "7978ef287953d5d6539bba550c74c1629fef7a59352d9b19d2814146eb2c201b": ["studysapuri.jp.", 0], + "79795626d6a3f4cac9f99bd5a3c2329c78095385a8a90dbfd3c9cafc313a9bd2": ["topurl.cn.", 0], + "797a085e0a5e8085ab7ec5c3156b896b92e41a07e43af8a78b0da72e21222bf4": ["securecafe.com.", 0], + "797c124516930511a0478b3fcd89d93989b2753f115da57b33e9de19db0a4481": ["emisoras.com.mx.", 0], + "797dc94cf223db09fe108661cc6cb7f74ea093c5bcdd2e4f9872ef8784679efe": ["cnhubei.com.", 0], + "79885ce3b9cedc75a8b2c6521d640ef72d9f5693199de35d600383ca4213149e": ["imimg.com.", 0], + "798a02af5e6c9314d181720dcb1cf41f9e39567e0aa0a4ab0773f4e53b5444d5": ["hfut.edu.cn.", 0], + "798b478605cc6586a07f427ad27b3b176d74650a311835d746fc267532480dc6": ["planetminecraft.com.", 0], + "799348d0293a382c5f0f409e758a8b9fa9edf744760058acb89eca13a150922a": ["goodhousekeeping.com.", 0], + "79955c1b1d9d5169d5cfa1d72ee573793ac40ceafe89469c51120eac212e34b2": ["xuangubao.cn.", 0], + "79a1573c0672185b47d9b70e65434fe02a2d5385661f10f66a53f1d70086da9c": ["webofknowledge.com.", 0], + "79a58e6c8c92e59543ab7596fb3acae03089961846c07ba27256de9c5eea2886": ["thriftbooks.com.", 0], + "79aa81e3ef494a95ccfd111e0c5526654ba41b878cbd55ada49a4a3c7ce1682d": ["cmoa.jp.", 0], + "79ac30da9dbea8acd53be380766a6b9670d9f89f05c3b00699caee397bb84faa": ["ketabrah.ir.", 0], + "79b2ac9cbb0ccab97eb13f9c0fb323957a6f29ec881b691f886ef41aea6de968": ["fpl.com.", 0], + "79b5be9a2794801d23c1b2207fd91205602e41fbdb5cc7193b32c516f140c045": ["postimees.ee.", 0], + "79b6367c9e05c77f6a80a05fe0a40b9fced313a38a8ce3184859093f2b37beda": ["direct.", 1], + "79b6a612e0474329869fb30aa79941a5bf821205efaa24ac419c8247f74637c5": ["citadel.", 1], + "79b914a22fb83cd46901e0cc70a837f9a559735dfbc1e5db7199045c9723bf07": ["binbaz.org.sa.", 0], + "79bb79f59d03f5145e650e647b70a523ec8fc25a76b9001e151450c254504fd7": ["costco.com.", 0], + "79bc30969f32cca24844b6df94d4dedb5ab96dd755e51f744a0e0b7ada5c7395": ["firstcitizens.com.", 0], + "79be951d4f3947387f07b6a4a46fd30199db66dd261cc8f466e5f7183df71783": ["appsflyer.com.", 0], + "79c9763b2456a883bc79363cffbf1c64496650defc8e01685d22a7d488f8b807": ["pictet.", 1], + "79d37556adbd54a8714480aad5795e1d167ce315015b9c5a1c5177beca1bd2f2": ["weather.", 1], + "79d667f384348550f1b6b26724ebbfa3181d3836e55a1e8b257adc30b417a3f8": ["rmdown.com.", 0], + "79d8b7c15a25a97fb46ea07822ec19aab4a94722c4ead4c03f0437f10c98180b": ["visualcapitalist.com.", 0], + "79ded2d4a8834cb91ae65b6cf76709879a10940e953e633192e3216b9bd52fb1": ["pingdom.com.", 0], + "79e13d0dbf7ac7af360a351f58f776693597a18ed509e06a25455f63f29a5c43": ["mejoress.com.", 0], + "79ed86045dba756305c936742936ba6e9852275a917cd3cf98432f7b6788964e": ["tw.", 1], + "79f1cf94767b985c062ddfbcc6d39ee9c3542c66bbdf14a43175ad3a11a9fd46": ["testufo.com.", 0], + "79fbb15b7ff62f1d6f349db21b36aeb8c50882c5be4816dad377329852f50b8a": ["bm.", 1], + "7a03c6458fdd9e3f241dfc2228a9c772fbf0eb007bbfe83fa1d2725788363cec": ["wangchao.net.cn.", 0], + "7a095c28ce91c4ea66c89fc6dd48811b74255b21df6e4b4f81224e1f04c0f97f": ["iam.gov.sa.", 0], + "7a095f8df401a13354dbdacd2a7ea77f36155adf5996d516123260d2a8fe3c34": ["oracle.", 1], + "7a0d11b62b9b693dd083d9fc540ce32ef0563500c447b9ca6061a9834f311883": ["enstage-sas.com.", 0], + "7a0fe9ad2f5904a4bcc2916f9c60f23888e3c1b60482ac43edd9ea435f82914f": ["espreso.co.rs.", 0], + "7a1268f97d7446f8a25b7634f06f6d187f059f53171fcaa0e23e50f683307719": ["perfil.com.", 0], + "7a1d04b58cf6ac67cd9c9dd9ddd8de30f0a914abde88b9027ce984c5ba2da826": ["truyenfull.vn.", 0], + "7a1ddea01977cf1b4217b47bf5abf5b2c9e79582c23916b91ed64ebbaac3cfde": ["avidreaders.ru.", 0], + "7a211ac875d64b2d648b9990bdfeee7ed9e8cc9fe00e678463e63d150edfe8b1": ["liveuamap.com.", 0], + "7a28528194595f534c32dbec7d8ac29a922b72ec7ab70d7dc74a5201d4b6725b": ["mytrip.com.", 0], + "7a2ef20770ba06ac2987760a3b294bb9b67fe6441ce2be86372f70a3ae72eada": ["sendvid.com.", 0], + "7a31d792159f5815e5f3a340ade2fb2a9d4577e612dd1bf8afd1be9c65584220": ["joyclub.de.", 0], + "7a32703b3e58f9c67fff326eab1e6c3695c23a4f59c76e37116f12b27ef5c6be": ["donmai.us.", 0], + "7a50ef3bad2c881209d1446da4bbe9a1443ba34b5c115339e9f315e1ac5bd453": ["audit-it.ru.", 0], + "7a5f75ad33be2071c898832a9176aa0ac7a2264566ad84c3b17b51704585c103": ["koolearn.com.", 0], + "7a6482c65f0c7bfb4bc4978c05774670b454ed1e028e282f0a4797f76fb0591c": ["frontdoor.", 1], + "7a64d37af32998e95934a0f90a9d4ee4a6451c30404005176c7325de4ea6169d": ["ulisboa.pt.", 0], + "7a68a20cc163069db473ea5e123206d0db65f340246e2e5223aacd7e144bf3d2": ["mp4hentai.com.", 0], + "7a7122f5c5667303994c47fae77d5ff30d218101349436e058d5e072be07d452": ["workercn.cn.", 0], + "7a84e9ee42f9694c0e88e986330bdc7853b94c436e6e91fc525944c228d4c80e": ["bet.", 1], + "7a87a0942782fab0db44be22c77105a65618d00d6087f40b94b5defc48917b67": ["hemnet.se.", 0], + "7a8f52798e7ba94dac658b23b2654cb3d5349015f10e4d5c5719be6accda366e": ["okcupid.com.", 0], + "7a9354de25dfdb1db2061ed9da1bcf87bbc3bc4b92a489ec5804fbc7fede17f5": ["girlswithmuscle.com.", 0], + "7a99ed30cb15ae26d24a540750ce091661f320503ecdafb32ea82d3f99632695": ["surveycake.com.", 0], + "7a9c2b2cf0c84149cd11c27e5aafeb33bc95bf97f9baeb4aa50892647a88ac40": ["lostfilm.tv.", 0], + "7a9dbd349aff8c11b1c866bd06aec2b1c79f97c7c88e94ee6007cee49cfcd212": ["secure.", 1], + "7aa2710e07feb25deabd320b8b0631852452a8546699a063dd371af35333cc4b": ["centrum.cz.", 0], + "7aa9d565a894b790c27027abfa2dbd131a2e1a572c8f118bc67f6cd4ab621f36": ["thanhnien.vn.", 0], + "7aad3eb66420dbed2d4a4dddcd5d3dfd0c4b0d7e1814cbfd8ff638453bbc191f": ["byd.com.", 0], + "7aafc3ef820c843b1856d25f56c7fe2cf0d89e2d90136c395caec900bc47ac51": ["myfollett.com.", 0], + "7ab17a27fb9df2cf32832a2c39a73dd4bae1c295e6bde3b951a75bfdb9a4bc66": ["szonline.net.", 0], + "7ab680e7bdca7117887338e791e928bf32b512139a35fb9cddc3df810ceab6af": ["qmb.ir.", 0], + "7abdd025bb89b02d25c63e595fe70744a637b3532da15000615e820d7570ec7e": ["sharemods.com.", 0], + "7abe6dcff17e65750e81ec25f6c255ebb305bcfb3813f4894724b9f2fe966632": ["tampermonkey.net.", 0], + "7abfacaff0fd9193f9f56f10d2362f6a85114da53855333cbb841cdb5779fe2d": ["takhfifan.com.", 0], + "7ac141e1add9219f0d9d94295a0db567183d900a34c0597575d389b7bd48aae2": ["uno.", 1], + "7ac3ad140d458eda5a3dc2285945a92a6289d46b8fb18f91928c67da65aa8e86": ["douban.com.", 0], + "7ac5b47c37a3593beb442839664e361acdea48b6b07abe8c376d8d8fe87834c7": ["zan.kz.", 0], + "7ac5d82335bd2eac7e41f25c0a9dad10d722d5d7f093876d36eb8c09e07949e9": ["myinstants.com.", 0], + "7ac639f5736f2720e9f4f85d7e2ca6943fc4db72202cb9dd67e6ca2aa77f523e": ["joinhoney.com.", 0], + "7ad64f0d35432fc0095fdf0c5793f5a74993afd413edaf7751772d8024d8d59e": ["colg.cn.", 0], + "7add0bbc152a4853c3225d8c7ef0c4e1c7c72cd4cbc239ee9c9a31d0f7455f6f": ["cbc.ca.", 0], + "7ae0924d3fe3965647f3dfc93c1687f79b77338ad6f34cb264bb6afb8c0ae1ba": ["beauty.", 1], + "7ae83c01c604e7578fea75999f98c57e1e5fbaba0979dec19ddeb3830a5425df": ["filmweb.pl.", 0], + "7af49cc647048aa9a78214608dbeb91fe12e44359ea613233439194c4e011a03": ["beats.", 1], + "7afa2fc9ca9dbfbf043bce01c0af2121dc77a92a04627e812ea9ef2e705e6e4e": ["aegeanair.com.", 0], + "7afcdc12a2a90f31a3b39a38cd4150e094a944d0fb3bd5c92389a3ce29453dff": ["depop.com.", 0], + "7afe2a3be3c24a8928e9bab5be6e9787244b6a4e8392126192319d51dceb705d": ["remixshop.com.", 0], + "7b05f6b4422cc4a785f4d592321e9a5c29e318f219a281aa470a94ac442fb309": ["eurogamer.net.", 0], + "7b0c0217923a8f686900ae8d7fb9eb55fda924d74cb9479a2af6e2b31eda9a58": ["idbibank.co.in.", 0], + "7b0ece9ed5f1c62f85cc5e8300c180f8a092c9b3d9c48d083d8f8263da7338f3": ["cool18.com.", 0], + "7b105ebeda5c25317add07848e895a2b42b675f4fd4f27dccef20fc991b3c7ca": ["derpibooru.org.", 0], + "7b1fe98c6b437f7b5bfde1cb5dff2f53182bccc104ef43afee11ccf5f1bd507d": ["7-11.com.tw.", 0], + "7b21734226eb151cf155d36bb3ac03d4d8e8248715e72c2bbf75e7cd4e9364b5": ["flytap.com.", 0], + "7b2cc7c48aff7cb54fd44d0a3937e6cb2969afe36a0c93f0a2663769d3026275": ["proquest.com.", 0], + "7b349f1c5cca411776d75ec8e3a1545510b1d46a9421787f57a30c38ab425313": ["alsace.", 1], + "7b357d89e90829ac7fbda7dc92b6d7ef1557b3582c89b89193754eb929fb557b": ["pr.", 1], + "7b359605d9176896a8d1e128c4dbfe27702ec8f812daa823957b9f10b9520abd": ["circle.", 1], + "7b37f88ed18a290a77cf3db0a0634b5c8d922a2e367b967704cdf78c89f84db3": ["cpfl.com.br.", 0], + "7b3e5191fa939f99e875746438f72566b81c448d29a326376e69f9dc82fe0540": ["revenuquebec.ca.", 0], + "7b404424ba02c43cc859eea89a9d7083a5753404acdbe0d7022c426770d39599": ["inep.gov.br.", 0], + "7b46ce4e476a442ce38b4af414870d7c077a1b835be53099f27dbfd81b240e7a": ["windfinder.com.", 0], + "7b48a56eb6729d4e391d32c5a8f49d9e2ffdeceee172a4d7877f38ffb5aad29d": ["touchnet.com.", 0], + "7b4a5d33c077ad9780921ca3102ea8d247bcf17b83b8f51431a125d23314b2be": ["drive2.ru.", 0], + "7b504982ea98af85bad61fe98851dafe1b8ef5d0c3a0da7865839dd876220a0b": ["namecheap.com.", 0], + "7b556e3c5b7b9c0a45a9e7469ad92f2d23c28a45d5ae7e0db5b714ba3a78d0b8": ["chargebee.com.", 0], + "7b76ce53183b05ecc5e7df440cae03350dee5ff73a3246897af2963871eabafe": ["liepin.com.", 0], + "7b7962edce8eb7a312230d687e6cd49288d36b65ae02c87e0472d1e465dbcd91": ["zdusercontent.com.", 0], + "7b87a12cafc03924ad3fb8075ad13905be36c110abb93d5e170465f781a10373": ["mohmal.com.", 0], + "7b8812a0244139a9c83a8668059cd33cdf5d36b32fa870cbf05fdbacabd48463": ["altin.in.", 0], + "7b89ca4bb118537b0518eaa6b50c67b7efcb72587f020eabc2d6b336e71695da": ["paizo.com.", 0], + "7b8bcbe6902e04dad8383d5db9b7f59bd3482d074e5acc9ef162671d5eedc600": ["smn.gob.ar.", 0], + "7b8c44dc0e21f78d3008320c3772c753681d25a1511b1f305bd828ee05d060c3": ["globalnews.ca.", 0], + "7b8d8802787a004b19c47558a0c7b3b08afc124f95bcfcb18fe069129d92a4c0": ["web-hosting.com.", 0], + "7b8ee7f0833d6979f51c036259b568bb79023a1b678ad3d66d491b5c3bbc72d2": ["psnine.com.", 0], + "7b9447c9ac431ab3eaa7972bfa43ef6048ad596cee0d008059675e2ccbcdd1b3": ["mlit.go.jp.", 0], + "7b9c3593f7d1d784cf4b0b674e934d8560182e4de70557ffc81c9f4f3e04aec7": ["wileyplus.com.", 0], + "7ba32274743e55e5ad63d57671936e21b511f7177209be4e5888af9ceb4c3b6a": ["shangrila.", 1], + "7ba88545f0730a5adcebefa478487971d4d052fc29f1bb2c351efb5370a6e69c": ["jobs.", 1], + "7badbbf3d9a634c7c4f1eb094c773c7b062c78f297e7a9ea797ea7475e81253b": ["coolermaster.com.", 0], + "7bb5394d8b81d4346feaa387332a0170a5774b6ba4914f43bedfd8c067909cdd": ["alinma.com.", 0], + "7bb907785f99ba3b1f94b3dbc7dc91ec041ee8a55cc0d84452a585279596d4c4": ["enetedu.com.", 0], + "7bba865a71d1a06b6d538c006452eaf60de8dc5af22e9a266c86ef5a8df353a4": ["chann.net.", 0], + "7bc71eb3ce6f097c89bb9e44c2a53f4c1202395b876513c8ee408a56887db0bf": ["turkcell.com.tr.", 0], + "7bc779cc662341ccc9cd8f90e991b6baee9d7ec38b82e864fbdcb2e72c32cbd8": ["bakusai.com.", 0], + "7bc9663eb38930deca0bcd82d8124745df463df876975bef4fd00f430d8b75bf": ["cipriani.", 1], + "7bce54fd25f1369aaf5a7ec11b57f60c78082e232ff705f71d838301429b786d": ["femmeactuelle.fr.", 0], + "7bd64eaee15121c54f717f2781585b2f52d24088d591ab35cc1491a7c145beb9": ["ysu.edu.cn.", 0], + "7bd759e71a5743284fb2a648030501333981372ab40671eaff07ff053aee670d": ["combinepdf.com.", 0], + "7bd8008884a00f3b10d4a8c526e786d04a5169e5b79ec3d4c7474312237d6f60": ["taipeifubon.com.tw.", 0], + "7be00a5d9ace4bef429d12b1274538a7db7cc15ea4f94a077bceeeab82f355d5": ["iculture.cc.", 0], + "7be6cd079fc4a72af33283a92f09ad0e12d8b9d9e477db436b7d178e246b3914": ["instructables.com.", 0], + "7bee738b6e943b3ffc3f281d5b0e2e287d4ebb753621e8e3df9fddc8d45a4984": ["lyft.com.", 0], + "7bfedfbceed6bdf3dc1540907b386638f854a9993918fc3fb3a3cbaca54e5c43": ["redwap.me.", 0], + "7bff91257fc2a98c9fe7c38119b194b63b7d80425fb57fbae47e5f1343e98690": ["infovojna.bz.", 0], + "7c014848011f4d481488e31fba221190faa4f5382bb142e7fb9be457e523c8cb": ["businesswire.com.", 0], + "7c0fdab34b00f226c9745bc2109b52e06c6e6db7917aaa1ffc78ce50da2a6002": ["earth.", 1], + "7c10a630870a360acc86c1c20d0ec90097826b2cd02ca9a16fe960726da74ab3": ["tradingview.com.", 0], + "7c263c5c510f9339cf55b97b18afd0f197ac31c975200118cedf845e74b5ec25": ["mmybt.com.", 0], + "7c3af9691126c03dab382fb0a49d64c5e42f384a35cd94f909c66387783fdf00": ["axsam.az.", 0], + "7c46188f9bb866cc7b65d5e2d4db4393f928bb5193d7a1089eea6a98517c87c0": ["openuserjs.org.", 0], + "7c4b92a5498c7487264a681811e0e1546b51772186ed0c92268b0b14e2240746": ["whitepages.com.", 0], + "7c4c4ccd96ce48ffe68acc00ef69b779b3b66c06072c9b17239a7c57a4bc4a5a": ["123test.com.", 0], + "7c500c2afe4c7ade1c92e06c85f4561831191ba1d317b08b1a6384bca54225dd": ["wmrfast.com.", 0], + "7c6170d3f0d39df826a8bf9d5dfda37dd086b1f0e8b5fff05f3ddaa1acf7cf2f": ["pro-football-reference.com.", 0], + "7c634a071fde3a8ac8c3d349db9630c30b27e9b7140bd623c7fe2aefa94d9792": ["cloudwards.net.", 0], + "7c6bfc649f70ef356e965f1321d618ad3cfa1b78fdb60546851cd0647b5fef2d": ["gulf-times.com.", 0], + "7c6d1733d2076c6f0d4ebfda16662fdd377da494d77cbc74b0e210614afe90d6": ["chegg.com.", 0], + "7c745267141d48be85bd79301dfbcaa9f86c8bb254c3b5777da5209d7adb8f2b": ["chinadaily.com.cn.", 0], + "7c7b193c3c38cbc007ba473473a01c4b8d88ac75bc181fe163a265d2a7632f37": ["v3rmillion.net.", 0], + "7ca6949d9820ab3b83ce66795256f83d20463833800bc9cb3430deeda2d85a82": ["hearthis.at.", 0], + "7cad44e255e4b81a9a988f3cbe6107daee9b57386bb23aac62b64b498c46a998": ["soccerway.com.", 0], + "7cb56aa796a376dfb1084eb4233c23e7fb1ad3c2ee7e1b8c2d53f807abb965e4": ["movizland.date.", 0], + "7cb6e39d9e63f0f5820853352c4dedaf3b5621993421c4699117397a776e9af9": ["ncl.ac.uk.", 0], + "7cc0420758e6b6bef4dd3b48994afc2a6832a1e49a735e04728d6cc6fa8d8386": ["likuso.com.", 0], + "7cc164634ada9c552548070028a033ce0cb53ae073cef8594bc135a93ee40836": ["merdeka.com.", 0], + "7cc84455b809060429d8a78fdfc2f82ab563161e1fb1ea3f448cf8e618a5a064": ["foxitsoftware.com.", 0], + "7cccaeabad1d969c569f20e02c73a106ed39c8facf942facd7d0e755bbe09026": ["backblaze.com.", 0], + "7ccece0cf436561c7ac1219c9271f30f6130aee2456215d7ed4587f7cb9a79ba": ["abadis.ir.", 0], + "7cd2d53339d3c7b5008a06e0427c6cfd892465f5b8fa541c1e1733a61782a15e": ["scriptspot.com.", 0], + "7cd699612930f189f8f3f8f8ddafbd85bc3a4c6491145df03b4c47fd25c2f6fd": ["polri.go.id.", 0], + "7ce37d4c9ccf062539c0c07fad6316ae8e7b82404f5d523dd40e2d8296bd9948": ["alvolante.it.", 0], + "7ce4b889f4e4cabbb2158067948a64810e2ac05216160d7f4f388f05fda84a98": ["auth0.com.", 0], + "7ce5cf5c27419a34d2dd4b33ecb0ea9ba358537ba4b2e57d841a0a8917ac7027": ["visir.is.", 0], + "7ceeff6642eab17ff6c7239a71db3f52335181d685ae895bdd29fe8b10b7cbe7": ["formswift.com.", 0], + "7cf0395e32a7578a755e069851ffafa3c366c4880922d6a53e816ec5f5a4e2c5": ["shopifypreview.com.", 0], + "7cf0587b6c635068da5e7d162cf80d5c207dfac395901b092a0468dfabcb3583": ["gaoqing.la.", 0], + "7cf75a0da0b4835897e4b48d8d19f849e38df1f8674b4b21a777d2ba1f576f87": ["comparethemarket.com.", 0], + "7cf7e561b8c710bf7ef43bea9a774326b92c272ba719cf3aef83b4723b9df786": ["privatbank.ua.", 0], + "7d0c1d257d147de016975e14b7ed11c00d192b8358e3309b26aacedfa451f803": ["ceair.com.", 0], + "7d0c49c65ba5f50e44e5c31142527de1868569176cf8cf8128aadcd67d928af7": ["onlineregistrationform.org.", 0], + "7d0f96d7a533302bae037d7923b8bbff2ebd23c56815f949bfdb3a2ba26f1e02": ["sololearn.com.", 0], + "7d111739da7b831156da92ffb1c858ce3a3106ef6da95ebbd0bc660a0b0d012e": ["kulichki.net.", 0], + "7d181dfd621d3a2ad5ac00db0da84f678a026f22b1cea697fff0de5620561077": ["itv.", 1], + "7d2382b5acd476b3b78538c459e7c0ad27a4b1d1cde97bd54a7132d5b53063cf": ["oneplus.com.", 0], + "7d2a75ce0578b676530b1602dbba88c020fb74117b9bdf60f15fdcd3ba299243": ["cointiply.com.", 0], + "7d2c449185cc4025e3672b7451f2611fa55b5b8cb8c15bbefe3f0f36dc6d11f0": ["fan.", 1], + "7d2c71d35d4598711e93c810e157519a395c534d7c279f75f1f692ae50d2f2e4": ["scb.", 1], + "7d2ca0010cde3244520e7453b50fef83de1f6e1ab4d6b7832913b7b35a10b2c6": ["cyberghostvpn.com.", 0], + "7d36e00e4a0f20ca11393721e420e17f737fec396134b1e14ed7a46f31e83835": ["myzaker.com.", 0], + "7d37c9ab8dd321b736205884c8ec80507de8130c19e4a42a6429fda95fdc2c6e": ["rockstargames.com.", 0], + "7d38daa568cb8b72919301c0260188f6c3e7849cb65cd12d879b81b1ad036f7b": ["tophat.com.", 0], + "7d45005d4c8c5899980c4550656793f068885507e8769c9daabf4500f8d3d740": ["esf.gov.kz.", 0], + "7d5227f6ce35d16e6e647ace668808a14f892dd42ad5d7763664d15a00975075": ["bergfex.at.", 0], + "7d5d0401b7cf7ecb6439c820e347a4f31bcae1ade7affdcae43f9156c0b98c56": ["levante-emv.com.", 0], + "7d6d4d5f00b6e61dab72f94c38ea6f4f4551d2c391d59cb72f66863ee96cf362": ["ipsosinteractive.com.", 0], + "7d6fbe588102f2518573a43d24ed2a52604d907e149277d971b8040555094515": ["53kf.com.", 0], + "7d729f2fa0302f58295afb8aa6f47b1d52082df228fef29cdf02851d89be90e4": ["pbebank.com.", 0], + "7d7657c9db47f3104a12751a5e31b20caaeb6a5e23d28ffa7685d9aee30e1954": ["star.", 1], + "7d7b190499be3cd36d1fd21413420248f861c6fcaae985838b8e75a83939a9a3": ["uoc.edu.", 0], + "7d82b5dfbded3e6c53a142d5b484b65b815be1139a5686185341df5871cec0de": ["worldtimebuddy.com.", 0], + "7d84f057d718112ff7861ce9c65cb20fcd7f13ff87c732ea4bbf0db5d62211b6": ["araskargo.com.tr.", 0], + "7d895a84c193172ea8e63fcbbd8f0848f761404ecc44c7d6f885e3b18acebc14": ["nationalpost.com.", 0], + "7d8d7fdf7ac9c7ce52c37765b2151d02c6e9b128d1c92997dfecb93728f35e4d": ["philadelphiaeagles.com.", 0], + "7d90de822ff30a319c6c9938e26f7a6a0d71cde9247a1adfdd1cb6f182267b7d": ["scotsman.com.", 0], + "7d90e65b6d0afef6fc59fde5166d9926230776bb623d56118e31bddc80312725": ["coingate.com.", 0], + "7d920331cb6ae016c23c1f34fd60a0d0a07b06b62caf696bb28c9aa0e10fc135": ["warosu.org.", 0], + "7d95bb93e1371f3dd97293b19bdd0fda47361942876cdc2e33a7353034eb5746": ["salomon.com.", 0], + "7d975f5f7be3592417cd44b5e7b0faf7bf4a067891bef3c656b6a786e02bad24": ["wordpress.com.", 0], + "7d97a3b5fb02120b7385568023093312153c7b758471e8210e58c70102fe96e3": ["lexpress.fr.", 0], + "7d9c1ae0c8ae5aca4018721356b21cdb042392c89a76c9ccd902b00a6abe1168": ["dhlglobalmail.com.", 0], + "7da822aff561d898357b8ca5eaee3f9331b8d60a021d46e9901ba9e15ccbd1d1": ["rit.edu.", 0], + "7db80b81b77dec1087d8b1c2bcfe0bbdbac559c3244f3718c788c6c7c651b81a": ["jiandaoyun.com.", 0], + "7dc36220d71ff960f16b7dbeeed996d7ed6639f4985a4019198e2820b4bfd817": ["flixtor.to.", 0], + "7dcaedace1ebd775377d266b9e12737fddbe6a93410458f77f16ec9cc9a5813b": ["ebayimg.com.", 0], + "7dcde80cacad31f1c3ed35cea7285f2f22be3ba936fdbbe83e9c0c129b05bc0b": ["priberam.org.", 0], + "7dcf56396983e638cea828ce9bee5dbd356026cea5c590308d7b5d43380ed2fb": ["kemenag.go.id.", 0], + "7dd24411b0ef8e9dc27f6c781ec48ea54c7adc5cc8cc1dbacbe1adfdd33cd470": ["tinybit.cc.", 0], + "7deb9fbd8d172096fbe7218dffebbd0a5756b152c455c039e906c8a3c21d29fb": ["abercrombie.com.", 0], + "7df0add6da739e467face2b619588baf8e8f46f1858ce67e7ea0f23627e551ec": ["suicidegirls.com.", 0], + "7df96ede52e92e4eee604c1ee922edc048c89ad318e6a1202f9af28cb0c9cc53": ["jalisco.gob.mx.", 0], + "7e01557a7bc8c0721dbc79ec200df576b2c82e7607b797f870f229f6b6898749": ["szhome.com.", 0], + "7e23d08e66c9df82d01288b6204c1977de7fced84da33b0745577b7ea033d658": ["bargains.", 1], + "7e2460fac9d5909c0297ea04a0b23930c54f7732b4a92144d52e6b8ec1b9cad4": ["myherbalife.com.", 0], + "7e2ecbdbb45038bd8affaae30202117d0b9a5dfe7b35d0ef97c8e9e8f781f49e": ["echo24.cz.", 0], + "7e43f1afe9b44fe69c0eddb841ba8a28de45325ebe6e139fa9697f54b4f180a0": ["hsbcnet.com.", 0], + "7e461e142a9dd363212829eb9661333155b58983456f7fa9ef43a09b31c04584": ["4porn.com.", 0], + "7e4c6ff2a1c44611155d03acf05b76ef8d5164bcce44d51686ed61ab11bec697": ["techiedelight.com.", 0], + "7e5c5a89e1ab0ed6f5c51e5b2f34c0b70a59ec9a0c2114e95bfd69f1da46af5a": ["cityeats.", 1], + "7e6113fc4f74d9adb2f5b882bc4c4b1e0a1e907e0113322aaa806f3a27e3ea67": ["videvo.net.", 0], + "7e66ad4de370faaec58faa669e63f964cf782732aa841b7be43543b8b1de9e09": ["android.", 1], + "7e66bbb07d5ca8f2614a85b85d6c45e7373b3c29895174181b42e2d13178579d": ["videezy.com.", 0], + "7e6704a1135e9207645ae3a266dbb0faa73a5c74c211fde0edfadf8fa4446287": ["sangon.com.", 0], + "7e6d05387e1023580ac406df4f95c628f51e88595f3e4ba38ce29f76f9fa2a79": ["mewe.com.", 0], + "7e71396c88b5cbeb93d1bc2aad3402cd157b7c7d82e7815bb8a2d18293525842": ["slashdot.org.", 0], + "7e7410e39c60e5b13121fdeca7e574c51fdc109d129e5672bd03abbdb1626a7a": ["hrss.gov.cn.", 0], + "7e7a3018ea3317c0588fba385ae0e54268839ff7be1e54486a280a5c676a458c": ["games.", 1], + "7e7b088823e5cca9fe4090ff3b9067d20a5eeeffd544e1e303d98f985641ee9c": ["shia.", 1], + "7e7f304ca98f12799535f719dc385e9527d98f86d4f80b551f8e569fab38cb11": ["ftc.gov.", 0], + "7e8491fb7f68c884c695ceaa1b0b163fc9e407b3f50ebf61b8aabf8af61c92b3": ["mangago.me.", 0], + "7e8ad24bcf0f48a38337e78f21ea6eb1d70b0e7f4db99ed174bef796c484fe01": ["ica.se.", 0], + "7e8b82b9bf1f7bfb0f7a2fe753c705c4269b19f2ec8f4f2630493656ba120a6c": ["kh.", 1], + "7e8eee6030eaae35b7b32ac5442e296225834149cf73dbd01311be1b49ba94d3": ["synopsys.com.", 0], + "7e9055d4e1fab4719d1d1f324711220d43205e62c5bcfccf13597c3595e67fa3": ["ivytech.edu.", 0], + "7e954b20f138d88c2fed069a3850ff88e83d4037cec2190d50b8f0600cce3b10": ["foroactivo.com.", 0], + "7e9d932663b779c238fb6e45289216e1e21a3c8cea25ea23c6d59cf39d872da0": ["tec.mx.", 0], + "7ea07e97afbe7b2c79e1685b916ce6f58035088f7927ccccab78bde5130600bc": ["kladraz.ru.", 0], + "7ea7b7b4e3b45e6d18037a757917bad120752f3b596ee6cb61c097eeddc995f3": ["gmo.", 1], + "7eae0060627fa1abc02f3f012162ebb6397b93c42625daf34af605190f8128f9": ["picstate.com.", 0], + "7eb328bd09f16cf3eae68bee3e1cdb70d49a5c28f478c27645572883b1598652": ["tianya.cn.", 0], + "7eb746618828ad72f6774846b29d266b061eeb0206b4a2fb1e8ee124308384a3": ["lanhuapp.com.", 0], + "7ec3500dad9789944c76144aaf32a41b1ec4b1d66c9337d22f5efddb4fa54dd3": ["vc.", 1], + "7ec927629aced0743eed253daa12bbe088bc1c83ca7a82cbac097370dbb18df5": ["hegartymaths.com.", 0], + "7ed23fbb0f5542d67df73017ba004da746848baef456bfee1ed6a858d3bc60f2": ["meme.", 1], + "7ed4830d8920beb2a4a80c11d8df084cc040067cf57f88f33406be9a3c289d84": ["bankmandiri.co.id.", 0], + "7ed80d88e43e26597839a649fae534e14b86f735742fde9f7e2322e13a4fac9a": ["vrisko.gr.", 0], + "7eda5555ca1697357f6fc2d2fe9fc744a788af1984d215a1dcadb57c7b41d55d": ["youtubeto.org.", 0], + "7edb2ad19e8e04fed2195dcce27fad8efe76185cc4194f41c955d9ff6e735d1a": ["gutenberg.org.", 0], + "7ee4997abf7eb17871afb5b998b27e8f256acaed72a786fd18ec044fcb3ff34e": ["csrc.gov.cn.", 0], + "7ee6639772b861fd67ab9350b352410bb5e0cfe74b135861a264805e64a4bb3c": ["vippers.jp.", 0], + "7eee62a7942162ce01288d5c75121873e76816fa2f788e98606bfdef25735716": ["forumophilia.com.", 0], + "7eeeb62c448bddf685777afae3a16d0fa74c10b885a96cb7b5857158f7bf24c5": ["iess.gob.ec.", 0], + "7f0b5b16f547766b3a80661cb79b647d6b9085e0675c8d557e317b5b081d1d6b": ["dominos.com.", 0], + "7f1a20b9900dfd9785778331a6bac5e27616569e9cd8daf1761163e768097071": ["taplink.cc.", 0], + "7f218af846c71fa430c7c9f94b771fc736ed20e9449618006bd0c5b7c1b96b50": ["av01.tv.", 0], + "7f2343a59206aa40746a56a67cd841315f90b27daaf64289eace8cc8e9b4ed19": ["sulekha.com.", 0], + "7f330df7471696f106ec0ee8edd17e05fc4818d7d41054dd58102808a44f8d85": ["rajwap.tv.", 0], + "7f390b810273f909d01cea80ef8d7bf0950f566f74b03f7c799a827d80e004aa": ["freeformatter.com.", 0], + "7f3a56d8af24434864ad97328509fe66f6ff8c661aed5da9d390eb084e01276b": ["putlocker.sb.", 0], + "7f3acad0827de533218301a3dfce66a5eebc275e480712f6d3da31a308e59bd0": ["north-plus.net.", 0], + "7f459caa11f8494e1260c66db82ec31dfa199baad08f5f957ffdf77c2bed4084": ["belgium.be.", 0], + "7f6523df1c369b3cbd7fd399fdbe30d506935bffa95be30ec800cb6dd0939761": ["optus.com.au.", 0], + "7f65a26492ee9d26c2decee7792e4480d32a26ef303c64d4ae140bacaabe1a5a": ["thrivecart.com.", 0], + "7f67eb996cd7b107e07564d5ccbbb66f721f90785509143de70b506201c55a45": ["anysex.com.", 0], + "7f6a635acce2b7b55bdc78c9279ee60c87dc58097c6c6eb9af4944640e74a226": ["freejavporn.mobi.", 0], + "7f6f4dcc465f83afce569cf8c7284c9d51272a64a0b58bc4320d84718612d5fb": ["mr.", 1], + "7f70b8967be49e516119312839f6aef30de4e503f1cf60894250edcba4c33050": ["immd.gov.hk.", 0], + "7f77b0e8e19ea189483861b6df788ca7196b559b7749c57c1f5f1c5d86956c2f": ["suning.com.", 0], + "7f855cf5d94faee901378bc3585423560f92670fbac6b15607776392a034d239": ["si.", 1], + "7f8a1731f756c94ca5d99cd3a41672752fcfc8a94240818d4eb4ac02b1a39c01": ["discogs.com.", 0], + "7f8a2965c76e4ebfb82fff7dfcbbd51c9846318f9f8ed5085e11d9b3843f125e": ["gnula.nu.", 0], + "7f8eba8f5359a8dccef8aa2b4cb44a38149578141e88cadd9359018c9d5eba5b": ["vip.", 1], + "7fad11b2d86a1153dce1f1cccffd08e7c86b6cb97c92381607696f31c4ad5703": ["bootcdn.cn.", 0], + "7fb025677c6d134058957016b59ce9c01f203e546da08e44a919bf0531562c41": ["getflywheel.com.", 0], + "7fb39ae7f5856e6f91c5e26df0f7abd2646141b9fe2daf89840536241a7f209f": ["et.", 1], + "7fb56aa58484f53150a694bc9c17d908d80e60b925a179d0fe0fa1a70be84d14": ["gtmetrix.com.", 0], + "7fb7a7091fa0a5d4e2006c53d71581e2f502bd51c21c1f2a76dc4d5ef5136bca": ["caijing.com.cn.", 0], + "7fbfe2592aaa1fb293c91525268241848804eb0db990b9c211346e54a4c773cc": ["mosreg.ru.", 0], + "7fc43ad53671a23b26522b2b16a38a039faef4e4d966ee2221f9a408de591bdb": ["couchtuner.show.", 0], + "7fc544544b19dea59a93923e66b6972d3d9b87ea0583b321a108556d11014029": ["whatculture.com.", 0], + "7fd21fd116d7f0c03624555291a4023a2e450a20449fdfd0308e8a84fd348456": ["k5learning.com.", 0], + "7fd5a9547ea76200d20f6aa44bcec6ab3752094294479cdcb9dc1e1b93bb6e31": ["wien.", 1], + "7fd7a140b4fd902f4c33f2f1d7c0d0ae7b95cffa34d695c541bc0a4fc4b67c62": ["zbj.com.", 0], + "7fd9761b41ffb10494eeaa868020142bb02a71213f2a81bb4535279fbbddd08d": ["chomanga.org.", 0], + "7fdd8ed076f75b73a19b3f61066e55a53b5f82480034670e63fb9c59e9a41e69": ["china-embassy.gov.cn.", 0], + "7fe06f6fdec38ad5800f3424c4d73f0376e056ba731a31544f5afa9c21bb2dfc": ["gamerch.com.", 0], + "7fe5ef6b3af17f0b640c8853f34e218324551df580598d8d0715fc99d29aa4e0": ["thekitchn.com.", 0], + "7fec4eea1d4a8d9790b194a05cce8db837669973deedae1c4b343700e6f80346": ["onerpm.com.", 0], + "7ff4fac03da9ab7c331534f999d8a857446f9c0580870ee52b7eaf8b75e503f7": ["skymovieshd.bio.", 0], + "7ffe10111d53f64cf4522abab253b9fa163e89de64ee76241158db4c84878f55": ["iis.net.", 0], + "8003e5d4d29afd6b34214012163b3dfb65fcb7561c53f1daac6490726aabd9b8": ["icicidirect.com.", 0], + "8009ea1e422062817db107c1ce4b611a84249d193e0d1115529a497ce728649c": ["unec.edu.az.", 0], + "801963d32b03942abcf4c1ff1b1ca2b7e5ff2baf3788fd8205f095fb93ad9c4b": ["admin5.com.", 0], + "801dfadb6cf061ec38c861b200c13ec30bae19ee0ec5438f70960a221a36bad6": ["moviefone.com.", 0], + "8023e43a28f8a75beaadcae152f66cb92d37dbf224f756e1f3777e24f7732ee6": ["deccanherald.com.", 0], + "80246fb4156ba1173768c51db92781692cee01554ca47e1956389397699d6416": ["group.", 1], + "8026212b92d80303cb63976f05b38764c4036fef4e2d58c5fe14096d26a296cd": ["anwb.nl.", 0], + "80273d51de3c0114d21529c18e5153e73b4340508780d5a086462bf6f1494298": ["fage.", 1], + "80332ad2de4fb82b6be77a01762d91d93fdd24b58f3dc3a7429c69768a945621": ["kerryhotels.", 1], + "8039525dbf39e73f28b3669099990a86ec4dcddec07176c23eb899e9a2d5ec3f": ["teva.", 1], + "8041db7b9039ac1b8862d7622eb3bfe3c464b7d54f3efe8920e84016550bc0a6": ["cooking.", 1], + "804366c160114579e58e6d9c2d4764afeb0bdbe7fcc2f7f74d11acfd325856d1": ["carsguide.com.au.", 0], + "804566854dfa4f38bd751d7c8e7c24006ef9076fbd7c118e266c680b66691a69": ["rpp.pe.", 0], + "804b8c0254206ef44cff4a8201fabbb395dadf5cee8e9124960e0e9819521f0a": ["sikayetvar.com.", 0], + "804ff3222303c492d1d6f5dc27bbb20a7e24b444ba654186519cb8104842d12f": ["hko.gov.hk.", 0], + "8050639a88a7f97e7a83c95b6f2ec042e3f73a04a125181525e994e51394c4fa": ["jjwxc.net.", 0], + "80517b75d4b04af210b7ce7bb1338bf716ecb3f5365bf18ca11b468d767861d5": ["e-klase.lv.", 0], + "805b413167d3da7767c4eefdd71a5a4ff28b0468c5a7cec64e5ce376b3bc776a": ["startface.net.", 0], + "805b98e58b860c9a3db18b2a4d15ad563575274efc426a9a03c9183c0aefd03e": ["hugedomains.com.", 0], + "805c83d9ffa6e577ba13649d9f4c517500dc1d6d3a7f5fa3114d626b5c47c997": ["readingrockets.org.", 0], + "806774827df45e3af52384ce0193f69d7e6952239aaa9daa7cc23e99cc006973": ["vedantu.com.", 0], + "8067ea83421e76e824ec733897d6ff83d153524affb0ac5bc9b53f185cd851c8": ["ning.com.", 0], + "8075688dde07a4873f6a5a0838b33261a478fdb6642ed2d6bd4bf661c62c39e4": ["pin.", 1], + "8079f4cf738cf637298755539f495efd9ba13b5336737153d38dc30f2136e04d": ["zhiye.com.", 0], + "8081776dfe34f34ed9f51027e43e4ffaa734cb8251d7fed326031b3ec1c26831": ["elmundo.es.", 0], + "80863e58f5a8f3a6a4baf8d479d97f0d80dec9aae175b77c5ab40d773fff5782": ["heraldsun.com.au.", 0], + "808a9d9233945adf035ee1c99654b6f57c9095331338fb540777626137560e5d": ["sirsi.net.", 0], + "808bcd029258d3155a75fe0868a9543132360a52d9ed916dda6f51c74f74eb7c": ["youtubemultidownloader.net.", 0], + "8090b0a304848018dc1d048dc69b3af20cd0e37c8ea1e00e2e20c2a76d95be31": ["townhall.com.", 0], + "8092cfe4d2090b9dd352522833ce61102c5ff67c8a6c573a70d238a8ad07534c": ["konami.com.", 0], + "80938cb26eccc6be2f5ed87b9eb36e413a8c3aeab13bc1000de63eb773931269": ["zalando.de.", 0], + "8095f745c45d87c6a282060277194b78601d45fafb502765e25fdc523a72fe10": ["booking.", 1], + "809a4dac6b867a472d4175cce867e7a456eab5176f2305aaf1fec428535653f7": ["azercell.com.", 0], + "809b129b5fad6d008f537165a3f196a65e56f0cb169ccd4014c0c439db9b3bda": ["hidive.com.", 0], + "80a3cc1cf9759da68b783680d28f2c2b9eb9fcecefe199844bba1ff9ab4b4ba1": ["allkpop.com.", 0], + "80ac7c757f46ae5763e3fd9407d4e9cf02beb76053b45c4cb61838c64ec9d4fd": ["rmf24.pl.", 0], + "80c718b1c50cb4c3e5087d5487bb33573e4313b32377f510ff75f8fc614b5842": ["ourbits.club.", 0], + "80cb6aa8606ab6d429487f26d5de42588034efc70bddde5e2baaa38589374197": ["porsche.com.", 0], + "80d2dd106c41e871d3f329d361a7844ca179a973f90c433c9e1e80c4486a3073": ["pagina12.com.ar.", 0], + "80dc02ca669cde6dfeca9acae24fbc472b00a1d04620a4e1fa12a2fe00e4803f": ["gsk.com.", 0], + "80de1e9b7598013186da2a0ecbe338f207c3772a61ed2e5b105b0653460b3a35": ["fotostrana.ru.", 0], + "80e2804b1ae7bfa95a93894097d1ec87427055e3ff9eb7740b51c0cc1eaa25a4": ["biobiochile.cl.", 0], + "80edf3197e1ead411d2658363e0959e73a6201b498804cbc9c09b10b55244cc3": ["modsbase.com.", 0], + "80f18c20dc13d19c991df87b4dca26e47f0302b288d16b3a77fb34e1d39851aa": ["crictracker.com.", 0], + "80f5622db5b613d48b1bd00bc2805714dd9c6ab26561d39e26ad310862e9752a": ["symbolab.com.", 0], + "80fb34a2c6bc537d2d044c013042b3b36239aaf3cfd381d62a9ab8e8753876d0": ["aaa.", 1], + "810347ee5ca0e4e3dd9fb2d60bbc285cda00ae9e671776a739a733aeca8e17e7": ["kmplayer.com.", 0], + "8103f82d2d663e7488c231d3e8f61bc2bb81163092c387319e06bea614925f01": ["vndb.org.", 0], + "8106a6075bb780e14d9789909f67a9e350e11b84965ab4082658b83bc309c0fd": ["tving.com.", 0], + "810e9b60b2e726f940c272ee59da42ec485f4cabb94403ad22869b0a77c3dbb8": ["hgtv.com.", 0], + "811fa2d6a668628f7c490a7eefe160b1e37c1139fee3210357ac0c0f9d4c98d7": ["zoosk.com.", 0], + "81317790cfdfe40e6dc9a05d63b00398f5dd7330cf64ca0f09996d2ce34c2b68": ["britishairways.com.", 0], + "813e51fab1c9919c592e595c7774e95cfa923013b748b5480619552e77bf4f85": ["linguee.com.", 0], + "8142af4b04143842689b07b8d9fae4d12913d933ea80753f6a326cd06cc994af": ["zhibo8.cc.", 0], + "8146a481da5c184c2a3beb5e75474396b77adceef5cc205f5afa554ffa27decf": ["adultdvdtalk.com.", 0], + "81497369786c26b1f64533f64ce91dc13b577210f793fe6341311bbb7f43e8b2": ["egnyte.com.", 0], + "81507cf223e4987d5c4fa9736cadb8dd900e563b7493fc4f7877396e271361d0": ["birmingham.ac.uk.", 0], + "81542af4185060fba66fea4d19caeec8746c48fda0eb3fcb8601f9f4f48d6804": ["zoom.us.", 0], + "81551c02b1cd671eb8629f8ad4c7d434599551f9dcf4be6b2968ab1dfa5d8fc1": ["bbcollab.com.", 0], + "8156f356a69c37c59941a472ec335021d20322665b86445f3e68af637fdd8133": ["postermywall.com.", 0], + "815ab133423ffd2ec43488d7ee5f54b7b0a3fb5ef60c839db7843b1639004a65": ["gzlib.org.", 0], + "815bdccdf913ade215a8d0739f1d816e4422a3027f7d38204a1318c641a5254a": ["chosun.com.", 0], + "8161cbebc9123738465663f6211875d6e16ee7463a5d42736f1244e467201c9e": ["ilpost.it.", 0], + "8162cd26fe6a55c588fd5c14a446a2681380fa55d91cd46d875a7f2d686ad3af": ["myflorida.com.", 0], + "816a3ce913326e366c50acd0308bb8d4df43678929567ff59c0af1c48afa02b7": ["pdfresizer.com.", 0], + "816b237b39ffdc8783126c46993d897cb14581e10c688b9420c9c7d502a913ae": ["hockey.", 1], + "816ddde974e1c8e2a5890a635e97d5332500bbcd90cdc4b14d12ae296e9d90eb": ["addthis.com.", 0], + "8170941d813ea6608374634506fbade04f63eef298abe965147d8b80548baf07": ["wallhere.com.", 0], + "81713088832a859fe0b8ed0f99f1ed36151194425f4012194dfa680f1732f7a0": ["jr-odekake.net.", 0], + "8172a52b7ae3f4eb2077cd2bdbcbda8626953b2c610c9fc16ef0fe2781c6d522": ["aeg.", 1], + "81740803a1c45be98816c0191a1a215d395c85af92bca4ad1d9dbdf8977d0117": ["sep.gob.mx.", 0], + "817c41734be2c0ee0e0a1ee36ee34f479e2629b73ceeaa5894da756fd4d3332d": ["wcpss.net.", 0], + "817dd6bcbc3ba4a6c7509621eadc71d17491ae553b26a449429ccf445cd8690a": ["mathsisfun.com.", 0], + "81858fe33d0b4dda18634bd482558b4d6f927b2c0228bd0081768a63b655a12c": ["jagex.com.", 0], + "8188cfc7734cf169f562bd479ce1cc5d77e7505fe8cd4f27b149c58383f994a8": ["cnki.net.", 0], + "818a331f6d3067c72cf00bf14a9de053a0d339bc68ba2efc2cabbf38ff88a67b": ["aparat.com.", 0], + "818f9f94c11a1365c68171c5f1eb34fbed339ad02580df3782435a5ecbadda2f": ["umn.edu.", 0], + "819036468bf3e260533ae7bce8e0cb0aed46c0e8b567ff4e0d01ad13978b3934": ["9111.ru.", 0], + "819d13b84c19815984e0617105705fe6b62243546512b5125ed873c7afaaf19f": ["golden1.com.", 0], + "81a01a5e39565f781cd498b57ac1c788488c2894a79e35357272989daa9cc929": ["ipiranga.", 1], + "81a77c9782ab213b1e015bd916f0cbf34c698cdfce6b79a97f47fa6d45fe0f5f": ["mercari.com.", 0], + "81b1c711a02c06fdde6cff4fd991285c0980559e8d5ee38be2b8cede3b1756cd": ["114la.com.", 0], + "81b7be5e3e2ca294c2e1519c4196e7976b8f17d238066e84d24c1c0679ce22b0": ["deloitteresources.com.", 0], + "81b8f4838ebe486b2e42d1c578264c60457302673294777731640a879762baf3": ["royalbank.com.", 0], + "81be8e8bdd19fbb80d0f024e4bd6c75bfa345f0da8299eea91c0cb69e487e63e": ["91mjw.vip.", 0], + "81bf1c5a8e10cc09622f267c515945cc15ec50273b78f620d76d1814004b886c": ["ilmattino.it.", 0], + "81c20040b4cc5e34413a70bea92a86260028c3277e6af0459be32cad220a812e": ["kassir.ru.", 0], + "81c43d098ed50e8397dbc683b13820850ffd45a6c401caa7e448a4c611feb164": ["calvinklein.", 1], + "81c75da9ce983fb23f607565c32e1ee09aca67e6f27bad4b6743fac349af9003": ["upenn.edu.", 0], + "81d4440c90c0771e1b20ce67757722f7ec09daea9eb635006a2da939b5d1567b": ["hellofresh.com.", 0], + "81e479ce164bd9c60ce69c7858c1cba7cd771c7cd4dc04fa65dda5f6acce9794": ["manualslib.com.", 0], + "81fab6e0043166cebcbeb946030a9909cb642b81b0ff266c0fe75e4c629b7103": ["ru.", 1], + "820811cec0f413a67719b59980ab513b9c42d6f2469744fb089920489b1e7838": ["whmcs.com.", 0], + "820c280073fc1f65dd65c82a60182cbc43ebd5a044b4aefff0b5e6c2d5f74987": ["scientificamerican.com.", 0], + "82126837f1eb7f7f20c7339406ada81cfbad0f27702d755e409d8596049c56ce": ["okky.kr.", 0], + "821d0785ff6534d34cf8e1a58512c6bc500e390508663d51946f32db5e8b32c2": ["sew.", 1], + "821e182dba48f411cff90aeb4aff76029068d41867585961fb784125b7bd730c": ["paytmbank.com.", 0], + "821e83b60e11deaaad6e74619ac199f3c00ae7b0287373fbacee74886781d7a3": ["eorzeacollection.com.", 0], + "82267b45eac3228513a2729acb5cc59a7be69e8914d0ddcd88f3d817b55a7431": ["smule.com.", 0], + "8226a8491960b4a76ae9226eaee0ef31e59fc1d2f2e78fa0b4c23a38e9a589f0": ["ennaharonline.com.", 0], + "822a807c7c94c595a3f610e86b0ccf231d314c0b2961dc05e980ad28cbb505fc": ["unam.mx.", 0], + "822f591a52a3d3f7246bbe6dc28280646c999e3e5e692c80d05bb3af76bc684e": ["waseda.jp.", 0], + "8232d4e947c80507503ec501f3a6cc96285dd7bc9376f214881bccf7b4a801ba": ["notion.so.", 0], + "8238064ca41698d33885e2e9b2ba630665e267e9b91cc4c2eefcf172cb5276d0": ["jaguar.", 1], + "8239984c62e54a315f71cb4210d0455b642851c661345cff22679484383a34d2": ["pervertslut.com.", 0], + "823ca5084fe4630fddf62b27910f80b4ddb7445c35abd9d28708c902131991ec": ["shooshtime.com.", 0], + "8241e706e338e213a6a804bbd61b06a760ca6a0c2dc66c4315c405ec133e7ce1": ["uga.edu.", 0], + "82427d59109025682948830fcd48c0db61e1743b27b4ec7671a4860f6a7fd868": ["nespresso.com.", 0], + "8245998618ac7f14a56c9468fc7316e8902edabe723022c7aea2680cb24acd86": ["nuaa.edu.cn.", 0], + "824e10536decb92493775d7e1b4776b6cf467af7223602eed6756538d97994ff": ["xitek.com.", 0], + "8255f8ce85bc728a2e1ff7b2dff6c29d5ac397017cd83c133ef553a8f277733a": ["bohaishibei.com.", 0], + "825c2a381d349f01c76cc804abd0c6f07b33fa0f14f9be7b17b8a8a5de15bb45": ["trade.", 1], + "825c75c065d70654091f7b351e9ef670723954664fa2e6457728f6cc21caa06d": ["varzesh3.com.", 0], + "825dd836968ff8363bd67274f7d837984857cd2eb4aec13b8c94bfc9d9b74b70": ["kachelmannwetter.com.", 0], + "825f47da8bba9615adccaa829a02160679a529cf3985b1435063e18c435f272b": ["dnscrypt.info.", 2], + "826620db7e175f69a484925b078f6ece5388748e85bccb42efe2b6c206bddf01": ["bmoharris.com.", 0], + "826815057d04abb9f8e69828021a31f6a3171b08795464f511324acc03a2f9bf": ["wpbeginner.com.", 0], + "8268648b46158657a94e4f8f65be44e61982d3fa5b1efdcfd63016b6270cc8ae": ["dexonline.ro.", 0], + "826cbf327efe17ae5b67095e1af07a332462846d6131d70530f7139dc27d32ce": ["thinkorswim.com.", 0], + "82770a38a7dd0982ab54fe96a137dcfcebce24612636a4a6ea37417a3f84af04": ["xn--j1amh.", 1], + "827894884b410fa50948ad0d2a41fe883c3b8b56608cccff534ff2bee781d906": ["kimovil.com.", 0], + "827a4c2fc37f21e1be8acd9586cf6143cb1e061632fc98554a4784b3f9817be4": ["pepperfry.com.", 0], + "827f0c23fd3298cd2dcd26b223a8ff1e8e3ef15fc37ddaaf7d3aca8c1ad771c8": ["myfxbook.com.", 0], + "8280db9eb4fdcb17d7e3bca0495b5c81408eda87df6b23a065971ff7c2f7664b": ["airtel.", 1], + "8285ee1df587ffa2427a2e162217cb06c7291034b4caf1d03cf871e5a36f8d3e": ["parlamentnilisty.cz.", 0], + "8287ae510360a343133f80d2e16ea7f0dcbe3bb6e169072baaa3c6be42006eb0": ["jss.com.cn.", 0], + "828d11f1bda4cc2f7ffc5a3c47f96cc8517e0a097c2bf339eb6842538ae1f6fc": ["nitori-net.jp.", 0], + "829cc129d6c874baae8afac56de6f0259121f252379e2be702e52ef87a6ff69a": ["cdbaby.com.", 0], + "829dc3222f8ba251e12fb2f7a489ed302dba512a403eb84303c2f9e6230e6b72": ["tuoitre.vn.", 0], + "82a4275423981f3da000e5c79b5c85ad5bcc9120540f0d26ef83f84478478614": ["bdstatic.com.", 0], + "82a45389b256ef994c2d37cd23addd7af1f3340cbf2980ba43a97e3f8c965ed4": ["indiatoday.in.", 0], + "82a4c63fdbf6bd1233cb9c5a46bde608e6fc2963f45cd7f9f9dc727cb26d2aea": ["joemonster.org.", 0], + "82a6e690b3cb321baabef293fdc730c2583fe5d4a3cd07982728e471d1c570ec": ["sanofi.", 1], + "82a8a83a9f01ffbf400fcac6f3743e77dff350830b0c2f0fd1b8244e29e5ca5d": ["myanimelist.net.", 0], + "82b03283a443c21642bec96b53fb787a4eda28e9ef641a3d3185b323ea019561": ["21wecan.com.", 0], + "82b4158b2077692e4de67a938e84cd65a8d89dd3df8065ec825c8788a67f5c0d": ["vetrf.ru.", 0], + "82b792bffa78c22ca44a41058044ffe47cc65d004dd27d0c65954a0231332aac": ["uanl.mx.", 0], + "82bb5fb91a7374e5b5849933deccec850525dd5b3f6b5f918997491423532ecc": ["delhigovt.nic.in.", 0], + "82bc269ce864fd73170390472b8581bcfc093f387f013b945ace1368c553af41": ["nexusmods.com.", 0], + "82cef881f5b26cbe25d5ac044e0de121c40d05ed55226c8e73328abaabcd7b30": ["12306.cn.", 0], + "82d75a6075238f0b65b2043edae3e7fb7b68e87a321fa3de1de745dc744a8255": ["geni.com.", 0], + "82d7b4db5f5c0c883d0cbfbb51782be1c6f216754655db95ca9b457587cac78e": ["downloadvideosfrom.com.", 0], + "82d97a8b7eee3f6f746d4e80a49973c687ff5717523bd6c8a9c80e2ad2e583e9": ["paginegialle.it.", 0], + "82db4621369fc21dc481a2d34a13d50d701d87b57f40661bdd191190a6f3cc23": ["crystalradio.cn.", 0], + "82e1f8462c4065d6aaceccc3bd58416af043586d23d719e849222dc4312e54dc": ["xfinity.", 1], + "82e61efa138a1f317ab606a10a4d83333e854c20a2ad65e3c6a3fae37beeb569": ["cau.edu.cn.", 0], + "82e7a7858fc47f1c16648a26482f6212922f3dca525f56c643351c559f02ab32": ["sciencenet.cn.", 0], + "82edce5589f3fb141cad3316272289938153fd2471a05df79682db3725619681": ["kemkes.go.id.", 0], + "82efcd9b109582d1d5ca16e36c751ef87014dc2c753de8bcbec841b554600ffe": ["detik.com.", 0], + "82f2826e939e2f146aa38e73dcdbaac0495228e6fecddcde331f2c7ffaf639b0": ["portalinmobiliario.com.", 0], + "82f45b9d211481a971e9b3f5bd32362650ec8a77c526b2dd330c6f9be8d956cb": ["c4d.cn.", 0], + "82f5c1c0937b698c439a24c5626d6bc753f11304390140438a653a3a1f6fa9ad": ["movie.", 1], + "82f6e253b5814cc5cdc3f4b592661b5b2a5c5855b507decb46b255b66a98c483": ["marxists.org.", 0], + "830493333c2496ef9adfd101dfba0863901e6fc74aa97eca95095eec01c15ce4": ["imdb.", 1], + "8306855ebbcddff331af760c37c0ae0935273ac679df99cc103fe32320903af5": ["roberthalf.com.", 0], + "8309bc5fb6ce17e0ee919f3a37da847fd7ca5dffc4dee50499c1ba449901f1bf": ["waipu.tv.", 0], + "830ceca8bde33aa3474c94bd42af23cd2f68f890d537550ba30683a95529fc26": ["sudouest.fr.", 0], + "830f508c85656dc43cb45e4a39b26787e95f26cea99c9f9c0c973e8fa5c9d3bd": ["usps.com.", 0], + "830fdc9a6356e68902526f725143c623f015dd5395b09f0813bc630d507cedf2": ["fzmovies.net.", 0], + "8315456d572dcd8a03133c44325d436d04b32dac604e0294b2260c4fdc3c84a7": ["volaris.com.", 0], + "8318c9ba6d8b30f6cf2f1605accba17c1493cf1df757c3202dedf5629ebd8ec2": ["omnivox.ca.", 0], + "8318e03ee50ae7b5fcb7a53a0e01f57f593bbbea613dd96064ec2a2b821a45e8": ["i-ready.com.", 0], + "831c80a74f6915eceb8a64fa88bf03f6957b9158ee2b18e56348f6f21800e146": ["tech2mkt.com.", 0], + "831e165213a8c6f81ed4415c0514b41510599f30abafa65858ba972e4deee3f8": ["get2ch.net.", 0], + "83202bd70c3677a82978e6af2a07dba64206118444c21b8d38229cb00483a662": ["pru.", 1], + "8325d7d7232d870a0e1dbd7ad01b7eadbe0bf8ec6696481d906576a40bae8e5c": ["amfam.", 1], + "833a98e308c49364f3a3b95d8a1eb40b316867bd73a1fbaf6993327667462878": ["csdn.net.", 0], + "833d29add7420e90f3b662dfdc84b62181efd55cb9fb7e579fef8d2f79de8ebd": ["partcommunity.com.", 0], + "834116132aa8bc752fed791cfde66837268342c7bd362149ead7afb35da825ee": ["eefocus.com.", 0], + "8341e306f00aaf293fe3940dd2f26bae22ca32aa13ba8e480b87f5e244edd058": ["computerbild.de.", 0], + "835181b1f1e54c072368d1a1716992fac01ac9fbb94a1ee105d84da59fc01b0a": ["lidl.", 1], + "8351b55f4f978b53036a7d1ea384229f242c47ea55f70893a4a8c6456b087637": ["novaposhta.ua.", 0], + "835394479c90543541b8efcafe2425a4e7b8fdd857abe129f638db6b9e01f840": ["citidirect.com.", 0], + "83594cde5d8e0d3d91af5b62bdd9f317da189491ac7ffcca3f7c570c1f1eaaa8": ["technology.", 1], + "8359775e24064248b06b5b7fbf47f8a82aafa5ddfcd784f37e44933aa667566b": ["xn--kpry57d.", 1], + "8366b22835603b459a406a8501503608fa47b4528ac7d8a7eb1af62acbb44b47": ["tarafdari.com.", 0], + "836c5b69f5e1d0b38827b887572048ff1592044c81caac6ff9e6c3380452e81b": ["365j.com.", 0], + "8391784b9dd646dd4a6d762d6b380502d0bd326f76d2489f641d4415b2a562c4": ["usg.edu.", 0], + "83932c5564c735a21cbafb260697f36fc472b4b69d308b3392532a2eda00a8b6": ["kanbkam.com.", 0], + "839711a6f8848e065c44d8a436cd494a2dd81ea1751fcdc23eec2c1b01f58753": ["healthequity.com.", 0], + "83a80d08a3247d4b4c48891a63c1a39241ef580e7ac38fef0bb6ab5741b1f650": ["modoo.at.", 0], + "83aab1d8d8908f1390c1f4f64e679b8f3f225dc4db4cd86b05e59e9211b8bcdc": ["shufoo.net.", 0], + "83ab99127d4774199fd05d4ce127c5160e50e14631e8d44fb86fa1c683d3bf82": ["uniba.sk.", 0], + "83c1b6060e10e22ce6e739929c83abdf9f8a56d3f808b62d2a1c245c4b51592c": ["assrt.net.", 0], + "83c87b94386569a2688860684fdc3dc041fe5d8a1f26ababf05fdaab3da36ce9": ["webkaka.com.", 0], + "83cc6dccb22519ec9dac62b79fbb3a9e816bbcb9081fa808c0a4c4359c693739": ["kxan.com.", 0], + "83cea24bc347890770a37e6cd0d978bb1b334ec066bad71bca3aada3f7f812a3": ["alopeyk.com.", 0], + "83d0b56859c8979a045a5acbe2ed045987caf8528a7fd888d908e5645f7b91d4": ["kpop24hrs.com.", 0], + "83d37c3f15fb08af819da5a38b4ff53e458a97da6efbcd5ce0ee0387f1cd5299": ["gigazine.net.", 0], + "83d7040ea7ac599c6798d05c5d4ffc28202e2a3fce64dfdfcb83d7c3fa507cb4": ["ps123.net.", 0], + "83d8c0fa9bf33e785e36d435564dcbf4f0b4f9347452e916973a8963fdd266c0": ["wpengine.com.", 0], + "83e4db451446391aba02881c0fa46538041f6b8d1a65bb98ecfd6b0ea0c67024": ["kshow123.tv.", 0], + "83f5aa8c2cebfac551f48d609b49f6c12099bfb15a93ea76d62626d3682cef6b": ["lostfilm-hd720.ru.", 0], + "83f7a817df0494dfbaa8cc2453b5196103afa2339021fec82ef5f05a187297a6": ["bt.", 1], + "83fb087688ea887990832b7265288fd9124f091d5f90a4648778d7cfcfeb7c24": ["ielts.org.", 0], + "84028b8fdc14a15afcf8ad9fc0b5ed652380f042d3111fefe26ce692b78d9679": ["sre.gob.mx.", 0], + "840543a98198b1cb3d4bcef2c293b8aa5e1de76969d3bccc0414649489533a6b": ["hsreplay.net.", 0], + "8405af17e0ded012b0e9c2e7e3784499bb3a27c5acb0936d0bb81b0eeddce223": ["xatakamovil.com.", 0], + "8424b40fbc594c5665485ea34c22a92c50e8a919274159d57c76fa8152205b90": ["hentaiheroes.com.", 0], + "84276ca28eb8105a646abe22ad30f28aa5029958c8a6cf146d01ee3f0c51702d": ["imagevenue.com.", 0], + "84350d31755aa85277c881c13e86854dcd06832fad228b07a4f8c78f776ffc3d": ["thehill.com.", 0], + "8436a126474513ab0b10f7bcc6bffcfbfb369616274d2ccb12dc20fa028a5eb1": ["like4like.org.", 0], + "844273de6becdc85cf39b5163bfecf55f88529b96959e8c886e7e0de7b8e142f": ["clareity.net.", 0], + "8447d28610dc51bdf8e781b3f5860342400125e1b2fc59a761f5a570388d6df6": ["vrporn.com.", 0], + "8455f13579f4cabc09b71c56e529a11a4ee5821f43ce3272bbd1fbba0de187e0": ["clips4sale.com.", 0], + "84589e6e2d92b42fefc31c6fa63bb2c136bdff680d8c21f70fae36bb8cd68a3a": ["camelcamelcamel.com.", 0], + "845cb7d88d755b026ba250de84d390d25e3c75464b46bf2f8d1e2d2e9834d786": ["interserver.net.", 0], + "8468b35797ec030e38b47d00faef97b6a0040dd0ed2d0dbf9beb498a8aab9c76": ["7k7k.com.", 0], + "8470bbdd4f3d54d647220b7af75fa4e1d04f97f7cadb326084c6ceee5a4da66f": ["clalit.co.il.", 0], + "847133a7ed394df9664939515075d336ea27b75b209b71d95856a53c8600513b": ["codeforces.com.", 0], + "8473dafba5445258aa9ac75470d16a4702a9abebb84bc4501cac0f1e5a5db39c": ["emis.gov.eg.", 0], + "8475476be974f41503b0943ff719401109a95f3b954c6f7cd16b7de4fe47b7f4": ["nobat.ir.", 0], + "8478b57e58f3b00211eeaea235181488c113baed59ef66454194f80ba6be7fc2": ["binghamton.edu.", 0], + "847db0dd384ce35e742d7586e586706670c1a7f2db3315f7200fcf0268805b2a": ["tenki.jp.", 0], + "847ea563f97131f77342e31ffb1d4dcfc3538a5370935cd48537f94998960240": ["ug.", 1], + "84820ee6023de07e790d84f04b7924e496b6c20616f9e7633dd33ad1eef1a813": ["idcspy.com.", 0], + "8483fd750ae2ed9c4e6ab2182a953bd90d5732307c09b4702247f2746a67ce54": ["bmw.", 1], + "848f48f8ab598b85779183cccecbfadd5e3efce316e1c7843ffb771d262cbae8": ["yourtexasbenefits.com.", 0], + "8498b05da958d2debc6b050a83a837892413ca29f894e0074632ed6a1f8ffdf3": ["rainews.it.", 0], + "849a395cd636778fd7c42e70a13e016875bcc2b741515a4f2136ce1383601155": ["mytvsuper.com.", 0], + "84a09bce6b79140ca55cb855243f64e3571086a11ae0180506a35952b0703930": ["navy.", 1], + "84a950413efa4a3e8f6e4cc2278853953ae398e2917f83945cdc0fb6cafeb6e5": ["guidingtech.com.", 0], + "84addad1e81a0b7ea6de0d2e9b75304f5141eb06290b8da3e0eff860678fdf40": ["shanbay.com.", 0], + "84b06d8dbeb7ce8fb848355a8c556abb5706e30121894da33e09a9fd3c9b7e29": ["paymentwall.com.", 0], + "84b99dc14d6adfc1dbc12a24749842a1061cd3b9b8f71e596a99a41b529decdd": ["lausd.net.", 0], + "84bcc555a18a1aacc8f21e22a7520e397c26f803ac576f6abe7c2d5abd3fca36": ["iceporn.com.", 0], + "84bde3a9a1995d11c125032fec518cc0293eaba027b618220b94ac55e00835b9": ["torrentgalaxy.to.", 0], + "84c0d6495012073a26b7aeab216f1bf08304838536db0cbdcff1eb90c740e257": ["xuexi.cn.", 0], + "84c2d23fa183179d12a8abbddb777ecc53de9276589ad5c1358dd7a212fa50c4": ["altibbi.com.", 0], + "84c38910caf6ef3625f0acac69dde14f9ac9678bf9c0e45e1b07ce696ad98c6c": ["freemake.com.", 0], + "84c7f20c84328fb2ed00fe2d73991a999167f3653017f522d582b7fdd43e3d8c": ["cmovies.so.", 0], + "84cb0555ebd5899b9ed809a61f8c25ed5bcd9ea54d7c4f4a06cc9b8f95b9e7c9": ["kinozal.tv.", 0], + "84cb801c3047e021c73e7d75e288a3274a2c9965f30893ecf6d505017ee73f02": ["sslshopper.com.", 0], + "84cd79c552a449d4e50257dea4c7226a3f76a317a2b50b7da4b09a6df9343d3b": ["hlavnespravy.sk.", 0], + "84cf8a25e5c6c3e34b20cbbf4f2dc4895257211cd733bf3477ccd8e978d4993d": ["perfect-english-grammar.com.", 0], + "84d23abe59376d2f26a4926841760fddd4ad29f56491206677b5f674caf586a7": ["storiesonline.net.", 0], + "84e09f21daf7fc4961acaa2cbcc94c79f263f7c060e15e299376141be8191589": ["digi.ro.", 0], + "84e56bc288f1a3a5a2e27d6bb45d6943d7c40d1c7856dfa305edf47abeb6c5e4": ["anandtech.com.", 0], + "84e5f0ab3c326b3e5f345242ac5f6ded0281b97cdfc928a547243c97282796b6": ["guardian.", 1], + "84e909fac441e17b997f31879eae549cb358c5c86cfa71d40a24199e373c79f8": ["greatdexchange.com.", 0], + "84ea033d9d227ba116de3be9a8d6639b28ba2810a29d2541e815d948c211e2aa": ["skat.dk.", 0], + "84eb0625d732afbe3644b56c121c254eaee1981338b8d8680d6398eb2391e7ee": ["sbs.", 1], + "84f19d9027d2586d78cb81cffca69ac59a468966aae53257a7cbb99cdb0c057f": ["regmarkets.ru.", 0], + "84f25e6c01d4f09edb4f28855454dcd091b2e6223a832895476e03884ee57bd0": ["underarmour.com.", 0], + "84f79fd2cf582cab5b63bbd0bbef680372d590b4419d7ae6efe51c3a912438d6": ["ncsecu.org.", 0], + "84fd1f9ab77c863569e1af0b4d92bde4f77e630f5e2bdc61846c46d71635df3c": ["jhu.edu.", 0], + "84fd3d62bc376ae5fae33dec6b7feccbefbfe294299f93a861c799ecaa8e9675": ["medium.com.", 0], + "84ffe311f927af82882a71883142afeb609bd303624c924441819b503a8e9768": ["doublelist.com.", 0], + "8501cb3f45895bfbdc90204fdacad0cf40fc8b33c150c091bf668a56e96e4b49": ["careerfoundry.com.", 0], + "8503eff3d6746c24bd2d8e7c22c9ef75005ccf159342b089dcf708e9d9c707c6": ["freecodecamp.org.", 0], + "8507d29c1a9dac4b42f0c8bdf75169817638b9762c95edf58ab40e0b2ec8305d": ["arstechnica.com.", 0], + "850e633eed86b2b5060b3836fcfa75b73ed83fbc1ba76a9189d5a1c727ac1477": ["farpost.ru.", 0], + "85117bfbad3d53249c0fcdbb3b7e420e1fd3c52e0f35d6cbe60db12ef988cd74": ["forocoches.com.", 0], + "8512d53a840965cf6584b8c6d5dd5756fcf22b48105ef194820feeb24e484407": ["filepuma.com.", 0], + "8515a6db0d47f659e75ba8476df76bd299c4940a5025d68070ff3e1ad9e69a90": ["jobrapido.com.", 0], + "8516ff468520522cb1e6f96cd4c3f67c5467b45804fcf7e782b38bf384ced9b1": ["coventry.ac.uk.", 0], + "851832c13f9bb21a0e61200b7e36ef536f7869a3bcf14950413e9605fc567cdf": ["soccersuck.com.", 0], + "8518741423a27cabdad32cf7dd43d50f0dafee07bbec9d167577bf5f076a087f": ["cdkeys.com.", 0], + "851c848bcda5e54a90bda2fbcda102802bc7bcc1f7c658bd7ebbc95254f69551": ["ibps.in.", 0], + "851cb92de292f27a86e2b4c6af50fa2f7204e875572797fa816e64ea5f19d19b": ["jalopnik.com.", 0], + "852026065bee65f91feca9a66e9ec2c3db9f16de1fec2cd4320da443f411b60f": ["uchitelya.com.", 0], + "8522c45d84670b0dfadc6e5e247d86ec8cc33f8ca65d752c3b4ee774f28c2aa0": ["irozhlas.cz.", 0], + "8536b6c8e5311b75bc83eafe1bbdc6e98befc3ebb072740c980b79044599317e": ["homemoviestube.com.", 0], + "853766dd695b7f2d9418d4c0021b7623c9ee0f1c839072a34b61d92403bcc987": ["yektanet.com.", 0], + "8542d257d386849b656fbf1dd5ccfcc65bb1946e0d80f69b4b278401063a1b2a": ["madrid.", 1], + "8554327f7b73b4a0b15cd567cd32ddb74a639640c6f2b72c4e66f128b68b4a01": ["divxfilmeonline.net.", 0], + "856338293ca06d43681e9fa193c4402814e6dc1a50c2b0ab87df9c69bbd9f577": ["standvirtual.com.", 0], + "85647edc71c2a59c438020e110fb19a23dc8a86d47b894df8abcee3ffefa0bbc": ["southparkstudios.com.", 0], + "8566b7c3c32d68e0f4f8140b181d3b51856b9a9f803ca1570386187c81b48a1b": ["inutomo11.com.", 0], + "85685ac1bcebb38901e3498106d24967bfbc74893ad2e20596886df9a304d686": ["jackrabbitclass.com.", 0], + "856a7c52ce2415075da8fc4b800d391d21d7d0cdc16b86b3d3ba659d1ecfd901": ["gulfnews.com.", 0], + "856d7679844aa911697841f70ead05aa0a65c5c3dedff9245d1747782a55e114": ["shemalez.com.", 0], + "857420e655db66b5f7640f482bac126292e3700ede0f832da84c57511e0c83b1": ["hotmovs.com.", 0], + "857729fefa99c5b9521572a920700ffc88e2c04942484821fa02170ce049bb1e": ["googlesource.com.", 0], + "85840c0a6f3c578c127e5a318b23a59cec0e142672af87183c3b6e7e71c97664": ["pistonheads.com.", 0], + "858495f055d84b45f3c2d41640c7290e81ce1a57efcd451f022b0cba78dd2d1c": ["arduino.cc.", 0], + "8586b7fba2ba0420afbab69e5d4e2163978053d60f8bd0c14c73eb8f27d1a495": ["yiibai.com.", 0], + "8594ae9637c8e19b8760c558e790a75b019600c8a7c73f86f63398c06343a476": ["risingbd.com.", 0], + "859cbbad02aabcd4d5144ed440fca63dc8710fe2380997da999cf0e84abfd096": ["x1337x.eu.", 0], + "859cbd19b98e068fe07e440cb69f824d74fc8d5715f272d6dccf464fe0aa6c71": ["name.", 1], + "859e73b414bcfd7995084bc4f1229186ee24a45ba55f0c1f4a44ab83a3828d77": ["on24.com.", 0], + "859f72dbb2d3b7ed13fde1dea58fc3f6a86a2a15a0dc81a36543027e08d02195": ["hdsky.me.", 0], + "85a0e8ca6c2c909c070bcb2cfc8296b552ec9f50ac40ae6b3fe10cafe7eef10b": ["yoga.", 1], + "85a1dd22f0e8ed686e2e56c4f4e820253bd2493a1c785c976103d3326aede318": ["bls.gov.", 0], + "85af857418b3f26a6f740af15a23788e30cd8b296aedd66d828b7e59eb0a27b8": ["1fichier.com.", 0], + "85b340cf6d983dd904092ede977e86fc0b1a27073c1c15521bc83eac9fd8e7d5": ["ccopyright.com.cn.", 0], + "85b6b366496c42af2e2c35bf45fb2ae2a58f388b5eab2750db9351c8be057948": ["hubspot.com.", 0], + "85c7bd60a924d500fc7615391f0da929669a0c735f376b4cb9d5fa40de432767": ["corsica.", 1], + "85d531daa5505c6192fbbddd7d819f6e9f95e75cf791fd2a1802c6fd3cfb6a67": ["tdbank.com.", 0], + "85e5c43b53285f2c8e3add5928e705b7991b0d915c1fe19150ecc5664c544ae1": ["flydubai.com.", 0], + "85f1110f2c91106f3b5bd425ddb96ea95cdc4c736438f259199d394ce1f39495": ["hdhub4u.team.", 0], + "85f752d13a672409ac5fbdcc9862d8237dc4c8fff5bec77ab5592c2d911b083f": ["yun.", 1], + "85fc65dbcc861a4abbb96c10607707bf3567ec5e7aef16e3ccaf3b79fec4e976": ["ufsc.br.", 0], + "85fceec3f125b628b91f00d3152c942ada7169e66284cf9a9ee4ba68f7981938": ["idokep.hu.", 0], + "85ff5975f83c1aec0c43c26e25f5cbdf84ef1a3def5c585ab054e98400c16577": ["thisiscolossal.com.", 0], + "8602a493f9bd4daea07d400ce0e72c80ef92305a735ec361de1464aacc83f774": ["ulifestyle.com.hk.", 0], + "86057f5b634a6fe9a7bb6471a19c26dd6e117141cd9b2922b30d01216df2ac27": ["grailed.com.", 0], + "860e9c5a61c01adab205f453465234872a9ceccfbdb674b01974df0f59b112ef": ["iresearch.cn.", 0], + "8614be61e24dbcb7f55621fe31de23ebeaafd2815806f113397f6d60e2a5d26d": ["skokka.com.", 0], + "8623b467518dd9c8816b58b72b6b633778ffa64019462e00a53ae2f2a9720fb1": ["hostinger.com.", 0], + "86387ce3528ceaf06866737a1b4de91da32291ce02c4e099b5020f4a9109ab27": ["mohurd.gov.cn.", 0], + "864bf13b5e669781d9ac0a302a2b32a13f20a97c5ac772d98615fa5293a4b213": ["howtoforge.com.", 0], + "86629b914f511ded03b64ab386b757f8b6005492ef22256ca047d1ef0791972b": ["adobe.com.", 0], + "8667481aeac1873b394d55d19bd52cf76045a9e89425ae6a7788a27f3a961911": ["adl.org.", 0], + "86678259324bed06441e226a5901a9f815e2d80f1a164a19a8f4f098d6a43951": ["just-eat.co.uk.", 0], + "8675188b603b3087028efe19420a5e6d5edde669f427827c3eb28e9268057255": ["kaigai-antenna.com.", 0], + "8676af1fd39c0c0725646563666260a23b58e92733af5fb9be50dbd7ffe0a462": ["voxcinemas.com.", 0], + "867d9dff15ba3ae0e01ada98c73851f3a0b7e85200f8f57873d27e3792d65037": ["gbf.wiki.", 0], + "867f6c382f15c724b6f26cb6954eb5a874e5fc0ae80c4c216786a961d0bdc46d": ["e-kreta.hu.", 0], + "86888833b584f18d0d816ec8d8ae6b7419166937f91cde7e0dd603f83e75eb16": ["cocodataset.org.", 0], + "8691301637df34c800b71bea93a7b1cb33a0213d04d71373b3ed0b1b8293bc46": ["nccc.com.tw.", 0], + "869434f23b13aaf89754a79898b9efa846f8df6735f8ae0e48f572fd0921ae73": ["fishing.", 1], + "8697232d157048e474d6dad9b5dbaebae19c820900c60e50a8cc92751a2a26ba": ["hktvmall.com.", 0], + "869cf8e65eddc63de601656dc4975c556e7213ec1d10d39e007ee2daf42afb1f": ["amex.", 1], + "86ad25706fd43fd338471d86484599b42e7ff5af8c592b667ca30f2e63e76d7f": ["nuxtjs.org.", 0], + "86afcbcc3f154f82eb4cc93a5d5b1b326aef7662b3b3082fff6fd3dbd70e01ab": ["gea.", 1], + "86b0243c031935c6a056c72ddee3bb29746fffec261ab74ea936120750ed8470": ["csai.cn.", 0], + "86b1611e691c9ae272c1c9fedc64db5584582f56d2b9a9ea2bbbb2d7cc2fe53c": ["wuxiaworld.site.", 0], + "86b3ae644ed932622d09139f6fe2fecefdcaf78a7e17d22180541d7329b12fab": ["kapook.com.", 0], + "86ba46b0cdd5b4df2a22b40aea80d7fb4c883c3ef823d55037529673c35be431": ["unjs.com.", 0], + "86bae9eea5025a0d939db4f6673724fe75694e5039cc04de1d1461b22b9797a7": ["msi.com.", 0], + "86c1bdffdd749865a59e4990428f3df1cacee3b5751bf51eea8c4ced1ad343a4": ["digidip.net.", 0], + "86cb39ceeac01b2c9f0239aad1efead7baf9bca31d2281e8ecd28cfe59ef4f1e": ["kmart.com.au.", 0], + "86cbdabeba46c2f05e460f7183d20a09cb01c354fcb81d29ab54773e4c7cd490": ["telecom.com.ar.", 0], + "86cf3f1dbf9c0c87a14aa1b614842c89d2054c69996af79da8075bdd8b5f0e7e": ["harvard.edu.", 0], + "86d2c454a7ae34d6bbc5c8a359e4674f4fd78f54f0c6518ae1ba7485d12ac245": ["oraclecloud.com.", 0], + "86d2d9bd18187c66481286d66d4ca352e52ea04d73c5c22c40af6796ee67ff23": ["iqiyi.com.", 0], + "86da501f7fb954db3802a39f06b158be45cba82b664de9f9c7c818bcbc65a20a": ["elespanol.com.", 0], + "86df3fcd8e49cc31aacd2bfb1a3f645212eeaebd4402010d791dbdc8f0b06627": ["hamrah-mechanic.com.", 0], + "86e3b2508a6c484b4aa95ecc7702b46764a96d844c199675ee8f3c2870fef3f0": ["gsis.gr.", 0], + "86ed66b479c25cd17b600ac7790bb5ef7693cbc3cf098de0d2e1ad4f95db3b9c": ["jmvbt.com.", 0], + "86f9a4c0ebd9212640690c3586458f3034ebe49b18f64c0bc6931a73c70a4510": ["zapier.com.", 0], + "86fffb15d86c4883597b2c680d5fe1f58e3b5d8d20552f2b97dad8110ad5cc9f": ["hentaila.com.", 0], + "870402dc23a58f89b459528d1eebfecadbe95dde3e7d606c29f349ecbaf54546": ["news247.gr.", 0], + "8711f04c4b7779d9e3331f7ce403849b82ac18cf72ae6f3f43d4b35d0d7bb9e5": ["2conv.com.", 0], + "871bbe1247b4d9efed6bf80dc75c96ba54452d5400a0aee3735964c164ebf8a7": ["gamesradar.com.", 0], + "871f7ce9e322941c2bbff1e702dbcad3c83350c46b3766e37a0e84e27a6e2af7": ["fhl.net.", 0], + "8721fd1fd4bfec9626454a27123051d51df211a1f1810f3b5018bf15eb50177f": ["hsex.men.", 0], + "872daf7b277ec2e02b1344d7ac9bca435c3881c8833fc32f648405acfd3ee031": ["ziggo.nl.", 0], + "8731ac4e63620709a73919ad1501635f12085f702c568fec71c474f7223094e5": ["bkmkitap.com.", 0], + "873ccf762238b8edd3aac896427840e11a16d58d8145af4b06d77ced7304c316": ["rocketlawyer.com.", 0], + "873e57ba44a1b9538fa7577bd9ce6a0748110a90b631704a44fabb7865a07f02": ["trainerize.com.", 0], + "87438c71913b42bce8b1351432d98e29bf346bdf47438352f443498600b35f5e": ["sinoptik.ua.", 0], + "8756c749b741c52f00e5e3e1d51867a58eaddfd9ddf264bb57455a5acb1360dd": ["elearningontario.ca.", 0], + "8758d1176be966521988378b23e38bee24e937644f040c7aeb143da66db78553": ["asuscomm.com.", 0], + "87599e9d5082a71e5e46ce464b16e55a38361b0c719ce64872e1ebb491346b6b": ["fakenamegenerator.com.", 0], + "875ec120f87498e9d0ce70bf61d9f07ab32d3e5e2fcd6b7f4ef635f738ed0ba2": ["taxguru.in.", 0], + "87660233ce903f61d6ac12ce18ea65eb433d4d47631542bc6face0ddb3bc5d8c": ["viptube.com.", 0], + "87678d039412aab63b873e1d54a9e1cc09bb9a24347f58163746de58e93149cc": ["radiopaedia.org.", 0], + "87701a1b63828f35b4074aa7529569eb2f71a8b0801d2210be77c0c5b3fb607b": ["rabota.ua.", 0], + "87794cbacb1ba84126edf7124fd58696f765d4451422e7fd48e96faa5f404c0c": ["flexjobs.com.", 0], + "877b51786732934e24cf56a2359ef9de014301493e540372c2051900e0330269": ["dedeman.ro.", 0], + "87808d9c40165036dea984a6571559383cdfd9acfa74236cf626d36af5932579": ["netapp.com.", 0], + "8780aa7f8085d960b69c088dac7e960585698639ea5c4912aa4e93c076857d06": ["siren24.com.", 0], + "8784893962a4c9edbc4da8767578b4b85ca459ebf655c199452227ce8d7dbfba": ["netease.com.", 0], + "87863b8b9fc654c8de1c4b63c847064af33c7fbf00895b23efaedf0c9e18a186": ["demotywatory.pl.", 0], + "878cb3fec1027690ce7f2dc3138055a9a4d5df31b2ea718d750b9c6c16e7d60d": ["technews.tw.", 0], + "878ef6bf0dd5c0b7f258c9d95d07fd70a5862f7f39b8852f5606159c16c9621c": ["kbcard.com.", 0], + "878f4b4f35b8b4ae4c9be4505b321efdadbedd3df29e8da65d8967fb53388774": ["meishichina.com.", 0], + "8792fd5ce01eb822d71e790e9b29e0fce564da42c2dcacf71d4ca9328cf87348": ["fido.", 1], + "87931bff2c7edf426ad5ea1e3c488d7a9108b1be2842ff079b7db6683140ffd2": ["nightbot.tv.", 0], + "87975e97203897a38512e6de74fa8d020c1bd18270a68cc0b8aa76b449196f84": ["jiumodiary.com.", 0], + "879c62b194b7378a3d1722013f44cf19ed04e6c70cb7bb72fae8c9d7e0253fa5": ["androidauthority.com.", 0], + "879d70a2348c52dcf2479c12a0942e552f196c82d99f76918f4fc35a4466dd5f": ["umeng.com.", 0], + "879fd1ca2fd5bc9aa6d4c719a883d88c87e0accd22ed7d2ba604a05bfe4f944f": ["lastminute.com.", 0], + "87a3a94a7e8f283ad14b907bb942638c5a8128b96efc04d66d975b87d4333413": ["finalfantasyxiv.com.", 0], + "87a460995bc23f12113cb9a85500ad900b185c83bc4d4a3b1454a3fe80e4ef08": ["minkch.com.", 0], + "87abfed1518124994f541d06becc348fa207a9e7299d2a5013febd911dffcebd": ["bkstr.com.", 0], + "87b6b5186548140edc913994519875bf6cab3dc8bda8abc0d23bc1fb5b391b54": ["xfyun.cn.", 0], + "87b7b3de8e13961dc270422bf6dcdf43aaa12c01bad09a8bd2191fa266ac95b2": ["ganjoor.net.", 0], + "87b9124541920ae00a7c1f8746b1fd130d0c86d2564795225013f57469920ce5": ["dzone.com.", 0], + "87bb2948300256c1f5162345234009d9ce1008f65375e6e05cb87f9f303221ca": ["1177.se.", 0], + "87c2e991847e0e50ad2a406cc65001dfe32f5742f863b5a094c472af752f37b7": ["buyee.jp.", 0], + "87cc19d4d69e73cb158039b0d493f41a3410b2a24d508267851534336457d891": ["winline.ru.", 0], + "87e8d7f97cde980b996d0815a7ae4532e3aba539dae5d892b41276c3151316fe": ["chengdu.gov.cn.", 0], + "87e9e3017218d003fb6c9f3906c88970d595e2d4ffdf023585fad4e6f9676f9c": ["icc-cricket.com.", 0], + "87e9eac25c22730640d24205cbbb0b5329e6dce03c033fc716f3ce90d80cb779": ["sabacloud.com.", 0], + "87ed142e2a7676b80c2a58838b1bbac0f549b946af5939deebda3c71b6fd0b37": ["okstate.edu.", 0], + "87ef75c7e5aa3128450db34aea626928534c4a6e5050077c49548a280f2daf29": ["2265.com.", 0], + "87f1ecd534aa136784f4e9ab995cae197123762caf4b39ca92e440b4239877ca": ["yicai.com.", 0], + "881dbb02488f091c6302c6d519440a050a386310a7b4e692e3c59abc15c7d174": ["buhonline.ru.", 0], + "882399c9049f2d88ae61c3ad30fc8bdc0ee544c2d1604e68a0db911c35ebfc0d": ["betking.com.", 0], + "882762f9ff90cd156c7d0766c5822e594e516d3f45d9ce92bae6a7b311f7139d": ["posb.com.sg.", 0], + "882ca0cc851fe790ef9fc328809d2977dbb35ad5b85017ccb78d866c131843e1": ["audio.", 1], + "88316d9e6759898ec175055b2ad818c8f2b9bf8a1fd4126e9ff62d7549d11eab": ["diretta.it.", 0], + "8835aa93346ca95360ce5de093cc86559a3d648eb8f5bc8388fc76f5a7b0a676": ["nicovideo.jp.", 0], + "8842116abb1ab958866fdb597947d066ce4d18a258f35598f1f293857bf6e699": ["genesisedu.net.", 0], + "88421e511eb19a73bc9c866ed6031492f29213e9e926d5ff7d1e795584b79fec": ["kyoto.", 1], + "8843e6ab97404a9b51d984b89e632db12046a3e7d32d1b6eb8d75d6b28bc1dde": ["truecaller.com.", 0], + "88466370900f6eb6181bfce6d87945586f5c08eb5f4fcdfb0ccb1fc7a4b073ba": ["namnak.com.", 0], + "88521368840c82c94a04da8ccd844a53256ef403e0f069eb6f688aa185fade0c": ["toutiao.com.", 0], + "88593a52fdfdcc5d8595389897256240e197049ca55acd841966d5bc5d8ff54d": ["fragrantica.com.", 0], + "885a4d2b43c3c5853265e59084458f184c49e79463bfa4ba4dbdc5eeef98b6eb": ["immobiliare.it.", 0], + "8860dac9fe0944e52355df938ada1cac90c1f24242e05d91c2896950a1bbdb78": ["theepochtimes.com.", 0], + "8864849a7fc6cf0ab5716e94ee6791db67574c0a196c8ec868c7d8373d1ca0ef": ["acfun.cn.", 0], + "8864dd4b8e40054102974d2362126112c97cb59180f4c1be648c0f722a4734d2": ["geeksforgeeks.org.", 0], + "886629689a6cfcec29d51b970f7b7ff5751edbc0a44f9c1e771398d42acb6df1": ["kiwi.", 1], + "8867cd3f6afc2aea9902af09f0f866deccc27ec7e55e11a04bfe7c13b67e6115": ["tirol.", 1], + "88684409e5c59b01433c9de2c74c4ba775a6eb3d17ca6ecc15af8bb489f3cd3d": ["hackerearth.com.", 0], + "886bb12fca546b24f8ef9c6f59c3039534968aa25fa1cf413e271001debf715d": ["roblox.com.", 0], + "886fbfe47cd82772043759d536cd083177868316f98b156b5fc501d8c4d08985": ["rozblog.com.", 0], + "8872495fceee7d6cbb0250819c3b5d8a7cda0a604e4cdf45b451133dd4c7ec97": ["lavoz.com.ar.", 0], + "88804843c0375dec6fc59147dd1c5e93a02accb38db8aa65c233bff7d55c2d82": ["agirc-arrco.fr.", 0], + "8885f16dedc0794b11fe80a5b09164006d8ae47f61cdfe4de50d9707a63b63f1": ["sce.com.", 0], + "88871461851a2a8a958312fa4722888c0de842236e54b82970e0ba578bf7ce64": ["katmoviehd.cat.", 0], + "8896564b2f5b5bc5cae07246f433a95ef9132abe711a884f96a7ac830ec0ec1e": ["omanair.com.", 0], + "8896e09d50a70b148ec18f9505cf6dec78ad643cae257b35369776d25e760384": ["shoplineapp.com.", 0], + "88978646099045df6e0b78bdc6ac1064df7485e4bebd52b8832cd0f8a35c1a14": ["madhyamam.com.", 0], + "88a5ae059fd773f7a7fea9c1567d03aa79c604f40c1e180e7f45415edfc8e55a": ["logonvalidation.net.", 0], + "88a9ee56b606ecf26ac8e3f05140c73eabc82f52d982bd70590c8014d2838cde": ["tsetmc.com.", 0], + "88ad36b40f805c1f19311c9385b93e7cc313aeb3df180eb1223e84e96c1618b3": ["openrice.com.", 0], + "88afce2e8564f45a923ce6f6cedd9110f1e395ca9c7afe90778043c8d889a559": ["onlinedown.net.", 0], + "88b0736f488bf91b007039f408d6f2968c8b045fe5df93a26eee5c5a578026c9": ["myfin.by.", 0], + "88ba2735db66f8aed67cceb7ca08ec5119a935d6b20aa0b569129ae691d3b23c": ["ulta.com.", 0], + "88baa48ea0d2851c21d87635dae0b9856a21f27a4387206c2bb807b148fa9975": ["n2ch.net.", 0], + "88bbc14bfb47d90f990214f262391735b29d2cd4a5a9cd58d06121b06a744b85": ["mskobr.ru.", 0], + "88bec54d163089bed76ab611c06022b7f17cb6d8dad3932a9ab95930382ac187": ["manganelo.com.", 0], + "88c332cbd36d3db1d75a45a8b852270a730cb8339db7269d4f5b5dfcd2944b60": ["cntd.ru.", 0], + "88cec1907c6fd011651931f400c36f6fa192caf3c67331b86d7e7d04d5e32483": ["jcyl.es.", 0], + "88d0111bdc32eadea882045c25bc42ad9d4a64022506eb0eba523ab62bbac5bf": ["yesky.com.", 0], + "88dac50c3c432867a8c2b769b0527e167a4585ffb4f7cc4b172a8290a46a73c1": ["etsystatic.com.", 0], + "88df2078d14c066c7a7d202b6bdb64d51028372174ac3caaab6b78a2ab2aaf8a": ["vlxx.day.", 0], + "88e09335886bb3046b930bc73723036cd97155d1a0304e3269d008194eb060d6": ["listal.com.", 0], + "88e79df36cabc0535598702fd0865e6f591e8381285c79d8d530e6c41a97cf7f": ["visme.co.", 0], + "88f349ade0f9f03cd6fa7c229e36fa0ed94a05b67837a92b57b13be1d8718588": ["hasznaltauto.hu.", 0], + "88f8502ca7f8abe097d03cb8cf5adb23de7c50ba1a9b21fdc974cb3487cf9ba7": ["seg-social.gob.es.", 0], + "88fb1deb874685decd9e1cca87c2656578e581ecf0d861957a4257bc11a901f7": ["stjohns.edu.", 0], + "88fbac2c8a12c93030f12bd125ad755458846b2f67219cb09cb779ee4ebdeff6": ["passeidireto.com.", 0], + "89031e8dc62cf714897c55f0e7737d91a7f68367dd83064b62fd6b2e5661d090": ["phone.", 1], + "89095066c5e6dc502d41e0435cf4bc2945da816954b7d43683a50ab49013a622": ["popsci.com.", 0], + "890a6bb0ad0aa2b35ee9ba1aab6418a3f428acb75bf355b111bebe510220102f": ["gn.", 1], + "891cd6488e9a27fee1abf1f370bb27ce9d31781978e2a8ec0a5a57435ccfef67": ["softzone.es.", 0], + "8921a8132d80a17ea37b3e8fe588bbe349f09beb102344ef59e41e8a3260ce5b": ["biblehub.com.", 0], + "89287c4f8865d576e81f20576fd5da91ecfa850c07067bbb264610e803f2c2d2": ["monex.co.jp.", 0], + "892f98c0e4b230b6365c3c445ebe73f07576fbccbe34d83b3dfd38aac7859a18": ["porno-island.site.", 0], + "89344350ab3145aaa0ea048daa9cf37b0986cb345c1c809d9a35a0775317d683": ["pbase.com.", 0], + "893d9e56ef5222346322b650703c4467ca32b0af6322b372a7c82cf85a8293d4": ["go2cloud.org.", 0], + "8940159ea8ce30e128294933921ce61357c145bfbe4db682fca82967bf6d2d41": ["cgv.co.kr.", 0], + "89404fb7ac4aabb99784a4b6e3b01741e0285ce15158a03bc64e8422cb5169fe": ["camp.", 1], + "89449fb5554d980c48835daa89506b1520a5c53c4c95c7c4bbcdf29f2cc03b99": ["coschedule.com.", 0], + "894aae6705a1660120951c24cdc312bcc449c7bab683a710b5858bd0fe2b72dc": ["lendingtree.com.", 0], + "894dd10160c2dd34ad06f168f7323e2344d4fa27b053594ea3a7853cd3228bda": ["zm.", 1], + "894f118dd6d36857ca275596dcb483d9fb6e6504f21414d943afe0c78a8b67d9": ["pingan.com.", 0], + "895704780a72a2c30190019233c5739185bd5f0ece4234909c528d8bd6f7d97d": ["camcaps.to.", 0], + "8957730ce542961b7a573a6cf925a55bab83ae2ba6ecdaf29bea507cb5b5e19b": ["xfreehd.com.", 0], + "895b3cd17df05199e2cfab3edb0080246029d5dc7836710dc164e39fa4a211ba": ["uzone.id.", 0], + "895bc69e271b0f7e92b4707f3e062433f0bb29bf8a077e49f0695c0ec3e1d5a0": ["soundcloud.com.", 0], + "89607f39bf1dbd0ab1cb152f9fc3c8c7345a315153aae126abe5ad562e95a7c8": ["gradescope.com.", 0], + "8963f2ec3340a17f2bb6b88faf65d800964b7ad27b838e8b80631730f13b7b4e": ["jimdo.com.", 0], + "8968d878ee2f565121e3f4f93e17f596d3b31265ad3b49a46c3fa8b0a8768d55": ["ifm.", 1], + "896b8908adb96527482330f20657310ce3f7f759013c17fbd87d9990633c8c8a": ["mg.", 1], + "89718ddf0cbae0f362f2615a03ae8745bcc51d2138c86f8b0c992ec0b6f3487d": ["odisha.gov.in.", 0], + "8979b61e59e0bdc00f0992ce18f43588691affd52d804ef2f531eb49a03743c3": ["racing.", 1], + "897cae45ba91205fe2250102726bac2dc0c48d4785e034b3f3a8e99b3b68f539": ["pcmax.jp.", 0], + "89880253ddd598650fa41f961370bc72a87c7bab2995453cb0645c0a421bb378": ["gtloli.gay.", 0], + "899202d7a09cf90cca5cbab6031ec88cb277f5a9fbdf25644db988e8e50f969a": ["telegraphindia.com.", 0], + "8994824865bd94b899a044acd512c06d176c12d9ef688b65850110571d43fccf": ["suzhou.gov.cn.", 0], + "89985cf73db68e5cb1c63a4934d9d37512f92629a29e7292c44730756391f407": ["gle.", 1], + "899cb52ca123dcfb62e7628fa90a7ac8be9f58c1d48c9c2363fc3917cd481592": ["resetera.com.", 0], + "899ede5e3a5e2e087d7764d8d0f53d922acbc178d299b2f789591bb2d0557825": ["omnicalculator.com.", 0], + "89ac25c4562d507d1f70ab3835e9daaf556fcb4b8d0e850c39d9d2bb07c94894": ["lamborghini.", 1], + "89b1665e6fdddf98f0873ea4b2be1ace263fcb3bb1cb235d464098fd9c4bb321": ["jakarta.go.id.", 0], + "89c2f92e3e114f1dca7077b9e4338f752d9cd7f012cf9f57b076a7d51ef995b6": ["acefile.co.", 0], + "89c42bf6eb94a82707baab46cf27652e26f6164831c7d9212a04761d44d3c6bf": ["re.", 1], + "89c4d1a68e43b97f659ccb7d302137d6282e46992d42ae6fe837fa1ad8f78367": ["zettai-ero.com.", 0], + "89c5de31fdbd6434c35d855e3a260ea4953e500a31939fb172bbfabec5c882df": ["5ch.net.", 0], + "89cbb8f1c9fa7b5c10c6976f65dcea4fbc0268db7c50b2fc84c2abb7758ef8ab": ["txxx.com.", 0], + "89d1b1b76ded02e7f7d9361ee2c852be7f7e73bd5881f12a344dc9ec5545acc9": ["hapitas.jp.", 0], + "89d3d7827c5063d92ef72e0b27ec5162fdb68531d80f6b0af013271c55d5a8ee": ["rakuten-sec.co.jp.", 0], + "89dbbde04bf643971d2d8bed533095f77e37c78aed4a9e27708f8d48829e8e9c": ["huffingtonpost.fr.", 0], + "89df42446970014394312bd4a735ae0b0276239d8867e2167b8460ed2ff7c247": ["gg.", 1], + "89e84115576228f88efc0aaa43ba72aa866b7e23fd626ce3b29a11e3633dede7": ["yle.fi.", 0], + "89f899ee21fd69ede52eb800959bf1966c11b339ed0b0fce8ee60d7cc7b78f0a": ["ftms.com.cn.", 0], + "8a019525bac52cbe1702576163acc2cfa26922140e5032945cfe8bee03943540": ["gapfactory.com.", 0], + "8a04eb73265a4ed72fdc486a9f715933a7b70c90c0aae927b76ba744b8c0a1de": ["indiainfoline.com.", 0], + "8a06268afd24e02f7f9f89089671f047cf5670df6674b7f11937b05b9b1f7644": ["masutabe.info.", 0], + "8a0b0af3ecdc6533953c7efc68f90c0e07500d1146c910bf00575f598d6de171": ["gamepressure.com.", 0], + "8a0c8fb256ab3c60200cada48405e535278ec17c533cd18cc78ddb87d86aa250": ["zoopla.co.uk.", 0], + "8a1a3a8c944c899fe078d8e7ace9721ebac672b0ce52bfc1837ea2ccc7c501dd": ["mv.", 1], + "8a25eb92a1a430679c6d7a1ad63e01b6bc4e1012904b110cb767814e0e9f4d56": ["gta5-mods.com.", 0], + "8a2a9cf31f7b496c18e86b1796ed7d7d7cddee73a5ae7cf7312d1f3b166f77fb": ["nodejs.org.", 0], + "8a2cdf1bd443c26107fdd0bf3bf50d8f443d1f7792947f94eb1328842e5f3392": ["eforms.com.", 0], + "8a2fe1f7ac5f1b19f3274dc2155a5027ea32f384550ba7783163e2a85376b4b9": ["deloitte.", 1], + "8a398cd7c5d1827d9d20b7e5b3c34826054c44e9f7e8ba0993f9ae463abff9e1": ["ml.", 1], + "8a3a8b0036aaea972081c69768c7529346f4c9915efd16092981460a53fad27a": ["sport1.de.", 0], + "8a3e7cf439ae3a94f48d0c1a592f21747c330c11ed8da2c1a82dc19e88deb4ac": ["researchgate.net.", 0], + "8a40bdb3aaf28bae1e0b32937664daacea9568ab29835cde47002f2a44ef09a6": ["10x10.co.kr.", 0], + "8a522ab9801517c1d393a9b172ed21ccc2d3814c050fbf81b9b735f1f4e1de08": ["141jav.com.", 0], + "8a6890fde47790e4b4af7eacfccea31b8924a9a53164cc9f794193bf5ae47aa7": ["crehana.com.", 0], + "8a6d5daa2aa719c09d2ac79c60541423c29a3c25862bc69fd7108a1c209cbfb1": ["libcal.com.", 0], + "8a7e49dfa797e65c937d0be6e2d58d0c0eaf995b614483494cc610f58ebc4ea6": ["ithome.com.", 0], + "8a84a167bb01469fb1d418ee45bc7cae320a5acaf51c14d1375408d47295e289": ["forextime.com.", 0], + "8a86d2ab1d8ddf2794e63225f62f573fc1dbf26c4b9f0576936b29d047b1eeb5": ["inmuebles24.com.", 0], + "8a8d9991c60e025a86fde49287c634c11e4c4423c702fda9734ec30ed1d36165": ["naughtycdn.com.", 0], + "8a8e4f8bde55544495e4466d389c102f09b55e3c34e7087575a388c0762e4a98": ["saipacorp.com.", 0], + "8a945dab6a63ae23c5763549c547912c54245063264c5f568c55ffaef60de343": ["koodomobile.com.", 0], + "8a983b0109e41f7d361b4e5e989c7a4dbf18839270f72829d2c880d685e8898d": ["credit-agricole.fr.", 0], + "8a9fb1702c3224130a069e70fe9b3138fcbb3a12b85296bfc684428e79a0fc9d": ["gulte.com.", 0], + "8aa150d9ccd328495e9892e3fc298af408558f252f579ca149fb65e4f79cbae2": ["cntv.cn.", 0], + "8aaa17ddf8cd17e6b3e4706a8dd538fa8778c5b6f600bd29f65938f93649caad": ["emiratesnbd.com.", 0], + "8aaaa0ab7a17f3856e4d30b195a85a4f7c969594926f1bb7a1db778b9b1938ef": ["ftchinese.com.", 0], + "8aab2fdbc4dd23ab196891169fdd9c3516e4bd6fb1a2f2673361434f829efe03": ["ziprecruiter.com.", 0], + "8aac9722a3e464117fd231c527d8b8a031bd2dd73a79fe520b852794dcb078d8": ["gd.", 1], + "8aad7684e53491e31fc7534cf2b8c1bb0b955aa7ea77f80c206a95616ec06790": ["360buyimg.com.", 0], + "8acc72e81fea2f29beb8aa7b8952875c8ea83fcf3267085aec52a59f87c366b0": ["gamepress.gg.", 0], + "8ad058a53ddbe964a022fe64e4b003dc40c62a336e7dd61fb3fefa72324f098a": ["canadiantire.ca.", 0], + "8ad08626d0c8910cd1222be1588ee5bb7a032916569d93df7223b68ac11f4020": ["crowdworks.jp.", 0], + "8ad09658b368468db170e48d63deb98a17bec0c0752f1a64b42490e51fbdb0f0": ["seek.", 1], + "8ad641498cc9cefa244852690460643600d77029274f1f1fd741777780826ae5": ["fi.", 1], + "8ae98a866145f0010ab4652b2e35db0b59a6703b7e8d37ad6772dd55b02188c4": ["rabobank.nl.", 0], + "8ae98f4073398cfa706e3f79e3be1f824da5d6cf4e061184f41f65b823ccdfdd": ["gostream.pro.", 0], + "8af13d9244618eee876d0431f3449aa4ff95274ca3e7e5c6541979499f5b85de": ["hola.com.", 0], + "8af4d0778fa6b9bb3dae3cd83fba1b13fd517ce9d520a74c6a78e1d4de441095": ["swatch.", 1], + "8af4db49369f2dbfd3ea587be6aa33c3fb71a364ee9b475d0b299ff77bdd161b": ["sanjesh.org.", 0], + "8b0e677b54ea474a205d7627f370c3eeff60d54426823eeccb5bb7f170414304": ["kidshealth.org.", 0], + "8b185fcfaf0c8b8f53ed5f91f04a6e92c1ce4cb34913dafa715dc4689f2bd14f": ["honda.", 1], + "8b1c8c018c97800a1548e040ed376820cb38170620ddfee95dafc0e13ff8cf48": ["himado.in.", 0], + "8b25f46dbaefcf7c577697fb8ccf8d6cebf1f4c5ec0d063fcde6f9a799295df7": ["citi.", 1], + "8b29e7f2df532e641f428e986c86794be642ff8f983029176e67cca06a4eb769": ["babylist.com.", 0], + "8b2ad88edd57d071fc5215f497f84b34cf76024dc3e7de73b6b64a99cac80f79": ["55188.com.", 0], + "8b2d82eac076221e1b214580884c11d602b05fe13636e87aa8d0ef546049d238": ["khabarvarzeshi.com.", 0], + "8b396cb28ee5eddbd20d9b7445176f09ffad1f0200546bbc7def05daba2de9c1": ["pch.com.", 0], + "8b39f07a178696d06cf41f9aba1f365822d6dc30a81451670df51777b6cd9e68": ["filipinocupid.com.", 0], + "8b3c7fab8a1bc19402d2c04350eb9f2ff4e591462350a2794d48148defa161c6": ["cr173.com.", 0], + "8b3e56cd64e1a0aba004ee91b836f25c3c53c236e170d2603dd783b8c88c2786": ["fshare.vn.", 0], + "8b3f3f73dd6d1cc3da54c6465a0268f87c400e6267200d3a670d896e4dd60bee": ["xerotica.com.", 0], + "8b4425a440b3f807e1eef42062f11e63519ac7d22ca315af13346d9ab41846e8": ["g2a.com.", 0], + "8b5175e8f17ff9448ef534e6040a82ebe0db9002a5798a75fbd6fd9318a043e1": ["blackmagicdesign.com.", 0], + "8b518320c911fb93f1f8a639f41b5fea2aba0cb3cf060a00290679b34c1f841b": ["intercambiosvirtuales.org.", 0], + "8b5217f6e486f7273f113e320a085dae8006cede1f2a8c41f764e942738d1bf2": ["bunshun.jp.", 0], + "8b587ed323f30bcb040339c44e621c881d38d2baceccc1ad31dc0c52e3117e46": ["silverlock.org.", 0], + "8b61d46d8c8ae0d48d9f1eac89033748797e7623209414c13fb262fb1ea7763b": ["proporn.com.", 0], + "8b73357592706831d07673f0f0e9d5cda198043cdc0ae50c47d017859b27074c": ["bbcgoodfood.com.", 0], + "8b779ee454dc32ab4f8bf7714bb5803eea6a7f5be1317e0c3b59eb3fca2ab3bd": ["here.", 1], + "8b7844a1ff98038984e7840676f3ca3a70bd2b51844a8b94c346ec5efd74a509": ["circleci.com.", 0], + "8b7dd1102c3cfe5f8ff9e52c52f5319ef45fbd97698bf8506386770ca8fbca45": ["mentimeter.com.", 0], + "8b8208e1c2d5ce90e80a636706f108c8cab2ec798612662070c817e552bb2615": ["funimation.com.", 0], + "8b8a5dd6a76293effa56cc020ee69c810fb9810559ddfd4cfb43dfe4fe1aff41": ["tv2.dk.", 0], + "8b94b772a7614c656ed233285852ce95f5ee20b04d3183bac18e27ca39fb97ed": ["medsci.cn.", 0], + "8b9c6cf988d2bf38958d2a0ed7ffb66b2a98c29c143a7e050434d013483d7a99": ["microsoftstore.com.cn.", 0], + "8ba006805b7bf9cc9d388247f564ad29ad6de3569c5b2528d715d32c5d9ae692": ["broward.edu.", 0], + "8ba41ae3efbab57d74c163e67328376fba808d51834b2efdcb2a5cecd58a2e87": ["bharti.", 1], + "8baf19a85577178a70337ed32e8a9c605e36cdae58985eb80043050f88e79560": ["uwaterloo.ca.", 0], + "8bb5bf38daa657bf71d7fc52df8bb32f19b0f838f43ee0d2faaeb0a847ab89ed": ["wordplays.com.", 0], + "8bb8b3d43775ccf5ce6e901160780f78a828a48047f16bb4fe8b975ed880cf3d": ["gaymaletube.com.", 0], + "8bbb1eabaf843ebc5d31e211ed5682ebf45611090c1bd54d4754610ef6d5c0cd": ["vpay.co.kr.", 0], + "8bbc02894f71723fd992fc1264fab20c0b91b90f3079d2c3b1caae0eda623d44": ["hotpornfile.org.", 0], + "8bc270684242a09d2a8e79f8dc3a663855eb0ac0ca9aa231fd2a793942ca07f8": ["flixbus.com.", 0], + "8bc2e6ca2e4c5cb3b6952c8eaf05517a282567b2dbce8e2a39152b8d7eee6fef": ["winaero.com.", 0], + "8bc544972bc0b7fd3975fa72586b4b117c505727fcc1e383052703961abfb604": ["padlet.com.", 0], + "8bc826e2b50ea5b572763b25882aad723f8dea7021de5a82614a35d6351302f6": ["cuni.cz.", 0], + "8bcac8378eddc8db5ccfbb373d7a66e3ac711ab8e98e23741127d6ff858766af": ["mollie.com.", 0], + "8bd271afbe601730ed3ef5ebde0023ebcade7ba64d0b6042b5755ef9bb3bcd56": ["bazos.sk.", 0], + "8bda8f89696c75f51237bddfc7feadd02e15aeb425bd3f7d60d8e82a182a3619": ["arvancloud.ir.", 0], + "8be4a8bbfcf3c7c62bd523590666d9f7bb154bdcb99d088320e203174c90a4e2": ["financial-net.com.", 0], + "8be6bd03f5250b295aaecb701599794c1c5f7aa4d5a9dd3b8e771fe0140acb63": ["meijutt.tv.", 0], + "8be9bfc270d9610bc4b1a4c7ba49743eaa7c1c55f7c7f07a9fb6f4a08c9c2bbe": ["mir24.tv.", 0], + "8beb172f5b2547ad117f688bfac8d34cbfb084c08dd02aef56594c8dd3a1c8fe": ["pmi.org.", 0], + "8bebc02c42806a0a236f6f0378a65025a6724452507d4cf3ebdfe2b9c79468d7": ["ucas.ac.cn.", 0], + "8bf3fbba8f35dec283de80a4749134d46d5f8e50d78a1894abd93c25e350f7f1": ["cmbc.com.cn.", 0], + "8bf74e2e59835d920eabadc13fa70c572aabc3f5fb589bd99dee0247355945fc": ["vsco.co.", 0], + "8c12537ae256b27cbdcdabe66934c7bf198a6cd34dde853ba94cbe0c5bd692c5": ["xclient.info.", 0], + "8c12e8e7e26cdd66148ddf222b9c7fe4addaa37a2757fd0c227ac0de1dcef3ca": ["wenku8.net.", 0], + "8c1420df9ff67385a851f1ed3646fa1ac3034db66505e84d18f732bb7b829fe8": ["kapitalbank.az.", 0], + "8c187f2a3ae8cfca14f74fe91e4df19fc473490a928c9b9fe07c6265b516652b": ["bing.", 1], + "8c1f524205612f55c39b1f96ca6ebd07262b0b3fd3b25bc90d7b8694024524b9": ["vmware.com.", 0], + "8c2236cf7b9a47318ffab47eb87d2131ba038526d181275f86e57db77ba6797f": ["xn--ygbi2ammx.", 1], + "8c2915a9be468a32f1d602971c9bc30b727cc9634cccabd4f1d8c35178caeef1": ["doki8.com.", 0], + "8c322f942ec915fdaafdfb03a940ff52804b3e97eb8d39eeaa04eabe088801b0": ["subdivx.com.", 0], + "8c403036a52c52275a6595b33b010dfa62fafa5bef6ada2dafca31f557779dda": ["videocardz.com.", 0], + "8c48528c9109eb7f063ce5b6e2082068aeda0d9da4da0235c11614b11046b15a": ["kinokong.pro.", 0], + "8c4c7d288a308216be50ecf7fc6b361fac86b68e2513b3316fb9456d4827df39": ["liebiao.com.", 0], + "8c548627676c10fe39730cbcad4ecafea387485637e8a953ba6dc917f374a513": ["ruidaedu.com.", 0], + "8c563cf59fb1de76bcedc707331a77f355e4e9f7200f2c708326c1cc432776a7": ["kongregate.com.", 0], + "8c58ed6fd2530389cb87b2ee41aa9e486f47699c12e5424ef10bfff0b2971b75": ["proxmox.com.", 0], + "8c596a1a6ef06ec252831a7d89d68ec489b5f0d6c37d9e2fa728b1c941d44b28": ["uclouvain.be.", 0], + "8c6044a4e3ad4b1d740a17840943d17712dff7e33c5809d591070870a9737ff1": ["leigod.com.", 0], + "8c642bf8c6a497a40a049c8b36359a4abee7d8048bb5ec96a3f79063aa7deefe": ["sabq.org.", 0], + "8c675be1dcc424ceaf4e949137198c654543a6d9d0c05ca8954e83ccac8f5d76": ["guhai.com.cn.", 0], + "8c74a5ddad95cca174cc39dc6d5e796788460a40588b6bc060465551c49f6ee0": ["onedayonly.co.za.", 0], + "8c78c22143e287a8959edc8be0c9f7e9151601d3695e691b35b49c61e8a29940": ["sumup.com.", 0], + "8c7b085320bb40bbb08c482d071da9bde12896aa5fe79d4094e705820a2d549d": ["swin.edu.au.", 0], + "8c8e0c444e11a1e77c9df56c3de0f8d5cf0b0aaf3428f053a83afeadc7a5fda9": ["zippia.com.", 0], + "8c96a328100acfc0e7ff0926652733b3df5ccd9792abf8684c36caf2366dc999": ["liverpoolecho.co.uk.", 0], + "8c9bfb72f68d3f6aa52b3c8b69ae33446869a43ca4f253a13ea4e7e4771afa8f": ["a16z.com.", 0], + "8ca52e53b2d7f8822415a394e4a0b6c8ff9a8719a6ecfe34b7df031eb64fb028": ["reforma.com.", 0], + "8cadc1b61f76d9e4a2d2a6d350072af2b449ecf17705e9f60fc5cf1f313aefd7": ["txstate.edu.", 0], + "8cb4c151f9237cfd4f47f9e18a4081ddad65cbdd7266cdca394f0620bb1e9a9d": ["porntube.com.", 0], + "8cb70845c5ef2a12c9af1beb92fef15b815c1cd179599f47f51cbde7bee0a4a6": ["culturacolectiva.com.", 0], + "8cb9b10bfcc934e0907fb7b17178959319f0d29887bce65b4778f3b6abac34ac": ["dealmoon.com.", 0], + "8cba22cee77c75e2458b60c3c9c1024fc10282fe8fc5f189feebc9dee7d7fd06": ["steampowered.com.", 0], + "8cbec30d79509409006b35da8541bb8f7199944fcb89bc60d4ca70ff33c78385": ["adnxs.com.", 0], + "8cc49efdc3afeaa6735cb7e985b6640bf1a197660e1745be40eeaa31c7bbc0a5": ["flyadeal.com.", 0], + "8cc589ee80d8b4e58b4f47f4e5dcf734937010fe7a74ff06238060d91a890d31": ["se.", 1], + "8cc693c6d9340bf5589fad673babd4167dad0544516a8b3893ba94fae73fbc1a": ["mcmod.cn.", 0], + "8cd29c7ae09670b03bd0494b57ed63b88ab37bc59da0401b8ea61e7b6681a58f": ["gaoxiaojob.com.", 0], + "8cd7ada0a67f6ae3d11f6141f9cdc7a25e7041fba426b9a736f058c62ca9c7d8": ["xn--30rr7y.", 1], + "8cd8f42955772624900d59ede90a5afea26043aa6d7a7618ba3d08bd061fa4f2": ["sspai.com.", 0], + "8ce549c8bd1c2c32c65638fcef09438df1bfd445744034b95006c95ec71006b7": ["china-railway.com.cn.", 0], + "8ce702a386281919c2e860a44e942e7a465fdcf24a20a1a3f898714d68d8f741": ["freenode.net.", 0], + "8cfa83e35c4620e1448923b71879aa32ff095d5cc67c74e14e3d6f18db461fdf": ["reuters.com.", 0], + "8cfc771fbe4ce83fc98dec8e259dc98bc5281c6e85a1e6e65441127b18c60fb0": ["iranserver.com.", 0], + "8cfdd984ace69fa0a95fc979cd3324342ab16d4025ece7d4904683c27f54157c": ["rule34.xxx.", 0], + "8d0470bd88ac46488a90e5e9a995563d92a6dc5fe1d5b88f6bb2b66f786c8c28": ["tpsl-india.in.", 0], + "8d0688f8b8f5ad82eb57f9349a91e58354100ba2ab3fc349a3d29e74519997b9": ["tophere.cn.", 0], + "8d069cefacd1c23948d9b455b32a74addb6d9a90e21911292395307e54a72d9f": ["teachtci.com.", 0], + "8d14320099659e2e4fcb329de49dd22de77ae5fc2a5ca78873741fda1634b99a": ["sagawa-exp.co.jp.", 0], + "8d2aef6937e90c29750ab3cbee542c2f860a433e5f99fd1fb4d0256c9ea03d42": ["pr0gramm.com.", 0], + "8d2e3c0c5ca7678306fea2f7f0b7677b3664de49acd4239b03c118bf7e76d7b5": ["wemakeprice.com.", 0], + "8d31b8b405c020e78e596e32ed670f384daded3f1a6ef9436dba00a6caabc2fd": ["esteri.it.", 0], + "8d34af9e2c0d5c4ae5c067354bfcbee9cf3ad7498c7fbfddfc74c7255b947aa4": ["ngwebsolutions.com.", 0], + "8d3bcbf44968212eb0540e3026aac7950cfd50f2c0d03bac47df09d8b7b3a9e1": ["khamsat.com.", 0], + "8d3d4dacef8fba2117a7258c7bf43f7068d0be3a6a2704653e94c3c037b0ceb1": ["guitars.", 1], + "8d4ce34a7d5abcd1e48f16d648f0864a6fdf29792e460bfc16296086027a788b": ["kyousoku.net.", 0], + "8d5833178143bd72bf19b4bff83afce63eec1a27dcbf5342e4f3b733bcff2774": ["yggtorrent.lol.", 0], + "8d5885e41caaf35e00d57640b375d04aa97bf29078d87b482fabbeb3ae6ebc24": ["imageban.ru.", 0], + "8d5ba968eb5f357c73ab5d4d523bae2db0d396095e43a1b0e9be13a08be5112d": ["infopanel.jp.", 0], + "8d61add072e62304b877dd894526245bb088e119fd5933a7278bd3d9b8c6abfc": ["electronicshub.org.", 0], + "8d641181f1c89b3fd97d4712deb85fe356bc9430bad2ecb1c6b2af7634f5d8e3": ["vix.com.", 0], + "8d654cff34741075c05679fabed856a4a3fee7655109041a41c3044818865d4d": ["sfda.gov.sa.", 0], + "8d65964ced6e9153fbe86f9bfe27afab31d91f2c1a2a2f8203ccf30576bf88d1": ["easemytrip.com.", 0], + "8d6a67c9ca275eb7f30a3c1b1874acd80007dc614e38798a831177e1693f5a9d": ["igp.com.", 0], + "8d701233a5d557a44679a4947ce8fe9a8fdc09f85a16aa8684555c625f4c45c2": ["wework.com.", 0], + "8d73a01bf7ba4720ffba1a29b0ec11d2435f43177ed3d6fdb13fe2deb04175e0": ["prosettings.net.", 0], + "8d79618591d50edf96ce905e4cd1aa5d74c2b3c7331cbc7a0f2ea9e7fa2d1da8": ["kaspi.kz.", 0], + "8d811c725c300f7eeeb109caf557e247a5e08377836e88b6b2a7b0a897c83490": ["brasil247.com.", 0], + "8d8a53819846d22324c365440512c27b66991f92463165186e37c9c6476a34d7": ["kugou.com.", 0], + "8d8bc65f81adeb520505ef5f0b4e88e1271df2d42c3896981e20056866287ae3": ["sephora.com.", 0], + "8d9172b84f04076d008923b2300839f61c099847b48e731d9f64225387fea4c3": ["worldofwarcraft.com.", 0], + "8d95f6ca54e2ac7829b2076ca27e211729fbe7e18a66767364663688d9114fcb": ["aade.gr.", 0], + "8d96876b0807dcf73480eb244699e8ad5e7abb06e33cbf051ffa523704566dcc": ["gayporno.fm.", 0], + "8d9751a43bdf891eb9823901fbe92d5476080f3bff9606e424f6ac47ed2ad81b": ["paloaltonetworks.com.", 0], + "8da35c3450b5b2545a9e32663ac0307a7c03240354cf02eeab04197a03637ccd": ["m-team.cc.", 0], + "8da3fde3ba22d52314a6987a7d7a04616f04bac588eb26f580ab6aed0cc31aea": ["sportbible.com.", 0], + "8da9f0d307413916a6382221730c712558fa047f83917c98c2e9467651f9d039": ["showclix.com.", 0], + "8db166af4719c16bbe3f35fc421cbe8ca64246cab492bb2706599f576ffbed21": ["quasarzone.com.", 0], + "8db9b8e6da28b26e33cbb5f663bc7eae9d008e0c77f6ea081b87fb2574e71f34": ["greatandhra.com.", 0], + "8dbdbf7e0f4576cc2c624f3e92773999668503005da1e84b26f5eb02bfd65682": ["ldblog.jp.", 0], + "8dc31c0eb0aea237441d5fe8dea3378059821fa103eafa435f261962de80a9a6": ["afrihost.com.", 0], + "8dca8250982b64dfc8c4d9e747cd73243754e40106a87ec8fadfcb725cbfd801": ["telemundo.com.", 0], + "8dcb78717427fca0f88a0417a046946716df0f182549e6ec41b89ba4f7f31261": ["utn.edu.ar.", 0], + "8dcf064fa283987d8dd5ca8ecf44987959956ef25e2b030d82cac4e7cdbd525a": ["republican.", 1], + "8de20d6e54be66c6f2f45150efdbc819f6f0dd9443c4c2265d909be5514387c2": ["securly.com.", 0], + "8de3d1100b1fb786d152c617ecda81f0c8ef9359403ec362c1bba1e29031bbc3": ["elluciancrmrecruit.com.", 0], + "8deac73cfcce5c62deeaa2ec1b5300619b5cf7126d5650dc7338fb4fd7d3c8c1": ["alo.rs.", 0], + "8df09170d13a979c8958fc0cab0ee94349e857049910e1fc9e891d3eb36a814f": ["windowscentral.com.", 0], + "8dfaa8982b337eb10528b0405fb0f54d1515f81ca4637d3e96b5208ec2a02b51": ["bankid.no.", 0], + "8e05b3a4e76e8dac723dc7e59140dc29c30d9821e01b7c45ee344f2a8882968e": ["xn--qxam.", 1], + "8e13b4373fe1bcf5d7adf303a7935fdcac6c595c662c77ab4a58b0d9461b843a": ["newsis.com.", 0], + "8e1adee9982fb1371fb3c1ad939ba606c5f6eb6805f63e6b0ddc916e67654004": ["rusprofile.ru.", 0], + "8e215245b8595878181eb78db47f3fae9af6c351c79adfff9fae1431ea40d81f": ["kaigai-matome.net.", 0], + "8e351c22dda7dd58095f0788deac0f503eecba2b0cee4220c4f250e86ff75d23": ["saxo.", 1], + "8e3be7d6d9e376a4ca79227a1d967380a853e55a694cf1eb8bb96765551fa0d4": ["iitk.ac.in.", 0], + "8e48254ddd6aa310a18e5375a541cdc5e18c7307584311217b66f93f0e54b5a2": ["blackhatworld.com.", 0], + "8e4cea2ad2e0a906801ee06ec51a462af08a8baed34d4798f8b1103b29786e31": ["rrk.ir.", 0], + "8e4d7bd87148a0d938b2e36907114f062471b96bc32799b00e64fd26784784bc": ["doximity.com.", 0], + "8e581931d6036fcb3609940d2ee722f355e09de68d755c93d4adbaae8961b34a": ["dgtle.com.", 0], + "8e5a445914791c21ea17aa073940aec6b45e4c9f8a823807c5f0d8393151f70a": ["dergipark.org.tr.", 0], + "8e6065fb068cc1ea9e493a29f53b429e9bdda44b15521bc2cfbaf757d6826dc3": ["greengeeks.com.", 0], + "8e6a99e5e270119de8ca34db1268d73d409361c98c2d23f06613c354ec55d66c": ["funktionstjanster.se.", 0], + "8e6f70be2f6949729bfb219bbd366df999760df6ba3874cd48295acd3b29719e": ["biedronka.pl.", 0], + "8e74224dc1495c80cd0d70f440927ca3793f8cc33d2b50b5db5e638825278beb": ["yok.gov.tr.", 0], + "8e82bde574936eb978efe11e1a740b2d7b0eb49b895cfa7b1f12376cbe9bcff4": ["irna.ir.", 0], + "8e85401f8277a0488bc90edcc912b115cba7a27cd164e8ed0d2e5f3f14c61e5c": ["aliexpress.com.", 0], + "8e9c7ff9d42f984e139f0e4328999d2254e81ccedaffb2df1c678b23662c36b8": ["xn--otu796d.", 1], + "8ea8a6aeb7bfae5b32f887d6d72abffda6aec8e331722dc053c6ff5ce08d71c4": ["dicio.com.br.", 0], + "8ea9e62fed1f29aae03e3b0ed839d11c1d754232496c710a5b14641e872b619f": ["jaxx.io.", 0], + "8eac7b943466a91eaea67dfc988a721f7b3f47ee21f3840822927460871e2263": ["princesspolly.com.", 0], + "8eb485c0ae37f59e91f80502a9e8ee484dce4c12590012158022b746bbfd185c": ["wikiquote.org.", 0], + "8eba9b62f1d7035db71930f426cd7e64c17505e6c32f783b623d6b96dd0aa2d8": ["pf.", 1], + "8ebc6a0adba37d068b2b6193164513828b8c6eaf1de9f53797af90bb0682fee0": ["scene-rls.net.", 0], + "8ec4354d56fdbc9b3368753ae8af5956286a3eebb3e694fb3b14925ac70d7112": ["soccerstand.com.", 0], + "8ec69745dcb8220201be935500af544244bdd88f6652188c14f91acf090aa833": ["gy.", 1], + "8ec97329300cfaa0e9028bcd73d4cfa77cc3a63ee5ba3b453df784cf949d8e11": ["dastelefonbuch.de.", 0], + "8ec978bbbee3f45a6f75ea700c8f644b05bd81d178ffea5bcb4d1820cb2d0861": ["nitrado.net.", 0], + "8ecc3bf59542d2f6604972bd5ab8ea3966ac6c62760b12a755eea6565d81bc1e": ["mangareader.to.", 0], + "8ed3f72abd1cbcb41202631d3daa51b096e97cd0c7cbd05200f6f1935d9ddbca": ["electrodepot.fr.", 0], + "8ed65dec4835a3b7e9e2f9ade49732e96f4e40e3c8053e046a0fb7f9ae059c7d": ["optumbank.com.", 0], + "8ed69f82477b9a06d9b390521f77b1ebad484c3ff6da52cc8a5b4c96a98cce88": ["trendlyne.com.", 0], + "8edb4e9d24ea33f257f7202d50482fea8bfe378407006b03ec9c9c56af38fee7": ["dongmanmanhua.cn.", 0], + "8edb7c47accfdd6c96a804ef5a986ab127ff430e37ef0a51ef6e58e4266f5d5f": ["the-ans.jp.", 0], + "8edc594200c770ba45b8355ab130444fbdcbf8db3630783a85468472475fd119": ["polimi.it.", 0], + "8ee331519d2c6eb64a1b4575e4cbc5f2a3395623e207ddf97f2ea51082fa7108": ["cocolog-nifty.com.", 0], + "8ee5823c3ec4c1618946efcd8e37cebc2b91342701778b57f5b9d2259afd908d": ["powerschool.com.", 0], + "8ee6bb9dc6dbf61db3b2c01086976ffac34b0820c1a4c8aeff92f59c33cb49fa": ["122.gov.cn.", 0], + "8eeaffb028c3903063766d791f50ce93771ac47fe046a065cc4e22930cde6bf1": ["clicrbs.com.br.", 0], + "8eeb62f715b8e46734dbbd4d90cc0b04f20bf0e65478822ea80118d7c6d054f8": ["b9good.com.", 0], + "8ef4d841961784d84ed6aa2a9e560b3652df2502c7172a7dd1e23ad775cce861": ["studenti.it.", 0], + "8efc584b340b267f43f09195072a1cfd1c5229704b61af4350068f4869e3bf06": ["arpa.", 1], + "8effff989a81ea5dd15b17f18b46fc09deb80b0ba6fcf4f6e2f177de41a0f591": ["jang.com.pk.", 0], + "8f046aee41310af53fb9e7fe8247b62313a72a31a6569f76baa200432189ea36": ["kompas.com.", 0], + "8f057257d2964c344c5dec8d1cc148b14c9f04f53abe7618d78e96f877980b87": ["tchibo.de.", 0], + "8f0e439d130acd9402c5f2d7dbf7ebe776805d41db5ce654d02c8f018087a414": ["ni.", 1], + "8f1c6a0a2ec888a1e8b59e4abec9419c83a316b2e5ef73d1feab2f591fbb3130": ["xn--9et52u.", 1], + "8f1fabae35e78697cdee3d3b04718b2e87aa4f31870eaa2817e973561427dc57": ["playok.com.", 0], + "8f21e78667eb77d657053e85f88316894935661dcf5cff3a82292b1c60bc56c6": ["besoccer.com.", 0], + "8f228cf6cc60a01c7332978a049d669383a5d26e9345906a8245b272026d7330": ["sharafdg.com.", 0], + "8f29935c3ee89b9f32f5e91a8399043ffd6a40543c738a061012585dd911543e": ["postgresql.org.", 0], + "8f38ef5a4b57b67949a513c81800577e498c47650e2ac22e56cc57976c66994a": ["wikidot.com.", 0], + "8f43472fb339ee001185b5d2dd637ee91353750eb358dacd1fcf905e9eae0b70": ["tunecore.com.", 0], + "8f4608829ac97f7b395f460e54c40e6b3732b498c4f3a1bbef19cd98bdab9f4d": ["deere.com.", 0], + "8f4be3bb9908f875699f2936331bc8f37604bedf2234c38869c446c04a595ac8": ["scamadviser.com.", 0], + "8f4d7edd7d8a3bbd375fb2c8e45aa99861d5cead4755f76754e060911b4e10dc": ["autos.", 1], + "8f52f15dc8c43dc220a0dd4c0bb926d559af453ca7cd8ac8874d7862fd3ce939": ["sportradar.com.", 0], + "8f5abfe97adcf9fd5e3ecc1c8c572623d46513fd2bda30298811bd09c60fdfb0": ["vccs.edu.", 0], + "8f6011b0c72a6956d2cb7689b3d03e34fbd642df634e5d620e1c0df647cba610": ["chatwork.com.", 0], + "8f62922a634ce0b8c85e2ee40da67708f1a1bc0fef580372fe6239b708abf4de": ["worktile.com.", 0], + "8f67bc658427bf12e6371758072491d91e31dc649084ae3754446ff40ad99c60": ["birgun.net.", 0], + "8f68409adc1633fac9fe1dcd12bf2c5295d8180cc9ecc3dad825f0ba184e3562": ["squallchannel.com.", 0], + "8f748cddd020b636f27424e7968b470a0890e3f5a4e5feb8d58ecd409b60a176": ["calameo.com.", 0], + "8f82d86f2221d82f4013a87540af317c914a73cb342c79685af346e23791dd98": ["heise.de.", 0], + "8f858969d8a81ffaad5c7a13cbd9b81527ab268cecf12f9965755b2f28353663": ["jwpepper.com.", 0], + "8f8c375315236a3cebd7d1dea403879c4f7fe918cadae069cc71dd48c9529802": ["csair.com.", 0], + "8f94a4b25c1126b4d53c4e5c680f0e0aa80df40b49edbc353dd88db4f72e40c5": ["abplive.com.", 0], + "8fa3dfcab1ece8a1594b07710a7d95ab7e01b097ce52890f2932c03025c3facf": ["videohelp.com.", 0], + "8fa4584f2419b3a1974163beef5cd3da7dd71479c51403261a1ef1e8c5d12dd8": ["rtve.es.", 0], + "8fa51f283c3e2c300e93cfb0d798197fa9f1c95b11f3a521715b9651d0783c8a": ["cnyes.com.", 0], + "8fab9cbb95d776883a235631255c16aefc6764839c85ead5c2a79b9b45e40ae9": ["nytimes.com.", 0], + "8faf5964fec0b3854a84b5c19f0077b14f3d021fc18ab49c8a272e346a520a2e": ["thegioididong.com.", 0], + "8fb1ab1dafcea87a308a824e504e5fe6522b1fdcff20e206cb331c5ada80bef3": ["imagecolorpicker.com.", 0], + "8fb3f756c28f7c6074ef048cf89ab8cbb3f1cbf4c2fa55da7762aef88ae31989": ["egloos.com.", 0], + "8fbc1b14887bd3d497c8a97363fd392acc17836f8d1b50d6f6356038bf796ec3": ["riotgames.com.", 0], + "8fc4cc6a232cf5518fefcbcf446d854c3974bfbedae583ce30960dd5633b31da": ["slidemodel.com.", 0], + "8fce94d4e5a2662605dd01e766e29f9b1ac950f3477f3d39e53e6bca16a7cde9": ["rocks.", 1], + "8fd4fe560aa80a0893e5240c5afd5e860b2b5bffa6c5668e12c2596c9eaba3e1": ["taiwanlottery.com.tw.", 0], + "8fd8659b1c5543cc74657000c5dc2456e343dcd25ff4c3325d63b434305a4fa3": ["blick.ch.", 0], + "8fdbe3a793e45f2b8721328b3670aa63c93fc4ffe74a5ce5b5facba515de9773": ["salliemae.com.", 0], + "8fdf99bb6da873e6eefce1c64a129311ea8f882754b3e799a8aec66cdf07b4a9": ["tandfonline.com.", 0], + "8fe44c00df3bac7078173ee45c29dcfaefd9c6e9211d4a251d1f62006f5736fa": ["newsela.com.", 0], + "8fec2428726f28f9943a205f2c66a683d9fd10f08c3de56efe0f73a5cd28a8b2": ["seven.", 1], + "8ff2afbfb735c1d79391fbd29fc453ccf71bf097bc3fbd276ae14c5585795708": ["sur.ly.", 0], + "8ff2bc39a212f52ef5617d2404e5145611e70bbb38ffba29796a13f094268f4e": ["uni-frankfurt.de.", 0], + "8ff320271ec3970860ed8a5383d75b2a2e9f1961fc3108de2af18dccb6f021e7": ["advancedcustomfields.com.", 0], + "9003a3e7d97078db56f591e121dd452ffa5aa985574394a4d21a041922bfba06": ["eroticity.net.", 0], + "90044544c68d48b63035f6892ada0c7a37f39fbd5f2bd522fca7cb297901da1e": ["eghtesadnews.com.", 0], + "9008612f7902e74ca1d2acaa7490350599929b1419a16cde24025abff8891270": ["filmorias.com.", 0], + "900a7ef9b8cb9aa38a7c04bdd7874dc1b4610a15b65c89dffa4b703cb8c90581": ["flipboard.com.", 0], + "900aea038a8bc4c53d6e9de2a37b281087ab4940b9b663ae43a2dc0d2a0c76e0": ["getyourguide.com.", 0], + "900e81bfef0ee5302f7da079ec329912ddebaf111ff07b36e8d4af2e76281ef5": ["sadhguru.org.", 0], + "900fd4a532fa78f3c59e723866ba7a1dcb91ef7488b9694a869bf32c3a5e1c24": ["drogaraia.com.br.", 0], + "9011f764b8f28dd049cd48be6fc90befa1746b4c3599da780dc29290ac98674a": ["xn--fiqz9s.", 1], + "90252b1c1d8b2340a598e6ff344dbad3058880a352f57add26974798536fca3c": ["crystalmark.info.", 0], + "903690f39bbf445e04c20343cc078d48c2c41851f59ed2431ff296e698bbc0cc": ["vanguardngr.com.", 0], + "903cc03a80a30dd647b493c6d1eff68bd66391a7ef893e0bde34d2a2f52158af": ["byr.pt.", 0], + "903f72c6d1eacf3af414a7480d5a3855cb1cc2af7f1c64076c5ccb520d338402": ["news24.com.", 0], + "9047cb5d2586746033f807eff8471cf328661d89c5d78279afd1319d4a055f0f": ["oricon.co.jp.", 0], + "904879e121ae2169e037fe271b708821d2823d8b0d10c2b83df818e2bf24c541": ["onlineocr.net.", 0], + "9049c3b3c8d5a587e45a340dccd78246653d52c8fa7c418e1243df0a05d024e6": ["wallstreetmojo.com.", 0], + "904db550d5b59e288168c5c8481bdd37bbc156323df81ccf4bd8793ee8e51a78": ["news1.kr.", 0], + "905b759144555361895032d11ec04b79e4b9d7eb42aa46d1985beed66da18d28": ["myqqjd.com.", 0], + "90673ef9dee525808284f4189a2099ef88f0af450166ab79e664f2ca18e06756": ["cricbuzz.com.", 0], + "906743c86886246e3239f2e153734edd534a3de5a17e481d04ca768375508c8d": ["vrv.co.", 0], + "906e73bd194220a5865470d2762d58d171e17e672d39b493b086fd52dbbaaf48": ["qiyukf.com.", 0], + "906ece4a0ef709266e0525077bcdc1477bfce165de6afa14e019500891aa6f50": ["mycloud.com.", 0], + "90707733867c5cd88f6eeb73083d308f63cb82ad7816e91f29c6dc0705f3a417": ["uwo.ca.", 0], + "90727473ec9be3cb17758edde20b52ea09fac81a8dde31a22754824c3eee9df1": ["fextralife.com.", 0], + "907668c669854894aca5da9490aef0113ac436206510fc50a788ee7f91c6d426": ["51sole.com.", 0], + "9077910a9abf45916b4addc1262d2904c214a57b535ab98062f0f3c061de9ae8": ["planningcenteronline.com.", 0], + "90853677b0da26e5739d61dd6680d82d6abdcff025313f0485d8081965bde2b8": ["esq.", 1], + "9086276434a6f6998568f5482a7719f04fad8b8fc47e763ee6a3543d54d083fe": ["edu71.ru.", 0], + "908d8607fb7d9fb19d64162045bd94baeb667adae8e854a758073eef07cde1a2": ["hotpepper.jp.", 0], + "9093f5cb6fe448f9fc0879986a7af9168d1a9b7c1b75fa2695bfa938bceeff25": ["elitepvpers.com.", 0], + "90945d9b473bbf5641440cd52e21777d0d124a64401ad3a871cd59d197b26b12": ["shonenjumpplus.com.", 0], + "909984b7e41a42fcdfa1cde3208287ec03941de331b3decb11738f8c73117afe": ["mm.", 1], + "90a4fcbfd13f4cf3a13c71a0977912749ab18b5c18a4fcafd2721c0cb4543597": ["24video.in.", 0], + "90a659c64679b9a0b0aeec512432762184dc3802a8134eecf75a1a5defc44a63": ["equitymaster.com.", 0], + "90ac43c0c3b719464b414448eac54d7b8fa6b17a2b5c46eb83d8d7e02711d033": ["bama.ir.", 0], + "90adde10d44bb1c00d6b2b2d05fd208f32522a99f3a0c5f11cfaeeb7baf89587": ["getcourse.ru.", 0], + "90ae28b7fefb3c5e7c9d398879b8648952815e1f74caf0753008dc8675089bbb": ["bmi.ir.", 0], + "90ba40d5fa3918a6f570d0b662945f04ea877d04c21e022b08db27e7db72768c": ["luxmed.pl.", 0], + "90baa6f7eb17b5cae9ebef108f5965f90440e9bab86ac2a1ad6e7876fb85dae3": ["voeazul.com.br.", 0], + "90c680da990760ecb3751110afa0f3b93d1fe4a9a2f3109e0cd67bcd3004e93e": ["usajobs.gov.", 0], + "90c84c0fcf3f154df54f16125b998b8085c8afadff22aef608ae39136b77e1f7": ["njtech.edu.cn.", 0], + "90cf6eccc5571bf635ea86edda2b0502b98eae005d4a6a7b4af2ebaba717f6ab": ["shodan.io.", 0], + "90df1b6b8403d40bf63582c81fd64f35822bad8e64c025c9030bebb79058f00b": ["purepeople.com.", 0], + "90e1380d22b8d59c06e5c3ecd86a7b9f519fbbf9a08409af21791050a3ca7c01": ["4kdownload.com.", 0], + "90e9888edc931e4017af0982656cfabef8471d24bffbb41ebd0e84bf8e4b613c": ["oko.sh.", 0], + "90ea1994729c78a1d152830fab75d9f0edcd9e42ae661af20f85b897f11f9ac3": ["azpolitika.info.", 0], + "90eb6c58ebc499cf0dc2821f25a5daa9cdd981ae99b4a55f742bb3ae64127246": ["twitlonger.com.", 0], + "90ebd7ee89c44f7f3c54bb79933fa12effd1a1a1242cd081fb3284fc69ad3928": ["elektroda.pl.", 0], + "90edda4fa7684bb27cd1d4560887be99e2014406d2862e3bbc9c6a202c437a64": ["ixdzs.com.", 0], + "90ee5586094826263ffefad51422829114c288e916413167d95a531d1a17d9a0": ["ansa.it.", 0], + "90f1beaf253ca03ab6ed64880db00c1cbf6c329ad8a151f798deef01aaaec230": ["gebi1.com.", 0], + "90f22b3b791b62a4d9255f897cfa8a4ab4e7d75ce2e8d69e0d31c47ff42c8183": ["okta-emea.com.", 0], + "90f5784596cfbedcaac957e348dd8cb8c44f8792544fc518986adb00959daca5": ["land.", 1], + "90f8af301fd5f76295a1859f05a195f1cb5a49816acd31decaf43f3e81a976d0": ["addic7ed.com.", 0], + "90fa50094938c3506643e347619c3d2b45e8afdef8c6303f29697204c7ee2b39": ["bankinter.com.", 0], + "90fb01dfb8d399fb181615d0fda869c4d79a02f772a6d409cf84fbd3774f269d": ["ucf.edu.", 0], + "90fecf6b9d1268d4c5e52006034a54bbbba4b303b2554381eef42b141b5b9922": ["carrefouruae.com.", 0], + "9111b2bc69555df3c92cb2fab1c8c534f4a5b5e44f075878b1d4a22ee6be3207": ["forebet.com.", 0], + "911f478e0ff0af11828cfe1c63834a8e3bc492d3b9d3e1f0a1f6123ba23fe01f": ["minijuegos.com.", 0], + "91211c7a351bfb6ce273e826e03e9df1e895b6475f6d3384a38de82a94019bc6": ["resy.com.", 0], + "912888bdfbe8a88e673de2897c2dc93ac7dba44038bfd4422dd989566c144ff1": ["square-enix.com.", 0], + "912a649551dc82357a1863fe384db4a31381a3c1a1eb99edd15adf31aa7c1108": ["olimpiada.ru.", 0], + "9131f5932f7180961a84701760ed982616cafc345827e3f47fde1e090762540f": ["live24.gr.", 0], + "91374a25a79ce7576e4c5d439ac56cd2a1066ef8e3b9255be4ec04f6f0633b1a": ["xuatnhapcanh.gov.vn.", 0], + "91395b1554595bf8ca8d07b6d8747d395bbc8bea0e40aeeabe16a95d9596034a": ["cgg.gov.in.", 0], + "913a8185164479752268a5fd4017bbee9c0b94d853629ef28753f48dba1f4941": ["guestreservations.com.", 0], + "913af01ad2d4a9510a68c80aed647b762baaf551af1b27e1ad5c1d2f1b1ed9c9": ["pearsoned.com.", 0], + "913afc45902c1f4aa436d83c0baca2817080a348ea5a30cff3b7ec62714dae9d": ["au.", 1], + "9143c1b32094ae470b0a297bb059cc665db0caac806c3acba58eefb919b42882": ["distrokid.com.", 0], + "914436dca32e56a7c15a7a0fe058615a705b65e5eb3c298dfd95d758646441fe": ["smarthub.coop.", 0], + "915598b15c08c9c386f9fd06993114aff1a00e95901030b14b01209cf1f344dc": ["athome.co.jp.", 0], + "91655e16667178d0b68c7e3c9dccafc08ee859960b5fc36bb47737fb971431ed": ["qnb.com.", 0], + "91701eda3de60cd04686374aa8428fc3d5a8572c0680bc045354e1f7419a4176": ["destructoid.com.", 0], + "91702804b2d4d9e1bec89f2cd6ac6d8e64262ceabdf9eace5ededaa344258679": ["cnbcindonesia.com.", 0], + "9176aaf95181f5fa58d1d019a39f28a4142df86dcc129a31b22fb5b8ab9372b2": ["ntt.", 1], + "917f61ad06ae24ba8fbf110574705f33626297922c2faa6d2435b5585dedc5ea": ["blogger.com.", 0], + "91822c2b0d4e58a3bc9bc31bad6e929861ed7c8d3d4aa93747ac947cc54773d6": ["gowatchseries.ch.", 0], + "9182bb8bf70c1ab89aad5c5af99d91196992a5eebc1180fb30b0ae047a01d4cc": ["sirogohan.com.", 0], + "918480b91fcf33d0c2378028a91a7a69eb69e8c0afd77d32c045688fab0ad00f": ["tripadvisor.com.", 0], + "918a82f21dbf155ed02d54bde926e1aa5876770abb6d5350a9c4035ddae66354": ["edmunds.com.", 0], + "918f8c255ee6ca49b096264a221906dc4378bc47bb6c270ab59ce3711ccbe1d7": ["wbijam.pl.", 0], + "9192afedf02d891dcd9ed7d4f5a91a3cee64ddceff5298578b8d2157b9be640b": ["football.", 1], + "91aa58bfcb3c860b2290d803816fc1f85c5bad05b63b7b5e934c314a13ca713c": ["tim.com.br.", 0], + "91ae92f1448548540e6efb2f47167b6e9d32d66aaa3dbd9ed911d5eda1b10aab": ["bpjs-kesehatan.go.id.", 0], + "91b067892758b2d7e9a6a559d6658577a58f618dfb3efda7eb094593ffebcf08": ["probuilds.net.", 0], + "91b7e49ec48928ce0d533f45b6390c2d41ba5899ae2809ee14f229ccc35f096a": ["bi.", 1], + "91b831950e9c928a84d66b786fdf7da81cf0323527593621dcf6f04fa10050df": ["uplabs.com.", 0], + "91bdf3ec9b2c7b9fcfd01d79a353eab97119d8f90a51f3a5866119cd987eb204": ["pornjam.com.", 0], + "91bf4e8a8bdf22da8c5aa19663a37615b16682620258d835df8d8ff706e63626": ["watches.", 1], + "91d31835171fcd7a79fdb558ad949a9e12c50e0b1eb7d0e7c6dbae00042585c6": ["scarlet-clicks.info.", 0], + "91d4a657eb2b0df85243af6a0094c8e72d624e2834634eb62be620bf8e6d6405": ["availity.com.", 0], + "91d6665067b3e863da6dd39b4abfbe564b241cf4e84ce5bf96c85df1535e575c": ["yimg.com.", 0], + "91df5ad0ac70753fa802dd3495465bac42dafed23f04e8bb39ddec47fa07b691": ["lamer.", 1], + "91e2603926fe3a6d011667a7d62b4f37412784896ba27835d0f2f86ed1d34df0": ["oglaf.com.", 0], + "91e266e097974780e6e2ea92af36fa222075a62e475fea1a806b13f8e77d6721": ["rdocumentation.org.", 0], + "91e30ec2043366eb6118e24a80e0224e8afe65e43c11dc37b53ca5a065871f2e": ["radiorecord.ru.", 0], + "91e604fda7ffb1f216bfe1140000a4c91b49a28ca5f3dc95b1e6641a462ee9e6": ["aftership.com.", 0], + "91ea4f6558573b6ba95683e1c7b96bc0fa2316d8ee638d4d07fb74d015f7d684": ["umass.edu.", 0], + "9200a33c0de6485a533b6e9cc02254622a445e14355d4a0217ce2b59942c4688": ["juegos.", 1], + "9202acc0b7f1b8de5e84c8e0e217a3b40f6c5be5d33f5efab11c9eff04181489": ["javcl.com.", 0], + "92077f7e2848a4a3e32586f952d83a3204606390e1e7fa250b15dd5001750d6b": ["vorkers.com.", 0], + "921ce7db6908d3b8247ba17c7509c4a9249c2c8db88729ef7764d93bb0a4cb51": ["reisen.", 1], + "921fa07972cced900ff2e564d280ebf4a9e296d3ec1fb227ebd16f572776e96c": ["uxdesign.cc.", 0], + "9226b68fc3bacc119b449759ba069e885f08791b5add27466f8ef5de7dbf7eac": ["rubygems.org.", 0], + "922aba0bbfcd91b5d1fa77a2cfc4a82f2dbe331f807b5b8795a3affc6a15e857": ["mokahr.com.", 0], + "922c01bb7e6b6ffa76c0951a4a711eea2b85ead3c294f1f1440b09fd12a7b073": ["turnitin.com.", 0], + "922e097158391808e17d5a962dd7fb8b257a7b3de3042ad45928fe657a52fd0e": ["theblaze.com.", 0], + "9234e448fadd10af225625fef1dd6973438321591d065e74222e46357514e36c": ["youtu.be.", 0], + "923618c1a6891a606d5ae8161e2d42d1a0f2bf0d357d1e717b6acac6edbde721": ["art.", 1], + "92391ab1f6c31119261568a5c16ea8bf4e81456a3f888170a52439ba9ea11b06": ["id.", 1], + "923d5c5155ffa7f1509991e2ddd19a2dc99665b93a5caea4cc5809e3b94daf8a": ["nude-moon.org.", 0], + "923e430bb1aca9fcc9d5d7696509dc253220e43cf99af72fba35b5c8129d4047": ["jfdaily.com.", 0], + "924228975c0a634cb4962804d464777c6c540d76b3f37facd8c7398e73de764b": ["ekino-tv.pl.", 0], + "924983108f2e4a3cc5eea29ef6d2936d4a28e7ee3ecc7542240b222b6802f2c4": ["gumroad.com.", 0], + "924de88725141d6e6119f94b4aa58e422766c957c25a2150e5e3f660c7ac7a40": ["meghdadit.com.", 0], + "924e20ccbdcaef9da93fc9d0d7f88e235199e6e3a7ae90f5e5f27cfa46fb8577": ["vultr.com.", 0], + "925e1a9256fb74ff6a521734121f36d63889804dbdd4b42a6ddb6258c625c18e": ["rocketnews24.com.", 0], + "9264df832d711eb38a5db73a35e79108952269406a6eff9d4682b4aecb6c9636": ["subtitletools.com.", 0], + "926b3af406c95dc4278fee8bdccd72211879e7d4fd6fbd6007698cd7c328ab1c": ["th.", 1], + "92706a20eb2fa6f6c13351e8abc57481ebd88dbd1457962142daedd56ca217a2": ["simfileshare.net.", 0], + "927631fae8887b53666c15dbc845cf9f01d88eb260cc819a3ced6ffa82db58e2": ["hollywoodbets.net.", 0], + "9277327d09971108914e4065d3677afe8a1c3bf8cb0c408289d3de48fe29d2ec": ["workaway.info.", 0], + "927aec3b427283a4a9c585df0e34b093fc02e582757db017701cabfa759f5dc5": ["barcraft.com.", 0], + "928775f3dae31348b309b9fe408072bdd6fbfe8fe0b3eaf91d1e84686b3260cb": ["dellin.ru.", 0], + "928e219a441ad2f890ef5454eda180647f7adf9207ffbd5038ef122beac5ce88": ["anten.ir.", 0], + "928eefcb96ce6d676c68a5471665de5f192f6258b38d804376e8c08842e1d025": ["bluedart.com.", 0], + "9298bad66f65fe7b17849271c178a26f00b25f837b41df978789c8eb04738ba5": ["ccfbits.org.", 0], + "92994634dcc113265c84ebda71bc8353d381ceec85d3f352797d0e2b2729b82d": ["sss.xxx.", 0], + "9299750a71892947764c4e5ff71575ba378f5107eb4d0f0ed0489c2502932f3b": ["messenger.com.", 0], + "9299aeca17a0cb55f94f3f980f42ae68df5b1e36e1cfa3b98ab60eb24ed9b5ea": ["comss.ru.", 0], + "929a94908a6d303fbd9f2af628cdcae65620a62a26b2ed2b4bc7f8768a28402d": ["sexvid.xxx.", 0], + "92a47e5fd8b38f956190d31f3248346e7c55dd775ab52e38c17ff7667a091e08": ["slashfilm.com.", 0], + "92a6306638e2475f0fede2002a3f2b34b85bb5db8615036dcb1829871a2cb5fe": ["ezgif.com.", 0], + "92aa7c29c779265618607d55edec8814897ffd475b62abe4fc5e78b5d9ebb46f": ["forumcommunity.net.", 0], + "92b9a25af1bccdf2056690798c086806871f2cd438d690c40fd0f750e8c50ca9": ["vietnamairlines.com.", 0], + "92bf57b45555d531bd2ce33228c8629f9ce0c085344c21549359cdca42e05c48": ["xiaoshouyi.com.", 0], + "92cddf3eed7d4804f5b4016589ffc45f82b7b2a1d3e5169be747d14a36be8c79": ["tvline.com.", 0], + "92e84768732f57982765343eefb8034ef0c010abea5c80be5829007b29bc969c": ["pardot.com.", 0], + "92f5702a549ed51641266e82cf3b6ae7115622ae2962a436657818a40afa9818": ["unicom.", 1], + "92fba6a2ed2c93cbebe9e57d32725d650f3553e17cf75fb3d6f5fb702c92060e": ["gitbook.io.", 0], + "9300193a72992d64dec8cc4ce1bcd35cb0209d3385467368873699a44cafa8de": ["eu4cn.com.", 0], + "93006935fa19abb14cc6bde128df81f08bc5510fd6a363d5e24e963213f823ad": ["gitlab.com.", 0], + "93028ee00cf489badfe34f08aa5cc6837ab613057f6831a3a1700e0343025da8": ["adobeconnect.com.", 0], + "930470cd1faef0ee1c217c27a3e8a5eeafe93df5070a63a29aec4c34360e1896": ["porndoe.com.", 0], + "9304d04ffd9cb344741737eba14f8c27bcde77f6ae93513555e9b99bfc6a0d20": ["bandicam.com.", 0], + "9306b5e2571e6669b5106b3c72c54bf3921724fa717d52b854a7ca4279cfb37a": ["mkyong.com.", 0], + "9308310735099edbfa7f8b83208d5370f28360bfa3ee5c3d882b26ca4729ec01": ["argentina.gob.ar.", 0], + "930a2df566d18f4e893aae0bd91ca514fc82ece96c1f519921a0087063e49130": ["ls.", 1], + "930b0b4e946dfb2ef36f3be5aa499cf54d5372dd6384da64514451addb7c19d9": ["readmanga.live.", 0], + "931796a5a45a49fc4c6267a52f36014d271c16ed0b1d9534c5632765e44239a5": ["europa.eu.", 0], + "931d540ce1fac3524d11f06b2f0c8f1bdb52a6b90ce492b8de84f7d154da6d64": ["blackrock.com.", 0], + "932148c3ebadde60b91d5943f325a33ffbbb5b8671d672fcbde75375da5a0232": ["media.", 1], + "932254989ab6f86970447f2178a6e420d25f15894f09ec0df67fe8e81e5b6d66": ["smugmug.com.", 0], + "932a27772ead7a6045f291a8ca32a7fe437ef39e318ebc9f134d54edafdcfee1": ["atobo.com.", 0], + "932abfacaf6ebddbd1f063c08957f323057142197f9bd56d4d071f063953b942": ["allthatsinteresting.com.", 0], + "932c55d39d239038ee790bcbccfd272ea94cf8f376981c8ff09dc87cd22809bb": ["dailywire.com.", 0], + "933172321e7062006f66bd3915cda38af24d2012b759f8bb90be6af451e986b0": ["locker.", 1], + "933d2d3b2d164acc65cf91244056d8ad9f8094a16b095f0b5b25aecf90afa144": ["dtdc.in.", 0], + "9341ceba3073ad75408adfda2d376f065ae23c1946deb3fc7cf32c619d4f0b69": ["dtiserv2.com.", 0], + "9346f7961814cb8bf649234ca609651f66b990e4d5fe4898f5c1c4f5d4333f04": ["newsnow.co.uk.", 0], + "934ffbe3833b6435340cb02035ff69bda334d123e70a9a4bad9abf5f09c246e5": ["ptrack1.com.", 0], + "9356a921b69e0a655a99aea66d5348e1c9496ce0fb86bb1780744945b31e9bc3": ["rblbank.com.", 0], + "935c18c40ccf874f4ac49bd1acf298b68e1b161b7b0024549a885dbf0b183619": ["warmane.com.", 0], + "935e04970929a3f6a7f8cfb5dafcb7d4cc38eea5d194bcca2fe4d5d54ad2ee2b": ["stlouisfed.org.", 0], + "936cb2ed89cd6cbb81c667d33f1b67ed6f831ab57bc90140f938efe5f84304f8": ["moz.com.", 0], + "936ecf7b9ac6ce77b83d41dae7936519ef28a73815f94dc87c771d0a64bef460": ["etnet.com.hk.", 0], + "93730eae4e40ee4beea17b6b7a2deb281d087af838eeaed3cb0a833634eb2430": ["thefappening.pro.", 0], + "93785aa7443e78e20e0065ec7c448195a5104162ee7b492f18a838b7a9a4d137": ["xn--mgbcpq6gpa1a.", 1], + "937977a30cbf703107da1a3e0baec86b9734c3becdb3fa8b030afcb516250b5b": ["exoclick.com.", 0], + "937a29ed68a3d9366cca9f18abed0ab563c7b64d5331366de7af302b9c700d1a": ["ozon.ru.", 0], + "93804a74c32bea25d3b9a877515a94020fd255285f7e2b16ff7a7a166e3fb5f1": ["bigideasmath.com.", 0], + "938828cf60d659f32e40f954f976797e371ee7d53009b629c8cc033aae7ab4de": ["mitiendanube.com.", 0], + "938bb14c58da667a247e4d4df387a246259bd1ee43534bffcf12f4517ffa0c53": ["wayfair.com.", 0], + "938df3a55b18c81baf3c7bff8d7c55b5c51069c165e83d1ce23a2449b2f28262": ["surf.", 1], + "938edfe6a08127f6accd8df4545f76bcac4a47d6904e374f894c7640af071633": ["thechive.com.", 0], + "93946f63d845862db1dbb241d1d895b06ea2b47386805c05d607118debb6abcd": ["accountant.", 1], + "939c61d0f1310137fee67527d2c0c3dba9773c0a9d74992e93db75d6f0ec0296": ["wbsedcl.in.", 0], + "939f5ce008464668971aa175acb295e4bd7805a5a8d5d045346fb4126cfebf59": ["bluenile.com.", 0], + "93a096d332b9c5df7a5a934b0343126a2814e44e1b94d4d8992d92ad6245f9db": ["nankai.edu.cn.", 0], + "93a574b289fc8201f719e376122042946160f3c7f6d20ab32b164c65d456a678": ["makemytrip.com.", 0], + "93bfd64b5bf28733cdd668fe718f254edfab81169661bc15ac58290290a67609": ["comparitech.com.", 0], + "93c2f707aca3d9c2cf73d365a2b48ac26977135d0e5200d5cfd59e5b05d40054": ["elecfans.com.", 0], + "93c44c2c4b7d7890efccc23d567ac599c54551decf7ab4ab6ac1fae8667d4e45": ["imiker.com.", 0], + "93c9564cf03d5719fab01079d2a629110538ad5038d0ba0de19ac454911f561d": ["epicgames.com.", 0], + "93cabeb4ff3730b3094a07cd601ca20b15bc68a06eb28018484db020e55229f0": ["hemmings.com.", 0], + "93d6c367585e0dcd052523063934f3741224686f299fa71ab37a4ab0c8e990d2": ["mattel.", 1], + "93ebc2719bbf88dbdbfb0b23055b0bfe88664e4f5c2dfba1d75d7d0da7092a76": ["eluniversal.com.mx.", 0], + "93f5e8b34328873cc5e03b5ef333c7d7541065acc1c8a85adb0575a2e9c486bb": ["haraj.com.sa.", 0], + "93f75beac4cc8045cdddb7172b0b4ef9ca4fc78984503ac0f1c99ff83fbfdbd5": ["vajehyab.com.", 0], + "93f9e80342ca07d4a6db3b0b865b3e4dafe0f09a087dae74b1f177de1582f73f": ["imgix.net.", 0], + "93fc1b364df056877f2dccf63e27b13b5d01fd418baac0f2b1cdf04ae7dd943b": ["documentonews.gr.", 0], + "93fe050c611116a4e676369ded88fd4a82fe06e49cededb668ed62c654495601": ["sk.", 1], + "9403c93ed4a468b85376672cdc08b78a7e8ddc43cc917d2a4384477f337bf5a6": ["to8to.com.", 0], + "940471f7f53d9cba96ebbe90f2b0ed6874818e024562caecfb6e51638355dd21": ["caravan.", 1], + "9405ea0591ccca72d9acdc2d2a2548f9a7941b115d93e3896e4b0dab511920fc": ["myunidays.com.", 0], + "940e182fed8ca60f67aff0b1f43e45d56803c6c55c5f3dd2fe02f3309bd79085": ["pdffiller.com.", 0], + "941d1fc0ac46c66b484b4b3a2b26a0d374c8690a9bdd4caeb730cc16faa22a2c": ["cloud.", 1], + "943056f259ad83a42bdb0a94a9d61758e6361f31c51a7fe03cb63c9858ad239b": ["target.", 1], + "9434403fa17edc05af7e1d6d192c319562376aac8b59ff35cef56dc0edddc753": ["berlin.", 1], + "94364654ef59b49c38f6a6a09c8217c814bb66dd224a068f6910edee61d9d06c": ["teamviewer.com.", 0], + "9438088e07c0d1c9c49ad666b580b6d77866a3282904627f0b92ce3ce801fc2b": ["pgatour.com.", 0], + "94472b50dcddcb4e9a34529381a2083ee6b8860209a527dbab213e13276fef8c": ["banfanb.com.ve.", 0], + "944cb53a26f6c6f1e90c9ae3f1da40b21f170c6b156afae2aefa4c68938057bb": ["campuslabs.com.", 0], + "944de5a8f102b7f9ea6415f338f0e3641e867fae4577d3996afd32c16089ac24": ["updatestar.com.", 0], + "944eacd6daee75dcbad45858221ffea861a4639a46ffec5f620fbcd0bd6979e2": ["werally.com.", 0], + "945011b783a118220ec5164fe8f9eea1841bf5f55aacc7e5c612d01324357a7b": ["ets.org.", 0], + "94520e8df0e20bb61edc6ddb2d800062fbe8a3d8f972d2f9222e29806aaba61f": ["thalia.de.", 0], + "9459ced566476f92b0c01b0bb2403d077e37952ffc752caa1784c87fae89f327": ["ambitionbox.com.", 0], + "946186aa80c4af44e2e3a3b5747e7cd5a1f42aaab7470949899f0272610d96b9": ["telerik.com.", 0], + "9466f507b2bea50edc3950c16c5210ce471654b2455a73b487b8e86b3282fab2": ["uriminzokkiri.com.", 0], + "9469794d5af2f01f4c56b3eff35bd0cba2f875c7d36ec8b7bb21c0156e169c85": ["ril.", 1], + "946ff84bdfea58b2df728a5db70763faeb9edcfe155697708b4473adc9fdf000": ["blackbaud.com.", 0], + "947198020e490d6a6e73deecf9dd226db804ab50826d6861ea23855a3235a81b": ["algonquincollege.com.", 0], + "9479c2aa8eedcd44842e5b1ddd0df1865fb10c81cb49c4f1e90fc330b15d0115": ["ymobile.jp.", 0], + "947b2a7d7f07669c1f399cde9c78586f4959b0fc4341d64bce3c74280f4b33f2": ["thinkwithgoogle.com.", 0], + "947b41f7d219d28d1e0f14c9a947aaf36788cb8c70c3e30f8b4dc1c71bc59af6": ["flhsmv.gov.", 0], + "9485561ff5421871418fd7e293ce85dd70c6d1397a4f09c9ea28fe38933a4d90": ["prepscholar.com.", 0], + "949c95145892e563b7326c8a41dd042d5aa32fa4c31fe5af9a93d158dc3966d7": ["voetbalzone.nl.", 0], + "94a2e0b660856ce9a6b7b79330a5060281f036061788fe0bc2ad5daca29db1e2": ["befuck.net.", 0], + "94a33de839fd67e8953399afa872e0bede66b60c47e4e5d6cf1b998c14f057f5": ["jdl.com.", 0], + "94a86c326fac7a307335674afc6c225ca86cde10d7ae162fb027946d15b7c77a": ["akharinkhabar.ir.", 0], + "94aa22ab5e5686651a0ff92a1f652e51ea43cedab7096c4fae919a77eefd5a18": ["showmax.com.", 0], + "94b0859ee3861455ce06ad9517f3356f0c532516f220249a1116e74c19feebcf": ["lrytas.lt.", 0], + "94d7cbaa4a7e93b44bb16bdfc17f06c2081bdf26b50c99bea6c34a298a20a3d1": ["sis001.com.", 0], + "94d8dab396f331a97958b185b7e6f342e19d74ed155bf22963ed9e3500526cfb": ["pic-time.com.", 0], + "94e2b9b675fc81f456ad4445400c8f1421cf94e024244e0895c6c34e35cc0db7": ["hemingwayapp.com.", 0], + "94e672d9791e6beff669e9138a61274c0e7d899a2f67195473737f7d37397c50": ["xn--90ae.", 1], + "94ebea187450f41ad8875e7549160a6f3deb4e928e367825f3003266274648b5": ["doctor.", 1], + "94ef7196806722ebb42be0c808ce8847c2abecaf9682401d85dff7e11a675f99": ["absa.co.za.", 0], + "94eff7ae224027f09968ee69debcbb2b886d4800ca0c52ad50750691ef591e7f": ["psychcentral.com.", 0], + "94f41cc85905ef0fbddfaaec9dff471e75af8918bf24ace0cc1c582938c63826": ["new.", 1], + "94f5b300489419313822afeb767b49f63f0edaf2f908c745e847214a23d973fe": ["priceline.com.", 0], + "94f821896e654fc1d1b97ab2663119e0b4ec0f7eff2d4f633883cb97042d05d4": ["fun.", 1], + "94f9e787731e782eac4917e0e0cf9ab11b9c2828c763e7b24b562d88495323ed": ["society6.com.", 0], + "9500947f88e1e115a7286d90c4611f10636de73cceb52b04d9ed96e44e4d77fa": ["airdroid.com.", 0], + "95041036d72b506bc91b0c4d046b9cd12d55e7f66323ad0a4a9df8eec0b0baf1": ["ceo.", 1], + "951f04376ed21b4a35fc33165056383225ef7d8fd75f6e9f0ac55737c7a0c575": ["openedition.org.", 0], + "953039547ea3136c81d8e03dbb738238474618b420e3afa6550cf91aebf318f2": ["creditkarma.com.", 0], + "95318edea26ba54459e46ccf91229201488579b967c5058a4741f314c957a4c1": ["mybroadband.co.za.", 0], + "9534194798695fff614500262ad7148dc63606edf7f50e2910484a9507374f22": ["universalis.app.", 0], + "9534758ed002924c548e550ca7b01019753389dada3bc6539237a2fa4d453061": ["sendibt3.com.", 0], + "953e06bba3772af2e03f896bb2c98326907315169da717a40869b8b745396528": ["sxl.cn.", 0], + "954a988fe5db1e2aa437144d9affe13a5b37d04c9e53c56509161a002444e4fc": ["screamingfrog.co.uk.", 0], + "954b6b0939b3035d15932380266f711821f234a891bd6cf3dbf1796fe954e0d4": ["ne.", 1], + "954e21b91a0ab86d88bfd3c537bbea817372b32909515534194b578f212cab4a": ["theater.", 1], + "955c09a57de90b8e84d0b36b77d5e8afc3fc7e9626e4e930d74eedd67abdfb68": ["tlc.com.", 0], + "9567cfac0e69631b77ceac2ab96edd59ecae6aaea758fa1f853a18102691bde7": ["smartdraw.com.", 0], + "9568a003d9cc847a0578d6a13b6cd5225f7238eb7a38c56d79f3397be3148066": ["cdn13.com.", 0], + "9572c1c15579bf726917c55717abd25fff3c0d03ed1b1b0f6f642a1100e2a267": ["moedelo.org.", 0], + "957903ba1029c04eea685d714952e8add31ae0ca74a57a365409f09eb4b26bab": ["residentportal.com.", 0], + "957e44df8fa31086206d9190055162a0960f053913e30dff4d45228c42fae2c9": ["fengniao.com.", 0], + "9581b0225f7770506ed7a5537f6cfca34d63491271fe03cc8d1690f840fd04b3": ["xn--fzys8d69uvgm.", 1], + "9583f60d95cc667fb802529781a79e2ea448e372b28bb57dbe6c75e88a453c3d": ["statcounter.com.", 0], + "958f87d732df161d32ef46ae2f1c3b6eb88cdb3b3f6fe5c40e3c76215da3b4be": ["gceguide.com.", 0], + "959321b73b5c903ac9df72e157491e179edc5f523a9cd760fabe68f3f3a65bb3": ["lowes.com.", 0], + "959d6124a3e5f35136a780c5c6bd561e698109c09992bb8b50b94b719facfc26": ["nipic.com.", 0], + "95afc75d3c784c675f86c73d5c2aa6c6f7d2b5acb28b1690d163f3aaed1ae1c0": ["apmex.com.", 0], + "95b38c3970e57e678ee7dfdc029fa7821b06974a4dbcd54e17e7c5b412ba049e": ["cqut.edu.cn.", 0], + "95b7c812319d909a9b4974d129994479a9b72f39e4c47c332da7cefe0783b48d": ["tuttojuve.com.", 0], + "95b99f3cfe7102cba7c1d3fc5ccf75f130c638d53e4e3d2aa6617efebd03389e": ["kimcartoon.li.", 0], + "95c5e6c465bf47d47aa18ddd81cdd94bde18d32dd3bdcd44cd60b9770d50fdb2": ["stackabuse.com.", 0], + "95c9ec38756e9007ee41322dcf5657a1b55280c9bcf771a060fc164e239143db": ["tutorialrepublic.com.", 0], + "95cc3e9ed80da05b08104935e15851cdd9b613db707f1810d0e6d6200dec8e87": ["6pm.com.", 0], + "95cda88d870cae183cce9c4bf9283b8268b0196b012b33729449fb7428cbb7b8": ["xn--mgbah1a3hjkrd.", 1], + "95ce53aaa77946241ba9fc84fab06a4595a693170d6b742ae2d15c06000a809c": ["comicbookmovie.com.", 0], + "95d8706d9e5c62bd43ba63d071b096cf59e5c769adbce28b8392203ec17e8756": ["tvmaze.com.", 0], + "95dc7b2ee065344fbf3b9ab5f778a76c7e372eafcd08df47eee6e55886da079f": ["sarl.", 1], + "95decc59834f9b805e902a07ba749856d6591b5a33842b6742d6c92c1d98fcb8": ["pulzo.com.", 0], + "95e4ee33cbb4eb8cd647c74877b1b16955de9a509c4a8e22b50e422accaece17": ["uiowa.edu.", 0], + "95e860729cdffbacd9f348bfb08d890a63dde4bce72e7cc700b5c9abf590cdb8": ["sharif.edu.", 0], + "95ec054ec8256142164424679e59f61aecc6571456ed47c797a07d1024c278ed": ["premierinn.com.", 0], + "95eeaf3ee9a1fb01c4559407dd772b8333c621fc170d6f2e789beb3f492ceb83": ["pedaily.cn.", 0], + "95f37d2a66def980e991d2b50db99066c610f1d64715e59bb02a0bd1c304daee": ["hentaistream.com.", 0], + "95f46ebbf48934da53ba9caa20f2f5dc1080e7c81374b03f3c797ac406fcbdd5": ["standardmedia.co.ke.", 0], + "95f8b3de38196d63aef354c780823a677f0d25411f552044b26f850114364a60": ["animeshd.org.", 0], + "95fbd5a428eb2dbed36bd75b67b85a46693af27af62fe3a4dea1404688d57243": ["ibm.", 1], + "96017c21faa57925833ccdffbda3e488f2cc2ad2e2820aef98baed80a070be5a": ["niazerooz.com.", 0], + "9608f0cd6d40edc81eea56304991f8421afe4e7e39fa8991961044db5b75b587": ["mit.", 1], + "960fa91d014ab3246a239188fdfb95adfe23afdf83193581442f3f0b2381b74e": ["wildberries.ru.", 0], + "96102464b7c5a2d2803abfebddef2ff30db7bd882358ec2c97ec008311f2ddd3": ["queensu.ca.", 0], + "96123ddf57e5e5141ef0d8f0b28b14644a80f90558732f7faec043350dd0d4c6": ["aebn.com.", 0], + "961727c87593053f996235d90f18c6a06ef615ad1018e2349c320579ffdffc3e": ["sexstories.com.", 0], + "96172dddf71b2bce39131916aba35336e75d7f4d1a45c6da4bcec1bd4065870e": ["sccnn.com.", 0], + "961741394ff4316ddd7901b48b3dc811143d6447d94d8c2e882aa07b90f8f0c3": ["santander.co.uk.", 0], + "96188569c28feabc35cae7730650bbfedf605f3bf219d293c3911c123c3d26ae": ["virtualbox.org.", 0], + "961fc6d7204473b7fe95b0df6bf3bb52ded99beedbac69a346ebb729510802ff": ["brazzersnetwork.com.", 0], + "9620c6e052c46a3fedbfa2caa6fa0dcdd9700a0ff76dc18f13db9b9dcb797215": ["aleks.com.", 0], + "96216a382bc32d528b94c90505ffc250097fb08280535d28f8beb98b4e455f73": ["philstar.com.", 0], + "96240769df73c0ce1d99340f3f80c1163c43f774e541f602a2b21656548d9660": ["basecamp.com.", 0], + "962b21ddc81589d8b6a65116feda1345bcc6def52a5c56cbe592d0ef9196d0e0": ["fbr.gov.pk.", 0], + "962e8d59d1bfd5a26787a06c02dc2078c685b2b47dedea474b4511d9b7abdb3e": ["lapresse.ca.", 0], + "9632eecb487ea329f0685142eeb6cbdec641474e4152ac07c4906ca93fbda131": ["urbanoutfitters.com.", 0], + "963a88636d4c9cc3f011dfc9dc0058e96669d80a89893c68c0bb08a6a8208db3": ["sqlite.org.", 0], + "9644e81692371ba3da1ed68dfb0bc6ef19b8449aefada03cd34b82d873523ae5": ["autoplius.lt.", 0], + "964dfafaa0b9cf8d3ed4d26112a4db6f89fa22550bbbe395a3afac7f5c3d3001": ["inews.co.uk.", 0], + "9660d4def2cb848a0bc21b54980c886392a30952941783b3820f4f1d39191ec7": ["me.", 1], + "96625b128cb85344d3a1659b4a277a8f24830bc0dfe3d889a181ded5b3716d55": ["rae.es.", 0], + "9664edd306d165264f259c010ec69c23467eaf1b18e03e3b2a1863cef0a2c5c6": ["bovada.lv.", 0], + "96651b7028fd1f09af9373385ee834b15210096cfbce6edd82d4b8cc9d466b37": ["viator.com.", 0], + "9668dccbd039f4374e2ad57af217f5ffcfd0b7a5925e761ea82e983ef74176ed": ["taxact.com.", 0], + "966c488801229219c88f110a17a37daa7b71926546bb9ddd0f40101ec82bcf88": ["bnext.com.tw.", 0], + "966eba2b02dd756faa8f75a1fa68e43ef6bebd9c4c12b31c62355f92166074e2": ["rarbggo.org.", 0], + "9674af51cf47a586cfc3077df6a390a2e8787380a22420bb0dd24f0ed7c18b8b": ["statuspage.io.", 0], + "967b9bf66d1779917ac9f2220a3cc6bf0437f71316ad5dd9f033e899bc6df245": ["ofweek.com.", 0], + "967e1d02abf99468ff6287fb0cc0665ba3ff6e9682160defa57607dcbad7d1f3": ["bki.ir.", 0], + "9686ab6057cae8786f43af42715755027b7d9f38aa12fd4e24b57e477df161bf": ["francebleu.fr.", 0], + "968fc05d8b558279ef92eb9ddadb204d96ff77d3951a24ee9581ff503e96c294": ["domesoccer.jp.", 0], + "9690f77a8fbb2b9449348d8b88e20e2b1b625cc39a93b3da10f5b1594dd3817e": ["soriana.com.", 0], + "969a5dc9450327f7efbd5ab3ebd164b147878564d3a9105dda036e025590e7e7": ["ziare.com.", 0], + "969fd388c3c2ee02316d7fa08b64ca842f08bf5f6b4c3983c9aa3c81a32610c1": ["idpay.ir.", 0], + "96a5610a2d08443c64f64ded7b39230cd0d54692cc5957d0c2c675c537ed8860": ["titsintops.com.", 0], + "96a8e19fd8e2cef4df03e240018a11f3b680660bba104a1e1b858c3fde72ff10": ["ouo.io.", 0], + "96b506117b3bee52e7c4b8f458778e65c892f8766ab466c238c0f994b73f0371": ["postaonline.cz.", 0], + "96bb3368781cf5e7ec82e32aa306c6b5c980ad360382a0f760ed5be16f706914": ["audible.", 1], + "96bcc7266f40d5b62b5588e7a8d5c4da6db77ff68359b51ba325a3f580a73397": ["convertunits.com.", 0], + "96c6f7b27e5851a8660123212f01ae53bb70a9eb33bae949f106ed8c6be6356c": ["strato.com.", 0], + "96cc97a91b1526dbcdd883262b7c329e023acb60fdf722479c49c68a76601018": ["isti.ir.", 0], + "96d10dba644153d80f42719acae41085e19af5ef6ccea0529b2c7f22601c0205": ["ikco.ir.", 0], + "96d1fb60b178a90e2ed7c3b35f6ea008adee8c11ad91e0d757e5030be1a8f08d": ["rentalcars.com.", 0], + "96d3e2bcbfff1f28842569a5f35487c99389ebaa7633f35fff429f65d6f3e38b": ["cdn-apple.com.", 0], + "96d8e36d84d6bf1143800cb228c58e418725403200cd55dbf655839606966baa": ["fetlife.com.", 0], + "96da8c7e15ae13137ed33e80a244a2d1dc891cfc80b5d119a8d5c92cb60d147d": ["garant.ru.", 0], + "96e56f5ab23aea2e53777636f31148da60f856f48ea5b27110b843634e0fb16b": ["cibeg.com.", 0], + "96e58ab0f98aea144497a0f50979fe1a749321e27afe7655e2200c55442254d8": ["vikiporn.com.", 0], + "96ee124188b405fc21cfda5495d96fe4463da0242a38f619d01b19d5bcdf97e5": ["eonline.com.", 0], + "96f2d0914ef76361fe5e28c862475369f01f084c07b1196197ee2507c7605056": ["moto.", 1], + "96fc5e000d5ba0ef4ba15509b8415857b1abd71c83f1a260c0897ea7ab177fe6": ["ihio.gov.ir.", 0], + "97067f378a528bac5250d37d87874cfc401f7f51910d45a00e2b19175d4e1160": ["warotanikki.com.", 0], + "970cdcdc71ed5179c63f4832202cca16cc348e1cdc654bb553eb1f1471b06d78": ["cac.gov.ng.", 0], + "970d5a7ba6b995026d85e045417703b8101fd74629d027f730acd7410d47c47f": ["socialchess.carlson.net.", 0], + "9711dfe4a5bfd26022ae9f3a2a808fad7014c44bb01d576c0bb44536bb429f40": ["signnow.com.", 0], + "97180ef1f79d09d523b605a609dfb1c6b8a3de89ab675e29f23eae91e8abcffa": ["bartleby.com.", 0], + "971f8120320c10322b98a7063fa33a21cbda6276b7e39999c77d5d69763e157e": ["bli.gov.tw.", 0], + "9728708ae9430b5a4cd38eb1d93d1f0be4a343fb53f110248c6fbcf3ee4fba55": ["androeed.ru.", 0], + "972ee6e9cadd56c0153b18194a88110e62e1bbe4eb41f67607978e59c713722d": ["nlnetlabs.nl.", 2], + "973b8a80b7110cac8b7a7a434b15188d4d8e1fe129ef758d9b7db51d7e09e4fb": ["maersk.com.", 0], + "973e35c879f81322f223ebf533f5ffa127f246969ed7ca5451240a65809f0181": ["campusfrance.org.", 0], + "9740434c4c53554590f6148842758eccf04221a86d6ebd5858390160a8727d39": ["johnnys-net.jp.", 0], + "974fdf976a4a8e71e04d877441c5023e4505a1778ac4c47e7e340b3e79555ba4": ["rulit.me.", 0], + "97500299f1c52685d24e1ab284571595f407e1dfd869076961d37f49c80237b7": ["beatstars.com.", 0], + "975731c009ed4d1a5722f16635a8bb2ba1ef71f1abd05140637d80f25c367529": ["intervieweb.it.", 0], + "975a0a182a844c7c522ca0d6f77524ec8eee82d480e6e8215746ae6e2f205cff": ["chobirich.com.", 0], + "975d058d37ae8af56e6d9c68f99df40916e3233a841ecced40d9897539baaa2d": ["camwhoresbay.com.", 0], + "9769507866bbc581565a1e69093320622238ead83ffa37632d123ff82b715a0c": ["bungie.net.", 0], + "976da8f5f669f4d4714560a5d25395e63568352b285a36f4aacb2bdee7049d6a": ["ultimate-guitar.com.", 0], + "97748142767c7a3029eba9373f5c99bd6a7420bf10a8a94e333d3dd286e31079": ["shahrekhabar.com.", 0], + "9778ed4db1f3e1aa659ae2213b4bdfe68ec1490eef55effcac252fd3538b3e31": ["funpay.com.", 0], + "977b0bb9efa7149729298c3bdb11f5626ad877419604f6702fcc4abd1106ccda": ["kinsta.com.", 0], + "977f9febf6d9a541a1cce2365621e284053996b82d5f14f67802fa2580559a2d": ["teamskeet.com.", 0], + "978342d075d000dbe2167d43f75901f8a04392dd68244cf149523866f11c7927": ["city.", 1], + "978a471ae765b817323f4f2b9dd3beaf827aaf6123444bb6ad94a9593397911a": ["domestika.org.", 0], + "978fc6b3e78fd9e3a8bc5769d844f1d0974e3b9adab55ecfa9f3c65e8dcecfd3": ["weedmaps.com.", 0], + "9792f6392bbf7cb34a732214bde0b8d00dc36967acfad53fcfe8792af84b8fea": ["litcharts.com.", 0], + "97949b8a35c2d17b03229b3c5417ade681c92fb2c620da6bdc89af01fc87423c": ["yaoota.com.", 0], + "979564e406567b017ac27e4256d8c97abb422630d3737e054817eddcf3f7b49b": ["tmall.", 1], + "979a5e893c81434f9488fd97330fcee9f2185ef806d6ab4bc596870fde4712e9": ["hujiang.com.", 0], + "979d93d15fbc6bf045d4194b825806d3b82c940249f9f6ff1e401a18f9086cf4": ["e-hentai.org.", 0], + "97b484de295d93685e5bd03402745124bf1fd41cebf613bb086513f8a8daa6b4": ["utdallas.edu.", 0], + "97ba24d1af3fcc424184e7362d11af251fa87e54dcbf833ea0887b0ad6aeced8": ["emb-japan.go.jp.", 0], + "97c8cce1f3003997bc1cd848dfc148b9fe688479d35a48652fcc220d6470b6b7": ["regnum.ru.", 0], + "97ca8a14b4e390ca677592927376cd76eab435454f026197153b4b95594d9120": ["pokewiki.net.", 0], + "97cbc3f2c799441f93407b9d2f8cd9635a2beb7e8a221d13ca8b54d30863c05b": ["restaurantguru.com.", 0], + "97cbecc4774096c1b2111e1d746536875b5c49fc704f524856b780de22b35943": ["colostate.edu.", 0], + "97d46c127dc7158e26229afc3a5ccba968e57ebe5eac8cbd702234e791ca62f4": ["routledge.com.", 0], + "97d72a70eced73ccc07dd319ecbd30285459d711408725f9f8e06ab0332d4641": ["letudiant.fr.", 0], + "97d9acbaa0d0daa6cb84a884916b4e4a3dd5633dedafef80a1246e80d606e6e3": ["info-retraite.fr.", 0], + "97dbbdb59b9c9ede74c641bfbdbdd69f7c2d789cbad49620753e5b5840f252fa": ["bci.cl.", 0], + "97defb80df8c3d9748888a2b8848dc45c7d1bcb8c25239c7c17e06c5d59ff633": ["wnacg.org.", 0], + "97e42f880af13a7eb9355d3b31ae8b0ba16deb1f4ae02f98ab1a2879f304bda5": ["spidersweb.pl.", 0], + "97ea89a0c93e2cc6bc16e21650c104c27fd33655da6fbba8e20cc4575b18ebf5": ["usvisa-info.com.", 0], + "97ed047447015bb8bba2e2d497993555beb71e494627c140acfc3254361c4238": ["kbstar.com.", 0], + "97fa5c50c1847184c9b0d61cb8b520f196552b09172a8fb3a9c79bd9a304824f": ["unusualporn.net.", 0], + "97faf8939e8e98b96476fd22cb10b35197d7deab2affa38154326e96d350deee": ["bz.", 1], + "97fc97d74ec08ff6a403891513196da95b1eae27750164c512ff6c3f73467dde": ["jobui.com.", 0], + "97fe715d49e2417394f71192f99f70756081323f64112946247eb5f6e0ca8a5a": ["ernet.in.", 0], + "9801f2ec961584a63f06eeaf465d8e8d33af85238cfec65f87f58e69deddd9a7": ["bancobpm.it.", 0], + "9806640dae10fda2d632fd8fa7f85fca53cccabcb55b2c9b573b0cb6309b7817": ["aws.", 1], + "980ba991aba1367f2877372845e3176041b4c43d909be5206611f9ae12011cd9": ["stiripesurse.ro.", 0], + "980e91409ef295faa7915026407abcc9590493d6ba9e4178d1ffaaa21ea0e747": ["ime.co.ir.", 0], + "981a4d0e7761d23e0c12fa4f12220d5ca2a166f5bf54477b52e58a7389fc95b6": ["nngroup.com.", 0], + "98244ef1fc64b84ecc012265450c8c23a001625f4dd750b6ada53a52afa51699": ["nexaexperience.com.", 0], + "982c59659af7535b14ee5964dba1389f817e30b748d8a68612a12b21679c3bde": ["facebook.com.", 0], + "9833ee82b4b26491ee0d9caad2dfaee1100a161251d3e9b9d45c2328604d7c58": ["bestlifeonline.com.", 0], + "983de1b2ced040b4adbdd0df6a55f6b58732d9921fc7b8da142571baca11c6fb": ["crunchyroll.com.", 0], + "983dff24db94ac7d70c9e9a1623bfb2e3897af9a2f5cef9381be4b39db5ae84e": ["ivi.ru.", 0], + "98402f39556f7349c251885453622434f4efc38c17302e7bd63e651d1ae553f0": ["livechatinc.com.", 0], + "984176bc1ce56e8ac1d2ff96866dd6b3a52df7ba34e4348fb4bb76b17cb1ed2d": ["actorsaccess.com.", 0], + "984353400a9d99b937d0d1628ccf13120428edde3f6a1dcd2335d6f45905daaa": ["koe-koe.com.", 0], + "984630d44c53e4016af57cdf3c8b0725c08bc437b71eb62c22cfab5cdeaa675e": ["activebuilding.com.", 0], + "9846a2b345b93c0a53e4f17194689efc5f7d7fcaead42fc3d3152425f31ed88e": ["ltd.", 1], + "984a4d080b5d0bf21651ce6a4fb7e726b7104b2cbf5323ab219935e30036badf": ["np.", 1], + "98596dd335cdd77b4518afa5a603ade0ab8edaf6fa9e81226f4af6a8eb00b203": ["centralbank.net.in.", 0], + "985f45ff65fdc73de9ba0312e7f53eae3338cb332fd782ade33eb136da8c59f7": ["medicalnewstoday.com.", 0], + "98662d568cb891636854e38bc2c3e3d6426136861d44e6a27067799630e76555": ["emol.com.", 0], + "986dc0ef87f59746f2f4c34f67d4632a544acbf06abac4c0da973f36f0aa376f": ["sportsdirect.com.", 0], + "98715ab459e230a0ce5d4863602a252fa1f8ad8a0af9e99fc4d7ac3bc939b0ab": ["consorsbank.de.", 0], + "9876033689dbb0827a0c616cfebdd2c7e30282295559950cc10649bbd6e021f8": ["nxp.com.", 0], + "98772e72ccdee5d0761d8780a857acab59688b9c42990f6028ff35a626e1d9ca": ["slack.com.", 0], + "9878c1439881e6a3bf266e45c2fe14b853d4b2aa8a5b1cec5c6122e7990d54c4": ["shatel.ir.", 0], + "987ec4cbc282140e14c367df91c6a780441cab8c6611177728395e9ba1ec810c": ["privateemail.com.", 0], + "98940ce8946078fdd5343b15acee3985dcd67e4f73ffe97d64b6bea979096604": ["fotocasa.es.", 0], + "9894203ad0be95b1dfa92af58809a31647699167ff7542864a8f61152f948b82": ["cooco.net.cn.", 0], + "989834a31bbef39722dadf7c279edd0b324869d983e7d8f0441da3e4e1e2f13c": ["xn--3pxu8k.", 1], + "989de8d37bf4a06b84577a3de7485957a252f2e6dfe3d1d03a123789e701498b": ["axure.com.cn.", 0], + "989ed2609c9ee766402306b37858fc03cf2120908bfdb1d768c4a69378053147": ["kamigame.jp.", 0], + "98a91336cadb6ea03553da24603238722a8ec0fb9d12cc169fe55f1559dbe135": ["nsandi.com.", 0], + "98ab682158d6f9560962205db6eb8378075811bde638fc30fd2c6a5bd66e5346": ["znanija.com.", 0], + "98bdbc27b35008228cf20786d6f71b62f48330899d601d51dabc5a4c73eb9d7b": ["coingecko.com.", 0], + "98c3b2c2b2fe17d50f09c9b0cd258bcb296776f2d855a1e67a4bb03a3b405a0a": ["kontakt.az.", 0], + "98cf3c540d5c5129398f27503e6a9a360ee1cd2e161537f93aa00c40daefe556": ["viking.", 1], + "98cf80d3a1ad8f674697b4b6f78c1e90e0ec2e2497bddd632889543de4cca141": ["bulma.io.", 0], + "98d10c87fbe7c71cd2acee85aa74d6b735fa388379fd4e57614318fb11cf6321": ["universia.net.", 0], + "98de55a395efa8fceacf100f94123517f2abdaacc303c375e312a8b7c21e853d": ["kompass.com.", 0], + "98e0d5c8193556635d57446e76792fb31056b271ac371e2566465ccf94cc20d6": ["multitran.com.", 0], + "98e4e562ecc7aa38b67af686c2d4b28cd1663abc70da6cc9eabafe91d32b5d74": ["irbroker.com.", 0], + "98e7fbb12fba777e5956bdd7c340146dc73ef8d6b0d9d8db167dcc81deef4bdb": ["mrcong.com.", 0], + "98f0d00800fb0bef4371592a00ab46d54e20b33ac7285eea0b04f170ef4af65a": ["technosun.ir.", 0], + "98f2685b9c1c2d81cbb85d7523d40ea9e1ffa3c291b70e2da20f20147f3b5952": ["luisaviaroma.com.", 0], + "98f47b476f9d6c838d12a60b3d4108ffe369681cf2025f22f50a15d4e2d65ea5": ["thesaurus.com.", 0], + "98f539959231404beab69a1578bf680c27f7163b8d1efdc0e3d9ef100cd65ac5": ["megatube.xxx.", 0], + "98f7c81c86e33ac8aa1ee097c8403c4b02336bb397cad75f7e4d0a6e04f0c8b7": ["smutr.com.", 0], + "98fcae0397f0a9632dd84c639759e5505c55e1e90de0aeb26a5ffe14d03ea9a2": ["colorzilla.com.", 0], + "9900bebbe45a90e0e5cbabe2efc45cf3c2242b028a21954c6c981760400e1f28": ["zhimg.com.", 0], + "9907347d0b852ab83b6cf12a6a7e05a03a0eb7c22035d2aabde357bd1e04e364": ["jagran.com.", 0], + "9907a71a3bccea7c4df0a7ec643d80f8b6c3597ef2379e9339941a6f17c9449b": ["onlinesoccermanager.com.", 0], + "990a6103aada7c0fc5480df1ad012660ac277cb72cb9757bea5cee40d85ead19": ["googletagmanager.com.", 0], + "9922163644add52d0acb2d0cba209194af44ecf11bfdeba47f813c51800c8e9b": ["koreanz.xyz.", 0], + "99222154b5aab43ffdaa3f76f6cac96b57a3e15be018c91c576b2a81f48019fa": ["eoffcn.com.", 0], + "992326426703c2baeb5beff622f3839dd3948ee6c8b2785bcd5688d0a42f15fb": ["pizza.", 1], + "992a184d1ab7f711c8e26fef220f034d2a09ce6f114b434d87175a4ef9c1af69": ["maxjav.com.", 0], + "992fae12c0c86df9fbc4694a3747ed33d320881fb3dcdec044e699d60adf9f3d": ["axshare.com.", 0], + "9931f571b26c2e4124e4abea7ce26fdeb852f918804202fed7a4cd2fb418b817": ["playerauctions.com.", 0], + "993cca34529d831019dcbc0675f15210e1b94fce36d168640339e33af27c1d0b": ["1news.az.", 0], + "993cd8b50c0a5518a5901dc8e5310b698ae3a692888b0e87a6df633e4bf75b50": ["royalsocietypublishing.org.", 0], + "993fd54f6284dea8d49e0ae1e5d491a28baf41786b4d7a8562fd12c7cd5e90b5": ["cam4.com.", 0], + "9947d7089fa056041ecc50ddb564dc49b08cd9625fa186da6a59be6dee1bc0e6": ["tjrj.jus.br.", 0], + "995c5a78dd42f71434c8108fb271670898fed9f6c45897715cdc2927d593e92d": ["solarwinds.com.", 0], + "995d37009164f1c3bfeacaaff69c18fcb508af1f1796d8cc3176e9a6acebdd38": ["digitallocker.gov.in.", 0], + "995e55354b12d9c046646ce9959ec13e00ddf35db27ee2f463f822d28cc0112b": ["unicatt.it.", 0], + "996129fab124b56e0f226e1b9b5151250379eeb3dedcaacfee48f25f6666d9c8": ["popads.net.", 0], + "996ef4dcc2e5d2920a996b3e0713de8ace2d8d41ae846f3eb7323e00a7061c01": ["cg.", 1], + "9972d7254a51f01af71920926005d16c78692f749c13d5080e57792fb82ab825": ["7ho.st.", 0], + "997744c1f9664cd52292d617a927d409e07280fd13f3a7fb9e2f07d74b314a2c": ["soft98.ir.", 0], + "997793e831d1a925bd1e36e8fd3d669a8cd89687fa69242f8c785ee0f2f5f2c8": ["dayanzai.me.", 0], + "997b6db13257dbd55298ecc41dec5717291617843f0bb87adf797c1564716cfa": ["calcionapoli24.it.", 0], + "99838b1ac99b03355f47d1fe35e3d55d5d548ed3d53bf1b1233662f74bce82a8": ["yandex.", 1], + "99844c144eca84f4d02692699b48f767acc9f3f999e0b09b116625b02a900787": ["laccd.edu.", 0], + "9990d6d4cb455726f784d8d7f56bad65c653bc4e8be3064c6f7c0cfe84d13ff1": ["yunzhan365.com.", 0], + "999131a1c2158980c568b99b459fe3940e3ce0108e188e79999afbbf64cca4ca": ["betano.com.", 0], + "9998546b0f8df173ae920bb684b32ccd9e76bfaa61b2a391b526be123cade237": ["io.", 1], + "9998e37c05ba9921e8695c34d3e53dd92ed3031e93450b7978e85b00bfade760": ["meteoblue.com.", 0], + "99bbf9a966f84239bd17b0ad9dd33586911a802216b887cfe19a8d16e3910f2a": ["planetaneperiano.com.", 0], + "99bc52c0e60a5b6243789a936aea1612de895fb056f6d179d739af330e6cdd3e": ["xboxlive.com.", 0], + "99bdd1b5d7eacfab1e61d8471fe80dfcb4f2e1903433c3b7a4ffc2aa7f8122a1": ["mov.", 1], + "99c76b3e3fbe77f7929c18794c6baf2f53e56f82c641800a0d6e04674504875d": ["cyberpuerta.mx.", 0], + "99c961c7d553079febbfd31a8b002a050be14f9f19c8114baf924309f8b3c3e8": ["mlwbd.help.", 0], + "99c9fc766b37c782b9e0e341f01df5e1114c3778646ed7db853b4d7e43033764": ["mobcup.net.", 0], + "99cf70b78b835e29998785379d5ef26ca00b18320360460f71ecff0f31ffa26e": ["tawk.to.", 0], + "99d260020d8e7d5aebb11c7d643525f4b536db4f29ae5676aecc6a12e1aaad7b": ["network-auth.com.", 0], + "99d5738bdb6173043e21599011c69ed2ff9e87563fbff705dcbe21dd57568ad5": ["audiusa.com.", 0], + "99d8d21371071600aec5e51bd92e07faaa345380bb8bdb3190ce8c5715113b69": ["online-red.com.", 0], + "99d9e920d0f8aa654e4469ebdc47583350826bd0331f0662e21089e2cdd97df6": ["drikpanchang.com.", 0], + "99dfd4982cd30f2e31d5bdccc1a602cc4c035d75f6125045728008dc64b18309": ["kaufland.de.", 0], + "99e01d8bf5983e8e1e1cd3bd25b34a8a45dadcc7f341f571dc585fe631de1a29": ["picclick.com.", 0], + "99e06104175608b635be7f0a565c4f3bc1bfe498391481bb41bfabce2299294e": ["dominionenergy.com.", 0], + "99e1c11cc34524500d2943de599b4c106111a5bc2e47a3242c26ddf1dd09564c": ["teachoo.com.", 0], + "99e3f413752eaafead64324a03d451186981b7f4c723a6ad8bad38e110e1129d": ["maharashtra.gov.in.", 0], + "99e5ae0a770cea32e2c3b4bb7c79a326ee073531aba211234a4a8a75d70921e2": ["keio.ac.jp.", 0], + "99e7485f53eb929cbc28e15a17ab2249eca76a7de67fe07e88bcf2b6296fba50": ["kvsangathan.nic.in.", 0], + "99ef1fbfaf5c05fe66b25e58ec8e3ee32b2df589c692ba71c9067d6a65baa9c7": ["techweb.com.cn.", 0], + "99f321ad258230db50cf49476749278d3b0282d6e763a009e14b4a2caf5c8dca": ["riyadonline.com.", 0], + "99f62553b09973a109b143a2101e29c0f006114b9cd548459f706a0d33f2a408": ["indiabix.com.", 0], + "99fa6691fcef121a01cbc2fa7da887d5623f0a3c319d053ecabfcf141ce9eed5": ["eis.de.", 0], + "99ff4d35987a9fa0fc07546f015d1a9752d662feb8677434b7054f0b3a241ca9": ["neolms.com.", 0], + "9a08310a112a9491f09e69d40993616da0e23eff231915af7e24851a4ce4e951": ["typeform.com.", 0], + "9a08a713fda5c79454bca961eb7df2beb2a7809e46cb66b44a230a7a26b39e78": ["wxc.edu.cn.", 0], + "9a0ec3fc9f1e5053e3c5fa186005f73f789b4212f1f4841c7c84289327dd5f6b": ["flexoffers.com.", 0], + "9a0f065b3ac48f023f84b129015730eafd746ca4ec7ee79fe3d82eae158d2483": ["199it.com.", 0], + "9a162eb9b0df637621813c4beaa4be6acf8e1acd9078532cabeb4c35a3d171cd": ["itglue.com.", 0], + "9a17750e425df4ed0c872180556d7cafbbae36fd4fa5e28b7f8c082b29693f79": ["sayidaty.net.", 0], + "9a19c87b80cb39ffa7adeeb0670a88e924448e3505330653a732f9b473bbe9e7": ["ismedia.jp.", 0], + "9a1ad493b0ebac1e33b45e31a0af4b46031839affd6b2326c358cf9819157a4c": ["gofile.io.", 0], + "9a1ae5dd062a098c10bdc572045583fd78f17580a1e396f61355cdba0e2efa4e": ["hessen.de.", 0], + "9a1c048a9eddf079cc6466782bab70002deb58788dd69b95e704ae1bb2701468": ["payoneer.com.", 0], + "9a1d0e5e9cbf6dae78c5565ec65c3a6f19211cf583934ef3d674d4c2b4f79e24": ["pics.", 1], + "9a1d19c3b6ada4128ae234c0dd39e6a3b926b6473bb656a310ba96c4c0dc6c95": ["xn--fiq64b.", 1], + "9a2423049c4f63e8904611b280b9825ed052124e020a14964904362a692b9d4b": ["bv.", 1], + "9a2806795f8099847f5d2c78a505e442eb0f2825888558be33c5d770aa2fbdbc": ["sheffield.ac.uk.", 0], + "9a3058db1ae27cd42481e77fd08a12ee8b5f52071f43fde5f22f4ead9430f209": ["x3vid.com.", 0], + "9a3152c0edc0eae4f89889894d85605d78cd56dd70e6d78a755e6e7c26f6112e": ["camsonline.com.", 0], + "9a31885c0e5388f66a9fcc5970363c03a0ec3a9639a8bb66c965db6663896103": ["moeimg.net.", 0], + "9a351ed82842d1a546fc3533544148d14f768bf3822a4c550791b44a3688b093": ["edf.fr.", 0], + "9a446068d22c88492fb856d7bbcb9b1f9ed7dc8992bb30fac0573bcf69f2671d": ["kebhana.com.", 0], + "9a44942fc98c363cafe22133f4e8a2ae5b7f6a792efd540033f417ceff17dd8e": ["todo1.com.", 0], + "9a4921cb878ca4d9e350016685487fcc78ddd3ca9d3d7b65d1a786b4a870eb46": ["sexlikereal.com.", 0], + "9a5313c3fbc071bf900665997d4e2ccbe31a9f734968b243626ea288ba5ca394": ["sputniknews.com.", 0], + "9a6a44c9f4538651e7ae4422e6aa93920421734c0b27d88fda44b258679e8951": ["medianews.az.", 0], + "9a733f59e0eff54ecdd9ef7c2e854cedb5cca5d929e38bb2b638e6bdd46096e2": ["tver.jp.", 0], + "9a7722f53f944ffa6cfb2db63644b8d86a7ed606f48f9f97597569b5f67207e9": ["minedu.gob.pe.", 0], + "9a7cf8c4f25feddd38b780ecb3870cc8b91c7ad6dc18cfac64e74ef2dbeed739": ["carrefour.fr.", 0], + "9a7e2961f2e53f222a895ffd357917419643e8ac20ddbbfae9519838b391bfb3": ["hdfilmcehennemi2.club.", 0], + "9a7f64b9d2b87d77f1d37f6415b415ed315baf60bed8d2c5cc518e7e9fb595f5": ["nseindia.com.", 0], + "9a83a8e5b276bc812cae91f50dd59e5ba7cd8d397eb6000b3ac24ea6dee39af6": ["xn--80aswg.", 1], + "9a8b5673e4636ce330f471e8a4461d1a51f610745cac4542c2b381c61d48e148": ["dz.", 1], + "9a8d131f1e6c40eee77d7ccdc5951be86efff78cc3a32fe9b875ea4a85d2c40c": ["tvtropes.org.", 0], + "9a9556d4959b750ca4dbbe9e60b6387aef53c51df0e4e75899fabdb9c96bfc5d": ["esunbank.com.tw.", 0], + "9a988af903d2da9c72903da45381c22bd87918d33a837b769e83bc7f0c89a55c": ["dom.ru.", 0], + "9a9f1d4baae3fde03792114d2eb4c0ccaba38d58feb8b1a6c526c8f14de96c74": ["r-project.org.", 0], + "9aa72b8eb2ca06a6273fc835af0df08dc6ddf1e36881f38b88cb350d35faddc6": ["dfiles.eu.", 0], + "9ab1d534231562d992c48246d1a7092de19e29db54d421b3dfa9df1b0037cfb3": ["animeflv.net.", 0], + "9abdfe21add09d3b2c7d2ac64931f23e38cff73dd807ac30d5ca5dece1fea90c": ["wikimedia.org.", 0], + "9ac1fa2f66eb0e322c87f62001c4cfa1fee09a5ac500e596853d4193d8faa242": ["invisionapp.com.", 0], + "9acbc67373466162d9961aab3d14153b498e613fc17aec58065a73f32fc21b82": ["lyricstranslate.com.", 0], + "9acfa7d8beb5d3aafab24445d5c0e249eef17b60ad7758f50e043a3150ffd157": ["profitcentr.com.", 0], + "9ada7a9fff915aa5cea7c6e3565f5161c0e5215c262e5479ad1aa4d8a7d75d81": ["prozeny.cz.", 0], + "9aded61e5a98e2d8e8a51a0b869d7cd0db6337a2eee294fca811a7fea6c96221": ["iprima.cz.", 0], + "9ae19d00f5934963e5076afc15546e76bafea9b8d21561f05b2237de619e5116": ["inmotionhosting.com.", 0], + "9ae8f3aec9727384b07ce1bbeab073c9b2f9bbb04b0310484a41695a62b7f22f": ["xn--3bst00m.", 1], + "9af396ce5ad63ea56a3b55b6c67ae406cef3b635b00a1491410308226184d8f1": ["bahn.de.", 0], + "9af9cf20c862bab67413ca9d2df03ebb96a5387f78affcfcae5f078b9f5854d4": ["remoteok.com.", 0], + "9afa7756bd1b2a9ec6c6e49ce77c30e83e0260f433005c6eca71b11df62c7f64": ["warbyparker.com.", 0], + "9afbcc709240cb6aeebc45d5baeadb6806bffe6401fa3f814d9b4878be6485e3": ["house.", 1], + "9b02b695eb6f0bece6d51e4d6ba31f7e691cb6083dfbef3f727b681a744f8e07": ["r10.net.", 0], + "9b03177d068c85187fd910ca7fe1eef743671b37c3fc33024b0e37362bfdd3b0": ["lindependant.fr.", 0], + "9b073c5622ed28ab4ae03afae1cc9bf7dbaf9bfe5ec00735a374d213eea3ef3a": ["asanpay.az.", 0], + "9b0b944aa9e26b1c94918f09c5c2ec5f4aaa73c5216a7a2d00db802af91c08b8": ["xn--q9jyb4c.", 1], + "9b10dc478180210a0f1a146202a42684c34773364e5f74c196ed4d01a9997057": ["freshworks.com.", 0], + "9b1235502da47dadc60b379a55ff14b6587d777b7d48b2538ffe840bc69bd233": ["native-instruments.com.", 0], + "9b1377561e176404b34b300a7fe852e58a4a7c918fda7a41e0dbd022d52ace64": ["adsco.re.", 0], + "9b1f031c7974c0ded16e0ce3e2a83c6962a4fb97bfba7c34f031f3a6e24a4260": ["e-radio.gr.", 0], + "9b269c5e6011c6bc54d2b29b6f30ef1948871c27cb7c17125db37edc0e912114": ["analyticsvidhya.com.", 0], + "9b325c87d74d6ee19a30d0691702d4119049a2a6df5de46b5ac84a8e567a27aa": ["wallstreetcn.com.", 0], + "9b384c0245af1fbb1eb5d35e6421a2938323bf60d08afecf5c31e31fc09f02e7": ["paddle8.com.", 0], + "9b3a44890ff406cf330bdeb00d37b9e49264ab64a6a742dd697490156942ba11": ["bjnews.com.cn.", 0], + "9b46a644706c2a6caa34bb94e289711b3dcb580a0cedff44d4ce822d89cf1dbe": ["porn300.com.", 0], + "9b476f6f82c6eb14546636b47cb9aa119b5aede0a31acba0c97cacd799480e04": ["syr.edu.", 0], + "9b54db9d6dad47ea29670eee1a4080cf3fb421a60f5b784a3aa86a9bda306754": ["calendly.com.", 0], + "9b559d39442bb91c0df4baad0eeedd7e8d22db7059599f16b2bf4a1385038187": ["scandalplanet.com.", 0], + "9b5e7816895fd2a71278b6c2638be99a36beeca23793a584bf389d354b76e413": ["demorgen.be.", 0], + "9b631d010b4611b9fab2208882b894505f86d497e1229d1ae4a5b131a6e9ae16": ["starsunfolded.com.", 0], + "9b65789629be7e0d7fa67c2c672d62ac655bdd75ad5e66e81d08b7c6170e1ae3": ["expertphotography.com.", 0], + "9b6599af4dee2145a939fa94171b995ec97478444aa59d46be838a0a0c3f0ad7": ["meraki.com.", 0], + "9b70c476b30dd3c35ffe95fb4fc6a79256675f45563a5a036f666c26f0e86ebe": ["haber7.com.", 0], + "9b72db8aaac477479a79bdb070c41b5b679359d3ffcf1ab4a88d7e50e19697f1": ["flvs.net.", 0], + "9b73fac1a1f0b46672bd6feac8d887e3bcfd85a974d23694ceb76aeb953b0841": ["marthastewart.com.", 0], + "9b86ac71ae12ff1f5802fef636a577b70907f594360733238a63c72a17905177": ["quetext.com.", 0], + "9b8bb855a15c83baf0b5c1965d5e3c6cc26c53006674fc40e3bccfd5bb7a402c": ["rec-tube.com.", 0], + "9b902a8fc0a746e78dacf504c9836dc861d5259a02deea1227cc47c41a9a28e9": ["tportal.hr.", 0], + "9b906a1b54ae91a0eabed47b112c3626f4fa3b3a1f5f539c7cc65dd51bfa0097": ["tse.jus.br.", 0], + "9b9749421e158b7f39702a7151bb91898c1ad0a58094f583b1e03d855c4bf861": ["tal.net.", 0], + "9b98e5e92f1d535f16c122572768926d2dae167369352ef8ed14523ac0c8743a": ["nearpod.com.", 0], + "9b9ac3df1dfac98e940750e8b3c16964ede07c910cbe7fd377e42bd5886ffe28": ["tmdhosting.com.", 0], + "9ba16bcb361cf075baa4db1c4268b3e8391b3492da39b9bd3b021b498712bd6c": ["uukanshu.com.", 0], + "9ba893d2703878584096d08a7509cecf3eae4b0756b08e861f260a8bd6b1271e": ["oreno-erohon.com.", 0], + "9ba9d53452c1d2a7d530685705709bf1c305c06359f0e008015ad88848e72019": ["classdojo.com.", 0], + "9ba9dbddbb7922cdf10fb7e01c1385397652b1a41e23ecf2ffb267d2306914ee": ["chrome.", 1], + "9bae70584738fe389548fe98aeeb7ecaa7b58465ac91f6854e09f8d8d00ca14f": ["rozetka.com.ua.", 0], + "9bb4e4d48465fbc25da6a9b798f93e107587310dd1d8cb1627cb8756c92b16c1": ["dvag.", 1], + "9bbc8b5fae08ab2cc3e8399debf431dba5da2526b5076855c20cc919217015a9": ["enbank.ir.", 0], + "9bc64ed82d54c287ae3efeda68058c2f6423a760f892f620782384401473e2c0": ["yande.re.", 0], + "9bc963acd918abcf928f87ef9a80182ac6d8d5ff8b4794daeeb991e0fe6d3ee7": ["vercomicsporno.com.", 0], + "9bd7dc296b935e79560c965d4cd26051a3b040f0fb78819d29fa508ba94560b4": ["temp-mail.org.", 0], + "9bdc88d9282586f81fb562413abe48a24fb20119ce7cd9332130cf53a4b6edfb": ["ndrc.gov.cn.", 0], + "9bdcb65720714be69c84aea3bfde5096f639206da27e4ba66118cc494aeed5cc": ["vpnmentor.com.", 0], + "9bde5c0df0099710199f1b03973d50f64a6c0d36b98acad1c9ca9197efe3eb2f": ["ist.", 1], + "9be547f8417d331bcd6f3b1868e4531eb5c5f152c1799b1e74e7c66fc4635bb1": ["contractors.", 1], + "9be86399991f41457429772ca1dcce425bb87e46f8fbcf24eb4da454849fe8dc": ["commentcamarche.net.", 0], + "9bea1da0fddadb2b0b424fcc71e3ed63281704b21d5bcbc2bd64d68e8a35565c": ["ostrovok.ru.", 0], + "9bee8919816c39740677d2d21961ce8490b8cb8051633074a1cc78e0703aeeda": ["pngtree.com.", 0], + "9bf6489fbb3931c03003627075c266db69c6132997a5dcde7ecde7ffe8909602": ["torob.com.", 0], + "9c0840fe2c39cf0d8397b39cd0bcdb1ff5007d1bcebc6f9656dcd2ee4f7faffd": ["baidupcs.com.", 0], + "9c0df6538cf81ee5a9c2696931de42962764351216c9206e88d1c774c2adbdac": ["kupdf.net.", 0], + "9c10492c6ac3a4690d8332364c460485691facd3fc6ad19fd9ab64b905017196": ["dogfartnetwork.com.", 0], + "9c10e27d3d8943eb8aa4fc8a1f3ab9785c9ccc234c14122048daf907e62f4981": ["myer.com.au.", 0], + "9c17edc0ae02357db42989d3e8b3a88089ae8d7b9b9b7d4ff0b01532536b6d60": ["netshoes.com.br.", 0], + "9c1abdfd60815b2035195db391eecb699b48fe06c1aa3e8e214e3e94e9a19281": ["publix.com.", 0], + "9c1c3b5be9adeaa7e29710c1d9ec6679eca24b0adebe5d333447e9809124207b": ["mlb.", 1], + "9c2235511efcc42ca0026d2bc9216dded233c5af703bbf70aa12c9fa057709b0": ["jiemian.com.", 0], + "9c22af2146fe6d3e608d82c419e2ac6dba52f52a94e3e331f63ff968aa0071d1": ["doramy.club.", 0], + "9c2372c5e98ad2f984973204e7ee2662117612cb895861bad9e2503f2a7dee8b": ["shein.com.", 0], + "9c2ad9246b241e362c85e9563190a4a488fa97ae34a3e8d57a101f51693a178f": ["iheart.com.", 0], + "9c2bf973f39da4ba2873a6f474f7afd30979e8fc7bbb5d90146b2b38d0598fb4": ["vivud.com.", 0], + "9c2d6179807f9007d92d8dff2f61850a83068be398b42c3e56e021ef39ad4967": ["net.", 1], + "9c34b84ddd75781ea6bdb43f52cd5dfc58fb05a50c7ee0d4356c470ebca504fc": ["classlink.com.", 0], + "9c3a2b812aa1f54cd622c2c70453b62338d3ed8ff576c3a7545f1f512a2ca6b0": ["jianshe99.com.", 0], + "9c3e84a402e918c9541981b587b48ed1e044bd4beb3c71185f79fab1d376352c": ["mysqltutorial.org.", 0], + "9c46fd6d267f2fa562309b882aacec45af274118ab3fb84841479cdcb04e9034": ["mps.gov.cn.", 0], + "9c477889d3d7583b796ec2dfb516bdba5d7c4c29568fa6075955ef90af058ddb": ["france24.com.", 0], + "9c47ef37a099dc36a0a1b25c6923efc3923b0ee1bc688297965ef038e52e9ef2": ["cifraclub.com.br.", 0], + "9c4abdf15a060b5a564781e32ef6ed0523a970ddbf1d9dcb645f5c1e86557d30": ["refinery29.com.", 0], + "9c4c8caf89cd91456f32b4a371cc488e481a622cbab1c60ef9520559231dda0c": ["tokyo-sports.co.jp.", 0], + "9c4c943cc960e40897a720a6e5d312b1346c200489629e4d48c28731608a352f": ["envatousercontent.com.", 0], + "9c5009e131bd9f950465a4744f36b856ae359a6ca10dda1eb008c5a3584b5f58": ["bestgames2018.net.", 0], + "9c535deed1831e796357c3a490d5bfbc213842137d3cd6b1670e208995c919f8": ["mediaite.com.", 0], + "9c58c8db976cdc071057a98f9e3cb2721015d2de8a1b36487761bf0f702617ff": ["powerpyx.com.", 0], + "9c5e244f774632725f368beb61cda1f591d009ac87a17f20dbe3aaf5eb77a3d5": ["platzi.com.", 0], + "9c6730a27e5445449dac53ddeaca868f326bb2ac4f06546b75534134fcf98db8": ["kitapyurdu.com.", 0], + "9c735f7862f3424eb390e575a47998f60e97c8305e637efc9a645b4ad8feba8b": ["paylocity.com.", 0], + "9c7df189214d1fe4ebfe0c0895362b9068ff82a04272f9319ff81ac58f7e241d": ["drive.", 1], + "9c819d356c9cee542fc372d7208b5e5dafc5e4fd58b3d354f4f570d43fa18823": ["ifunmac.com.", 0], + "9c85ed7c36dece217d5c75b28c188a9d561c41c6bc00d473965be34594351d69": ["pochta.ru.", 0], + "9c89e657d2cefc3f4418cc8bd75e0fc2a361c32bcbd0ca34825ca764b8ea2dab": ["elcomercio.pe.", 0], + "9c8c66ed3b5fd11adb2dd1b5a8c1d0f0d3bac5feb31bc3d3289f662253ec2bf4": ["cn-ki.net.", 0], + "9c8dac92d59ef4c858a9203e4d1a9e2c66d9c07afc56b57c7f5aea5e7b4804da": ["fnnews.com.", 0], + "9c8f73596a1c6a313965ea2a2c7825290fbbb937f94fa32f4cbccface3f1df23": ["mediatakeout.com.", 0], + "9c910b1b99c0fa409578daef11a899400e06c81b2ad94a01f32bf9d9ddb31793": ["whoswho.", 1], + "9c96cb80215daf2335f11a74949d4c4c89690f32b714423e40e13ed4aee23ac2": ["forsale.", 1], + "9c994b4b8090a5394fe182a3fced071e028c5895036e7fdd4fc8b16bccf27886": ["national-lottery.co.uk.", 0], + "9c9dd055da6a7b400e1d7a9a6615f2696a4e99693bcfd5105321577a15c1783e": ["solidfiles.com.", 0], + "9c9e75edbf083dad891cd6bac7fa6e524a72db237cc7b17248bfd94808f37c20": ["biglots.com.", 0], + "9ca20bba5c95784f358a36023f62f3bccd003bb544b1be3702ab4e0ddb6b1d07": ["huanqiu.com.", 0], + "9ca3a97c96dc6f27bef0f33c36af883c8fe600e3da532b1aaad99b5ac6d93b4a": ["zooporn.show.", 0], + "9ca6b1698f5d15c8d65cd8431289f84021fa5093c062e3e82947400a8f18cce6": ["icai.org.", 0], + "9cacbfd189e42984e2f62a6403380011f17236a01dad279e98b6780d419cbc92": ["fantia.jp.", 0], + "9cc304925499ebf5ca5991df591fe1f0650e183a46a23fb86b24029f3a787c05": ["osdn.net.", 0], + "9ccc184d0046e770e61c58e394cd55748a8302010b9ef4ecef7c343e8775afe9": ["tripsavvy.com.", 0], + "9ce0a3251a092bfda333c7c85bd990bcef6c5d97711b68449b79b75387738002": ["f95zone.to.", 0], + "9ce265473ef6c7854ee3806e4a0c03c0e737103dc99846c05af3e7fdc30eb232": ["got.", 1], + "9ce63625561f5d4a37958999a56c70d9929c888a339ebafeb6bd0e9a40f43660": ["site.", 1], + "9ce891b155d080a1752d26323999df5f269eca6efcae720ea781ad7f6fbdfadb": ["ekb.eg.", 0], + "9cebf1fde798bfa8f90ce9159cfe4e0348e86664a266cb4fa91db20583916fec": ["berliner-sparkasse.de.", 0], + "9ced586f01915a33bc1060765f5ba1ea72e0d3dd5100358c3701f88e7a67d7d5": ["rentcafe.com.", 0], + "9cee645df4e1a1eef8a107d91f0562fb7275abf2923de3d96ddbe1fffc708419": ["events.", 1], + "9cf191ac8bb0c66193908d0c176dce93f380db41e21f46e25a69dfc6b37da7d7": ["jojo-themes.net.", 0], + "9cf1a9a51934ed1ea73db39924e377ec24119036a349dbb3a8aab5ca1f8d8498": ["euroauto.ru.", 0], + "9cf27184b0eb9156b61d045d98f6ab32cabbea567cd65fd9c5540ccc27892b16": ["offensive-security.com.", 0], + "9cf2b7bd1aa95d4b9f0b33ce2c9f2aafd1b9d55e6d7ba181d4968fd107bba20b": ["okko.tv.", 0], + "9cfd3c3f9a8ba180591c16531e65274d90516c4541fae06266b2cfb823e53672": ["appuals.com.", 0], + "9cffa0cc6f336572749df91bb335f6b639ad31b7051f39d2cb0266cd079062dc": ["utiitsl.com.", 0], + "9d09c10419f88ff5e65b8ec7e954070f91c58ace9971f65b8308efc6c9a0ff42": ["sciencedirect.com.", 0], + "9d17fcfbb375529102e108514ba30e2271009dc10d9427e1707a27f6ff3be506": ["bbb.org.", 0], + "9d18d027ea29c910aedcee305bb75da4ec71a83b51772f2816dfc7ca00ec6a56": ["directadmin.com.", 0], + "9d1fdfa207f7ee580a776dcd7b1682dba8ef0c61c670121d97477794170e61b0": ["w3resource.com.", 0], + "9d2c4f93901dabb6b35521465ab7b8df50e159018162fe40a4ea3ba640c7adfa": ["imobie.com.", 0], + "9d2ee7efbdf852d416ff1b6ad3a028867e779ad4a685d4ce78d171ac8cb0e89f": ["cpagrip.com.", 0], + "9d33f6e28434323183c4ac61151747f0b688273243306d0e38e118ad05eab84c": ["mlive.com.", 0], + "9d342b4e22a33d5af873067cd1de3fe89ac992851f26e335a51fbc061d54893b": ["spunteblu.it.", 0], + "9d3c762d366a0e3971f1802be24ee395932a83cc32851aff8b23c3b2883eaef2": ["fanpelis.la.", 0], + "9d435e9e7d664c8625a89bf9be4a4785d77611f27c92d367173a86dbb82ecdf8": ["opentable.com.", 0], + "9d482ab98f49ac0dc16ac14ca415c0565d152983242b3433da268ee0a35ffada": ["pluginboutique.com.", 0], + "9d49283fbe93af6fd23f16d11d15c3a83376fb1735b45330fe7f5cfe9faff254": ["znanylekarz.pl.", 0], + "9d4a068073493a27f4a175d67744bcc15dd673ab8432b63784d2ddfedfce449c": ["bet365.com.", 0], + "9d52a221150b2ed02147a01fec634a277cb1ee88d61a825a02533736ea76d9b4": ["lboro.ac.uk.", 0], + "9d5e47c036d3074c7a1e9fa0c04a549fafe631544208024035de858cce26c9cf": ["coinbase.com.", 0], + "9d5fa16a0972cf686885b3c87b339b73b55466170686c02a64c52c9de9a082a7": ["soopat.com.", 0], + "9d6361951d760545efc6a6438344a8bb5fbd1e3710ce1b76764ea2f59e1723f8": ["realme.com.", 0], + "9d6eabd6d91e2c125e1575072fc038da42f30b922ed78f147598271fed472c96": ["mountblade.com.cn.", 0], + "9d718843aa8d5d8cb8c24651cb3b5130113a8ea0b1dd841c22b1df44a523b9b8": ["dcinside.com.", 0], + "9d7688031233b27c361aabf1c730e65fadc2221dfa9b1df563927d38b0692801": ["tutorialsteacher.com.", 0], + "9d7b4f5e06fc5ded5375a17c493248deac7d94896a9ad8424eb38d461b16a638": ["atna.jp.", 0], + "9d84eb4526d2c851df75f80da42a654c3f4fb99cd68cf5b32b358729dfd5996f": ["brightside.me.", 0], + "9d883de85a30b858297c0cf2f1c1cebac74a39d2ea6565165a4a1b235e50a484": ["equifax.com.", 0], + "9daacb2b3b6d87509ce9a2e7cdc1eba8fecbaf50e1a7155a05a25f19643046cc": ["tor.torproject.com.", 2], + "9dab24e75e3fd2b127a4f8d29ba3af87d9b515cfd5168d1c073c07a475a6f494": ["gearpatrol.com.", 0], + "9dab39fe1e6bdf85fdafe088414733ecbacd5ee424b75e7de11fbe971398751b": ["logitech.com.", 0], + "9dad147aaf69d1e6d636ce7be160d46856f0a247c411e0887875384a06177405": ["hurriyet.com.tr.", 0], + "9db444837fb05869898c423ecf547903e2681cfd6eec56f3d976197f5afc409b": ["obsapp.com.", 0], + "9db45d61375d4a041beccd48d130fe0079b0a3016059dedc50c3ef786f34028d": ["pku.edu.cn.", 0], + "9db7f80133975589df0a605ff8adde4e6d2add5961884f33be9a24144d34a30d": ["eversource.com.", 0], + "9db8ea9830f847dbfac4f772d7600d3a97125aa6366efab06297ad64bbd27749": ["lavozdegalicia.es.", 0], + "9dbbcd1ef74d0888c9a620cc04ae3410a5137d92b4dfb6c8d2e983af52b81f3c": ["gamepedia.jp.", 0], + "9dc39aa18b4af07ffe5baccdc5d09c901a275a53f4f17b72e76d5bc05623ec4e": ["espncricinfo.com.", 0], + "9dc5a0e698958566c4eb2f163b55515064fc7e9b43a3d36d7f2d32f0ef1f9ceb": ["idntimes.com.", 0], + "9dc71c3fde88b807493be2603d3c5e76f621e4918e699c9c3df55d160ae5a8eb": ["xunta.gal.", 0], + "9dc872acd62a9908a7e8aed5d0063d2d975036a68f3a17db7291fd6d86c414c5": ["multiup.org.", 0], + "9dc8a1c809e836efc7e116da2d3c63be2cdfcd0647f2450b55503ee550d929d8": ["51cto.com.", 0], + "9dd012dc4044c9b3a13f624573fe27dfec619d849fb60cca17269c8af6dd238c": ["online-filmek.me.", 0], + "9dd0c7fa4a85081aa9987a38aca95e5baef6eeeb91941dd10d6f083fd365889f": ["xiepp.cc.", 0], + "9dd1f0ce9145411a4f2537c96c910b5275de112fa2a3fb55dfe168b99e43a97d": ["torrentdownloads.pro.", 0], + "9de382fa97648cd8ba52991589bc6574421648350212c4831282a0b16963f8e0": ["phun.org.", 0], + "9de4fbf5c22cdc7245e2513e25f50a2c00553e8ecafd2d59c6d2a2a0ec04d8c4": ["fundsxpress.com.", 0], + "9de8eac0818e56fb9a1d96312c73a122d69465a1ae9e0244950e0c1927c14ff7": ["mm-cg.com.", 0], + "9df0cbf5d2b98f721c66fe89b1e340e7254e68a5ac31ad44027c9f7f65ff4f4d": ["gnome.org.", 0], + "9df13fd55d93f173e6429acd8182f36461e57e09e91b86ec4963563ea5b88fe6": ["shaadi.com.", 0], + "9df3490e5e69ccc0f6fa82de1089e9757dc5832b48b41bd069e13aade63ce7bd": ["stocktwits.com.", 0], + "9dfad66442849f4d3b43e2fbd4bdda098e43a2d84ca89fdeab4b1b364599034c": ["adaderana.lk.", 0], + "9dfeb14af19e00e1498793a50772f0d11355064ce4694bd235c726f57bba475d": ["saveeditonline.com.", 0], + "9e050e91b0d9571849c3208ea67e12f3f1af350aefebf7c27851dd722fb619a2": ["ethiojobs.net.", 0], + "9e0ae54eb039ea529ef72688c5445df02f2a74e3108a957f9b75d2673e395744": ["petco.com.", 0], + "9e0c06d9b1ce6e12ef84feff003490c826016478eb1eec2c8c704986a6c95723": ["mp.", 1], + "9e0ef5739c3343b565024e3bd52e8e72eb64421165ec10e92be97dd09697b4bd": ["gaokao.cn.", 0], + "9e1e107190ac15248150fbcbc2b46ee02e2273b5736421c51d735007b75798bb": ["javlibrary.com.", 0], + "9e26dd3b1e537a1a2fdcfa94a7ae5b4c54bb7f5b1c228642a5cc52da9571d1f8": ["maxlifeinsurance.com.", 0], + "9e2d22d039f8d499e1f218a6e881ab2fd972d3bb97feff3930e3dfda48a7556f": ["kuaidi100.com.", 0], + "9e30bdb554f75add3600b1c5718fafdb016b0ef2512bcc177beb4078771f46a9": ["kn.", 1], + "9e31d84e2509618499ed6791109ec9ba29ab36a39c97875d9bb8cc588c37e789": ["wickes.co.uk.", 0], + "9e360757c099f5fbe2866a456ba2fdd0bb84726b1604901403f0605aaa903aba": ["onlinedoctranslator.com.", 0], + "9e3f3559fedf22065f5a2a3ef177fce1d07eb643354d8812b5aec84cd30dd679": ["xn--kcrx77d1x4a.", 1], + "9e44529ebd3231ef4416c98646748a7e337e20e635189993e32ada2bdb55d641": ["pnc.", 1], + "9e472619035405a7d21bb7f1b358e79e1b391f2d70af7b1efb6063c4bb754555": ["cba.", 1], + "9e4cb296580132c59a35e9c3b44026c2838589ff4cfbcfb02be7ffc9624d4d06": ["hihonor.com.", 0], + "9e4cf06dba89fc33189f9e7704ecf8f871ad3a1bbb8e947c46d4ec478ec656e3": ["komplett.no.", 0], + "9e57d0cce3a6d284dd45e583889cbbf09e60e7603041f648deb3877463ed5969": ["eg.", 1], + "9e58a25406f68bb0c9dde1c769e2e5086fb571a2b43103e343213448614069a4": ["coupon.", 1], + "9e5bbac3e933913b156ff1636513de29ff0ac7cc2c017e195afbdd0cbd77753b": ["pandadoc.com.", 0], + "9e5e7107c8f973c324ca5496cec9a7f2276e78a6d22f07bdb163976c36a37832": ["zsxq.com.", 0], + "9e6131e9877b07d189b60a4be412fb3d5ab0bd43932f498d48709fd754e43601": ["flytoday.ir.", 0], + "9e7068c630f01e372cf50e9ce044680f75bbcb274fbbe45e2ba0358fa4dc1189": ["finofilipino.org.", 0], + "9e741567be9e34f4bfe79094c5cafb405f1cb220babb421ee56d6aea4d16b26e": ["jumpsokuhou.com.", 0], + "9e751c76f8cb357259eb49e7dc70683dc966b50e04ae57d1ac62a09d0d4ac424": ["cheathappens.com.", 0], + "9e757e40421eee34d8613d8d4d8ed03b681eeceb09619af0a21a2de041fc66b2": ["scmor.com.", 0], + "9e7a71d3b6860b08ff65f00b4c1d893da650a21a1b8217b881cb109e955275a4": ["comenity.net.", 0], + "9e7cabf922d937d4c083caf691d2b499d10390ab6da087d0bbaa62b5c652a4ff": ["metricool.com.", 0], + "9e7cf2e18c120b8cb469d11b002ba6bbbc4b563edbe5492eafb93405fd5f6d66": ["bankrate.com.", 0], + "9e8157d05a870aae09fe0d18dfecf4ef706194b142fcc61a7197148cd459ba00": ["smashingmagazine.com.", 0], + "9e9198b1f2cf48858d262b09dbbd080cac3daea986a6497878473fac679bb9e9": ["biorxiv.org.", 0], + "9e9558cb7be984f14c5fd05217af0506b953ca2c25bcc37f1bf1351a5d17027b": ["consultant.ru.", 0], + "9e9bf10336df42ae7ee9efd390191c2acc66fc18295f93724feb14917e610709": ["xn--54b7fta0cc.", 1], + "9e9cd984fb7d77ec7c55125eab628b7d1580cf46afa5f4d7782aa887a8a13133": ["manga-news.com.", 0], + "9ea1acb7ac7fcf2568c1a06e1ce79e21ac26203ef7e7d8a8d0bec50e25dc8975": ["bancogalicia.com.ar.", 0], + "9ea288db199a89202a75fb60d6093e90c2c51b4e533e0bc88a9e5be6d250caac": ["fju.edu.tw.", 0], + "9ea3d1d2fccc2843ad6159ea20c175e71b98b933fcbba00cc27c486f1f397edf": ["thereformation.com.", 0], + "9eab65f7e01837e25111f392ca66e9b5dffc49913af04c914820a087afd91374": ["travelboutiqueonline.com.", 0], + "9eb523fea5e6f2f7cf3c5cf92298c554e970d367a38ec445e39ef3dbcffb6aec": ["xing.com.", 0], + "9eb6876a3c9e2d84e999a21a30b4e8c22a0ec3c80eaf63969073abd43765106a": ["fssnet.co.in.", 0], + "9eba0d8b2d4149fdcd7e23f6591ed71bf1aba412a253a14d63e1a0ae600888a7": ["porsline.ir.", 0], + "9ebae7064d939df05500f513b11f4c69c3f2280e008c00d07f40f4a4b81a1f5a": ["proxyrarbg.org.", 0], + "9ec336944de1e18deb9d5abf0453bf6f7fb591dcef7d2e1eda115d2caeb1aebe": ["newsmemory.com.", 0], + "9ec59404d4cb2945436ba90e7efdec87965f9f92e10327a40eb5627f627b3567": ["hainan.gov.cn.", 0], + "9ec948092e3e690e61fdc988314e722a94af02f71a7e588dfa9b4f8e4582edd3": ["jogos360.com.br.", 0], + "9edaff07316fe82c44731b6615a3eaf6e98dd70ff32605449a17aca40646d377": ["schengenvisainfo.com.", 0], + "9ee1623c009c7776fdfc77962f77d6c1f2a51d0d61d1e960eb87584514b1051b": ["vueling.com.", 0], + "9ef057a455d3f097522fec0dce6118fc9da8f70f7f3891780aa99099128d4202": ["marcus.com.", 0], + "9efce53070a82ff15d8fedb474bb8217c2c7d015d333bb7a112f633baf8a27c3": ["arukereso.hu.", 0], + "9f01ae44525ce93dd7618b9a350261c5efe27ae12f49d263461d485f04700b31": ["forum24.cz.", 0], + "9f01c20b7209fc138cbf9a109ab63f27aa843d3b7a62df14dca8ba802d07b45f": ["sky.", 1], + "9f09704a92f18ae062340b70b0fea2c900aa0b277397015c337d76e7198ae722": ["pagesix.com.", 0], + "9f1290da289056f1b904ecc30102a0d828df312c9bfbfff35368818e0e10246b": ["ebookee.com.", 0], + "9f16c0a3a5c6933a6db3d78806907fb0cf7e7e3b5b09686285bc83be582f649c": ["waybig.com.", 0], + "9f19d63e544d4223a4a67d7a2112c6b05b304544fdf5d0a727bb50794dfe58a6": ["blogo.jp.", 0], + "9f258c59df29e963ed37d82d04141132acb0466213dac817fb318889d2cc4de2": ["transavia.com.", 0], + "9f26e773f0544254c32ff4d4fd461e8d7c0155348a365a015101856bf06db995": ["hero-news.com.", 0], + "9f2845a97059e2742ed71a7f002fc46e6d20d2f6cec4316c1edeb04295cba78e": ["linustechtips.com.", 0], + "9f2b9afc37889c1deecabce7da77c8180e60f334ad2cf8da298e9d4ba8bab99e": ["rijksoverheid.nl.", 0], + "9f38b2a9aa7ea7607e561c7f0e0fdf6eebf3ca318eb0209cabf7563999363fdf": ["gowork.pl.", 0], + "9f3ed843f160cf7b2b6ec183d29bd639f10111ec27db94df56d7888747dd486b": ["ifood.com.br.", 0], + "9f4377584d91b4f30de54b88ce03e788e53fc5a17b90ad2fc545823112cd9c68": ["wemod.com.", 0], + "9f441f46b422172cdc8d3ce826e1b5aebdeaa21147b9b23b8bf381be5b03bef0": ["aemet.es.", 0], + "9f449eb869cf5707a123910eabbcb8cc78f73ee718a5c9d88745e2d18d31793f": ["pitchbook.com.", 0], + "9f49f3fcbd211f0050585c88417f47f41a2e0ad1bab218086effbeb1a26d84bc": ["irish.", 1], + "9f4a438817cbf47706e852d29a8bb60ac3328469c044c3b65af581e2ce3ff0a0": ["cgpersia.com.", 0], + "9f53d55e7826a33132677fb678cf04b1e08cb2f6518a5a9dee226227d22c7762": ["farabixo.com.", 0], + "9f5696f245d1091b4efd9917484b1453279152b112d3a9ef3098aebf9bc707dc": ["mahadiscom.in.", 0], + "9f571610b81d1ea8107352f95c3160ca0ecfb9be0ce06b92a4631498396aef01": ["hunt007.com.", 0], + "9f5ce7f5780c71f4fd247793a30d80177e9da34ad86b4b6f344a31cda0925d29": ["nikkei.com.", 0], + "9f5cecb66da8fbd55b599c09ac7776b03de9fc22824447974c193f697d77e8da": ["file-upload.com.", 0], + "9f64de6653c1c5b4e23c4c50d626763f0c1d1341df780a4e5aed9029e98c4b9c": ["silkengirl.com.", 0], + "9f66b1f6f1a05d0f432752778b856ad7050eac9d96aad607ece5585bef6e3863": ["tvanouvelles.ca.", 0], + "9f698676f3e4476fc49258abf4eafa9e9b4199f34e70d78bf71ef90fb53c4f72": ["atypon.com.", 0], + "9f6a95c304ff275d4f4b8b85850b00fed36e5425f8d298045a92fc0ae32a01de": ["receive-sms-online.info.", 0], + "9f750efcb23840985c1b022532baba186e6bcca27fa7ac5e5ae562fa88cc4410": ["mapbar.com.", 0], + "9f77bee9c1f65afcfc5a050f673ddddc94af758885d14601f39fc03d157ec2f4": ["telegra.ph.", 0], + "9f8992594e10477d3fa4b0a465bc3f11a32cdd22c6d3063cccc6c5a50b319522": ["opinautos.com.", 0], + "9f8dd3bf427960d0df8a141f2b36b74393f2a05994bda341a7928ac1c39b3681": ["prof.", 1], + "9f9497fe4e8e90095f9ee17f91e2657ca4a4f824ce73e4d6455538c3155fd421": ["javynow.com.", 0], + "9f9936abdfacaf95e0dbc1190ccffdb53378cc555b93116a4e73957d2f119c12": ["gh.", 1], + "9f9fe1e78ee03f5f48270d04a2ac6f617cf6148b2f824b31fd0e783a1c9f4462": ["trendyol.com.", 0], + "9fa2284e4880edbd555ea8e21d72ed450d6a2613bc92753ebf766c786a7c7c46": ["newrelic.com.", 0], + "9fac8c092c4afa41608e912c5b272e20bf4f88260192a56213d02479c9c0d978": ["planetsuzy.org.", 0], + "9fb35eadc71325445da7447db4d19cbfc33df4c38b1616a4d824d2e17ec089e8": ["maimai.cn.", 0], + "9fc1da125bdfa4b93ed4591e6dc6239502e3a56129d78599e88287a13de88111": ["sprzedajemy.pl.", 0], + "9fc48ddde4ea97b2c04e58e36b802bc0cecd2f959c078871d38f596532485294": ["pof.com.", 0], + "9fc68578656b6291b3654e531a47d18156356facd84d882c822554924c34b539": ["mint.", 1], + "9fc8cf16afbf91fb89c47342163c9f2128254bd286d78e3a0536e9363ae9594f": ["namshi.com.", 0], + "9fcaa30874f5fe5513c99206cc23c41ee05c3d5927266a53d29431330e79ce4f": ["btdig.com.", 0], + "9fcb289f606759264a529d02b35a2b6d9e82506b32c562b14ef358ca848bf468": ["bringatrailer.com.", 0], + "9fcd5e9054cd1b6e703a5bad62285470a612c4bf95c6ba811e3efc9372e9a99c": ["practo.com.", 0], + "9fce4a5bdf4472c6805b7ab89fbcd1f351b600aa440417ec86014e32f5c0ab05": ["drivereasy.com.", 0], + "9fd71372413ac7e86f304a9f5800ddac91a40ee28c1932e5fb341c591df8bba6": ["vetogate.com.", 0], + "9fe52ff6dc872f64047b89c66e4d6d9b2ed25fe19ac5ca0eba3038ce7228034c": ["tkmaxx.", 1], + "9fe88636be476d1d3d75da313aec2b4180a7c5e1e3b30e63f49986f03bb602a6": ["squareup.com.", 0], + "9fe9d47f394358573d6e6460a41979f0214737880c7232c6fe9d17317a57edd7": ["codebeautify.org.", 0], + "9ff46c25690672522c53fa25b2279f47cd278c52f3b124d6c1a0a02af58d33c7": ["mheducation.com.", 0], + "9ffe23323996c319641b39b11e88b568f76df8764df06438ece773e8b3cb5690": ["getepic.com.", 0], + "9ffefd755e2be7991299ec66410331ec5ae2a3adc97d9a13eb7ebda0fb27683f": ["hiyobi.me.", 0], + "a008c7c4409849200c1495f8b747796f1223cb657a2a219d9e77d50ae49bf867": ["javhdporn.net.", 0], + "a009a5120fdc17e5f4d20f5d7ca8d0ec27d2f8d7d22f6c8a672025c35d3f79c1": ["loans.", 1], + "a00c184e56ea3cff8e1baf879177de61cb2b5e7ce5e94f38b3a2ce1aec686de9": ["sysu.edu.cn.", 0], + "a0119817c513980c70209491cc94225965d26a205d629d57e7f5b3fec1b20159": ["mohw.gov.tw.", 0], + "a018aeceebcc008febf580fc8457cbc6f54ea6cd4a761de0b687c30527e8e908": ["ome.tv.", 0], + "a01f0636034d890efa86c4586cdc12b18485eb5de1373fe38e70d03dfd05ac66": ["na.", 1], + "a01fbea6928d3ae9f4f0d5b3ea12a217bcfa399324935eff724dc796c17645c3": ["newsweek.com.", 0], + "a0243ac2d0205686015b93c33278ded1e965b5fc46aa141763d1adb5144a5c3a": ["hangzhou.com.cn.", 0], + "a0297a50eba20a81bd3c841eee29bf4e1956219bf88a8a383aa458f0e6b284b9": ["seikuu.com.", 0], + "a02d94d8d89e811edc2b4a73e9a26bf3df86b17ab6a5e629b7007f1ae5747624": ["sciencedaily.com.", 0], + "a02e67fd96ecc1365f278a57b5553e201215f5457321ed1c07c2c54fb3ea2503": ["maroelamedia.co.za.", 0], + "a02f6495e752cb1aefc73f328bbef2df6e1ccffb7496f8699529df247e55725e": ["myhermes.de.", 0], + "a0371cb6da01b61cc890b301cd083d6ae694e6bf0485570706c0bc791b3e8e1a": ["qatarliving.com.", 0], + "a03764431168307f5c1b518c1eb23c2fd9412ba37cb161093195c1b05dbc6b3c": ["bookmeter.com.", 0], + "a045b2d54af7d72a25c5e8b2749424962e62ce29e59f5501d47cb7f3dacd62a9": ["zestmoney.in.", 0], + "a04ccf3ebb84c27178cdb9dcd0bc096b9267c4f7b598557433d4a19217b4be6d": ["tunisia-sat.com.", 0], + "a056c52b29428dac99f1f91e3d54e98a7b01c934a7f2c537bd8198a15669efd6": ["cpuid.com.", 0], + "a05950439f1c0cba1bfd1b92bd73e8caef4adc9f0113b87489c5a3d7a3bf6b39": ["hot-sex-tube.com.", 0], + "a0697d96d719cecbcc5b36edf3cb5831566a1c4692c48e224b19d9c2de2c7816": ["sohu.", 1], + "a06cb0209fb518a631fcb468a55a7ae7bcb15d3cac8e23f0c6eee800efd6df9f": ["tiava.com.", 0], + "a07759f76c62a7294eb90cc096b8db65615723324ac5f1e2f9f9c550b585bb27": ["win.", 1], + "a088333164e59fe76d3163022ab7d7e876f67e720e0db55636353edf79fc1a5b": ["t-online.de.", 0], + "a08d5e36daa105d3d37413209340f7a650709e3834bc20ada9c8c9989f79bc60": ["boundhub.com.", 0], + "a09174d7166d483e60c88882d1af3c15018f25277d5e4166f0f6fb9f00e28d0d": ["sona-systems.com.", 0], + "a091f475e36d5e255c8178609e780bef0be661488d8230daec5ab3d172ee3d5d": ["kukaj.io.", 0], + "a095a5884b09242f314183a794dc7bc29a8802238c5b720aee6df61671e6cece": ["cricket.", 1], + "a0969a985e117f8b77bf3b5a03fcbdfe705461eaa6af3d844be340da25a8404c": ["trojmiasto.pl.", 0], + "a0977aeee3c6047e010ed63767516d43f33ae3ba590d67fc17ad4f6930fafae7": ["aldi.us.", 0], + "a0a28038629a6034765ad1a1da1d418fe6424142fa708f685299c416e2d3bd0e": ["mitelcel.com.", 0], + "a0a33ddea0303de35bcec028ef784019b2aa2ccb13e8ac46f764647aceac1352": ["todoist.com.", 0], + "a0a5439a5071a9f8d654b8cd72726186600881ef8a5060a57d9451794d2e02a4": ["playmsn.com.", 0], + "a0a8c3d2383abcd3012c15f0d98a4186f2ebf36d9ab96c5b3760e6b903a7aadf": ["ticketland.ru.", 0], + "a0a94eb654e8352351af9d9a90855fb770780a821c718cdeeb36ad60d86971c6": ["zus.pl.", 0], + "a0ad912ec751f78cd7b5f87ff99e4679e19ed6ed9f56816ec5337d62bdd3d00c": ["xpi.com.br.", 0], + "a0b31f764fe30970b57909732a9f105aeea4a4e2bb5f4d294ee8b22164299d6d": ["mango.", 1], + "a0ba811a13f07fd00cd7b40e14de477cbcd092e023ec5f17a2947e149817bbc7": ["mvdis.gov.tw.", 0], + "a0c388ae2829e54532fefb4fb6e9df5d0887c14b326c59aeb636112d5308bc1b": ["diet.", 1], + "a0c88388c56af3e47ad9506ce5aeb03e402ef2fba12794ba3618ce54815c4f78": ["pricena.com.", 0], + "a0cf10b7daefaecb020675684139397fba2047ea732c5df366a01f8a6a446867": ["hyatt.", 1], + "a0d266b3386b4c9c02cfff38118a6e73c71b5681dd25f3bf65739406ef691865": ["conects.com.", 0], + "a0d29fc61da711c554bcf27abae666caa863a0a99a36cdffa2c2446c665d1050": ["eljur.ru.", 0], + "a0d30c34ed2590b3b2dbc300e5601eb49adef2ed66ed751b063b374fe320b8fa": ["simbrief.com.", 0], + "a0d3e4424a718e39210d04dfe42ae2a4522798f7ad8cc3e6e2c837801608e4ff": ["tabooporns.com.", 0], + "a0d5057f23999040316b3316b74b7868899efa440ca762e161dc9ba458286c34": ["rapidtags.io.", 0], + "a0da8435cb72e8f3ec5f525b9dd46a76928d77d07eb0ed44dab7f69de672c786": ["filejo.com.", 0], + "a0dab0325183ed75cfe4c3c4384bcaf8ba899ba10984943fac405b5f690e4579": ["spareroom.co.uk.", 0], + "a0e05340ed719bfd557adcc85f3f5ef1c8587b292c86e471415763f97e9b0c3f": ["microsoftcasualgames.com.", 0], + "a0e1f8f5c8cb5acf78498f83860018bf276c57ce86042765ce29a7016d035d66": ["xbiao.com.", 0], + "a0e570324e6ffdbc6b9c813dec968d9bad134bc0dbb061530934f4e59c2700b9": ["lol.", 1], + "a0efd40431adc51725cf59f64e9f83c661951c2e8601fbf6c35517052a2e6b4c": ["bimobject.com.", 0], + "a0f4b1248bea0b4f712410b969be848863e334da4d0f8555bb2fd690460b356a": ["virgin.", 1], + "a0f599db3e07d0d699ddf5fa3dfbf58e8977a57ff616c37b4f1d7d041a485d10": ["trustedreviews.com.", 0], + "a0f9184053419df689a82164444eec895edf61784c21aca3ea35c4dac9eebc3f": ["unicamp.br.", 0], + "a1056eb82492ac61973183134fbed8c1cdca9dbe4fb9c5189b76f0735af21f7b": ["entekhab.ir.", 0], + "a1154f682d9e844112592a15533778321144650cd2a8ed764256bbaaaba175dd": ["zhipin.com.", 0], + "a122736113a6b1afa9b1fcc2225818544b4b78ed619de8dfcefe0a47f7e2591d": ["fril.jp.", 0], + "a1234eedee6df14cef5caf3e6ca2d7b8a38aa379893a5cea2773d967cd2e0ef8": ["tubepornstars.com.", 0], + "a131a90fa84b0b928377accb28a61bcbfb6daa34b4a32aafdb1f1bf758c4ad38": ["thecalculatorsite.com.", 0], + "a132317e97bb53504ee3a247ab96c404d82421e118c8cf03f2d8f6dbb95bbb36": ["animeid.tv.", 0], + "a1339cbf220d4104574b73484886ed8b4fc3b2471ff060fae06ce96838fb77da": ["moe.", 1], + "a13dc102f5bc12d74b66dc01f97b28a6a06b9f1c3a350f0dfd23609a4c55967c": ["7sur7.be.", 0], + "a146b2dd9feb85d5a7dd77fa8f523610fd370475fa3fd8c1acb495db3c7a7928": ["zhaozi.cn.", 0], + "a14856b8d9a159e3a8c41526da748c70ba4ad79d85b9074fef06afc07b78437e": ["xitongcheng.com.", 0], + "a148863d0d5e123680dbc5d0766be5708d4b342ed3ae91f2e029c2b552d14e67": ["tk.", 1], + "a14ec74aa7da3d1c86571123415cc93ea4df38e7406a9e4d9d65ec33407c5ca6": ["cool.", 1], + "a159e3c77cb17dc2e245cc018aa135cf01454a3b4ef3d74d260f126d6cefcb88": ["coinpot.in.", 0], + "a16124257b706d7792d0cfbdf6ed8af6e7cfbf3215221f0dc47d06a99b89c458": ["7learn.com.", 0], + "a16524e77c271870ee037c38f8e7206e2aeb411dcc91fbad583c20abcee918a8": ["webengage.com.", 0], + "a16a7a18ee17a1ee78e2b46f64a7c92d9a1b648108b47851234ce88f13f098ad": ["bubbleapps.io.", 0], + "a16f4d9294f3a1f70574d4f4a003fa56c5440b84f80bd7dd8a03094140e98645": ["cssbuy.com.", 0], + "a1707a8de33fe716ef4bf4b2a00f83e448402408c6ecac2c8929f650bfaccf1e": ["animedao.to.", 0], + "a172915a65f08aa8cd7164a6040c0654b1586256a41ddfb595eff9be76dbafac": ["albamon.com.", 0], + "a17e4dbf3c6b75643ddd116dcf05397a4ca8f8da466c9b8e68d9372b44780f6a": ["dafontfree.io.", 0], + "a1847b0baf5d91b14617ea2140095ed7f65bce4b39f90cacfbfbaf06010e34c2": ["edumsko.ru.", 0], + "a188b558109248c65dcdc2354ed60cecffa610dcb2efe8b97db80ae1a1dc53d2": ["timebie.com.", 0], + "a1a10ba56ee21bb1447b9bf0e17fdf531853c87d9762196565b38189f627260f": ["chdbits.co.", 0], + "a1a51be8abc15c063ba60472f3cba6ab7a361ba341d9f654e133a7a3f880c097": ["nocookie.net.", 0], + "a1ae2e5da7e1a608556a25025aca195e435445ebf0226191294e418142748fa3": ["wanfangdata.com.cn.", 0], + "a1af87dc3904e4109f30b57a60c4ad0c7a8da2b36a92d8ff9e63f612a6e7a120": ["rtp.pt.", 0], + "a1b708de9dad186421e643ec8997ffa45ec5bfa93a8b84d3fe452931ce38a717": ["pikiran-rakyat.com.", 0], + "a1b7f7f9766452b801cff9b33ffc2dbc2da042ebd2dfc17e14ff074f24d72d62": ["fx361.com.", 0], + "a1bf95d71b45f710cdba973fa906720e2f7fa19b0b43dfe9c8b8ef5c1d5d972e": ["smartschool.be.", 0], + "a1c3262376a20415bd39aff9e1f06d7bd5df7f90084e64038e83a9a6857cc91c": ["esuteru.com.", 0], + "a1c35c674c67deee94db764e87916ad92adf9d2d0d366f1f2182ea2dfbcdcecd": ["aviva.co.uk.", 0], + "a1c50eb9adfc40368b368e28f4646c595dc38a24bed896e488f54a13fa95f2b9": ["devexpress.com.", 0], + "a1c8a856dca9b574d1b56dda294d3490dff0aecea277f73e8562fa8da9b858e2": ["mql5.com.", 0], + "a1caf9da8ffb6c0893219985e49fd8a110046638ac3607e341c4a22a7e3f4f70": ["jmty.jp.", 0], + "a1cb58d3b7fbe227c0a6435112ffd4b5723373beff61f05dfa1e7d358026bf19": ["nzherald.co.nz.", 0], + "a1ccf52661ce218647d3eb12f721ced4dec4e47129759ce2a1d13766f8794ef9": ["eroticbeauties.net.", 0], + "a1dd289f501da0e87dc4e1fa0dca9ace0e39d30dad788670ad40703d4141b4f0": ["contactform7.com.", 0], + "a1dfd518f1a1c7dd1e448353d261d7bf4ad518cd7c0c2287fc4dcf4caadb163d": ["midwayusa.com.", 0], + "a1e06f34bf2f46458be3b32e82bb6812b53c6048027bdb63bb30f5067ab155f4": ["mobalytics.gg.", 0], + "a1e7106f352e71018daf9c4afdb9592b01c1045ce1306d82f0177bf23c32f978": ["am.", 1], + "a1e72abc0538884fef45e934bc3dadced3ce7c8fe01a3a3ae40c6934300146c2": ["werstreamt.es.", 0], + "a1e8670954684b3c1aa8a42fa988a7bdef5d35d0275c73fe8b45274c7b2530de": ["over-blog.com.", 0], + "a1eb1ddfc0996dbfea33a134ad6f2c7250ea140fdc73a2b0eb8b9e82ead0fe44": ["plusportals.com.", 0], + "a1ed13bb94e5437a2c27742a1e0cab509bfb3e08b7b2cea35fc691cd498ef500": ["bananamall.co.kr.", 0], + "a20422678daef04c92a566fa5c21a12bd38092afb561353fc773af181301331c": ["ondemand.com.", 0], + "a205cd77570560b03398e38fb508dbd763b49ecd9e51e3db22ed641e819b18fb": ["maryland.gov.", 0], + "a20ad9ed86978b9488b4f9b37288bd17612495f48d31c77be7755651895e7ea2": ["audiobookbay.li.", 0], + "a20d6bc6c89e1127abe37a1281f6fcf15a775913548a6d867f2432147a427934": ["muz.li.", 0], + "a217d82fb1f7e139f1c13c0516fc2eaf75e24cb40a58ba034e90d70f9b87a88b": ["mlsbd.shop.", 0], + "a21f363d8d6a2c7141b0197262c300a74c729578e6d06913688d25c722a48b84": ["3dmgame.com.", 0], + "a2203835ab7a427447c45ddd0aba5de40a5a9a5d588be1191525a7c232437980": ["mydramalist.com.", 0], + "a22bd123884bdf6bc7a1ae77729f2069a83aafd38cb92e08dc0eddfb530b82b3": ["dd373.com.", 0], + "a233f1ae5b2a63e634953874353c208a2fb10d6e71bb4b349555559c05b6e403": ["koimoi.com.", 0], + "a2372a092c05673bb5c13070d8987876baa2a75f15fb5db3ddf21de0c0300309": ["abnamro.nl.", 0], + "a243aaf3c9d0b7ca3da869e7366affaafd2d38d7c8f74ce3ab8886c0d970adc4": ["xbox.", 1], + "a2506623ddfe39078e5ad3fccd78eab5d3bcff1fca95051bde3e97ebf386e226": ["ctvnews.ca.", 0], + "a259afc020a39f9e14956872294b2585e3d6ea698d525a56919ecc18cb0b4e74": ["movoto.com.", 0], + "a26315d053a8e900d1fc61fd18b225885f3b6677fe3cdc9812566950b8808763": ["seniat.gob.ve.", 0], + "a268375dcc19a1603247f94f0afd90d42937007f4d2c5d43d905d00f173b6a3d": ["credit.", 1], + "a268e1438fd2abd080bce5ca782d2c93a9aacdd7af9dd45f1fb5102be9a6e429": ["xatakandroid.com.", 0], + "a274679dc3eb0986fa6e9fc3dd99519fde06de6ad38e8c9224db98bd1eb6f79f": ["zwsoft.cn.", 0], + "a27f59ef325acc1bdd5a70c03e1edf36d2de811e6d5a90440a7d937a3af45386": ["rtbf.be.", 0], + "a2823694d5c297ce6ab5e2d28f9b7ffeca2d98718f3c96afc552409b10f4638b": ["zhuwang.cc.", 0], + "a288ed070a165ffe8b4dfe2b7660c9f7741156489e87bd321cd3747e05bbc8e9": ["ltda.", 1], + "a28b487846a7aa3eba1e4263803eed7c41fe3ab286950ae80541fd4564bbd27b": ["wowprogress.com.", 0], + "a28f1ec77e0d1e37ec55c92dad7bc6ccbf97ac8f428defcedb5562443b7d4716": ["signupgenius.com.", 0], + "a291ec9ae3d274c4a66f83859d942ba57d97a568dc2c5ebfe2330b524fc3a2f7": ["univision.com.", 0], + "a29274fdcc2112ea030e5718168e62f50cfe4b4eccaff922daf15a9fcf6509b7": ["tezfiles.com.", 0], + "a2929c79d07477535b7a57acfd7545a929476a1a9db00aa5dab2a99f3df303bf": ["uberinternal.com.", 0], + "a295a11e5d2aff4741da8153255ca81f7fffe346973487ee976556f41aeb18bd": ["desidime.com.", 0], + "a296a0b69e06b0a7e6e09d50e63747736c08a727a487efdccf540b3adc9bf5a6": ["dreamwidth.org.", 0], + "a2b2ab30d3a014b07528a715d673d93298b98c41205f944d11a070b9e95e3e84": ["ittefaq.com.bd.", 0], + "a2b9f4387cb7ca434811069d7e0c47f31922bdda428389afa849e8ac4b94a7a4": ["javpop.com.", 0], + "a2bc4f3c8e16223d1621f156cbd7d69ab5f50f3ec8d1fba7bacc1cec1c7c6f56": ["tumblr.com.", 0], + "a2bdd51e11ecd8b14d663b3eb3514b394675fe2218d8c4dfd0b1a689c12effb8": ["cargurus.com.", 0], + "a2bed4333b8a199f925ed5857237a1611d464eadabb54fe670ab1deb12343307": ["adityabirlacapital.com.", 0], + "a2c3d4c11dfd83211960b27209d17e238b011195ab6f4ecc3b6089e82de9a50c": ["loomly.com.", 0], + "a2c55b6b22359840edc616a7b4971ce098b3a0ae62d2a79f34c3132799afcfaa": ["bookdepository.com.", 0], + "a2c97660b25df4efe5a87caaea9832c156277b75cfee9500b32b75769060c998": ["xn--fiqs8s.", 1], + "a2d192cd0d1c668d77d410f9604c444d51f702824944fc422f310f05d054490e": ["woshipm.com.", 0], + "a2d986d1f6ffddf1e22c666c172e94173ab85ec42167c42b7ac63c167b6a25c6": ["zip.", 1], + "a2e64e33eb25d1079d2d229750bfd23dc6ae92c2a22b6aa1f95344b6a0201b94": ["ky.", 1], + "a2e892ee4a3ca3c4d24ee8ef1dae678613dc4913b3e48a00cfe6c0a317ec9b63": ["zoon.ru.", 0], + "a2e998e88d72fb456dc31f4cc675469d3bd55c3ecdba1ef628caeaeec2cb2e03": ["bakcell.com.", 0], + "a2f2740d36d6ea497abb5c8280810f5bc0fce833c9337e4ababebcaae9124465": ["oraclecorp.com.", 0], + "a2f4d4bcdaa344f1b3265007445b61008db4915070339f8c3e9906eee82445b9": ["yaoimangaonline.com.", 0], + "a2f7e03fb3c23e97267717efeb9d0210239bb9d79913acab94506d71127e8883": ["homegoods.", 1], + "a2f87ad485a3a822b6daadf1f9d69bcb140a4dba9c1242e26a6a5fccf28ad248": ["fliphtml5.com.", 0], + "a3061b854fef1d6875beb5a6ba0fc27f7aac3088d1b21fab247c21a375576e21": ["seeklogo.com.", 0], + "a30c065b1b880ac5b6ee000578912f6308afec465916cd0c9d9e12cf48d0d80f": ["deltadentalins.com.", 0], + "a30d611df1dcf80c32184d74aaf855ffe5e2dea21c9f7779e43140ed16fca6b9": ["anl.az.", 0], + "a30f4f295daac0ead1765489e22f634af36de3e8cb0e747cf30977b765c6fb21": ["onion.torproject.com.", 2], + "a318e868f4abde68ca6c1dacd0c4caa7da425d41b7b18f2ee3dee19ae532467d": ["coolapk.com.", 0], + "a31d8df2e603e390a82a05371fe17fa76adefbda7bf2ff04e47edf26cb60fc87": ["enedis.fr.", 0], + "a31d958fbb42b818083f4762a4bee64b073973142402852da80113eac99d3418": ["asda.", 1], + "a324415b0d5b0550f4b7fa8263f0b7ed31faabc87d11da28cedcfe20ba01b84f": ["barnesandnoble.com.", 0], + "a3299ecdb54b236fc5902931c8807646ed2d2fc0fbce947415493f0141862a11": ["travian.com.", 0], + "a32ae0222000e7fd15176f5a26b161499c3d70ad0c144cbb21a1c52654682393": ["lacuarta.com.", 0], + "a32b7f4658de2a15d7d207f5cf028b61177fb1bf3456bdab996125bc9cb25719": ["filmpalast.to.", 0], + "a32b88a293f016a6687909ec75bca7aabb3c4bf8e77a88ea2f3569be2704b05b": ["msa.gov.cn.", 0], + "a34d6fa1b2ae417c96ae7c1adfa28a638d9deedfe5381d13285621f4d24b7a74": ["keybase.io.", 2], + "a34d73a02fa4cacefbc67edf752724a2dab608ea8b45d98adb6da2e443388af5": ["cbq.qa.", 0], + "a34e0fb0f523d4cb8627f3803050c234e6fe0c6eab158049504ea69562822f04": ["ctc.com.", 0], + "a354ae309eaad598322c1fbeadf123ca3865a852981578c6ffdd6d20108f871e": ["swjtu.edu.cn.", 0], + "a356aa6a0874f3839a98845076140f1664a73bcf80d1a265674f8104bafb62cf": ["patientaccess.com.", 0], + "a35d71f684a8ecb604fd9a86161dc40152d87284d43a5f0c51c2ebe97eda632f": ["hn.", 1], + "a36f1cc2a53e08de13ae721fbfc8c3e9ff7baedbefbc4e51db6776f2e7adc6fe": ["mercantilbanco.com.", 0], + "a375fc43aaa05fd8943c2ce882c85caee374a5d7ffe500e6ee1640043ff98f57": ["betonline.ag.", 0], + "a37a11ff8a5a302ec5b0d7ecce1ccbd10f945986c872da58abea039eb3a55a4e": ["hdfilme.best.", 0], + "a37ac9fb227377cad41ced9629a37e83ad28611ed110b93170170726ea75d14e": ["pornbay.org.", 0], + "a37d7a44d0a8f881252ad6fa8d70021d4ff34032c9e4116a29ebe9df8366b4e0": ["monsterinsights.com.", 0], + "a38098ca6bbc2df9c4647f249da0fc68634974a55651af7098ea770b66ff5487": ["guge.", 1], + "a38501e36a8bf5da049f0c1f3da264ff961acceb0b8c74829c0c4271216be2a6": ["dahuatech.com.", 0], + "a38589236f57f061080c29580c167ce5630eed960d615e2558879c3f42df32a3": ["yiihuu.com.", 0], + "a390f1d7cfdcdc57fbeeb6683aeca0800d687cdad2fc89d83ea64ecd8f3699e3": ["edudisk.cn.", 0], + "a39afce63592c1dc7b5a1644386d6a618be72254184361c6ac52c30c3c43d78e": ["okdiario.com.", 0], + "a39beeb068cb093f9b0ece73e9e1d067ec6838f9f76ca7965451b8462ec9989a": ["lalibre.be.", 0], + "a39c5c17811da49ef5c6c74dfc3bc8993b26cdf85230ca6ca7c2f4a099b4f882": ["dailypost.ng.", 0], + "a3a19bd1b5596e500a227e13f8eb45e0bc8f76c2b74f43e6ce4cfd66e3974685": ["feedspot.com.", 0], + "a3a2e2476d5e240b5e5e1f66c451bf72943ed7209f4a9c2591a02931d8c7468d": ["shazam.com.", 0], + "a3aaa8b120ea96afbe2d41dc14ebfa94a452635b9bc17ea5c771432f0eb1bdc5": ["cybersource.com.", 0], + "a3ad714d08f64b39d1d1ef8d826076f5b7c2b6a624e35158cc0a5158bb1b7846": ["qafqazinfo.az.", 0], + "a3b35cd9033f0c3566ef816b9df08f3803a3c35cc13a72d0aed3b72ce584a8bd": ["52pk.com.", 0], + "a3b372bd25b1381cfa89321a7f73a4294bc7252a6a3066eda193ab714f2b5635": ["infox.sg.", 0], + "a3c420422ba58c332de2c3c7bb35c91ea40128009006c7c4cc339fb09659fd40": ["impots.gouv.fr.", 0], + "a3c7a884b030562850999ae0e4028ad0dac33fb7ebdc8e59399530ca5d3cf1c4": ["jetfilmizle.ws.", 0], + "a3d57b2f2a2eb886d83c21d7452b6716be9a2bb9a0cfdf479b23729963116967": ["gramedia.com.", 0], + "a3df17b9f226641cd72c9d562a6fc548729f09fe1d997f2ec90ceda786a8bfce": ["maktabkhooneh.org.", 0], + "a3ea96e660281dce6d8257d58636ab129747877965a5f91528a2cc1a71ef3a6a": ["ecust.edu.cn.", 0], + "a3f38ff6764c64178ec6f0ab661d33e37eb7f9a17c759cd1958c55f80bc95df5": ["ms.", 1], + "a3f495720504ea0d34209ffaa6e39fca4f3967d81f8c1b43b6cc8b6fe04f997d": ["hsn.com.", 0], + "a40a90706c3637e8315c9ccccf45c753043106629b79cac1bbf367f034271343": ["nextdoor.com.", 0], + "a4118690af065496557c3c09300fb957599229365300e69b9a2f7868b765aac3": ["tc.", 1], + "a421666cef798f1414e3ede7a2ecfa3d09f7b6afcb0900553cb9bbbc1d214ca8": ["xinpianchang.com.", 0], + "a42327cdb09380f250c8131c7736f0551d519b35198e72673e141c3547493219": ["freelancer.com.", 0], + "a428a582f323f85d3e344cd9e2f09f60f4487916a0f9d468f712bc81ba49534e": ["squarespace.com.", 0], + "a42cca6857b0511995d9091640dad8e25cc31b7acba6f2f4612b406b4ea6878a": ["boursorama.com.", 0], + "a42cfb4509fd112375f9fb7098bb8884fd9d23568061e86af314fb0ebdac7fe1": ["negocio.site.", 0], + "a4372c4eb77e9713a7cd708ab1a355c09fe8b4ea4634f6ad1d63f54ef67aa222": ["zawya.com.", 0], + "a4392968c26284427fb3503b245a13b0bbb422e754eb6cc255121a44dc69e188": ["pg.", 1], + "a44292f1c500699f09d21d7d3bcd8e2793b542e3b177276c430aeb00c96834af": ["nfl.", 1], + "a4491401881fd83c328aec09da3f1b7c67618824f311aae3548abd85bbb26787": ["leetcode.cn.", 0], + "a44f60ed7c2e0bcaf39d222fc8caac9f28929db3bb202ca5164578464835c1ad": ["oremanga.net.", 0], + "a46053927c925f5284b646da05c974b4e828699689ee1db598b3e15c3e10ca8e": ["wjx.cn.", 0], + "a4651c92dd1f62a1f9aa74145cbc04a39978666d6bd45b7118c3c69ce89a9293": ["landiannews.com.", 0], + "a484a390c26bde99afe4e50d861ec8ef3bc1e3530270c53e24d212ab081b4fba": ["worthpoint.com.", 0], + "a489aafee9c9ecc3dfe68df0f416c11212dcece674f8fbee6c9b20bf6c8a0ca1": ["edupage.org.", 0], + "a48d0bd4be82fb7d0c934ddcfbc705dda10b6fdec4e32df35a5dc6353d02bb4d": ["montclair.edu.", 0], + "a4917166b658f00392f9afceac36369f41ac671dc04fed154a4a4a9e63a5eee1": ["sj.", 1], + "a49232b2a590e1ca944fff9d3aac2f21dd7c299f1c00fae18f780ca24af499ef": ["gravatar.com.", 0], + "a4a3a7cc0d1cbe64a87e795d12f0e749b4beb06423158da4d165eb77e6331ad1": ["dzwww.com.", 0], + "a4a3c82082a84033406bd3e4b4a05672d3eaa88a4860a09c7d2e55009174f6a9": ["works.", 1], + "a4a5bca8d7d51d9a4b8f6c6c229f1577184708543ce093d7b45ebba7929c1951": ["sakurafile.com.", 0], + "a4a6c474a8017915d94b245ed31e0db00bff74d406eaa66bfb537265cc71820b": ["shopdisney.com.", 0], + "a4aecb08bb0528b309a60b65d617a540400a41726067772ca68a7c108287cb6d": ["cardkingdom.com.", 0], + "a4b04631dfa79621df85409611b2b97728566b21b9003e58c43da45cc14f6c84": ["lightinthebox.com.", 0], + "a4ba315eccdc67a82f4c2626bb6aa77f1c8389fed87fe140cc80c814b296c17e": ["fastpic.org.", 0], + "a4bd6818c32a8fbe1455acf368a64a0694909feb18465a47649d23e2ca287c7f": ["my.", 1], + "a4bf4f8a5fb1abaf01145c9cda575509ff3ca9f0b6036cd0550f05fb8535d5a8": ["heroero.com.", 0], + "a4c026e13c5fa67c40b8dac904afdc4a73fcf1e35a168071876891240daf0f63": ["codepen.io.", 0], + "a4c24d537d65a4a3e373db2d10b4542ed0429ad44aadc6ffc1336d753041dee9": ["osd.mil.", 0], + "a4cc8ca3c0f20f20a0dec83f3349355be36fa8468a4f073ab388c03f7a80b441": ["ady.az.", 0], + "a4d5f1259042904ce1e2f064350f8839e627308986d9fc6ebe525058f26a1d66": ["metoffice.gov.uk.", 0], + "a4d95871b35b0a1fd3e6280317992832ed91f75383430a0ca09ff8fea48995bf": ["ssisurveys.com.", 0], + "a4e7626131dcd1e547224530f6b9aa867f7e70c2f078ad073de5581fe3e4129a": ["marriott.", 1], + "a4e86a5991c4fbc2b89d81ec5f812e4dcd51d14f2e3d25820f54fcc726e9869c": ["enuygun.com.", 0], + "a4eb1a72fe2945f6c68d5ada8199d03a5ff795409593a32e0aed773ff696e65d": ["qodeinteractive.com.", 0], + "a4ebf75097518dc84e965fa280041984ee4c1fcc361fea1d49999cb62c09bae0": ["full-count.jp.", 0], + "a4f89ae0ba2c4efd0106a6b97cc9df057f16886778855d2bbc12566589924f5b": ["nurxxx.mobi.", 0], + "a4f9d54a7377e06a4b6a3dc3ba637af3e86bd2a0dda9e41644c54f4ce28c51f7": ["erni.", 1], + "a5034f813e049fa751ccb78b9502737014a7240650630c49011e2b8b74e026fc": ["weiyun.com.", 0], + "a50ed6613f418486530a38c92c62a9bb25d157a6e0e375f7b02b0b8da8bccadf": ["parasut.com.", 0], + "a51119dc26e6cf8a60bff238edce20fbdba585ba21cfe13943da90d09006df0f": ["verywellfit.com.", 0], + "a51dbb39c391fefc09dad7d26c4e543a131e83ac7e31649463270c61b4705033": ["smashballoon.com.", 0], + "a521302e898b59fd1666b12643b0e93faaf8341cecfe22c6977f945c515396da": ["sharechat.com.", 0], + "a5266b8b297a892443e6503e1dd4f830669d34cf52f36c2167d20d0ea798311f": ["kyodonews.net.", 0], + "a52cc99d9644e11dfa28d2cd202b1a27a8eb303014c4474d470b74cb898636e0": ["pracuj.pl.", 0], + "a52daf1de181e68abbfdde4451f9ea6d190ff8d997739156e1cab59cd3ea7667": ["setlist.fm.", 0], + "a52fdf9a9ac89cf86a707d1af4cf11dc447b904498fa8310885162c36aa5d86b": ["givemesport.com.", 0], + "a5300404442fbca303483117bf9e845c5d7aa056c5bfa0cc0b88df0814a0f6fd": ["reviews.", 1], + "a5418d0bf55e047232e2309c93b4a0c73849caf10a20b041c106698456ad0842": ["ultipro.com.", 0], + "a55312db632d3218c6b22d2a18a24a7700720401ba24a89e14e8f05ced538428": ["blu-ray.com.", 0], + "a55852c0d303928add268d3f62856d23cf64810b34383ae65b94da347614b35a": ["clearscore.com.", 0], + "a55d52185920a5557127b552366df27bc9b8984fe99dbb37ca75027c94897832": ["bazaarvoice.com.", 0], + "a55d804a5ef2ba1159a3f599e83bcb8dbbe3735a760b44f34033663ba099b702": ["marieclaire.com.", 0], + "a55db1e22ed5d81d874b0bac0b9124339f9d30f4c6a6c7fde32890c0eb97970b": ["watchwrestling.ai.", 0], + "a56193d4dbb2fe5606b023d7513734a513ebaf4091bdd7af51c66dbc3322f64e": ["kokopyon.net.", 0], + "a561be52a442a68075286122feb28e454028ee0edc5c3b78d7665af4c3931d8d": ["thelott.com.", 0], + "a56f01d4035bad92ebb5a86c128464429da6d0c53c99460425421afab618611d": ["codecanyon.net.", 0], + "a57189f6634ef46c4c4c8c07606fe7433b6e3edd35ebbb4bf60e5993e170de5c": ["istgah.com.", 0], + "a57864724749ff74419d1f0e0609ed24fc91c00cde297223445b7c5c4e3b2ac0": ["tube8.com.", 0], + "a57a522d2ac7b8428a791d46ca98f289b0befff56622d7e8caf2492ce55e8b8b": ["optinmonster.com.", 0], + "a57c2774956d1af4bb36cf175b4e5f536da9bbe7ed4f58467163c14c1a3b879c": ["orientaltrading.com.", 0], + "a57d77747e66b43ecdbad56fe85d067bd8338db73952915cbfc5042411434991": ["newsit.gr.", 0], + "a5847bb8a398b5415aaf8b5f85d670892673188993835313eac7f7a055fc0d6a": ["wix.com.", 0], + "a58b1d073834bf9fc9c7b969f1f36829c58e46bf9488316bba9880696eb0fc16": ["manoramaonline.com.", 0], + "a58c94ffaac492563d0a12d73d7d179adf06a8062428440999c75ea14915b21e": ["mp3cut.net.", 0], + "a598e54b2a1309579a0d56d2e8e964a129764468062c0867f78bb268bf21325a": ["pixabay.com.", 0], + "a59cdc1c618e2f3f303e83f95ff3f4628043a76ee05e594457f27fed9c29a03e": ["yidianzixun.com.", 0], + "a5a4439e6745271399e121adf9c13a26b4828b3ab4925bc2e62d3630967ce9ed": ["dramanice.ac.", 0], + "a5ab32542e939e6a65bca77634aabb229adc5b7f8bcb1c9a243cbf48a58aadc8": ["nijie.info.", 0], + "a5ac7c0d753de3be2391ed4b821e96718181eeeab584fa9a5dc8ac573c0bfb62": ["gunbroker.com.", 0], + "a5b081a406f5e9a88b11bac56725dfb6d6b987bbdf5af2589267ab85a789857d": ["sexy.", 1], + "a5c1cf436453437c525e63f5c680b7d1c9e184940018961dc3e4624a66332fa5": ["azki.com.", 0], + "a5c242e8cef33cf371a267d56d4a7ae7056b34f61a6da9afc97e2cbc151d6aeb": ["mioffice.cn.", 0], + "a5c27fcc018265dc01a67699dbbe8e4ccdebfc4ad2f4b2d9a30ed26dff552f6c": ["clinicaltrials.gov.", 0], + "a5c406fb13f73aa74084f329d20e269f7a78d2b67f828fd405ab9a3e40c59d12": ["wipo.int.", 0], + "a5c62d8ec8520c66431d73a8fba5624d2f94be8b17dac3856923d455c5b42add": ["banesco.com.", 0], + "a5d3440606c24f13fe7e86412eeb463c975c040602f5b80d786cffd6c58f570e": ["qlik.com.", 0], + "a5d36016d093f09fde19862c43a945e6824ca8b024a244a6e09f39a9e95cb21f": ["vjshi.com.", 0], + "a5d804a3d56ea18078e8775e9642b29e8a17238456f76dfa644879c0166ff159": ["erji.net.", 0], + "a5dd1e86ab1c142df5c6d68a911f2ef1565e52769a0ef528abd7cdc7cfa9b014": ["gwdang.com.", 0], + "a5f4dc127cf4dfec3a0cac59af3547e40b1f0db3892cff21cfd7a0cd942c1d52": ["zxxk.com.", 0], + "a5f4e6be709e2aef7bee9217625ea0225f776b00ab81e0fc37d1e1f096022fde": ["lihkg.com.", 0], + "a5fb99efd244e77173522cc0e149d735fd6b7b54c043ce0d565d11cc49d120c3": ["alipay.", 1], + "a6015b3c1fb4a6442c5e6237a03c1191bf23a7725a457ced99ae996b91172363": ["medallia.com.", 0], + "a6019a65ef4bbd7ff25f909b71fa3a245ab66f9a5d2580569fb6adf7bbd32625": ["thoughtco.com.", 0], + "a60ea7fcce986b2b006413e5c30cf24b400bf4f467b02a532a11ac23c8a0cf4d": ["maricopa.edu.", 0], + "a60f75309003d3c3b946203247184b948c458c1c8d43b51bff6158c0bded5cb1": ["mongoosejs.com.", 0], + "a6153a7e9d24558aae5f74e0a660535e9d6123a853f09bf98478174121968942": ["iranicard.ir.", 0], + "a61558001d624711d2f28c027f93e6b6abf5b295ebefa2d01bcbfb66e663c1e1": ["vseigru.net.", 0], + "a626d0c00034b295ae5410a25a1b3abe04fce13bd96d94646f8d892f835a7526": ["openoffice.org.", 0], + "a630861be4ba6dbb6475c2d210ba8f3f89c5accbc18bcf06eca28c0785c9ee47": ["yesmovies.ag.", 0], + "a6352c035a873c45921d42640dcd1e99fc66ca3ccffec5afedbe9b67e06f9c9b": ["aok.de.", 0], + "a6387261322cbb8f1fae38987c005ccd82d926ac5fbf3823451b66fd2ec74fd3": ["humber.ca.", 0], + "a63ab47c025e2bfe14250ecc9f1e72dbc9067262a323c8a2393d55c2a9529349": ["onlyfans.com.", 0], + "a63db9e68c396269c3b3706d06ec9589f88ae3525f955446891fe0e4756a1356": ["rightmove.co.uk.", 0], + "a6442c0025d695140bb397c43eb3f2d18d2e51e06a55f6ec28052f0fdd198e21": ["zkillboard.com.", 0], + "a64bda18ff1ca9f28c6ee6840ab97c242c8aa2969673755be8d81602a79ea7da": ["sexu.com.", 0], + "a656f132f5e9021da38b22ce58f77510d7bd14f27047e822208ca23fed1188fe": ["logomaster.ai.", 0], + "a65f2d5e14207365e6e5164df3fe8d1bc5ae96dbb77daea207c3a11cf250d504": ["abv.bg.", 0], + "a669cee50b3cd40df896872bbfc94af933eac7aa210c4c168ba568aa0e466c3d": ["hust.edu.cn.", 0], + "a67ec3018d1922810c79445fd9403880fd54bf7749e2b7a78b074036a3ab81ab": ["unionbankofindia.co.in.", 0], + "a681525357d4d0e3f510f6509c0d4a54627635aa8c102edbeddda0782fbed252": ["wheniwork.com.", 0], + "a686e8d8c90261a7af72d5718e568c82f2e85f5485373e1b8c3289cdaf1ecb31": ["vider.info.", 0], + "a69aa711bd4c1ff872607a0adddce7f6faff8fd548a651643ed4cab04bf271c0": ["vodka.", 1], + "a69aaed862d20c4d19baaa865303c22959ca89b0505b41629466179e97977091": ["downloadly.ir.", 0], + "a6b0048ae7f2fa11e52dde5db5f67ec78aaf6a54ba70e9d3c1c6af8fb4225675": ["futbin.com.", 0], + "a6b3a433941ae6846eab59049a32529f1932456a554c868e44660c3ccc97e137": ["mynet.com.", 0], + "a6b86b795071b87f153a3496ad726d19c47ac1f6a8f4f1ae4f210926e42a48a4": ["firstpost.com.", 0], + "a6c80054612d04818e83b5ffff3356d2d039e99de1194e869efb05469f53aefe": ["walter.", 1], + "a6dc97ba2191f2d3a2b0a5e3fadb374d4e26bee06839880a42eb7032c20e9fb1": ["mulesoft.com.", 0], + "a6dfa3f788e06e9b405db3b8819185fd76c133ba140ab942aa9eee82046c6f58": ["ampproject.org.", 0], + "a6e18911e593ad4dc9f7142a8f7b496be27e479e39a444b66fe79c8c1925fdf8": ["pokemongolive.com.", 0], + "a6e4b17f324acf7223adc45b2dee208747a5f5dbc0194d2edc502a450da57790": ["themelock.com.", 0], + "a6f2342b2906ff4aaa54ee903141dce64ccaee1f4d36e8e5a20344a67bdafb9e": ["sagepub.com.", 0], + "a6f715e751843aa901d778d834863fdefa9b4b69ba44bcd196178e56e64d9e83": ["tuttonapoli.net.", 0], + "a6fa4892ff467138b851144535d243ad11b01bf0eeb97fd05393e70bb49ad3f1": ["armorgames.com.", 0], + "a70387dc564f7314add48e691c665dae4530dbfa830ca5cf67cfc1c8bf8edfde": ["goindigo.in.", 0], + "a70d8afbb097082b80083b965fe511e76ee019ad62bf5d5a985b22f6725ee8b4": ["mcls.gov.ir.", 0], + "a71048fb3933cb89d896afa6297b634bbae88dec057a25e528cbaa9ce67d4f6f": ["xiaoso.net.", 0], + "a7116114078ba85cb93840a27da9791a840ee74766002175b71c6f8188f96ab9": ["heraldo.es.", 0], + "a73aabb129c5d4adc5dea7d88f13d655b4c55c2fc10742b69263d06282821006": ["ping.", 1], + "a73bba6b50784306c58398c3a00b364ff62a7ca28d635c1fd043e7a0cb755404": ["haitou.cc.", 0], + "a73d7ed3086ca8a9d8edf745fe07167a81090f62cc1e7ca7045e3ce79704bf48": ["reactnative.dev.", 0], + "a73dd2ffd8b0df9707a1f9790232bd6fe15f637bc4cd0c17a9888c93ee6b4adf": ["privatter.net.", 0], + "a75a0dc189cfb77c372dba841be4ca9fa5602358b5d0b3a4473bb13dba83ca3a": ["wyborcza.pl.", 0], + "a75c384bd8f84dd7347befead4db03d2baa373e9244e4d6352aec74646ab2f3d": ["nist.gov.", 0], + "a75c47e37aae040c2421eaea19eefb571fc59002555d514993ca18f329279d8d": ["slobodnadalmacija.hr.", 0], + "a75cfa88da377fc0f5866f2dc6f3fdaca116e3b8f7c2c1e608aa703d68af3f7f": ["huya.com.", 0], + "a75f19910c5c22134365a47b712dc07f3176c20d7e2d1d5e3f7656320e221532": ["mendeley.com.", 0], + "a75f937a1ef73ee652ee00e35b762278722a8f682486328a9e26c4946682f5e1": ["splashthat.com.", 0], + "a764172e7a767d8b40b0e1c782398b2399c1cec68b6c70ea8ce4b7ca49884131": ["coach.", 1], + "a7652f453790580e53e13a61d9f7c274968b34e603ed9020abc0219d33709f2d": ["agari.com.", 0], + "a76d6c88d28a3f94fcae54dfeace55b0ed0cab2cbaefeaf1a6bff5eb1bf7cdd0": ["hentaidude.com.", 0], + "a7712fdf4a8a13c17c5aaa96016e99e624eeafe638343e1af44d6d87419cbee1": ["mysmartprice.com.", 0], + "a774f9a94a1f0b6e0a3af757e2631ac530d53564652ecbe92ce174df3d49bc40": ["lb.", 1], + "a779012ecae49dcecf8c75dfdae69ceee35ab101f4b5609cca7788da5576bd30": ["edhrec.com.", 0], + "a77df4bfe1cb8abd316ccab8c52b3b43245534f6dde65fcd177b772e5306f39b": ["gluonhq.com.", 0], + "a7867870fa15387e220106f999da28dc998e2b881897d23889ab2c8521fe415b": ["cryptomininggame.com.", 0], + "a78733538ad888519c4d6005a6b2954b07ed8959fece71eaf9040c50e5d30d6d": ["progressive.", 1], + "a78bdd1a9b0bfcb8e800432180530c9734ba77b8fdb0ca10636cbd8c601fdd84": ["digistore24.com.", 0], + "a78cd1cfc8c2d0bebb0853497afeb2695de62837729bf023d422e5318b703a0b": ["york.ac.uk.", 0], + "a79055f9d1070d7c0b93b0a36522bc6371c7aa9e21414dd98afd3a3abe98ac25": ["mamastar.jp.", 0], + "a79086eea9a4888b9440ffac3ccc10a8a8414e6bea8d1fcb1795b082fa2d06fb": ["elcorreo.com.", 0], + "a795c06c4a2bd0c879a300c46e1206fb122d828e9e37a29165bad007094c00ec": ["cesc.com.cn.", 0], + "a799e79dd483425b64e93908c5d20c503650d31426bbf07e3d20196aa058411b": ["rthk.hk.", 0], + "a79ad8398c722a99ad361d2b3fb1e109fa5ad2d6ebd6bab041bbc4901c7d9ed4": ["momoshop.com.tw.", 0], + "a79c49da09121cddc820970ec3b7e35e0a88768512b3d5253eadde9774b85826": ["bigjpg.com.", 0], + "a7a9f36a748c712c455288a7fd9d155b40051019bc8e9f197e8ec29367833473": ["msn.com.", 0], + "a7aafcc0b8a6917ec672ee89b3c27ea2e9029133772c28c1cc8281194af0588d": ["ecpay.com.tw.", 0], + "a7abb618a2ce0143c9a2318e60645a6bb992db1f4e80492e931bc49c134e0a32": ["pinimg.com.", 0], + "a7ae343865a1ee5be355536752fc7e3ee05dec3fabb2791bf44e63f23d60c871": ["opm.gov.", 0], + "a7b9fcd89f44e3746afafed62b1f9fccee433944f39b396193a491097c276402": ["prodoctorov.ru.", 0], + "a7bc64b47ccda6a7e4d063fc06966fc23306a76498ef106a4866e8c65fae315b": ["sendinblue.com.", 0], + "a7cb8528830357404c8487cc194f622a63064003c3471872fdf70323a1f287d9": ["ysl.com.", 0], + "a7cbd94f4fa84b475bef22ba6f0e380192453cca4acf1259a3e6afebcb60dac6": ["macrotrends.net.", 0], + "a7ddfaad9f7ca7ea8b531e22083dca250351228bd9cb93f045967910ac93c493": ["thingiverse.com.", 0], + "a7df06c7c9ce8af39c4df39d776b6075b0dfde4598a26310c189f0d6c78ce88d": ["coolors.co.", 0], + "a7e425176e7bbe9cf7bf92cbb1689164f29d5d42dbb46be1a59bf64fa03b0e39": ["jnu.edu.cn.", 0], + "a7e9c572f3e4abe9944fad1f1e103198b3518e10bcc908c88f1daa099b8e1a99": ["nflshop.com.", 0], + "a7e9cdbb2afe74c38cda790b5e424b9b0e2c03b74a3626a39544bca895694f73": ["computerhope.com.", 0], + "a8046e8fa79cec15fa8d0db6ef21a337654c366ccb6b5717dc8ff03ebc05f0a2": ["firefoxchina.cn.", 0], + "a80590b62df356da46f42b72fd9d386938b1a8ce7921e16d89afe0f0955b7ca4": ["espressif.com.", 0], + "a80e379955f3b5949ddca89d2d5e47d782bbe509d45baf0093d86fe692768f05": ["brandreward.com.", 0], + "a811bd6caf812ea452b20f9e305c6a391afa40edc3c8425f06d114bdb2c33fa7": ["adorocinema.com.", 0], + "a8137e15b72fe113a8cb9e5a813583b5a6491d1d64f9431ebd81d132a73fd684": ["eliteprospects.com.", 0], + "a817fec9558ea07847ad2fa5e9c72b81daa71fbbe57334d298f4448adc25a21a": ["ftr.", 1], + "a81c98fef7437e24beb521ef748443db25ed7e4307a393aeea73fb5252076ea0": ["powershow.com.", 0], + "a8228e3e16549a9c78c9adc6d80891471eed2ee6a530e0b9fe06320eff0a49ea": ["getemoji.com.", 0], + "a82980a53fe33ddb018976b30298c7ad2615b2f19fdbbe2c3f530090701a2b4d": ["computershare.com.", 0], + "a82d5ab7362b740e8a27f8e631237a3ecfa27d44aea6988b1a24eef7b970226e": ["companiesoffice.govt.nz.", 0], + "a82f60d455b2577cde03126a0bab9d1dd2cfc095972f0aa6c35482bf5f599f9b": ["svt.se.", 0], + "a83105589f85c098e61a1679218b747df1a9a0f5ea307a91bbeea44930c44d54": ["unwomen.org.", 0], + "a833c72c7482296ebf2b56a1d744fa12edd6493f6cad4d9f46cac4021d368058": ["pytorch.org.", 0], + "a8397bebc518635eedfee0d68e8c3c6170a63a7c0a2ef3ed7bd4f61784222432": ["sima-land.ru.", 0], + "a83987e97f3881c44d479175c54dc7836b776f0253da148540928182ddf573ca": ["rp-online.de.", 0], + "a8462593087f96370d0bbba4d65dd512368fdc27232bd202079831fe3099375d": ["stripe.com.", 0], + "a84a6774c0feb747858b1f0deebd9676fd3b84d0b7ffeb3700b3f87a1974b759": ["hsbc.", 1], + "a84afb87f73aa272f62b1a4496e05075281ce6bb8e10bd1a68f5e30a5d071d45": ["gazetadopovo.com.br.", 0], + "a84cff20a6345f23fe971c8aced77f8fd470ba747e3939f8f6aed6f87f93ae5d": ["postfinance.ch.", 0], + "a84e4bc4fef803d939ef720613c900391ed54c89bfa1a450f71bb73915ea12e2": ["bo.", 1], + "a860d3e91d23e6cdfd47cd88171a8652f7ce5de8b62a0713501c6b013554a39b": ["tagdiv.com.", 0], + "a873deeaec5f7fb0637e7a6d3f3a4bf3f09c0dd2396d01a3404936dcc3075a81": ["snapp.taxi.", 0], + "a8755e59c7695364a18f8ea6793eec6869d0051d6f934cb62e4ae3a88b23a29f": ["datprotocol.com.", 2], + "a878c520bf7c905f01389cb4a9dfcecce16f73fc8869ddd8bfb2d07b2f4fbb03": ["iviewui.com.", 0], + "a87bf45b3bb1ba4f0ece86c372625aaf9e0f739911ab23c81b48dd60b4cbdd2e": ["all-free-download.com.", 0], + "a882c911dc8f3897ec69606becbd2e7e9c638db62fde42259995e96d9992ce72": ["myhentaicomics.com.", 0], + "a8867d49b52613d57302e5906aacf7e3d0acdeedc8617cf5003843b62036297d": ["rlsbb.cc.", 0], + "a88810b4975a2d850e653d5a546783df3865b9e366dbbdbf97b20eb3e0529a51": ["rapidtables.com.", 0], + "a8998085ba42c731f30e472a85de64227f2bac4418ed57562d52b765207482b3": ["apasport.az.", 0], + "a8a5bb8a3a8bb43c1b78ccffe3269d3400a911301a811083b4d3f639c3c95363": ["tjsp.jus.br.", 0], + "a8a759a000c56eeb47a6e64d2aa932f52571c3cb1722a7cdbecbf21d88ca1848": ["9to5mac.com.", 0], + "a8ab750edbff9a2cbe943d452efe7d4eb07ef4c8414ef301c1f9b99382ecaf79": ["avira.com.", 0], + "a8acf69840a5a4789720aac8588600eb5f221565724dfadf1ec953e0c67d2b58": ["bcn.", 1], + "a8add3fc15647e6b43560c618ecbd0d0e9e64c93bed6e52de25a451a19faaee4": ["geico.com.", 0], + "a8b1cdde8dd6ef7d8d40b2ec16ad6cb6c62e8d1141ea0ac69bee3443193d1899": ["bestchange.com.", 0], + "a8c370d04694ad675377275a25b614ec484070b38986396d18de784690d42650": ["tjmg.jus.br.", 0], + "a8cbb972027eb06bc28062f50dc41c4259e8d701b4dba04b82d70d7800386a3d": ["acuityscheduling.com.", 0], + "a8d4959e558ec34cfe0597af252daf82737cd899a749b839d70432dc2cad32d5": ["siberianhealth.com.", 0], + "a8db183c7112864de27beb5de55f50c93b476fcbcbeb95fb3b3f539f846cf9a0": ["wowma.jp.", 0], + "a8dd36a4155944ebfb5ba962bb88f4b98b4442262e7865d61f8c296695bd3caa": ["searchenginejournal.com.", 0], + "a8ddde675918c8367279d0c595be71ca9a99bb8ae962acf09a1975382da3456c": ["haier.net.", 0], + "a8f2dfa83b675642b3c128af97b6fc2d0b5f84e87194fef4fc8438f873769c56": ["rocher.", 1], + "a8f4d6d224e854c15a8406d326b50a5d267c6d9124413b4b0d6ee480b29371b3": ["epey.com.", 0], + "a9047c72b99b9b4763c949189c912d5a9c299c60c09949c7ad4ad99f76d7c934": ["doctissimo.fr.", 0], + "a90641c1aa25bce4f73582bb15006b773b5a8358042a8580c4f28d829294d602": ["designevo.com.", 0], + "a906e9f41b75ce60b88007ba5081a62615aa6488eab48ef3ea255b10dfa46844": ["tra.go.tz.", 0], + "a90b4dd2f366cb50c4f155d9cc09c233e083551170d0d526c825d74b1fd19772": ["ngs.ru.", 0], + "a90b626699b2e81469d40d2b7c5b8b517c8040d2507c5ea16a719a33b1a1bb9a": ["mma.", 1], + "a90d09e69738644d57bd097a764e1ea699e12781403ef5d0da5ff897e8e41a90": ["wellsfargo.com.", 0], + "a90d124f97fec63d25d1d63c0a66216c15713b1a60ce79e9ecdc461958059100": ["lehighvalleylive.com.", 0], + "a91041351f9070e8ab992bab1d84cd8750038e9f12380fcd9675296565c4ff81": ["sharp.", 1], + "a919903f090cb810155d60a86288d0e899b407738b656978f72b856c87bd0460": ["e-ams.at.", 0], + "a91c9bb173c687fbb62370bdc8995c892d7056bc58a95333f48e16bbb4250bbf": ["imgonline.com.ua.", 0], + "a91e10a53319f408957f87af41a4da3413c70e3e367d4e69fddb767c2d787def": ["humana.com.", 0], + "a9203bc41e7da0779b8a23f8bda1554ab3d485a9c70fe6dea655a44a63306f89": ["mynavi.jp.", 0], + "a925395a66bd86123aa60f5eaa021881cca694f9e5de0af0f622eb7335a61ba8": ["pornoxo.com.", 0], + "a925e955a71a7fdba6f2a4a7db2ac8b62ea495462823268bdf7fab40511cdffb": ["ccbill.com.", 0], + "a926e4413b48394e82d0735aeef960f67fb1752be9213c569cf66eff38574fd8": ["png2jpg.com.", 0], + "a92fedc15afead4c4382b895dce5f25710c70937593c18a82e1604a64a0e09be": ["pointi.jp.", 0], + "a93226a1e7c121a306944c81f71134742a895ceb967da422633571d1cd3ab9ce": ["academicworks.com.", 0], + "a933648bc8249740373ca7f2fde9558c0f4c6fee6f21c5019b893f793aba2ac3": ["f-list.net.", 0], + "a93db15f2332faa0e9e229b04c84d11cb1131484243d9e8f91aa55f344026880": ["pornocarioca.com.", 0], + "a93e3479b8790533673d1f2a1d07e43eb4c594e1d03843ce0182c534da535efe": ["laodong.vn.", 0], + "a943b5bcbdbb1a1d3aab2e063541d578b6a78ef43a79d05c53e55beb035f1baa": ["crx7601.com.", 0], + "a9465002dbe5166fe311abb60edb84b82294fd48138913c1559aabf7c0acd2eb": ["miles-and-more.com.", 0], + "a95045b8df57290db6bb1c8dd2bdc585b693909d4a2a0edf6563967fe1b41b60": ["synxis.com.", 0], + "a951c6d8d547f33f46e5bc602835b6cf66cecd7019361871c8c630b300c65784": ["dianxiaomi.com.", 0], + "a957bc33272ee90cb6307dbc2c9f586670cebf79b7e1e99c24f8b6c53f0ab059": ["bbsmax.com.", 0], + "a958fc162abaab540557f9582b39f2ed4793c958c28c2cb9442635d0d751df39": ["properties.", 1], + "a9705280cbd9fbac9aca2eeb8509abc1d30ff9e8bdb98869260bfb6fa87277f9": ["tongtool.com.", 0], + "a976d4423cdbab934711d873cbf34f9de420634dee785cb06cc24bd51dfa8241": ["glosbe.com.", 0], + "a97caa99d9f56efaa1537eefe6881017269bc5620f8c60aa160ff1e5890bbe91": ["tiaa.", 1], + "a97def83c7377e2abba2797f555a5d642d7123c45a21c7a8a8a3cbe178804cb1": ["dhl.", 1], + "a97f24334f15e91f29a9f9b7ec2b552c909dc66b07940925ff04e25e99888b9f": ["sdu.edu.cn.", 0], + "a982c585549b086eebb9836037dcaff78f8489dc19805dc543703524471f8023": ["hatenablog.com.", 0], + "a98a09f6c267705719338f85ee99cd6bc447f1664634f4fea81013445c5a6281": ["kemenkeu.go.id.", 0], + "a99dc2527eb3b41f565cf590d1bc1caccfbc7bb0f0f086f80ed44f27cca4aef4": ["namu.wiki.", 0], + "a99e22ef294eb678a5ce651398517364a4c88d1c90071283d470fcdbc6f85ada": ["amac.org.cn.", 0], + "a99f52d4c96d03f7be168b9e2b87cc9916d3d6b302f0051c39c4e7b6a1df7c6d": ["made-in-china.com.", 0], + "a9ac72e261c28f01f9f31232d601497f25fe7d938cc685e89385fd18cd4dc6c4": ["kaiserpermanente.org.", 0], + "a9b1e6644d070092aa498a907205e573e92401edfa835cf3771121b91aad8d71": ["ctyun.cn.", 0], + "a9b4513bb405e6a3a5dfe43c73f8777eff41f12f5020c5eb189ea18e3f4b1424": ["sbicard.com.", 0], + "a9b5ddc98a5cb100fdb6219b8cd857633619818c64b7c9c9458926994d313e1c": ["sensorsdata.cn.", 0], + "a9b6de5f2a15e03949618e03cf902dee35d38b0a8a439fc14e9e00840f75ecda": ["ntnu.no.", 0], + "a9b7434e4c6665b063f20cecac6abd6efddf1484d032d9f506c224e2666e0e71": ["maisonsdumonde.com.", 0], + "a9bae525028514777f7318aca17954fccb316e1a07cbf8e03e528bcdfabf2527": ["5156edu.com.", 0], + "a9c6b5e9f1673cea2aa4cd9509f2c5deefcac1f1ec26945252204bf703973550": ["gismeteo.ru.", 0], + "a9cb2b16403e5247b71c08d4e71d74d2888f638c95e931ef27bea4248c8fc2a8": ["sharekhan.com.", 0], + "a9cbb3e1f8fc5ba8b3ca603b65d7ba27ee18bed176bb885e987f1b50e74c2d30": ["abema.tv.", 0], + "a9d4565139e46bfdc54d5b8dd7bd7682abb57aae94bdbdf3cb1197280b7d85bc": ["mihanstore.net.", 0], + "a9d49b78ac41efc247ace4f9e726b92d54a678c6fae084f12408d7f057cdadc6": ["zuerich.", 1], + "a9df77c5aeba038a6289f51035e206bcaa6044c4d1d6bf6157ee6b5a7b303b39": ["pk.", 1], + "a9eb70035e1b13b498ec8d7ea433d5e0b4c5ebb4eb51224e324268ae85c3164f": ["istockphoto.com.", 0], + "a9ec5c69f06968b8a5d53338cfaa846153d7841a915edec2774c8c5d92d73994": ["apicloud.com.", 0], + "a9f4b0043970f47be7cea496a437ba588e3e8384cc798d73dcd9b01dc0dad25e": ["imooc.com.", 0], + "a9f911677c6399324f6682158e96022349f02e7c13d87c37cc74bfe5e35dd8d3": ["sokolov.ru.", 0], + "a9fcd4020e902ab2f3b4b83afd1ded96be48aabeb39cd530c6531328a5c928fe": ["tellme.pw.", 0], + "a9ffaee8f09c0128d67250db1f369646062c7544d226a49d3690b07549c35476": ["sravni.ru.", 0], + "aa07e602bf995641a8b05cbf0cc1e33ccf95d5e40b3f92dc7d118d7e4d13d53f": ["healthkart.com.", 0], + "aa0e45eb0325224328a519ada0ffb9e96c1c38b55ac61266a60e11b9e64c33b1": ["ragalahari.com.", 0], + "aa11b92ad77ca9cf445c597054cb1f89f4029f423602826f26fd6a5cc3b2057e": ["totheglory.im.", 0], + "aa371ccd7e3f622397d89fa97574dcfad601e37ae06065ab1533ae1078621c2f": ["sportmaster.ru.", 0], + "aa423368c45764dd93221a1a52a09c9c2ec322ac52bf5949b49cf336fce974cf": ["vig.", 1], + "aa4360769b93742be6742dc0174ad4e0e24a109d9383d9de8448a83b81a7e797": ["leumi.co.il.", 0], + "aa45a2ba63ec93ff6b7a04c96b683c888f4f08b245d8542a503445e72f16de58": ["mrguider.org.", 0], + "aa4c5146122228b472b1ba0d1e0dad113ec0bfa8e30beb32940d4d134735a2f0": ["hangout.", 1], + "aa51acbb88ed3a6cd5565ce6d525a73bde7d4bf9a0becb26b52b44f0f09e7fff": ["bipblog.com.", 0], + "aa6ee1bb70572235cee417a4382effff0d7c2286a964b061b687dd4c2d48a427": ["ctfile.com.", 0], + "aa71fd81db6c71a0da2ad48fba04b3bf6cbc6445401677b23a0345da5923457d": ["cryptopedia.cryptionary.io.", 0], + "aa8418440c911cc1f7085fd62fc233eca1fc3d1c2e84f5634432b993144b4186": ["gdn.", 1], + "aa89b80a72a6b1fa04e9b2d17771664183f1de434b7b4a19e96a465af1681c8d": ["softlay.com.", 0], + "aa91b828eb45afd54ba806bfb82548c43d9f61f1141a844b49f46a97fcdbb5cb": ["steemit.com.", 0], + "aa931614de10a622657daf02136cbfff65e959f3ff7a33f71ee4cfa42b305ff7": ["scau.edu.cn.", 0], + "aa944ba73b9716ea353b808e2b0a3c440af7ea660bf56029b3d0c7043c800c5f": ["arabiaweather.com.", 0], + "aa94adba7c29a5818c45557ce34ab6e6d975d22f14c08a02098a39203267df82": ["lkpp.go.id.", 0], + "aaa351d4f5734f51584bbd36f445e3d4e2bd21a08043ade2b23f2ec84b7a58ca": ["yugioh-wiki.net.", 0], + "aaa82c080787d2bddbcc99ea98dc6be2392a7c03a3711cdf4d8920a6e8f25214": ["madeiramadeira.com.br.", 0], + "aaaae98676b6d522432f0da4edc74928921cd00b13812531bb35e305bc90819b": ["mxtoolbox.com.", 0], + "aaaff5d5a223447ff9be961297a153f415fd0af2fe6a2074c5935ed6f1258555": ["optum.com.", 0], + "aab999f3df15e886abdfc4d83764779970ea4abc579ca9e308c2ed03982ac09d": ["virginmedia.com.", 0], + "aab9b8b6c505c8cc2f09e79d6102864f9ddf2eba1c6b66b8490d975289d79d76": ["lr520.net.", 0], + "aac592e73a34dfb818ff0c07acb56ce301615733eaa2db3af898039091720d2f": ["sonyentertainmentnetwork.com.", 0], + "aace6ad414334c671f989abe81ecc76216f6240411b9b1b296f8f8a0d405a277": ["pbh.gov.br.", 0], + "aad2708b8a7e9a598a8b099b94e4c625499ef3e5ee26d1a1c46dcd32911a996b": ["alrajhibank.com.sa.", 0], + "aad42904bc8c12c80be60cda88199255071a6b169b6694f95585408ef719a282": ["24h.com.vn.", 0], + "aadff565f65f591c4915297c0da6fc5e98d5d7500faf2d166c1016ddf3b170a3": ["vinted.fr.", 0], + "aae432146f8eb6f6df5c0fee2ac003db107d926bde8f6ade7b4dbcf25c92a1f9": ["zzu.edu.cn.", 0], + "aae71123fe319fbdb9bd087cc89b176306bb4d935c22e3173fba3e3ed6f59ef9": ["aninews.in.", 0], + "aae78958610093477491643c5ec952e784217192fbf470b999d8cd365ed402c2": ["how.", 1], + "aaed3122c968efd76ea67a22ac103ff5cc1ef91e7c3697c99b29a565034334ba": ["theringer.com.", 0], + "aafa4b10a12603d54f817f6f3f5dd524f0ba05262a8757f67120d003cad005de": ["kuokgroup.", 1], + "aafd3d635920e28bea8774d1da6182715b5573812b3259524fe133d6327ba44d": ["mangahere.cc.", 0], + "ab0506e97c8c0e9f9c0a1026d3ba6d19f2c22e2a167b4990ab6d2aa7b6d53257": ["gurum.biz.", 0], + "ab061a850bab5f6aca40505f49f00a46e8e554f0608028b5c62576f0144b3621": ["ieltsonlinetests.com.", 0], + "ab06e8701bc236f7294c29c879c51f40ee0a047d0c4649de84e5109391c7fc8a": ["book118.com.", 0], + "ab09fd1feca98a4f94d978f4bcf573464d207d99cdff8ce481a74148be5dac80": ["exlibrisgroup.com.", 0], + "ab1141abe36abcff01b7eb0f13d2000bc1a2771d2faecd6fe9c91d794a1d6f24": ["en-japan.com.", 0], + "ab198c3857633d03459ea705b6c1a01d2d49969a4f789de79964526e8655f195": ["statefarm.", 1], + "ab1c86564e082fc1856ffeede3bd76bade665d08070e80251e4b64ec2d20132a": ["viagogo.com.", 0], + "ab1f07104cfc279f1607052f4964e021ee2bd4905e91b0b4b8d5998325d833d7": ["ipg-online.com.", 0], + "ab23111c5ed942f011109e2c985c48eb07e960497f8fc22b87aa255c645e5a98": ["zee5.com.", 0], + "ab2ec9f60a27cf0b4cb32bb6729d358af9c6ceca0c9254a665f6f8179ae1ae02": ["wplocker.com.", 0], + "ab34d0dd16a4cf3f3900b8aa34230228de64444ffa044626867e654e4739199a": ["renweb.com.", 0], + "ab3a91e023644c6bd49775d3873f3ef5b5d915d4637a5f5730299ebe146a8f90": ["jetpack.com.", 0], + "ab3e0997605b928b7a298dba84fe2037a7dac85f0a99621de26fdd759e0864b5": ["boxrec.com.", 0], + "ab3f1ad037796e8bb7c11af057ea62bfd5387f9569b41d539c54d607534c1025": ["email.", 1], + "ab462de1d369267898d37611b6f76ccf293c07e6c71e2f5d1832d775b7b4f53a": ["ib-game.jp.", 0], + "ab483dc472af2816ca4668cdc677436c824e276512eae9a419e31fd2ace76d8b": ["utsa.edu.", 0], + "ab4ac9170b35d3099009a23347e13a44949eee5f8ac310f7d8b8c4dc4dc661e0": ["filespace.com.", 0], + "ab57e49aaed8c596e2af0491a412599557c1664f67c0e9d85dfc60b051786ca7": ["setapp.com.", 0], + "ab65ff833349a54f922f21ffc02c0938f93f45fd71c9322b56f31c389b2b0516": ["jameda.de.", 0], + "ab6b33c37250fb9e802b8e76f91fc397f4b18018bea799f46fd690b2f23150c3": ["appfolio.com.", 0], + "ab6fa147560238a12023931ff1938121d3ff4ff6e7cd166f61ba1a0d97c3600c": ["vidaextra.com.", 0], + "ab706cd323efae9701658dc59b24f17ecdc71928fc42d7e2183a48342235ed4b": ["pornhd8k.net.", 0], + "ab739f78c3c385e85294f7169015a8b9093a4fab741c4e85abba3da940989394": ["goshippo.com.", 0], + "ab833f009b81ed8ef0db85ffd6236e2ac5ed134baffdaf375ab8df9e4404bb77": ["luluhypermarket.com.", 0], + "ab85969050d0f0bbd37b02ba03df640a1f702e2e6ea0f7de1743b7d68230f817": ["toranoana.jp.", 0], + "ab98bf624b74fa76589cbd24883336c94e880fe314be7ac944a3b2c8ba4c4e93": ["2gis.ru.", 0], + "abb14c67eaa767c30e53f9345b9b26d649a2a4a0371f96829176d1ff90d67d83": ["cra-nsdl.com.", 0], + "abb22f520422d49629338899c62d588fde4e6abaffd16ca7d51e6e3e237aa812": ["manchester.ac.uk.", 0], + "abb39dddbd10c1ccd5f301c7798175bb86391bb19c526e81981ac4036ccc397d": ["myvideo.az.", 0], + "abb4405ef0ed714cba52b2bd5c0c96e46ec7ce227cc0b9e554ba38f46db02700": ["youzan.com.", 0], + "abb76ed4b9a4ded78613fb15e2926a94e14c51d714e0a4aa4502ae096ff08dc6": ["cyberforum.ru.", 0], + "abb97e8bf641058f693f975961c40a0b36ac9619a3b6339555b1993961411f2a": ["hdfc.", 1], + "abbc45384d8cfae045b36726ad2f899f0183e1947f06604e3d958516fc9f89b9": ["eloqua.com.", 0], + "abc16738cbc42e4e639f0ae466850e60186c87c19e065668c01a4fcd8b666ab4": ["canada.ca.", 0], + "abca0a0b5cfb7d1d9ca23d6465a8397a98f3bab82b502d079a266a8d4c03fef5": ["shaw.", 1], + "abca89e93ea5d42f32c1b30249c413ea4d7c6cb6aa50b9edb43dacc718984471": ["fossa.io.", 0], + "abcf2efd7a70bf0ab047af28f0cefa25ec2101ecb6a424be3a4243a532ee203d": ["urasunday.com.", 0], + "abd8afc7b4732aa005c2c64b520f49e489e8b2713970f67d38a65dd97428a94b": ["minkou.jp.", 0], + "abdf82138fbe5a03d996690e0afecdf9efc205c6be586db8d61fee0aa9ddb80a": ["umamusume.net.", 0], + "abe9f3c2906d705ccf3b7cdd5f7410a025b5fccb1793c22fe6b885698dad09aa": ["doorblog.jp.", 0], + "abf06b5a0d0c74d8c339ca268625f2377c72af332931d1c16450ef01dcfb1204": ["skipthedishes.com.", 0], + "abf5f3e44066838e144553570dd9f337e6503c6b2b67eb5aa4b94bfd45e41a44": ["scryfall.com.", 0], + "abf8737916641b30778ab367a4b26fa206dd885840801f4abb10c744d6ee9696": ["bluesnap.com.", 0], + "abfe774ae65ec17c72e9ccad67b797d022d4408a371fd477b94fa61aa4351122": ["redsys.es.", 0], + "ac1057e9b74734ebd29463f6273f932ae6bace340f76866abedbdec00907bb4c": ["abasmanesh.com.", 0], + "ac13f704f5203ba04c3426c59d53613cc14389e4c17839e9013e8678b1d45f9a": ["ai.", 1], + "ac14b967725fdb627a0749950d89cce228e11341766fa9279afe873e5c6aecb2": ["xnxx4porn.com.", 0], + "ac1630068bf57f666492d5523b98ded334f45c3148806c0ad7ca8e5953db0675": ["unian.net.", 0], + "ac1d010a86bc7375cb489e73e93c5b3ee2d5e183c134f38e6166cd38aee650ba": ["berkeley.edu.", 0], + "ac1fa6046dcbec8a8aa6579e6bff0e79b0ee866f00ce9896d9270a738a6bd0fa": ["edu.", 1], + "ac23b4f9409cb689472decdfb171858d86bd3332b987bd898c4d09725699fd26": ["getjobber.com.", 0], + "ac25241b66afd45c3c86af0865230028614340bd3f3e4c7a4d1cbcbf76f1a89c": ["blogterest.net.", 0], + "ac259f1ea52a84b1fd2a8a3627fd2b5be8463bf617603aa82a2dd9a415195010": ["zongheng.com.", 0], + "ac2e24eb1c8e95fd9f516e343fb6902142631f22870ff3f5471e95296367da1f": ["crown.", 1], + "ac2f9ae6c812ad67e7dbf2407f4720797cf132a43fedcac5f19b9d86546256d4": ["poetryfoundation.org.", 0], + "ac382fab2d1942a6174c6043446743aaa73d86b1cc5b7f7be0c5aabb8087a5e1": ["vehicle123.com.", 0], + "ac3b1bb3eccbd0f058196bec845ddd1064fd3e8aad8c57b8f2d543eb1bc61eed": ["bizmeka.com.", 0], + "ac3baf95d870064c08f5d5391512acc2b9b5acad81d5d04dd32fdea1ac0d6587": ["gigglehd.com.", 0], + "ac3d80fe683b71576a3ae91d35ccf9eb11699e7ce29d56e3e10bb53261073e42": ["hotstar.com.", 0], + "ac41d3397a73c9d70dde8781998f722f3a8a02d32e1bb3ffe317f9cb9a86e7ab": ["elbalad.news.", 0], + "ac448ca7f40d546ca20db1d282aa5282a01701f37d22e12d46b2025aa15cec01": ["knowyourmeme.com.", 0], + "ac46316a2a945549ce1757525c5b56bb507f68daa0aba9bda1d4665aea991918": ["niagahoster.co.id.", 0], + "ac4e3d7184d8da9f8d972faa685c384c60959318e7d860c1a90ce7a41deb8800": ["bangkokpost.com.", 0], + "ac5bacaefcce0aba9d8b5b60cf1d13f79f7797cc2b2bd7996a5cfb2e58195104": ["driverpack.io.", 0], + "ac5c2eebf4305f6a432a14bdbb665552d2c69d139b164ee5c812255a80b33456": ["docusign.com.", 0], + "ac608d55087b284d89c06afafbd461642105ac2a5b784ae22606981c15c96471": ["uba.ar.", 0], + "ac66eedccf27c4576becae12cb7a843e797b895e58e48bc8a051b32a2c772b10": ["vsopen.ru.", 0], + "ac681981f755e1a32c0e793e4419213144d86f483f00c0edcafcff8c1680211d": ["gpwebpay.com.", 0], + "ac7c3ab2583ae93bd17151e853a4dbe6a4c15f95e9d5337ac4ca0c4ddb84afc5": ["lidl-hellas.gr.", 0], + "ac7fa6228de78e42dc56c2b069226d187d54443b67def3342c0982de57ad2498": ["kingtime.jp.", 0], + "ac8b3d32b52a23c182d8730b99100074a0fabab4621712d31993221c9f8583cb": ["prowritingaid.com.", 0], + "ac91fc6a7ea923d783dd07859883bceff2794cce5dc51eb70205ad97f75de3e6": ["surfearner.com.", 0], + "ac95bc13ca48302039d8597d9b7114745179163b36b7cedb3db57f6f6b004822": ["jamesclear.com.", 0], + "ac97c9cd17096865d3422b9a363a55449acd0551c4dd4c2482b88c23cb4dbd45": ["screencast.com.", 0], + "ac999fac28ab9bdf6ed2416af868bc4dc92f3c6939781c7a74e81283ea5ca4e4": ["tamu.edu.", 0], + "ac9cabc7d65438b69a2bccddee0cd3ba5bed016419b275be33bbc0db686ede40": ["shobserver.com.", 0], + "aca66440a13eea18ab6ed35b1cc762f9390a2d6880e49dfa2dbc60c19d0bb14f": ["faa.gov.", 0], + "aca80269475e686cb35f9d16ec4f189daf66e1eff8f05499bf8d7d6674b1c4d0": ["sas.", 1], + "acc9ef1c8890910eb85657fcb5e0ac509c1c9281940bba8423dbc33c56eb9bfb": ["miguvideo.com.", 0], + "acd46ee8488c560de6e24c62e687ab12c2c472fc2b0789e582a0c042261c2cdb": ["dell.", 1], + "acdf7e884a914e0dc00e27f25fb428a5ab8f64dbda7ca3d13f1eb2440050c5f7": ["bond.", 1], + "ace7bffcffe3369542ad741cd2409ba81462dd69b3b590060ffb0787439de1a5": ["investing.com.", 0], + "acebc1f730cf0d26c913955695df6d694aef3f4eebc49699a29af76f18800853": ["smartsheet.com.", 0], + "acf9c854b6d58269e1667ba4c2692363428a631d60646315d65e926e8c8ea34e": ["chinagwy.org.", 0], + "ad01cce4bd30b8d6caf5991ef7cdfe71d7bf42e044017fc32794b4e06c7eb6a9": ["taqat.sa.", 0], + "ad150053bd3c6662889c76fbf1b36f46583a7a92b97623fd2c4df1834576b626": ["japanpost.jp.", 0], + "ad1ae29a3f60fb8507a3662c4f8425f49d5958b6cc03cf0cf4f0e86dbf28d705": ["vazhno.ru.", 0], + "ad1d2ef7a710d8d1e809a355bf1fb66f59415faed05f3aef9c1eca2856a5c4d3": ["putinho.net.", 0], + "ad1d4fa6de3263924949515c075abdf29c80f48f237e77782b587e92bc45c99f": ["slsp.sk.", 0], + "ad24abe8106c2666eaff04e661e2788f11ade1eab0e77950b23a901a3029c6fc": ["finya.de.", 0], + "ad3258feb9379e7a3e7383323ff3c53cd5a7c4f5bc00891a9235044a723be461": ["dentist.", 1], + "ad3a55bb04ca515c23e0835060eb49a59f1a4a5902c68ac156b326369c78d683": ["slrclub.com.", 0], + "ad3a75e29ae0dbb66ea82cec3ceca6646ea3cb3fbe157dfa8a84700c3e74e2f6": ["mvideo.ru.", 0], + "ad3d7cbf5dbcbbe25b097b055738e6d96a16dec1c16bd581dd0ca5293d758b33": ["coop.", 1], + "ad4038403e8fdccbc8695e0c5b1bef3b8e6e7f37a6b911368f9ff18cbbcf5223": ["xn--mgberp4a5d4ar.", 1], + "ad51c8df00009a6d422d94c963ad5e14fe6e140a0475023107f736dfcd87cb04": ["cab.", 1], + "ad549584e6bca0c479519787dcc1701e19572eebf294afa8370c5ec15640a46f": ["futhead.com.", 0], + "ad5522ad4f4d96708c196972e82d0bf156fb4c4b4b6804d5836940ed88b52093": ["brassring.com.", 0], + "ad587d5743c7a5c00a9949198a3a8d23e180dc1e007966c825e9127087e7beef": ["baike.com.", 0], + "ad5de4697e701a9e5ee31cbab0dede8d7ad1c60adc0058dd1bda6ae8d9b8839f": ["ilmeteo.it.", 0], + "ad628478d05c6f41e83798b3588ab16377bbf3e8779d6023f60c0150ea6f486d": ["phb123.com.", 0], + "ad6772d445949257064d24ae494dd0e156837cbfba0acb5148ca535761bfa6b4": ["gsu.edu.", 0], + "ad709e5e5bd408d119801d45f277b2b37d3b9878a9ce16fd6676c85cf991bde7": ["xn--io0a7i.", 1], + "ad79afa9691816a0306bfa4e92f7c8b6fa9967dab3891fad4caafa5fc51aa8d9": ["radiofrance.fr.", 0], + "ad7f2222165608e62ea52e0bf7cce0e1650828871a557d126672576585265505": ["tokensoft.io.", 0], + "ad8af8db642e933a253bd58f29c6db9ad6f7db5d487be1015b7a19549d651f1f": ["thenetnaija.net.", 0], + "ad91860468d38ff41f88dbf248f1daa431f2e01bd3db3f57c70a66092d3fe4ae": ["rentmanager.com.", 0], + "ad94eeb12d1bfed349c62e540afdcb97ef50b7798946bf63f5259e22443237fe": ["adfoc.us.", 0], + "ad96aaaac24497cfe87a1ab7c754cffcbe7cfb60ec237c2898cbe6a1387d9f28": ["schoology.com.", 0], + "ad9bcbf807f33a45710acef4c3d841d0fbf8ef16beb01c7890d9d8460650518a": ["dorzeczy.pl.", 0], + "ada40852e75fec2ab3c7cbdeb4497afcbe6a0192ca0c6d543b87d21139786134": ["natashaskitchen.com.", 0], + "ada898b1ac8623e3898aa9485d60299d1d8ef25b7cc04e66c3ad00ec628e4e8d": ["deviantart.com.", 0], + "adaeaf22473817602ea767397abc0795c304bdb277942facf1233a969a63bc8a": ["ncsu.edu.", 0], + "adb487b3661ef0ac1f14d215e3c6ca31becde10e667c3627177e501950b9b854": ["castcertificatewb.gov.in.", 0], + "adbeec258d8b39d0f82df6e6284d6a365c712ed81384305248cb8bda089d4e55": ["hiveos.farm.", 0], + "adbef689a8a0baf1eeababcfb4977e2de30e739b68138a37bac0df77f061d545": ["elchapuzasinformatico.com.", 0], + "adc396e919b78284f39291081ac7ea4a3f8556393f64951c1a714094c4684c39": ["alphapolis.co.jp.", 0], + "adcde1120dcf6955a9e2a144fe5783f6627dc682ed57952a324086adbf2a1762": ["58pic.com.", 0], + "add6170d19d7a2d5fd6c06c7f917df80f12cbef4d0b8d5e513834ba4eb15436b": ["melipayamak.com.", 0], + "addac5b5da9ebea6dce025a38846e280011ce5f3a74dbf703ab705c24ece93be": ["guahao.com.", 0], + "addd12f8aed799db364c1b3a234b350d35431654e454b851b5d4f75e66351563": ["teatroporno.com.", 0], + "adddd17d93941be3ca21209898d3279625e455463ccecea3ca5fb5caf4ebad5f": ["sammobile.com.", 0], + "addeaa8d7e41ecafbda7c5db71bc973e5262ec69cd365f5a1ac5b9c497333352": ["mindbodyonline.com.", 0], + "ade716bab05a66777b9de8a8ce95f189e6687985eb2c50517764f5451a6933e4": ["imagetwist.com.", 0], + "ade793d25efafcff2c3c9d734be2fdbb7d31e22a7ff290ec4e66fd5dd5e890bb": ["indusind.com.", 0], + "ade9624befcdce0d524c18d8b975f070a40e46dc4e6c4984c4ae202a00110922": ["cnr.cn.", 0], + "adeacd73e2848d0c3d37958acb1a2019919aef14b6d180570d28b839c811153e": ["delgarm.com.", 0], + "adf20028551e477e9093ed3953cfdc655a622f08323146292d7582d52a007c30": ["vecteezy.com.", 0], + "adf39d4aca774c456bdfbb57d5d20fb906891904813f2c77107657f263dc0a36": ["hometax.go.kr.", 0], + "adfc4831e629535f6800a7c07d3a696d8dbb3f4b9b8f640f5f3a1da16892d4dd": ["qurancomplex.gov.sa.", 0], + "adfcfd05b4f872d721ae58b3f06fcbd10355c2e3c95725d240f9b685ae0a92a2": ["punchng.com.", 0], + "ae03d9b827772b943decfc8b98ab992e6e16358bb18276e9d2ccecb1e1c6cd89": ["walesonline.co.uk.", 0], + "ae074221bae7fa008ee3d667fc1047bed3e19bba411e47536cd1ced1adc3c83e": ["shanghaitech.edu.cn.", 0], + "ae09e3fe70ff61175e4a8011a466c7a92af389ee95346de3b28e399d02197e5f": ["doshisha.ac.jp.", 0], + "ae11cee1f019489d427e9192d1b15cefcfc8d08142c0dc4ed4125467f2183fac": ["nga.cn.", 0], + "ae1747bb1feaef444074ccbf16e87f2970be3286f068f0328f2959effb6ee8a5": ["juming.com.", 0], + "ae1a700746653914e1e176122bd6d9d0ebfeca47704a08ff2e5227c77d5d47b7": ["realmadrid.com.", 0], + "ae21aa4b57ee288bde70eb43f251986e927d186cc7cfc0b8e5476cb68926f37a": ["tab.", 1], + "ae2a78d455053537511705e089a2c8b9e25e7d05bab8cbee0e36f7414916bf79": ["americanexpress.", 1], + "ae2c6964455dd1f378c1ed16c873866505b63d8c4296245e90ad9057025cdeae": ["sparanoid.com.", 0], + "ae2e856d8ae2dfc8d56deddc891ecbca0e9bb2367d1c8432d172286a6f12a470": ["ency-education.com.", 0], + "ae35c11f1bd12f880d3502fb3d22c0428df296339c4949cc5851145a108df2d7": ["uveg.edu.mx.", 0], + "ae3e5075289730ea4edd46b0e13c3f3acc9c6ab1e046d5cb95f9f1c0a907ddfb": ["lrt.lt.", 0], + "ae3ff0df1a534fa087c64464a56f71d3774633d1498b04229643adfaff209795": ["chronopost.fr.", 0], + "ae44be5f9233a37d88622341733036b0f9de1d9279a2f00861586b46d462dc3f": ["dol.gov.", 0], + "ae45b8725b318311c1d40f649c3ec90115fa1ef2d56d019243c7b33a04339e86": ["seagate.com.", 0], + "ae47baef5c50ef28532e4d800b9e4d54d28b1885254a34a78845cf29ffd5e691": ["puretaboo.com.", 0], + "ae56497b90c27cd3042f888f1642282dc5d7666fe7ca8d835992116cc6efd34b": ["123telugu.com.", 0], + "ae5b93b2ce4253ff00ac5065433160283575e1dee7011b2c688ad9f48ae62403": ["dtv.", 1], + "ae636a3e2312d2ccd3962c2a4f83246131d924782c5a9d59b90a56e816125c82": ["wbifms.gov.in.", 0], + "ae6685fa963ce5d71fa1aed71f58b98dabf8aaae4de85c8ad47b7d90e37b01e9": ["boats.", 1], + "ae683a5d0eba181e93dbcd0bacd5a7f5c76d659c553c3e21babe3c901fa48407": ["getfvid.com.", 0], + "ae6d1d0adf2f31cb84d676f3eaf7024da7659606161ea2f1f448b5b14b2a5e91": ["zaycev.net.", 0], + "ae71e99421bef07b249f02663d6b41c03d60d2bdfa76b54ab0682d0eacfd9d7c": ["gf.", 1], + "ae7388b5ae567cdf3e8ee42a8d6f494354f1242a59ccc9b2394d388248bbe667": ["heb.com.", 0], + "ae7d7f1bde40cc06a49979026f960919cbf52e89eb95efab09fb0f956be485a3": ["bukalapak.com.", 0], + "ae96e52f1e78aa6d3f443e00666e73517f1e2830ad6e781a794ed27577245277": ["ip111.cn.", 0], + "aea1201b9f13732ec6c75d3884af8fa51ea6ff9cf564339ccbb5fbe413a46a6d": ["giallozafferano.it.", 0], + "aeac486a85ef9b550e1fa31a251712a2db283cee04eac0cfb8788c4c3456fad0": ["huishenghuiying.com.cn.", 0], + "aeb5b5ae34cc9b906b95497ac7408d995d49ad5d1f194738a3bc696da1a5ffae": ["thaiairways.com.", 0], + "aeb8f5d233e14f65aa5be48c456ae649ba10fb07c5f5b43555de331780e048f2": ["forhims.com.", 0], + "aeba82493692703ac1c0cd36ed8c8204767fffbe298dc517d93d54fbcd615b83": ["diariovasco.com.", 0], + "aec15a0e94b3885e9d7562cc05616cb505a3b01da76cd6ca03357a9f25c7af7b": ["tomsguide.com.", 0], + "aec3d7e0f5bfa77cb714ce73d4e54e9300a8692a85c70bfff567ff2ed01485d3": ["baidu.", 1], + "aecdbfba0f4154d81ce5c21ce39a1a5e3ec9e7736d74da603837770ad359ee77": ["octopart.com.", 0], + "aed43db1bb38f6cee6449e7798b2fcecc410e15bc30d66dd4683595b54c1f1ff": ["concepto.de.", 0], + "aed4864532d810ae37a6564dc91e7a5a19d8f1c60b37571013aa94ad99a30d95": ["valyuta.com.", 0], + "aed4a98d5910fb68f6ac3ed19542fe1e55d507974c5eceedec293f72cad1aa8b": ["subtitlecat.com.", 0], + "aed54661509797e518e8daa9214f493158174917776e2b35df49aa4d8e2e526e": ["bizbuysell.com.", 0], + "aed54e8c6d911c004f133e822e662c9996d1979a92723141ce28ccff69166c5d": ["studio.", 1], + "aed6eb9a816e65be37ffff2e6fe251517e727dd819699f49cc2867ba801ea7c3": ["myshared.ru.", 0], + "aed86637756a3d2290b215273715e72f8c005cb26194c9c1c5cbcd72bd6ce22d": ["orbitz.com.", 0], + "aedba9e5c432b1a6ed46f4457e9d9e700c234b460c2b3c23a4d7c9053777eaad": ["blackboard.com.", 0], + "aee038996c80682277999ddffaa978f8caa136f5e614d8358ee2c562a20d03ed": ["onthemarket.com.", 0], + "aee06ceadf934ffb078c6b0fe886c714d4ad96190f79c9c967b3782dd21b9d11": ["jmp.", 1], + "aee4226da8bcd5e0e2bf50b18e1b3c1c7bd46e345f31b4c16ff38db610b129cc": ["cengagenow.com.", 0], + "aeef1dee8644dd2cc09fafe1a37f7ac3c112e9074f94e834156282dec2897a11": ["skynewsarabia.com.", 0], + "aef6a90ecaa75cef348272205ad537563278d3eb1e691edf55f7762c7006ac79": ["jav.guru.", 0], + "aef824498691deaca1a6d7de8d14d06257a6e450d324d0673e39d785fe7ca480": ["chinesean.com.", 0], + "aefbeeaeb5ddefdf3011794c50822a3bd90e8381bdcc1d1ceb0bb9e062df040e": ["verizonwireless.com.", 0], + "af000f26d3a2822a62e8d3814ed4a0d31652eb946043720b38f216c842143098": ["10010.com.", 0], + "af009acca1b188cf78e563d85ca6736592ed15c8f36ce27e3a8b87473287f961": ["nbd.com.cn.", 0], + "af09e0c37d449dd9216245a8a185084dbbfbbd29caa8d607382dad7dee07a055": ["redis.io.", 0], + "af0bc3181bdb0f55be98aa044aee7e7d4a26d7359c87b8eeccf0c47095774e71": ["wacom.com.", 0], + "af0cfd6093b925ea764078c3f16e1eb342bf98b4d1b5d3910f9ab6c440e45a80": ["a-ads.com.", 0], + "af0ecaa75d51081944121a700f14f7a2f67d9c0f049622dca7dd54b74018365e": ["pipedrive.com.", 0], + "af0fea8e574b2cf640ccd8846deefc96d56ee05f682aa8b7951b4964b1493355": ["dk.", 1], + "af1eef2aea8232c919858d41a6956d77f27ce239dff083dc3856f91b22724710": ["aamc.org.", 0], + "af327e80b67f24a7f03e2f25e20a4d22a1e70f452024d95da93d905d3dbcca14": ["4399pk.com.", 0], + "af32d0b940e0194bdabeb7dcacd3433742b7c378d3fe69dc4e57f2c48c54e2c0": ["xn--kprw13d.", 1], + "af34f24d860473f5c12de913013cb3136993acdb000f3c4d6ba0510e7ad9010e": ["voicemod.net.", 0], + "af3b11d2fa6b3dfb4e125f4ef0906bcc48de20365e9aac5a4b5a7a3f2101c8d1": ["libsyn.com.", 0], + "af4501b74a61e3513177ddf11f6e7b23ec48dd85d415ef726d27a6079fa0f99b": ["football365.com.", 0], + "af564fb97a386fff8d3f1b879fbb7998dbfe28378cf10ee5954e6f839593227e": ["13dl.to.", 0], + "af570ad2da1ed8b6a677624f2dc285e2c6ead38b31d575d994976e82d3c9576a": ["anquan.", 1], + "af5a10216a0a8a5c42559c1f113ea74a2b7e6b6f164863427d49a1821cdecf1a": ["libreoffice.org.", 0], + "af5bcb4997c13725c6a14a572cbb017a490a88ab230e8244619d60f93f235f9f": ["bancochile.cl.", 0], + "af6ea5b6be456a8f82fad69ba96021350c310fd73a0377ee45d733146a4dde6e": ["thecvf.com.", 0], + "af6f29794dd992c2430e3edd278c6e322bb7527f71de69389527aeaab9d4f582": ["qconcursos.com.", 0], + "af71f91f5a0983e3be96c360d14d25de957f57cbd859302b1ba2e50b5e658ae5": ["umanitoba.ca.", 0], + "af7b71f6cdc93edaa729be68c7e1fdae96c04560e0fb1b121102c9de61e4b5b9": ["crazygames.com.", 0], + "af881343cc7daf6235bdbbe7358e2b378851ff546df1a8445c0444acd532ffa7": ["tubeload.co.", 0], + "af9bec3a9a5db80143a95a7fc70401065c8bb40cdb9e66b907f5eadb05ed726c": ["uptodown.com.", 0], + "af9faf94b9df9cf63bad26c7c2043e29f2c9079aca0c14c65ce7bfb1759f9e08": ["educba.com.", 0], + "afa74f1c123b8ee2612b08e81c4147eb945466640ea08a5ef89247187e9276d8": ["incfile.com.", 0], + "afa998021475363b7ce364c1fd1489b760112f836abbcffb69c93706c3bb4057": ["bina.az.", 0], + "afab32b6b795ca0f0cb9fb471a157318585767be0387bf1bdd78196b00e6578f": ["gbp.ma.", 0], + "afaf892971a8ca9acc9ed5242fd710b53432c2976245fbb860f3938bb7e3b23f": ["ceneo.pl.", 0], + "afb010a05b3ab8f08741d074532a0eb80fcdf7a847c6751445cba0e9a4c85af6": ["inpixio.com.", 0], + "afb660f44a3db5ed153e43466bda690401fe23dc289d0da28a0e395f4910b0af": ["map.", 1], + "afba34a234d1e5027c11da07da763d5c09009fd40b9248dcaeebf6f11485428e": ["kmcert.com.", 0], + "afbeb125ec1532054d7d68cf6578f9a11fd46a510ac51f212e84fe90387b6497": ["travel.", 1], + "afbf2dc41d32b8e01d3e40242818976a6b9eb3938e0e47dfcf3f8a529ff21945": ["myairbridge.com.", 0], + "afcfefaba8229e1eb7d8b3d030501ccbadd5374dd6c7f3383d2e76f828a7aab3": ["hiv.", 1], + "afd267c721c166b39c7bb878d692af7d6caf98cd89b621a20e1b1724cb0a2231": ["homedepot.", 1], + "afd9222e7af9a5198a6ddf5acc616c08add1db57bc8b6ad2881c34328a3a7a9e": ["apnews.com.", 0], + "afda7181693f83c4a4c0a68afdd26edbe30d9bd1f31a6a2bdda548e09f2a9244": ["compressjpeg.com.", 0], + "afdad861a1505905d6c91df249e0d734d0e31962be0a004d43a5a20e245f6a98": ["metamask.io.", 0], + "afde89e68e03fd41977541ab59603ac22db8364f30cd1619a94f0a6e126b3921": ["whathifi.com.", 0], + "afe4b11d80e524a0d2f8ab60ddcf746602d426e5178d56139df96a74919b42fe": ["overwolf.com.", 0], + "afe86b160b1ace6114e7e9a2013198bb3684f73e8d787612b50a39557bd5109d": ["yamibo.com.", 0], + "aff1f15e56980e2b466204021bc042235735af4a5752a1132b47f2eefc499591": ["xxx.", 1], + "b0046595f1216609d1335f92ab0ba124cfa385ec439131a8b0ef808425389b2b": ["photography.", 1], + "b0056c8da7a01eb420af6c1961f0593158514ae9bef3c38371c6ec579ef9521f": ["analytics.", 1], + "b00a24bae6b54395ed795562a971002b9c0e20b97adff91e7be0ad736afa7a0d": ["bbspink.com.", 0], + "b014d0eb1680405e8d71e02b4cbeadb8289d4986a2ecfe8cf5bc2f3ceb133a21": ["ea.com.", 0], + "b018461f742ab44f5227d684c452a5067d91559d2552e1eca33ce22e7ac27c3f": ["axar.az.", 0], + "b01a489ee46cc660cd837171862268354ea1b50204af2680329d305b684b5227": ["bjx.com.cn.", 0], + "b01beab9a24fd873688d3c39e87f8c7d847d7f1b036544f3c3d41390b7a39eae": ["weekendhk.com.", 0], + "b0216319dc3d4717df552e5638055eae6515a93af24a79e7eee81d5974deee5c": ["nubiles-porn.com.", 0], + "b0356262f377992302b6e1fe2e8d7aba75d0276963bb76824abe3d7ac1d2e856": ["theconversation.com.", 0], + "b03b0a61bf8e1e2b31884950849819fca08ad3735b51502f0c148dcc1fc85e8a": ["cofool.com.", 0], + "b03fe575cee8de77583b912daa2c5edbb1b831d1ab6018af51f72ee340f00b8a": ["jamanetwork.com.", 0], + "b047533efbed210677f8aef8d100e4d9f87b14eed5a02069e45bd6d56f5e0c71": ["publika.az.", 0], + "b04bccc99e5fa9b00cb568cad4370b69ecd85c162babc0dc2dcde81086f28b76": ["muchohentai.com.", 0], + "b0594b52fb8ac6372707e5673097491ffa234f6ad31cbc66e9f4eaa1382d1df4": ["sport-fm.gr.", 0], + "b0646c5e545a41bb1557303af8b793b65aa53c8eb7ffe626e454d43de2ee5601": ["geenstijl.nl.", 0], + "b068b6ccf5e747c500e6b08952e8dcfc10c428b1c529ecf321eca0fb94e6ee04": ["lilly.", 1], + "b06e0e226f53e12dc6c3ccf563c225da6b006283a133b3d9be39477e03647c84": ["novosti.rs.", 0], + "b0824df3d22fdd969a22e938e20460fd66b6baa86897073e2fc17f173a3aec11": ["add0n.com.", 0], + "b0919ebdff0036bedd8f70249a377095d5e953855b4bdc5f49ead8956e55f046": ["6park.com.", 0], + "b091abf6e5fed33bbd8874a42f8da6fdc6a47354724b6754fb4b94868aa225ce": ["plo.vn.", 0], + "b0981854fa56de326bc2889aa04c82274c641b7b518584267c1768b5f25fe442": ["codeproject.com.", 0], + "b0a2d5c42516b1593c6e1facf25e77a1fd19436e5706673b0a6d816ed11650e8": ["suumo.jp.", 0], + "b0aaa7cc0e5adeddd90a39a34a6209b59a8796b2489962b2cd5bb261a6c5ca94": ["hubstaff.com.", 0], + "b0b652de59fd27a8cd20fcb863d4855d0bb849955e22e180bd731fd30d0a2e7d": ["glodon.com.", 0], + "b0ba1d2e959966a7e34a5c5f3a40a2221177ad6cc9ab2b3786e83fe19f1406be": ["digicert.com.", 0], + "b0bd14fade99f9d5a236f33d73998ccfede497a8cb7b10a7d7e74108353c6f28": ["xilinx.com.", 0], + "b0c03149d41790bc62715334297563c77b7180865b0bf2c73d1f7b6d4e233070": ["almaany.com.", 0], + "b0cf6b6e5a4bbc6edfc4a30339b803d9caca72cf150c8bb9e078be0a3bd76406": ["cleartax.in.", 0], + "b0cfaf13bec81d061c15f302ae2c5f4aedcf310096247593a42fe92ec1922815": ["36dm.club.", 0], + "b0d2579cb6779fd3a4739dadb9582d8acaaf9df1611c1b2c472b368f5a8c7f08": ["trafficnews.jp.", 0], + "b0d26afdb8472fb398a181c934dd14291e935ac6d78e51038db6a826d1db3aa1": ["roboforex.com.", 0], + "b0d504ba29de8377956296b7ad4a8929a582b64581ca0186d621ed76ced800b5": ["pluralsight.com.", 0], + "b0d5664aa0c1581c51514571e48743ed073f88c619cd63da445c493a5e255c1a": ["pakwheels.com.", 0], + "b0e12e24f885edb379a949f2357c1f913dec527ff5657782b905d302778a846d": ["sublimetext.com.", 0], + "b0e40caadfc2f9ba71d2bf71dd4be6b7e169328854d001f4a3f683fa63a21229": ["deppon.com.", 0], + "b0eeae51409e162c73a8fae872cebd9ff83438f2ecf5f4888c330a8250eca907": ["iata.org.", 0], + "b0f0b7cb819bfdf1f421f119cb56d25a5c31a70a6db3ece5c1a84c5def15a40c": ["courses.", 1], + "b0faa1e12958c4722336a638e272b40489275521bb0c60ad74045c6af8d3559f": ["musicradar.com.", 0], + "b0fda6c06f157f5a44ad87bb168bec7cd8d16adba09bf3b59a37d73a6e546772": ["harley-davidson.com.", 0], + "b0ff52b18d7f18f02ecf9c6b931ab959e84f56edad2be16c0738213d80317a80": ["jugantor.com.", 0], + "b10151c9387d5a81c0f84a6c6a315d3b0ac049c96423010b0d753876188c3883": ["hostloc.com.", 0], + "b1049b8b8a65a8e799a46df8ada3fc113ebb77f3a2735e6040b27b1b7ca2f834": ["fu-berlin.de.", 0], + "b10cbd2c3baffbf11705298ad7b9a822a1a357daf270777677c2d0412c105c02": ["algolia.com.", 0], + "b10d8b5886b6aa16f8b22ffd9b1c9ba77d623b4b46624fcb47eecb1a6a636fe0": ["hupu.com.", 0], + "b11150f87130d6ae5d7f238ecdb9afa674d4939fbfcd59e4327dec07431958fb": ["webtekno.com.", 0], + "b116dfb478af79b21ef68a5102e5267bc82d38f476539d258ebc0bddadf52947": ["panopto.com.", 0], + "b117e16cfa0db71681ef2b076e8baa89757226a81c4929d4e780c29d3a0f56b5": ["everyjobforme.com.", 0], + "b11d013ed51bbfa6d1835638e560ef4ba2d29c3cc1c00eaba145a121b3b71c39": ["livejournal.com.", 0], + "b12766d1f9ef28bf9ca7564f22e58513c9ed2ef583ae64def8ef975fae031a59": ["astro-seek.com.", 0], + "b14a1c5447bb1eec61f62bf472e262e8fd961206734bf55e12c0933438100575": ["eduzz.com.", 0], + "b14afbb3b8daf0358c4e641e60ba4cdfb3802eb0f5d5b4f9909b20691d71e775": ["cheezburger.com.", 0], + "b14cd8c91a95529f91b2bea19b1862fec78a3b6b2ea04bb5cce744b638150a25": ["loan.", 1], + "b14d43cd70efabdfc6e9b015e660a4914298d33f8de240853588713a4070be5d": ["deraktionaer.de.", 0], + "b150c371f43de8db4f0feb6ad729840db106c0ef75510a611d4006c2662f92a0": ["f123movies.com.", 0], + "b15530cb4168a41ac017b73510b150114bf1b64c3edebc0eaf6998d58b9974a2": ["onlinevideoconverter.pro.", 0], + "b15f37fbe7aea95f37d0caf8fb5df9e8669c735a9d60c61b6cb636a2386ea79a": ["dailykos.com.", 0], + "b16d56be39de2a0c537620d56dcb554f725356b5ee55509bcce888fad117b520": ["lightnovel.us.", 0], + "b17080c322a24e8d21cdfa8e650875f7d11a2584a184f7822e50cb345278c354": ["xn--czr694b.", 1], + "b17582db83312519b1063b8cb972ba509fef999059250ea42531206131c93cbc": ["football-zone.net.", 0], + "b175d622a93c59d5425da8c1f2ebf208cc7dda14e187b842e4a84b8cdb39c5b4": ["toptoon.com.", 0], + "b1788197ec46043c8119f42b05a2837e95b5ea40fe6dd4fa4c9b150b6bd87f2b": ["ubs.", 1], + "b1808d3c555a3b734ed5a980ff412874a669f63644f43bf3730ba83023891aef": ["dideo.ir.", 0], + "b184a5d95d1126e037abc88282c5b4f9743299487d8e9dc3e79066cf10724bbb": ["eol.cn.", 0], + "b18cc3343961e714d7bafdfb09bfb8cba9b2fc3be46b44e952060267d6e9bdc7": ["zillow.com.", 0], + "b1906913aa1b18951b2350f55e4fb5cd4ad9c9324fae1ad430be948fbb0e8df5": ["xn--11b4c3d.", 1], + "b19670404842cbd8cac1115830bc3e2fc4877004ffd99a069c889fa4212de134": ["ffxiv.cn.", 0], + "b198a27500e390c21c0b2177b2bc347e7d7653155a261e28b85048c9fd8de2ea": ["abril.com.br.", 0], + "b1a6f759ecd8f1369d1001674eacf79fddf457703cf8cd7f3f62409571297901": ["jobcase.com.", 0], + "b1af4d34d38280121ffcb23dc032512e8a379bb3c71f732597f18a038c295c98": ["hrssgz.gov.cn.", 0], + "b1bb953fd308f40afdfe55a27210f0b708fcc5b561bc3c08f6f13cd8ff776c04": ["senscritique.com.", 0], + "b1bd7d8cc5b141d1e4d1a7350ec9db212046be076a91bbf65763c24ca8fe19f6": ["polito.it.", 0], + "b1c30d9f6966129c9911fc5738946ebab870a54dc08ad7e5274f9eaea42f8e84": ["javfree.me.", 0], + "b1c3e1436e1e621dd8e9c3e3d7f4c2782ff5fb4ef220c29aadca3cc36ffa01c7": ["amplifire.com.", 0], + "b1d309eb42367c22d34fed9db8c61dc651c58d479a96fd0ea3c49106c82250f0": ["novinky.cz.", 0], + "b1d4a421b5407c8b024dd5bdf88cd7b7127adf955af92b2f258770c99e95d4b2": ["screenconnect.com.", 0], + "b1ebf14ad37208c57926195f16bd465ad3c54a6881313f3887c8a753898fb9bc": ["kooora.com.", 0], + "b1ec7d894932effa4da5c79755f23101b188d130f2d5036486e072dade093876": ["capterra.com.", 0], + "b1f5f6d33b8537fd8532be5e1067ac10d680ac254627ddc03233b61632285be6": ["maxmind.com.", 0], + "b1f637381e3065ebdcf3b2ada9041e1770eb7b75452a3f48f61def560d311e2f": ["playno1.com.", 0], + "b1f86bc91e17845129b648837cd70a73f62fae822a07dfabdb68eff92eded76f": ["xiazaiba.com.", 0], + "b2089d7b5c3e3c0e33046976126d1ab0a1ad9df63b289572371747e8f10fe9d3": ["epochconverter.com.", 0], + "b2092216b03176d761fe02839d5190f0378b2a66351460e28b70d280d199deb7": ["mangapark.net.", 0], + "b20b3ff9200a6a5e061af2e7449e1387055e144d4dc693a04d8e290a0d97541c": ["enamad.ir.", 0], + "b212506c465de9ed6d03ac5e91f8dc83c16d825568f81fddc99c1484ad757035": ["carid.com.", 0], + "b21d6d14f24c9694a3336b8ed8bff2a785244e2cf58906b7230c3f0959da69d7": ["factcool.com.", 0], + "b22132c27886bfccfc8fa3bd144f7850c65608579877e52c22b80f2a4ed21fb2": ["ietf.org.", 0], + "b2265f8dbc0b3a82668ad9260acd85a3bd9cc0ae570750cf2b38d8fc25894b8e": ["filmnet.ir.", 0], + "b22e30205ca4ccbb6b42c8ac514271d72adeb4c67cbb4539960b6c995ff242c8": ["tvp.info.", 0], + "b232892a1dfde4689665832dfe05c4295833d5596d5c145d72f0931791511ce2": ["shipito.com.", 0], + "b232e35154fe14d72d1463ae45d2610418842f37a471c2d6eacad4375776d04d": ["kuronekoyamato.co.jp.", 0], + "b2362be5767c81d6dd5c06c1ccb85be500f8c569b73393cfe8e4b98a330ae079": ["tl.", 1], + "b23761e8fdd9eac5e858ec38dd9d40202dbeb80e24a72568366d7a43b31c41a6": ["akashi-list.me.", 0], + "b2377e6f3675bcc08b365b0d69f11911bc48f97d8512685c41055a6bb9ad7571": ["quechoisir.org.", 0], + "b23a7c22af501a47cdebc0dd7f578c29c1e7d395c6e1fa9644bc664aeb3a0d0a": ["mytheresa.com.", 0], + "b2444dd8bf0c16778e644a787ad9d1e1f8044bef278b2234ba2d0e366a3e8084": ["youjizz.com.", 0], + "b24b1580b6d8295ae40ebb78c4a87c96cffa387cb90f2e4f603055b85f7cc695": ["sympla.com.br.", 0], + "b2581d54b57ee7dc3af80a2278f35618841bfa83d55e388c05aa9ecbaef2062d": ["bid.", 1], + "b267e3fc110bbb7dde7beb225d58dc8356f31538c2acf0adc60ca97972c97bd7": ["evo.com.", 0], + "b26c64e720685890c6863c664650d1c69880db939386c19199af5e344894f92c": ["cisfrectt.in.", 0], + "b26ff25ac9a63f9cc0cbd1ed405ab5cef5dd4f9fbf69ce5535c8028e23851ca2": ["ipaddress.my.", 0], + "b273f469044a5cdd630835d6518fd573c033bd1d9e7e5fedbe1a3e06cae325cf": ["rtlnieuws.nl.", 0], + "b27e5e1a94b9926abb6b20ffb82a3bf836aee055cdda02692de6edfd1eec4e1c": ["directg.net.", 0], + "b27faca1796eacace9e92d66656f2119da1ef20ad485bc7b28640c951fafe5bc": ["bankier.pl.", 0], + "b298fdcfdedb6d9f84929681e511dd2fdc07a303050845641778606f5f7db769": ["paopaoche.net.", 0], + "b29a5b850af7d76336d6e3af5586f99cd063a04509e8b24ae77f0a41606d11f8": ["steelseries.com.", 0], + "b29ee36395946f5e4a9035d5005db5c6335c545cb2521903542e68bcb87e737b": ["livescores.biz.", 0], + "b2a4a41388b0cb3693cd12eb6c4db5921d112b402917476a7fe8de6d3ee25589": ["porn00.org.", 0], + "b2a6bc5369dce303b6522622502a36c12545f4462a29a34962c6af56fe5402d1": ["wur.nl.", 0], + "b2a7e71fcf2ea7d81c77c2708c5663ec4aab9934d6f0f38f293fbdec97a314ee": ["namasha.com.", 0], + "b2a9681483dbedda1668a1be444c950794f3d83bb27b7d1a0af0b78683b08aa4": ["rakbankonline.ae.", 0], + "b2b0ee2a2a09f3dfd797278411d9a208c676406f034346a973844341758d97e0": ["135editor.com.", 0], + "b2b3adbded06c278a14c325da2be62675b84d7e482c130fcaf8da13ae00d610f": ["bitly.com.", 0], + "b2b836eb5ec796697e69c5ae2f8cff97b0f13d2a51fe3c60a25f4e77ee03d90b": ["nintendolife.com.", 0], + "b2ca99c3f69e4182a849f687ddf03adfe8caff4f1b89fb7a181a9ee803edf4a4": ["verywellmind.com.", 0], + "b2cedce86387c84ffe3148a79189936cea4c162e2177fc3de6ffbe93bf9795f7": ["speakol.com.", 0], + "b2d145a8ebd03b77d8fea012e682a8e32dfa4fca4dca68fecc436d205067a2f5": ["questionablecontent.net.", 0], + "b2d275971a456386f422b7b494341f04cc54a4f68c44f66a118ab8fcfe91294b": ["tgfcer.com.", 0], + "b2d61ad60f658ff75a7bb5fb4b056f3ba7bb0a61e76e021c521ff7b2b8902fb8": ["awsapps.com.", 0], + "b2d9e394c01f688a7220db545882ad592160cd295ce064afd2ff68f6feb34e73": ["lookae.com.", 0], + "b2db307ab4d3d2253d535d016129380ca66015bb6bf3d373065dffb106230eed": ["tnt.com.", 0], + "b2db4325f0a86325ad15d995fadfaafabf1908cfa45b16209a771302b0318e33": ["bnc.ca.", 0], + "b2df2b80978f40b2876be4095c780067772348f77fcd6c259309b241afa78fc1": ["hentaihaven.xxx.", 0], + "b2e980e6d6a117b2a0be90ae2cce6dc678dcb74e781a6bd860fcc747d32ed68e": ["ati.su.", 0], + "b2eba64c4e964d3df2d68973ccea1876541003c3ee1c2735c4f01dc5c5ccd9b9": ["ninisite.com.", 0], + "b2f717422a855764e4109e2d166936e870b78e82e709726b179e39ad7c4f2f0a": ["vjav.com.", 0], + "b2f82e4f8f7456956f1fa95b5c71a5066fcd8a1c216f00c882fea1406daf0c18": ["embedy.cc.", 0], + "b2fa906c7f36760de8384d7e18aa95e107e0fc1b71931246eeb6533f8a88888a": ["livescore.com.", 0], + "b302383f4fec15e3f411c8e7164bfd877920f495b87017387f930801ca43e281": ["msu.edu.", 0], + "b30be54cd199f7074b2de0ef1572bc6abdf8776728aec48295186158263129ed": ["flixcart.com.", 0], + "b30d6722d93936899ded9483306f5ec2fbb4ebd71c0e325ce7ee92074f497b3d": ["concordia.ca.", 0], + "b3129f6d921538edd30f53d3fdacac7f45845a5cfd6e9c72061ee69819a85eaa": ["whatfontis.com.", 0], + "b317933e86ed5118a3e8c32797a3424abb3220ed31f459bed2f23f625602a5e5": ["opendns.com.", 0], + "b3183f30dfbce8089bec580e19a236269b41ed3d1e2c540b6aa505f1768e8617": ["firmdale.", 1], + "b31d04a7a4aac4eb658fe584bd015d50a70abd2867d524f5d534343a5aa4ba93": ["hpoi.net.", 0], + "b323e083eba79d0355bcbf01f378377c06154b857eb3466bb60cf32961729fef": ["geenmedical.com.", 0], + "b324745e2bf32bb53d80b63264aad39a92d10e538499a4373a62f58099ad3697": ["bigrock.in.", 0], + "b3249a8c23672d51f659e45f7c23a023e9daee0d115b41f0248e73d00d0c03bd": ["103.by.", 0], + "b3276a897abf12c150924e71d0d05f485709cd1c3e97d3cb51222eb78fb70626": ["rfi.fr.", 0], + "b32e3a294f2f43b7973d142df2414e7a43a3f016bd2d6f4f0caa1249edb6a115": ["avvo.com.", 0], + "b33001d001f7709c0dce5f0849f314c30e733e9d4f64d58f514cc8f134806be6": ["mufg.jp.", 0], + "b3304aef289fed4ce805d928da96da70740f41cc5acedaa08723d4faaed4b731": ["arealme.com.", 0], + "b337c24977b672cec1cefe2a3f5e57ce4ee936cf7de383143538928f416c94b4": ["fubon.com.", 0], + "b33920ea84da55e38d2352a58f1c307460e42b5f44ba0f23049b17cc76a3aa71": ["accuweather.com.", 0], + "b33a99411581f9c6c490b311434e8fd9f5b568dce5657d15354718ed63ec9c5e": ["ecourts.gov.in.", 0], + "b33bc942a71e727bf1959e24ce5b860903eb7529167cbd2a7f63f76ac691359c": ["motortrend.com.", 0], + "b33e4d7a087919999cd317369091a54c68691f7c93487b246e278f7e3d7e02be": ["icegay.tv.", 0], + "b340ad7ca959a423b6678ada298b8b0e6e43a787f683d54f2d2ccaea5add992a": ["coop-land.ru.", 0], + "b34145068b8d63e54b442bb55b7daa6cb50ee1b176fd2e9c78ecfcef41bf1976": ["pse.com.co.", 0], + "b3464291a58f65050d16b715a13e1c4d422785e733f7a9c44e381dfe620b5855": ["1111.com.tw.", 0], + "b346927c07af28cac0ee6f3ef7597198eb461f637fefc61cfa140b1e9eeb3576": ["kpmg.", 1], + "b34af6c6f6003d1f0f217a8505f57ed47accafd2728e07da908e9631fb80e6e8": ["sy.", 1], + "b35542a95e0e8a7a05e7fa4f8144f012f2c8974fdc6353016ff086248137749e": ["builders.", 1], + "b35b1694b42db0d1bf3c771786aca24036034a0cc8bcbf673a2c8801ab89ebf3": ["dnschecker.org.", 0], + "b35ed57ebb8511a3acf4056215d17ec881b5dc40aeaa07cb4a6c44b40b6e067e": ["xn--ngbc5azd.", 1], + "b36e7fa3bbebc7df3a8c005e53c413028fda1d9d0be603218f8961d59d19eab2": ["o2tvseries.com.", 0], + "b3741078ccb649065c886e7c95112402c7969a5c5ed84ea116398db4f9dcb990": ["pudn.com.", 0], + "b375e9f740f78e5e430d0b818bd8ed758811fe5a7ab6580b485bf36da7f3bc7d": ["mediafire.com.", 0], + "b376a1dfeb551cb1c17be15934298537a8e1e447826864757b3d1d4cce197fee": ["geeks3d.com.", 0], + "b379ebf46a902e494aeabff462077382107566524d9219e0456438dd40aff8e7": ["iust.ac.ir.", 0], + "b37b9a34f44d57a591d60acbd8e57e55a22f6c990533a8ef6474e7cc1d96c2ff": ["bolavip.com.", 0], + "b3882bbb278b9fad703023ed10522c062ca262cd4fdd4b3055cf1874449609f8": ["funda.nl.", 0], + "b396690b4ed287af4c63575e4b1f57bdd8ebd14324fe400effb577b7155a2f14": ["showroomprive.com.", 0], + "b39bb40fb48afe7e08e89f38daf3dabafe1599e8f089266e1adad9ebba769def": ["29cm.co.kr.", 0], + "b3a233f0dc4189ab3a604c18371675f9f22e40bbeef94cbcff7d34b307e8ca85": ["gingersoftware.com.", 0], + "b3a840cdf1c411ba3c188cdac971c7a29f3291ebcd594057aaf0f65092db9fa4": ["socratic.org.", 0], + "b3a9cb1b8b67e75159fa8ae1c1a4e62bb5ed8660c8ed1c28a69b85bd1e375fbb": ["sunat.gob.pe.", 0], + "b3afc26d1c52783c61a832025516b0db046f31b55e723442cb133afe664dabfd": ["family.", 1], + "b3b0fbb4299c28c96f44ac5c2bd83e81705c2529be30b14a8121353cee9254ce": ["bookfere.com.", 0], + "b3b2dfe5190e116af5df08017f4f939d39cf3179a4bdb97dc7deca06cff4d29b": ["ecwcloud.com.", 0], + "b3b7f0b085a9d5c9b59ff2b74019c18d8d4a98e1381edd8cc66bb9a170bda206": ["fujifilm-dsc.com.", 0], + "b3ba0d9328fd1c1bbb36504f32c28c91cda7eba1d967d17cdf26fae713edacf2": ["commbank.", 1], + "b3c1d9acd7c0bd94b82e5b185cc8f88efd2e9efb320f513b16eab10432d700fb": ["kopilkaurokov.ru.", 0], + "b3c22e85938b41459066bf807c295677eb9decc58baaea105fe7d77dbe2b3e0b": ["fivethirtyeight.com.", 0], + "b3c7c978ec5743d3004faef0cd7d05ff9374b6b7d7c720f85583b74843b361bb": ["xn--cck2b3b.", 1], + "b3ce4df0cbb0b740c8eaebf5cd6c257f38b933fbf01668682183ff787c1ad861": ["mashreghnews.ir.", 0], + "b3d82b0672db5967d7b492f909bd50235b6c435ac194ed9cc8fc4a156509c48c": ["pearsonvue.com.", 0], + "b3e0bd48b26afc013fb9a587f114d1dd10792f2908226b47e7370ed1d9199772": ["netbank.", 1], + "b3e25b5365863829b240bb86aa3a52bb354a8c12957940290e2cb5eb6a5fc76e": ["thecut.com.", 0], + "b3e3c91646527478b12e1dcd492a14dd3fdb632f3cc66e798725a272f26b6918": ["su.", 1], + "b3e4aabb1bc65bb8fd2445c79201be63f3d2c755ef566865a3cbcd111c5d48c0": ["trueid.net.", 0], + "b3e4e3481926c17d00752a3d830ae6b8ecd56142746e5d015cde44b45566f9e2": ["search.", 1], + "b3e73addec7fa068de373f2a6d0eec9a1403b20d53cba2d2ab76775d74c527cd": ["pa.", 1], + "b3ec22ed6ac257015cfa51b9b3a81e0c0b42f22ab5ac7a7cc0c2a7126a4762a4": ["dazn.com.", 0], + "b3f9c5cbfad3c7e7f2fd6d7fc9cb8e0978d837facb9a13841fa18dd29f20504e": ["10minutemail.com.", 0], + "b3fefbc086d8bd41f80e1e147909c4a8aab590d9ef98fa449a645dff3a62ca2b": ["doeda.com.", 0], + "b405bada49bcebd5badbb67fdb5d835e24eee555e7a189e6bcfff1af9e7f8eaf": ["usherbrooke.ca.", 0], + "b40b037e68a03061825c66c2a19573ae5f3a8b094970b5ee970893aeb28e717c": ["vhall.com.", 0], + "b40e84ba6d8d1e4f1a6a1a0ca70cdd5e0f76d68c7262d4b76a0313eeda43d21b": ["colliderporn.com.", 0], + "b40e9df6a0540cd9637d35c77457b6e953fa2e4cb92926e3b1b83ef5992bbea5": ["pkgs.org.", 0], + "b41378a22e551835c7bb74b567a2d5b9fce3f26533b79f78c48551b8e1bf5622": ["9xbuddy.in.", 0], + "b41c5fc871015b23a10def8b79e1a745771ced87a1a76fd49914d3ea33a81363": ["cbssports.com.", 0], + "b4243c514f06d5c3df1d082cee6261c17853ad3d2de4ceffd3984944e221640c": ["cloudbeds.com.", 0], + "b429f729ed49f0eba9b7f55773889d553b03391525b3349d853fd05946e18c60": ["arabnews.com.", 0], + "b42b787dc600a33ea84de41fc8b3867c954b6e815b1511df02bc42f24787b84f": ["testerhome.com.", 0], + "b42fb8ae161443c56a0d2af02d91f06112abd7d576a5255b4599137047071031": ["buffstream.io.", 0], + "b4316d750ad8b911101df1f4e20fb89d4c7f5bd03f9763533c35c60e1210d824": ["kommersant.ru.", 0], + "b43cd0f327d31d1f52864462485d3c6cb981292439bec84c831a68e2564b9400": ["jobthai.com.", 0], + "b43d2dede884cd2aa216d3b1e2394082328429726aa9a18ad28b66d736b096d6": ["fux.com.", 0], + "b43dc026017f83c1afd46d4aae803b43180220957bffe7b126cf353401c1603d": ["kiwifarms.net.", 0], + "b43f0ce43a7feb6b9ff5f32d0e00c51fac2adb69131e0313af63cc136f896a7e": ["fnb.co.za.", 0], + "b442b6d561a95f9bc5a751a37d28428d01008b0a182b7f8ac555f64829eaf52f": ["mathtype.cn.", 0], + "b4484fc198b932a2b16666f90f4f69fb4f42467c5817bf91294d6531e7762d03": ["abebooks.com.", 0], + "b44ae5abedd7be5d180c59ea52d27933b336e85e624db8e67034b254734b2bd2": ["imslp.org.", 0], + "b457a6f68b0dd7a39d8dc52d31d8b85d3fadbb8da833b6a87f8683f36b783369": ["airtable.com.", 0], + "b458f7eb2e2dd1ddb6ebd58fc3a53ea2848ae148818cad3e440159d35492236b": ["lexisnexis.com.", 0], + "b46ced7f73b759680182e4ca62cbd4f7241aada91f82e7328dd3ec5c95df4cfe": ["camp-fire.jp.", 0], + "b46e6ec4436db8b1d3e53b62056abca023d1d783d9961ddcfa9d4feb98ce3640": ["qatarairways.com.", 0], + "b475fe5862f5bcec8fabb7f3421afd41097f55a277e29ff2155e7782d0bbefb2": ["seloger.com.", 0], + "b479a367fe3901db5f301a33fbf8e2e672ba97ecf6d9ea8da38ebe3105cae269": ["104.com.tw.", 0], + "b47bf96fc2388b975a0882d67c0261152303c2411b131cc030b3f2bfd79f06ab": ["xiangha.com.", 0], + "b47fb4d9b4d9d3974c41990b037035d083178ad92716d533d34bdc55f7724733": ["ramseysolutions.com.", 0], + "b480d73e4e6ab68d62c9e40460c2674a327c1cad4b85d65561484c028d862565": ["newtalk.tw.", 0], + "b484a2309a27582e3eb653a33b53b54009154882fc2414b416da5866c9f31ab5": ["skribbl.io.", 0], + "b485e6a0c9d4e14dfa0e45f39557ca0ae9a3395ea2ba90f5fb908c834820a491": ["primefaces.org.", 0], + "b4878f24a6e6df0016b56e3967039a50ac8c8677d7a4f25f627df76c3416efdf": ["gotquestions.org.", 0], + "b48ab982fe5388689ab601f793cedc489f05da080cdff932d6998deee1e639e1": ["vexels.com.", 0], + "b48befde4255ebcebdf53907ddbe48f5e5d01044066e283e4cde4b9c73ead512": ["cigna.com.", 0], + "b494455b4d418d7b3e22024998a71b08fc07b085550509b842e359089ff4aa5d": ["llc.", 1], + "b49b2346a81365a83c1e09ac20ac45f247f64d71ca7e3a05a9f4c2e7ba7359a1": ["pearson.com.", 0], + "b49b52d998d89eaf5b1b0ce40d2bac50afb2d04baf4e1514492906c4317258d8": ["uky.edu.", 0], + "b4a7a8f91ac8ed3bb6c8d0ce1ca72ed25e777e7ca7f575d7bef2530008758905": ["shuge.org.", 0], + "b4a9c5211aa74c622270eaf9da7ffde885fa8425e0329937c749236b4d009d43": ["kogan.com.", 0], + "b4aca815ff3ea3e3dfbeae893f3033b0312bb1ae6922cf358893b6b525fb1352": ["aktuality.sk.", 0], + "b4b1f9cc4a07d70e504736caa381a1de76d2391b632b2aa42b116d392546b001": ["mtb.com.", 0], + "b4b5baee88a33c412b142aa512233722d9943841b6dd59527a1265d8afa573ca": ["mnw.cn.", 0], + "b4b6a7c3df1647cb8a93eb66d2d3c4a8a56edd2a170fe454bbfed086e2fd9b48": ["canva.com.", 0], + "b4b7f3b9c1a81ccaf79f172172c453c110f8fcff77f8cd80892b4365a511db1f": ["qimai.cn.", 0], + "b4bc99b42ec83c8018fa62f6a438b64e1dcb5f55816fe676d31c649e1beb0020": ["crowdtangle.com.", 0], + "b4bec262362c2d0c2c406f371606b974e90b92afd84d87ca551e1aa1ff1afcb0": ["bancobrasil.com.br.", 0], + "b4c06e2abb58abb6d0a4293cd186f7856a25efdfa34aecbd1c0b02234cfeeb2d": ["greenmangaming.com.", 0], + "b4c896208267bb7de125e432d657644d4caed89b250571f4598efbf4bc4d890a": ["community.", 1], + "b4cc568ee48b7e5cdeebf93e07f4cfb853bc45fcd197a0912702832fd80fc3b4": ["bayern.", 1], + "b4d667b11f42748b096923cbcc41037897e916d266da5187d94a9fddd1db9cf1": ["trannytube.tv.", 0], + "b4d6e88e59ff7c06958e2a381f9e6035fefb93671c5c0b8e93533e7cc1f1d424": ["royalenfield.com.", 0], + "b4dc32d5982b0f2e14d49d45ea877d8af4eed3744697d30e9be770660a1f6890": ["mrcooper.com.", 0], + "b4e7d31f7d91b425c3ac372551fec15889eebfa0b1fefdcb71e379e99e6384c4": ["autotrader.co.uk.", 0], + "b4e99ac6e7af325a57f7f45cd07046362ab47d989ec4f001ea343eacf33298b0": ["xn--45brj9c.", 1], + "b4eb382d1850fa4dd26e8bb0eb9e88e656e1180d1b164e1b91d4c4833daccb2b": ["kw.", 1], + "b4f17ebce76d673e05d5d25a73982ad082fe6bd50f6f0b50cc88ff568fea932b": ["yelp.com.", 0], + "b4f22583f521b4191c24cea28f8f17529e950617530b1e4dce4aa7dfdab49535": ["afternic.com.", 0], + "b4f25c4d8dd10b794cf95bce48cb04d046cce7b4b8f146eb3f0ac8508f101d44": ["pcauto.com.cn.", 0], + "b4f4f6d2ef2cf8e480d9045ae313a31a78ae3b756dce8416dbccacc3c131d0f6": ["uvm.edu.", 0], + "b4f91a700cc8282e061b41c664ae724c9989376fe8dc3d8eb04693d91d35d558": ["saudia.com.", 0], + "b4fb1e82e4d99a194bf3d670812b3c6d15bccac6de5fc9dfb653fe176120ee22": ["hindustantimes.com.", 0], + "b5108908911fd7430ec27574200307d6eef56986c20c6aa9f2fb2b61a448a9ed": ["leadsleap.com.", 0], + "b51ab8761793180bb9b546c8ef3436289b6174be62d293a1a2c72d574eea7006": ["xinhuanet.com.", 0], + "b53136d7c75461f7910b498382ef412810a04ddeb16b04c9d5719783578d89e7": ["nordstrom.com.", 0], + "b535c9e2a7225f255bc92fb97ffd29e6e8332e4ef264d24e9b44700d4ef8df1c": ["banggood.com.", 0], + "b53eb15cdd8d5a15518c297450b6583dd59ed67f16b30bae75f1779cf87a443b": ["olx.in.", 0], + "b54069f5da514ba40c15903eb67f61363533b1ecc37e4c8b00e4ee0c940d61b0": ["thairath.co.th.", 0], + "b545ab5d5895d033ad7071a593efabc884a275b0c24b381457fc3c5a17f4743f": ["uibe.edu.cn.", 0], + "b54fa141de2085aa2214e17bd937060af472778e6bfdb623494cbfe2ee43b34f": ["sueddeutsche.de.", 0], + "b552400584cda37a2d44c443a65a9a5eea34f4932b21fec4a43f6643fa12a258": ["architecturaldigest.com.", 0], + "b55b51a7a14b0d77450bd667592239a6a62f05fee24a52dc281a99f62a8655b1": ["wordcounter.net.", 0], + "b563f83bdd755002aa66f299b5429eaf05b487ff224e09dc9601239bd4730efd": ["pokemondb.net.", 0], + "b5658a040ccf27e51d1430af6af0d739cb1b3414adcf835c4f224070bf8dd81d": ["iwantclips.com.", 0], + "b56601fb94cad6341148342837118f486b4d38f644e4fa6e889954ff1e4684d3": ["nea.gov.cn.", 0], + "b56a9747cc661972e5629faa63741d118df02d847c0067b9f7b121a318884b0c": ["subdl.com.", 0], + "b571283a78f348745749061e497a06e6baca3448df24dc22da983b1255083755": ["audi.", 1], + "b57311bd4c19c1fdb553dc0b76755fcfe1b1f28ff98aff2d95ad25a44930c124": ["sstm.moe.", 0], + "b57d4e6d43beca9f0abaddb60201b378937b2a8f8b5179724139cee4c9b96f23": ["goodreturns.in.", 0], + "b57ddebca98ab3872a0c32eae16f951e46879ac37e15d765eba85ef5a33fa323": ["tubev.pro.", 0], + "b5811b4c1ba60af77bb7ce729aa4a696cd9420942fa9cd59b47ebeb32bf866db": ["domclick.ru.", 0], + "b585731adfd00af15bc3b08ccfe8cc6f4c6798f617c257ca5aa91eb900e0815b": ["irantalent.com.", 0], + "b5885d2f2a087a828e132c135adc809f89844a0c54f654882305d5ff1ee32258": ["mathpapa.com.", 0], + "b58ce8ae3582065e57392037a29781eb9a771b73de9b96b7c3541ae4d745998a": ["masrawy.com.", 0], + "b58cf88508b835aa3c85a752a0c12fa6e956d24c33b85cf6dde149588c47dc2f": ["apteka.ru.", 0], + "b58d44c184efbe30e0f0aef1915b4ffe18e89e064cd3d30c69a3571c7d678cab": ["ualberta.ca.", 0], + "b58e8b40ca8e5e732ca0771bc599cd632219a2cf8907df144341b8a565bfcb6c": ["sbisec.co.jp.", 0], + "b593fbab5a927d9bfdd2f85bd4771fd586a4d5c7f4b819a39dc9f81dd11ed706": ["gay-torrents.org.", 0], + "b5982b10eef73fcedf68e9093672fece351cf29100ccb224dff5fe8bcf9c7b58": ["dailymaverick.co.za.", 0], + "b5999adcbfbdaf49dd3012f23eb05913cde36fbdb90ffbad0661877b7b110cef": ["makeshop.jp.", 0], + "b59d377f74cccdae5450513fd63024e4b20d875aa2d1a6711a55c9ba20c26714": ["jm.", 1], + "b59f9f4700a881935087afd2a497b8be1715720e4b8d942d62c5b0848f0b56f6": ["worldcat.org.", 0], + "b5abc98d89e5dce8a91e3253ef3b5bd500820e09a09c87833f85a9b93867aed1": ["elakiri.com.", 0], + "b5afe71d8ce0e26612b553f5cdd21920bc80c71c00a42356b1c3393fccb60038": ["kkday.com.", 0], + "b5b0e380744200de8cd134268246e6a1c7df41e10e9dd4f6c74e4ab675e715f9": ["codewars.com.", 0], + "b5c3b0adeb90bdc1d624f6eb4ff0af95e0b34eca2bb481c6d9b3490a9eec6856": ["saturn.de.", 0], + "b5c76eac1def83d5a81cedb352b9d59fbf7b866d6a0974ea334ccb6715cd647a": ["gamejolt.com.", 0], + "b5ce8f763125cf2ff595db532c557a919a19b01dd8c6840df704a69dabd79d26": ["xvidzz.com.", 0], + "b5cfb67a2bfca0a16025a5af2f7ba0c9b9185bf32aa3f642c32319b6b22d1722": ["jobsearch.az.", 0], + "b5ddb9bd925d31412972df63eef26aba8a11c3c642a1f67db674067b530ae27d": ["doujins.com.", 0], + "b5e333d778952e1778fb62d6da9921ea319911ecbecffd8da7ec146013a58425": ["tokyo-gas.co.jp.", 0], + "b5e80741e385578f816d37c76a4113ab3f5074446c0c80393bbbe6a6844aab1a": ["machinelearningmastery.com.", 0], + "b5ebda5b46e8adcd9697d6cbf8753659803fbe7674d5702baac84bdc96de1ee6": ["harvestapp.com.", 0], + "b5f46514257bdea466e9dfe60bd22b167d112c8b4dd160dfac364061f8cc70d0": ["sarzamindownload.com.", 0], + "b5f5d127202d9ee482ca76907639fc6d990d58f99af376b7dfa63ebc28ee8d2d": ["coindesk.com.", 0], + "b6049fca1d4877b9e0dacc1b987653699fa79042c91fdb1a23e09225a2867a76": ["sendibm1.com.", 0], + "b6077bd8ff3528ff8639e8bd1796390c232865a27bf8e2716200a983309d4579": ["thestudentroom.co.uk.", 0], + "b60a05f489b8e47ad9f7c43db44bd509764eb7384e2ac8313a6b28bfbb7d6b71": ["ecitic.com.", 0], + "b60bc4b7a28651623439dcb4cb9257031771f9ad4eb774a169e6b7671bc1db91": ["thunderbird.net.", 0], + "b60d442073fb7d3bf5fffdbb08eaf1ecc5d4c6cd015cd983c02092d230fbcd4d": ["gw.", 1], + "b60fc9285b414d18efa9766553bbef7452b4237b6bcb2c69b72765487176e728": ["mobly.com.br.", 0], + "b6109f3089df246b0fc668bb387d462895d1836b85a0da14c1ec8c266bb981a8": ["latimes.com.", 0], + "b61abafd880a016b0efe4896eab7629baa01bef9f0fd45a44c997da4c2326183": ["seu.edu.cn.", 0], + "b622904e33f49cc9d0e42b9a5abb14b5c6810066e61e39746d2b2c0678ffdb83": ["abcya.com.", 0], + "b626fd17e82052b8abf6ca3ef4fdfeb3f6620d5aece892cbf616d484bbf6aced": ["ams.org.", 0], + "b6316e10054471307ebe0cd5971e552c5f03ca1ffa2384c3015f0791e526edb0": ["cangku.moe.", 0], + "b6362a9ed949fbf6572d23533c37a11cfb1ef79efc10279247289358e41f3311": ["seo-fast.ru.", 0], + "b646b9a5ff7072db7190c43c8e7422c8a0c15e3d46f0c3f7ad71af1af94445c0": ["lt.", 1], + "b64c474705bce9c8bd0515fc3d5919d4f9697548ee17a6c75a7d9dccc7a04de9": ["clio.com.", 0], + "b64cd1ba8f33e52737c9cd044aaacd7909462b8368ea0ffa45e16fb31003e2c8": ["gadgetsnow.com.", 0], + "b651e5ba9ac05f577aadec74696bc43de318f13a8c86c7cb458bfa7f4e9d59b4": ["girlsvip-matome.com.", 0], + "b6606b893e353658bb7e11a84294f91a4ba2eb79cc959cf055f1b36381942a86": ["pnu.ac.ir.", 0], + "b66c0cbbda82fec3085ab01c2f25e09e9a0b3fd80e21e98fbfdc79c26d85d9e9": ["magicbricks.com.", 0], + "b66e9d4848c52caf322022938479af93a3ab564ff1986efdd2506614fbe149bd": ["barcelona.", 1], + "b676e29ea6a5251ae46e1ffa0181fe193537d85d34f5e78a184e93ddee8809de": ["theync.com.", 0], + "b67849b39dc9b6fad1e5717b5b4786c7436ef32e7e1abcc283169d7f60ca37e6": ["liveworksheets.com.", 0], + "b679c425fcb643e616c2b39b8197716b2e0947be8caaa276f9744dcc0f27b5e8": ["bannedbook.org.", 0], + "b67f6ca985e520c7387817d83e19fbfaa59a42d0eb86552414d41d61596a9dea": ["alignable.com.", 0], + "b686ac43af65e0e48a7029e716d983c81c36c9de2437f274efef6a297757d159": ["laredoute.fr.", 0], + "b68b050c20c23f621c8d1f7120f7a6dd587a1d514e5927f43e1edbe4581b71b5": ["traveltriangle.com.", 0], + "b694de1018240917b9884a4069a55c9e5838d878ab7ca5bbd6299262a9b66467": ["backcountry.com.", 0], + "b6959130a353ab4f22ffbe453772b10d82de4fdbe5cd1b03955a9b6ed353cf48": ["libretexts.org.", 0], + "b69730fb1b71740c7668d8baf67a98fd4ad3e70d4c44ebab008547aa027af152": ["reit.", 1], + "b6a0cb7d1d6ef8ed7ff2d51459790e1e504107e548e3c2a18674bd4bafb3615c": ["dramasq.cc.", 0], + "b6a72fb0afd42602005d1eee9b30ec5d8cd12655b11560b0d734a33fce14c767": ["imgbox.com.", 0], + "b6ab04ea8ec2c7c71ca7f35c48868eaa4e24118f644f2d4aa8b61c27ca6b33bf": ["productreview.com.au.", 0], + "b6ab53fe4f693336e24672e6bbdc585a3b869db606b197cacf194f4e462c47f8": ["tenorshare.com.", 0], + "b6b0b5c9aa7eaa721b198910e2b92d7ee6efe02278eeee5e053e07f1e952846f": ["il.", 1], + "b6b7cf4a68806f1e6f0eca1ad04748b0ccaec3051d28885b3bc89f9ce163685b": ["jus.gov.ar.", 0], + "b6bbe4afc67e91fc2961806b3917fc9d47d9035eddf4229693ad613435ec1d26": ["asianwiki.com.", 0], + "b6cb4419757a7b5529c9ea9c37b304fdc903e7722c9364a35a126360d569014b": ["ntv.com.tr.", 0], + "b6cd615e2a0baa79e7ac997b236ae8cb6e44a991a5c8cfaa03293ddb10d7c12c": ["deti-online.com.", 0], + "b6cd72412ca637db189f9fe674524bc6e8374614497425b60d14a0645715d588": ["uquiz.com.", 0], + "b6d3e6beeb11783449265ed6380aa9a7c0468e26b56131854305a6966ecbee0b": ["joomla.org.", 0], + "b6d53089f14b919e59d138add34e2877e7dbe6c50ccf508181b5da3a8e13dfdc": ["clegc-gckey.gc.ca.", 0], + "b6d62c2b442e01d748a7bb3b3a133e678fb5f4e645040017437acbdd0c1a1eff": ["poedb.tw.", 0], + "b6ed38378e913854815bb495c1f1232ad53dc482aaa34cb8915ef8cc13dadca0": ["gelbooru.com.", 0], + "b70376ec32783811741098c0ecca42c215997c588b5af0c45dc40db589895cf5": ["cybo.com.", 0], + "b70bdf861a9deb11eefc6162e779ae0529a0e0e58b87df799e299cb5b153e608": ["irandoc.ac.ir.", 0], + "b72414a1722506b00a0220d3102cf5680f4fb34615924422d26609cb75017423": ["mediametrics.ru.", 0], + "b726aebf113cfede6844364b0b705d3c1c0f1cdbe3e85d3905415cba9f092214": ["eqsl.cc.", 0], + "b738c1011299e2c65f372134764f748208c6f5dbcb6826688e7056c782d3fa54": ["meinbezirk.at.", 0], + "b73e3683d39053e8840a86e30642016e7fd420a26712a11992c65148b237ed48": ["gogov.ru.", 0], + "b74ba6ce67a1325a7399fb64af2cd4967c48343d761851da7429f100a9092e2b": ["foxsports.com.", 0], + "b7516d18a1d55e54c35ba18667eeeee290475b78758e3f51897f0d92de751641": ["trello.com.", 0], + "b755c6f5fce45a75b49c7d636974814614e1653f109a4e9e174e74c0bb84568f": ["skladchik.com.", 0], + "b75f5432e0462e9d3977c1ac4d3f2b30255adfbf6b9a492366f768538b1bfb86": ["carousell.sg.", 0], + "b763072fc57ae4c62eae3efa40d733a2b4951842388be7d59aba84c323225adf": ["ifeng.com.", 0], + "b76bf8b60ee116bde2f641ea6d7340dc6c8f40d65bef7234aa37938caa7b8de6": ["kotsovolos.gr.", 0], + "b76c44f750f8eaa70eb10eff2a1d14fbbfab82ef18542400f6f199fd50da338a": ["xn--3e0b707e.", 1], + "b777112f5e3c9f289b0bb801eddb9c557bc9b09b1035e40f53364a8b934e370c": ["nba.", 1], + "b77e77e9a413f276d6e22340d5a1d0af4746420250892466da42a7ed37cfea20": ["tesla.com.", 0], + "b783f80c86cfa8a0de42d91023d95e5d560fb418c838a751c168233a4c5bf7ff": ["onl.", 1], + "b78f5dd32f6aa8bf3f0976a8358893aaa2aa62821999b493a36cefd343eed941": ["southerncompany.com.", 0], + "b7910bda20cf443337f8d155343b04a6cbf4c469be566b0f571599e2160cba7d": ["xn--mgbgu82a.", 1], + "b792a3c35d10c1a20bd0f1de0ec4d3523d2fb76971382e6753fdf4dc804e9acd": ["hdu.edu.cn.", 0], + "b79993fb4b16d7253550c89e15fd299b33b31f1986a00fc0cfeeea75d3dde983": ["terraform.io.", 0], + "b7a7fd984097c70927892b68c34557188252b1da5777cb15a7dbc6334a1c8aaa": ["webpagetest.org.", 0], + "b7b3af7d64fed0ad01fb2659fe92c9feb49abf756008e9ce9a9c19441eb3e0df": ["skt-id.co.kr.", 0], + "b7b406a0d065bc01694ec2bbf7162303cbdb47c1d983c981ec2103a1281d874a": ["ksp.co.il.", 0], + "b7b4a7ca4bdd915aacb28359183fdeb67a89c35f5c1419bc5205dd3821f90de3": ["pewresearch.org.", 0], + "b7b86f7aff4938eacd781de86e7f51b512000a6598c9c6ec9fdfe69a4209a864": ["kitizawa.com.", 0], + "b7c21321338b5481a63b03350889c195e7bc93e10b47a089165e179ab92dff73": ["jharkhand.gov.in.", 0], + "b7c344c288eee0d7320df28edbf28ace39137af0b61a741def4b815904472e1a": ["proceso.com.mx.", 0], + "b7cba71f64da701693e78493f35a7e8448aa79e18fb0aefb25f208a23b5681ed": ["3axis.co.", 0], + "b7cbfbcbb5877b073c6a3ec1a9ede2da9cdd76f7728c5cf60f954b23bcec77e3": ["iobnet.co.in.", 0], + "b7ccb4c60d4813e2e9881089ab45138f3cad6521259b5b25ebc43226149e707a": ["moneycontrol.com.", 0], + "b7d2bb04adba7a1085fa2cb36383fb03f5e825b04d80eaf3f8f4e55774c3a342": ["megapeliculasrip.net.", 0], + "b7d7017b902a352f72273605ebe81e52d8eabc1ba203a944130b4948795864f9": ["sz.", 1], + "b7da6b609a39beec6e4f1619b43291ab273c9fb13f6099b19875742363361e0c": ["leggo.it.", 0], + "b7e0e1af75cc9462cdb0bcfdc9878f33642360d65ea7a64e65459341c1a682b5": ["meo.pt.", 0], + "b7e3f05512bfa002b349c05f13bb7cf29d972ff752ddf4578c1a5b5634d40a14": ["jalan.net.", 0], + "b7e9dd5dfdcab12c616d173346afdafdd6b208161f954449a9638f511c2cd71c": ["customink.com.", 0], + "b7ebbfe037735c69448766f40b5e3fbd4b0a0ae7614128cd7c4924cac1222cda": ["recipes.", 1], + "b7ed00758fad0185ea0c87beb6eead9bfcd129be94e5864f277ce87c2ef36482": ["ecosia.org.", 0], + "b7ef9230003f9463775731673b9e29beab76e66c886415b088f384c0ebe70124": ["celebritynetworth.com.", 0], + "b7f3930beefb038170c53270316e15c576476718846d8c45fb1c65ca07684bca": ["10times.com.", 0], + "b7fa3bb893ebacb2310d8dc7352e132a14441f163ad91bf1731113aeb78c8ab5": ["prepostseo.com.", 0], + "b7fdaf97828729d23dbd6d695e24e0a41117dbca77870500df87b5cd1d4980cd": ["xn--gckr3f0f.", 1], + "b80418e96122ed289abbceeb825fb931615ef551577f3623da889ecc600a890e": ["51job.com.", 0], + "b80b11803f8d0fa3d716ebc48bfdb2e5479c737b4814a97525441e368f9c622e": ["familydoctor.com.cn.", 0], + "b8105bead3eab04b235b141cd6ba40b3ea1af912a8df9411e62f8d4caf643886": ["bolly4u.team.", 0], + "b811e9264609935cbf718fcb8002ed1eea24142ba8a3e24d8dca457dc1154843": ["pacopacomama.com.", 0], + "b81ad976722d41ef36ea24a63be3abcc9037717c2d17750d5edada3762d023bd": ["mesrs.dz.", 0], + "b82019c1f5776faa14ba731de2be5d3c2b3cd3af9bade2ced67902ba2890c0cf": ["nar.az.", 0], + "b828f5d4fde9a4256f8e0f293352f1ea74820a525bac268c9a53f5d234ed6a08": ["veeam.com.", 0], + "b83958a61723c78fa2da278521421ea439d4e1bf6c63f77d4c84644001658cb6": ["usersdrive.com.", 0], + "b842ca58763272815d1bdcabb88eb15f55460d7f8e7efb9d971e3d12f8134d18": ["politiken.dk.", 0], + "b8438df20f6ab8f2dc46f4ca506693bf6bc8927b8544d2dd6f2e3276375d84e4": ["altadefinizione.navy.", 0], + "b8454c752a2a954ecddd0f9f95ea2872b9f7a67e8863d3628d7112155d34e7cf": ["infourok.ru.", 0], + "b8536e560987f7f5b18539a007369527d207a9402ea73b06a15c69415f8a28c9": ["nate.com.", 0], + "b8538a16d1a664d7c2fd9d77952486aa7c653abff78776a7494831adc0eaae61": ["hdfcsec.com.", 0], + "b853fd6aa9dc38e29f1ce7c78a650c2f55b5d07f800cd0ecf1bed296a4012ce3": ["youku.com.", 0], + "b85425199512f84b4141425f8e81ee9c0fa1e6233d7a87f6df58b2055f40875d": ["usaa.com.", 0], + "b856f8b2a3ea45516a0c95938b329541968ad612a49a45f4ccdc35ff839e9bab": ["imagecurl.com.", 0], + "b85815460669b2682393d8ae34516b6d9b9fdf62d31186acc62dac30aa07a78d": ["burton.com.", 0], + "b858c1cd5c614a3349f6723160679cf5e2eb926e960ab9955ed0f4c7764c4008": ["dailyrecord.co.uk.", 0], + "b860cc38f155f6e7609a304f37df3e0964f8554f80c3f121110ef7926e96ff4c": ["pornhub.com.", 0], + "b86631f6d548966a5d05a4bafd8280108a80715b950aa2822a97215aca4c8022": ["farsroid.com.", 0], + "b86916580b7be68d92fcd9fbcb5917ec8b36467255ec77b14c0965aaf4512aaf": ["ly.", 1], + "b86a2d1fa454353e249893ae9438298a5674514f19c54b90b08fb7507f9868d6": ["hentai-sharing.net.", 0], + "b86a7925e4e93c51a9c0be82e9a17d060341ca5696e33a696b70e0347f02cdcd": ["polsatnews.pl.", 0], + "b886463dc27a278b9d44df8ec943ca64ece13635ff2f7b4d30f615408a4e73d9": ["fedoraproject.org.", 0], + "b8951851e1ff5e8b52093972dbac1656aeed5d77f4a05e2774ed1758e06e014d": ["ameblo.jp.", 0], + "b8a608be07fad5abf4c95266e9cf596e99ef4e03a213e973475c7a2cb49023ed": ["case.", 1], + "b8a85879cdcd65b3f4df5f867c561f50a839e3434a80b99a1d31e94731736c15": ["iconfinder.com.", 0], + "b8ab3e582d6d52cb1b5b8fc6e625fa72665f03c6106569f2ce19316c597792d7": ["datsun.", 1], + "b8bd32787d6826a697011415a4906bddb2765f46147412c4a5dc2daee49ec1ac": ["labcorp.com.", 0], + "b8be16b1e1da55fec35f3d5e28321baa8d5ea03aa09c51e2d428e517b53e80f4": ["trust.", 1], + "b8c176a7d462937e26ed79783f041a99623e078e8998bc0028202ef97b83b435": ["unito.it.", 0], + "b8c40e35200805165f01bf5a527d9dd29441247e65bd3befe3d2e4bb3ee25ca2": ["hkjc.com.", 0], + "b8cd76e5b9d120542a39fd859811f716e7b20b3b49dd12d70dd2818ba27555e6": ["ocado.com.", 0], + "b8d702242ed42a9e701620ff5f675403cdabf39a470f07dba1d04b3cc4f0481b": ["vizer.tv.", 0], + "b8dbde7049bf3425f3c7649949dae60268d549b961c8985012fbe60bf58e7b9e": ["nexon.com.", 0], + "b8dcd6533d1c6cea7d501933709f03c41e3482f878f75f372c3918b4d2fa7480": ["carleton.ca.", 0], + "b8de266f108b09391f82a126860a9375ec8e74090594042d2ec6f95cb4fe89f5": ["warcraftlogs.com.", 0], + "b8e8018843e21c02cdc86d6f9225ba14d30d780bb9751999c3b562d555446315": ["skyeysnow.com.", 0], + "b8ee960016baca16e7918cc2082ca469ad9396219031d0f6aa4201577885b6f0": ["techpowerup.com.", 0], + "b8f033195cd69ff487b757622ffbd18c6973fb8db789d7a965e3a79e3ed1c254": ["whotwi.com.", 0], + "b90536b39d8a6942c8444ea5d0f516e439537802e3c5c0f31c6514ed71a7c822": ["dxy.cn.", 0], + "b9104603bcdfeeeb9c6c61d0ed047c6fd65766b14b181fd7895479f717e26297": ["macys.com.", 0], + "b913358ec3fd13f1573f88f1fc13f6dd48a36d6e40065a9db424a973aa9cba22": ["caf.fr.", 0], + "b916e332a25177f7571da8bb354e25be64407ebbd7a710bc862d6fa4ba5df59b": ["rateyourmusic.com.", 0], + "b91af280a9eaa8b6a9924c99ac3e3a11b4404f1ddad793624ae120cf0ceb009a": ["toggl.com.", 0], + "b91f8d4df2ec82e55dadd402747d9ca62d99a3ddc424dfaee01e3f87cfe03ecd": ["zoomg.ir.", 0], + "b929a92ecea03d6670ab04a03021f8782faf37a88203bd587c13c7d1816a428f": ["appllio.com.", 0], + "b92c1288d9019e02f759f6d9cd9dd3189d00ec09934ad8bada2e8e4ac1cfafc1": ["newsbomb.gr.", 0], + "b9390341c1a1ebd766e68e370ee1a50330b5c0e00fe8fd7a3f5043d46d0c3dbf": ["newser.cc.", 0], + "b93ba4c98c8f4d4a0de1725bbb22e1e19af3f2bd4dc2376cb65f5c9d64018262": ["foxbusiness.com.", 0], + "b955cc60a020cc81dbcf9f62f0eb6fc9172a69bc2989ec5ddcc072d3b235cff7": ["electronjs.org.", 0], + "b95a4434fc4c2ee11ba0b8766071e174cd5291086374b38f9e8fb8b1dad00f20": ["jiocinema.com.", 0], + "b95d63df2f8a7378ea2a02e22775f2d0cc8f7207cd7c7971bfdd1c04da98ff29": ["moudamepo.com.", 0], + "b9681f1c6f3d77a46d3596fc4c7a6fdf6e0f9f01bdf6b527f5d3944f37bb38cf": ["xitongtiandi.net.", 0], + "b969253a7344f0bc522bbacfb283f707cd12fb7390525577ba41ff80cf910d7c": ["irankohan.ir.", 0], + "b96f073775929c9960dc51b8d96dd4d98f908c4145fc5b4d3408e42206f61a1e": ["ehtracker.org.", 0], + "b973fa55ff47032da8c791359291e41bf11b69af0c9c55fc72260c69f3b0c5e7": ["agkn.com.", 0], + "b97f217f29828d2c30ca7458d3f48c110b321d484eb72c82660c89d5de721c60": ["dzkbw.com.", 0], + "b98241a0543ddaaf8d4251841a4888f69dd47f351ce46562cda86062fba1ef20": ["robu.in.", 0], + "b991310f76ed74ac973fd1b2119e087ec6a97775103a4c8cc6cd8c36a33eba57": ["deal.", 1], + "b9992a7726dcc72199442ab8717850b27781b7f5585087dad38bd5ea6fcccc07": ["stepstone.de.", 0], + "b9a00e55f22b3d77656ce571529b6843a6a9fcfe2a6934f653847c5be96716dc": ["tools.", 1], + "b9a9105fb23515d9da05f051becdef51f3137bc2a558d14bf0a001417a57eee9": ["allegiantair.com.", 0], + "b9b02612458fda79b0b7b44cfe3528c8a14700cb83b3d538f1e943b5f96b7e81": ["kucoin.com.", 0], + "b9b42b3cf63152a7d3e5d1c932d928959df9b09385d4cdb3eeb5be4e72b2cada": ["pciconcursos.com.br.", 0], + "b9bed8996e77dd8d757ac29cf4585393ac0660736b0003409a26ed22d58781a5": ["dexerto.com.", 0], + "b9beec59ffcd47970c30e4a3c837c79a45af65ad6bd13e938138e01e075742e8": ["digistyle.com.", 0], + "b9bfc286421015dbb19bde2252bd66ddccdce045d9cc16a05e46a3045134ea7d": ["cgtn.com.", 0], + "b9c2cee0f1dbdf1edf7947e030d010c266d06cd1859f710385090c2bbfee504d": ["kprofiles.com.", 0], + "b9c64e03d8801e345d4f3828dc1aec0e5a3823b468065fba2eeabbeb433dd2d2": ["carsensor.net.", 0], + "b9c83b069cc3d3cbbd812c843e552dd477eff62c53e8df9a1700a454e6417cb6": ["seriesyonkis.cx.", 0], + "b9cb3846369b59c8c5a5bc03b6168e8a7b8ec50cb07db8d4ce7cab846797591a": ["nudography.com.", 0], + "b9cc7d1cb27a8c46de66c1edadc15d6ef8cf12cb1bd93b699ae58a2096f7f1c8": ["cmoney.tw.", 0], + "b9cf16564f466c420a296d7768ead107887357737f9b17ce3f5b081604d5643a": ["juniper.", 1], + "b9d219337f3da4ee6e984fee4aace7b38ee8c9b7a82e52260c344d311879a3be": ["quicklaunchsso.com.", 0], + "b9d735e03e8d89a6209ff65f36e72e24023114647e57f490f75e1c6b419fafd9": ["4pda.to.", 0], + "b9df361894607af6b0c8f930294c7538c36c076ccdac00fc8ced7e90c5513a80": ["qzzn.com.", 0], + "b9e1db8ab8cfb0aada4eea7416bc76db52b6ac506508d54d02495cfd60c7c980": ["kisssub.org.", 0], + "b9e9bf3efbdeb56557c2a4cc782651dbe8f727be25167d6238a0b15ba1809e05": ["southernliving.com.", 0], + "b9ead4950e71dd51cb79f699797cba0ca95a29c8df15f4076f8a09bc3167ae33": ["hypnotube.com.", 0], + "b9ef20c201d5758d255eb5621f3cb11147c10dab3f691d42e0191eabf57e0860": ["hinet.net.", 0], + "b9f01dc48b634e70ebef9a24ed50848f8120d6e190daeef01ae8438173c41af5": ["nlc.cn.", 0], + "b9f55acc4e3e2a316233e2835d6480cb8e92cf9fec46994e9afc1b18a15186b3": ["pudelek.pl.", 0], + "b9fb156c4d7a99d6fa4f560a07d5175788d17cb6c6cb9d3b9af6b670a24025b9": ["rmunify.com.", 0], + "ba065f2c74c010aa28d99fbf4fcf18dfd80fce0d20e371a565624fdf05dcaeb4": ["oray.com.", 0], + "ba0772d99d8ad81ee2e0aa466ef84df1a6d23fca9743ff5df3e568ac7f28369d": ["janeapp.com.", 0], + "ba0bacdbd1b8494a3424ba5e6a4f22186b7b935e06ab3a2cd40bf50afd4b94ea": ["boardgamegeek.com.", 0], + "ba184247b41e66c8fa9d8163182bf7e23c55f99d5b9a3099cb53c993aae3ea39": ["instagram.com.", 0], + "ba187ab122ccab5deab267fb31fdf4ef209fe29041a124b8c4400c01eadef031": ["rstudio.com.", 0], + "ba1a85401ad805c55bdf557c570430a17a84fe5a8451de8b59fde4b03529beb7": ["cscse.edu.cn.", 0], + "ba20e3674e7149b84fb92a1d93d51098e076f17551b0b24d28b4fdb46c6af0a2": ["hindilinks4u.pm.", 0], + "ba33e8b455e2c81d618cdbdee2d91f160bf58cc5831eac5e3bbd9110b33710ec": ["yw56.com.cn.", 0], + "ba3ad2b3942f6cea2ce187c34d3dc2ad4d9d6165b97b414447527c08ef91cb14": ["xn--80aqecdr1a.", 1], + "ba3e8605f15128598daa88971881e17848c5b6f209afc4e7d2fb147fda831cd2": ["redalyc.org.", 0], + "ba426d7423b54a6e9bffae683a44de4987fd6f57511e4ae4065c7eaec77e0568": ["sexmex.xxx.", 0], + "ba4685f10548afe23d330579e4d59625adc1f5e49485eb1d0577cdb469c2ada1": ["jeevansathi.com.", 0], + "ba4897e208a832eab98ca320b9994408dcf652bf75fef1540588ed64490ce501": ["khinsider.com.", 0], + "ba622291f5ac10044e6220e000beaac504d84236a1ae300b2de7c8bf74cf043b": ["azet.sk.", 0], + "ba6ef140811fd378755aa316cbd7c521665acc0ab57a2c7084eeaa03ffa35272": ["turbosquid.com.", 0], + "ba7dc99fa4a49cea40c9f751a8475721d3727a0585ee1c8d8cd480e3891908f3": ["imamat.", 1], + "ba827acdc348c2a51ec2ef03fa73d9609789bc64aed3ff13d364f17a895c148b": ["suruga-ya.jp.", 0], + "ba82d8db9291cb7ed1560af681554bdd2173c78ad629873486c099271ba67e2f": ["wpmudev.com.", 0], + "ba8c86ea7a0e2d9818535f3351c6062a173be752bb5611432d2629ab73f72ad4": ["sejuku.net.", 0], + "ba8ea557483999c7b1fd8f8ce1ae9524bd4b2bfa2bb78c205704342477e22a9d": ["kr.", 1], + "ba945f9f3dba55830b34f8139e10c00d7ffc03f357a0eb168b4fde3d8994d1d5": ["tuniu.com.", 0], + "ba964d59f79ed353d86c3816878a7121c57a452be94161bbcab9e366b39ed783": ["dance.", 1], + "ba967128952d77089d04b3d4314d8ea05caaecb65bc00d32fc6d1f9e0d187a7d": ["gsmarena.com.", 0], + "baa2d6d59a5040f4f2a4e47da3103d1ffb9a545387850c575747ed713551b785": ["readcomiconline.li.", 0], + "baa53f278d711a7a220e7d8f744e326eede37f7ecf44fb9bf5343c14d74a9457": ["xn--4gbrim.", 1], + "baaa0224ab6fb6413fd498325066d25293852b19df0bd86a4412c8c897495b17": ["cosmote.gr.", 0], + "baaf2b95a56fe68cb643976274a98e0bfde1401913bfdf7201ed642be152ec58": ["turktelekom.com.tr.", 0], + "bab0391d194be0bac18eaa477dfa003faaa885632b88b0d8be7db26922a873ca": ["scot.", 1], + "bab2f0b2ab7cb091478a8a3a291a90f084d413b8c606ab265fc156963339cd2e": ["bulurum.com.", 0], + "bab56260e8bb62c4d797449cd483dce5341ceac821dc3509359d01b59ad2f6e6": ["yidio.com.", 0], + "bab938afcff26e851f192b4ee05c727fb29c9ed58af1654fcd98134cac89ca38": ["iteblog.com.", 0], + "bab9f30c2f5ee19f4bb2c40553c9ed6e5228e44e02f745508fc58b570d1ddea0": ["taiwanmobile.com.", 0], + "bac612b146076f322f02a2cf5dfdff5187fb512ac5fa7669d92961e8efca8603": ["poder360.com.br.", 0], + "bad43d5b50d93fb35fec5409a890cafa8dca65156dcdbe750b0b49540b94d4f4": ["agenziaentrate.gov.it.", 0], + "badae2b873f0d96f05580014d4197742aa2e279a9d8afca37542ef1326da822b": ["nio.cn.", 0], + "bae1b237ba6ff685a38321feaf0fc529fbed90e6246a7ff326d4fb577b259058": ["taishinbank.com.tw.", 0], + "bae1c17216eeb4c77fb69a2adf0ca4e55be8ed30911c9f9b6eba784f96e939e2": ["chaturbate.com.", 0], + "baf8c7e18a07e4d5fd54f5d733ab5d34618b98eaaac64d21d0eb5eecc367a14a": ["alison.com.", 0], + "bafab8e7dfdfc1d4d758c870e596381d03c766d84537e38cebe39989cc287019": ["wondershare.com.", 0], + "bafbc0a3c1c0487feb4cba74e042b4c5747003dc5bdc8a74cb59e882fcf0e5b1": ["stc.", 1], + "bb0057dd31ed99a8f6fad654c904a87349f2c4857ef6c970f1c4019fb5bd86c2": ["interactivebrokers.com.", 0], + "bb0dfe23e2f553a73f63b8a39362e20ae366972d3a515a960213874c7425b3ae": ["seesaa.net.", 0], + "bb0ef4dff134fca7493f8e72c5a1059e69c9d4c338158e8ee9353242d6bffcc7": ["m4ufree.tv.", 0], + "bb1334f517f77b7c891cc029131b6c9d4bc43450158a7b80392b56f9b40e4acd": ["hellomagazine.com.", 0], + "bb22ec875487db77bdd61b927c427143e2c4ab4a2248d25f88c887434828876b": ["internethaber.com.", 0], + "bb230e64fe179c20c3cf58e3d39a3d2a67f1647ca04f772e6063c6becc615305": ["wallstreetoasis.com.", 0], + "bb28613a5260a450fd57a53dd204bae27343325ebfa668ac949553de4b1ac5cb": ["espn.com.", 0], + "bb2ad2588b1f3477f45fb241405f408d6a03150ab8b6f089392adbb19a93a1b3": ["promodescuentos.com.", 0], + "bb311770029e4b47d51600c07c439bf519792f96b64393eb2923098229ec8dfb": ["jianguoyun.com.", 0], + "bb375935371821c72311a0bb28b328b8a9a6cf1e5236762a1646039dd573acd7": ["kaochong.com.", 0], + "bb3baa2d6820848e6936ec5d92bd5b70cdcc452178523605f8e577a3c1bd9050": ["iqoo.me.", 0], + "bb40ab0cadfc93f3bec9c5f83e42fae1d112f6dc442a20927ca41b5c0f80c38f": ["airarabia.com.", 0], + "bb42ff226a4289dd8cd458cae8e03a8f703079c696b4ca7653b6bc02cd272b14": ["d1xz.net.", 0], + "bb49f0194302cd1d1b898ae8e474b1e86a09b6f720f4ee59a8ee2c53375d737b": ["steamstatic.com.", 0], + "bb4b7e0e9bbd9e898a587b62f6aac264579043e328064ca90ee3270ce02458d7": ["getdroidtips.com.", 0], + "bb4c0fa5b7add8ed1ffbe35d1549e1dbff444412cbef765ffb52cc55c400f944": ["yingjiesheng.com.", 0], + "bb51e4fa801983bf6b3d49350b41f22f535bdbc907e4613cf340ee526d970161": ["onlinenic.com.", 0], + "bb61b1acc30baa653a495031ca7045b25841c631809f4040d5c7418c0f711df4": ["odu.edu.", 0], + "bb640ac784836af69148103c0265cee5549d4835335c1e1089e6cad569af2eed": ["xn--55qw42g.", 1], + "bb64e3956c6edd054a1169153423e35779ccbfa9f2611abd8aee665bdcb0a1ab": ["10000recipe.com.", 0], + "bb680a5744a32ab544749c1f0e48470c0641347da2c991dd1b929c916a24abf7": ["netkeiba.com.", 0], + "bb68261b2e0d8968e027e61d1904bb7a96af752f73778d16eb10ed429f62db47": ["gitbooks.io.", 0], + "bb7637f4cb7d4f019ca727e92e2ff872cca327c6bf980e99cfc8c97ec1465628": ["techwalla.com.", 0], + "bb763d377a89e85a909df5df1cf745990ac9481927d20ae6148b1ac4dae40a58": ["3commas.io.", 0], + "bb771e4a703b97797ce17d20569c3f00a80105286d299b947e6e6c198588a86c": ["aib.ie.", 0], + "bb7e03d7f92bf0c029a265e8068647b8d438f1d7481b5bda8d868e72f3098b03": ["newsweekjapan.jp.", 0], + "bb7eebbb7049688f0dbecb69efa2cc399e7a57b9e8b7fab95dc03d3b392d09cc": ["marktplaats.nl.", 0], + "bb8495c48f898db44e95e327fec8318cc8a346970fa4fa849d9339a3b921a43d": ["uned.es.", 0], + "bb8b962373ae40e9945534bac46e73fce3e8169dc66c535ef17346f1644fbcb3": ["myisolved.com.", 0], + "bb8ceb366ba0b00e648c07d70c56684e26e320170b13b81c4773028a5141ab40": ["teamfortress.com.", 0], + "bb95a36b5328110ac43e6f475d9fa53e227b4ede669b0f90a663638416fedd3b": ["idealista.com.", 0], + "bb9dd3e4004644ba78761599fcf9685ab736d136892a0925df1dadc3101ff337": ["computerhoy.com.", 0], + "bba6e7bc2d20557a5b819e90fbff95107ee82d594598a33dd51e5e500282ef71": ["ro.", 1], + "bba80e8c809e80275eab7e328dcf88cfe82e002a12e169689adeb75853f38f11": ["ped-kopilka.ru.", 0], + "bbaa4ce74ac54c513e2587c7ad45ac565f4728427a3504317e16b81bd208300a": ["toon.at.", 0], + "bbac1218d2d173d0fab95f521d106ba77289c3b483304c74d71f022023d8e0fa": ["jioconnect.com.", 0], + "bbaf7c8a81223f754bf6b50ec0ebd1caf361aa5986fa1f7693bae4837995e689": ["richardli.", 1], + "bbbfea9bf75d3acfc85130dc3a2f41d3de4c4690fd657196d589473066c21709": ["bollywoodhungama.com.", 0], + "bbc4fdcae2306df618aec4a7012e890e66479efebb41ff1689c795e516641ece": ["aljazeera.com.", 0], + "bbc919307479c45b61dce8f51732510a1961c70b32b481dcb69ae7797b2d4839": ["square-enix-games.com.", 0], + "bbc98e633ab89ac97e9f68c2b350803a4915ef951a5732b58ff46a5e8fe87a0d": ["ansible.com.", 0], + "bbcf21900262a7a8fea5cf91dae15ab2dc1b7de06c13ca90ea27e03ffcb3fef1": ["hdroute.org.", 0], + "bbdd9d41da790d523de7ef60bf7247744760d13e276d593ecffc07979b40cd4f": ["bulbagarden.net.", 0], + "bbe0e7ecc01640955fac1584ce7c19e136a63af1aecf5f55d931cc2f1451afc9": ["makeup.", 1], + "bbeb99ec31ea44be028e335e15e6b6dc6729312eb7a4e396113feb86dcaf8179": ["hostnegar.com.", 0], + "bbf59fb90aedd528427db476d26d90cc198f9f58cd2c4719aaff24282452c448": ["lesnumeriques.com.", 0], + "bbf9ee5b6b94e1f11d6f9bc3f45a28a5d86b65250cb2047e817e576857cbd0c9": ["nonghyup.com.", 0], + "bbfd4c849bd7a396cda1a2f51809af8e9bdc634058ac5fd3754a6082356906dc": ["cruises.", 1], + "bc054bdbd52c1fea00e4af1cb1cee84efbbc2944c9aa2800dd547cf676c16443": ["noaa.gov.", 0], + "bc0d78e8cd47f07d096f63bfa3f4df50923d3125d3ee99f4607b7937c8fde275": ["aafp.org.", 0], + "bc12c56ed26cb7bd987042f6cb919469dfda3ea5a54db85f7120dcf2ab84ca08": ["krisha.kz.", 0], + "bc16302faaebc424c02b9596babaca56e4de567206b3fc7b60e58ea0b5e9a1ef": ["lesco.gov.pk.", 0], + "bc1ec398832a559ec04d901ed0f6e613d9ebbb7fd99989643929c0d11ab0cefc": ["theglobeandmail.com.", 0], + "bc20ad9057df99c5b2cc7b449c8c5260c1e7d032badb44c647e0d54ea44169ff": ["opensooq.com.", 0], + "bc23e6b39180636f6af6e389392cd2cf7de52164bdda682f10e33dea5c376d75": ["b-amooz.com.", 0], + "bc2ae5e33ccc577891b42b65c6e4d50cef6d833551f65ef001b7b12f0e1cf570": ["olayan.", 1], + "bc2dbbd56e5b74d12fa4010e4b054b88dc9a9ef33e09122e53248635494a82b5": ["zoominfo.com.", 0], + "bc329a26144d8dbfc33cefc5bd285433980617b4267aed3c0955edaed5c30245": ["stylecaster.com.", 0], + "bc364d026bd3cbcf815102faba4ea397093ca5fae04795cbe45c6539b64bb2ec": ["britishcouncil.org.", 0], + "bc425537441dc67c78e318449ac5414c1c5ecb0197df9c238d85e760c7db434b": ["philips.", 1], + "bc48bc2375ef6edbf697dc5c0deb24bb9ee88f3abac9f1f360974133524b4195": ["apollo.io.", 0], + "bc4c42fc5c1cc8ff6479f1fe4df7c827270ccd58dbb026b164af3e7b5461c29a": ["justpaste.it.", 0], + "bc4d6a5d981d35e571c44d490ff96399e8cb6cb7d2499531290994f48ff1784f": ["easyjet.com.", 0], + "bc56f95ab1ed5870aced45f082c4f422fcea838247f3eda96834cf1c814a603e": ["zurnal24.si.", 0], + "bc584ec27f5d3b170d151c4c566c80c621ded29f006955de39dbce96f4916005": ["sofi.com.", 0], + "bc63dbf303d7aa3a1be547dea74668a8f65b3f9fb42aa6bcf49c9dfcee8e5be8": ["fws.tw.", 0], + "bc6d62538d5023049cf2382475315ec4b4855ff16a1ce8518ba8a0c25facc9b1": ["waldenu.edu.", 0], + "bc6dade531b7761f1e74d4e38c46167ec0f921690ab117dacf2352ef6d74b870": ["9ku.com.", 0], + "bc6f7b6c3efe39fbbf746433277ff17359594d78ba08edacc6cfb6525c7f49d6": ["sourceforge.net.", 0], + "bc7a8d346ab3f338468c0495db95aa01081a2f4d691f984728edf0c3231474bf": ["500.com.", 0], + "bc7e42b9f7925ae8935eb034bc573e09ba2289947c66c534473b7343e5e3dc9f": ["storyblocks.com.", 0], + "bc809011f41eaa9d1aa5c36dc483d0f9cbf2e037fa9613be98719fa666287065": ["unimelb.edu.au.", 0], + "bc918d3e4637cf73b73ce57d7ad85ee22197a4d91209fb5ada0c8a80eb7811ae": ["codeigniter.com.", 0], + "bc93b29a9fdc1f6d66e70f73b95080f2fa7ad38f9df5d4e9ee71202354991947": ["bestbuy.", 1], + "bc969338e8890e114eda1fdb5f13dd253f559e35b2ee74f6097752443584fc9b": ["dengeki.com.", 0], + "bc9a8dc79a7c091c793105cf14955fe435487660126d52eeb33111d9bea4a59d": ["socialmediatoday.com.", 0], + "bca23db282a9d09881792ab185ba75cd478b8374c95aaf81fe622cefda9ffec1": ["promobit.com.br.", 0], + "bca751290bdf60f00dd8442fb07eae4d58812375f9ee5674d1d878cb6514b2bd": ["jq22.com.", 0], + "bcb8d4cc0e5f4d4cf01054114ed5955703228da3c5362114c3c4da9abfe5d9c3": ["zfrontier.com.", 0], + "bcbb30d9292acf4c5fab4c43cd89dbefcefb1a355224be1828baea298432e237": ["shellshock.io.", 0], + "bcbe660de458aa5304c40cf4f4d0a52c60bb88503fb145b84d9ae5bb5ea1cf8a": ["sdcp.cn.", 0], + "bcc4a14edf46429c50fb0f317dc98c2e8371a6203f2509671dbb20e2509f67d0": ["rarbgproxy.org.", 0], + "bcc773aad8598785217d9a367482b3f6e5738d8b6782c59871dccbb5a4733448": ["rtvslo.si.", 0], + "bccc9e53ffa45b918c7cb8f54654e4a93bf49e39501415e87cd3ae2506c077bd": ["xunlei.com.", 0], + "bcdbbd14fbd37de62a66d8ec2a038440d266e5b0630bf98a28c6c30ccd975bbe": ["minecraftforum.net.", 0], + "bce016d886c240f6f57f3085dcddbb7f23d238fe7d674c6c56a4e89ef7de82dc": ["2ch-c.net.", 0], + "bce117e31c068217f9867f890aa29fa658b4ca3fe6762a739cd621b472884a24": ["wandoujia.com.", 0], + "bce44d3f0ded7a394fc585af9f60cc984b5558c6fe7b9f85f53fda9e7bbfc710": ["seasonvar.ru.", 0], + "bce4526fb92a65a610183b2448899a6e465f75694f6febe0b6e32067da55f436": ["lewdzone.com.", 0], + "bceb432551d142d5fd216a58933cbc4abab9f328e6be9e744ca37aafbf736dc1": ["us.", 1], + "bcec02e6855363d176f1a94d6bcafd127ccba1bb252c9de43dc92a8578c117d0": ["booru.org.", 0], + "bceec9f08c1b09012208bf1f9d5f829463af8522b628a86862380b96250fd40e": ["rakuten.co.jp.", 0], + "bcf098013b6230857b8a46a3ceb0f32ecc50de2b25e53249049effa66246d5de": ["xn--l1acc.", 1], + "bcfd7c6b3455e29154bcfb44bd3eeec2aab0fac147d9e5b653b4fc2d9cd513b7": ["trycarriage.com.", 0], + "bd038e3771e971819eb2cf9709ced149364f554614d9ed1b065926ad0024a48f": ["xnview.com.", 0], + "bd06ab61eb5655f04b7bbfdc3844613e34ee275dbf571838622aa27810d6999c": ["playbattlegrounds.com.cn.", 0], + "bd0991617b2835e7344d3925ddaceec9efff2005f0ed3b68d70e4bfa2bfc330e": ["tenies-online.best.", 0], + "bd0d96dfed5def0e68f9d367bd138b92bb7915fbbb0049b26ad1a930668e6161": ["neopets.com.", 0], + "bd0f0f2e8ff7b43f548c67470f154c1e5fe2af77e280fd457830477ce4e384aa": ["landsend.com.", 0], + "bd0f9e3ca79c038333c9840bb1dafd32d659e937bcc7c959493c2ffc341cd813": ["pingone.com.", 0], + "bd1b2553d2c31545af322484ece2bd39da07ac2d29bf16504a233b4bbfe76a57": ["siriusxm.com.", 0], + "bd214cec0262485dd59b21b058b1f33f65a0a819dc92a2529d4757b22c2230c2": ["world-art.ru.", 0], + "bd24c2ea664f1f17f02de3dd1f1f529e2f0186c43ddcd4274a75aeb295b74904": ["boredpanda.com.", 0], + "bd332ee8225ad85e8dc3af2b100f9b71d87428cb66ec7f4b9d4f38d95054064a": ["sri.gob.ec.", 0], + "bd4aa80d3bb8dd31f8bb6ab48d7a8e8b58c51cf23012d41e485a697f7126facb": ["minnstate.edu.", 0], + "bd51c039a125467082aac018a8d280e1f16515423745c14a7a10781ba9c3b493": ["388g.com.", 0], + "bd5b103d595031b3b931c13a3c150c401fa01ac1174e4e6a78fc196ae2990c03": ["patreon.com.", 0], + "bd6238883db1bd1ec003777477a9e771bc86b0f5dfb13e11ab6719f8c831a1c9": ["lu.", 1], + "bd64a17d65d6af61d27fdd4f23fcbec54321c52794aab31bc76ae9dd331f0cac": ["samakal.com.", 0], + "bd7b9785f537dbded198746e9a95261e188c2c0c392d4efaa5ea27167be7a85a": ["xn--yfro4i67o.", 1], + "bd7c4bea3e7d238022f7d1d961755bb31c656bf5f410b4d44eb34a25381111ed": ["ndr.de.", 0], + "bd7f4ce72e66dedd82294c312621a7ab1d10b7fc152310338a106e871e3c74da": ["taboola.com.", 0], + "bd8002552113a36650d30bf18f8cc4c978412ea9ade9f38cd21929ea36639e1c": ["kemdikbud.go.id.", 0], + "bd854a160b18356a916f7a8ced6656a1a5d466b92b627dc88150410718c0260b": ["hsw.cn.", 0], + "bd85944e5b9c8d5bcd84d38fb8ff04beaf2cc42e5d23a295bc559192c0a05453": ["klikbca.com.", 0], + "bd88914a7daed5dfc12e4b5fbf59ba84e81a107e3f78ef296b091ead46232849": ["boyfriendtv.com.", 0], + "bd8b4f59899e87b6f3b91a541015c39562f3ef200321ad1116d3e70e94a68289": ["interviewbit.com.", 0], + "bd8cdc99ecbca57a5b08f86476ef8d2b49b5ceb74887266ec58eaf1af8f98238": ["uts.edu.au.", 0], + "bd8f047c1d6c36c391c118fde5c414c6d398ce5f27066076907d183b72bd42fb": ["malltina.com.", 0], + "bd983203f9b3e6dcb8489f97b4099ba1fb9588e502506b9899d6802e7d4d47a1": ["wallethub.com.", 0], + "bd9caea0671e57021359deb9cdbc5a5c13b993ed3b509772147419ab3a593cb9": ["roocket.ir.", 0], + "bda8f1ba1e198f475e0daed79bf730daec389cd2e317471d7d9aea77fd852702": ["savemyexams.co.uk.", 0], + "bdaa1047cbfda2380e55378afc585028c37da69d29baf70f6c0f538fa0597427": ["interaction-design.org.", 0], + "bdb39f81cfa3ef30618ec2e136d743a613d0e662700bf5799262f1246fbcdf53": ["finn.no.", 0], + "bdb6787a963b1263964387cdc0366929ca264cd450008bfdeefa957d062e9ef6": ["llp.", 1], + "bdbb9747ec8b8f5fa50467e87f9d5652c63965ea6c3ef947fd787a0b4cb5f0c0": ["finviz.com.", 0], + "bdc441392b161df6e69598492021e434afd868eaf91f4de969e121a5e1223b06": ["spacebattles.com.", 0], + "bdc89661fa5ded16e11f61cdd12ac1b91305d016966abd7dc6eab90e783dfef3": ["blogsky.com.", 0], + "bdd20f085b8e1c3f2687b85b493fc7d5446661958754b75cc9a3d43074a1ee70": ["vanguardinvestor.co.uk.", 0], + "bdd3fa55ef0cf064892204e180a1ea611e501e3ea5ce60a2a18406cdac20b732": ["kaltura.com.", 0], + "bde7b144b38385ca58093bd89b1dfa705ca324036f0386dedf2ede4626d79d27": ["ruhr.", 1], + "bde95474408e163641c273eec8d3d88051555d775cc4ec4184e99563c842674e": ["simplii.com.", 0], + "bdf881055173efb73bb94f6941ccc8f3d16b5d8a49d9acfd73fc4b46e40dd259": ["javfor.tv.", 0], + "be014e974ed6b87ff064fde702ebcb61dd9826ebee0c6abef0864241090043b5": ["sms-activate.org.", 0], + "be03ad8a091f1d690ce84beff3f12b818b1a05e1f7b2cab8aeed53d160b86f32": ["excelityglobal.com.", 0], + "be04c5b7ac146721c0b882aee3218fe1fbe931839c4b316cdcbd8e34e57f085d": ["splitwise.com.", 0], + "be0a8422b249ae8e3fe460170b097d1e75da4c8c499b4db57b3f606014a1063b": ["pimpandhost.com.", 0], + "be0d19bfd03d65ef6554d8540b2690cf4ab3c006dafcb37be6253a06579859ca": ["hosyusokuhou.jp.", 0], + "be0e9d675b41f65f503cc5d580b62ff2897c8552dfae2e75e37fd5c2a2540fd4": ["cardgames.io.", 0], + "be17e5f23101ad8b4d260aa14ae248677d0b7eaeb5c8509c1ae81ecb2ae5e246": ["ifastnet.com.", 0], + "be1e3deed03938617ca679a02471ee23f8179c3671a601ab78ec84c3983d3dc2": ["ncedcloud.org.", 0], + "be22ba6504e60a01562405efa77afc362ca62db787c40ee0f1d8e7a543ddf4a0": ["destinyitemmanager.com.", 0], + "be2d8be18f88bbdb25a7d1f0443690481db5951fb1859fae0b2ac0de5d2fd322": ["prabhatkhabar.com.", 0], + "be3179543c9d1c265ba0a3405c6d81a11068cb9f5859d103a1e6d7dcd6e35a63": ["fnp.com.", 0], + "be4289196b80c0ca0831ebe43220a3d32cb5a5cc5db236783d4e21ef79f10db0": ["jcrew.com.", 0], + "be46b9a23e7e30bdef3e195055b6987c4c6bd514834be26042c8669f848738d8": ["tjx.", 1], + "be4a249dad01a127436fab038aa2c83207a432ada4743c41d943620c67d0de20": ["cctv.com.", 0], + "be4efca3225759b6777b2c8f74b31a7e2f03d7498e9e26a7707bf57384151b60": ["sex-studentki.love.", 0], + "be56dc6e405399d5f7691106e4f257aea1cf6a689f1d2daa317d11aa26b7a1bd": ["yanmaga.jp.", 0], + "be66b07bee739ef4cfc914096f64dab42f5086a44713af5289b74c576c53f62b": ["sallenet.org.", 0], + "be6d455ba76835cdc9632b4f473ca37578cf959152ba4885367120b8f0b049c3": ["betterdiscord.app.", 0], + "be6f7003714e54786933fcdc9e838023aa6cd22c0539bbb23c80945e004f8fa5": ["protothema.gr.", 0], + "be73703432110e81564093608c81382158529c99d0457115f761e9e5044f6d74": ["sendpulse.com.", 0], + "be7bf417889d9dad197abed16605262bd1d027c1814afa7e51f73c257fc82ad5": ["gundamlog.com.", 0], + "be7e5dd1c48b1e5a6094db06895a41962792754899220792bd9c34d6a195c24a": ["msdmanuals.com.", 0], + "be7e800dfa0788c2d2d9ea1667602e506dbe87f8d230c8856b4cfdd1ab821bc4": ["x-mol.com.", 0], + "be86a4cf4f117b821efcc01f1f44b384b6e00137e4540c35140bce344a8ea119": ["tuasaude.com.", 0], + "be89c75f973de16a454e1bcf57f8a9465edbc3ceddab8fee81e5c1740a266cbf": ["sutterhealth.org.", 0], + "be8f3b5758e28e47d32e3e508f277f09744e91387c80b59693c622e48b17b247": ["worldfree4u.gold.", 0], + "be93970186030112a1f859fca4db9a40063c9d0899a8eae8d3ab09646b0a76e5": ["pusan.ac.kr.", 0], + "be96eae1107c6a221ab527b6093c3ccb3667552940d496795cb74e47a52d28c6": ["disktool.cn.", 0], + "be978fc7686aa5261b25c5b9c4029e501f4eace805881a3ccf4bd7a50c3a9b00": ["helpdeskgeek.com.", 0], + "be998f84ba6f97ff7c2091dc7d620372b83603d3979d06e6a74661bcdbc9b01d": ["xn--e1a4c.", 1], + "be9aae1d74a722a83ca91e7cda9e24fb6378fe1984f91c392ddb49fb93662f63": ["gr.", 1], + "bea230c929d21327f03446abe9ed60138bd9ca3cfc6909638547d30374c53fa4": ["chintai.", 1], + "bea4b6fa209f9c48aecb6c86864022d584f90a74861e4b0c6a311115d552a05c": ["dj.", 1], + "bea55404ec73a536c1ca71bfcc9b1b6b581f37bc3a6640372c4276afa79596dd": ["xoom.com.", 0], + "bea6828fc10099fcfa2b2d02126394a3629852433e3c5ae40d3fcbaaaedcd414": ["linkedin.com.", 0], + "bebd0f2de889b1ca5bf684652fa4ab584686cafd7ebc10c4f4772bfc106c1e4b": ["userland.londontrustmedia.com.", 0], + "bec330d8943559f57d58e277ef840675f2bad28614336fe90a4c3da5becf4d51": ["ink.", 1], + "bec42736a630d0b294c5aa9e36be948a3f39be32ee6849c2d3567ad8d4cbbff0": ["rust-lang.org.", 0], + "becf9c71a13ab92314e7ddbb701f2ceb3d8f8354e14ce461cd39dd0a78a265ac": ["mef.gov.it.", 0], + "bed2c2f796474135274a4dacb6ce9cf00ad70a696b868e4cde6fa7b7f9d27ed4": ["programiz.com.", 0], + "bed44cce2c81bfaf7e15f7df994c8c44e62d86d35fb65ec1b30f2a6df4053c7d": ["thenorthface.nl.", 0], + "bed675efabd4096b4873f3fd987aa82b104f81b8956bcffd64ddcb54fe949d00": ["mathway.com.", 0], + "bed9a11cc648ff55a40e0ae95842cc1793c6b807483cd3f729cc892f817d9776": ["tutanota.com.", 0], + "bedae580eb58895e2105cfc58d786b3b8f1fb344ea2b8021cce21fca342970aa": ["rtl-theme.com.", 0], + "bedb5fd8e370c626d47e3b9b77f60cdfb5327ab580f7ef363fb524e66dfaf406": ["waze.com.", 0], + "bee1e571976711c99a562828e360233e0047712350a2a39dc44c110817a45d40": ["ntu.edu.tw.", 0], + "bef2707f908fcf0932477f595424e0183571d2ffdd24e00fcfa460cf9ca7b26d": ["alahliecorp.com.", 0], + "bef70544646e4ad90653ce27008938647c86f22058fcfd62a6bea7afbb016a05": ["hulu.com.", 0], + "bef7d142da65428d53a96fec6781fd35a1a9faa0fc3d12fbc0b4e63ab13ceb5d": ["yifile.com.", 0], + "beff9a9cca5528a635846d6edebaa143b0ed2410fa021539810722f6da7b2ef5": ["warthunder.com.", 0], + "bf07de8872c1c471b2f9de2c5be7e98775936d2127c4e83aec3bf1232074000c": ["xn--vuq861b.", 1], + "bf0895366d337e98165f1796d27c60c1a67e458dfa3853fd815f2c4d570ba18e": ["crs.", 1], + "bf193f1c39143dc5fd96117e7c8ae630e2fbacb88f7f34abe2cbe53e933f2630": ["vuetifyjs.com.", 0], + "bf19799c4715734f6d8631dc167586e61dfca2c4ac1e4a06125b4b4a70bf5ecb": ["snaptubeapp.com.", 0], + "bf1cb10e8872f05f3f6f924acb60cd21bd1f39b19dd8f52a5bbc4df3f460ef60": ["huatu.com.", 0], + "bf1dcb9eed35700b62f3800dfdd80819801ee04d7401924eb36938deb09cc443": ["tme.eu.", 0], + "bf248bf4fd9c8ab5c363e6644fe1e1a32bda6d48254469e99601a7db162ec429": ["unr.edu.", 0], + "bf2d6557e13a3d7fae469f47da04126a5b37d5f6179d50a593a6c89bc3ec242e": ["maigoo.com.", 0], + "bf311083cc52544bb6f979ed8d977dd82abc0bdee88a2eabf406069c54cf6c4a": ["livehindustan.com.", 0], + "bf32feb7f7c44384d75a049495e94608436a86b211eeaa3a8d28c1fcd4dd4508": ["metbuat.az.", 0], + "bf35da7ad26f4816f1fa5a1c85ed540d046e600d71ad233cc09006c206af991b": ["groovypost.com.", 0], + "bf3676d9e63ad25851228f01fcf092f7de0211c9a74717c2fa00c4f413800456": ["mubasher.info.", 0], + "bf37f7c300cd5a978eefc0efe0c715e5228dfea9e8d5b8e0d6a3bff3d62b10e5": ["activehosted.com.", 0], + "bf3803a4271c38b947c92301720d221bea1ab49eb2059a8bbeda55ccfd1f6b00": ["xn--mgbi4ecexp.", 1], + "bf383ec5abee0924582fc292d635b331330b3b852a229b98428cb58cfe2f322e": ["medicover.pl.", 0], + "bf49162a058774ea484554b8e4d76ec8c05766e45cc1d655f1b19c34e63a9854": ["nontondrama.lol.", 0], + "bf58860494aec3c41b331470fa70a4de67fb7344a7282222f082c54126a19a39": ["allkeyshop.com.", 0], + "bf5990444641291c25da3ddee250c5024d308e9ead899800add102375ddc95ab": ["privateproperty.co.za.", 0], + "bf5ee8989e686a93fd69d434c962c4517bcb4654742cd088adcd6c7383df8676": ["veziseriale.org.", 0], + "bf66354b421d81aa641cab30a475cfbcab5a95b8b90e23b3e203891ecd971bc1": ["clickbank.com.", 0], + "bf6dbcdcb36859aede2d1cb2d2e2b99320da2490788418861c8b73d98349ca57": ["axios.com.", 0], + "bf7087dd8269e854737a89b4945dfc3edac996ab8b0185be81ba2529a40a738c": ["duanmeiwen.com.", 0], + "bf73be8eeeacd4eef3f2563436e43b67ac8af5a188488f6e1ec4823d9789046b": ["nikkeibp.co.jp.", 0], + "bf77ed1084706f28268dd4fdce68920a733fd5d626cb8bb2909ec2ec743ad6ee": ["turkustan.az.", 0], + "bf7ba479fe5de2d00261fe6365887632d43f0001b999005f64592851d30083aa": ["giustizia.it.", 0], + "bf7bddbb5c7861a173c8ecc8215b937f384c34dc221940bf5268b48a7fc0ec87": ["elnacional.cat.", 0], + "bf7d2dd95a5434d26d8088da28f008c2921e381591ba172af8aeeb7dcc698560": ["cdninstagram.com.", 0], + "bf7f4f3122224d78c66d785a3e931b8b5cfb60ca032eab6bc3dbf4625b5c768a": ["dns-shop.ru.", 0], + "bf81f80a9df21c91ab4e44c3642a9510b34d6c3c9025931514e4fbe4271b8aa2": ["ilo.org.", 0], + "bf8342378b0e94469990830f0cebf8bb07eee76275a09bee732b41a0bfd95432": ["desi-serials.cc.", 0], + "bf8422ef9c174121ee78889671894f5dc67027d480a30ec53fab0fb9b6da1aca": ["worksmobile.com.", 0], + "bf8448cbdd91dea92db3d6ff27a0b4336ac5bc96ae3b492a00827ffeb521db04": ["visit-box.ru.", 0], + "bf87ef922bde88e027bc0981804ba27192ecae6d9409e12a41581a33a23d6df5": ["christies.com.", 0], + "bf9f239afe11b82d5ca4ec9cff114e44c219f5eaa90c547fbc34169059ebe057": ["jobbank.gc.ca.", 0], + "bf9ffc990a7bdb80b6a469aa039547b68d94c9f534dd308d8a134a7ef4bc1c69": ["xn--3ds443g.", 1], + "bfa977b18f8f6ecd25eeea1c3755a6257e429ae046de3e2a0b52ddd97b8fe871": ["googleadservices.com.", 0], + "bfa989e85968939daabb6f0d2aa3ea13eeab0baa357e9a7451cded4ce7da5f5c": ["jqueryscript.net.", 0], + "bfbe01df83f891d5bac52621639fa0673dd0fdde1a71b32e9982eca65294dfb8": ["ninjatrader.com.", 0], + "bfcb89c8bc62dc6a9ea4a89ea44a79ef5cfa4b52fecb11302e839c50eb0f326e": ["cf.", 1], + "bfcbd3e5cb7ea6934968e37e554c17765c68fa2f7653525fcf6a4ac1784bbf77": ["3dsky.org.", 0], + "bfcd4af091c3670f499840594a39e36b54d569d67b5798d5608d86821e572a0d": ["suntory.co.jp.", 0], + "bfd1660788b650d0e318a11b08e30abde4bd7b9d9e8d64d2c716d40fdbd95140": ["yahoo-net.jp.", 0], + "bfdb009730c73558e33df5b0ab8542e04711a487a8accc83178220fb1bfc5ae6": ["summitracing.com.", 0], + "bfed0f169e884897af6da1a21137d27e56ecdc3cd6185a9033e7b0cb8206fa21": ["myqcloud.com.", 0], + "bff2e83a44eb5975d62b8385d2572a74598472f81a4f5ef5b64bb16cb01a7c08": ["rw.", 1], + "bff8d580fcddbdf2e24fe22addce93f70660bde2ceb82fe5052a5398cafaad77": ["primagames.com.", 0], + "c002a45a851974d259b794a08ef4b2ac098a772c1fd27b2db08b112834169bca": ["rugby.", 1], + "c0082bab7fc74cbd480e63a86e70d0d6fdb90846a051c1a3aa9e79dce5b29c1b": ["supersoluce.com.", 0], + "c00e2538b85aaa66ac8dc3cf64a330905a209137ae7f51631578ce2e1029338f": ["asiatech.ir.", 0], + "c00e329d66e9d66a6c53d9a9022e50b02de9861a531ea6c54b04a0e951653c3a": ["fsymbols.com.", 0], + "c011c7e888eb338ad44cd6c11bbf2bddede4be7cba67ed3235c3338a176a3830": ["dignitymemorial.com.", 0], + "c014ad914953e3f9e26d12e790bd1d878d53453805d771c124383f458ec99759": ["bjut.edu.cn.", 0], + "c01fafbcf44921f2f38c549831746bd18a79a69f33a5e63073191021e0027460": ["rsload.net.", 0], + "c0278053ba88d277839145844e168c4d3c2509cdd3bcfd2ef1f70ee57121d7d4": ["gmgard.com.", 0], + "c02ccbed9a664c173892e063eb51b0ed9bed955f4384e12f01872b567b7d94d7": ["4gamer.net.", 0], + "c02ef8b41074c27f0f1bb6fe9d624a2c570ca098820bf94e933db7cc8f819f80": ["joyreactor.cc.", 0], + "c0331accf92acd7ac8fdc250e4172d762abdb81af48b6eea570b0fd95630d71f": ["goto.com.", 0], + "c033d3b1abe2cbcb0aef8830ad54def2900db1ecc869a72634c25494ea1ba4f8": ["mc.", 1], + "c03417172d22481eca864ab2ac21d37753a03d0250faf6dd4eab76107a50d86b": ["ucalgary.ca.", 0], + "c03475a8a3c8e55e8c44988bf998909817e466db5767310412cf60ea4637d931": ["o2tv.cz.", 0], + "c038d092f5cf623a6e0a60c155c87d8572b9209eda3f04325a568a315302454b": ["wowhead.com.", 0], + "c039297bf2cfac59ae57db6c3b8be140e4b13d8ae9c7b42846727565dae949f3": ["eapteka.ru.", 0], + "c03bedb3b6b7736891afd0e4907983d89600a9b0a337c38914db8bb430502c8b": ["pixelfederation.com.", 0], + "c03ec675290b7553bf1f0e16caed2f99fc9c8828218d6875ab8f099fc0628a02": ["elnorte.com.", 0], + "c043195862285badbbf5d884ca9617d85a0eede796645b5f9b19aa014f2f2448": ["uark.edu.", 0], + "c0432756230cb5df6d64274cbb4d65d8553c5d685ba21147eb582254e1b3a31a": ["cookpad.com.", 0], + "c047f1dda294a7a451802a4d926f4d980cde3373b29c7fab8f7db3727155953b": ["xn--fiq228c5hs.", 1], + "c048558d2d21d8329af998890909818930eb7de86e2a32681474b4b9818edcd8": ["meet.", 1], + "c050a44015741a1372d7a403a9028927b976c334ad130f646d3770ab4574795e": ["copart.com.", 0], + "c05330307161f16cf1098980c27bc4c31571fc0149e13febe4fb49d31e582d59": ["blue.", 1], + "c05dce5a2ccd66e50e68c982507c8ac53ca9f3950fc9b8812d3e72b50eb35d0d": ["mehrnews.com.", 0], + "c070b4a54f0bfca9038bb5c7bfe188fcb52a10dd717f067140e4416a0b933659": ["jdcloud.com.", 0], + "c0738797c853661130d50cff5b7208cc2bf6d252160deefe80a1aeb033ce76d9": ["today.", 1], + "c0783906c8a31087a039665d74e6884888d905a4dcafd6482a1ba9800eebb2e6": ["mci.ir.", 0], + "c08a60fffc087b2aa63b469703a72ac8886e019e3bb855447bd4850362d36960": ["allrecipes.com.", 0], + "c08d6b90fe266b4da4db8fff56f5e890c908753590abf7e4d0e00eab171e9b85": ["digitalspy.com.", 0], + "c09786763fcd0a5f253b8c871ee69028d7f6a9dadd970fd23bab2a72389fbc52": ["monotaro.com.", 0], + "c09d48b3d5671f93f6bbe8bb21f15e52ab4e3cd035574200694c965b64f75998": ["expert.", 1], + "c0a05653be54108aa2ecabf2f9df442bf35046a82b99c06e48b310b11f328981": ["mcb.mu.", 0], + "c0a7265f831c6d703505f6c3f54d72839c090250bc05eb1beaa6295b71efc1cd": ["associates.", 1], + "c0ae7fd84e9e42698ccc3dc404a484a7fa9d020782c78ad141443f967b6d1578": ["inkbunny.net.", 0], + "c0afb53e8a14ce05468ee2372a030f7701b81b14ab910c077d560f9d24a373b8": ["mailtrap.io.", 0], + "c0b267c8123b2951db6b686c625afd30284e0efda8f9d1f03e39da603d7bfa4e": ["itmop.com.", 0], + "c0b67a0b466d294d060507f8318357049445118576d93cffb8feb59c5c400c24": ["dafiti.com.br.", 0], + "c0b81e6b81cf3a672609cef82105ed5f64e9b302ba76ba75a1c1bf8ec0044036": ["bartarinha.ir.", 0], + "c0bcac042ba8879033860cc15dd3b07274c75b382aa6e4e613f564af769b0537": ["show.", 1], + "c0c057bae5be1ae535109cf59a3b56a5a71b255a29f7a8fad9d1ab9ba2364579": ["novaskin.me.", 0], + "c0cdd17dd133c8290312961ab5040c99df7b5e19772b3e78bbb688a9282133c4": ["pir.org.", 2], + "c0d260bc89cd40ae5a07714598637dd9a02dd385d63a073ea4ae0f749074fc66": ["zybooks.com.", 0], + "c0d3998273475a7ee9bd74a2aff828e3291e42d6d87780e64a477cb61cbfec14": ["booth.pm.", 0], + "c0e301da923c364a068ec300aa74958c1e1d3530e144ed5934440bcf8200bf7d": ["expressen.se.", 0], + "c0ec56806a0cbe2c24ce79eab921d2b570e477e7ecb9a9ef06fb7d6e90d1c127": ["ventusky.com.", 0], + "c0ef738d344a6a938adea381f0f62c7ab410fb98868586e1a6d16d5e685439d2": ["subito.it.", 0], + "c0f096be2d68a0f267599f45ceb4d2c4009e5312ca357085446801214edbfce0": ["iribnews.ir.", 0], + "c0f74f0fbc8e4e9d0d44bf0644b1f59895d5615865f27e5b9e16e68fda325565": ["whats-on-netflix.com.", 0], + "c0f9aa71f754675a7700d99ea681b330f3023575d18d8099ff8ad9b60e507450": ["commentimemorabili.it.", 0], + "c0fb6e9b4cd6fd70d4cef2fddf7b51c66f33d6652757a6f9edc2b49a197350b9": ["rutor.info.", 0], + "c109dad4094e8116bd12615e7348904ef120850577a9c66cc1ad248985f048ca": ["zalando-lounge.nl.", 0], + "c1109eae39787fe90f73577ced24c2c9b13e188043ca5d9e6e82c711a52eae6f": ["volvo.", 1], + "c1134cba5e09a5d244a94feb3d57636a7b4a33781a1d185ce2db2e90e3a09d8c": ["ax.", 1], + "c11614295ecb9b06ae35dcc067791e8a7fe6327c07d344818e4f856d3f11f9bc": ["hmhco.com.", 0], + "c12604cfcd518af528083ee4a42a5a98fe7294d2d364ddb61ac199587eb121f8": ["encar.com.", 0], + "c12d72b6eb74ccd1ac3ed19388919448ebd4a14feb73a5973d8872c70a2d247a": ["pocket-lint.com.", 0], + "c12e9dd7708404db3349e030641a00c1dd6f08afb773c99d9da7956720e22088": ["alotporn.com.", 0], + "c12eb5985b50536b3e8ef436cd651c380c8f873e4354fc8c4ee1b2145c272180": ["kotaksecurities.com.", 0], + "c135d8f9f140ac951705b0939d36457aef4ce3afb682eb9237272afa5ce15572": ["wampserver.com.", 0], + "c13612a6ea55b51d0fdd0fb26ba2d9d20bb6fbe97c2913e2fcaed926c9062e52": ["hirevue.com.", 0], + "c13b382956a188464e8d0e4cdeccb08f39cb222e5cf6b0d0d75c7e18c0ef7b41": ["avis.com.", 0], + "c13b594b81e668d840aa8694002b9242ee13978feaa49111c6fad701bcd119a2": ["2ip.ru.", 0], + "c141da516edf1d4675243cfad24bdfcbb5c911fcb54648179af4bf286b4f90b3": ["mcbbs.net.", 0], + "c1440143bb1bb3a6ed889d0ea8d20d159a3d45db952ec8a8f44567b308a60e76": ["sat24.com.", 0], + "c14b227115807656a474f0c85bf594d1ace7be838f72daa81f8d00f11970e14d": ["printful.com.", 0], + "c158e8370564826e0e8de0eed9bd8e7b944a7687a70bcb74b04281bb56ff6eaa": ["joinhomebase.com.", 0], + "c159535b92c0cc7336508da09ec1c72b403ff134474cc55e686ca6d84082c60f": ["membean.com.", 0], + "c159f79bf0ffb453a712c3f7fc8672bcdbc9a88669d4197705108f3e204f18bc": ["dinamalar.com.", 0], + "c163f19760196d3bd106b113ce6a3d3b43686de256d6a129d847d920325da308": ["gamespot.com.", 0], + "c165b83a829d1685d0cff64b55f1cf6c1febbe6b9b06208aa83109d025fa75e0": ["shkolo.bg.", 0], + "c16def5a1d1214d077788270f88394838740529a6a262bd41b2a89a25de7eabc": ["freepik.com.", 0], + "c16e093bab1f592f5b7bdd89b2a3cdddb77882d09220a6fef3490c74a8835954": ["adac.de.", 0], + "c1743cd5422699b8666dd1a7640dbeac768dd5041ab32c046472a74b81ad00ce": ["win7zhijia.cn.", 0], + "c17681d91f34237be3cedc17e0d16df9038e0ca4ab38f3a974f4425be82eb9fe": ["ppshk.com.", 0], + "c17b8c934c710a15b059aaee1f10d258f9a3c1f6c79e38ac6150d9d250ea02b5": ["androidpolice.com.", 0], + "c17cad9da5a48684e92ac007f0da2b66edfcd86d20528eaf2e6ff3301e2786fc": ["battlemetrics.com.", 0], + "c17d582d2786abc2b6edb7fd956560702e693147d9dd2b5d914b9c4c6addaf90": ["xxxstreams.org.", 0], + "c17e16e86fe43037bb29749456eef9999ae6838feb212179f47e3c6ac2fdf841": ["hamusoku.com.", 0], + "c17e41f6ed9ca0e7feebe1523a71e0b7aa3a0d62ee12a8c1af47a1fe5b7c8551": ["archive.org.", 0], + "c18a7e6ade4ddd944c4a031ae1f1763eff590f29be8238ce9c3d8ac5c8a12208": ["commsec.com.au.", 0], + "c1977af22eaafc3e5afff02646ea7bf4cf5625c9773c1960ad29d9f4950ecf63": ["elster.de.", 0], + "c19dec9c7e7ba90642b4e9b4837d7cd849f91c778243849dc88bfb6d962c767d": ["suomi24.fi.", 0], + "c1adc6202adee7c622b43b2629d898d9dfe16ece135c2bdd7dc5f769eedfc9d2": ["teachable.com.", 0], + "c1b8e305a0497a47d1ba69a67aa3818d3bbbbfa83179ee869bbdb5b20758e86f": ["muni.cz.", 0], + "c1b9eb20e35c012c66f5b42835eb3766e1be7214d479c7b98b8585e9a59fffd9": ["recode.pw.", 0], + "c1c54bfb2c87cfb961ebefb6382aecd18966445b8bb5c139019869649f48748c": ["garena.com.", 0], + "c1cabf18238a324eee6e81593d8e5bc1ce16f0bbbfea07aca88b095184cc319f": ["skybet.com.", 0], + "c1d8ee2b15adbbb220ee2fcb332c02b40b614e9e8fe788cdb0932c9ebbf76833": ["live.", 1], + "c1dbd77466cdd8768ca528f9d0ef27cd8321f88901d742712fc6d07df82f4898": ["okta.com.", 0], + "c1dd0300e68092ec18421c52b69e437b8586e7897194e2e06de680ed913208d0": ["8891.com.tw.", 0], + "c1fac2ec3f74161da0e45760c06031ebe06c0a59b22633ab3842213051972ec8": ["kalunga.com.br.", 0], + "c1ff0f01c60eec3896e3ed6469cc6afd02ad06d94c06effeec40445fffbf384b": ["fdj.fr.", 0], + "c2048b114590065c35641a439dad32b33cc1f879fef38b92bd63cd7bdca915be": ["sudrf.ru.", 0], + "c207e61db6e7a93394942052898ad034f391c98fe0adb6d1d525703f109baac5": ["elpais.com.", 0], + "c20d0a4d10b0f6cdf35b9d91088d76d98abb47e01d7b80f2ada20d465e22da74": ["xcelenergy.com.", 0], + "c20e1e268ffc49642a8cf354ee587c4eb98b64219cc094642705d712f9723d1c": ["nadra.gov.pk.", 0], + "c2110c8f1677ce764da69dd808a2150ff082621431b671c7c274576dc16a8d88": ["mgid.com.", 0], + "c212b06ca00985583de8bda3aac5e44fa5f132ab9eee2dfc35a5097153c18835": ["nutaku.net.", 0], + "c21c17d954c38446e8f1a3548f5f232cdd4747baf5373eb81e85ce3decdb488f": ["docker.com.", 0], + "c222ad204f0e71d9110591ffb33a3f03a76514b37b95c9e29dc9a251ebe6c8dd": ["77file.com.", 0], + "c22403d9fc4079faabd16a215206fbbc27a78efc18802375dba3c257119f8a09": ["xvideos-cdn.com.", 0], + "c2316b664728d5931e28ea10998de331c427324098b9aac4d2df29d993e0f57e": ["kbs.co.kr.", 0], + "c23281f0d4483dc9ece40de55baabf311ea0134cc88a2d4ddadc7073a75b24cb": ["car.", 1], + "c23965676a9434a90533b30d808b89c6e868704e184bba51eaf411598268d451": ["academy.", 1], + "c23a13c91046fb6a77aaaec937e6108b73ec6ec46c371fe4c931835bb2aa12b5": ["foodpanda.com.tw.", 0], + "c23ae9345adb08a72065b858d392b54cdbaf36d53b60ce27425272df6feaee28": ["gaitubao.com.", 0], + "c23c6faf2b845313162872c7588e75620dc48b39d64bb792ee8d5bfabc8f9768": ["mypeopledoc.com.", 0], + "c23d373769532a6a2d8e8d8f5a9cc85876877b76a4eaa6cb8afbe881a123b7d4": ["wargaming.net.", 0], + "c242692ea87f29c109bb4a444374247521a593894b45a0257db23e091c8edc10": ["paperlesspost.com.", 0], + "c24f4ac871b8a7baeb28c638377bb947c75e72c3243d10ae74ec22544db12896": ["promiedos.com.ar.", 0], + "c25726a7ea8f0d8049ca7428b77178d7fe3059c5e89c327128d812a034e48ccf": ["synonymo.fr.", 0], + "c25be7023baf58b7d47f9393c10f11861bbab5a82200589dc300e126849f4eab": ["blesk.cz.", 0], + "c26a52d510c18a3b83201ca2be18fa2713b2f87f786bee7b833965aea17d3fca": ["shoppersstop.com.", 0], + "c26f162225b05d9cf8053400ec88d02a18b76a21d4ef1b2d64babbdd5b27bab0": ["radiotimes.com.", 0], + "c272b94791a61b2a85a0523242ad9c78082f2a19353d6cbbc85700889a1fbfc2": ["umassonline.net.", 0], + "c27d68cadb023fffe8d0b81c8943de440f76cf9008419de40f4ebcdb41975f46": ["koreaboo.com.", 0], + "c27f276da92a3dce844742d65444e32c0a20f7528e050e76fbae19c59ec1a3b4": ["stgeorge.com.au.", 0], + "c27fd5e122bcc18e44cc3d3bbf7c330751fc7714b005e19ac4f3b803f6ba1112": ["bigbasket.com.", 0], + "c28336042274391a1130c0670dc01ea38ebf3a03d215abd7c3bbbd1934ba72f4": ["mandrillapp.com.", 0], + "c284b172b9f4f93129c6a07b43ad4297817faa56b14bb6f32b60f99706e3722b": ["ust.hk.", 0], + "c2854ef91e61e02f6d8c0163a7482f790f696f5309ccfd0ab52884032c256af3": ["locus.", 1], + "c2877d668b40fe4760e15a032211ba6d570dc338983db772cba66506c9fb1624": ["skrill.com.", 0], + "c28ef5bb7e81db62324bf53f9c3a40b51777d275b613aa7c8c72687646f9a6eb": ["ifunny.co.", 0], + "c291455ba4f4c33f0640bf6e60c975d08be4e10b8bdec31cd9181c669b2d2a49": ["fetishshrine.com.", 0], + "c29dece1163131affdee22f2155976da4240f1d94f9f4cde4c1abcf23804f8e0": ["ecitizen.go.ke.", 0], + "c2a1f00651f409c3a4842cc377f423520005cfaf2379db50f9a9e17f47f5aac0": ["donya-e-eqtesad.com.", 0], + "c2a3e6c8c7e55c90c6a07feb3ee94c30549774d28f098898002f1f1366c01d1e": ["iciciprulife.com.", 0], + "c2a833d9f5cdb7220a6e89cb044f80ebffc81a570d940e6fb4963661a646121d": ["torrent9.fm.", 0], + "c2ad66de463e7966471bd1527a306fbf329395853dffa827f06cd24aeff1b61a": ["puzzle-english.com.", 0], + "c2ae44a4d74577523d9e66a7db0e7b164b649c6d8a527cfea244cf60491c80ca": ["9anime.to.", 0], + "c2af433c8de5a3ed51aa3bf2a9b2a93eb7ccf3675c0545861994575d4403b002": ["goodsmile.info.", 0], + "c2b2806c8ddb6aee0b7b6460f0a7de34b851f07a78beeb8f045ad9c73bf66cba": ["salyk.kz.", 0], + "c2b65f4609b676d178e2c7cbddaa27a9e5983d2d22a50c97edbbd8355611de27": ["murata.com.", 0], + "c2b72f8f99d0403afbe2bd58c33a1467fe2ffdcc16a8b224feceb63277d91dc3": ["xn--mgba7c0bbn0a.", 1], + "c2b8f5334d0a2203639381df33acf6f2d926f59193220c8055a1efc49e718515": ["gnu.gplv3.com.", 2], + "c2bd85bbfba2b394c4adff9ed2f29bdb197e8355cffa255a1d2c03a0b697f537": ["unesco.org.", 0], + "c2d2c377cc3bcd2136901c9dd27c2989f034779300576bbfe4ff9061470bab3b": ["prudential.", 1], + "c2d93a66d1ed209eaf55f66371a6e332bb1a8858d0a2ac273392c4db86a57180": ["mediaexpert.pl.", 0], + "c2da32e6ece3a935f077aa1d4667a71f31811b3f1824633afeba18e742c31d83": ["a2hosting.com.", 0], + "c2dcb27a71bdd23b321b6b5b245dfc492708bbf51c753dc95f93d893540bcd59": ["privalia.com.", 0], + "c2e3673eac4429023f6fb220804c9787b623e4258a667f4ecb11a278d2f636d5": ["milenio.com.", 0], + "c2e411c80a7d377f8293fef5f61bf248ba03c66cdda2835aba7359cd38706b97": ["sheypoor.com.", 0], + "c2e96960d0ace545a4880d5ebadfdde3cb14fb619047882aba5e9dc4b12db710": ["fnn.jp.", 0], + "c2f1060c47544ef33ccab88ba362c11a8872f127e158975243d73bd48284eeb4": ["boca.gov.tw.", 0], + "c2f267405d21a2b3b5ca0322b22e62332329dd62daf5ef5250c9f484d328b6f4": ["bradesco.", 1], + "c308c11c84bc9cdccc9832e3ef15fc2285bcfff0867949aa4bf03a203eee52d7": ["ac.", 1], + "c30f0aadab5159b35d4a96718e2603fa89abfaff538b76b2c2c810f06e9ea8d9": ["deepin.org.", 0], + "c317b81f2b784fe80ae8beaf4d678215006415f4d917a02e3edafad7f37b5546": ["vidiq.com.", 0], + "c31aed26eb361d84404b0f6c75a2cead00797d56aece01a8bd0ae1aa03d71dec": ["mansurfer.com.", 0], + "c3229fbb062d9d188ed62da5bb2b2412691814edbb75e20e7001d3362f75707b": ["torrentz2.nz.", 0], + "c324875e20f4dbd367e574a11b91ca9bda9cc87a55704b6f89b9472a01907a28": ["visual-paradigm.com.", 0], + "c3299f6c64b15a209a8aa8f441beed4ebac5cc3d660fd07965821e78b4d16b60": ["ong.", 1], + "c329cf9be9618163a7acd62e195f2a051b72dcc1124639dbb7f897cde7c9b03f": ["walmartmexico.com.mx.", 0], + "c32aeff1eeb401f44c313745278a45b302b79ba0589160350b568cc583aaed15": ["animaker.com.", 0], + "c330dba1abc398422537ef151bc99adfc0c1f6ce07db86eb497eb59a9764207c": ["librus.pl.", 0], + "c340eded89af90740efcc97e5c144349f97e3088aa6747870aed0a3806c62fb3": ["razerzone.com.", 0], + "c342cac39146fa0d890ecfd812c2f2f5b3886569fdb1923067d1510adc61838a": ["sunporno.com.", 0], + "c34748016faa6e8d5e795b0918dc32922c0cfa9ba7f9cbe2d77515ff96736ca7": ["g2g.com.", 0], + "c3556852c0133cc76867a572f887600aa4998860a78a3e31028b264b77baba91": ["emag.ro.", 0], + "c35f94eaa802cfce3e5a92dff259a3b2af24cc888b6aaf9a6eda19789f8bb588": ["travelandleisure.com.", 0], + "c361e7c37bd6a77ec33a2686e85bbaf7d666e4d36caa1f041faa40c96a97b8f7": ["lamenteesmaravillosa.com.", 0], + "c3669a70ce96a2c3a71bdc653ef4e39368816990b1aa6512a260ea70ee6bf0b4": ["prestashop.com.", 0], + "c37411aa2e582f34e248ae9afe234979b8851d95d3b6429a648837bbc2ade42b": ["webassign.net.", 0], + "c38466b19a05c926e89e7e714912cc6b0c723cf89364c70c6e924268af6585c7": ["xn--mgbx4cd0ab.", 1], + "c388244f9cd1be76f14312122e4a81a4add65a945d6bc2e9304a7b3b94521c5a": ["ntdtv.com.", 0], + "c38d5aeabf1f7a2d833629bfa1a66a81b0503c1bf424828612236c24465f5e7d": ["mangafreak.net.", 0], + "c39b746a3f3b7805daf471768ddda91a53cc933bb7df6e02f7e77d279035ce7b": ["diplo.de.", 0], + "c3a0355d7dd7d8c3c11da93d2c7fd1b59703c8867296fd8a2c371ec392719ba5": ["tupianzj.com.", 0], + "c3a5ec3f4aef9eaf7abcdfe5df00b6e7805ab01dfc574dbd7af9ad2c675ec55d": ["myfitnesspal.com.", 0], + "c3a95ab810d4f0b998df2776d3a52ce8d70ffa824ea940b17a37071b2bb543a3": ["tdscpc.gov.in.", 0], + "c3bc2a78e676d7bda44e4748b8ff5516babd8f3b9c544e0a43f3dbeb85f52fe3": ["hotnews.ro.", 0], + "c3be0d46345c6cd778b32addea03cf185e78590ff700da93a83920072e0940be": ["flo.com.tr.", 0], + "c3be12885fcdda1d96be1c7dcf5d62daa5884bf577764061f2b020f825a28a40": ["rade.ir.", 0], + "c3c4b4801b0e263db3fb21a63b9d17e92623c49a989657571fb25bd9d5482af7": ["shaanxi.gov.cn.", 0], + "c3c607f2bf76f36cbf752b34f700677a1f3a67394dda66b8ce6efb63a8399000": ["macports.org.", 0], + "c3d569c9f06f6432b4cd98a5df7d3ffa85cc890e092b344977903e35aaffcb5e": ["fairwinds.", 1], + "c3d7b8d493c114d306bb17dd20cef812f25ebe067cdd999384a9bb2ed50db9dc": ["anz.", 1], + "c3d943a012c0cb801c7d01849ab4894e021b40e4cd8934e8c8f5ddbd72d5d56a": ["rankedboost.com.", 0], + "c3dd4acc6d70e478afd16c8fa44b1ce232484c23043f039e7856f26dbc45f94e": ["getbootstrap.com.", 0], + "c3ddd7560424ea1aa96171b526e71f53832fefaa8833c752cfb5c14e9f454f17": ["tag24.de.", 0], + "c3ed687e3eb66e9f68a48355e0745ec0f8bc7800a3dc237e86f0fa631719dece": ["gaodun.com.", 0], + "c409d92d14ba783c8237910561e526882992b1209a1ac40111df24eb98150541": ["groww.in.", 0], + "c41219b448ee190e3cc5625436c46b74f1f673b973e8b63f9654d36286db07d8": ["stackoverflow.com.", 0], + "c4167c00ae3606b8815ece273af14fdcfb0076cd1e0eef749354776060c44f91": ["thequint.com.", 0], + "c427f7c472998ea866e9693ee2db15a6aaedad2ef7c974713b67c807bbca138d": ["buenosaires.gob.ar.", 0], + "c4334a1cb4a11d7936db15f343417a133890dbc0ea233b6c770ed9ad224c526c": ["p30download.ir.", 0], + "c442356405cd2190b3ba177e428d4cc34aa3fd7aac4e6363636eda41521cc842": ["rakurakuseisan.jp.", 0], + "c4450136ef8acd12391505930857fac6f6b2a0187b204965aa545a508e5e3b5b": ["gartic.io.", 0], + "c448de0a08f98a380c808b83cc48f5ccd3cc8218d76739213581989f028597c4": ["milffox.com.", 0], + "c4590b63090568cee47571fbb425fc0e24e6822b86cfa5847aeb5572ee06a1d8": ["cornell.edu.", 0], + "c46223d8d47fd1799369acdf4eda0db51fc2055efaaeb5bf1a64ebbe91f58b62": ["alibabagroup.com.", 0], + "c465c0949f3ea943263226ab576204fd8d0a24538584591f43d500904e597ce6": ["indianrailways.gov.in.", 0], + "c4696263f093d170b95e479e81f6ffd50d903064df885e0406d6edd4c8794ec5": ["ablebits.com.", 0], + "c469c87308eeeda77cba065496406814ed4e0baa3e0fcc6ee14451451eb8da20": ["pointclickcare.com.", 0], + "c46c9aac62c67bccae3d8aa23e62106e415b539539f59c1604d67c2161dcfe72": ["shl.com.", 0], + "c46f3f17ab727a172196241a6d190a5888aac730375d431d91a4980c82d8b562": ["dickssportinggoods.com.", 0], + "c470b708b683b4414c89fca281c57a885fe413552eb8ac94d9e5a0447906bb03": ["bangbros.com.", 0], + "c47bc5fb54833d6ea14b78a8fa6209d71442a98a5dfc0888dedecc1fcc951211": ["vn.", 1], + "c47d5e6b46cf590a5d3d7f235513873dc19428e67f67686fb4f2442d9eca9bb9": ["wufoo.com.", 0], + "c4809b4ccd544fe08fbf436ed5cd48180fd0d0d941fafb917156ce508577c6dc": ["labanquepostale.fr.", 0], + "c484ad875450345345bb9d83f814c7e9019fb4504215124cbd27251eb7498d70": ["fernsehserien.de.", 0], + "c48715fd82c41c84f863ed1d73ccdca5461db98b2e69c64cc317e545be22cb3b": ["fednetbank.com.", 0], + "c48a93325052013b2875087377a9d2b21c1aa2fd6c9f42df6d8bc0b9e4252056": ["mahaonline.gov.in.", 0], + "c48b7bd66e370bcbc22f26e6dbedaa0a2a334d1096616c35bc8f28fd83c7494a": ["lolinez.com.", 0], + "c49289942fe48ac076f7856f6a1787063f9f688fc62d98c637fdb4a45051a4ac": ["lcwaikiki.com.", 0], + "c49316bc27549f30b0ebcff7f5ff878cbe00cc913f038aefe6c5a882ece6145b": ["ggee.", 1], + "c4986fa3fb7cea62f251dd2fd70f4c50085fb85a332eb9e81760e8d463da6811": ["heroforge.com.", 0], + "c49f3e28f0ea4adeca6e950ae550597f15727a3b52b7da4989804bf287ce2fbf": ["visa.", 1], + "c4a121ff24238b665417d9b09e297171283c867f577f029f8ea5a57b5bca4bed": ["dreamhost.com.", 0], + "c4a31834a13cd892b499d4a4defb14bdd6709b1a9309ae44aa074fa03b555526": ["readme.io.", 0], + "c4a42708bef514793f8db432d851358f70cca550b4a23ed5fa6cdd3680462fef": ["arabseed.ink.", 0], + "c4a95bf2ac60b6c2486ad99bdd4212521b12b6849bec558c33d945ad01e849f4": ["fantasticfiction.com.", 0], + "c4b0eb47c219f7e460c096057275dd8e67e5b27b4cb414431f3dd6891855e699": ["mangaku.vip.", 0], + "c4c4d14db073769235ca92752fc7a1a26133d783ea15553b96d585347c08bdf1": ["walla.co.il.", 0], + "c4dd5714eb6b3c184cbf406bef76e4b19dbef7c5dbc077837100f861a770a1a4": ["nullpoantenna.com.", 0], + "c4e5ac10f39fbf01991583b9082cedd0b73d4bcf4989eec4e221dd272cc25488": ["jio.", 1], + "c4f30cd827f9ff5e5c74447b7f4ebfa38753f132b16418a5cb82f3e7f47cc8bf": ["jobkorea.co.kr.", 0], + "c4f4aa3835633e7e875477166895ee413372c336327a4f4d57c54070c683c27a": ["gomovies.sx.", 0], + "c4fb9f6d59cd0a426a779b92e1c956b0618aa7da69f581cfc3ab676077716e58": ["allsechro.com.", 0], + "c5064aa7377c25746cd14056be6a699041ee46cdb264067c3418b3654b58cffa": ["cbinsights.com.", 0], + "c509bba04d6bde314fb46ec8932fd37a58ee67fbfe8ff5de0c2b3afb3c709f02": ["ycharts.com.", 0], + "c513e79afc13212f19fc2336c000b55f91173d524aa4d6524ba3138418b69af6": ["ccgp.gov.cn.", 0], + "c516c5ef02cb047f8e0d17ff07f737a96fc48b42d77d0d30378ad971cde6dd39": ["labo.tv.", 0], + "c5179081136cf8627a0ad856db99f65c8286f6a260e43130f96b92df7e85c821": ["tkgm.gov.tr.", 0], + "c51dec624ed58ba185e28afa4b26a13d31dc22be40a5e1f30c45732a3a10f2d8": ["vcu.edu.", 0], + "c51eadaef673e00172ee69717cafd4c8b6502231e064e046a6c4f1b58ebf593f": ["islcollective.com.", 0], + "c51ec0d9c09ecb0cf3b3c934411cbcc270d64c1cb6f646eb48612ddf62f9a494": ["01net.com.", 0], + "c520a32316347224c2bf4cb6f81684afd688025081972bb5feba60876bdfba50": ["infiniti.", 1], + "c52323d9c0191cab4811b80938078140b65ecf23be1897a382678fb2617af707": ["tui.", 1], + "c53ab2967fe242022d01376df3276b4244fdb36dc2a8446cc78c8604ad887b52": ["infojobs.com.br.", 0], + "c53ea6a63d9248aef34966ee7df00a4177d012874338eefc576063a79322702e": ["radio.", 1], + "c540910b4519b937048e5df92334b2edcb4e51798c0ccc9667089a3b7c812e0f": ["morganstanley.com.", 0], + "c540edb69429c44411d5ce8322949544f3375674a9b8d4539fd89c6dd77cede1": ["m-d.net.", 2], + "c54417443818a8e918d177f784951045a02ad9c3c49621002b488c7a88cb90cf": ["hardoff.co.jp.", 0], + "c54b49e7c63ba4079a096ca9a88f81adfcfda36e6ae5c8ba6bcc6b0d5d8a55e2": ["planalto.gov.br.", 0], + "c550244249855f5742cb84f2c065cec1d4007077a73a25c5102df36a87b47442": ["tehran.ir.", 0], + "c5533fa168203b40bff537c5ebfd09b5b2e8f55a0f03a6ac199b9db284adc5aa": ["scipy.org.", 0], + "c5558bc3618bfc188b83301aebbdcda81cbfe924c6e8d0d05ebecf39ded6d32a": ["rarbgaccessed.org.", 0], + "c55ebac5ea94f7e1d1b3d0ff210aadce3f4333c911269cc865c8b90f9859a612": ["mtggoldfish.com.", 0], + "c56143207c188f9e125cbc4170f8e139dc79678e98233e82a1b30450c91cb717": ["winners.", 1], + "c563c67cd7da4f869a29f745b716a618e7c3f55fcbb4473b5581b2928992a88a": ["guildwars2.com.", 0], + "c56ca9dd1bc0f8eb329cb00c8f55dc64e5fc821bed096141dd0807459a83e934": ["theage.com.au.", 0], + "c570cf2a07ce4990831ac146fb1fbc9d507cd494f09283a34d3fbf83c8f0fc0c": ["top-radio.ru.", 0], + "c57275065d24dea03957c299644f23f9208118e1d6c0017f33c7177aa0f8372b": ["bigcartel.com.", 0], + "c5769d01d2bb57e352449b7b5de14e9bc89e4bb1a640eb9c33b5a40d90fc54d9": ["myprotein.com.", 0], + "c57df77d4f0837cdb91bb54724145dee75312a9cb6e0177a66130373e72eba4d": ["watchjavonline.com.", 0], + "c5804e9f991041117c2d457dc638d1a6247ed8f169edfe61d400e4dfda8d7378": ["jotform.com.", 0], + "c5847eb29c538026733c2921d2c0be1406e6c4c6b342618f4d17654f524cacb7": ["bnu.edu.cn.", 0], + "c592ac10cbc13bbcc730f98680d8fbb0ad43bae5864d82283bbdcd2d397e1cf9": ["mayoclinic.org.", 0], + "c596e6bd2352546999f9f94eaa182b3fa55afefb38bc45f49e39719df86c62a4": ["pianbar.net.", 0], + "c597b37d2f580895875cbb0429b8d20a9a7268722ede3771a514819ff325325b": ["eset.com.", 0], + "c59a3d6c6c4137c2e50fcfe80433034bfe1ea690f55084552c9fb18f54a8dd7b": ["nownews.com.", 0], + "c5a556fd84cd658cd5bb1195444cc2c2abca8533e9d6cafc42daf34027821fd4": ["digi24.ro.", 0], + "c5a5e2cccd9cbad0d00aaac7b762d0bb5201c975deb9f2ed47670bb7681c2f09": ["bd-pratidin.com.", 0], + "c5a858953462709fbb0a54d4f3b94cb7baf860aab3b0f745d22d593c43cdfd24": ["profi.ru.", 0], + "c5ac84270c96f720d77b2ad55504c2e634305f30fae9976de8afd67bd523b6a3": ["lankadeepa.lk.", 0], + "c5afc69ae79b2e98e7a9f8968db4396a7d765ef67f0898209906f72088ee2bed": ["tsukumo.co.jp.", 0], + "c5b5312a757c8aa8c2a637fd8a90f1a8ff97e4336ff7c41dbeae2a0f7407b961": ["dlut.edu.cn.", 0], + "c5b62eb3adb961a9e7369f61e4adab4965160020a12897ebcd9a1dd2d723a71f": ["nesabamedia.com.", 0], + "c5b800e4716c0a9fb8301d4d752478da785df67f7ecf9497bfd482af1e513e99": ["gocompare.com.", 0], + "c5cd00ed717feca8350d7cc5de352e5f0d7322536e79750880a6a1538e56ae0f": ["zopim.com.", 0], + "c5d54c44eba2e55dfdcee9201bc9ffe733ed36abca8d9c46ab09a11e93ea1812": ["cgpeers.to.", 0], + "c5e04be90dfece5c9ec08fbf759b02e7c46915189b24f35d63213487af79873b": ["luogu.com.cn.", 0], + "c5e4b10ea890a0aabcea427f077987250d8b5370a7c66a19e2686370104b56fc": ["consumerreports.org.", 0], + "c5ee319a217d6ce55a44ee2d777c736d233fbb02e29cbc35f2ca5b3e8307c5ff": ["cyberciti.biz.", 0], + "c5f485bdaf650f54b4728c4fe97234ee9245f49b339615e04e58e4313b9e547b": ["neocities.org.", 0], + "c5f739e7493d0a70beab5b1f8b81db085125bcd30b523d37dad4a2a9550cf7d1": ["manychat.com.", 0], + "c5f87b0340b133aaaedc02499d4be00a5999bcd75b9c0005b9db553d109ade52": ["homeadvisor.com.", 0], + "c5fc9b5b984513d18be1f833a44f34d2bc06efeb5bb1f4f1da8fe234f7d5e787": ["share-videos.se.", 0], + "c5fe26a52740145c340aabb6638c6fa3a3b6ffaa16167a653539a63c3df95f9d": ["thatpervert.com.", 0], + "c5ffc6c72312595d5dd035a9e04d930c2a1fc6c82c7362bec06f7e03eea6e46a": ["cx.", 1], + "c6078c1f52507ebfc8d64fafd83208723f2405a7b7f6e1edb96babc6f3cd78ad": ["amobbs.com.", 0], + "c60a51c2ca376c84eec406e5325a3104cb4f805f6f9b3fd1efda9be1140f7131": ["adult.", 1], + "c620a2f48589c6d7ff275fe6b975279b69008e7cd4b620f7202168b09389c59e": ["medimops.de.", 0], + "c6220931339feb025287f0a88c5b16054d80f42f60e8a7554def7a891c4cd755": ["comcast.", 1], + "c624322cc2114148993c0da40c5f12affb8d2ecd6188a7706b47ea57fe34a6f7": ["abb.", 1], + "c625f19054f1db2d58506212509dca696ebc8b373eafb9d3e188439ddfef3fec": ["aqar.fm.", 0], + "c632d098af9d86e582886ae6105ac07a2bba092931653ac4723ceb3309736624": ["fx2ch.net.", 0], + "c63493639297d60c420d096ddd1d62c9d798e567b0fd75adf29771b8d2a2b075": ["bancomercantil.com.", 0], + "c641823ed1d6ee2133901792fe9df42f0269fe6e5a412310b1444fe58db80898": ["uupload.ir.", 0], + "c641ad73a02d084d8b711b20d261bc74f776dda3be678348a09ee71fc78744b2": ["viki.com.", 0], + "c647d8e25cf2622ea56f57cf173363501e0b312e82f0c605e2c9d864251e59f2": ["reliancedigital.in.", 0], + "c64ff7c14ee482e6ce29b1e399a016fe67b7bcf28968e8035a6acb5c197951da": ["doccheck.com.", 0], + "c651cdad9323ed85c9b9cdecfc0ae984dcee4068838c364eb995a2bce6eb4984": ["autobazar.eu.", 0], + "c65b05ab29f11f1ab5bd6b00c30aa3d9625192465508c2d391bf20ab5283ad67": ["dad.", 1], + "c65c4c12a62f37c2c72f91d613b71aa130dbc9bd32339ba93bd9b145edd3a5b0": ["filmtv.it.", 0], + "c6601dbb5ff2c452ecb934055c0c6869983776612d339ab9d597ef5bf1322986": ["fontsquirrel.com.", 0], + "c66582fafdd22b14616f0134d02f448f824448a7ae3193d05308c1083d652b5c": ["filewarez.tv.", 0], + "c666776df016eeac93148fcf480f82c1577f0e535584b537daa7d09ed845fa11": ["etoro.com.", 0], + "c66b5fddf514ff4acb983f7d264372e22a8ab5d6a392b0f6b891e8710f226bab": ["simplywall.st.", 0], + "c674230b32187b13eef2c973f308179e5aa57f246f901ec6bb9adfdaf4e0f81c": ["themoviedb.org.", 0], + "c677248d591acb75e56ef1c68b78dcae843dfc703f0755e51236e36ba4f8736c": ["ariba.com.", 0], + "c67742363bffb820c46e8d67a8ef4252e064debd31064b5b19b7f4d652f8f678": ["guitarcenter.com.", 0], + "c67cc28c77f98d7bdbd204f834d2bda2f77d921d31c4bced2144d6f52e79d15b": ["pictures.", 1], + "c6953ebddf06d1f170b67da74fa19cd1b80426adca776d48af978fb5e903b168": ["check24.de.", 0], + "c6995ea480bdc40e5e6948841313fddf6860bbdff61e027b68496845520b2e08": ["lesechos.fr.", 0], + "c69df6fd5b825b4c025c4a4c75a8172899e2150ecec93382ec7c997e5a065fb9": ["rospotrebnadzor.ru.", 0], + "c6a08da1d9369fbeee60b55a750efe6364eb87f4d2f702579a54a589272934d0": ["xn--xkc2dl3a5ee0h.", 1], + "c6a865953659045e55d62211bae4d3f0ea915e9fb99be06bc807534ca0d291c0": ["zero.", 1], + "c6a90c6cc2d05f19e66579b86f81b6e0c6a566c78f6619af0ea6f4f65321d7e0": ["architecturaldesigns.com.", 0], + "c6aa646a454d87b885e7859cfc52cd86d252549fc49372f91753be5366168624": ["quoka.de.", 0], + "c6ab5e9b50b88e0b1724ac0d5be00192b01c39f42644ff94432a522c19a345a7": ["advego.com.", 0], + "c6ab604b6867239fce3071722ab1ca096063d0164e730dfc557d1f3f0fcead6b": ["food.", 1], + "c6b1f89aa5e78074fc46ab7a253e24ef4b6b6c8eb7330f758a5e5da56ab97553": ["filgoal.com.", 0], + "c6b218a649fa72959242ed8f00d82814cd3b78eeb6a3eae728aef1f45725888b": ["modthesims.info.", 0], + "c6bc8b7a2c19f4b54f35a91335484b8092ca37c638a9fb17e43afa8c4df9fad2": ["mom.", 1], + "c6c34ab6dd1bc52b7b515c7b064b58593ce449b7fda77c4adc37672746b81bd7": ["jst.go.jp.", 0], + "c6c42c75deda5d25df373866b2d66852a99ca3d6413971ac334b3630fa64a19e": ["tutorialspoint.com.", 0], + "c6c8271bc2898bfb8dda8674a8c26159facc9bd362f8d756fe1aac8ae9141e2c": ["laravel.com.", 0], + "c6cce556df40a8382cf3bad847476feb83aeca1ffcf6a3b07f7890b6c47dd58c": ["steamcharts.com.", 0], + "c6e55234e1b56d43d84306f74feb5fe64b4cf825f696cff34908fe55d7e32664": ["cricut.com.", 0], + "c6e92a2d2e315f007a0e82e11b79bd22c707f5c43aa5670b8419585d6d4059e0": ["groupon.com.", 0], + "c6f12ee74af3e2a40df72936c5dd5cf8c126ed044d14bf248afe4e06c0cec2a0": ["ilmessaggero.it.", 0], + "c6f3b2e4cc9ccdcf9fc6160d4959e356a09a22dafbaa27a902b540c95ea63c7f": ["51fapiao.cn.", 0], + "c6fa364297e13c6a8f4d61755ff95174087b62138d8dd6363b57f1084bc3a81f": ["icubeswire.co.", 0], + "c6fce06c45feac23671114a7af0ad1162d89308cb1819f9ebfb6c15bb37375f2": ["fail.", 1], + "c6feb554518df07f10bf15fbde093d33dd11fcafe6b39a2aa00077d89254e5ab": ["dengekionline.com.", 0], + "c70a6c6e4320aacd9c2ebd041aae06307dcc1341e26372a6441ee7cdd01c4b5d": ["tcsion.com.", 0], + "c70c5f1ca8fe25f48a499e6823adc28fb293b29c88cb5af872b06b7e01441c55": ["onlineradiobox.com.", 0], + "c70ce2f4bedd97277086bf23da652d63d4e52c1e9bd167de77de6dc99617a1a7": ["thedailybeast.com.", 0], + "c70e19b4ff90e8a804e660cfafd5c4840a56097f082ecc64c767ba3a448ffab5": ["xdf.cn.", 0], + "c712967001165bbbf712a3d448f661dd096abe9a5de19122656149345ab172b0": ["stheadline.com.", 0], + "c7252b0dc52c6b089c5b33bf9be4ddb091ee8d605b870bccb89c8648b1af9dd2": ["mubi.com.", 0], + "c725a3b9101ecec1d0c6a8616483839a45362822384981e02152fc4e48e5fff3": ["gifts.", 1], + "c72641f7019c159439981d43f26cb11b020b0db700f293198b693f0b8f7bb90a": ["showingtime.com.", 0], + "c730e7f70f8a15b0f637f9ae908092dd90f1445fd1a713febeb11ed49c14b809": ["besttrendnews.net.", 0], + "c732003b1b44b6d6527baa612abfef270cf04b25a28f1453ff01a1d32b4f9f93": ["downxia.com.", 0], + "c7326a8714f15459fe58f2825e4925fa95421a51af535e720c718e54dab7b937": ["ipcc.ch.", 0], + "c73f9f4cd06ef60b2fa927cccc44a5d058a05800e46f990677035ed1ef611ea0": ["techtarget.com.", 0], + "c73fdedb1d36b28d70c46ce0e3d17ca5e7a3d5c4e0b82fe4ae45510eee1f6682": ["quora.com.", 0], + "c7435dc74479ff318cc5a1ed76812896ff9b48973bbf2627d70b907c4edd31e6": ["tradingeconomics.com.", 0], + "c7442e1ee7643d8399d666149b16ec5202a1e12ccaad35e451616312a83c16ce": ["jkforum.net.", 0], + "c74466e9ba38e18f16367328876e5b7672c7f423a8f80c9da1c3139bab7a7944": ["grammarcheck.net.", 0], + "c747e85688a8992653726f568f4eea41c0c97a265f1288a0fc876a7ad07ab6f5": ["lipsy.", 1], + "c74fdcb2425d9df39ccfe3e8f918402448936c32696e81810f94a7e8ef7c55ae": ["gujarat.gov.in.", 0], + "c753a7d956e8354bf09455a2d39c27e0c90504dda62eb029eeaef77d5d2956b7": ["rottentomatoes.com.", 0], + "c7581dd80facd00884eeffce13fe1509fc33636aab6d39830dd81c0aa8a6614f": ["8264.com.", 0], + "c7603a400bb84d5fd39026f293487be955503ad93f15448ff4bf0953e4d138b7": ["9to5google.com.", 0], + "c76189f2436034260f1bfa44665d2a0fa41a1f19d78ddbef9fdbc68d1fa9e4b8": ["qol.az.", 0], + "c761d16c4b8269119d6a2a3b9235a16f71fd35acba2e30fa8c8d1fc3d76c8dd4": ["mingpao.com.", 0], + "c7620077ec52a95e8adb1ed386350706785b218bb4cf1536b59e6501e90b3b52": ["unjobs.org.", 0], + "c76903927fcab90f7a94a52e1f278c75cc089457418a377d0e04948b64c59f68": ["uxplanet.org.", 0], + "c76945e02217710bfe163b2b60097f1eb74f8095a4e448ad2163e35731133174": ["partitionwizard.com.", 0], + "c76b9fd33f2a541d10530be183c60a148aa53c2f9796d661abe89b5f9c4cbfaa": ["chinapp.com.", 0], + "c7729a5db8417d295f79de9f2274e622b919e46981f4e76408313e74e8285c8f": ["myvirtualbranch.com.", 0], + "c773bbcce832f4397708e9eaae58cab340f192800ebbbd57b397337d52fa0955": ["dior.com.", 0], + "c778185f6b396948e9de1f764bc0116b57f981f6d6cfff7ecad08ef43f8c9c11": ["azure.", 1], + "c780611dd1400ac566208783aafdc3c79c05fe8049dab87094ddd535c5deea54": ["binance.com.", 0], + "c78eb6ea8a78034097df2be4e60faf4559e0a654299d354c6546b586268d2ade": ["ecwid.com.", 0], + "c793179882db4d83880e2a8298dd1ab570d5f3f8fa552db4e3eb80a8e22c7d49": ["zto.com.", 0], + "c798cb8addef7f24a473df313b6719db3eea770ea81c8d5391b91c7bdc9d85f6": ["confirmtkt.com.", 0], + "c79a0eae47dd1488aa9f9acf0ad3e9ffb1ce2ab1820cf2bedf95694eaa578852": ["logmein.com.", 0], + "c79f729cf8e3a06241973a9fb263aeff0aaf1a980119fd18e8a9ac3fe94e72c2": ["questionablequesting.com.", 0], + "c7ac4ede53103dd63aa4f3db254629b3a267ce319390fa4c107df724cf6b4649": ["tunes.", 1], + "c7ae785beef0aaba9d726c9bad51d1c1b75170516608f90a39ddd94bb97f865e": ["pki.gov.kz.", 0], + "c7af7c32e4ba317d12793b10327e93fa2e39de729bce7ad9138551765e8084ea": ["grapee.jp.", 0], + "c7affb51ffb5ff84bb886108952e42aca7d74619544295c4a431d66021ca4732": ["rewe.de.", 0], + "c7b60079e3c11cfeb327cb07b474320aa0ec06a2c3c563b73c73eee7a58eabfc": ["kroger.com.", 0], + "c7c6c09cbc0fe98a6f55c54d2f65fd2937eabf97c67d4410eed3b3ac79309cc7": ["cosme.net.", 0], + "c7cde151b54282c77ce4c211d9ebe50c4e3d5026c204aac7dd366c33d2367cf0": ["xn--flw351e.", 1], + "c7ceb8ab56132606f1f6961e745f4505bdad9ab7e213cca4ec95a01a5b7e73ec": ["siteground.com.", 0], + "c7d08218282378e15e4e82ba6ec842f410c23f904e99683ddf4d280e04191b46": ["polymtl.ca.", 0], + "c7d138aaf3ebe11b17101131ce831328ce9930848882324d5275cb85fdf36519": ["hitomi.la.", 0], + "c7d28f901c3af850e1aae41b241ee29e6fd4619b8230082c57f4b016acaf127b": ["abbvie.", 1], + "c7d502971378ed51694314607969c9aab9e39cd290884d31ca84266bf979028f": ["clinic.", 1], + "c7dade7c0748f58baf8d49c5b8739bca772951e9e142bcd6a2131333b9a59e6e": ["daumcdn.net.", 0], + "c7e1ef3976877d36f91c615fe5b0ca257c540fb18a8575c3a140572ea4b4eb1e": ["rico.com.vc.", 0], + "c7f7950deb5fa8a9ea2e165026c1c5b90363de749bf984d85ea11e7578353608": ["newrank.cn.", 0], + "c802b03debdd638431ba6e024959d83272a320ea6669cbc71bcd4bfbf112b5e1": ["tceo.ir.", 0], + "c803a8b870a9f48f345c97e5cbfc83b7680f55c0780917790528991ffdcddeb5": ["whowatch.tv.", 0], + "c807d11dacf2b4dec5b035371b93ede8f1af03385b1390cb002ec0822844475e": ["ucla.edu.", 0], + "c808b35713f3df4d313919a4d918929936cebd384af533ec31e5959c4dad1a65": ["cakeresume.com.", 0], + "c80c903d8956190da911456920b49ea7133b4ba8e628373443eb46918578b98c": ["weatherchannel.", 1], + "c81bbc71c67aa09b57273e34c0675b15f33bd3a05a0416403bc45c63ed6ceba5": ["jiayuan.com.", 0], + "c81ea60bf2987a2af9593df30e774b35f3f70d128614a887fc36b643cc1616db": ["usa.gov.", 0], + "c8237054c80fec071571bc48225e698efe3987d13bfdfa1d2254112126ed398d": ["libertaddigital.com.", 0], + "c8278ecd8572549fa256b813966bad5ded42ab6c0ac59d86d8fa727df79f0ad3": ["play-asia.com.", 0], + "c82e54faa090d650ecc8cf36456e4e8f8b472b7bc76ba33871daf9d6df8333a7": ["startribune.com.", 0], + "c837aacc01ad7b0b286dc4ea57251e696b765e811d323d963bad93d5561a319b": ["hm.", 1], + "c83b24a5cbd67fa5cb5d00f24bccfc4f6f615cb8ca045d07b070d22cc708ec34": ["poker.", 1], + "c8452f23028679c3d2978a8b2be16783e13e26f004ad7c55b84904e972fa3f27": ["asics.com.", 0], + "c84b5e36ca289c26bae5279868dc5a6a542e3e22b565c37e95723569ac27318a": ["cpabuild.com.", 0], + "c85227919339c1d4d461a80f5c58bbc8216de81a3696e7544e384e1621116f6e": ["youla.ru.", 0], + "c85bb7bb67293c0503fcfa0ab8c514190751315de7072d4af1c753436595c1d4": ["tushu.", 1], + "c85d13e2dea0effe2edb18cfb6b05d9ac6d6366fd21041cfcd4b02548e7efdf6": ["sampathvishwa.com.", 0], + "c86efc7cebb9bccf175e93638bd48bdc802afe41a0c7a7ebd7bdc69543bc7894": ["99designs.com.", 0], + "c882f2aff5bc0f959f9ca04cef6c046f8c87f556df7f52e41f03d78fc1b39b31": ["gucci.", 1], + "c88327709c28e080d888ea94d47092b0dd347c60979c96dfe6cc6a2336cf2ab3": ["lonestar.edu.", 0], + "c887dcb1ba1d441e560e17f7eec168cadce970e4cf517899582f38db4ff9a066": ["openworldnews.net.", 0], + "c88a24547f94fcb27dc6cef4ac60c871c372c39a96b67620a34e92bc255e46c3": ["99acres.com.", 0], + "c88f49b1cb830045658693a1da684bad8acaeeabaa84b953df1fdc67f16a9235": ["nu.", 1], + "c8972a9666f9095e861d817d80c9aaeeb344e6eff91bb67d061533d62c70f74e": ["mirconnect.ru.", 0], + "c89f182c0a8e18b9b0c0a9244eb5cc74223ba0d68c9e60ed3178bb9a12caea96": ["ninja.", 1], + "c8a3b754bc73af06cbf0a006ffc47cf75e46ceefdf958b356ae7d94b5680d896": ["natalie.mu.", 0], + "c8a469503de901d7027d9dfdc904aca3897d5af27e7516272d2a6b9eac487227": ["wlp-acs.com.", 0], + "c8a904ed69d96d3655c02017798a9b70381b84f90d97169afb253887db9f8fbb": ["acs.org.", 0], + "c8a922d2b04cb7c496084723d19351501e0e7cea0418e7d03cad8cd9076037f7": ["republicworld.com.", 0], + "c8aaaea1da23d12a9fc79dc7f2ab701e0063936128788c27f35b563f138ebf2f": ["omegle.com.", 0], + "c8b3c30b74f2aaba743b0f0f6984a4ac5236e9ed40fdb6730ffc281f27ee4c3e": ["genting.", 1], + "c8b92c582b3f5605f6b18a18fca25607d945012b83c56da359305a65e306228c": ["tvnow.de.", 0], + "c8b9369f69b8212982a9642881ddce203f7ac6a3aa6f6a28159ee8def3fcc20b": ["adorama.com.", 0], + "c8bc2f7f995e1d844a0f1a575b73ec4595f7f82292575ed49d26b43ed147bb7d": ["kyobobook.co.kr.", 0], + "c8bf719c3419155bfc2da98c39283cf43c270dbf839bf2d18d4e34163328301c": ["wireshark.org.", 0], + "c8c7290cfef8e9666b427681962201c8352f7461dc5198f86d0eb6832f65a6a7": ["yinwang.org.", 0], + "c8c8e5ba5c34f3bcc1d95c04244484c9aed5f0fb4ff91356a25f130f0cd6ba17": ["zenmarket.jp.", 0], + "c8cc03eedc74c2b659f18a19fb63ca6da554ab5a80048214b5fefcfcca7805f5": ["allbest.ru.", 0], + "c8cdba31dc9b07dfbd758c9a03637082129181e2d58288d3c776006a3031258e": ["calibre-ebook.com.", 0], + "c8d327d95de435ad5836db531cab78c1afad7416dcc55db19e05fc266691fcfc": ["jofogas.hu.", 0], + "c8d44c68bcdff3322a675aadaae233d7948158ff03f4a133aa8fb1fdaff49fa1": ["ningbo.gov.cn.", 0], + "c8d5163939c6c84fc91c2bcc9bc73fa73cc5a00fbb42513fccf0512a9dee247e": ["pingpongx.com.", 0], + "c8d61a684fea59b77905536b57d1907f91cbd8d5c160a48812ef9ea91669c2fb": ["hotcleaner.com.", 0], + "c8dbbe09ceab71d8962ec635c0d488688049b34ca5f5a99b539cb5cfecb53838": ["abzarwp.com.", 0], + "c8df05ed2505180b3a6044ebbb55dddbfe42dfc78389e8efd861c8dee2a8877b": ["1und1.de.", 0], + "c8df276ef3880e7fa19b9868bfcb432e2a03d37a50d079ec7f1c91f4c1f919be": ["istanbul.", 1], + "c8e08d2549b84463661a64857a46260488b71fc335ea84aabdccc7186122a0fb": ["consolegameswiki.com.", 0], + "c8e2287ee97cfb5d59d1e07c081bc82da76ca08183f696361b3fe35cf35e904b": ["disquscdn.com.", 0], + "c8f07b8003c9855e0d287290baa4c098890f957a78232b98966b5f9fb8292881": ["knifecenter.com.", 0], + "c8f711ac71e84c493bbc4139feb8fff4de0394cb0bf93d2055be5e90b8043fb8": ["gouv.qc.ca.", 0], + "c8f9cb6a50adfe9a48c46d09ee03b1aee871742745f15ee5bc8c01b84e26bbc9": ["nogizaka46.com.", 0], + "c90190b804c66e2eb56d94480d5ff23788ffa34f969d3f38fe85a7d62e458b76": ["handelsblatt.com.", 0], + "c902feb4692c85e6d7a9db4bf2e7cb9db95bbd41d6e92d17cea8d802e9da1904": ["redfin.com.", 0], + "c910b5fb0df56592cc9b5c8a27df96fb79f5de8fcd222b8322f02ae685e0ad57": ["jeecg.com.", 0], + "c914bc4d8b09fd426c2a22ebd53c233258486b65712595758fcd55ebafbbb6b7": ["usj.co.jp.", 0], + "c915c17899ff677992b524a3a76cc7f4212dca044d0c078e43f70674f5f2c793": ["nubank.com.br.", 0], + "c91c08c6a45e6440c8c85e9ba3efd762eecad341c81084cf0f9992aae6742095": ["mymusclevideo.com.", 0], + "c91cb7dfba71f4267825490f31f98532cc919a30f7c2cbdca45e54b4df60d857": ["stocksnap.io.", 0], + "c91f876f2a9361dd2b492d8db5bb60d0fa5eff76dd618c7e8a21a12382206dc0": ["fanfiction.net.", 0], + "c92407ccbe106b4677e7d64abfe5633c906a422d1ebf3717aaa748774c346c2d": ["qiniu.com.", 0], + "c92db723b489a2e4270c6eb2943c24240dd301a05fa0af2161639906cdd5970a": ["lady-first.me.", 0], + "c9361158ff2e5493a34b6b0195b50375d7ca3b6eab5398c58fb90faf1465030f": ["textnow.com.", 0], + "c937b9c74ca7f76c99735ecf710e43040fe48a153767041b2239a17e98cd3256": ["admob.com.", 0], + "c94011541fb1856f92d665ebd6bfc48934e16a10f4f7c926ff68fa2c82550f53": ["pokemon-matome.net.", 0], + "c9494bfa7e9c95f1ed76f7869132d3d442cbb55811e41d6d5e756d254ac4616b": ["pink.", 1], + "c94e58cbae853c7d419464e4ca7e40cb4712921bd4c740a5c637799b877aaa4c": ["oneindia.com.", 0], + "c94f75fe61a4e364efd4259b326d113f64e0cd6c53170c9dbbe90a6113f6fc9f": ["bit.namecoin.org.", 2], + "c952c9b0a6288a9f43b199b49c11a5ec12363cf6b613bad795afc7eb47d281a0": ["bible.", 1], + "c958167297108853f62fd770f187a4bf929610f215a69f83a5e97b55527e72df": ["twilio.com.", 0], + "c958b34798edde722b944c39971c4ecd8f12e5d51a702c602082808e9638d12d": ["xn--d1alf.", 1], + "c95a2641c65c46efed5d369aa1bb691637376c0305a9e5449bc0f6d58eadf85f": ["stihi.ru.", 0], + "c96029ef547a13918071fc3f00aa8bca4d768575fcb2cab9ab1f38b8b14a880f": ["mhlw.go.jp.", 0], + "c98314df093d08e39093462b7df4f9a2210abba31daadecd30ff72e8ad17ba5f": ["obrazovaka.ru.", 0], + "c98424e5047853008dccd2a9ab51503f2c8f698ff821bb58eee5c59870de6770": ["haodf.com.", 0], + "c994a6bbbae6901a0b6ea014a44e80f7e06eed7edaeb57a7417ae35aca1e6ab8": ["techcrunch.com.", 0], + "c9963e15b4c9ad0fb884b6fa41dd4ce6616119459e5bd1b55373679d943a2538": ["sedo.com.", 0], + "c99e67026666d609456bc1896cbe423977248229622c25a07c12bf513cbcdb6b": ["wedding.", 1], + "c9a44d56a7347c365a7c5a059fc0a1b9362be7b163ccc0c7d348033f7e8d1d2a": ["whitehouse.gov.", 0], + "c9a703c63b7a013c90071a21edadc8cc8b3493b5cc9c03df9609c1267f57b592": ["creditunion.", 1], + "c9a8e8bb9f83eb0c6e248ecfbe7f54027db856f7b1ac26ea109ca04b728aca3d": ["successfactors.com.", 0], + "c9ad82be421a7053d324974382ca655941ed950f183a27a5c7e9eb678b45860a": ["dragonball-multiverse.com.", 0], + "c9afd78afd7fa604d659c80a91a72c4aefefbeb1b38524c298f91d53b8eb827c": ["piliapp.com.", 0], + "c9b7261da04ea1cd87f1776de4caefccea18bb644b27714c35848a56c1535186": ["zohopublic.com.", 0], + "c9b871f337a97380c9b1d581ed0b3e57b190f88db1cabba632021e8158e6bd32": ["nl.", 1], + "c9bc69ee975fc6acf90da0b13a379e138c6ddbd6ef06023c6ba673792c53a68c": ["watch.", 1], + "c9c012b05a8ed34f4059677a909b4ab0b1a8a7603dd30563553ddc236ff4c397": ["hostgator.com.", 0], + "c9cbec8d9a04daff9aa00731ea1e40650d5214e440f360b7bb2006a5af70b95b": ["bj-share.info.", 0], + "c9ce0e90b537114912a96f29ac4e56546c0616ca6bbef47870a6b144cbcc9ce5": ["caremark.com.", 0], + "c9ce328b8713c1828e9422c675f7ce834f76ccc0130afe20aa6606de676b0672": ["eurogirlsescort.com.", 0], + "c9d1a3ae4cbf8c1a67c9de22a89609dd54092c8c4dce4eb6d67fac4bd7daf5cf": ["losrios.edu.", 0], + "c9e2d42ce99622fabf9ee96139ed109249cf936378ccd7d15054537269664674": ["kafan.cn.", 0], + "c9e35c9074f21916574c41c30713343e1459bb438575f4c45c4a4e2f87215d33": ["sougouwiki.com.", 0], + "c9e49eb263e99db4540aa290a56274540ff4e35db571fd37dc31943bc22afbb2": ["dfcfw.com.", 0], + "c9e7841fba1377f94a2b2c333ab84938b12d9b5f882b824fb7c6defac42de683": ["djangoproject.com.", 0], + "c9e8dcd5a51784f8dce47d9bc5a20654c41df16d1b3951d0340ffd8de091707b": ["poringa.net.", 0], + "c9ebe1bf11ea6029f0df02ee50d2c5fd96c3bc578eb9e8221add002ecc3c186e": ["cqnews.net.", 0], + "c9eca4b4c941cd56c23aaea2d97bfcfca7c2f13a402cb85b9dfa3e80943dfcf0": ["meridiano.net.", 0], + "c9fb88ec09cd3f9cfff15214b5e52bfda83537e95a885df14510e25a753381d9": ["tukif.com.", 0], + "c9fd5c3e1e71747eb1090ce17c0cc025b23d77545fdb396c3f642333e6244d5b": ["iteye.com.", 0], + "ca0eb32bb4af6a5bc0f7793b28bdac9f1bc3dc7b6eb327ac1fdc7ecf3295aed5": ["nicehash.com.", 0], + "ca173b1917937885435db60d3fcf9c908444787ef1bd9bb76329ecddea2d6739": ["znanio.ru.", 0], + "ca189dd88a13ab5263f49f8d053129e8e1db6a4dfc8c1ffca641f2b0d713845b": ["domains.", 1], + "ca1ec7f336f5fa054b06a143f86c9f39215ebcb85b43f6667875094366032881": ["aiseesoft.com.", 0], + "ca2881dc4d1c89449de530cfec576d63b4e9b994049fcdf8320ab9f0190084a3": ["nordstromrack.com.", 0], + "ca33502f737e87f4369dbef7ad376c53134a36837068d28d39514fb5d0a567c0": ["komica.org.", 0], + "ca35ca048ba9834d0123a6451db38b43d9ba4a9ad88260532c6f56968b3bcd1e": ["bigo.sg.", 0], + "ca40af98d2e8cea9f52d6691dd1af8a052f41c3d351d8dbfcb51e0db25a6403b": ["nelnet.com.", 0], + "ca4425739ef9ddfb20fa451d117623528e7678e0ed811e57f824d0a7ce7f7216": ["hbr.org.", 0], + "ca5c1f99a36e37fddc2b68976a1170b6c7845dce7725861215ec71d3d0d59a37": ["repanso.com.", 0], + "ca68fe856d7c2cbe0c9fa11726aaecfdd47cc348041e9d2f1b575bad048ac211": ["nii.ac.jp.", 0], + "ca6988a4b6e8312ff86a854a2d77414856383994d2877250a7ed56ff662792e9": ["ghanaweb.com.", 0], + "ca755e7e8144096608e357fdb8bbcef04b991f2de2178470c4a58583b1ce04fe": ["skysports.com.", 0], + "ca78ad90ca712aa1be0a237a7aeb30ac70400a4f52e941ecf93de0f837cd52bd": ["sportsnet.ca.", 0], + "ca7e39108337843f4187202de5aa8ee7bdf69b4e12441292c23e0320afa1cb97": ["sexuria.net.", 0], + "ca817771366a4aafe20d93a699e8184e96eb4ae1960313a32c9b874e1e254736": ["hentaiplay.net.", 0], + "ca83c5827d02bd25657a574e0de3ff5590a3d87f437ba44e82b60afd46fe7b8c": ["relap.io.", 0], + "ca93abfc987698d286126bda6e01a7ec128070063749645f6835324f5e2f8ae4": ["ionicframework.com.", 0], + "caa8a5659a5a62ab6a65cf597449db88eba733e35aa881644d8b15e0ca2606f0": ["neimanmarcus.com.", 0], + "caa95e7ec6bbecdc83b4736746c5b873e44e3123afe7a3517e2841c0c27f30cc": ["rackspace.com.", 0], + "caa9e859fa863a93df0d686fa75a768243afe8c23ef31c4c2f0032fdabb7a563": ["v2ex.com.", 0], + "caab822a82397fd6ee4a7c1baf98baa8fdf679ce4ab36f558f33ceebbc633f5d": ["kwejk.pl.", 0], + "cab5d964e3a1a8e9d4f5ff6e7fbaec5ddad4bfb3bc678075f8f310b9084a96b6": ["bullhornstaffing.com.", 0], + "cac5b7f2137c488658670238950d348a084f372642dde7679ce6c77e8c63e34f": ["gu.", 1], + "cac903ce1162f0319877058980964804447bd676fee2c1ba0618794feae1f78a": ["filemail.com.", 0], + "cad0c27d312b7e435e5c49ce4d71a808a776a1be580216fde17d440d929b99d7": ["cliniko.com.", 0], + "cad2e715d4e2b7387a721bf623fd7ef3a750ace7c55be1c50bbc9e0362a203e1": ["buddy.works.", 0], + "cadb1c15ef6a5eea2936388918720d49b06c6a26af100cecf57a5a4ee6dbd5d6": ["tapsell.ir.", 0], + "cadbe3228ca4d4b667e3b333df16a96617bb5d22cabb59cae54aa6e76152a7a3": ["ecorp.com.", 0], + "cadeb984ac03c070c7a77c7ca365b1d9552a3968fe6ce59b46b48467602f9d06": ["aq.", 1], + "caeb202fa35c5b50f3f57936609599fe4571f38873d8188983806a3d53aa869f": ["drweb.ru.", 0], + "caebc78436db72f30c91b0b30ddf13c8d17b059002c8e1f291bf576021359c10": ["lumendatabase.org.", 0], + "caee876f280098afa83de87b06aa3ea5bc6f5852416acacecd31ff7fb18a98fe": ["kino.de.", 0], + "caf18f26b0ecb71659c1db8a6c68af53d8e76798ae6641da76b29c6da167ef79": ["msecnd.net.", 0], + "caf472bdafde263443889d6dd256f74dc2fb7261acf381af48f042e3333f4f78": ["csccloud.in.", 0], + "caf6f813fa0e2c09bc105d6767b7028d243e5e5cdd12d1cedba8d167df9a27d5": ["bejson.com.", 0], + "caf7b44ded1c65957fbf7e0c3fe0dd860cd12251cead01eae7971c6df00821e0": ["telkom.co.id.", 0], + "caf866a279c24763bb345ff47ddc6329d7717423839dce04f6775aa0166d1f75": ["uploadboy.com.", 0], + "cafc8c8eea4b1bf97baef3b353c1f03b6d922a16ba79267ec73d13d7a6990048": ["arab.", 1], + "cb02bf959ea3256e4c30d9f2fe55271cadf91f845caad34413c26562f012b48c": ["igromania.ru.", 0], + "cb0c78d5d322cbb80fe3415a32842efd71cdbcfecee22389a4998a08f0803ac3": ["cnbc.com.", 0], + "cb16c5f362e62c4f27f3a825c7d1a39e282f09b2958a70f9ae5488fc5233d2d8": ["topys.cn.", 0], + "cb1d5fed2f70b0fae54ae813ca236d7f0f358428e3eca87b59361a9cb7ef88f4": ["lr.", 1], + "cb25d4b09c0e22bf7e8280ac438e561852940c5dcf352a5d609bcf8aaf7c7186": ["falabella.com.", 0], + "cb2737b97260acbbd3624e91bea514400ec17397524edfcc0137db06b6b01a81": ["numerama.com.", 0], + "cb2c0e24d813b6d3c114c4a7f230cde4b23d10d97cf51fad308896f67061af19": ["phonandroid.com.", 0], + "cb336e1b243098a04cc7ba0dda181705cef56c6cb05bf8aa2b472edc08bbd124": ["barclays.", 1], + "cb38713d84a20e37e537ff8f83fd47109dbbdbf03021e19b408b124a59379924": ["phica.eu.", 0], + "cb387e33ce515a5ed8305f8e498e7c7816aa5884f3ba7d8f006b6f40c84df1b7": ["jav777.xyz.", 0], + "cb3ea33fad7a1f3e6ce33cb2dc4d6025d479cb40a90bcd7ca6b41ccebd7750f6": ["holidify.com.", 0], + "cb4698f4c221f61ddaa390c3c00332debe87e9c880880e9cbf349fa76c5b3194": ["topky.sk.", 0], + "cb4ad70f48c54ae88f4946064f18e51c6bbeb862e213647f98a0671971bf276a": ["banesconline.com.", 0], + "cb4e49c885b6085bae707f6bb91f7874131e08bbe4f84930e72250dd94a771d1": ["marinetraffic.com.", 0], + "cb4e747b256f8dae75158c55e89401e90dac8f2761829c772988b9cd88b1a3ac": ["arcot.com.", 0], + "cb5059029e056767e58f77fab27dc4faf68dbbb562c7a6e5096e481a0d37f4c7": ["finance.", 1], + "cb542921db05eea9fac08137745d3044917b53e978f9502d4eaa6812ed70d9fa": ["pole-emploi.fr.", 0], + "cb5ff02e9121270db0c790ffb31c71be320ae444cf613a8d1d55e6000491bee7": ["sl.", 1], + "cb67f0a61775d0cd03355d4c2a24784e741db64cdfb5cab17852df00876aadcf": ["8muses.com.", 0], + "cb72abf8c1988af0151c950284bb984a958e19c2c34a8a6972c1f9f7bae238e8": ["pximg.net.", 0], + "cb787bf8b5ac9eb64048cdd28a34ef0d7324aa799df1d470fd2465438cb3fba8": ["snhu.edu.", 0], + "cb7a8b2f7668a87ac9940b3bc3556104ee0df1e4eac14732e2855c1c893b6176": ["mcmaster.com.", 0], + "cb7dafb6360009e9ff455ccf0956b67239787724bd8323537e6afbc8dd9d2e23": ["usf.edu.", 0], + "cb7e5081b61e9169ea8398f19159b02f5169a61f9f5189e203b804d56a5381e4": ["gemius.com.", 0], + "cb85c88bd8fdae0162fcc048e69ac67a6f60dfe0bdf55075454cbb07a12e918a": ["pharmacy.", 1], + "cb8f30e9a7fe0c8e16569758e59ac0fa0bb1cfa126123dc91593e53539f21c58": ["bigcommerce.com.", 0], + "cb90c7cf31228a5711bffd7659643e122574f81a375b9fdb4cb0f7fb6fdafda2": ["ilgiornale.it.", 0], + "cba15ff4559d326e8fdd8f374f58e2b5225cfbc2d0d1dac4316a159d2dada2cd": ["megaresheba.ru.", 0], + "cbaa3f2797bb9162f0f80dc7039b9a971cacb58f6e23b2a60a153eecec56969f": ["statebank.", 1], + "cbace3d02d562d6955da83b80a75670d46a269d48b83fecbb0669daa5cb03ebc": ["registro.br.", 0], + "cbbcdf67f12653bd4e0f406e25c1f8631faa8855ed4fe507fc27cbe861594e05": ["redbull.com.", 0], + "cbbd684e3585ed19502b681e81e5df4d1a92279c2b93e80d53bcbc54fb8a51b1": ["e-estekhdam.com.", 0], + "cbc020ac5b0aacf1ce9a7be24be11b05251acec42036d8ae76929478efdd73eb": ["xn--mgbtx2b.", 1], + "cbcd9b816e2e824346c6b3b83b9493780454e08cb7606e2e1fe9b2b550c662f0": ["iocom.com.", 0], + "cbd237b76f4c34af4e4b21f111ed0d919fb341ac883cd71e142a7d8cf116dba3": ["clicknupload.to.", 0], + "cbe2921e5ea8cde541b159cee19c1ccbefca0278a7e6dde8d0f91f915a4098ef": ["copaair.com.", 0], + "cbe4aa2f52320e4328493ca1f4ac391ca97758ec5d5bcdaa7766d4f090eb8135": ["reliefweb.int.", 0], + "cbe7aac57cb7a0a552eec56f7c2ea9706dcda5312515acef768a9f5c7e4beeb5": ["basketball.", 1], + "cbea1f2b55cec56a1f676a40fccaf8d9a4eebe646319a23926b8abe066b0e0cd": ["unsw.edu.au.", 0], + "cbed6276fc1621d271575ea0a38eebe682911bb7139743337c6c99c4302ee26c": ["smithsonianmag.com.", 0], + "cbf19fcab5f073c66b523eb8fee25eafbb77bd7cce51862e4d6930690d83d291": ["blogmura.com.", 0], + "cbf27573b5baf916ffdaca09528580e0bd7a7bba55b4dc35215de514459801a3": ["wpolityce.pl.", 0], + "cbf52ef0112402ecff8e1c5e63302a262be10d4683d298cac2dd884f3b0b9fa9": ["cl.", 1], + "cc00438a4af402b1fe6bcfdb0782fb839d601ea98063ad507e3fad0d46e75188": ["gogetsy.com.", 0], + "cc035999f7367e3454aa0dde16790cf992cb47c376780273e795f7e08e75b5b0": ["similarsites.com.", 0], + "cc04fc395530d7b3d55ae730e33c62b5da415b220b4d6a723e8493939e0e3886": ["mileroticos.com.", 0], + "cc0e16037dc81991d33231f0dd0565a15f7272c05389ea15d40baaddd77455f0": ["waveapps.com.", 0], + "cc0fc520aaa7b88a5565c4aec70b61621cc35fa4e03f5c149dc86030d92354c1": ["awwwards.com.", 0], + "cc19b90d148505d0687e39d0db94f4524e34c819278f15d160f720163a97901a": ["catbox.moe.", 0], + "cc1feb3bcfe4231c56b5784cd97ac3721b529804130f484ee13548ecb422adb5": ["tsb.co.uk.", 0], + "cc22d953bfb13768e051829a887f4f5404dbcee3f2c71c3e39c2f668b83ac9e2": ["mirraw.com.", 0], + "cc387245f97093bb39a069742e725aeddf7dfa872756dbe25e00697c96a0868f": ["minecraftforge.net.", 0], + "cc3d8e32c35e0cba3723527cae047fe5ba685b3de4d778e6ecb98fb9fcce94bc": ["sydney.", 1], + "cc3dda18b11ec05c71e40f0a991d85acec4b282b5a21a2174955250013f50f1e": ["doramalive.ru.", 0], + "cc4afe9167f1f2ce8b93221b4b952182678bfe55a61bb29a67856c3fb30a7032": ["hughes.", 1], + "cc4d79e7664e18ceb15d4b8eaa53b77f15b04d87c7382a1ba5e18e48305a576f": ["ko-fi.com.", 0], + "cc56e3273314af8951c52cc42798bf5648d929f6d7d97e7cdb5efbaa3dcaf6f0": ["myspace.com.", 0], + "cc587c6cbe37f90f61f0b405a7c1aaea9b0f168e2f873986f7a6302b70f627fc": ["corporatefinanceinstitute.com.", 0], + "cc58920aca2bb4925215d54918a1e356c877a39c10164e5464f8577a2ccd541e": ["joy.", 1], + "cc5a575ab22f809ad62fe7dc20f8d3bc9a763c77be8cb18b85dcb63a9e9c297f": ["wolterskluwer.", 1], + "cc61bf86eab0a1031ecee83da4886dfdcc520f7227b5a345fe884ae2931afd86": ["oper.ru.", 0], + "cc73f708591d13188851f9727e8d357cee9abce5d1ad708e06d1d61d0aa1fbf5": ["netafraz.com.", 0], + "cc7b7897975df7d3f06027d92817d7641870352255d6a9669a2eeb4b6d52f555": ["hotbit.io.", 0], + "cc7f7cdf15705d7450dfe165085437042677a325861a25d8ff2d47375337ce43": ["hvg.hu.", 0], + "cc82be7379f500bcc1f3dea24ba6b54fc55c793aa39a2627b58480c4912c7ae3": ["sankei.com.", 0], + "cc85389fd41661070717918c20ae84ac772306dcb3b559cd324fa0c8c64822c2": ["wallpapersafari.com.", 0], + "cc86c075f0baa39f52c6534d0e22143dcc548ecebb272b7b373ed3837a093ad1": ["bashgah.com.", 0], + "cc88562209adc1b34dd155879f6aa939bf7fa37d8ad906d92cb641b2e0319b6c": ["fesoku.net.", 0], + "cc8d34d4446fb6fe138bacfbeffc774b31841821789aede9b31b5a8a45c7ae80": ["4archive.org.", 0], + "cc8f8275b0ab65db536da57f031a0996dc03a889ae642667af50eff120121707": ["numcalc.com.", 2], + "cc91e10b01b342db2f0344c5c82324f8628473e5cd1c4dfe67df2598f6f2c7d5": ["cyberlink.com.", 0], + "cc92e8bf2e79a8f1e22e03d475a2f50516f3dc756376a6a606b4b5f51cb92079": ["farsnews.ir.", 0], + "cc97017cf88ccf5feb85f2d3a09a90d7429721210edda5a493b2b1087b187ba5": ["bamboohr.com.", 0], + "cc9c264d6f7670ca7b139a8f2b64f9941795e88fb9996affbb7b1a0ebba70228": ["cutestat.com.", 0], + "cc9ef0dcafaed75e027d61b0a1006d93e5c87b2bc8e14dbad60714e2e73efcee": ["brickset.com.", 0], + "cca34417e884350df729e05eb3da366289ca0caca45fead546a8f7b9c4469a5d": ["apesk.com.", 0], + "ccab001492333dd3f1766e8ce5e52a0c51338e87edf2341bd52c1d14dd164f63": ["263.net.", 0], + "ccae6de442743ae36c2805220356b8a5fdf582a3af99af519a94c48fcbbdee0c": ["credit-suisse.com.", 0], + "ccb6bab18da26215ad2f5d7dd5b4204e3ec6a1a4faf14d89e312a9513ecaa6ba": ["worldbank.org.", 0], + "ccbcb65cd3b87de5fea2fa6b66140b9e9a6f5b608151836751f076f1d0b39070": ["gmw.cn.", 0], + "ccbd9759790a91337c9f4da5bdaf689b049c995a6c18fe69c6910ec2eb617b25": ["tamindir.com.", 0], + "ccbdb82c06cd1a3da1d68aba4208a6cc59918953c2ad6deaeabd837e1bae6d07": ["parspack.com.", 0], + "ccc6d6322a82507ff229caff6c6d95165f03ddf73b197d1e499cd92348faddd8": ["xpressbees.com.", 0], + "ccccdee8cd549ed7421fe7374009759440d49dcc6b8d5d7438ced5a61eaac311": ["audacityteam.org.", 0], + "ccd086c7f76dfb6721276d5350b99fe80528b25f9686338fa06f0f94d4dfd92a": ["hln.be.", 0], + "ccd08beffbb6855c3763b1aac4a31e6a2a59596f4a719d326db4aa7f4f69db3e": ["elfinanciero.com.mx.", 0], + "ccdf17c39eebf154252f24da288c9ffe5fdb8058509a58885d9352303f52cf3c": ["sfchronicle.com.", 0], + "cce17a6ba2df9f5eed450520d4114f5e2d67ebca890ed47977fcf35ca8fba7cb": ["xn--rvc1e0am3e.", 1], + "cce9c73d80d183a99b3627a6c3f20f2a9dfef70dfb83116e8605832b109f5528": ["solar.", 1], + "cceefd7e0545bcf8b6d19f3b5750c8a3ee8350418877bc6fb12e32de28137355": ["bar.", 1], + "ccf86f487ad238b1814e1b8ea4f6dd6f44cf53c0c9eea665ca7e5ef8af041063": ["imleagues.com.", 0], + "ccfb0e8a8176dcf30b2e17f9500f6e23ee5691979525f9d4a5061ef61050e26e": ["saharareporters.com.", 0], + "cd070b7bb52627d74ba41581cfa3d35a8d2001f1c4ad21d1b9b0f61a757fa036": ["yanyue.cn.", 0], + "cd07a3df1cfd8c736fd1c91b9b3dc83dbeacae99e2c9cfed05b431cc0636ff73": ["tankionline.com.", 0], + "cd0a8cf56218e4ff89ef864293f896dc02588f5fe48037ed46abd8d52f06c582": ["ui8.net.", 0], + "cd0e6636f676848e7264decc3f8e3a46c2c95906839d4be37c0a482cf363ca01": ["sabah.com.tr.", 0], + "cd0f60b11aeebe9bf74f66bc41d6a03e2ba191ae3b245c8f86e15e63d18e64cc": ["recipe-blog.jp.", 0], + "cd10d20ffc38fc48dd5f04da8cd7669b68d91fcff46460d2ae09c784d889d8ac": ["ch.", 1], + "cd1984b144233807c63bcac47a053408ef4eeadb17854077d8ba5942c6a2b846": ["qq.com.", 0], + "cd22884652950612fd9f90a09392da4776500d1562f8d5d65b71d2d63fff52a5": ["hostelworld.com.", 0], + "cd22f81860596c9243d15022d3dfc323fa6ebe77ab4ec1a691c0798b7b3d6086": ["delish.com.", 0], + "cd3127d0aed83e05d0c055fe60a88d3f39a96d0b24b2512e8cd109061eca9107": ["kasikornbankgroup.com.", 0], + "cd3e833f4ce93a588f42a84fa76a625e42199783850efcf6b53491e8ce580230": ["cas.cn.", 0], + "cd42c43b38eda6993759b15c4655cd3292fadb2152037eb92ce11d65e7b002d8": ["nirsoft.net.", 0], + "cd45d8f06fdd40d1ec831f27b0de05315de09a9094a892205c685a603bb8ecf0": ["zone-telechargement-albums.com.", 0], + "cd461fc7727478a9c69056d6af69dc8e3ad7b6e9d2fad690d8fa03aed089e56f": ["axisbank.co.in.", 0], + "cd52f3000217132936b2f8189ce7f3c7a8724fa1c0c3738879423c24c9184c39": ["inps.it.", 0], + "cd60533ad2ba265cdc4ec54dfe79fa721bb3a06126bbf1fb37538fb10d7f9b9b": ["foxtel.com.au.", 0], + "cd65b747555434ddddf51c7d72dcb4d6487170f9ce5bf7a1e9cdc4f822e6b1bb": ["ensonhaber.com.", 0], + "cd6bf54a74f9d6974b4ab0703c598151885e5f7792323e01f7220dc0866be859": ["sonos.com.", 0], + "cd6eabc83530c226c655cb021ea0d14f5e5675059796de05ac6cd7ea26bebaa5": ["host.", 1], + "cd71e6ef94b7df466891c735c843436a110a5a187c39160daf2a764b43586f62": ["natura.", 1], + "cd747d8c27718a79c14fe405d283e2a5d397857b7890d99ba1c54729afb12117": ["moj.go.jp.", 0], + "cd78aa0257fa73c84636b064432c83eb3d9c099b734658659aba20f8deca71d7": ["safeway.com.", 0], + "cd7de91cd5b02d53a1ac4ab20378c40e8c655f02591032deb268f7397cdfc968": ["netmarble.com.", 0], + "cd7eba2c7f66fcb5779ed7714b4b06afed48290cf30fa7a350c1302ef729e3ed": ["algerietelecom.dz.", 0], + "cd8a1555a94a0df4af2b63b3d350b249e87aa5754790ca322e3c6686fca8eb16": ["creaders.net.", 0], + "cd94fcc25d90c2b76dd7784e8787dfb2cf87dd87179d42a13416174025be0e9c": ["ooreka.fr.", 0], + "cd990f2a0fa535fbac968504e44d2b60ff11b1af3398773a19f2eef85ba7056e": ["tradeindia.com.", 0], + "cd9ca2bbd68b96c67cbd9707406f4abe10b242b39743cfa7e4a763747df19dbf": ["kindle.", 1], + "cd9f635e44440ee2dfc3abadb67590e485b6fecea366161842b1ce0c7f52d4d0": ["futura-sciences.com.", 0], + "cda05b2c530fc69a148c60d70bcc5ff69bdf642436ed5f0a9597dfb8607709c6": ["ybjk.com.", 0], + "cda1018cd9a883c38de0e2df33512581f968889a0795e1adeef9f511beeb76a5": ["knigavuhe.org.", 0], + "cda5fe94c4efb7415f15601ae4a0ce3abfead00ee18adc3c5eac2790f909a906": ["lge.com.", 0], + "cdae33e04c9a1aaa846c9d61f26193f6af1214708e06a36cf85fb160152d4ddd": ["gamerescape.com.", 0], + "cdb84c2cd88cdbf607a16f94ca2193573a8e09b43bc80985ee956ddbd4468093": ["pptcloud.ru.", 0], + "cdbd4cd5e5a5c6c817c78c027f09c42716aa55807c4586af5185c44c76e78574": ["transfermarkt.com.", 0], + "cdcaab5c4e1a32595271fe1aff1641f6f982182d7d876a4259235453d11e9f8d": ["codesandbox.io.", 0], + "cdd3f601d3b42c30ac436183aae4b336dba366640d4843d532a8c51b66a0d079": ["aicte-india.org.", 0], + "cdd719d6aec391dafe8e89f9015564c04828a2d0300deb2b7f7c06412d6894c5": ["calculatorsoup.com.", 0], + "cdd869c4ee4aa55de485832431a88faa17ba975c7247d83dada3167b07a3e8fe": ["celebsroulette.com.", 0], + "cddc830a533e3dfc2b9a07d2ed734718791feecf0a28ac03a0f6e475792f1644": ["neu.edu.cn.", 0], + "cde4789bd3ce7f6bd8026257b30e7f6630f282e29014319910f10cd6451bc730": ["webpussi.com.", 0], + "cde8ac10dada5d4db9f87b2a155cd86afbfcce656aa962ab11ee721ff17a0cbf": ["senecacollege.ca.", 0], + "cdf28af43851e5e471abd50e67a44ef08aea83aa79383969db077f897b3f7e5f": ["degruyter.com.", 0], + "cdfd2e17d3de43d95a48f2f1447bcb4fcf34abf27f7f557ad9c7832553c7513c": ["stcgroup.", 1], + "cdfd4fa40a84604ddfa1d286648ebfd2b936da3cd39ce5f351dff3869cfddd06": ["geoguessr.com.", 0], + "cdfe0be79e9870df311eb89ebe20d9b005318e7953a98902e394ed7bb2920b2f": ["tt.", 1], + "ce0533bbaf3ae1778fd9432a65ae01b97e939cf7d297abaca14aa6ce34a1f561": ["scu.edu.cn.", 0], + "ce07876e60bdeccf8036052983a17942a69ac4b11582ea5372bee843cd77043f": ["alfalfalfa.com.", 0], + "ce0d5a279e582c46ce0227ae74130ee7d6583c41679369a08f14505d6d48b226": ["crazyshit.com.", 0], + "ce131b332995928b40fc4fd09ed76d4ea8ea2c15104ddd2b6370f24fdeef2d8d": ["burusoku-vip.com.", 0], + "ce131e2bf9fed0009ca643b4e17d9046d3d794f23112e00a9646949134163a83": ["iliad.it.", 0], + "ce17572b1af3c55ed4caf83312b5d8d2947048e5902683ece101aeb21d4f9b6a": ["universityadmissions.se.", 0], + "ce236363ebd5eb06eb58672b0b16df29311c1045502205b921acdc29a2c9a286": ["dataoke.com.", 0], + "ce389082baf2a6f4612a57ab83d711c7c463a605334779ef838b63abfdb39658": ["filmow.com.", 0], + "ce3fdb8cd5d0e2f4357621ff0e85bcbe149633912d3b6d357f4530a412190bd6": ["helsenorge.no.", 0], + "ce4321ed567e69ad59413d46f5bd07f510b5845f8fc13ea59d7ffc3cd5b3950a": ["cybersport.ru.", 0], + "ce4bd5592045b2ae56d5be2f197e2d59f8bb0f488500799e9d7bf6a320f9511f": ["gesoten.com.", 0], + "ce52eb803258061df516f5f3c154f76307c65af0716e2768391585c6d5f4b444": ["moneyforward.com.", 0], + "ce563731ae82fd229205985a69e255b56cc283d5115c28f8787ddbde82268590": ["inpost.pl.", 0], + "ce60ff62db4a1260ffc6c3df11c225964c2e2467bf9a4d544a47b730112779a7": ["xkcd.com.", 0], + "ce64b08fb941ea6b1125f5b4800667bcd23c489ec6d8ba1527a8e5d2ff97db7f": ["paris.", 1], + "ce69481b08eb39cc127d09c5ec33a384352518c7cd1c03b4bd75b3c68269236b": ["foundation.", 1], + "ce6e5d7d2c7d0ab695102d6910685c324be9931475de496cd3480b7af8ffc8df": ["anjammidam.com.", 0], + "ce6e7c8d3971a7dab40d3edc716ca9187bed043d25ae33516bb17ddfac600e26": ["payumoney.com.", 0], + "ce7693b348f457ea685ba0acdb78156677aad3adca0af2f80075ab75c9958db3": ["suara.com.", 0], + "ce76b8dff9237fd3f000d5e3f13add0d5f6cb53b1434e5bbd4b7f91fe0a93969": ["bdo.com.ph.", 0], + "ce7f39119b001d6bff9b47b77ab833e18cc25438e24b8d5f0557b28e6c8a4c8a": ["thermofisher.com.", 0], + "ce83c506b265478f50f438cebd97d12ba21bb34336783c8f0a646dc971844c41": ["apprcn.com.", 0], + "ce8b0544bf5fdde743ea699df319ac244cc6c7bd8cf76c809e117f3215104088": ["forexfactory.com.", 0], + "cea0a846a509175008f506bcdc854a20b9a301949383c38084c84d57c568c8f6": ["hentai-foundry.com.", 0], + "cea550af712347a04c4c415778307587cac1f8e57996c774d0ef855a788106f2": ["rwe.", 1], + "ceaa6e4afd772868bf64e4e02a92f0c62b858d7277614504c828ec716257af1e": ["wmich.edu.", 0], + "ceb05f4e0cb97bcdf41c0b90d5b230cbb7d901cb6523cacb561c61add3e68e21": ["muscleandstrength.com.", 0], + "ceb9a40b27882aecb02b6e4b9c5db3a1cbe2970b3ef8d0d869cb4dc2b0b52d28": ["allfinanz.", 1], + "cebba16c299b216d8621d88728f02d17ffd045500c7171a38ef04e7ba1c02583": ["xn--pgbs0dh.", 1], + "cebd14458bf60de272c16d51a20b77fa858ff92999c8bce15107a65fee904205": ["shareasale.com.", 0], + "cec0aa950d750fbcb0fbd7a490a88beac461a1941369e638a3292c41818f31b9": ["doc88.com.", 0], + "cec63ae5acd0761de1462585363d95f6f830ccf2d980cb850314e3fc8db6ff92": ["linkhaitao.com.", 0], + "cec92f3b7f1296603cfd3a439ada369e168ec8adc983532b9a713b6bd3c659f2": ["menu.", 1], + "cec9fb9a34f31de6df71a03eb9a97b6ef805f5bc52f07ba0845e0182f4c5b98b": ["gtainside.com.", 0], + "cecaca84000eb8cc085c3ead4db6bccfcb340e10b1c629528eea6b50681fe0a0": ["csu.edu.cn.", 0], + "cecdb4bc88b9735eec34bb2a1c6461b74f11c25267044b40bec19ff3fc350656": ["parcoursup.fr.", 0], + "cee590e40df47ba8c82194cfe526717b0b54a3c136cdc0db2224365a89d1cda8": ["cht.com.tw.", 0], + "ceed9626c3bfa17073baa70d12a4530442ad23857a77dbd48c460a70f2c833d5": ["mainichi.jp.", 0], + "cefff4ba1c544299ff9218a3779f177521795315c8cce0f81dd08d5157c44f5c": ["songsterr.com.", 0], + "cf0d4e62c07e7afb68c4ac7c27bbf50f44270fd7439a8d6a1e372efc1768264e": ["metal-archives.com.", 0], + "cf1455e4414b2629bb6cc0c1ec2e6ccb9536dd4f2c00e7d763b18aa37f714c2d": ["misumi-ec.com.", 0], + "cf1b5d147d2e56e5b5941e2e6f3227b5d03006947fdf8818d0f5b59b51205998": ["nasa.gov.", 0], + "cf290cb8c648a6cfb941b8a18c3858cd23bfbaf4b7e7848bd0c46cbdad016d59": ["spdb.com.cn.", 0], + "cf2c150db33b065f99c02c09361ede7e4a34832d81644a02d4cbbb5ac73e4490": ["iwara.tv.", 0], + "cf3409808cba562eecaf899db498e35ac374ffd5c6e37483facbe49248d750e4": ["mi.com.", 0], + "cf34122dc03605d8c7ae48663ffca77408e8831254e26822e9d5ac61eda481bf": ["phoronix.com.", 0], + "cf35e437f0f6313807428f7bcb2f30de3780d83ff5ade82179574979c806609d": ["garanti.com.tr.", 0], + "cf3fea2dfeb95217f371ab91bb62208333e72fb6b3e9f3f9b42f983b7fc7c330": ["thepointsguy.com.", 0], + "cf4276108a557655405df7eccbd5cd9e4c58dcda36c9e969d7950cc2fa6f060d": ["thenude.com.", 0], + "cf4d98337ce5eacfb90f441c2e49fc7507d694c49bdbf469edf975fe3cfd1a4a": ["dhs.gov.", 0], + "cf524a78ed7783f6bedfdb4dcaaa09420cdde4b8e4e1388e9e96d5a1b9037868": ["cz.", 1], + "cf5f88e61e95cdbbb5374489745987ff8bfada4bec565d5c4080da2c5e24292f": ["studme.org.", 0], + "cf602f2eebba79c4e480f30457c5e5f0528627d3335505e8d541eda7f05be098": ["documentforce.com.", 0], + "cf648b1880bfa33a314cda363268c1525efe3e96b117fc6a0a9a7d873003081f": ["studypool.com.", 0], + "cf744e107fdb8918d0d38c2112a9215a7f1563ee98f43b874701a547d491eff0": ["2nn.jp.", 0], + "cf773a8b30b56f1b0dc28625e88917ef49e3eda25ea86eb311a743c4b120a26e": ["online-convert.com.", 0], + "cf7ca8b5f98cbd6067fb60e958cec8ae7f8e409f56850de6ef00be9c6efa5bea": ["marshalls.", 1], + "cf7e108449f9b23088c539a034a5e31642eea82469eb69b873a51b58f8d75565": ["prensa-latina.cu.", 0], + "cf7e8af79be1933326c641fa6b03af3635ea8b1ac668cb36a41d1b5794e8e730": ["vivaaerobus.com.", 0], + "cf8372bdb38fc4df3900cd3358310c9592cfacf9bedf70066f73f3a3f068ccb7": ["rocketaccount.com.", 0], + "cfa041c077ab2cd9ff733e32ce92baf2c6287330759e636a316ddc1a574c005b": ["cdac.in.", 0], + "cfa05231e863399e97f8bd735c8e76b39932efbba4bbcaa3d9b99e8f5447e488": ["mangaupdates.com.", 0], + "cfa8b071fbaa927c8f03b052f30e33a28d4e4c6d1a4e660624b26338e5e783d0": ["adultdeepfakes.com.", 0], + "cfaac864126385556d543d39a06face868d591b19c69a54282528ebb1c56aea7": ["islamqa.info.", 0], + "cfad9f8638ff96bfa8de313d32efa614c3fee1cb788ad1239074af144339c0d4": ["unbounce.com.", 0], + "cfaf15a1bc2d40763288676e785597938b3d7a9161c18f241b3a2c0be9afa2ae": ["newsmax.com.", 0], + "cfb5924a881c5d6488c53fc271a78063556c4389506bb4f88ea0b8fe7aa9627d": ["cumlouder.com.", 0], + "cfb8f9f60ded67e08e8a77529841bf1f89d6f230edd7afe20b99e464a3a41cb1": ["c-wss.com.", 0], + "cfcb1614ab969076fefcb178de703b2324ee485c0e1b98fad110d77594fd57b5": ["libero.it.", 0], + "cfd4eaed2fe4506ed0b3361ec9dcef6aed93a60e4820b2eb7b9b02452dfc1fe6": ["passportindia.gov.in.", 0], + "cfd5c721d40cfe7544129051735ae793692822183e92e92e624b7f388bc8e341": ["coccoc.com.", 0], + "cfdb0b22f9da048fb36ad86ff28440214e42566b59af0bbf86950ccc7ab12ee1": ["bbw-chan.nl.", 0], + "cfdf749d1235c69d3540377f7de77e8f9b3954467d8d153152c0c0d43c95787c": ["otpbankdirekt.hu.", 0], + "cfdffb5a27ff30167357493ecbf302617bb4c6d3fc4e15c733ed954e262777c9": ["me3d.com.au.", 0], + "cfe2fd868116142dfd09dcdf169c4a44631bc095b2a71af4acd91c0714838909": ["reduceimages.com.", 0], + "cfe4a8b8d099c1be132efd2ffcad389dec437d004b3239b264e50ae063b1a334": ["pajak.go.id.", 0], + "cfe65c2bf09a6bbc16dc2f61030b840cc483f32dc2223b923b3a25e05ff29276": ["al.", 1], + "cfedf58e038b637afb7f926bcc4348826d537d94a65798e576c8ee986ec67d6d": ["metasrc.com.", 0], + "cff4af78075de1a6d5381bb43c8af20e556b60c289cb440da275431206103a03": ["axs.com.", 0], + "cff6d7da106b335d477eb7d09ff7ee78e584f3034f0eeab4c00cb902dc0babd3": ["roll20.net.", 0], + "cffb99daccd8feb3f9469730febb8dbfa6d2f7449583cd5e8fcf46a400260ab0": ["bhaskar.com.", 0], + "d0069766a4f39d87bc5ae46e204698655c68c5bfe3cde940039d1570ceac11be": ["1377x.to.", 0], + "d007f62be3d2632229186242508cbecec7f7f513e556001e0f75d86c1691acdb": ["hmv.co.jp.", 0], + "d00b30f721f88813553dd526177e6874e81dee8c22e3135bb2fc411b171c9882": ["ebanksepah.ir.", 0], + "d00c9b32266ead1719fef18c85e650dbec796c52bf4a07a228c2e1e065da6d1c": ["musescore.com.", 0], + "d0102848695576f44ca44044ab47283dcc8c55b5202051277cac28f96967d781": ["dreamstime.com.", 0], + "d0112169741a3355752db2f486858290ccf7c8d1ab2df3350f6d105c8562919a": ["canstockphoto.com.", 0], + "d017322030f0dbf0b2437519a1c668d005a8156eb058230044011afbbee203f7": ["nld.com.vn.", 0], + "d019a0cc9fe380002501791d317d4bca2cf318b5bed77cd79462a9c0f5170bd9": ["theathletic.com.", 0], + "d01ad32f9d53c44b16274ca41773e68d9a04514a565cdc4f271a62708c3ee6d6": ["tjmaxx.", 1], + "d01d285fbb72b1f18e5aa9ccc5cf4e75284300e83607259f9cb0ea3cf8cb6127": ["amocrm.ru.", 0], + "d0254f5a0509da1a88757ec596e9bc243d963da10df4a0032b45d47566d1d66b": ["591.com.tw.", 0], + "d027597751541e62a949868f9bbc019ec40e021c23f69390ce860c333ca35a31": ["shiyanjia.com.", 0], + "d0289c15d97b713aef6a9381b9a76eff9f9bbed64923b2d42b2728e79c2ccdd8": ["gaypornhdfree.com.", 0], + "d03e88e092f1acc36e400538694d1e5b55513cb7080926dd10f2691b1c22f8b2": ["elsiglodetorreon.com.mx.", 0], + "d04d504a95d492404576c6fefa5827fb5954be63f23c1bb0accb60365f01ba41": ["lifehack.org.", 0], + "d05be9c43d79789a6683e24e9c3a9e0620cbbd69fe9d7be92af378f18b4b3998": ["casasbahia.com.br.", 0], + "d05d9f76ad070c3bdb5c8ce6b028377381e95a099e19f11d084ada1f0b3a8448": ["buyma.com.", 0], + "d060daa6262f346490eec2e0ff702cd069722318ac8ab00587427babaa7ae2ef": ["mahabhumi.gov.in.", 0], + "d0615543c9910ef10e07eacb2865df4936bdd53d13c0428dd0c89792008169ed": ["vfsglobal.com.", 0], + "d066b9588ab51865d68fc7811e132c9dd49b7d2ce1c55d7736ec96f76abb2ccf": ["ucsc.edu.", 0], + "d06c602607ad30bc566feee99ce81df2237731fd8e2511c0d2ec3674a7db6960": ["fashionnova.com.", 0], + "d06d2c5e4d81a5738317b4fe71105690289e795a6842f2a1364d42281b99f37e": ["microsoft.", 1], + "d06e0f04713d791142b5a016318f1ee7b7cefb585919eca8605f9df999979e29": ["dental.", 1], + "d070e92f0f4d0b4872894214ccf2162baa11a22bdad52e1fb3269a938b7c539e": ["otanew.jp.", 0], + "d071e1fbe040185bf57580c3426d8818315e907395a111a190a333f119a2a02e": ["pipaw.com.", 0], + "d073016e076e63d029b4f0dd0761ccceb088c784acb0a8579009cc57e41b5743": ["saorg.ir.", 0], + "d07564f393f575dc6154f1df1ad8e5b53b4d0d7e0317b56a4fb1dcac3dda5c48": ["weebly.com.", 0], + "d0860cb18d7fcee3d9c0a04e81a9952e94743c8ff498d6c69e2451e55363c1c6": ["schneider-electric.com.", 0], + "d086cfb3556b7a5014c2496217036d9937d3b1114192c530c44dc27f63c89c95": ["pianyuan.org.", 0], + "d08db05576f01794f5732643d347613b7609e887764e5507812afc82d75e78e2": ["putlockers.li.", 0], + "d0983d7202e5922f0a30bf3dee9b1239c8aa1f92844046a2435589d1922a9e90": ["doostihaa.com.", 0], + "d09b38cc2543ecd34e6e4109f363b84f5398044370a6fe14c65d731c2667565f": ["zennioptical.com.", 0], + "d0a1373ad4c8f36a3428025e48847661d66e1b4d9f0a800e7ebe4eb78289eeb7": ["film2movie.asia.", 0], + "d0a4acecd216aeb6e3f88dc8e0912391252049599e5b7888e2ae254e3030f95b": ["algoritmika.az.", 0], + "d0a8eb86b8d9413c39af1e408f95808b0ac4391a43aed4b6aa8c99869db55a4c": ["gofundme.com.", 0], + "d0b1d3c4639f404db0028b6ca6c307f63add885cfde5d995d9f0608d9d47a596": ["superlib.net.", 0], + "d0ba97ddad8be40eb5ff4dab6f1e28f98c2d414425b610c1f44eb3fac2902338": ["nat.gov.tw.", 0], + "d0bd7e95431300abf0d2a5a781a6fb3d83cb41cc13ff23fd9ea322af61e3d562": ["certmetrics.com.", 0], + "d0cb58e4f22e96de782386af7e6fcd433c23debc84130aba4785d91e1c0ae0e1": ["life.", 1], + "d0cbf39dc9bf8eb17be1fc85d7f8fe8d0fe1e1c0514672c59c90461b9f5b9993": ["nikkansports.com.", 0], + "d0d3d7ca86866c3cf8412a47ca685cf9c88bea1d6d62012028381cd1a736079f": ["nulled.to.", 0], + "d0d68b1e0fef80876072d31b2611894b86a1f6f4eac0ec7bbb086c1548c2cc66": ["instiz.net.", 0], + "d0dc898e187f698ec2feba08e1fe3a7d467cfbf45a7c3f1e698d20dc7158f642": ["wooribank.com.", 0], + "d0de5bf1acf220899f322271c334dd5840e42d21d6ebfbe7aa78f0446da7100c": ["xiachufang.com.", 0], + "d0dff8fc30800691557d00a2e36bd78612135b9ed08acca7dd8b2a66d39ca4c9": ["javmost.com.", 0], + "d0e5ab475f05ac72d94b72ed8047cf37730b22dc8167e503dbe99321e77f2059": ["realself.com.", 0], + "d0effae424d6eee7e7b3b07e5db2638dbe1d9e40fa0dc6e2754d168417d5f789": ["mba.", 1], + "d0f89ed039257d674396f6c907f87a0cf4a3ddee9bf6f6224635f09b6dd1f740": ["topsage.com.", 0], + "d0f9da560d0d133107107fd68d36c7a9a5de6bf0a9700062aa422f2f3a5511e6": ["management.", 1], + "d100a9d57d3ea678c4897b75ae75132a5de55cda8bf4139d43007e9789cb13db": ["crsky.com.", 0], + "d10aaa58dec8519b0c7d269addae5af241b0c2c1e01c1e0cdddfc46060534409": ["mk.", 1], + "d10b1e5d59910661639132f89f0f865f0c8d4c432e8b9bc040d6fd3abbd6ef5e": ["tv-tokyo.co.jp.", 0], + "d110771ebb38e2aa92eca517a338c044ff21de6c26497d3286f67089f05e6dee": ["keywordtool.io.", 0], + "d11e2021452d7fb113eee0060b36289b3e98585312388a59811a4aa28ce1d17f": ["hatenadiary.jp.", 0], + "d1249b39ce547d856caa3894b4e8369bcd2e5611d30f8ab8cc213cf2b08ef94b": ["campaign-archive.com.", 0], + "d126d1cf017169e6b31c76ecfbe47448fc7ae611bf0a8c7454e137034217ad70": ["4shared.com.", 0], + "d12741fbd035e6555c1015897e4184d19cfc65a6d5d7bb45772f9047bad13ae6": ["vision.", 1], + "d1301e1e54375fb8972b293814151183d7bcda4898cd03338d2b6b42ca127868": ["hatarako.net.", 0], + "d133df8d615fd5fed2fd478287e7bb978978bbfddc797d531abd6d12187c0e03": ["cnrencai.com.", 0], + "d1435450d43fff6770d30d4edd641ecd6a77c199ec87d86c4ea616d31c9f2c87": ["kaoyan.com.", 0], + "d1449c0c5a261eae2c4cd9f04eaac6070a5170b795d8656ec3a2e5d12f089ca5": ["barefoot.", 1], + "d151f3091216cc742b5b7f809ff78e9059c1a6a2773026e837741c704b526244": ["transactiondesk.com.", 0], + "d155bdd75aed526c4f78b46a7d66be91e3599982681eb5fa62eccff495f4223e": ["byted.org.", 0], + "d1570b2e696cd3d98636fffcb9a7218ed9760ce4b99825847eb84ced177b407e": ["washingtonpost.com.", 0], + "d15b366d0da7474497b8c2e1b355a839c68f8d3c1f55ac61ac0d5100e7cdf9dd": ["histats.com.", 0], + "d165e5f8dfc05be85d163cb60e53091f7f7238ed574151bc9a74332f43178371": ["xin.", 1], + "d1702cb30388372f11b816378d4a6e019c11cc7c6fd12c844e9bb9486219fb0f": ["zch-vip.com.", 0], + "d170c375a0d81aff606399f1de6f64841163a15163c06ba75d9e4358518e6231": ["ssdvd.net.", 0], + "d170cddfde3dcec3e72ec826122676ce31ea918b78efec22703a88a4dcb598fa": ["qpic.cn.", 0], + "d170fb4dcf4bf14ac0d1de131f87d682a3230072eb7d3a3d5ac4872f14960b65": ["ketab.ir.", 0], + "d175acb6722afdce398bb4425309250ba36608d3c42a880e3724e353ca644926": ["endesaclientes.com.", 0], + "d17a4ccd41388f9e12202cb80c6a47d7aa0c69e940286fd8147293069aca1fe0": ["freekaoyan.com.", 0], + "d17fb69e5c02be923ba6b1a2e611c982c29e4bae1ce75605ceda997e2499227e": ["penny-arcade.com.", 0], + "d1837c26857cd849f3982cefc20ad2fae1eb9a6f97fd9e845d1360440a2b6f7a": ["evenue.net.", 0], + "d184a817b642865f0c89dd9c5a7dccc130d36e0efca3f57c2df4023d1b96f11f": ["eromanga-time.com.", 0], + "d185a1baf4746bedbb9358ff769e2942c8ad489026bdd44933da0cea79fabaf6": ["livejapan.com.", 0], + "d1884ce6ddcd39d7ca71ebd3181b23a24fbaf51a12f9615fb74e85b2fe34ac50": ["xiaohongshu.com.", 0], + "d189dd8c40197cd43cf3df61ed0b4f06d53bd55b44412704dae4adcea9c0d045": ["mmorpg.com.", 0], + "d18d1e84eab1fd79d1e1859f9557323ca0ccca26b160609bb5148c00d0ce9439": ["razer.com.", 0], + "d1a676b6999616ca5ac289bd22c242b02c1e9f5cb85534cff3947e8eee2976fa": ["jvzoo.com.", 0], + "d1a8518a5c1fb9065f5832a3250b24df39cdd1d25a5206dc01c1dabddf061db4": ["metropoles.com.", 0], + "d1ad64f0bca106b1c950276b867b8bb846be0fd2664e7dec8f7df2811905c0e8": ["hmetro.com.my.", 0], + "d1ad6bda3db1fdacef0a8346f253734d9b0d280e7713f032b33d70c36ec5c0c3": ["cebupacificair.com.", 0], + "d1b1f6e09da275039ad65ba436130fe22ae612466a0e8a19fc1da578457fe230": ["thegay.com.", 0], + "d1b26be60e90feb854b6e6d0b7fcf86fbf7462cfd59116e9d2a75599b0623152": ["huntington.com.", 0], + "d1c0e3204862a0087ad5e34421d76f5f9ac117819eff309505f18013269360b1": ["einthusan.tv.", 0], + "d1c5fd640086239feffc1a847d26cafb279a28ad17280b93b3e1dfb0ca2ff77e": ["office365.com.", 0], + "d1c8be28e5671fdb59b540505300702b45252633ecc9e62a99a4db93030cc822": ["arte.", 1], + "d1ca153e9a5fc813e56de2fd6665c6c1107b9f9d7bdd46a6c4b1c29be6dd438f": ["supply.", 1], + "d1d3ecb68bbfe79a14952dd694050fa98dea4b856f9d7712120422db7c6e9e1a": ["pornlib.com.", 0], + "d1d71511b50769c744514d8ec6c9194f0d6f5e08709059544b57af3ddbfdd7a4": ["diplomatie.gouv.fr.", 0], + "d1ea8df1ad0329eff3b91b981830277171fbd2864ed91f531492b0241e6e01ac": ["gib.gov.tr.", 0], + "d1ede7b06f7f1a2b40a96617b3c3fd13a907420b31d54293e14fd84a6db7dd0a": ["toray.", 1], + "d1fa1c2ebcf89b7e136c13739f7c19778871aac874642ece01f3d2a6052dd71f": ["yucatan.com.mx.", 0], + "d20c455c498d8c2333254c560c4fb16f38ba6be530eeece3eaef26c014d09cc6": ["makuake.com.", 0], + "d2133ceea2b597fad9111f9082940e2526b4d1543796ef55dfa72dc2281ca61f": ["sarsefiling.co.za.", 0], + "d220049676bce9b20cc684fb2abc7903a861e0cf9cb167d5e84f728c78b7413c": ["bankleumi.co.il.", 0], + "d238596c114600fced9ed8d3e2da2c31269c73f9fd2ae474fe9bc927cac9f78d": ["turkiyeburslari.gov.tr.", 0], + "d23c45011646e551417b1e359d4669b27ed73d8578d1a2a3ebdb9fd3081363f6": ["ddproperty.com.", 0], + "d246cbae47fd4d4a62118f24594db1f437c27caa435c93c53718fa71e8b839bc": ["69shu.com.", 0], + "d248e6676908d17f1a0a234a92bb726cfa0085468becbfa7767b3fb7e2b8a7f8": ["portaltvto.com.", 0], + "d25139b4101d6897445770cec3bb39b5775c0d2573e8653ee29286708bd6e69c": ["quickbase.com.", 0], + "d2519b537a4c67c624dc61a260f6d861462e4ee00f8debf94db7c610c3cb0975": ["bolshoyvopros.ru.", 0], + "d258f17a6d0d5acb5e6047149b5027d93f155ad6d50101bbed5b40e5a8ac9684": ["southmoney.com.", 0], + "d25bd3f01e64bd13a058252e4be30a8ab97f2ee0f31bcde8e401d812c14e8691": ["donationalerts.com.", 0], + "d25ca2429e204f97435b5c5a22d1cb255c1af45fb42815533e79a705f271d9c2": ["kotaku.com.", 0], + "d272b799e7cb8d127680cb84ea976fb2d930e2adf28ee51460b70964125fe9d5": ["loteriasyapuestas.es.", 0], + "d277cb2d3eb886903672ba397905dedaeb2be6c9d1283b87712f6f202c604dde": ["bmo.com.", 0], + "d27846a41e147c20900faba5c43f07b5d86d99cb1ed259136f9bce99b985b88e": ["wp.pl.", 0], + "d279b55d67c36e9fd5f82846b098943e545446b22adc41fa2d590d52b6d870f4": ["mol.gov.sa.", 0], + "d27a3ff6ddc10a8d2ab3c6b9ce49aaa5b1285a574a47b6c119cdc7ba7420f719": ["huanleguang.com.", 0], + "d27fbeb5608bcd71122ee14a111a699c91a549a8f6e5e53e9bee3075b00f45ff": ["kaskus.co.id.", 0], + "d282ff51db9fe6648f57d78e3142714260f6570a7f16a5b14cd9291261af5c5f": ["israelhayom.co.il.", 0], + "d28490d89679aa96b91d139fe65c07c2377205e2cea5c170748b4bdafb79cf65": ["vecernji.hr.", 0], + "d2914f115253458b6204fc6a23e4bf9804252a6539059856e24c6f7b12a66d4c": ["rokomari.com.", 0], + "d294b3d8e04ca0ac43aacd3e246305f665abe5a132c88d6fc74c495611b845d5": ["hubpages.com.", 0], + "d29fd66352dcaf46333f592df9dcef94b4face2cb5e956fd3b7cfb29c93f3e21": ["xvideos5.com.", 0], + "d2af4575d096fdbcda2a21342d1ed0a968ad70569d117c8fd325e0c55dab764b": ["obi.", 1], + "d2ba477cd03ecb0a553ce1af49c0b97793f3849619bb2cfbd53206949ebfe73b": ["degree.", 1], + "d2c6a9ae7db6e138dd3e9740bdfe5c31c26444e6b4e3a599da46571a33c0d9c6": ["ingrammicro.com.", 0], + "d2ccb1f05bca5adfb24460d6b344768b7998dfbc9c9399fade82e3ba511c5928": ["10fastfingers.com.", 0], + "d2d29ff1fca449972f8fc31b8ec7408431124248e46c2387adbed25539d19882": ["sakidori.co.", 0], + "d2d71937a44e1f9f9055657264a9449055c2c6d8814195a9936d726d44a1a701": ["epochtimes.com.", 0], + "d2daa2ccbf69db063e5df188dca2ae04adc0ca782b5f6f71d8ea1a03e07546b1": ["sej.co.jp.", 0], + "d2e2af0b51526cf500c42f6e75bf090c034aec4fa98a1243590b11c05b87cc00": ["1001fonts.com.", 0], + "d2f4d032da82916cc092810b2e56a5377becfbf1e30451f58d1ac7ede76bd8cd": ["rightel.ir.", 0], + "d3093a1f637dcb4671ddd0d93ff47382858a8f251dec58661d20dbe321e34a8f": ["cp24.com.", 0], + "d3095cd604848518c4d4cecd16642e16e693d3fd9dc94f52f0ca73489013b2e1": ["work.", 1], + "d30b2929019ec7b4c86854657d66262c1eb2ea9e14b25dc0a68a079019d9773a": ["jcb.", 1], + "d30e18666cdec1c734c789a1e48a9cc3380d3d7ae04955c45546b565dbb935fc": ["yakkun.com.", 0], + "d3126fec2d5a1535a2cfab98b1c2098076e9c396fdfd8c0453a6471860faa8a9": ["hbs.edu.", 0], + "d3190660616c30132f5f84a104f2d0e442a29c3f8052452588f21e9e5b149976": ["walgreens.com.", 0], + "d31d70a2d3a4ca7c2ffbe0aa34fd24215aaba28220cb8b26c2174b664270169f": ["wyndhamhotels.com.", 0], + "d327eaff2dbab9bb27b1296867722d01f8e4c19d0257c0a2f9fd324cf18f7015": ["epub.pub.", 0], + "d33572080d71fea4784a8db057c2b5537e3612fac72a4c9e62aa290140b3820d": ["brave.com.", 2], + "d338f216fb13d95ac3e34d9d180e5b9de4b63375fa138812e434c123de136a91": ["ename.net.", 0], + "d33a41af5dd805d86328293922b40d5a27e683bf8cd5221446a0f7c8c2876f0c": ["nikon.", 1], + "d34d276335fbb56d4b0fdb756184148b16aa5bf5afa49619e89cf35c3477a39f": ["adp.com.", 0], + "d34fe1290698727f28d44b5b4ce0ee22382d6da3574eea1600b684da572ae1e8": ["lokmat.com.", 0], + "d35739dd6d01c734c85036d17eba26f96649e25c1cd9fa0c76430894febb772b": ["eudic.net.", 0], + "d35c27527b84e91f50afa6accaf94e83d907ac21ad97c9f90de18a3cc2236e47": ["gungho.jp.", 0], + "d364086e09ccb0f4058ae3346553787c8761430cfd2ee66020ae967eac65d502": ["otzovik.com.", 0], + "d366e5b9c81694e7704d288dd58a1b407a6f280848d78d8d9d0762a27b844132": ["analdin.xxx.", 0], + "d368469f840ccffd3c4f994b1d4bf6cc8b8ed85ee17948bbb84870284ae1b2ee": ["sport.", 1], + "d3692ae25fd44b2945d5fb749447de2f64995ecd71b4f7ff615992b284c4e8c1": ["chongbuluo.com.", 0], + "d36bfe59381b79a1410ea82e61534e328037b4a1a7b2b31030e59c2f989da93a": ["exposed.", 1], + "d373c4cee9d034bcc3a91c13a856769a20cdd59b1e638345aa94697406a93c04": ["netflix.", 1], + "d3747ca6c6598c72c11a01f48e7e8895041ffc6db8071ef368ea7732fc6b3bb9": ["digialm.com.", 0], + "d374e57358101d57deb9096cdfb782d3af6110a407ac974c01cfb62831e5d5e0": ["qservers.net.", 0], + "d379cec86751c0ae49a2235cd60673f36fe84d902c70143bfe39680646eeeb9c": ["svscomics.com.", 0], + "d3879c85cf2d4965400ddb2aab4a8d4f5ca60c85879015aa843f5380f6fa5a91": ["fitgirl-repacks.site.", 0], + "d38b514255983ffc4aba31055f5f4139ceb18a3159ffbc1ffe0d35127bc03fea": ["tt1069.com.", 0], + "d3920ee665995df6488f22103077f3267804c85dd5844871f6bb713639291338": ["hhs.gov.", 0], + "d392f10f70ed40fd285b04cb548553b78d93a8993416445434f5e7e2d98f0018": ["livecareer.com.", 0], + "d3970cd21d1b0e6c0bb293b4da0561dc4f6951c05f34f46728b09950b72b601b": ["duomai.com.", 0], + "d39a42d46560b3291aa2be3b43308659ad0e16562f7985a8e07043fbf273717b": ["webdunia.com.", 0], + "d3a2f48877bd3edb0240ac877ac6a74204d5accb227dde3db2dc58b62856db41": ["monstercockland.com.", 0], + "d3a78905b81d4c34822dc4a088fa88c6d264f4a4e9b1aed15942973d1b5ca2e3": ["scs.gov.cn.", 0], + "d3a9a5d9111d6ad54f009fdfd201c337c5e253be30a41b23c1b295ca303d4b3e": ["insee.fr.", 0], + "d3b203f8aa68c8a422a330e056e8bc3fed835fb34249c3f175de31e883a91141": ["ihg.com.", 0], + "d3bb3f117fc37df437512da49a8692b5249849acd9752b1e264d115256f170b0": ["xn--j1aef.", 1], + "d3c12cc951fa0907e640408ce56b0aaed7f080a6abced5f7a519ef3669da43c2": ["n26.com.", 0], + "d3c1b8b4c7b13ccf9c8909aac86d3cf3267570e4a2122b8d4d6c6a7dcf855b67": ["csgo.com.cn.", 0], + "d3c7b38776a1bb6e00caac303099823e8dd7f3d5c04d5ecec5b7a96143c587b0": ["idp.com.", 0], + "d3ccc7196226a96e9590727316a8e1342b9408306b49fe8910d26f837ee8639c": ["todayhumor.co.kr.", 0], + "d3cdf715e4bf6a9844d7bac238f154cdff08a4da01defb69a2744c0901e91fe9": ["ebay-kleinanzeigen.de.", 0], + "d3cefb40216a031b0b423002d5de10d7839eacf993bb5d63ac6224ac23a3ff20": ["paytm.in.", 0], + "d3dcf9008489253efdc2f80b7e6044e07ead09053a303e8625c6f2753f543325": ["lettuceclub.net.", 0], + "d3dffad258859ac629bef169bff6eba03f9e68ff25ae45577327e668faf29aad": ["rentals.", 1], + "d3e1e9de482bfe3668ed1148e225fe35239d7af8c34fbe94fee3d4cd1c3ca6b2": ["onlinebank.com.", 0], + "d3e47269353de9f9b279b8cb7df12ee4e0da5e599049e1a733cb04c093bf7a6d": ["livelib.ru.", 0], + "d3eee4e73407fef87f66fb5d8381f03af988bab0d907b24fdce07b678647ca2c": ["whentowork.com.", 0], + "d3f717fb947440e88dd45bd57a46410fae1fca05866268c8ff986d29166ab2b7": ["howstuffworks.com.", 0], + "d3f8403b0dd2ee0367c0b214f63963c06672504ec316194aa4d2e18ac53ce780": ["kraken.com.", 0], + "d3fafb7bc576d99aec88bbd75422743510f88a81cab6a80e679be64c25db02ab": ["retailmenot.com.", 0], + "d400c6d4f2fb26aac1c9b9ae41292f4ceeec9112ff1911c9c4abd2f53d65b3c5": ["creditonebank.com.", 0], + "d407052dabc1fed57e7dfca3696c3ed9d5cf0ebfb02927748e0daac015659c4d": ["redumbrella.", 1], + "d40f48177f512a3d615c61f22ed1b18f28818d70261ef2822d9d8b526350e66a": ["wustl.edu.", 0], + "d4101961e7954fd4e37e050551ba688e29e317234ef7ae60461fc89d00a916de": ["eki-net.com.", 0], + "d416e3172ef023dd4b9605a6e0c8c101aa0b616bf844b041cb986195b60bb698": ["leeds.ac.uk.", 0], + "d41d47325fd3ab990e57f0ab196231e5ac100f973056dbd1acad60adcbb79df0": ["teamsnap.com.", 0], + "d424d1cf9c0b1dfbc6ddd77d698597d77becdfcfcf2ad52b56825d6efa892e5b": ["ir.", 1], + "d4270b15547192d8ad8e20df46c8598e9c346d7afa042ba80c5fca2ded3520d4": ["licindia.in.", 0], + "d428e253b38b3546772e673483e053d8c81c3a0c4b9d4917d75781bce0dc0dcd": ["npmjs.com.", 0], + "d42d8c23136d7d1bdd7a3a9e1ae467aa7a6291ed8918d85b9016194db2605004": ["hqq.tv.", 0], + "d43022479d0a386c64d5ba3715ad133c731fe4a4fd38ffb51b5304ce332b329e": ["nrw.", 1], + "d431aca5207ffeab76bab106e9b0b6761cd305ec88ee23679de700ef9a5c302e": ["kissasian.li.", 0], + "d4328acb5726969fff2ed75f5d60e897e0932a598ec749b8c0be7e255f3e20b4": ["oldnavy.", 1], + "d443238d29af22add2e604fc0c9159fed2e7aee727b9b038ba8ae6fd4a3eaa1d": ["riafan.ru.", 0], + "d4490fc8ef9a8f6d9ba0e13997f8b2b99587ae9195929f2f3ea0ba70f22a558c": ["typingtest.com.", 0], + "d44de169cde6b3d9e994b9875719d1d41ac424618dfb1a82a44d449e115f1746": ["vcg.com.", 0], + "d450a3c3a9bc9ee08d51cbba2183aed8377722ea6f7a8ac7081def22c2912245": ["cbar.az.", 0], + "d4539a328559085e56d98c78e909a89e6c1236bbe362ef7f1418fc586c9c87d2": ["tribuneindia.com.", 0], + "d4569b99843d9b331e7a414cdbd5a1a8ccef0bde1ab4f03a835cc5f33b5d3da9": ["scribbr.com.", 0], + "d45ace9b228df65faf9464d8cb028681497a2f9ec1d957f0a4af0857eb1610c4": ["adsterra.com.", 0], + "d46017db52db9817b8bf0ecb706cb27032509e1debc4d433749ea5a3a3a33773": ["tonarinoyj.jp.", 0], + "d461c4c86baef4e5192224ad48a7578416d03ecdfe3b9963f4217da6bfd04271": ["lululemon.com.", 0], + "d46404bc24563b7924331cf0ce6863788d5c51be391c5bcf47749f43ff0656ce": ["thepioneerwoman.com.", 0], + "d46dd31fa02841cbbe6656745db83a8849ea1173c8388492059989586c31042a": ["mywape.com.", 0], + "d4785c0dbfd24a44feadebb73cd882f6460d45a4b248679a1185e8e079bf37b2": ["online-video-cutter.com.", 0], + "d47adeb35c41e7b692cf535380a85c1a08442444f3a0346a7253d9d94d3e2e32": ["aek365.org.", 0], + "d481528e10769d549439942c4b8f5efff0a85ff811bc02a2f9c77f25e133c253": ["openlibrary.org.", 0], + "d481b13e318554aa13ee7eafdfe45260bf57cde3001632cb83bb6b6da848495d": ["hse.ru.", 0], + "d4852c3c28cf9364c6d11a3b988470ab1469ebc5c99b9203bc58a69430c8175b": ["itsk.com.", 0], + "d495101942b815a4f13f5c45907b1bd3446238801ccdec4ca922bccb6702e2b6": ["cskaoyan.com.", 0], + "d4996f7a0b57db163ee1e77567907e3b90f7d66a1e1c2140902a6032cb9c48ea": ["uoguelph.ca.", 0], + "d49dd9e6314515b9998a9b3646006be598dbc976f12bcd9d903ea8863b17421d": ["nuvid.com.", 0], + "d49e789be0b3d13f8be9deb701466d85b29e5967902a3f37741a72454fa3b2d2": ["timeweb.com.", 0], + "d4a91c53e65acf0931ac7ad88dc61f1c828630f187c9e50fa765af7ebff59145": ["gry-online.pl.", 0], + "d4a96004f55a1f85aaaa7f76db10164ca26c7108043c921780233166334d1555": ["alicdn.com.", 0], + "d4ab976b698a3495e5f4d997f328f1a6ef7cff5f96faefae887f6ba03927d4dc": ["3ds.com.", 0], + "d4b4126db190899fb7746912d7e32463bc165faa7660a91e921b8d5391877740": ["ctfassets.net.", 0], + "d4b50a37612b04966be58a25f4ab4d98489c549bde79701b4a2370c137569e5f": ["dailydot.com.", 0], + "d4b811b5fe79f3cc3a1300cc95e401fbca602d40c0427f1bd11bf653e405f019": ["fox.", 1], + "d4bfa6bc750099bb3056383987914b3dd2ebf07d2445ee1c429ba4b398d6748f": ["usembassy.gov.", 0], + "d4c4112e5bb1cda7164a88d26ccaca9860276e180a20b175decfff0a58538f13": ["hardwarezone.com.sg.", 0], + "d4c51d1e7728ffa64687014ccdcc80453dbf00f5cf108a8ff7699e5959be540c": ["icy-veins.com.", 0], + "d4c87ea805eb866f44531b5f762e770107463f92f76ab1534067c9c3890ffeda": ["ck.", 1], + "d4d31a36f54ee5e6177059e77839fa958e25adabe3a76bee2da01e550144773f": ["goldprice.org.", 0], + "d4d4a8a68ce3a010d286f5858bc1cf88bf66047e1423363aa11e85078d90d49a": ["expressvpn.com.", 0], + "d4dfab1afeafe0ba3dcb9e6838b2a921680ead785d3ea2006f11cce716e633ec": ["sme.sk.", 0], + "d4e0593c6d20e9b719b5f1dab21e9850e116ae6e36356dab3c985096bbcf17dc": ["17173.com.", 0], + "d4f144f997c6a855e9a17b302d86a22c731b3c8fcda941a1abc235dfc29c293b": ["tatacliq.com.", 0], + "d4f174c8fbd3dc3169b5090ce2ed231dec38ff3f912e125685e23dc3294b065e": ["hentaifox.com.", 0], + "d4f5d804c721af5554a7d1e16ea9a250e819430307c9a2ad89f420eed3bc3032": ["desiremovies.asia.", 0], + "d4f9fb85f85bed6dca47a1fee5351aa43e892087794730aeada11f9178936523": ["zenodo.org.", 0], + "d50160e57d97cb49dd1736d367c28ea605d8f7d4c65677bab19c75f4a18bc8fc": ["hamad.qa.", 0], + "d5033437a488b63c510f0a09db86bf4ff8f22cfa521c30f6e7a0f8b595276dda": ["giving.", 1], + "d50999c6023cf9fb499c2ff0d37ca0dd4dacf4e87eac03fa7af16628326e96a8": ["influencermarketinghub.com.", 0], + "d50a34beb7bf7fabccca058513646c02d778b4f6a01f4fa0302047360cc90e0a": ["muzofond.fm.", 0], + "d51ec27a4add5f64fab033d876a7f67a00de098012ac570e910f244d1fde8ad0": ["yale.edu.", 0], + "d524463cad041498eedbcb7fe8161b71c658a54db0e12871a81ff4b410899760": ["cncn.com.", 0], + "d5293c1591f3175123eb31ed21d3c4c5d20d24cfb7faa792a83007b3ab4eed49": ["glassdoor.com.", 0], + "d52df0f667288cc18f8b0c28c9a7786ec4976437457f092e957b98e77fb584bf": ["shopgoodwill.com.", 0], + "d531ec9a6417759508e52446cbd5deb5911eb953565c517372b83d46e94f9e7b": ["propakistani.pk.", 0], + "d54664f686fa47973b8f5ba47651a1b228946f78430b3c13ca2e53f104e7ffe5": ["sta.sh.", 0], + "d5491629e12be0cefc1e38a30a7fec048f7bd2a8bb2198b6f8f8c36da0673edd": ["fareharbor.com.", 0], + "d549fa2895b495bc8e719563916124b33423576e04bf42c821a3e0034581824c": ["rakuten-bank.co.jp.", 0], + "d54c14847feca3f0bedfbfd69504f4a9ee6c79e33902f3cc4beb92d687c81adb": ["elitedaily.com.", 0], + "d552f4f75abba6fc58d78bc2e35d0c9b85d16065639724d216929411ff1220d2": ["bild.de.", 0], + "d558cd46c369c40b4c56bc7f0a7138bda0fbce7a3609686c4a35c1e7e5bfc443": ["dmm.co.jp.", 0], + "d55cc6e6cab746a9cbd6a4009138554dd5d2c6491724889b5059948f56883c7e": ["przelewy24.pl.", 0], + "d55d8b449260eb1699b81c5b572a515df6324b427d0e74be1e657dc25e6f7fd6": ["dds.", 1], + "d55e38d9c664f0e3e4216f84c87184d424cfb01c83b8460b0e8a92eb2521bc4b": ["mockups-design.com.", 0], + "d563a812c29fcccdce0d7a33766a3eb2b2f86f15ef10a99f4ef7df0249a023db": ["mdpi.com.", 0], + "d5655fc9b160aeab682a2cbccace83778fc9865c7dc2528aa1369e5d09da3e7f": ["lds.", 1], + "d56ae905ec9ad02b0b93d31cd0544ab1596e2a127f2f3c54afb47e2425e608be": ["itsfoss.com.", 0], + "d56d2f2b1f1bb1b3b9c53ac2ec896eea55683c6b460b7c36294d304d6e80c829": ["weathernews.jp.", 0], + "d576a3d20987c1c1acba0ec8ab7e740830f7d125d3a99f8acf5b2b3839fa0acb": ["oddschecker.com.", 0], + "d578fb8280f7be83e63ed1c1303d37ada950b1924dddf753985018751234dda1": ["imna.ir.", 0], + "d58634a2f4b0b7c1facf785e5cf1b112930c3ad0e1b00eba6a59626d5813f78f": ["allegro.pl.", 0], + "d587d25b6557e9bbaabd6cadf5f5b09432da5b4db1ec89aa8e4016cc79eba9e8": ["outbrain.com.", 0], + "d58ecf6cba82c9e0d3258ff86d62103ba87e562ee1381f45a0ca85b700edbdae": ["osu.edu.", 0], + "d593ca80668ff9941bbdce23a382f4482f7a0616dc70f2bc76fda528bbbc69b0": ["kodi.tv.", 0], + "d5981112f4ee10f4327ef64ee4a4f76ba54544f7ff62cd6eb307c62370ba513a": ["bgeneral.com.", 0], + "d59d097d8290cb89b734cc334d416c65d20a0fc3754d47c8c6e3c1f459363a6c": ["florist.", 1], + "d5a33978a1fabb60404bcc77760314aba8de36f3df22a877e4f66117b154bf0e": ["nedbank.co.za.", 0], + "d5ae4d81ef1bc142a9dda607059e88519887b498361fb503cccf04d1888666f6": ["freebitco.in.", 0], + "d5b171b8889a34766cc4d75ca2e3df2326e4e2a7b87521f63dee971f92c4810d": ["pantip.com.", 0], + "d5b192d9f587d64a661a781328b5b4f7b587c7df61adc5e9e2d5f7f152f5157e": ["2miners.com.", 0], + "d5bc85e5114295073788c0d0721602fd67248a472cd09ba15a50cdfc598c871a": ["ird.gov.hk.", 0], + "d5c6f5f2239e843fc46039bfa5d750a547cc69e33020e352ca4d8e51472c7f8c": ["rarbgtorrents.org.", 0], + "d5d0911767e555f8d04dee18e749cc6ac35fa0971919a205232a65bee80ee764": ["creativefabrica.com.", 0], + "d5d2ad6e2da656768b51bfc7fc390d0094c533891cbc661fffc8bff5b6eb101c": ["thisav.com.", 0], + "d5d7311f3c48e4f653cca537453f1fdadce67edd2c348fc30399c827d0601ec3": ["premiumtimesng.com.", 0], + "d5dbd654f5624a7a02ad6e02fd010c230c367b0f36b1314e7c1e2ac5753372a9": ["jnj.", 1], + "d5dc182f20f9f71edc0b113a787a8d70f495d9bd28bfe0274f603f231f3062fa": ["infinitecampus.org.", 0], + "d5e47a5c8b74c66e96658fe878aed45af7e7a61a0eae97e4041ba76c6e5170b4": ["rstyle.me.", 0], + "d5e6744170f211d7920cf0e0c4475aebb521de57d7c3a477330811108029c79d": ["petfinder.com.", 0], + "d5f104bd9038d4f3b0fab02afe44874b1e027ee2632d65b3321c588353ece49e": ["jjgirls.com.", 0], + "d5f259035a538332f42ded38a2dd60be6e4654aa011fa58663c5f20cd612484e": ["studylib.net.", 0], + "d5fbb80318d5fa800b475287c94965f98a113ee434ec0c1633bd6257d9d4eecd": ["91porn.com.", 0], + "d5fda74a1f75591208da701332ff662a4773132e599ad69f21390da185dba2a1": ["afisha.ru.", 0], + "d5ff41b5c786c1b1308a57fbc30c22d2e6934278fda5ef2eccfa40fd2db5ff98": ["dalong.net.", 0], + "d60a04493ffd997b44cf88e88e3b208596502ae340dfdc1bbc36f5f50f61fcec": ["shutterfly.com.", 0], + "d60f27c05b292fd13744ed5ffb8b4415859794d7f770a273fff5e9e21cd58b50": ["liveauctioneers.com.", 0], + "d6146d0f7d7551a381f7693facfc0fe9483939e1f7f18b48f541ef5f4724420f": ["gmbh.", 1], + "d616607d3e4ba96a74f323cffc5f20a3c78e7cab8ecbdbb03b13fa8ffc9bf644": ["cat.", 1], + "d6177e669fefe754a11f8bbec060b67708ec206e459f81be70f3bfa446995a93": ["icmarkets.com.", 0], + "d618c72ea2f610dd46429c3427b8673f3ece71db2c3f9a45eb595319a6e1330f": ["etsy.com.", 0], + "d61a00be44e409024c9437e73c608e0a57388e55b1ac78777f81a33595716270": ["20minutes.fr.", 0], + "d61aa64c7543f8c285caf43a42065f5de3bf65d5501b577351e3b3c5260e9ecf": ["videocardbenchmark.net.", 0], + "d61d123fff4685dfaa3329d5abc6d3de67614c30cfca9ca555e44023f30447d6": ["tcd.ie.", 0], + "d6376bfc33d299dfb39c035036623f5cdfe9145cb68faecc583b7ddd7e7ed286": ["elcats.ru.", 0], + "d63829ae614c99f0defcfc24fde3e1c860cee10ec2bba00b29c541f4272db476": ["daftporn.com.", 0], + "d63854c766b4a240106f379afd6c14c8f461933454e91a27577b393e6f1fe667": ["accaglobal.com.", 0], + "d640baeac00c91c35d4ccc5287f56a6b95a6cd6ce63ac36866afbcdae11c4170": ["umontreal.ca.", 0], + "d6458aad3b04621275658f4cae1e58027e8fd34faaff38da8664f7e4b92c3b8b": ["agoda.com.", 0], + "d64b21a52fdbcf7147aa2c605a0a4b7406335002cccc7bc57049b0ce8c188d0d": ["bananarepublic.", 1], + "d65067c0da645e0bbcacb8c77b86235d303e0f088654289df495383126579d50": ["kdslife.com.", 0], + "d65498342c133306f78241d5fc324e6d9801cd4245f3186d20e4cf391b569820": ["dmhy.org.", 0], + "d65736464e5cc5d62905e7726ac72bc3fb9fdd8837080f405aac3f03f6eb439f": ["queerty.com.", 0], + "d659c3b521a9fe81800626c6658d9dd5014d11fc1daeb6206c8260f407674c39": ["dcu.org.", 0], + "d66457bd0c47cc285bbf78b78dcf887cdbb070385e3b4abac8250acd9f958a4e": ["clicky.com.", 0], + "d66a79c53cccd808a2eccdd5346a910ff0d9891feaa4e6c88a7686c7fde190f2": ["att.com.", 0], + "d671cd8d314192203f336d2c4c18d772055f7d1d2b4f7793fd861980deba2f9a": ["robinhood.com.", 0], + "d6725ffb263679c207a87ca8d3b4b449ecf06f04f5c6361e7e7066a91b66b318": ["moneydj.com.", 0], + "d6775f21fbf1c679292d3543e7024e440e84995574ecf4b0cf4f040d2cebbe18": ["prod.", 1], + "d67cd314de38d5d944d23f3151539e65fa6c09a792dbb406c635138a52766400": ["kasikornbank.com.", 0], + "d67d2b530fecc4be5cf68a4696f78fe66f861ff072a6f740e612e0a74a0c6c3d": ["pc6.com.", 0], + "d67f7341c184526a3ea42d3d202973e3fe16dbb0d62bf751e6f3e4b9f83b871e": ["algumon.com.", 0], + "d680af01b8f3035c577c88ca67a906158987536e490ffbdc205fca224f9b04bd": ["databricks.com.", 0], + "d6874f7dae530f480776c69da6e4da3afdb159134c4acdaebc34623ceb7cf07c": ["zive.cz.", 0], + "d688c3ecab43289126398ab36f26f00d9d78066644f987b0e38bc7eb4bee08f9": ["carfax.com.", 0], + "d6917a82b2c1960358584352532ed890d5592feac539585fe9ffed73cb79869b": ["gst.gov.in.", 0], + "d69bb5bb6907bbbf87029ea2e6cb5763aa1fa5fc21d642eaf7abc3c9944516ca": ["trv.", 1], + "d69c417f75faee4173115373dcd61cf8fcc6d2dba1e281b431f781a93dff9ffb": ["csus.edu.", 0], + "d69f4d3453ce8b7835bfc40d927c893f5d77b222cc457a351b67e2954c84c276": ["bradsdeals.com.", 0], + "d6a16e2cbd798218898d94490fc6210f30a4eb2367d584c91833a08e17c2007b": ["thecable.ng.", 0], + "d6a6ddbd89d00d4f488c081907baf7fda76a29de222e70e2ea793ee487738bc9": ["uopeople.edu.", 0], + "d6aa1f293850061f9a5af4f6c940cc00b3000f543d9ad03f881dc745a18c86e5": ["alahli.com.", 0], + "d6bc55b0aa284b77bdd0a95aac9292eaccee20b6bad466b31e3d72c328499fd5": ["csgostash.com.", 0], + "d6c1cc4342b4b157326fbeb0cdd96ea95a8dd37fa302653b406355bbdb12366f": ["hpu.edu.cn.", 0], + "d6c3b807e29097be071dcc33154dac430087c433e15493d3d732b0924ef634d1": ["dapenti.com.", 0], + "d6cf1537c2be5c677b1315a7beebe49cbdc53932b1230917580d297ec1b91d7e": ["seedr.cc.", 0], + "d6e00f3141aeef3c0bd62f8ce104209049ed2145e6d2f26da995014ea311a604": ["gbatemp.net.", 0], + "d6e09c5d782b04fe9f3008d39a6db540135722b071146c9f5206005ef0988692": ["startech.com.bd.", 0], + "d6e8055ba0d85aca8fb3680167a3fe15739ae4d8b91fbf0015fefad9d6079ae2": ["cafe24.com.", 0], + "d706fa9091544d1cec7f241a2074abde72a6edf49068ab76c7bc249510a41d0b": ["tailorbrands.com.", 0], + "d709ee5044478124825d0d8392e1fa41c91cb48b8fb8b96c6c91e5c5bfee6cb0": ["gazt.gov.sa.", 0], + "d70a941236e90fbeec015098bc7fccebc793623397be4949ca9715801d442fd4": ["etmall.com.tw.", 0], + "d716e7a8ae199e20af63a8755672c0e5664fb5d032ba2caaec1fc4a4684624aa": ["dynasty-scans.com.", 0], + "d71d65927b9736f9fb5502e1f5549e539613ff10b3bae705849822d80f852203": ["iefimerida.gr.", 0], + "d71e2dfa784cbc695ebdc846273d3a5b02dbbd161e6611561f35358e7f003675": ["pennymacusa.com.", 0], + "d72111d5f6f12d21fc0d9bd2912c5ad75b531be1e15bc4c89d364a29ef7e2be8": ["mydirtyhobby.de.", 0], + "d7213cb4bc12d3bbe7c5dbbc71d65b7333d18bf30d46e715426466381708b7c0": ["tcl.com.", 0], + "d72f2492e4d32e62dcb10d439531584e46acf4eede93187a9160f669a1db7b8c": ["ohentai.org.", 0], + "d742271251825a0e368b6b9fdbd224eec58f8d0516a17c28c940b6427265d91c": ["gameranbu.jp.", 0], + "d747c1e707d53ac8c8282430cdd5852a372d4fc03207502edfbcb587482ac32d": ["hh010.com.", 0], + "d74e2b0211c88053692ccbf0d151d58eb45cb79a70498145c3c871edb77ff006": ["sqlservercentral.com.", 0], + "d7524129b142df01a5daf0215f123c5308ebcccccb0fcce9e49ebb1fb1716d09": ["bot.", 1], + "d757793738e5cfc4b935de6da8acffe11a9424d963577faa215c21b46d13c4ac": ["dida365.com.", 0], + "d765aa4516892837e7e47c6359d8d4a99e199edd49cf62e47854dece5c55dfbb": ["tepco.co.jp.", 0], + "d7691dd66e443afc6ac0e2a35de35fe572ddab5638263b68eb29b3193589c9c8": ["x-minus.club.", 0], + "d76deedd82e15246a3d800d1bb36cd97adc78f48f096eb7fdb113b0d41c0daf3": ["seznamzpravy.cz.", 0], + "d774040c9479cc0bbde0f417dc8d657c5a379051261d4cc8e22db13e5fc0c034": ["secondlife.com.", 0], + "d775d18e5145594432cfbfb8f88b70b8df1a2a86a3e0eade6e0fbaa9517c4da2": ["porncoven.com.", 0], + "d77a917f5ad28aff6d886b1744e676d6c4d5216cc9638979eb7c73899619ee14": ["gov.", 1], + "d77c21b77dd5e179b536fbfec82937cf33e15790c646d4d9ab696a6acc979976": ["qksrv.biz.", 0], + "d78d5fcdcb912caa0288725ca9dd8e8986cb6185bf8da8587dee7d248b2dd105": ["ahcdn.com.", 0], + "d7a3282c3a74db02dbb58216b71e95806f198b06f5630f9cb85049d4200f9bbe": ["midea.com.", 0], + "d7b43264dc413c938e09030c7e5d89f9769893b819234f6e447ef7f9c20a3b81": ["autodesk.com.", 0], + "d7b6a2dfb49ba0208f149f6d2fe369a8c210f7072e55f95df3304ce1fdda2164": ["draftkings.com.", 0], + "d7bb807d63c40286f56e714b9cb6d4e9d634b941a0d78731bd69ba41a4e29ae9": ["wikidata.org.", 0], + "d7cbc9760b45331da60ab18e4b8a3067d25d2a7b0629d523a3adedacdb24b880": ["ggpht.com.", 0], + "d7cf77e9135a19dce34f76af02eeadd6885f674a8abd6f9a0718899cbfae04e0": ["ycwb.com.", 0], + "d7dd5877214e7463d7552f1b90d75bce69fe394f2343a1a675eb36bf1b2d6e79": ["sherdog.com.", 0], + "d7e056ae1c413572d804abfb6609c5574e2a60d6c4dc383a4c0bd6fde701392f": ["jprime.jp.", 0], + "d7e29b26b3f865bc091852e8d4a95a8f2066becd4ce69ccacd3566950ae19015": ["airindia.in.", 0], + "d7e65ae006fb6d1e15f5492aab38db8565a8b2c91bfcedb3406aa580e3cfde36": ["wplay.co.", 0], + "d7ecf73de8244d2ee1ca0b0370d3ae47432731f9301deed22bca1a37b6ffbe60": ["rosminzdrav.ru.", 0], + "d7f382ef93d9f4ce62a50e372ec584f60744965e69eaff109852b0fe87f2f480": ["imei.info.", 0], + "d7fc9e05125ff4daf8ba5faf338e50efbd48001b15d8c095c48ea6168261ac68": ["downkr.com.", 0], + "d7ffbea52473985f98403f484c9564db0dbe4ffd1c48bc010ec9e84afda0c8cd": ["otomoto.pl.", 0], + "d80bc1b188b2fe963b8061676180373916f21a01badfd776ce2f0cce7276dc5d": ["bab.la.", 0], + "d80c8a764c64136462cc443a93bf0afbc782e331201b475a064ce8b3e26365dd": ["xn--ngbe9e0a.", 1], + "d80c9365e3cab251bbe51d71a596bd81b3b7bd52d6e0f153ee5f8dd872d151d4": ["aramex.com.", 0], + "d80f6756a02855605aca6387e4f2ea406cb810638c34c9ee89b8e0404de832b4": ["onlinegdb.com.", 0], + "d8115ecdd5bc72ed1b27690e9e9a088c507aff783a1b4bce589edac1479bed8b": ["kbhgames.com.", 0], + "d8137088d21c7c0d69107cd51d1c32440a57aa5c59f73ed7310522ea491000ac": ["horse.", 1], + "d814b22a06d348363d05f509f0f4184af56b14ead011c0a9e2d7be6fd80638e7": ["vozpopuli.com.", 0], + "d819771cf918a6461edb3f6ecabe373bdcdb04adc0a52fc807b088e4cd74e2ea": ["freesound.org.", 0], + "d81ca7e58ce6172547fd64a65f627210701c1fce073d6656f60c58243450adc6": ["cnpq.br.", 0], + "d81ccf02af2df8628b38a896e2fa91a0e4be644be69cd9465b3491b3f3209a5e": ["xn--h2brj9c8c.", 1], + "d824aef89598053be5d0ca89270b0f475fd865b7b41f1c7555cebf35c697cba4": ["videouroki.net.", 0], + "d837028d855b57b0f3ffc6380172e54783971cd6bc17a5fd35fc0b228d1a7fb8": ["dailytelegraph.com.au.", 0], + "d83a93bb79303955a632eaa092c2fabd948dac606edff6f7a60a103421097039": ["moegirl.org.cn.", 0], + "d83ca36e8a26d556c85efc05d15a47b9fd6cf31a28b376dd1e39ccd7f1ac79f1": ["privnote.com.", 0], + "d849ff3dad8afc4a37c75f5f344b0e102a7dbac54b0cfdd17af391d6a95c0096": ["blackfriday.", 1], + "d84b358fe728fd74b30b7542374f6a8773560a83a83196d1e7c79dcaaacc2e13": ["digitaling.com.", 0], + "d84fe1a2d8fa03a8a80fcde6bef44f830f893f20a5be5596ce3788a967e87b04": ["filmaffinity.com.", 0], + "d8508d241b6158ab54d749ad0ad531902acc8a48de2ec7ccf76263e2c3c2f361": ["dataquest.io.", 0], + "d850b829d38cb8cec7a04825d1460db1bf27ea0a4ff0f81c0f1ccdbe7e5a745d": ["pubmatic.com.", 0], + "d8528b123c25b07674fb195bcff2b198af1dd22a349f70db28eb3bab8cda750a": ["wallpapershome.com.", 0], + "d8534cc5e6a210ca1e011929415b86f89042789354d8995ed6629d68ab116a24": ["sambaporno.com.", 0], + "d853b880808b25533d9144e12e4331f9822c709be7c4fc88aa4729e6e18acd43": ["egyptair.com.", 0], + "d85a8e063842f7eb48f1961a3decc3f54a24b333b3a1e42afcab6c571f8bbd5f": ["ufret.jp.", 0], + "d85e9897aa77785c84130b4e816da0a2a5e4a1f8465977266ec173e13053721f": ["akc.org.", 0], + "d8655e4667eb77f4699ad8a6135837e907231d025b1caf351df5ea97a2394cdb": ["goog.", 1], + "d86bb018bb09cb149e271ac70661e9e7a5e217205ba3398bb43e32ad854fc32d": ["tvguide.com.", 0], + "d872331b39744d683424d7e5cd9b7c1ff18322f75994907d4d088f1a5f425f30": ["musicnotes.com.", 0], + "d8825b2fa0df2400fd1bfe8c61014f1c03b177560d65008178db06166466476f": ["reactjs.org.", 0], + "d8835f392019124d82f52573982970fe0b4068a28c49509fbd8bc9e0d055b672": ["imi.gov.my.", 0], + "d8845d01da21ccdf783a356097f28338e12aa37b502a67d0383f6008ad59faf8": ["alc.co.jp.", 0], + "d884de224d307254a4799c3ad3c8b96dfe97ddb4dd0a1231cb87daf3d185a9e6": ["pornmd.com.", 0], + "d88601fc9273776294f1d9794623723f783d07dd5341b0a7962741c745cbc100": ["yaytext.com.", 0], + "d8932c32f09782064376d2a104733e9a57b1d6bb18ecb4663eac640be14c7e1c": ["tokfm.pl.", 0], + "d89acbd9e701898fb2e511045b2297769913ae44e7c01450c2e3e683f89b4eaa": ["minkabu.jp.", 0], + "d89f05ea3ff207ae5efe9dabbf8293a267e52db3fe0112fa26fa6ec67442a5a3": ["museum.", 1], + "d8a06b123fc1240216e72e1584a823139706650ea15ed15675bfc679ba574035": ["surveymonkey.com.", 0], + "d8a4e584333133f7e195159286de6a29742df41b280a80a122cf9b3d014bf2c5": ["jdpay.com.", 0], + "d8b227d202553f2ab8ca3e240442351395ee2130d612d1864d929368deb4f792": ["mindtools.com.", 0], + "d8b250b165e48fadb68a140222376ffca8a5f50ea7ae02257bf74585a9a9f0f4": ["dubizzle.com.", 0], + "d8b6af36b19c131cc2b2102ce8329dcd1cdfdd0fe0bf8ac57ef7dbd53c4566f1": ["downza.cn.", 0], + "d8b86fa39420fb974e09726287695b3f83c9c00d03cfee53e86cd4a518e3c67e": ["twayair.com.", 0], + "d8bcd9938a4a9f5562f7ceecc977f83914880d4e0a315f7ab2a26ad1392fd7b0": ["bofa.", 1], + "d8cdb8e56d3ae0fa26934bfb15cf8a6df14b5a81d83577e47868e888804a76c5": ["taringa.net.", 0], + "d8d195b6a097db186afe91cdc42bcb9520c272a842cd8b9e3df7069e6b117233": ["zhihu.com.", 0], + "d8d7e22fff99a891340d746c118ee4d36b82d8115e63aa29040ae1475eafa4ab": ["repec.org.", 0], + "d8dbf1abc11145558606ea2ba07aa4de39de35ddd99a9e8daa67fffb5e3de9e8": ["4devs.com.br.", 0], + "d8dd8fcb71da5b155aa69bcf6362ce76772400367932a554d9e94682c7fba5c4": ["blibli.com.", 0], + "d8e16bacec11b65c53a8efe6150c2688e62e7e53c1357c7b9883eff746944d18": ["xn--qxa6a.", 1], + "d8e45808bb3ea107575b699307ce72aa5a6f677d43cf242c8a4b71e4e7390199": ["hankyung.com.", 0], + "d8e63344fa6035504505ce61cf7851d8ad3f4ed1f3ee6d884a3b5a1a5ceb7efe": ["quanxue.cn.", 0], + "d8e8d0c5b222e16fb068dbfcb84f33ba6e55f39f793c3cadff9f1208b1a27a52": ["pkulaw.com.", 0], + "d8f7f54ff3d628b8cda15493af8b96ee14d283fd77f39ffec643413f0f22ddc2": ["sbm.gov.in.", 0], + "d90f2198c65eb2ed27e655def0131fb52f97638db3d5a0055de81b09d658a35c": ["xihuan.", 1], + "d912c48da2003dc9999e8304f464ab88ea8d99f4f53d55258b77fc005cd890d0": ["swarovski.com.", 0], + "d918d56cff3ff6bb1530eaf7cafc07ae8a089a2956b5e8c8639147236f7690ca": ["incruit.com.", 0], + "d91bcc816b499a61061e4a734e07ecd4afdbfc4b58690a980f81319d5d67ef9b": ["kino-teatr.ru.", 0], + "d91bd2d0e8e1b9b5f5a02f01599f43f75b12ca1c991b2664e5b30376a0229617": ["rt.com.", 0], + "d92035b6c28491c667924f5cc33333d9d5dc5f4eb37e12b134fa389982fd83bf": ["fnbr.co.", 0], + "d92984828c1ab30197033d7895e757ca02f78480417ef8b5f069ffcc345067c2": ["qrcode-monkey.com.", 0], + "d92b61adfa477e0f3476bcc135cfc6fdd6a0c22d421f95a65b8055506071f071": ["bancocredicoop.coop.", 0], + "d942b48e2f392ef6c70d3277c6c56265f74283170a927958124ff66ff398877b": ["snapship.it.", 0], + "d9451679595c3cb79bf0ceee60d437922d6b685f2972eae3b9e475e24ec9d430": ["axa.", 1], + "d9453fa211e1844715d4052fa60775761b7f53ab2e20056b859cb3d3c5fcb760": ["fish.", 1], + "d947d297b6652233f10a5f2978ffbec0dbd2879ebc38d245e918f57fa98dc696": ["goethe.de.", 0], + "d9486c806f58494cfde41415a0659bada09d80c5fc27d245cc6a797b2981178d": ["jecontacte.com.", 0], + "d9498eb9c139b52d26f05d154a8a5122582b7d678fec8d5b2599bf609d5eb04f": ["ye.", 1], + "d950dd691654abc662ea12a8782c1e07d44d309fed70f58b2b998fc1cac6ae0d": ["997788.com.", 0], + "d95d9e7f0281aefd4e04d3bc1b54ae64e5e8e401f8359b59575c3ed4d92c3caa": ["psicologiaymente.com.", 0], + "d96d6784eebdf1913533cbb1607c94bfd3c195ff13e83fac6979ea454d576067": ["healthline.com.", 0], + "d96f561374725ca9e43a086b1c068097535ed83ba6bf00594904136ad46ee92c": ["qingdao.gov.cn.", 0], + "d9718a915400df510d3540b856038e7596f1ddac6eedaa6b0727c0dda65f492c": ["tetris.com.", 0], + "d982d79af564c8a07665b51e3e330c6807442e0d84cfe71d1bf0c3919977c79d": ["xn--t60b56a.", 1], + "d98358bc550864ad32da11f195faabacf0481862625c7d54bce6e93bf302663d": ["bs.", 1], + "d993f5de3c4b9955cbc419820e02bbf563063da721242d0016b4e35aa7cf9459": ["majikichi.com.", 0], + "d993fa7cd724ee6e0d9153790cb5c23ce45de6261e97289e0f166e816079f169": ["allocine.fr.", 0], + "d994144e616623cc45b12db9f7ddf34b88d5bd17ae9506ca70aa7cc831c1b194": ["rhymezone.com.", 0], + "d99d93855c16a2b28814b7ab6bf37fcdc541cc0887233c1adc9e7c4c3286acb9": ["onlinekhabar.com.", 0], + "d9aa420d730f5c70b70f1328c0d557effb2617a2c3d546a746bb120f873da5b6": ["pensador.com.", 0], + "d9ae3290c021530e485cf6c66b6ad51ac42a102373f14f0a9613c4abd7bb8f02": ["uflash.tv.", 0], + "d9af02af9707977f7dd3520c01770f95eb414b0b4cafc3dc6f5028518c277978": ["expreview.com.", 0], + "d9b488a6fafe7459708bfd590de2d01fa5facefc3b3640c9f8ff95e1bb4a2e0a": ["eobuwie.com.pl.", 0], + "d9b5a003db33cd141483d7c26806e7a3aa7c999b09dbcf714ab67de443f59b89": ["authentisign.com.", 0], + "d9b96bfd2e9b5d5bd7bc2a5426dd7847f461ec90f06727ce55b06d8d9f7ae6ff": ["thd.", 1], + "d9be21da75ae278142cc0013045d11059258935efb9fd1626fb69282ab629afa": ["apkpure.com.", 0], + "d9c5e293f2f890012f68f1f3091882423c4ded0f0df732878629b2892fe99fe0": ["greenend.org.uk.", 0], + "d9c69ce1050f72db33a4a51df834867559f5a8ccaaecbf3a018299128b4546ab": ["sakura.", 1], + "d9c9c3c6d86ebc4a2991b45a1d436ba01e7d9f043f2b79be0864888d9fc680d0": ["thaifriendly.com.", 0], + "d9ca589a916300bc0ee10f6c8f44465d3e7528d6a356736a017e741355ab479d": ["otohits.net.", 0], + "d9d5907576efb39e1a970adc51fdd765ed38eb264ee4ad34a2f188a177b56e7e": ["bwfbadminton.com.", 0], + "d9e5cae7580b05853c292fcf87ce018588d4e45e1009a329b181b421091cbc15": ["tinkoff.ru.", 0], + "d9f7b35d220484908638ae57def47151688153ba1d09f412e3b7b186cd397794": ["xn--wgbh1c.", 1], + "d9fb76979b0863daff44b2f6e6381d8178a855e6b2fa71869722aea88bddf2ec": ["bcb.gov.br.", 0], + "d9fba907ce6228f3dcc1d3c7d1aa52e9d5aed4f9c5ae6b314a59fe80e177d597": ["bihar.gov.in.", 0], + "d9fea7f3ec454458f4778984459394a554f91433bd6abcf3fe7e87419228e90c": ["ohchr.org.", 0], + "da08661fcc1beac9856827b9849220c731bb0f51f5e4b6d7fa03b16d0f0ce1c9": ["steamcommunity.com.", 0], + "da105a67fea7ee8fc5e969a222784692216fb1d9bb5040ac60cec900cf397a20": ["challonge.com.", 0], + "da14f6a464cccf323b6f1253788e9bd33ea65693ba28ae1576bd9c381b78643f": ["synology.com.", 0], + "da15df48d71fc26e60e8835a19216b6c208648cac6b13140351e8a3c8a7d62ad": ["amadeus.com.", 0], + "da1b01c54ee057f63a036ac33946fbeb633cecafc5cbb89b7045cb1d3a9b9040": ["navyfederal.org.", 0], + "da1ff7efff17b2d0817660e434425090418d489e8c5fd624f3c7475a2de372a9": ["teamtalk.com.", 0], + "da216f6521aa4dd40e4cba96bdcafcf36e281d09b44d23829b0fbae666961b06": ["uniqlo.com.", 0], + "da2543d927f18823df93e9aa1aa11016ae9f66c605dd6582897672e57a7207fe": ["room.", 1], + "da26fd741b90c010f472aa12bc2a804537fc3f9f7282c6f7bef39620770f63b1": ["xingzuo360.cn.", 0], + "da283ae328c54e455ac1f8cef57f2b1e30fcc2692fb863c28a8a15624463932e": ["monday.com.", 0], + "da3a0fc0e8e3f05c8b01537496da1ad449d765b366dee7dd589a410692d74a95": ["w3schools.com.", 0], + "da4100b6ca125810e110b3ced06952eaff9aa3c5f4df1fa8c57825e25ade27dd": ["matomelotte.com.", 0], + "da4e725c64cbf851715505eeb90f9d8b4edceae8cd7170283b9f0c9719bfecd9": ["furaffinity.net.", 0], + "da5ae8c4b9e378adf1508215296ce23cf891fbb698019ce3dbd258820f8cd383": ["cnpj.biz.", 0], + "da652c2fe3503ab98b71104f408adbec29b23b1e33ff87d9dd232834b352c903": ["acronis.com.", 0], + "da66a7dad8aa8f2b8e0c5cf156ce094d281c2bb1a8fada73d901a20b0f2e8da9": ["uol.", 1], + "da671bb59057bdca717c53c67064c5b4d79eb97a61419c86652cf9148a533dab": ["physio-pedia.com.", 0], + "da70d3bb069043e4c42d62535193c1db5e331e3f6dd2e920246bbf6d6bdfc8d5": ["languagetool.org.", 0], + "da7f106514b86daf73a3d70c646b7a4e6cf4dd528eab0817f21f540dc80655ea": ["doubanio.com.", 0], + "da7f793833a627dc3355853dc5d181950d9f3caf311fdb0f5a192a2ad68977f7": ["3xiazai.com.", 0], + "da8db27052b9fc3f8933eb274a6091790c27a37d5b472ec4218251ffb112d636": ["444.hu.", 0], + "da9bddb73512767adc8a8796798dedd53b6a8784236acb3a2bf8b585d3107396": ["apa.az.", 0], + "dab1033648fd68bff38e0cd6906cfede407e2b31be7b42ad895674e64acc3146": ["cm.", 1], + "dab29584c96fa327ad8760d2c362b12c069bf558f22adf01efc0a72bda30b5bc": ["reverbnation.com.", 0], + "dab7ca7bca331ec4a0cc0abdafef26b08002573bf28f1ea35a27d854a2f4810c": ["online-tech-tips.com.", 0], + "dabc82ad393a545b3d509fa0cf8416b269f2e580e065ee3488bb0082270bd399": ["189.cn.", 0], + "dacbe98a14031ee7e3981f35304f2fae99a2ff8eff016c1a4a6400560dd64fc4": ["soso.com.", 0], + "dad33335e63c4291772fa407ba0a4cacd6db998e1958152c5092b8d366f1506f": ["dnevnik.ru.", 0], + "dad3d0a512f816b9e28a64bca8e8311fbc55abc0f22af9abedab5a36eb5df312": ["pid.", 1], + "dad8496e9bb273476e7c9ebe7710a76b4ad1316d499ee4ebdb18181b503abbc7": ["ufl.edu.", 0], + "dade0ec1dd84db03d3e17f48c95f05a89002f89e07def2593df025a8debb607a": ["xtapes.to.", 0], + "dae03c5bb55eac3922bc0c4443345ee3c1193b3f665522217b82c8abb11d63cd": ["snopes.com.", 0], + "dae1402397617dc8cf39bd229834ad1f23150fd809135bb452108b91eb1ef738": ["payzippy.com.", 0], + "dae6a35d8f2707ccd732c0bb9186e8b896893ad3e5bfae1e12938f03bbf615ad": ["pathofexile.com.", 0], + "daeed6308874de11ec5ba896aff636aee60821b397f88164be3eae5cf6d276d8": ["dev.", 1], + "dafb6582db865576330703d3fe53dd9809b30a82cf858ba711c9ea99ba7dbcb6": ["japantoday.com.", 0], + "dafefe9069837243e814a608533b39fbe47a801ac2ae1bae2a0a25be6e7e663b": ["meiji.ac.jp.", 0], + "db00f50e4e553f289a2c1fda80717b6400d10ec2f944116aca494061aa3fc653": ["ranobelib.me.", 0], + "db02d0a9273e3529f0684595631ebe6869f8827cf6fdb918e52e867d414a116f": ["kupujemprodajem.com.", 0], + "db03dd40e23a3b6d2dc3082a189464d5c85307d62ef85036e154ce922d9a5455": ["aiweibk.com.", 0], + "db047a0bab6675de1daeed6ef23d73d9f0bf4df35b93d4bfdda73174c9c41fc2": ["dotesports.com.", 0], + "db09dc1116f13c1024f1edecf9fdcaefe3284ebb524e7fb810640178fb278f9b": ["epark.jp.", 0], + "db0e4cbce7dc022558893b0d624917afdf15894600856564fd35a6a273cf21af": ["alean.ru.", 0], + "db1507e1275f1f7f96195257e4745ecfdf9c76cb45edc72ec93f000df6051536": ["mizrahi-tefahot.co.il.", 0], + "db1536264ee8374c0b07407714b66b3f68a1f0a34cbd9a940f233cbc8755d7bb": ["btrl.ro.", 0], + "db1fed5fc0e3b7854d361f30518303cc2ae6d6912f11208736960fb09173a46c": ["csun.edu.", 0], + "db252abdd93db4ea63063196a3ff31c8dd1c5a7633e99a83d8414d61b6c629e7": ["godotengine.org.", 0], + "db2e819cf26603cd4ef653440a55ef2a09d149103c63bbb6a9ac613d12211c60": ["cuitonline.com.", 0], + "db3ef3429f9c43f35a6a642bc2dd0bca26e207f0da39672af485dee332c38346": ["cumhuriyet.com.tr.", 0], + "db4122dc555b46aef3a49564e79b0151de1d84bdec61ec62617781f4f609d468": ["snahp.eu.", 0], + "db4652d27126adc652bb902fedfc1b70da40fc83b88a1556764ddc2a7b1a0566": ["blog.", 1], + "db4c8c2dc93f6e17482420cfbbbef05ffaea1b8816b0167556d90f7b3d508ed7": ["sahamyab.com.", 0], + "db53fc7a0b162d6bd6c27017c3c3676b4013ef226d8b274fa9fb07f79d38a87e": ["indianrail.gov.in.", 0], + "db5ffdd6b0cc28d3d451d0f43f438be9b695e6e94e1b8abb5f80151013037d65": ["goibibo.com.", 0], + "db6c1d0f62d6bdb8e559fa48df53916b4012b1923ee2c5e9e5589773d29fc668": ["travelersinsurance.", 1], + "db6d9fc8db83d7036af3fb041c85f56b3655dd23e647bf361d7519b2fa7a9e29": ["zx123.cn.", 0], + "db7370290e9462f59e6aa1088c96a3801706852be9d9f8d7484ec0eb71bd0869": ["myob.com.", 0], + "db7a638843392a6ca3bf90c722b95364388517ac348c5bd455ef943126cc8685": ["shfe.com.cn.", 0], + "db7c4af6b7d1c9e2e2dea1912bd029190c841bb1438ab6140958e3827c00ec14": ["zaobao.com.", 0], + "db81ea273d418ccc13e241d508742916c3527229ca445309d3eeeb7607b82e13": ["aicai.com.", 0], + "db836e1f88392953b749b815050d896f49809c21fb08b19c6959a19d0f8049fe": ["band.", 1], + "db89f6ecc6e4aad99029c7e69affb8748a9b914d6db718c22855e7701a5c8c86": ["balenciaga.com.", 0], + "db8b2b3a611ce663eb73138b873c97dcb9ffe7dd09d751514a2899fad0765ca2": ["xlibo.com.", 0], + "db95a0bb6b776c7a8ea8b7d861f6d1dd3416c90f3a891a83959b257d80b93aae": ["piaohua.com.", 0], + "db97440473a971b4cb5c17249ecf482e8b1faaae3db53ae8ffbe882ae911f2d3": ["nvidia.com.", 0], + "db97f190df45d33afbcb5847ec80535612de81b1b0d155ce8da68778c85eca16": ["turbobit.net.", 0], + "db9e94a302ca88b3afb1985051d7cdc582193aac9c388fb1d911245b34256b31": ["jav321.com.", 0], + "dba7162073490537b090e9e8fd37f85ff55ddd30a410cf661fa36701344cbf1f": ["estrategiaconcursos.com.br.", 0], + "dba75485aeec1401b1b53021aeff86876114e3e5c30a629014be8db37eae84b8": ["tranny.one.", 0], + "dbab9cfae0e739d06ddae1af815b715f6a46926e88b34eafea3490ad00d66fb5": ["triviatoday.com.", 0], + "dbaca3cf23afb0b88aa4f160e989ec17dac884596e3f36ee1f634269d3327a76": ["motherlessmedia.com.", 0], + "dbae6cd7eada065e3a2653d776086d6c5b2b4c34391a894a7172b4bb4c7768fe": ["androidrepublic.org.", 0], + "dbbc54f416f9cc2989b604635d1e7dd8679d8ac31eb3226e29773b96528c814f": ["mercedes-benz.com.", 0], + "dbc0a4d0f329f6473f48d2b16b2da49a4821e3d275f160e847f4ef56a26f2a66": ["mypoints.com.", 0], + "dbca819724c7f53357524f65d59dafde0189be132c1f39d40376e7307280f9f6": ["surfline.com.", 0], + "dbcbb8001640557695d68445df1cf7581785afce47ada0f5aef98864176df0cf": ["turkanime.co.", 0], + "dbcc42e35c3ac76aec7f891fcbefc5e3942568a123e1d7229472f21b0140982d": ["xn--tckwe.", 1], + "dbd130588eaf6f99beb37bb1e960343592a617c6cc0149379baa173980cf16dd": ["jlcpcb.com.", 0], + "dbd61f86de0f62ed31553ee8e6b0e094e6f0f19ece3ed60e79e60d01274af569": ["vagas.com.br.", 0], + "dbd72a55af3127f972b09e3e1cb9075a4c745490325b8d09e8c4fc7bedc0f16c": ["centris.ca.", 0], + "dbe87f22f636b9697c52fcdc1e05e1e927ce8b9029b5083c4658d64fba37d5fb": ["pelisplus.lat.", 0], + "dbecf26cced7f9d2eba2cd96f57bdeabeca09fdea39dc628f6af67a064f8dc2a": ["yeniemlak.az.", 0], + "dbefa9aaa51dba51f6ad5c99ae10989eeaaf1e6f8ea1fbf0b1aef1cde896e09c": ["chunkbase.com.", 0], + "dbf38e9b31e6f73e5b0a775f03fdc2eb787e8fb1e26c6699c0e310e10ded25ad": ["brussels.", 1], + "dbff3f8c4cceaa4f21a1eaaaa716e76966562cac4b04a1b8bca7798450d37f1e": ["itsolutionstuff.com.", 0], + "dc0681152c7f055677003a08cbad0bc4cbd57db736bed20cd5acd5af6b6bb44b": ["gmarket.co.kr.", 0], + "dc0c033bc8f371975d6499dae3cdde212e478cb0ed6ff3c5909d5fbe84a83d57": ["tiki.org.", 0], + "dc0c71e96e3d0c4f9c2e7cd3b072a25e8dd20176e5237e7abd38eab12ec1996b": ["mtn.", 1], + "dc155dda630dcc6e0618ec73691fb2585005aa7275bb76d04f29ba56a2bb2da2": ["filmeserialeonline.org.", 0], + "dc1adf0dacf06685e713ee9ad793640221bbf7cabc47a9322383fd721bb1757e": ["ecnavi.jp.", 0], + "dc1d3d238aa4844dca6c87b15c8fc58e128952e8bb45f1d6e94c9102c07e05cd": ["brocku.ca.", 0], + "dc1f6fe459fa22944e09a19cd4d7c88bfb33eb1347d6075ebbf3255d13f50364": ["my0511.com.", 0], + "dc25b7e718d8d3336127933428f82ef4d0b79fbd6cadc5dd061a6865233ccf80": ["tr.", 1], + "dc2933934035be3d2c46160d85e5a3d3a93e02e104d5c23b0ce33df068f5f032": ["sexinsex.net.", 0], + "dc2ab9362a47eecb8436d9d9873d38250afc6c8821775d0701f9fc4cec8eff6d": ["flybondi.com.", 0], + "dc300a1ad8637c212f5e4312c731cd5c90b4444b8497e7cd32fe93f2e58c76f0": ["jira.com.", 0], + "dc333a56fac802654d2f690e191a83021312c7f784d37b1ac4b5be6b4a2d410d": ["bmwgroup.com.", 0], + "dc40975e808d9651b3a35cb8ff51461a42f19c54db2c071b3111a599c3f3a8e8": ["condos.", 1], + "dc40c9ddd0d7cae948c68cdcaf192443fbc36c69640f0ebf5ba24be223eb048e": ["qixin.com.", 0], + "dc428b0be123df71a73c6b78e1fc04a35a10bfcd5843781acb179fded33403f3": ["usatoday.com.", 0], + "dc44a8b3a731a912880ac770830e66b384600f0c65cdf57290b47d2a9bd9e816": ["orf.at.", 0], + "dc4cc0c496c79947fa82bc4bab4fb6f1d5b784c963a914e0ad34b09036f3eabf": ["elitebabes.com.", 0], + "dc4e567fe6631ca01274ddcba73fced191e33a90b6f5633fc1f1c82a1415f1d8": ["pccomponentes.com.", 0], + "dc5070327c950f087cb438207c5a25be30321c6fc2c784494fa1c553f9dd424d": ["do.", 1], + "dc5fcdcee1a1d1e66756b735c4bdfe93c1afb88aefbaa6d01f66df0ca3b79fd8": ["voetbalprimeur.nl.", 0], + "dc63acb2bfa6c595e14fa39e36bf018d198393a07cc420c1861eb25f33f75f8f": ["xn--vhquv.", 1], + "dc642604c392bd52520fa1b958629b1a50c8d0b6a4a9693734f0e9f642bf00f9": ["shopify.com.", 0], + "dc6af8dfef97d167f7c8bf53187e52fd6d3f9c87377f28503975ccf25c757dc1": ["kwai.com.", 0], + "dc8184a86ff0d11f711537ac7813b0c90f177c12240ae61e9cd859758590a469": ["fuskator.com.", 0], + "dc831d402c5b5dab22f9876a50124455ab97284b08b85af561c3a3b0e6aff19b": ["000webhost.com.", 0], + "dc8da711e2a0596cbf8534aa7b885e31e85c1867dc6281ab506586bd2feb7eff": ["arxiv.org.", 0], + "dc8eb534801d04e8ba0b927bb1a9972e57f4cc524cab97535c9fd7271a09197e": ["backerkit.com.", 0], + "dc911a34f4bfc1e8f22205b3f393fe95c5458a3a2dd8f01fe4a0bd3b548244a9": ["multiurok.ru.", 0], + "dc933c04cba24a90d2fe8af13cdc18d23c732b43e636faaed7730e5bc647eac2": ["zocdoc.com.", 0], + "dc94fec3112e666fb30e9bb8ad68986d6f164aa2778af55b45c616cf58738d4e": ["next-episode.net.", 0], + "dc958498d993214aa8872afe42f1069cb788f31feb244a2729b64ecb7c7a26ae": ["yoast.com.", 0], + "dc9d1212f4dc6e06f98a66f2f2bba8b1317abb8ff23d64dd97f692f50167c947": ["05sun.com.", 0], + "dca0c5e12f55cdb1bfb69eaeda2831cf221e1c950d4fc87d1482c03074918d13": ["141.ir.", 0], + "dca7d41ba813e4c50b673a856972bf0c8692f116910d718da2be7fda35d29768": ["hespress.com.", 0], + "dcab23af8e35635b8c82303fd80c18c2f985ee11982aeadbc073fe2b274cbb0f": ["dinotube.com.", 0], + "dcae7f9783df05a25242e5fbcd53ce5d304ef88b6df7878eb8bada1e3f758c7d": ["review.", 1], + "dcb154ae628b22261abc0bef70af35de28858fa6d12f48e5ce907ccfaf37ac22": ["semrush.com.", 0], + "dcb263fb5abb10449c77a45ef96b6ce720af0d8926a007437b967c39511102f1": ["sexemodel.com.", 0], + "dcbb13e076d09c43800670826b8e3b6953963189a2fe0e85015badd1fc01c979": ["advfn.com.", 0], + "dcc4b7917b910273f7f579ff1a970879cbb91064b7a985d54e2f136656b06aaf": ["thehindu.com.", 0], + "dcc60b2e826e8c4b0abdd11aa74a7f2630ec73bc85818b6b4351b3eba12885a2": ["wordreference.com.", 0], + "dcd08e3eb7ad77d47e3068172a275a3d879763988b68a23811c1b6d89a980355": ["autoblog.com.", 0], + "dcd12bec6ae623523d85b0723e0db51606a52bc75f00f51ccc6145bf57944642": ["eblik.pl.", 0], + "dcd324f04d3ff427a4b51959bdb9b9e0a11ab5cbbab649208fb3c097f5c2b25f": ["boardgamearena.com.", 0], + "dcd36b0a6d4827ba3652540766d6bdf527c27d7cae53be4391cc4587d54534ce": ["autozone.com.", 0], + "dcd55effcf5b5eae684b964ce6d4768e2906d3655bda432f4630e903e46a132d": ["series9.la.", 0], + "dcd909e0185920565bdc1646fb2cea08519bd250911cdc1bea8c08a7ffaa93fa": ["fishki.net.", 0], + "dce7fbf4bacd7c40424ecbfb37daf83b23d4fae024a2b3d787eee302933620f3": ["cztv.com.", 0], + "dced245469fe301914b2b9a97bd3a40a5c03848759485cb7dfeb82c8aebe1a19": ["yesbank.in.", 0], + "dcf6e72409868b363bb1a960fa4d94dc238121bae03ebafe01cc0e598bb89c2d": ["tickets.", 1], + "dcf7c6b712ce34235a0b5b56a67fe6700311c3ee7ff60d60c70d17054baeb87d": ["duote.com.", 0], + "dcfaae3eaaa1e1633be6bab69845912f63200fe23301be98d3ab16a271e012d3": ["film.", 1], + "dd01d490e0a1a6553c8bac70747357c2379ebcbeecfb44deaa2ab94b80f69345": ["cainiao.com.", 0], + "dd059b0250c2c1255f227c79f18cbf36fb797e74e71eaa5e51990576a1bcc781": ["jwpub.org.", 0], + "dd09a4bdcbb7ff2c2c16f1021a090b1732f23a0eb6b36f681f5c229128f4e448": ["hanacard.co.kr.", 0], + "dd0ecb0d42fa7158d99f291e90214b3985e957b561fe311df26d4c3049e51b7e": ["pr-cy.ru.", 0], + "dd142c0fd41337157a0d541ab4d628ce89126670028f8b238f9c7972f567b723": ["hostiran.net.", 0], + "dd22d30f4dce90bce3b236807674f18475dd3215803f19575c8ad6a0114f8a4f": ["luc.edu.", 0], + "dd2d2347663a16cc6a929b9dc40f34e1cef4a72762bb3e31dff9eca43f59b975": ["safety.", 1], + "dd38872d7a1414e20e22b7b222d73173b5939590180fc8e707ac4afb8e857a5a": ["westjet.com.", 0], + "dd4125f4fca04a67c4dd169a5f307ce3b13b2d01ea748ab6c633c53e3a8324e7": ["nationalrail.co.uk.", 0], + "dd41a296285e834c90fadc6674e40ab8e90b129bdce663163b09b2c5c6279d7e": ["namava.ir.", 0], + "dd42467964337a15fa69428a702b90ae83692712d88e662a8a20735cd48c0a3f": ["costcotravel.com.", 0], + "dd437c1650c7a98a4ba48c9df28273689e6d8db54073f7efec599c6acc8c1c7c": ["mbusa.com.", 0], + "dd453095b7727ac4acb5289e596c95589d896c61b67f4d11278b6c354d312d0c": ["krd.", 1], + "dd4817215abb7fd3d33a4fb4d2031e87e9f0f958b2f58c0e6a0e5ec77ea78f8d": ["pdf2jpg.net.", 0], + "dd4dd0bc1861853addd92496e38bd2a877cb29bbec2396ffdf5bc85453b4456f": ["schoolsoft.se.", 0], + "dd56e66ed09a049b0eb33b8c807742a7188b6084cb43f8d9351ae1b56df1d9d4": ["ubisoft.com.", 0], + "dd616deba0a25d80b86b54e8922a073ee8833c199d216036fec81184597d3f52": ["c-sharpcorner.com.", 0], + "dd62906e054b9ebee8d0ed027a78bbf4b0bf724caadf54e164cab712ab240fbc": ["hypebeast.com.", 0], + "dd6440d7a0b9211df208e7025acd48279a289bd6448358254acc7c5fd51be953": ["boutique.", 1], + "dd67d964ef45b49cd8899baa0d3ab2653ca9aaee4d684bb161852c0cef8ca43b": ["cccmypath.org.", 0], + "dd6d8afc5a44aa900e28c7f0b67f106477c92fe0c2d8816175a0f2d7ad7871ba": ["adkami.com.", 0], + "dd7b8922f9bfd6b9298f8be5dd1fde4d9cb8431f02487865ea6c6620396b0984": ["sdamgia.ru.", 0], + "dd7dcf535be3b9c6a3c4f5d05a2561d3f6ebe40e6ee9c2147e35ab3357dde291": ["zedge.net.", 0], + "dd818f141ebd1d6fc3a63446d5f171b16fe75fa99ad1bbead9328dc4b7f5f263": ["vokrug.tv.", 0], + "dd99c8a1404f89969bf6d37d768beac38c13aabd7b2802b18da7858c8263e532": ["bloggerspassion.com.", 0], + "dd9c2027b2ce24143fadb4c9c3c7af80cc8fdf69a953d958b98292fbde7284dd": ["caniuse.com.", 0], + "dd9dec4441f5424164df133e45ce8bc60b16354c572787748ba60384ea8b60fc": ["infosecinstitute.com.", 0], + "dd9f735b608bf18dec1f53b07c8dc9955045ea2dfd5a7b8d9fe383b9612cfbf4": ["bicentenariobu.com.ve.", 0], + "dda0e7e28ca4fc6d433013be56e487986d673b3bcd5795bfd7f44cd6798aba7e": ["xn--mgbai9azgqp6j.", 1], + "dda324261d7f0cecb7e38fb0e35322722eeefac0c9b0d855838c72e551a36d3e": ["flvto.biz.", 0], + "dda7f54e1b25277a42a08277a80f56fac3ae771b659d2df2a0f9683e3adf3bfa": ["career.", 1], + "ddb2f50c4c5196ea10fc8d4d937ca35e6ee8112e44d6aab36c5cf418b5ee626e": ["cninfo.com.cn.", 0], + "ddb7fffaa23dd67e77806a60da3b90af1a24106ab54141a425bcb3492cef54b5": ["christianbook.com.", 0], + "ddba97af65c2bd940dced7d72765047ea0b44eebe23669180001ea73f41748ee": ["xn--1ck2e1b.", 1], + "ddbbd40307286eac1b9222eb39a466df1b0931dde0f13fe7a3d2e2547c9bc5f4": ["realty.", 1], + "ddeb86d6f303baf01dde6b1009e61a99b180df89b2bedf7c218dbde87794df51": ["xozilla.com.", 0], + "ddec892cc26055630c455306a55c1cfe538c6e672684a301d66f2b5690db2e70": ["ameritrade.com.", 0], + "ddef54a2dae6769342a37cbc7f95f0c8de83afb16136c78a362a0c170f932d8d": ["webnode.com.", 0], + "ddf564c97b1e1bd8656bf457bf823735c35c9b8931c4280d5e180e445aa1dd2a": ["etherscan.io.", 0], + "ddfe9aa48ca77a2e36e39f587dcc09c0ca0bc6d91b8e01e0a725d55d86132d1a": ["modao.cc.", 0], + "de032e6cb5244558e1eff599aa752c002388397b766c0fe3c1913b1d6f84ea43": ["sbiepay.sbi.", 0], + "de053f91331e9ad28bd90ecfce17c15bbe5fe45e257db22a8898def2ebb94d4f": ["mindfactory.de.", 0], + "de07de584bad07ba6ffa620040946e9ce0f264ebf92dd1c6bf273fad2d369902": ["za.", 1], + "de141320a3083c1bf677311711203a9a68cb1331664d44df7058c7299cfa4e34": ["procyclingstats.com.", 0], + "de1ae032172f72d7d5046dd4ba1ae3e6e7bcd2f5fb4e89a5362be7578652e80c": ["al-ain.com.", 0], + "de3833460954761dd5dfc6c2ff7edf51a966daea71bcc03d326b80741112cb42": ["services.", 1], + "de38b5a293103b521b4d94790be6bd5041ebf9187804026b6848cd555e5ad444": ["usc.edu.", 0], + "de38dffb47870e75ccea0f404c7bdc02dd477b2dad5a6f5f5b02bd00bb3c5bee": ["hottopic.com.", 0], + "de3e38571fd87bf5616d8a1973b6e089505a87238770b86b94fe164501490ed6": ["xn--node.", 1], + "de3ea4dd3e88ceff5100f92059f6e97b9e6ba6b999b378f8688cbcdcd66b9b39": ["realsound.jp.", 0], + "de4187d259a5156a60ff7616f4bf51394a6ae80ddb4656ac2f888518e8f39f39": ["xn--fct429k.", 1], + "de4cd481c2ff1e51a2a94a7053ad18bb036e6dd85ebf8aa02b68b0a2261b4462": ["jumpmatome2ch.biz.", 0], + "de4f7cca739fb85d981c5626aef544130e8fad31cf75a286e862219935c66246": ["17track.net.", 0], + "de5a0c5232fef6743046b7c9c6b5094ee93c7e7c9ee76039ddc817a946381fa6": ["cuantarazon.com.", 0], + "de5d707b296553e7ea3b248e69a7c7ace6fadf73fb2972045d8020791c537f6d": ["skillbox.ru.", 0], + "de61a886fc6acd53b72ad998df0288c31405df220f00eb00235e8023ac2bf53c": ["dhnet.be.", 0], + "de69f1dd94713d1dee9d91fe036d2a300d663783e16428c7937f5643d7c49131": ["3djuegos.com.", 0], + "de6e26de46817c7609de2a6681105ec6245f810c5d3e6fd4d7e51abce862df29": ["coinpayments.net.", 0], + "de7ab637d69ee137f621e738469a738b7589713e324b0ce78b5d496b44290a88": ["lifewire.com.", 0], + "de7ad5a6a60439587f245a68906ced8bb4485743f6c04459f7b0f58d833e2997": ["archi.", 1], + "de84ace589526448953eda680fd47cf46daa5f55343206918c6a15a33b857e03": ["walmart.", 1], + "de87fcd912d43c486582f516e7c186f24f2b94dc2ba58f01217625db9f5d70c2": ["pagalworld.com.se.", 0], + "de88461a284fa6e2754e148b5a6d26754b12ee9adc3ecbfaf920515b671a79fe": ["tsinghua.edu.cn.", 0], + "de8fc526a79055b436fe3fb2a156d762573a62a5b8e7e22c087defdfde01ff09": ["ucsb.edu.", 0], + "de9a6c2fd92ad34da6d92a521c5d918b8607ced73136a7fb70dee4540c14fb48": ["gushiwen.cn.", 0], + "de9aeeff65bb93c857b8d277222536d52ee48cd5fb344e523168cd50acbe4fcd": ["ninjavan.co.", 0], + "dea0efd722afcfe1b49d703d5bc8b644f00c8d9ab6c1ca7a1ddb42e61167fad3": ["1xbet.com.", 0], + "dea1952d8e632298e5784aee39726a0beff5d350bcda827aa5caee12caba0963": ["ultimaker.com.", 0], + "dea4dfdc1282abd0d3afadf6975458d917c5ebda4fc7e0bb91a51425cb38da7f": ["naughtyamerica.com.", 0], + "deab00ddd93207213d511d1980ab96d74b2885f4feb4067a4077c47c881b7748": ["marksandspencer.com.", 0], + "deadc17287a15514c97b04c44ece015784f98c9a76809e8069d7a1bfc79ae6e3": ["mairuan.com.", 0], + "debf98d8cfaf59dcc4665d67c03eb49b5819f4ba479e285223a782e0d73da182": ["leafly.com.", 0], + "dec2fbb9d70afd46480d5a383657a7ab8822b287c4286aa33ccc5924de3e47fa": ["ooo.", 1], + "dec55af2f4c5ffc68cfa27d15c72a9dc4391097acebe88f75ca14d8b40324c9a": ["yemeksepeti.com.", 0], + "dec98121c764ffb9fca6cf3ebe7fa9e4b426c998f6b085e3c5632fd9df722e28": ["uqu.edu.sa.", 0], + "decbea050f9c50d2af2699fb270b8b93e13d83b7fd74ad357e8e2dec7385324d": ["doi.org.", 0], + "ded3a52ef60f6166da2e99af49ecf92df12a0a8ac53c448a12b455890441f1cf": ["jutarnji.hr.", 0], + "dedeb4abb5d6d6598aee57e13999d0fa3c5160b60e3d33c637743df39d25a740": ["mercadopago.com.br.", 0], + "dedefa326c7d72c00d081ee7ecd8303bdff359216b70d1de489643c9a39b1372": ["chouti.com.", 0], + "dee2f0fd01b49d6f136fabb273fa57d948f1e1acb3a80aae58417090f7c0ccb4": ["wepe.com.cn.", 0], + "dee3fbf9c8f41a34810ee4b17433a00d9bc1ab613bf64a686113d0784e835d6d": ["mizuhobank.co.jp.", 0], + "dee69c3ba4607143785cf7907f5e2c7f35adfca01cdd299b42fe8b779d29a602": ["vietjack.com.", 0], + "deef103b4d9745f2e5c78aee04f386fd95174cf67d10a54522539fd04816b31e": ["jobhero.com.", 0], + "def174905a6bb6a0b18b6d13e77c35abc891ee77bce99d629baad7ecbb5fe438": ["allover30.com.", 0], + "def62d69d8151beca46085b6f6641722c4096501f5fc23bf42df43f807d75dce": ["abcnyheter.no.", 0], + "df03cda898b2a335b67016a7e3d92641b4c510a36fb3acc3e8c69faa4d4c793e": ["myway.com.", 0], + "df03d5bdb4a735db1736c69c66a4175a7cb897b0b132e12029349f3a70fc1b3e": ["healthgrades.com.", 0], + "df06e203ab6a81c1f69e991191d26003a5273c8b0f80f892a67f8184652257f3": ["wsj.com.", 0], + "df0cac7af2657453ef9f9be0c6895ba8d072d166dfe19c48aff1c3649f012850": ["pangzitv.com.", 0], + "df12d79eec6076f7f23ec0c44599940d272e382cd58b3de88929c3a53f119d44": ["gazeteduvar.com.tr.", 0], + "df16b7adf630241d3c003db804b46b1c3bcb54e766576d00bfd0a753e3efa3cb": ["voz.vn.", 0], + "df177ebf81c545776030508e0fb9e90994833d0b54f0381f2774311ec8ed3702": ["onecall2ch.com.", 0], + "df1890a38a6c86d4008ab6ba792b13c71f2f7e0523056d572f423663800ed15a": ["voting.", 1], + "df20285a608ffbba5ae0d9d1e5b7bc4eec9f920369937a41d078a5944aff3cf8": ["myvi.in.", 0], + "df279fadc8984dd0b2085c6ec363681eb3ff3e8b697007be52cf4e945ba66c1b": ["arbeitsagentur.de.", 0], + "df2915be8a37f5dc49b7a0d468230a4be502e830c379aa9e719d9767659c2d8c": ["9now.com.au.", 0], + "df2b74143bcd25c0e3c339e95837bbe8bdc265c3ce6eced22322c149c3f94719": ["basketusa.com.", 0], + "df3162a56083660ec4e2d6775a66354fd691e381e4fe28866811ec98a0f9d8d0": ["paddypower.com.", 0], + "df33311f15e34020388a05c73edb382f457bb31f2bb23a4b7ecc899660d75f86": ["nastol.com.ua.", 0], + "df3cccabc8229e5bb25c52e2fac746b370eaffc581fb0937d13ecef98eb15d82": ["frandroid.com.", 0], + "df479693286171dae7450a4d9de820c2348da1d4a8c47e4c3ffa7ec9bf6037b0": ["talent-soft.com.", 0], + "df47ed525600187d7a9a35fe89dc01a887f2059a86012d9fb8862eb90cf5458a": ["internshala.com.", 0], + "df5249e8c0ec762022ebcb08eb75440cf73808cf48e10a2b34719f4e8cecf0d7": ["wowow.co.jp.", 0], + "df547f4480cdf936d50266497efccc788ddc13159c306bddaa155889511694eb": ["lk.", 1], + "df63569a0a4f65acf574d5301caffe2c42ea4702d14879a813ad2ad26a62f9ee": ["camvideos.me.", 0], + "df63da3e81685ece63a70fe6dd29bb2a9b21f3c1adc60c6dadbdba586e456170": ["ncsasports.org.", 0], + "df6691fad532618e2dc3845efa9cd8000ad3f34d9ea15a35cb3f6b4e6558d83b": ["punjabkesari.in.", 0], + "df6c0e8f0581d8489e79e2e24d00fd691e7b9f3315d6fa7f64d1db0a7d814d2e": ["udn.com.", 0], + "df7067e7bde20c7e347a8d950bd181a474d00722b97bffecc0f61b60229acff0": ["ratemyprofessors.com.", 0], + "df71bcb6eec8dfc73932a0384756992ae771b78ddcad23715905d05cfd4ba914": ["inss.gov.br.", 0], + "df7ad31bceec81ee09aa761a84c0a2d2c25dbf6cd82f7dfa703c4bf9aae035d9": ["egov-nsdl.com.", 0], + "df83a901cb8ed7baa7b733d0d9c2cbe90962fd7f7ea54bbba8d66e09b38369d5": ["citethisforme.com.", 0], + "df8a041fe7bfee1aec0c8be85a0429b819ebd388ccc6570247852df53377087d": ["hotjob.cn.", 0], + "df93e37f151485e8ed8fec1d02714948b85cea779a96d86ec93321f534523bed": ["webank.com.", 0], + "df9d57ac41d2f3f6d0905288b6608d011eb5044560caaebf769a2d907fcbf5ca": ["mmoga.de.", 0], + "dfa1b222df7dea84a6cb10226a00d30e3cbbae981999515195331a8c8c0b909c": ["kaotic.com.", 0], + "dfba8d3274ba319e94a21ab1c6669799cfba53ed3817069105f4542648b84a43": ["phreesia.net.", 0], + "dfbe5f54401027fcccc38dd5e193b87c34339dd7a8bd44d0c07120d00c8c2941": ["cwb.gov.tw.", 0], + "dfc2228755eb37e0425e853043db29175e24a31aefff1fec4c37dfd3b1d6982d": ["serebii.net.", 0], + "dfc23e88967477a4156472e26b713f3c638ecae5d3c4bc5b6de8e9f379dc6343": ["molit.go.kr.", 0], + "dfc789a483860dd27aef0cc5af7883a6133492e2befd52aad55e641617b12350": ["hrblock.com.", 0], + "dfcae5bb9389a863833863822d401255ccd765fb5f9b207013d1b867e5125e09": ["kartra.com.", 0], + "dfcbff9f7fa30035c326406dcb177ed7b5815dd3326caa37558a8b0762c7c4ce": ["66law.cn.", 0], + "dfd17f9e6339d954679f6219d7f7949829f7494fe89e5c2f32297c16f225abe3": ["iqna.ir.", 0], + "dfd5635afbf9b1ee94563f27f11f67a6c73bc94b09bb7655e740543ab4c34219": ["jjang0u.com.", 0], + "dfde14db6f8f9680cbfa0e5a758186b7089cea2cae18bd9a2095773647065e17": ["maison.", 1], + "dfe0d80ea9ccec95ae32185a33097811073fc073bf856e99a348ba3828b78efa": ["hotels.", 1], + "dfe2dff144bf6b7cbdc6402894853d3f7d6fd403195a43db92576903ef2d2cdf": ["creativebloq.com.", 0], + "dfed42ba6ddcdeab1b705e92c9e6626b845da9be96a8d3c2a391299022b6dc0d": ["technopat.net.", 0], + "dfeeec3f4350ddb3d646e65c3549509e63006285be7ad12948cd9b0937b755ce": ["hoanghamobile.com.", 0], + "dff17d0593664b1a48832a7744a7a06bcd65b466671e81fd11da42448f634df3": ["coin360.com.", 0], + "e0192cc9f9840e2da6f1b4177a7636b415a5db4e4a4891591c11b1796790ea12": ["enel.it.", 0], + "e01b6465f75b6b6d43c4205216813c7f13313bbdaa25d83ad39340e52c657d98": ["latrobe.", 1], + "e02ae013917897013fee24d587c3cb2fc59d91c8ab741a92cfe81678d61fef79": ["resumegenius.com.", 0], + "e03641ec48cba581e044b8252524c551671866ca57541a290065e2a6fde0709d": ["studiobinder.com.", 0], + "e03d8d9b9530604165916e2307807eae467af479860e73d7dc15507887758485": ["66s.cc.", 0], + "e0451ed28ad6c623f1d684307e58b706a627c7172f6f8cd9aaa0493bb5df5994": ["pioncoo.net.", 0], + "e045af63c7c1a7f54818fd403d65ea11a10b1d95006945254d1fbb10b7f31ee9": ["vnexpress.net.", 0], + "e05304940419d52d96d9609b0588d6c3f96de9bca46db7cfb79261cd215884d6": ["al-akhbar.com.", 0], + "e0579f5527746377fc1e5cc8faa2eb9adb32db9bf009a3ed1cefc6cf4a8b1f0c": ["geneanet.org.", 0], + "e05cf970d42f79f11470ca5a286f52afe9cb1eb2e82148482044bbb919a29b8d": ["xn--kput3i.", 1], + "e06f48d769a6714ee33a1d8ad0a8d8c7117d88193b8fdc62fff6b596c6e2110d": ["addtoany.com.", 0], + "e07049e8567c73f19496395673c8f19ce20c386ea81a1029487a3d631a88b32f": ["giornalone.it.", 0], + "e077abfd0258c4350e47aee2aef1a976bec4a3fb9399768d6e694e05ecb6d867": ["aucfan.com.", 0], + "e079aab2ed8d80a6da756ab5280b9dcba8418a9ba757f80f2bc41a455a4ede5d": ["nypost.com.", 0], + "e08003c1ee5a86481f2b7b045ac2d668bac1679467f3abe029b0ef56945ced71": ["javarchive.com.", 0], + "e08971231895bf8966445bc42ab939d35f690e5137dd760004cc186d668996a0": ["redtube.com.", 0], + "e08d3fdef7f10cdf6900e96da0a12f58855f2dbc245f1aee7cbbc2fc6d9d3fed": ["wpml.org.", 0], + "e0909167530fce1fbea818ce6f06f832d1f98e540fb5f4fcc948665fdb336408": ["ahzwfw.gov.cn.", 0], + "e09cb20c9725385fe3e28efbc87e4e913e3c5bc250a9b0d7fc18d0d5b3461d50": ["annualreviews.org.", 0], + "e09f1572a0f4a865d3f3571b0be48829424e6f797aba666462f6d486f5ec2bd7": ["solopornoitaliani.xxx.", 0], + "e0ab19001b1fbd380ef9da46e258a4e82538517b71a4d9f1041855901012d7e3": ["ifmo.ru.", 0], + "e0b30372a503918a1c58131731255ca360767ea106c6a445be67fcd33c610392": ["rochester.edu.", 0], + "e0c36c0555ff4b4ec9810561ca7747bd4673128865b8ba6af411280fe5ac4a2e": ["ebc.net.tw.", 0], + "e0c5bd9a0600ee8c59a8e44fdd551fa9c5a5a02bb15fa7ff331a1ea75c5a60e6": ["youtube.", 1], + "e0ca322880ab3ec45e74fdc333f096d9d4b34f17f72c5b46748aca3cea612c38": ["vintage-erotica-forum.com.", 0], + "e0cd4dc9436bde04ecad3a060cc1dd9d57706fc73a5efaff7bb130086575c1ab": ["phonepe.com.", 0], + "e0cd8f07de64b0851a0de20c445cef38e3953725666b70188893fe1ba38f3750": ["docin.com.", 0], + "e0cf0ff9401faaa748617cd92b83b62c05cba62a2ad47d6d7d206f3a1eb2f848": ["rumble.com.", 0], + "e0d3c6d283ed978a1f58dadecb137bc97399cf8823b0b20acb34cc3da20fb1f9": ["bankofbaroda.in.", 0], + "e0ddbdf989d5abeb0bfbbae39c21156a87a04ef3fada4c98ee02bd84059d47a7": ["infusionsoft.com.", 0], + "e0ddcab3a39a1c40438cb2d5dde93a5fce9f00ad4254800172e495093afbc22a": ["shiyebian.net.", 0], + "e0e0470bd655c0881aa93d31e8f0036fedb7dd9d7b97449af43a466c42fc3b30": ["yna.co.kr.", 0], + "e0e4b6e9ada0c98b51ec8dbb43823855c42e03a48126ffccfeaaaef28dc00e5d": ["ubank.", 1], + "e0e64243545710708298432e5839cdcca90bf0fe2e180f937ba45b99a530bc4b": ["clevelandclinic.org.", 0], + "e0e743f4704b25c156cbfa3181f9057a18c196da5d8336f8d51b5b816735ce0e": ["serato.com.", 0], + "e0e86c958be446097318d5c9b39caacae9e330a29fae0f3bf825a07bb65f13e0": ["leboncoin.fr.", 0], + "e0eb6fe6d0593904b004d44fed00e9db52b925e5f719a0244f2048af276fb4fa": ["halfords.com.", 0], + "e0ecaa450bdd3382c69822b8afda0fe70da2c3163039e0a4eadb11a0f3a84fad": ["bt-tt.com.", 0], + "e0ed5e6d0d8569129abd1d09222bcac6f83d5f9c2bd62fc673ab6497a977d537": ["nvsp.in.", 0], + "e102458462c776706800a9b04a96f1aa07a84103ea6b4afc136a83d2ae3f8d75": ["worldatlas.com.", 0], + "e1116125412e1c3c96c4f4e47176db1d4b1e3b708aedee061d971e3ad48b28cb": ["g123.jp.", 0], + "e1175d3a056e83671c6598b7fb74d8ba6fdd74547dec8e831bdb3e722799f87c": ["mts.ru.", 0], + "e1186246283025cc5cf5097a87a4f35941f92fa1107ae72da648f6ab51219c99": ["e-shop.gr.", 0], + "e1190439fcfd4eb769849ae6ea8a729927cce7cc595ba2bcfbac049d8f23dfe8": ["adultfriendfinder.com.", 0], + "e124e3401176c8fae8a6d71541a98edc202f8f831a84e25cbce2433f32a81296": ["logomaker.com.", 0], + "e1268a10a2e32cf187aac0a61c218f0be7d84edcf79c803a1782f16f5adbd303": ["nicusa.com.", 0], + "e1294661f096d443fd25656a36c488d725418b9b19793f0bf09fe74881cd939a": ["selfridges.com.", 0], + "e12de882c3676896e8d9c0b5f6e00e17697d1f2587bdcc9a3b9e34ed15b29083": ["adidas.com.", 0], + "e148a0fb6812e657412ac55a06a65d24dd713f5680de1a3baa0ab7228c20a0a4": ["honeywell.com.", 0], + "e14c591547981b1a426fa833cd47dcab38b88a4c2925ef5419b8d20fb4bdbff9": ["yehetang.cc.", 0], + "e14cc2380e586b4f5a7b1df15a0d960cca3bb4e5bbee137f66110c3061154bc4": ["oceans-nadia.com.", 0], + "e1503027b0606459d0ca79baa6b9ee466086e64c21a5fe2be55ca3295879ace9": ["zone.", 1], + "e154b899dad51b8d8cd66c2e7c1d2b7903b56dcbaeec669a7d10b67bd7afd463": ["mackolik.com.", 0], + "e155eb090700e651011e2ce07763dd9bf8a8dac14908cf6ea3f52959de2ea980": ["fmkorea.com.", 0], + "e15ff3b7ccbfc3ba478888233c021f545a9f3bb6437605918a673b6d414b9811": ["fashion.", 1], + "e163ccc0231dd078d5e677cc79bb01a319d2a5c46ccf151a67324cc6e49beee5": ["multicarta.ru.", 0], + "e1643d1da9514a045472f9585c46ada1b2c4baf7d871650118d056e770aea273": ["firstdata.com.", 0], + "e16aa5cb322b1ecb9698c50f900b22ef61d17d1d30746ea072d85720062c3119": ["portableapps.com.", 0], + "e173f95f2b34209dccfa73afad0e39f7cce264dd1de06e1b0c20a4ed7c5f4ca4": ["sinopac.com.", 0], + "e17d9d0e7c2ea7e12635f545049bd2223b72a7bd2e83b92560d3fb2d8db8afd7": ["parts.", 1], + "e17f8d7c14a148c7260c079d456157f6441d40863125e2fe37d2c423375cd315": ["scanlover.com.", 0], + "e187c68434b2f857e58c16a76d62da96413453e8da3598faef900cd77c639b13": ["libertymutual.com.", 0], + "e1887316a2f54490d3f624a4e774a1737447df846a71a2eb250f61eceac2a38b": ["immobilien.", 1], + "e1a1f47982bc744bea21b1f6f6ff1ca68257f3f5a6477bf82eff9bdfecb0b5d5": ["toptal.com.", 0], + "e1ac5446165bc0cf31e41056bceec6bd719284175777af0a6bb10bd2cf4e9e9d": ["python.org.", 0], + "e1b12516c6d74c3c09e7621323bc20902ef5a35befabddc7f1e58eb7c9aafb8d": ["instacart.com.", 0], + "e1b61bcb06223810589f42f6d5cc8ab9f37e63513ce9c06c633e6fda8a84684b": ["bikedekho.com.", 0], + "e1b843285d2d9a095efb1e3cfb06e4f07aaf014d031a12879c4a89303f18a65c": ["sfr.", 1], + "e1b8459844656c9c442b72ad3c0c8fa5a6cbc852c7bc8720704edeea6092c980": ["imgbb.com.", 0], + "e1c688980a8e3d80fcea34dea7d80ca14aa92b909e7847b87d1ffb2564cbe0f1": ["uwi.edu.", 0], + "e1d97cfe5d1883ea23a310971bd68b97eb9f29eb76c7b7a6159705ec1bfcd760": ["airfrance.fr.", 0], + "e1d981a14ebb7fd4f77f40d8c9ce841e5729ad3346f64d2b7ad884b84d028064": ["tinhte.vn.", 0], + "e1e334f0a81509c7b0a44e89494bf8e4243c95f219af75d93e9db85e6d7ab080": ["mohela.com.", 0], + "e1e90207c0b386e5befbc96721081a1a0698cd6452eb030f04e8311b05249044": ["flyflv.com.", 0], + "e1ed6777ea8d9657d49301b8eaa0c957f7d23427b92c67bc658b873c131ae226": ["kerboodle.com.", 0], + "e1f2dcb2a5ddf02466b0d1a2c879b618aeac041def31352cbaaa6d292cc9cb27": ["ddanzi.com.", 0], + "e1f5877c08f4c1f4e86e6e71fcc398b12725b4280d582c4f30974331a2b1c1d6": ["jquery.com.", 0], + "e2127f6b8abc638a016e1081a6e219e9388326a9458dcd639942e181388801e1": ["lacaixa.", 1], + "e213c9652a1eb936382e38ad6b2082ab0bb798cf51f5c5c2936e3e02a52c8fbe": ["cuhk.edu.cn.", 0], + "e2153c8f789adf0afe5d04089b8684e4dc2d869ef740bfa19d59154a86f9d44b": ["deichmann.com.", 0], + "e218ba09bb0148db2d7500d7a584dab2b0ee09c94c64731b69ae3fb011f0ca0c": ["babesource.com.", 0], + "e21d508504f0a8f7011ef7f231f6317444971048c0bff3a8e8f98bdf91361fdf": ["scimagojr.com.", 0], + "e224a5215e385a0220ac20f86ab1921c1f85d22bcf85c80d416c5b4691f16f1e": ["sante.fr.", 0], + "e22a3a990cf7771d838dfdf8671296b331952fac02d660889b9e6cae9f55cb55": ["bupt.edu.cn.", 0], + "e22b975baaa4cdb13d5bbb93a1beaeb79ed2df6d00c9ab4bae9cc2e6f921786f": ["saikyo-jump.com.", 0], + "e2300353b4a1e8cfb70dd587146446d6debd498579a0b727984f16cd0db030d9": ["bri.co.id.", 0], + "e2351a736aef2dd7a74e8231180d90f720fcdc5e89e99c51deae3a560f74fd3d": ["ferrero.", 1], + "e2351dc649ee76990850b68ef55a557c90526cc160f72ed4a16162478c6e64aa": ["nissay.", 1], + "e23c996d3c83e89da4d1334a1f75a40aa2451e2d300714e41e1908cca6c8fc99": ["interpark.com.", 0], + "e23d89bb9f56e804aec69a2528746ba946a57cb07548738ecc23cf41088a10c5": ["russianfood.com.", 0], + "e242526c6ea85d93a102e88794a219f7aa19e910f19ee12a71296c78ced94a99": ["callofduty.com.", 0], + "e248561e9aa5ae60f1899ed4c652040fe91ab84a7cd49be4055a486b9c81f7a1": ["desjardins.com.", 0], + "e24dd3159c2263258afa6057ffc68026b04a49ff3e3d081c51758c11b8da2a5a": ["italotreno.it.", 0], + "e2560139eebe7003aecd51b5cf5ef559bb21a6214e40c38d5f905116ff6253b3": ["elfagr.org.", 0], + "e2567ef2b08b9618b1ce7334756a5480f4096e5fae75391ee7193070d021934f": ["wego.com.", 0], + "e25d35ae7dec5fe1c54bd38d52addeba6bf471a3560782cc219d673f0ccd3c22": ["fileplanet.com.", 0], + "e2603d3e79a0d23ece65d6c0a8e6df839afe5d5bdd578fcd71830d79c572ad78": ["cbcam2cam.com.", 0], + "e264e802d3eb22874b08400756ccfd8c0af845d60d1aa962ce5935876c7ef2b0": ["argaam.com.", 0], + "e26e69e0c1a57ea0b442d15c6dc31dc431ccf1963a385f80f3d8841951d2a9b3": ["mahidol.ac.th.", 0], + "e26f4dae6daa8f6e887a29c1792eb60aa3fc6ab95cb6a90b43a17ca15dde2a5e": ["cambridgeinternational.org.", 0], + "e2765aac1a2b3e915b98657b5a2e77439244ef14daf78b27017e59e2c140e10a": ["siteprice.org.", 0], + "e2793c52c7bdaff9113bbb0677dcb76a8b35b79354a1121b9c57d2b2ef182b05": ["theiconic.com.au.", 0], + "e285167cd1be9b568ca9a61e085613cabb0f2e83d8a07860eaa3f3df0d263b3d": ["hyundaicard.com.", 0], + "e28b9bf621a6709bf3ab36105082562249bcbb7549490d352c2341f091d4e802": ["kurly.com.", 0], + "e294f422dbd83ab3ef118f637bfae745c876ab87773327345c824446507cfd47": ["dongfeng-nissan.com.cn.", 0], + "e29b114ab636e0261607a5295068d0cde49ece5a6555f5eea25e87a45de32619": ["yourupload.com.", 0], + "e29cb9a90424d2c360bbdb3f1cb4132a4cbfbfb283b541225fd57235a761f7ec": ["likefilmdb.ru.", 0], + "e2a171c6436d1e0399b8b61fa63b9d784695842d9c8d74e5e93f3c9d4ba53d2e": ["hh.ru.", 0], + "e2a7e98ae5f462e06140f58c0189575c310423c8f698e1738887a4284b536ff3": ["paragonrels.com.", 0], + "e2a9a53a2484c1d9f557b28ea9b3c89a29871033620dcc5facabbaa0e8adafe4": ["naviance.com.", 0], + "e2aa2287e68d6ba85a46d1cebcad8de1397bb61a97bf46902ac5264d6ac863ab": ["livescience.com.", 0], + "e2acbf6a47e04a7de595e59a03dd205250ccb15870bb0dc5cbed386208be2a37": ["janebi.com.", 0], + "e2ae6eb7b14b1d9b589dcc1c52574a27881135fec4baa7d75cde18f1c7fcc48b": ["questionpro.com.", 0], + "e2aef34d5c77f5cedbc19c1e0eab8439be159c7136d1c86bd4aa69e096250bfa": ["aktualne.cz.", 0], + "e2c1900deee813ed0ef01ccaed0f5c2fef9a297a4fa29f856a074eaea479470a": ["filehippo.com.", 0], + "e2d3bc379194972cd16809f31ccefc60d567ad2648a98e75a8f1026ba5d3a296": ["coinpan.com.", 0], + "e2d5192d33ca044cb7b157d9d26858dd0b34ed7dd3d908216c167afacf54dcec": ["fanpop.com.", 0], + "e2d98fd750121a44ee96b7ff970f04db1f699cf0ca743c78dde3b5965dfa3acb": ["shanghaidisneyresort.com.", 0], + "e2e54f0efef0eba9923970239d5bc946fa230112e7dccb4fc0064db8f1324969": ["kickassanime.ro.", 0], + "e2e6e518226a3eaf8befa7ae61e23eb7ef09ad9436dbb99276e3b6d270ae0e3a": ["setadiran.ir.", 0], + "e2e733d7f6d1a9cd4f4211a9613d1865726952c8bdaa58a6ac9fc521b6436b1d": ["onepeloton.com.", 0], + "e2e9c731ac60b0ebe6b0fb14d022d97492cd7e27b27cb017f5b81f86e7b61fd8": ["toyokeizai.net.", 0], + "e2ec1807782dcf186cd489c1a5d2fb13b89c1269b52bb96f4a9ed5878804db83": ["morningstar.com.", 0], + "e2f51117c9e7ab054fc44c48e445e873e76e25afe45774202bcc23bacfc6cc9a": ["papajohns.com.", 0], + "e2f5b50846f7b24e25bc3d94fa546262180420ba99d65490b952ecb225a76ddc": ["alienwarearena.com.", 0], + "e30047cba5959e18873b25ab4cdb4c216f616776060a913f030903ae30289e05": ["snappfood.ir.", 0], + "e3050a63b6030810c1bab0859ae6123e6e3748ccf57e9c164a82d0e977b3865b": ["winxdvd.com.", 0], + "e305cd8c3c61ce2e062b4451f124fc38cff22ce50b56f95a5e139555a77ec311": ["mailchi.mp.", 0], + "e30880fc624d70bf067b6fbca133433c113298adb6286632f71a3916c151286b": ["elcolombiano.com.", 0], + "e309832839400e460473ebb3517021a0da9f885ed7befdee314fdfd015462d00": ["ekstrabladet.dk.", 0], + "e30b284dd32e69b6d5965e6b1deb25dda5808af0ee5f82f851d4a3ae25fe35d1": ["actor.", 1], + "e30bbcf6e06fbea26ea39a731e36c2b6a0b8de35bfaabdc59db701efe0b4f53c": ["libguides.com.", 0], + "e3125bbf94e63c0f6d568793fd7f335a9bacda8741080e41e31853a36366c8e3": ["vesselfinder.com.", 0], + "e316e105b3266af40e443af34d75d631457689c1f2849bdeb33bce16c93195cd": ["anime-sugoi.com.", 0], + "e318fc39d8676ec0a114163b51687e235967d97785c5ba638bd0801d1faf464f": ["fflogs.com.", 0], + "e31c76d331ce3782f45e20c22c68ed622e8b0c1a9e6e6ee5f9a6000c0ec396b6": ["epidemicsound.com.", 0], + "e31ed4caeabea7f269a37324853802c707b3d1e859176a6ce335e1dfb892395c": ["manageengine.cn.", 0], + "e322dedbbcb24e41da6a33c747b731fef395909ca2d3bf47e81ad82729f1ce3f": ["postimages.org.", 0], + "e33046a80f5bfe4f4ff70abf9a925dd1a51f882384a92d0d5313e38102578c66": ["bom.", 1], + "e335253965bf76157da08ac65ff53cf637859aa4908cab4f3932be4e75966076": ["larousse.fr.", 0], + "e338007a465c731264cf40750f7ad189533f179dc5ab11cb3debb2033c2942dc": ["hetzner.com.", 0], + "e33df86cdff89d4cf5327cb7d8dbe795a7794de6daabe8a78671ea2b41b108bc": ["bauhaus.", 1], + "e342c2bd942418992c72637f3dc28bf52b7f02e93e5b511cded02b929a40b2e9": ["rediff.com.", 0], + "e343abb7188b1cd533b8752f314bbaa81ad48151572c2b5ead9de7ba4885d70a": ["bunnings.com.au.", 0], + "e346f281b2cbcf6e1dba4ab7543afb733c566c911de1ab3a4b3ad71693bce01b": ["saxotrader.com.", 0], + "e34e0ee20728680b0acf3f49f00417e0301806f76c5aacfd1fe6ae6635a69b82": ["agh.edu.pl.", 0], + "e35a6b4111ec21687d446e973c4b50161988af8b85f879f1a3aa827f1bd04ee3": ["hinkhoj.com.", 0], + "e36789946fba20615ae674cdf0410c63e36f8e646fae7615d6f8eb50e4aace3f": ["centbrowser.cn.", 0], + "e36831bbea197b752f1d78db0e85a69fac770e4162454cb5329a312fe881038b": ["test-ipv6.com.", 0], + "e36a04979d17e58f4bb8e4da3f1f1a8d2ecd8ab04e37ba5df5a93911fd0bb07c": ["serieslan.com.", 0], + "e371cf873b705a08fc57c779aed6a272cd99c204fd992b9ae7b52114858b729c": ["emerck.", 1], + "e378e3590e37697ea423e8ed414c67f507292f15535abb79798af4300a7e2f1a": ["om.", 1], + "e37d1626848d47609d658e4f11a9c72c945ce7b363b249d391299f6de2f46865": ["coupons.", 1], + "e380643d7d7d9eda4638e560c2cbda65ca1ef98b464212f121090bd8daa22a3a": ["limo.", 1], + "e38b30f55dd815b900b1d070b5f5748ecf0496fe531337278739faf556bf23a3": ["nwpu.edu.cn.", 0], + "e38e57aa703f3b114b177c927543de6ecafe4652b68b387649fdc442ec09fa15": ["lindaikejisblog.com.", 0], + "e39007c99def05da62bf94347528387761089726545c797f094ba119234f42c2": ["thisdaylive.com.", 0], + "e39d32aa0c065bf83d5c4e6117ab13e8ecdb473f100bfe2cde97e2de5d63a512": ["vi.", 1], + "e3b559b6d6866735e29bdf840733f19699238ea80bc44c52a2ed9bcbb48c1449": ["bluestacks.com.", 0], + "e3bb1a183e38f845c560f03162ff441b762bc7eb9e641ffa19ae313921d70ae9": ["komatsu.", 1], + "e3c31a17dbcc12150b29bd23baaba05132fdcb751f92f1dbb5c3742de4726e74": ["infinityfree.net.", 0], + "e3c3660f67cc21f8c3269f827feb19f9e64212d88feba8b172476960951e3ccf": ["immobilienscout24.de.", 0], + "e3cffed3c5e3c53e323588b1fe6564753b6af2560383d7a5002281e1f41d8dbe": ["hi5.com.", 0], + "e3d24cb51042dbad2fbf88dcc4424ec07c9eced572af1ff6b8dfdb2979362f09": ["cnet.com.", 0], + "e3de1f268b34fe5a003d71afa353291da2588e96042b0739f7248dafd0d5f200": ["journaldemontreal.com.", 0], + "e3dff4f620121f3d0e6b19c6bdb218bb8878f39d9a199d5d9a7a7d9dd16b21cc": ["tsn.ua.", 0], + "e3eeec5db2859e23cefc757c058f9749affadf89c98db08d818abaa518254916": ["indiafilings.com.", 0], + "e40111ec31cbf5e08b1ad453fe9bef52ff3e4afcc657bf435348ff215ac8de47": ["klook.com.", 0], + "e40b11a2361da16fe34408f5f4a1d4b8149b682f36589842077e21a08268e34a": ["href.li.", 0], + "e40b5cbbaa389eb658bd7ae45acb4081652bdb78fded5c11b5de9c2964c9baef": ["uni-leipzig.de.", 0], + "e40df2903f3cf51bbe5a9df51784e0b350adc7dee7db8c4b13cc2a5ae76df667": ["soujianzhu.cn.", 0], + "e41470ead49decf98057999d7207c2316c303fb8deb8714af9b53108fa2399dc": ["360.cn.", 0], + "e4169b53aea4269f1063d3a5d4e98b8b422aec8e6abbddbc341bc128e23d2aef": ["foxnews.com.", 0], + "e416c5d1287befb3c532c42ecf121cd1cd699cde2590cef4019b0f4970d9226e": ["cruise.", 1], + "e4175b482bc8ab290f4b705abe4ce19dd3b44527461342e4b0c79e5daac8eb08": ["ipip.net.", 0], + "e418cefa70fc06d4996a3599b8fa461eacc75531ef041acb7b5cc829a0fe8eb0": ["clickfunnels.com.", 0], + "e4197482275e42f74ca4a0538de3bbcd3a80853df2405ccba90602eef1cc3771": ["boobpedia.com.", 0], + "e431b85dc047e0c5721a8cebeed4c2b72a78585fcf8687f5f9bb289bf5113f6e": ["4plebs.org.", 0], + "e43a606fca51423da587b8cda59fb41bf1034a3c747dd84540af9164a19345b1": ["pcpartpicker.com.", 0], + "e43b6d219cbd86770653f33ececbd32241a6b48b93eef78e9b6cafd7fbc8642a": ["iplocation.net.", 0], + "e43c86d3c6348f0f7bfd97efb7d14f62051d0d72b312c0ff7c8820bd38127691": ["amazoncognito.com.", 0], + "e43f24509bcf1f6f426c529fcea5c1cb61b1bdaf10cfa6e6529afea95a5e487d": ["tokubai.co.jp.", 0], + "e44058975b3c594b145f29702c1444c4414b8af4283ebeffbeb578b6c6de92c3": ["20minutos.es.", 0], + "e441ffed4ac472c8d9676c740413fe60815ea0e194c1d33d2119abc59719cd8f": ["fwxgx.com.", 0], + "e44202355dc2a60507deef89280b5c9a896712e160b712414ae42617b7cd7d1e": ["maam.ru.", 0], + "e4467a2c2b44e694427882491ea62cca03878cd0c7c8a6174321d7778cdeedc1": ["international.", 1], + "e44cca2092d69663a14788c43ff9a6cb6815297af01d995e47498674e7b773bb": ["ftcdn.net.", 0], + "e45016228600d8b6d8fadb5ce0573f45983d70e849f89365ad2acd0c0b319625": ["vhlcentral.com.", 0], + "e455e51b0cf36eed42242c01a32fc4d9f410aaaf9dfeaae03a4cc08a49765db5": ["video.", 1], + "e45c9d0c6d05ac54759acf84e03cbfdcf14b924d2a45aa021c85dd8ce7a567d6": ["symplicity.com.", 0], + "e46479fa3b4212736a843f06c1f50eff985d86f44fdf4ad22f304f4bfa0d8329": ["xda-developers.com.", 0], + "e465f6dba39b57bc7f1b961733fa77da50e31629a80256a96fc753f914ef7519": ["maariv.co.il.", 0], + "e46857b68ee3551c0ad3eee01a8c730295cd7f3f964f4e2fcefeae70a2219e84": ["vakifbank.com.tr.", 0], + "e46ee2d7db887a99f349758ff15f48e9686308fba1e8c527fe063b89f4975ff9": ["iask.com.", 0], + "e47741fcb999ef06eabbdf00a54a837d4c37d759edb4fffaee85689aaf6db47a": ["travelclick.com.", 0], + "e47bc4f949b3c1714d032b33273c3a88f67e60fc3f69a2bf8a1baccb8e6ee939": ["ebscohost.com.", 0], + "e486fdc9ef7fc066632dd0e8877ad38ed61927ea690c2e5e65343617663947ed": ["cards.", 1], + "e487493b7fe28af68d5095f11832f738a04550bc0d5c6364773c2a2603d06def": ["nonktube.com.", 0], + "e48d9ab8f7eb6eef86492c2a5e7f51faacc9b2e5059598e00cffd522abb278c2": ["flipkart.com.", 0], + "e48fc857db8e8cf99c40f42ff385cd4062382078b6ff8c194fc52fd344ae8542": ["conrad.de.", 0], + "e491b1030585e793a3ccb8eb1fa566e310af8217ff46adc305311aeccb38ce4c": ["seatguru.com.", 0], + "e49531bb14c5506aa9afd91f5af690447e3aa4fc54ac3f882ae2e71794beb22b": ["pornet.org.", 0], + "e49ae6f36d7ae73413cb0aa34e30cc2766bd259cae382b42ae86bb421d9b05bd": ["daraz.pk.", 0], + "e4ac4144bc005675137070f655ba0cefdcd2b5ab729aa37f6eb6f31fbd850787": ["convertcsv.com.", 0], + "e4beb6379cf2acd9ff60d96146560ec501b09fd1a4b2e7829ad39d6665a60d5f": ["rojashop.com.", 0], + "e4c311c0840a89be2abceef457e04816cc12859efa895660fbebdc1e681497a0": ["fcc.gov.", 0], + "e4c413a0a8bfa53e510b371cada225d090b5510a15b8b55d349decaf7c281d8c": ["kijosoku.com.", 0], + "e4c72d2e4a60598a457462b341f80a29a09a6a8cfd34840d669a9a89c984a1a6": ["gmanetwork.com.", 0], + "e4c7a667ab5869e1ccd3ceea056c743c4888762332491111af69a66ea31b7c7e": ["fc2.com.", 0], + "e4cf368345ad25dc9a261d58e3c69c4ff50faa779e4a27c66feec77e8476c956": ["siamsport.co.th.", 0], + "e4dccbdf3c0ccd6de820ce96c870095bd06d56861ceed3c901fc04c6fb976aae": ["bcg.", 1], + "e4dda25011481e31908e2f1c388ff4479548b8716cb6fe9ee122150e3676168b": ["rcsb.org.", 0], + "e4e0fc87b7cb510c97a94867d9ecc9e5e58fbdf2e63abf585b55a007145ecb65": ["cbi.ir.", 0], + "e4e104a31e62e66bdbdabb6746cae47fe8ed86250bb5e7517a25f2fdbdfe1479": ["flynas.com.", 0], + "e4e3768d4ff27bb5fe4fa41f7257359e36086554f7f8abea3dae5fd3c25ea0fd": ["pornflip.com.", 0], + "e4e37ccab01ddb1e243f08f42b0a29d00b53abfb7cd11da0761803d579b98021": ["unrealengine.com.", 0], + "e4f2fc67e36535c3e4779380898b65ed6b5bd53ed09a49c0527146f44cf394e5": ["mojedatovaschranka.cz.", 0], + "e4f8b6ae2d3c02927ede6d9445c0891c137b9d089abea230eb0fbb949a0083da": ["ustb.edu.cn.", 0], + "e4f8d5b6a61329f35d6628b3b670c8034e01a01c6a6335378b0d601af2125990": ["ricardo.ch.", 0], + "e5021c2c828b62d8433e6ba7da43c8195166dbfbdfc259501ec0cda6b2b00f66": ["carrd.co.", 0], + "e50845d58554106598b94d77697e385fe0f65f0a75b3ac9c3c8e9b3fb3e7fe4a": ["dothome.co.kr.", 0], + "e5184337769c57291586af6127f76bbc1643e660a6ed9cd83252ec341a730c6a": ["dotloop.com.", 0], + "e51b92a6f17809d406cdbe9f24cd687800974f4a2ff3c7415b2410068a0ded73": ["qantas.com.", 0], + "e5220b5d2e7abceb28b70ff0b023604206d8cdef69457955cebb0467fb444ab0": ["cibc.com.", 0], + "e53a964160d4eb779a490967ec382cd6dd73722289468d1b5c41dd29f5b1efd3": ["sadadpsp.ir.", 0], + "e53c645f75c8a4b98a615cdee661215b0dcbc72ee21d32bed68d241b821da5f3": ["dailystar.co.uk.", 0], + "e548d03365199fe510492aa7a84f28af76af36d52317669da46c3aa2a9aee76a": ["wordfence.com.", 0], + "e54e82d48eff81f23b7941f1bcbb44935672d3d54324063629a9850a30418131": ["tatamotors.", 1], + "e56a1f496a405f3ceff676b0de9cb2a464396c3b9c385fb6334f672a08a7c2f1": ["reallusion.com.", 0], + "e57471ebb4d703540a87b61225f5f5dd1ba02edafeee6ef2364393aa9a37be21": ["abczdrowie.pl.", 0], + "e57d477cec5a404247b1b4346c3df3cd0db2b7699e6b6c32f6e0ca20ed0c373a": ["infoq.cn.", 0], + "e57eaf878ac35f0b5d2ac4caf55fcf5015d3fc8a90ea0ebc94a26cf14ff83711": ["arcteryx.com.", 0], + "e57f57910b12476bac83ec981556cc23a6b5fad0f2c6aeb1cf283ea2cebdac5e": ["ndl.go.jp.", 0], + "e5854afaa2e66e88a97ac7b732ecccc08e0a4f46e6d039dfebeccf43fe6ac08d": ["alphr.com.", 0], + "e590588555d76f22d62da03749ff3fa3f28795ede449271bef6248c157a9c674": ["oschina.net.", 0], + "e5955d732570e3ec69df4a34f6deaefc0fb908e59a4d5f13f0beef8dc4e9314c": ["chyoa.com.", 0], + "e5984d33040e5084f9bee3562d371f32eaeeda41e0858562c4ba89e1a1974589": ["columbia.edu.", 0], + "e5a8ec19b53214297ad3405f20386033d5b2fe1dc10915441886f3de3c2122f2": ["eatthis.com.", 0], + "e5aba97b833061e445c1874c1916e09b00c2cb4addeb9e1c82254294f5daa2ad": ["njau.edu.cn.", 0], + "e5b13268d469a4514083410b4bbc9c2897960fd6e156e1b3bce6b76127e08715": ["apta.gov.cn.", 0], + "e5b842913d50a541250ab1f95d5c8687cabd126ba49984bcd95a55fffa3a8a68": ["powerbi.com.", 0], + "e5b930aa9da1201d90b7955395d482283b7d1d3be45837bcec4f02e8929de338": ["uast.ac.ir.", 0], + "e5cda5c84be4b90ec127d9d85eb81f44a2166f1fd7af0b3eb46c462096a9f9a0": ["payulatam.com.", 0], + "e5d082744ef048d9b001d992c3c8ab30bfa4997d9e8d86b51cfd0169ee2ec07c": ["myportfolio.com.", 0], + "e5d2d08044a6fb6785833cde9b4547ab69a11012c062317ffde184e81cf8e917": ["learningapps.org.", 0], + "e5d92e8dff51a1fa19380f4453ffa98d33b53b841d81cc08947f28d0b8ae8b99": ["ldlc.com.", 0], + "e5e128dfcbb1d899a9eccdeb27991c13e58241d869c1a1e9f4fd0fd871c114c7": ["vend-o.com.", 0], + "e5e214bde0698ab30920ced76e892f305622965d7a4fc45b613535db03231276": ["hexdownload.co.", 0], + "e5e37bfb1e0abb4e8a38f4eaebb7406e92741c8125c1f99fb6e2c8ee053e3368": ["lazada.co.th.", 0], + "e5fdeb088b2977e331a42d55d1786e0bc5d1b7054a134835b8346a7009deca4e": ["bleepingcomputer.com.", 0], + "e60d8b9c9a1e5fb38c2e29c06de2884dd3eb3c2034077f0d0ddaa6a3ea7c43ab": ["interestingengineering.com.", 0], + "e61462fc2a2fe2e0a64ede619fb243cf2a0269a1acdaab17e67b05c2c26d6966": ["hdfcergo.com.", 0], + "e6193b7640e328de3b9bc5fdba6bc23a0c42032e93c989f7e0316aa6151be39a": ["lego.", 1], + "e61ae87812e0cf7e4804faace56a01b84618410b5656cafdfd0dc593364d2ff0": ["jpost.com.", 0], + "e61aec57efc629e07878adf2a684f5d81873efaae4d32ddbe2a66e128c82e22a": ["wooordhunt.ru.", 0], + "e61d50934d410a85d656e7fb6173e89c8f477ad533250a741b09ba04815526f4": ["humoruniv.com.", 0], + "e64be7a550e81278e38f85fc7344db71f77366b40ec7c850a1926547b15e5d40": ["propellerads.com.", 0], + "e651e47b21cf3a8f9b8274abbe41d59a0dd40d398972c1a55254294f09fa3e65": ["profesionalreview.com.", 0], + "e652a457bf167c7d1306dfa9f1bb30602786ade9728dfeb82f1c651064b838a4": ["stardima.co.", 0], + "e65b2cfbdacfc0ffc276913f630cce6666d59b9f2db516df0704768a874fbd9f": ["dienmayxanh.com.", 0], + "e65dfb380ffba097fb5d4f31cd495050a01b3695e867875ed427f840eafe52be": ["sling.", 1], + "e65f7bb1d3e271fa86f3971a87aa8eb2a31601c69d5772a454f72858425db30c": ["filezilla-project.org.", 0], + "e6611aaffd497b2c20db7b8bb46dbe344499d5b39883abcdf6ad9918d19efe9f": ["mcpedl.com.", 0], + "e66affc6a64444ea847da7b86e19a9df6aa43d901c81742435807ffb58e0ffba": ["himasoku.com.", 0], + "e66c1d37b68d15a4c81010146ca3ddba65a08e4dc4309ef02e187c4df48a183d": ["bhphotovideo.com.", 0], + "e66db754642bbb487741fb42e98c23fcbb37dcc620edff521204e35e3c7f1385": ["todocoleccion.net.", 0], + "e66f4bb39c568b34aec6f4f8eecb9f3d7cd4d684df3263a35d18897c5d17e308": ["monografias.com.", 0], + "e67c46f681cfc44e69ffc44f817eba18b1423cdcd5cf13a28078e0880a332a1a": ["mil.", 1], + "e67cb0e882a7977f9c653ded2edf009db749c22daf0285e42344ed0a5b157425": ["xn--i1b6b1a6a2e.", 1], + "e67e5e8bb8ccbc7de14629d7fefea011ea6e42e3ff5c78c0e7d6cca51ec13af2": ["9news.com.au.", 0], + "e680f88171ea61a8733a00da909586389d2015765a35c730a08a523a54e49505": ["myvidster.com.", 0], + "e6854f5b6aa39fd2eedbaa7f78c9a670f39809c1e31d6bbb4463879a0e99c045": ["tuchong.com.", 0], + "e685647c05a8ea2ab885a8c4b3c80a753621e5f75b2809dbb8c202d46ab3bb98": ["makeleio.gr.", 0], + "e68791f7e4e29f4117c82aa98cd5ff6021310d592bd3ae8c38c32cc50e716a3f": ["telecinco.es.", 0], + "e68800e7da8d062ae58abba7f98e99398e39e91c675917e5e6d209d91c8e4509": ["timeshighereducation.com.", 0], + "e690f4706f6c1e9077a986a8e9b20c92bca726d13a32621a4255eeeab477c600": ["skyeng.ru.", 0], + "e692007629a3e0421195ce0d82cecc3ada6248971b55d12a1ff47865c54cc18c": ["uuoobe.kr.", 0], + "e694fb8f617b4fcc9a512394bc090fc7e2ea7fc1915ef32d5deefa0fe5d4e7b1": ["social.", 1], + "e69b2af4e7a1839662bf64365afae6ad09458e5daf1383d0f176ce17b3f8e4a6": ["biglobe.ne.jp.", 0], + "e69ff01bd40e33f342e35e15ea45c87e56b67fd887a822665d5ff1e3ab3f20b3": ["java.", 1], + "e6af87623f702bedba5e5f395946846c5c8123b7ba1fc4e9c357040376c5e890": ["faberlic.com.", 0], + "e6afee06514e4da8678f8f6bb3ab99445977e414f3baca271aa76c2dbe48964a": ["winit.com.cn.", 0], + "e6b58611f509603faa1111ee1f4b811f9c2c101fbbab32a14d14f43f9bad3f53": ["itesm.mx.", 0], + "e6be95d2ace5aceed984c0b17cc8e35581c12ca82d591529e436e7bd17ec7b55": ["21cnjy.com.", 0], + "e6de4325348c82443d3447ec58088e5d96bceeb61af39a56f6200fac22619a97": ["citic.", 1], + "e6e4348918f970b20bd55de21f1dc1947914ed8c82999ddad75514c0045c51c1": ["pussyspace.com.", 0], + "e6ebec2ee707b3d83c4583096cb6cd35939ce5898b1e1c2faa04518c596ba181": ["anzalweb.ir.", 0], + "e6f3fc2ca430df8881c6e5917227e0bdf32078916857516b61064aa9eb00fa42": ["notepad-plus-plus.org.", 0], + "e6f78ff32baaf95cb8a921a353fe29f6d9d92644f15f822bd32abc32f591dd13": ["bankofindia.co.in.", 0], + "e6fc07a765512c219fd1975d8803d9cddb1839f707bb9cd9a071e9ad7acb7f5d": ["ticketmaster.com.", 0], + "e6fc2a8225d1f6c56f95a822e640b821bf30ca21b2d0826710305fd310eeef1d": ["emojipedia.org.", 0], + "e7021badc0503350b879bf2a5742559fd7879931bdecc20bff53e2690c80359f": ["bh.", 1], + "e708b37964d9f666ada78a06527040b3ee096c109a13ba0849646a1c614fdbc0": ["pcmag.com.", 0], + "e70d0b26c97a0485b50376a64f1bd024ce2c67a35b19730d394e1156df4680f2": ["drtuber.com.", 0], + "e717b0654ea02f46bd3dda9548ad6e69cfc2e704410d68ca71447d32715cc3a0": ["audiofanzine.com.", 0], + "e71b9ed848d02976520000da7a4a43028e85fb603ca60583d612081f36123af3": ["dou.ua.", 0], + "e7282fbfe920d9fd56df13bef4f770b8f5f5a09ca4c9588167732018ed6bc376": ["bancainternet.com.ar.", 0], + "e729c45b7774988fd8851f9a0c8ef1ab10b970172e5afb21ee2ffde7ed907b97": ["kissanime.com.ru.", 0], + "e7304a96b2e4803d9c6b2dd06413d0999f5ea1b0e98cf8a26464dfd5acb4d7ec": ["in.", 1], + "e731d605de2a6d9338d6fccaa07b5bf6a546cdd4df1770d9a58ff7bf81836739": ["researchnow.com.", 0], + "e7326c78aee2c61a6a1b153a0a823e84cda9172cdf4897c02b05f682549cbd7a": ["whu.edu.cn.", 0], + "e73d924f6d1f786e82a8d599c06071f768b3722c494b07c52d734d548c9a556c": ["eleme.cn.", 0], + "e73ff26c709adba986a692cc76d2c961d0e06ec12cab416894dcf3d8b168e353": ["115.com.", 0], + "e742f81df2e9f003ce3d5c4f160b4aa395c221b70f0a7c1c695cf9bc5f4e86ed": ["jpmorgan.", 1], + "e74a123d849886587190b15e8f5d6ef8a89df94253741e66a867466c09ec06cc": ["actcorp.in.", 0], + "e750cf863f4469709fbeb4d6d29c68b0751a04a87dedf7c4bf25e547dd4e1ee0": ["hilton.com.", 0], + "e75896f956e25f969d19899a26d32e78a18a3fd68e4f4d67a3ddb69e93a79ab2": ["hellosign.com.", 0], + "e75939a4f82107dfb77ddd1d9bd10281d2835696bc275535143ea7bc99e1c629": ["bronav.com.", 0], + "e75d62321c63c2958e2b98bdd8672d4c7926ff58dc138d5485646eb883f9d1c1": ["skatteetaten.no.", 0], + "e761c64243a41e4e98a9bfccf2dffa73092a7130e489556717a15876ab78dd53": ["viblo.asia.", 0], + "e765156041d99b29c1320c719e65cffa7a092ac4c059257a64822b94cab269d6": ["flights.", 1], + "e766123329b031a68fc985bcb110f3d587c64d4efe22ddbefb155aa2a10a5664": ["ski.", 1], + "e7670aea2b8d625c75d4a2f3565cd4dbd2ade69dac92d03026a7fb4e10e3ae57": ["exacttarget.com.", 0], + "e76c04962e2fb5a44228de150904466e56dd1ee23dba893bd0cda3f993442736": ["pacsun.com.", 0], + "e76f0eca95a56ef00b28ea2c5809cf76bca892a36c355f1b563cf06bfad51b3c": ["surfconext.nl.", 0], + "e779b3a442faac2a222b6eb9d5bcc3375f3164fb2db5acfc008563dad1b01087": ["screwfix.com.", 0], + "e780f382906cecad74bc83c63aa33af6c0d8b05773c4ffbc12107ebb4014c35f": ["qiwi.com.", 0], + "e78c97223761c97bc4486f786a8c71458ac5ea89c3356d231c9ef76323edaea3": ["rmit.edu.au.", 0], + "e79ec7a554b6b2321b47ee0e767b4c7f5632b07bfda286b4fa24ef7c29ef08f8": ["howlongtobeat.com.", 0], + "e7a0e23e6248fa8e4698e183849aaa550738a7cc4000c82d3709f7f6a1315002": ["mobatek.net.", 0], + "e7a2af18883a2efcc4e100fd3629d1ece1a7f28dd3c844a2dcbf9611421d2e80": ["indexxx.com.", 0], + "e7accd780afc54467cfced2a694d65249b70b066672323ce1211e2e84aa1df74": ["directv.com.", 0], + "e7ad14466aa3c6b8868c55f01d23ae4761ed4f28a1c6db08aaece5e8039900b7": ["peatix.com.", 0], + "e7b4441b1a2d6c6d4bfdac2d1b10c233282c59fb9ba175608a231b97478d3c23": ["kidsa-z.com.", 0], + "e7b63f81a77624684fd1badd290618c1e650b6c5c7da40368edebcb7ed7926fe": ["behindthevoiceactors.com.", 0], + "e7b7224a0df917cf6350e6e4aa1a4393a818131eaa96a0d1b012e53d81a9a2f6": ["pervmom.com.", 0], + "e7bf0aeef8bb1627669fe072ad98fe35aa2173fbebdb680c2be6f476d26a1d0b": ["belk.com.", 0], + "e7c0f4d15f358e74ac64e630b50a6813af489c7ec558adb435371c410535b85c": ["uoregon.edu.", 0], + "e7c7146e88b2b54e64cc803d9d546d48a5fed727460e21b61e00c3fa25e383e7": ["buienradar.nl.", 0], + "e7cf084ada76b718f754c73fb69fd54c5ca6aad3834b2b752d85bc8b14c9e2c9": ["infowars.com.", 0], + "e7d83b5f90c47dccf8c3d8df1eacaaa29611a41bb51ebed61ede118b2a5087f2": ["singles.", 1], + "e7db9e90f4e31b27f6625f52c6544044484a438171fb4b39af64b14a23b757d9": ["tengrinews.kz.", 0], + "e7e1f9278b1d326ff55645625f4b45f07819d379fb0d3ab87ccaab213702d08a": ["mbalib.com.", 0], + "e7e5040660ece5dcfc28147feb6a8a6a5194819e7f9dd3ed63285667af8c8723": ["doctorofcredit.com.", 0], + "e7ea0d525c7588603abc44c4a811db2ff84627179ef43de8ba96ba0985407dea": ["recurly.com.", 0], + "e7f4ab45d33a18ea96c26986f723259926be59c4ff9171f7295d8027b28e78c3": ["178.com.", 0], + "e801db7d1ad2abbc50224568b46e2cb7f509c1bed20d2e32c73354fd39a3a51a": ["kwestiasmaku.com.", 0], + "e80272b3f5c54d41adb0fe8f4c61a8b4fb85ea58a2ae9ed92be90c341d963558": ["pervclips.com.", 0], + "e80a40dc01e71b1350702fb625eebf8bd11dab8a2c9b1007dbe4e351804b57a0": ["digiato.com.", 0], + "e8104dd1da1924933e69ffaf14a9c17a050da5368e8ddfe8534b29edcba812c9": ["watcha.com.", 0], + "e81b54263d4a4d4414a038ac8ebe80474975964d89fe3e1a28b1c225d301184e": ["bdimg.com.", 0], + "e81dd073cc9caaa5ad858bd8448d776655a910aae423f35139dc45fd0f884c27": ["krasotaimedicina.ru.", 0], + "e8205563dc49bf5d277575cd05d87e587a39c8d2d9e64b05dcaebef2ce12988f": ["e24.no.", 0], + "e82cdc3ee3cd3f306c17785194a8dd4dcbe66417a82924d7d7d2a0c48f4be6c0": ["nikkei225jp.com.", 0], + "e833d56a11c907146bb9e0ed05d9c4a214dd707ecdf3bf858bf34f2dc7705ed3": ["anilist.co.", 0], + "e835a4d666ca4522bb497f6465a4a5e86fb2f84ca5a7efa88953d95df1f26998": ["lcl.fr.", 0], + "e83704b51aaf54a10d06542ea93788ea36b1f69dcdc1d513ea0d0273173e4759": ["free3d.com.", 0], + "e83b4ee62fbc016f17d31830ff90cb20ee8345624dad43eb335bee06518de53f": ["docer.com.ar.", 0], + "e83eca710d993811f0167455722cf45614ef0f41a37c31739aee4e5d7d1cce16": ["to.", 1], + "e83f490fca491ecd0c09f8a4d89af87352ca346806261f2c83ffaf2e697565cb": ["flashscore.com.", 0], + "e847469fb8f4153ab6d5f6fdfa73c92d23db838430b44012eb7830a1ef2e82b5": ["managewp.com.", 0], + "e84840b256061fc69e8f03e9e99a5e42c74c2e3dbe3308da1ffdc92c6fdf5eb2": ["xhamster.com.", 0], + "e8587e77f84ef1abd254c3e8398fd28198ba4b60b93c68eba121a30dd9126ff3": ["sbrf.ru.", 0], + "e8591871b2802771f6b63a29ca9387eb731531658c64f866788b3072888cd474": ["jewelry.", 1], + "e85fde5db9bf3b1fb0e514255583f69d87009d9fd099dfc9c00e2f219b0c2003": ["open.", 1], + "e86364b98b39cafd6ec51903ebe84c57313b593df13013c76d00803309d1bb81": ["farmers.", 1], + "e86ce4b26b98bb2e38581360eb7d290c454be3e446a8e7c0707a872f5f462d23": ["pet.", 1], + "e86d8065ddaf91606586b88201c1c97e5da0ac20df774afc6006c27228f66b24": ["politie.", 1], + "e877d0a2ec7241dd8437f8e16f1f1ea6f03248ba098c10c131faedd83c2da5ae": ["nextcloud.com.", 0], + "e878a263e1a407afe9c9a932ee715f8617a66357289a0e095c87909698de3137": ["familyhandyman.com.", 0], + "e87b79964147cbd86dca94effcf161b6db4c751978e20570d0829570423e8f89": ["iceland.co.uk.", 0], + "e87d47d9c4faf073707dcbbc961e026f8ea589f2935a55b277c6eb4c706e58c4": ["staples.", 1], + "e88d754ea151b2d6f2ce4d1f6cdcca23b7b33b70c56d2462e0e1b0e713fe2598": ["ntsw.ir.", 0], + "e88f55a9d19bc8ec0e2136facc3bf34d1275a6a40dfb02bb15ccad78a6553547": ["hentaigasm.com.", 0], + "e8919c8590f4bcd5e9130092e20c534619e6c6d0f8e32542df74f065a557a8d3": ["fide.com.", 0], + "e898d5e0d008d6bdce97fdb9bf84d33667ade9dba6d298a0a41b9a262e73f6f0": ["canaltech.com.br.", 0], + "e89d073e00affe4e9bdf04fd915b544e8fba045bb105f509c513071298990c38": ["freeiconspng.com.", 0], + "e8a0f470195869b97cbc011506b0dd99f852cfd36912776c4582441aebd2a0f7": ["mz.", 1], + "e8b0443e3fbff7ec8cd5aa5789bf826fee02621a9c71cfb777363d4e17f17a64": ["unip.br.", 0], + "e8b0e41c84a865f98789a1db1327812d08dd0d805313875d99392443c5a88f23": ["mcgill.ca.", 0], + "e8b74bf0ca5145d8ec37c8c24d85e2d1f069e2112f95799197117a7606589f35": ["saucenao.com.", 0], + "e8bc7f8af61c3fced7f0f2aaaa8f66b6f28f8a1f80b47935b5fd871093875d77": ["wallpapercave.com.", 0], + "e8bca193a3dca0b8658babaf0a1adcbff07e41b0e43eeb2243228cf3d52ffe8d": ["shinden.pl.", 0], + "e8bf9edcee407cdb402ef0030f7dc22b19700a8da8a8a3d36ff6cda9fe8e384f": ["techtudo.com.br.", 0], + "e8c120929bdd4d038cbe9b7805f8a692faaaea366123490400ce9a4bfe8b2c7f": ["cma-cgm.com.", 0], + "e8c17eeabb5b3da511c56e1cdb8e0cea3b4f7c03ca2df7003eef2be938d8c075": ["myfigurecollection.net.", 0], + "e8c75bb6fc40b9f07f538a7ecc9a0af6b347a68dffe68cbdfd8b3387061afe3d": ["srl.", 1], + "e8c9fba74662f0221b92e63f33f0a6fe9826a22fb44084461587981ac270edef": ["org.", 1], + "e8cb7be8374894f1f04d0559957ce6290eb0e053f32a3dd36816a9a653b410ee": ["deals.", 1], + "e8cc8cd6b8f343c3a9272614c413afa96967bbd91cdccfa861a2f9f5a4746f7d": ["court.gov.cn.", 0], + "e8e022e19af9a4bc3faef8250c0de8f7282ab5022eecd0126c87f7379ef2245d": ["linode.com.", 0], + "e8e1b8c1f89273641251c76cc8ed27301ca042f1bb375943e9022125119dcc88": ["tianqi.com.", 0], + "e8eb4d46cd7848821f938083e9dc042b378b454d27a0a86ffec370d477c8f309": ["turkiye.gov.tr.", 0], + "e8f203cd15e9ec3b81e6d3bf2bff630cec615de90f391e9b3a286badac6bd9fa": ["santanderbank.com.", 0], + "e8f24372566563c81fa9d3571c7e36c04093020891301d441abb99f1d46f5016": ["ieee.", 1], + "e8f2755869ec61aa51336fc5322d1bced1ea9202f140e774f681a427810096e9": ["24ur.com.", 0], + "e8f54bd9306ae3bf48c15652da1561af3282d139409bf830eeb11d005ffdf60f": ["jstor.org.", 0], + "e8f7573dce524ee1dcf322acf41fe375e1ac84a1367ec3f377ebd8000910bd44": ["javatpoint.com.", 0], + "e8f92edc5bef183159818b9e77ef441682d6622203c97b6e8c0d98981c479e32": ["iciba.com.", 0], + "e8feab02c90cb5b204ed82782472dee49bb1561183ce5a0a58eac1fa265bdf60": ["9gag.com.", 0], + "e909e7ee019412fe164f0b33164aa5cb8aaf1c5e85f14166b9b4b23f5caef1dc": ["i-like-seen.com.", 0], + "e90cbcebf102a3ba4e36581e80daf7ff097b4e34023e9de57b83a352f7e259e0": ["socar.az.", 0], + "e90d7ca64f800f58b16bd78df46cebff8c02fdbe8d4d5183e82609090b505913": ["annauniv.edu.", 0], + "e912ba79de7d083353a3f50f08a69d16a01b8bb56708530cd1c51e2ed7e46bb2": ["karapaia.com.", 0], + "e9133354b6dcc3adc3157e082b2ca81f889ff46f31c8dbb0fb4fae8d80156b43": ["tinkercad.com.", 0], + "e914c57572c7650ce5451fa99c551fb4143757c3b2a6510e62d31131eb5820a0": ["jooble.org.", 0], + "e91bd5577e5d89e42efc3c4512370eeb73cec59aa9cd3171163d254857fa2709": ["cnn.com.", 0], + "e91fcfa738e1b596d503007aa2cfb5880ff040b86b7508ea2dc9f82354b6340e": ["koreanturk.com.", 0], + "e920feca1522134a4bf4cdf8f72cd74059c08365b9f3bc592d99f956196eef81": ["yout.com.", 0], + "e923649bd6b1739ae8a4151de9917da37816a2d45312ae027eed859aed129766": ["toplearn.com.", 0], + "e923afc9d176489edb088d76a54451ace9f14406d2d796df60ae798a418b31ff": ["psnprofiles.com.", 0], + "e92acfb1c055cd75acfe5a703e936149ef6137a7d4cb736f646c96f104d859ed": ["vbox7.com.", 0], + "e930e7b9568ccc2b39aab6586985c74644e101a0ecba02c918c82c5e237f111e": ["mega.nz.", 0], + "e934de99d0154486f65e472883cf74310cd6c146c0c1a040ee79fc88c0fd284b": ["aftenposten.no.", 0], + "e93ee50d428e11d4377d402ec10ccab11c18f8969f1fa098e5eec0b200cb5ddf": ["revolut.com.", 0], + "e93f8dc29df4b29453b1625e3287c6e66f795e5dd41a8de2a85f6bad7242b5da": ["p-bandai.jp.", 0], + "e9428c9d12328232e55112b5ed1b0f306d9ecd423f53405a39799d367bcdc72f": ["solaredge.com.", 0], + "e947c1cfacea4042613d9f27bfbc833ac0b15bfe06fcf87b5c2ecd9dfe946fab": ["backlinko.com.", 0], + "e9486924af41dee31742c5d2a44be506058ab9d750cc7cc38d07ee91a6c44609": ["bloomberg.", 1], + "e94871353aacc91fa444f48c23d2f679257a792f198a785f95ef6ddcf1e0475e": ["gmail.", 1], + "e94af047d2a6ce850bdf3310081492c3fda59bc7610f64b212a73b0ccf1c225d": ["sfacg.com.", 0], + "e94e63cb00ab4d9db51e1643a90d7bff1e5b16bdece80369d52b543cc14695b4": ["denofgeek.com.", 0], + "e94f4684fe5c2788004d05483b54af93ce3b2a71b7b9c3bf27ff8f39c4c30e50": ["cam.", 1], + "e95df672500f086e6f40d0ecee2af6d44484fdb1653a588de8050c9b00cc809e": ["cleverpdf.com.", 0], + "e96f8cc9cdbfaa77caf2a868c20be86dc90283a24d45716fdb6ec97f1d05b4c9": ["aimp.ru.", 0], + "e97272aa9bdba6c2640ffd95ccb8e98772e2c818b262c414889fe1766ebe41f5": ["ssg.com.", 0], + "e97364f2f968554a727c9791c83def114fd969a03ae2d0218020f93cc2efc2b3": ["goodreads.com.", 0], + "e97960730d8be733bfb5b5807517ba597d63f59ce31fea9f96db060bafc39544": ["lyst.com.", 0], + "e97ad2851c5a640fbaa61fa38effdb4710e977d55fd1ddc1b51ab9642e2d8645": ["xn--czru2d.", 1], + "e97c821f1d37c1463ba6adc12d4e2954f26aec286c3a5717983bf2e60396955c": ["mls.", 1], + "e97de36370ea97a69314ec100f5723b7074a80e4abc2b8a2138d48ccb0a33ad6": ["bazar.bg.", 0], + "e98470b273beacb588c3776ee1449c92e301d54f85a2319bad586f89ba1981fd": ["beijing.gov.cn.", 0], + "e98fe5e18686b5ca6b74443c556dea8e544461c1a1f190ffc78a794e9cbee84a": ["pm.", 1], + "e990326a68f14fb9167efbf9866d60c5f7da58b525634ed924110e1041d9eb4e": ["anwap.tube.", 0], + "e9a3560858495f444457368eece53cdf4c53db2205984f2e039b883724e53700": ["clickaval.com.", 0], + "e9ad94e5e0c1154fcf4bdcbda8ceab607a6862a283ba44f29361cdfcd6c4a687": ["iitd.ac.in.", 0], + "e9adec0ba71293344b1caf4a453f15bf7734c108f5b8bfc055818b29e860375d": ["men.", 1], + "e9afcf0d6ea72c7942e6003eb64a83049a529e536adb8bf7ea2d7bafa50a960a": ["rarbgmirror.org.", 0], + "e9b290109560dceac27470bd767692e0c1ad98d074d367aa19d831803a9f8e96": ["seneweb.com.", 0], + "e9b78f5f88c7132b3637e82ea89efd859b8692042d023eeecc0bc7df7bee1629": ["epfindia.gov.in.", 0], + "e9bbc7b86ae1b32c7cf3c3023417c488817252bd6e7977b089f424ece3dca2f4": ["zara.", 1], + "e9bc70a0b902352404419a5538e7fd7df6e25ea91631b7551d4ad273485ee02d": ["gift.", 1], + "e9bcf21850361af7553158d92215934ba8384bd18d6f127485e49e46a7582e02": ["unity.com.", 0], + "e9bd00b1fa93f25769c77639a220537ea2152f52985863ebc7fedd51a0964693": ["loksatta.com.", 0], + "e9c915b0acff49818c85e116a80c117e9bcb31e7847f311067e420b1e97c8323": ["gizmochina.com.", 0], + "e9e222b5e2a4628d8691d4145b0e6bce59919ac19cd055e5fc73f9ce8bf268bb": ["edpuzzle.com.", 0], + "e9e7c26b66f4df1b8a4ec3c4f704530472a649738e674d24fe9f99a81684eaab": ["ruoyi.vip.", 0], + "e9eda428010c47f69911db9339ffa5845ffa01e634b4dfdf83b90aa667d87260": ["hentai.tv.", 0], + "e9f39614800e1db955cfcdb8f2785e255bcb02f6c30f61d1f6165f8ca32870d7": ["schwarz.", 1], + "e9f6ea566e1ed3b07513e60ef565791580b98e62b6ac8531c122f40dd4bd1cee": ["femdom-joi.com.", 0], + "e9f77663c376bb1399f47d373bd06d35f9641256b78fbd33d546d958c5f1a789": ["letras.com.", 0], + "e9f878df017d3ce536c06af3fc5a998b8780864b9b46c23e63df572ca31ccb8a": ["mimecast.com.", 0], + "ea04ecac60e6db4f9de09de7b5d4b292a330cfb8858747106a462f6879ab6ee8": ["tapatalk.com.", 0], + "ea09950e580667e0e7e7f0d77ea2594ec0f8cae531bbed60295b9cf2fd286a37": ["katfile.com.", 0], + "ea11763494a6e7eec9aff1b400010249355695a9aa21eff266c33ec40d5f70fe": ["myworkdayjobs.com.", 0], + "ea18973d20d405b97ea0917e54bcb0d457cbcb928e43e309d7942844b3860c33": ["cpmlink.net.", 0], + "ea18fb828a80e4a26eceb9339e22ab075cf2f93f618d5ddb72e9c75b29ec260a": ["infor.com.", 0], + "ea1af13846a941ac3c5c665fdc5d73f995c210f7280de984757204e78327c0a2": ["gematsu.com.", 0], + "ea1c6a7c8f32aa0becabfc40d9dc47143f00ec02d785d2e97e752160b52f3b28": ["nolo.com.", 0], + "ea1e31f6e1ac42951a6d3b82782ed0c56872f0f06a39b231d226ba2fd7b2c5e5": ["sportskeeda.com.", 0], + "ea211665cc16225c7e6b84808bd33c21ff73ebf0fe48642e911fb0928b6df151": ["opodo.co.uk.", 0], + "ea224d7c84ed37ffa2f3e0f482d4d32828d2b79046cbb306b146d4e6bc2ec026": ["lumosity.com.", 0], + "ea2754fe45aadd96444e8514354aab3131e9fb83580551329e1d6da570f0128e": ["itemmania.com.", 0], + "ea2d1ca4fd6d94fd4ad055528ca6aafebaf2fb082dd36115026c949c64626eea": ["clipart-library.com.", 0], + "ea3688b7b2160decde4f951b7134355b393464b7a74cb8210f39748931475042": ["telegraf.rs.", 0], + "ea4285f278e52a7734f740a34ee4562eac70852bbc56db60a0ed778510bcb996": ["iranjib.ir.", 0], + "ea4435b3e950b955e31fd4ef0d733a451105248248c901545e64a489f2940595": ["newcger.com.", 0], + "ea470eeb9ff0b3f08c962bf9869d17a2de1a823be049a7412398e8c430c5d241": ["theporndude.com.", 0], + "ea48bd93a268e22452785fed94d963867730cdecd55479e3485a25a2d2bed5ed": ["washingtonexaminer.com.", 0], + "ea4ada012e68a1d2c1de873a9df4f9bff52cb80572534516153fa457b7334fcb": ["xero.com.", 0], + "ea4d2e9ce6db9dd9dd2490e6f3388b91863665864cc9ef5ccb6f48b1bfaa91d7": ["kikinote.net.", 0], + "ea535dfaaa0b5a17f5334493c4d8c6f8d3bab37517e0b3ab8b5c9d6f754f5ab0": ["nuwber.com.", 0], + "ea57bc338a0e9abd27a41a025ef1d06f2793453604b1f90fc90097bad0d65537": ["xn--45br5cyl.", 1], + "ea58a79fc734cd2f1dbfba5b5a3c83927e9778f8f671bbaa61b93ae8f725e8a2": ["archlinux.org.", 0], + "ea5912309576548a14d8bf07e51508b743de8172c737e4198ca455f4c9566cf9": ["zougla.gr.", 0], + "ea5cfbc6e0921afbf7de768f3d1fde24807c71dfa1538e4dff74e982131cbee5": ["reverso.net.", 0], + "ea5e592d75c8e539f543a3d5621f90be192547b85d4fe7a825937312493d0420": ["thedrive.com.", 0], + "ea6414474047b7bbf8cfb9bfc0a6daf35fdb09daad6cff14f70679e213688831": ["kosher.", 1], + "ea672af56f13910d27e50f52fb6c78f22f29a642298042ff99717558814e767c": ["napaonline.com.", 0], + "ea67cf2d7e24f7987615819e01f5a7beb088eb6b4afd3df35468127839e367a1": ["feedly.com.", 0], + "ea6ed0d2463b0144863c4cee63303072e2987fde991e9f13cacedc0385f39150": ["ksu.edu.sa.", 0], + "ea7077140b2f029b6a98f04e3d61c9d5cbe4aff969df225a5ccc3ac43e4893c5": ["geniussis.com.", 0], + "ea73b48363fdeee9b7a0ea930b11e9eefd6a09ccad891824c085ba6896ed1dc0": ["91huayi.com.", 0], + "ea7421186a6db6927166c74b9de3133ae8697517782d0325842117ea8eabce41": ["hamburg.", 1], + "ea7c777b2e274b5bfedc6889c1d1dec2ccb9498a0df57f6eb70c1d9f6ffe6032": ["musimundo.com.", 0], + "ea80b1461ee1cfac83be5c71703ec3c816e156f899f5d01b0cd5a3dad8af9162": ["ontvtime.ru.", 0], + "ea821088b72047a89e1defa25f91194760493a6c2070a289fa8c9a73c6428861": ["torrentfreak.com.", 0], + "ea91cd9911698fbebcf763f6dbf36f80a66e22b3cf541054895a0adffa2271fc": ["vidoza.net.", 0], + "ea927c43b6eb359a8f1b9487d70c301680854112d3d34ffd28a3139337645feb": ["banki.ru.", 0], + "ea93aa6a297510d7e3d23e6c09ca1d207627691a6d4d580acd8159a727369f85": ["airforce.", 1], + "ea9708d5b4330b9c25e33b484329c0962090970033b281a5ac32bdab98827f30": ["sexygirlspics.com.", 0], + "ea999be35bea819e7fd823ec5c421b20eb1bad4d439e33f8cb98127b1ad54290": ["smore.com.", 0], + "ea9e503e7647e9b8b5e4d6891fb7b37b3b1167030708c55a5a46951d19b73c79": ["sensacine.com.", 0], + "eaa5dc28dcf5a69894eba7b313707b05775e1f96e20d8d71dd7e75dcd335d7ee": ["sonyliv.com.", 0], + "eaae61cee50897eb0b7a6861c156501e963b2cbb489f59bfe214884fa6e72376": ["mydigit.cn.", 0], + "eaaede59f6a1a5d4e914ac49f40b5f18d2d9622596872f58b4a82456ab9d6896": ["truity.com.", 0], + "eaba122c19cbfac099730b93836f9002e1df18971cbc2311cfab8ec2c5a1174d": ["gestion.pe.", 0], + "eac2bda9cdaf2794231c313465e91a055cb39233ac5ae3d54ab1b9ede3efc38c": ["glaz.tv.", 0], + "eac7644de2c3765e3b8fe6ea09e968d793fb4e92223dc4de36234d38129ecbdf": ["mof.gov.cn.", 0], + "ead39f31299dd7a17843bad1119fa19121ed711ede7bc1a59ec077da1f1eaad3": ["theaa.com.", 0], + "ead92be270fbf2c565d8d72276dc374d3513c3735660e991c92d99e56ab21732": ["tn.", 1], + "eadafc2380c973af8ffe4e22c6678d60a9868a64f0a27366cf61c07022e3b80f": ["modelmayhem.com.", 0], + "eadc33b5bc591280af8a360084fc02e605443e0554bb90eab52b2e1a4a21b868": ["amiami.jp.", 0], + "eaddc3654e71d4266eeeb0ba3fbb3996948e23c3b2cba2ef1eca4e65be27a165": ["free-css.com.", 0], + "eae7d9f60ef555d6d3b9ff4caa68b93b716a355983c56611cbd5dca4325047f3": ["inosmi.ru.", 0], + "eaf1229641a3e8c9091352ffe058f0836b0c27893782078234a583b0649b42fa": ["eu.", 1], + "eaf7bca27b5fee5a87492e0c92fc989f62156301f5999fa18fdf638055ecd94a": ["espinof.com.", 0], + "eaff1c07bb6566c6dd4cc62435b88d1919c16a9eec70b5a14735ae40e4635ba5": ["yodobashi.", 1], + "eb035464e92427e1b2f027cd7419ba15db4697f93632433e8376bc2c6faa67ac": ["rei.com.", 0], + "eb03687b75749f50dc368946860642f928e14e00aa80ff438ed0105f3a608bcd": ["nokia.", 1], + "eb06b9114e91919eceb346961882c0e26c0033c0249907495d105afff77e49b7": ["maybank2u.com.my.", 0], + "eb079d76edccb58f4b9164fc2ed4ff69e8a0f6ef41bf59f1aa9d71c1794c990f": ["moh.gov.sa.", 0], + "eb0a4d926455433a930506bae587e551c8434ccd1e412e5106dd9178f47b98b0": ["mrooms.net.", 0], + "eb0afc448e09f83e94145f9d09118f7e44ab4428b68754e8621781d291557f17": ["ourtime.com.", 0], + "eb0cb7a61a0b266a8cd181e02b77bc251948d08283f714cca5b0a70565b0dec1": ["bz-berlin.de.", 0], + "eb1013bb8d1dd3927f64bf3956db6f01d4864c5fb499182e1574d183af725c8d": ["shenchuang.com.", 0], + "eb157e2449eb78b489a988ee266730a97211aaf15c6496fadb6112e45d9b0d42": ["tubebuddy.com.", 0], + "eb18abdeeec008e6ea539e7756c7288e87fdc7cbea179d05948c127cfb2e8b82": ["sendowl.com.", 0], + "eb1e1d8e6f56444b0689ad293f01220737da7ae4c6fb32e2e94f8d995826f032": ["oanda.com.", 0], + "eb1fe1a1378d290f98a78d01a47582e2323e51b54234cfda5f16d5b9a458a9a7": ["thebase.in.", 0], + "eb216d985cf2b71e57b55cc82ac39496ee71536359575dad5ec0d13ae010ecf2": ["practicefusion.com.", 0], + "eb2750ec5c7affd3cfd73f372ab6331030f7f73c49a66fba4d3242c35effc0a9": ["pcgamingwiki.com.", 0], + "eb292447188712530a07224772e31b93c532882131ecb3cda5b91153a8b7460e": ["usccb.org.", 0], + "eb2a8bcb4f7ac64dc3dfcd09dccf713eb7a512ac432c4160da31cedb5bb8d61d": ["foodnetwork.com.", 0], + "eb2cf20560e8d56c48e11374add57fd061cd8e8cda755100c8619fb8e93d7810": ["viber.com.", 0], + "eb344be812df636002edc5a9b0409a5f5751c7b000d44bb75d8ebc056305a9be": ["daad.de.", 0], + "eb34735da5cdf178b4094f09648d0fd43d6cdcb450faea159c4037e471ee7d37": ["preply.com.", 0], + "eb43a4da9c750b214a3ea1def77fefbc029756c2ac420412d1beb3a1e9885d0c": ["changyou.com.", 0], + "eb47202ef29960a1a2d6f539ffbcc45b1af11a6f9e9023fcef844d32bf2d2d5c": ["tuttosport.com.", 0], + "eb50f1c42d1b1d5fcb053daa931c778146e6bac07769e25a138bb5bc2e0d6962": ["adsoftheworld.com.", 0], + "eb5366bb6dc4f4d7cf1f3067a5ac4aa1c64c2faefab404afaf4da8f4e3777a0d": ["telmex.com.", 0], + "eb547c9c4cff65a348637767f581057ad57227f4f09d7ca1c11e30c925deffbf": ["kongzhong.com.", 0], + "eb56e9a4f38323ae6957f149f3586575c7be2c4ebb1be4a9160bda5b14045881": ["superbet.ro.", 0], + "eb5af3fed413ea0bf01916ad8276de158cf28e60ee0f6e3e84ec1891b7b308ac": ["report.", 1], + "eb64ac4f26039a0175100f1b92140aa67425ee327000b8aa3d08814668802388": ["t7meel.top.", 0], + "eb6619ad799c44cce722af05ec3a550005d7831e2b202b240f0d52f96e522321": ["bleacherreport.com.", 0], + "eb69492285330d01bdb8c386602abf8ec0020dba015b7f2d3c1fc50b207c818e": ["place.", 1], + "eb6a1c6a9ba33fd8caf5cc77493113fb679d88790edbe471221b50fb8ec2805e": ["xn--nqv7f.", 1], + "eb7aecc17eb165f5b1116de6c63464a5a72d891cf19a2937d80400488f8ab40a": ["mathworks.com.", 0], + "eb7e216fc2f4286c57ecc3df91af04ecf5ade690dce1bb782f446458e240a479": ["zhujiceping.com.", 0], + "eb830690d5f3a51f7e8fcbcf61ece354129ab4e1fb90586a25ee2bbc19b2155f": ["nurumayu.net.", 0], + "eb919e96294de442a3e7407ffc427be564fd5e508292fdc389715a4b07ce0626": ["buct.edu.cn.", 0], + "eb9f17725950e9992d17a09f7c89311e8c7bab458f89a440a24fe77eab0996ad": ["stlfinder.com.", 0], + "eba445defb3f82c4912de91186a9800fad799d433b6d1987ddc173c94196f2fc": ["lenta.ru.", 0], + "ebae499cdd0e7ec3c55edab151cae6d0fa9cc38da8cce7ad1301aa0f2f642185": ["headphoneclub.com.", 0], + "ebb6684bf216f5c1f6ffb871cd2293cd67a9bb27e1db469037b3fd268a232b9d": ["tv002.com.", 0], + "ebe259ca4cbaaca11f22b53458c0adc437c1e7b39fba38f9a367b1befb008a69": ["pioneerdj.com.", 0], + "ebe8bda432c9750fa265ad0568363ce07eb91a286e5fb68073681e6244a457c6": ["simpleimageresizer.com.", 0], + "ebeb449b8bd3a94492f4177e737ae018408062ad38ed6882eb3c8508ec76eef4": ["businessoffashion.com.", 0], + "ebffc75b02cb8118e9cc7bbf2d9d75c6de84f0763d075bb1ed507d0bf843df0e": ["gazzetta.it.", 0], + "ec015b32af41b00a0af1d7c2e03eaae71e0ab4d3eaf7594ba9c9f43dae150ff9": ["whatismyip.li.", 0], + "ec0c1d164ecbb28849f115ac5653b35d33aae4485e3af2cd4cac3b4d112b7668": ["mediamarkt.de.", 0], + "ec1285df4eb89b2cf49e2a788c74ed2b67d8e9a655fdf62f1ea72332fed4cf80": ["streamlabs.com.", 0], + "ec141517996ecfc533422ec0bc294c7600637e47c5b1b5a62fd9a2849f9febbf": ["datatables.net.", 0], + "ec16205a854dd40b857eebc6c751507ad7f39de1efc35f86b149240197782626": ["sreality.cz.", 0], + "ec1b4515fd43aa1ee8c913052d784961b86b2711ae2fec1928863b379b33610d": ["mashreqbank.com.", 0], + "ec273df8c64d7365287c14f98e2013676da70d7a885f436adb7cf096b80bcfdd": ["openstreetmap.org.", 0], + "ec2c5a40195a33cf6da594fb33b7a8389f50cba69b6857fb991307196a5e541c": ["tudocelular.com.", 0], + "ec39ccecc207602e11052c25c35834902627399c830c611558fd3bc4a998aaab": ["battlenet.com.cn.", 0], + "ec3a533d4a1c8f624551ed4caa8bb3cbf936cc12f3561512c96be9e8cfc6d71b": ["javopen.co.", 0], + "ec45cd616414392eac6d78491df5aba5e1956eea2f3ad60c2af49202887c7421": ["hjd2048.com.", 0], + "ec4b4ab46326eb393399893e6f21203c161f4a8707a9a481ca08cc78aa87060f": ["timedoctor.com.", 0], + "ec58478e0c64168b42a4877436cfafba57efde7222d5d52ee54898647b3ea89b": ["soccer.", 1], + "ec5b2e18b8e561fc5e6b39b7163b6bcf687ffdeef2718c3a104882073f15a3c3": ["simplypsychology.org.", 0], + "ec6589f25a59a4eec0bd49bf00aa26af1bf0bbe6f5ea45aeea157942b27d23fe": ["correos.es.", 0], + "ec65fde39f08704b65e9b3a9ad2c48311b8ad2c38103f42bf4f39c2f9bd566d4": ["picsart.com.", 0], + "ec6f30f33e9ecd6f090ba0d77c129da756a337280d1ea3722d1f95584620b56f": ["springer.com.", 0], + "ec6ff324ff360ce37e8170584b4f2725c19bf8ed696eb420c3db2b4d8c2bbea6": ["colorhunt.co.", 0], + "ec74be1a68253ab757bdf82396615acc95ab32393eb3e89106df97bcae2db0ce": ["ncert.nic.in.", 0], + "ec84935297413227d73351aef75d1fb25da668b7c477414d82c2d4e91d6362c3": ["universiteitleiden.nl.", 0], + "ec8fe6bdd2175d06b5b39bf30bbba490392bb89506c8ad0487cc99ecef2e7a4b": ["xn--90adear.xn--p1ai.", 0], + "ec97cee60029db4c015c0397a8ba7fb868f25c78edfa2b599ccc9ad4043dcde2": ["huorong.cn.", 0], + "ec9b92b7836661e9bf21180a7e2e996927cb669e12bbf8fb50a6efa612848a8d": ["musixmatch.com.", 0], + "ecb0bc5cbedaf4f7418ceb3fa9e8b17435fb7ca4beffd41f03348cf38e1316d6": ["hdfull.life.", 0], + "ecb7d4dcd051ce590c278972fe67aba1271e8a3e594b0d55acd1de919314b261": ["kvbin.com.", 0], + "ecb8f8c27c40ebef964e6f033f871de55b9473ad3c1354dd68e5abc781df402b": ["nettavisen.no.", 0], + "ecbc42fcf874eaef1eb35a74b6b7159dfc5bd1d171077d25d87e13be4a49f151": ["li.", 1], + "ecc41492a38e2112d4bf9b9530e12fa9f682dc3966e13a78199912a9064b6746": ["hisamitsu.", 1], + "ecc7bbf5be5bf3da79507cb5fca58639bde195470ff8c1166d3aa45ad99ba157": ["profesia.sk.", 0], + "ecc9ca6f5c5f5f22a26859bc33f5d6455c5280d63664b9b7e5216e0db5130662": ["ana.co.jp.", 0], + "eccbde88ccebf8c04a5edcaa5cc2043a85a4e0e18419615d3c58464c0d7625c4": ["cmegroup.com.", 0], + "eccd6d905b7c7ff25a0699e388260bd9e1049dcdb4ef13d80cd65d97f137eceb": ["estantevirtual.com.br.", 0], + "ecd1e4d6194f46b8a60dcf6e6e2a0f7cae8b32cacf6888841e934184bfb9ab02": ["tiffany.com.", 0], + "ecda48b5980959ea5aaf3a9fbb0b80e5b0d230366c36be3be4e804c6f441a892": ["meshok.net.", 0], + "ece4aa52ebfe25d804bb9e34c309cec512cfc391d54181cab470859c91388f74": ["pornoeggs.com.", 0], + "ece57d3e9af5c9a91f0803cbde1dd47bedea5cee9d21751a8abb87655aa60f5b": ["jll.", 1], + "ecf15db4fe289d6179a67d88338be49224c15e1f49a1bf801c6744ef00059c74": ["mn.", 1], + "ecf29a55c7bbed9b501c720500e207842aace42b7cfbbe7768aedb3a74cd0039": ["kohls.com.", 0], + "ecf9e374cb46e7779b5876ae3be7cb9fcb39e3f340ffbc7389f657ef41af6098": ["sdarot.buzz.", 0], + "ed04db72885a06922c59a179d6bb6cd7d4576d1b4bbf71cc7c5b7ee712bad20f": ["akindo-sushiro.co.jp.", 0], + "ed1300a0711253f1f9f54081628a781403c9d0ea10c56e80cf888e48d96bda94": ["jd.com.", 0], + "ed142ad5a9e5c05a4eb12e1b3eab9652dba4e0b2d9b437029c628ebb411274e1": ["soha.vn.", 0], + "ed24a378503c1d999539ba4361260be3cdf894b172c80c9cf640462a67628bd0": ["xn--c1avg.", 1], + "ed2ae2b6d5244a35da671198a45db8c094b3ccb7dc78197e441f0faa5a1c0266": ["nhs.uk.", 0], + "ed33136582c048b190d150b925153332349cbb4a6a72bc51df130a00021d6616": ["appdynamics.com.", 0], + "ed4172e3a6f6d85d0620c335ad58ad96cabefa8189b6f4192a29113860a0efb2": ["hdporncomics.com.", 0], + "ed475f8e40e77508d74a2193c9f815758520ff1a9b39068a2d4468e7d6b5e978": ["justia.com.", 0], + "ed53c86fabec5a285c9073e217175cab3f9644281053c5353f98c7905e0d7b43": ["bandcamp.com.", 0], + "ed5a21b668f1b690657720ad1c77c6cade7935773b8b871b8239410a40b76b60": ["mafengwo.cn.", 0], + "ed5d3075d2c44810df2f6fbc543ddf6bfe19ac7280e06ead68f1bdf50f21f38c": ["5278.cc.", 0], + "ed5f6bcb470ec0cd9d0e0279c5f0ca59c66447d7f076d986236734ba4b250d84": ["akbartravels.com.", 0], + "ed701757943b34f4497fb90c8a4c893130986e73a97ebb8a15c7bbef470d8617": ["agilixbuzz.com.", 0], + "ed89c8463db4011ab18dc230bf7ad65c9c16ec18b66f451d65b1aec7ebda5df2": ["ria.ru.", 0], + "ed8c368db79b5a434f2a0e607479ac907b9475aa34a8f98a25f2e5d94c6560fd": ["faradars.org.", 0], + "ed90c20ca028e045927912be18ebf02a0c9b408c1a652dbdbdedb56da99da820": ["ismaili.", 1], + "ed95284ec8930fca03fd12917d208df91ec849e508e1e9c5bf435514b5a79fdd": ["gla.ac.uk.", 0], + "ed9e36fa06a282d0572283f9332e1008e159ec1d234e55fc4316c7fa0f3f30d2": ["smile.", 1], + "eda1ac915d5de397f7b1ae89c3700c5b89f9104dc62d19a6aab27e21d1eedfdf": ["filmin.es.", 0], + "eda54b60cb12a8b416bf1b0faf9a8721a84defcbe6fef9e0963e81d652a9e6cf": ["consumerfinance.gov.", 0], + "edb8877f08fdec9b5e0c16ddf33ad37e6fd8fd7e5a99bd92db76fd4f74a73d61": ["cronista.com.", 0], + "edbd56eb006ef087fa9b9068ba9188bfc6978b21841c67dfd99af908f122fd85": ["codecguide.com.", 0], + "edbf32300328d9537479ff7a152cdfd03b4961ecd6eadf4af2a5fc4b19bc70ab": ["gaosan.com.", 0], + "edc6393af2a8ddaa807da099a6e4db0f8e19846ddb0b4fff824d19e623bd7c99": ["chat.", 1], + "edc7234ae0d9ebfadae2b0b3adbb21572b29638c28d70340755fd25245544ce0": ["jkanime.net.", 0], + "edce6a6cc1159436b25f09534460bda1ba19e890d4bfe153c125b8b9bc745cff": ["duke-energy.com.", 0], + "edd45a4d9a6478a4c2672b695f58e9e34e0c4d1f9548ad92f39e4034d49592a5": ["timeslive.co.za.", 0], + "ede01683e5e161a505153dd9359400f428d13dc47b1e04ba278147d58e767598": ["fiverr.com.", 0], + "ede209198fc632231607212e740453e66737d60c9d8dac01c18c1a0f48798cc3": ["biblegateway.com.", 0], + "ede6d4f593d762cb0b5a9573e7095705946daba82756467d2e3ffac3a16ee44a": ["klarna.com.", 0], + "ededd2b321442cb2786c37f485a0fc6ed73705c780d05f859a7baa8c57f767f2": ["xn--ngbrx.", 1], + "edf3d43471b0a354324b815c2707e4b18e5243d232b51cca40751c5d126e6d79": ["mt.", 1], + "edfb9e9678189b0cae9f7b576442632c343baed50527ede41e7fc6f612218b49": ["chevrolet.com.", 0], + "edfd9d82fc6d0ec29e9f69194fea038edadd07329b17ae8b16cad06732529999": ["freshservice.com.", 0], + "ee00bc460445f3114eec5974d90ffca1468c281252dec69131ae805fb31f8938": ["tm.", 1], + "ee032559b0bd0f507011c603013d3dc2142cc72eff75d9f95582d587c4791e2c": ["sorozatbarat.club.", 0], + "ee054b97e98733d51bd7135c0d42294386810cdb1f71cb6675c3f123d7e6bc5f": ["proff.no.", 0], + "ee05f6797bcda1a6b9d1a966d78097911842f0ffe9ef25fcd3aee01cdf3f4b48": ["k2s.cc.", 0], + "ee062db57ea6815b5de4ddbc4909b30b0351e4c86bc366ad869cc28a5a44cbdd": ["mckinsey.", 1], + "ee082328035d9bf76d89e3bd11ee75170c15c2cd51269e278dd57470ee7cb549": ["cambridgeenglish.org.", 0], + "ee08cee9619593cb3387a6ce869348279d027bd5c54e51221436623fc47bf15d": ["ashemaletube.com.", 0], + "ee0b1b3c42aebbc21107b7093f5bdbf253a3e4f2c013c3f7e2ce81d0066150ac": ["wistia.com.", 0], + "ee0b1f6e4cf8f01a5f0c24ca2b1628c2e215777a408998462d169a53130dd267": ["pricecharting.com.", 0], + "ee0e585fb8755b614f4166179c1fbc775347b8af17a590a513028b5ac2c9f7d8": ["cian.ru.", 0], + "ee163a148ae44fbbf414c3f0d0eed7e99e83095fca2b09064c969bade8996ea5": ["aboluowang.com.", 0], + "ee1cb62cf1ebeac734ddac771e92dbb20c6f61dec020dea24d6a896c3a47f461": ["helloasso.com.", 0], + "ee214512d8cee33083284376bf8806724644a2dd6f0d2c8489ec5f05f9ba155a": ["onlinetrade.ru.", 0], + "ee21f459f24bcc5883dd271a3bdd036300a9ac9cf468e1ce65f1f9541493da72": ["sbtjapan.com.", 0], + "ee260a1f5f44b5db411af70e5dcc8404c4445acc1dc77edb8f53e895227a9628": ["wikifeet.com.", 0], + "ee2673ab0fff1f855afe864e6ca6b8ef4770c888c1e2037d41c89ea82797481f": ["giffgaff.com.", 0], + "ee3bd805652d68227b91ca52d1abbbdef1592b331d4e08c997e28f2a53051c94": ["reise.", 1], + "ee3e5cea36fd092ef9ee63dd3dacf581e9fa2f4e0b608d464683c5a24fc124a7": ["onenote.com.", 0], + "ee3f4a30a2b67d597863d11f6f1f00a272ec424ca89cc5460b0bf08454a7e99b": ["univ-lille.fr.", 0], + "ee438af7a6033f9b350a3f8986ef144dd0a5f163b941f3d1eecfc4c099c762be": ["vaporware.vaporwa.re.", 0], + "ee470fc9db5d364f8549b7e04b6e4ba90e78b6601bc63c806d34f914b983acc3": ["va.", 1], + "ee50182606303e93bee4eff94824cb189935bfd4e26bd62c64b56a1ebfcb0604": ["javbus.red.", 0], + "ee5951d4203a6b5202d7742b305e3aeacc383d9dd0fecbeee237d26022ee8c29": ["wfaa.com.", 0], + "ee5dc21aa405483eb523b1d3d80948d1fa640c62a35f13905c69891c2571eab9": ["smartasset.com.", 0], + "ee5f85e812c469a4bf29b8e54626968442ddd98ca794e84b4aef1245e1f214f8": ["tatar.", 1], + "ee622441a592d29075bdc9ce39fed18428e65c783f0bcf8aab2af7511adf8397": ["starbucks.com.", 0], + "ee6797260df493a008231f9d54d74c0b4781696b6e382f18eb5e86ea6ac850ee": ["thrillophilia.com.", 0], + "ee6c625721cc3ceeefb5c7cb91735d0a84f937c96a4631d751bda5f9bd887ff8": ["joj.sk.", 0], + "ee7633e662c14282c45cbd739171c02c29602ce7a98c2a4d143ba3fdbd15f4ec": ["pydata.org.", 0], + "ee79bacdf95509a96fa42d79bd8432ae7280b45847497664f64f0150cb628b45": ["trf1.jus.br.", 0], + "ee7a4d18a1b7504f01acd3ab1c35a04578204e43b5b8e1cd480fc0b41f9b4d2f": ["sendibt2.com.", 0], + "ee82283603ba849205d4c258e72280d0d9b19cadeae7cd607830e96961c0d766": ["shouji.", 1], + "ee8f2b2ed55955d3ae676f4e5f421b6a39ea412e7fa63bddee77e3333b589ac5": ["natwest.com.", 0], + "ee90d5e7cc98574c75027eb83945f25293ca31aff30a56c99781cf00ecb7a26f": ["rehab.", 1], + "ee985a0dc040d87ab3f2dbcdc4b2ce2ac8e45e32147bf716af8864c641b20e06": ["ashampoo.com.", 0], + "ee9c269f54c049e07f81e56ba34f4eac290b129834afed72d50f774c32bfed7a": ["breakingnewsenglish.com.", 0], + "ee9f395d9ca393833866cd82d4bbb732da6036124a3dbe234dba1174187b037f": ["cifnews.com.", 0], + "ee9ff37487d5595a8c15cd8ac4c53a92050ff6160b9124c2ac2ec0bf73ae4d09": ["forhertube.com.", 0], + "eea1ef1d0a3f14ea693d6d8d21818d51b54ba3f7981ac3e76ee5459fbb678e15": ["coxautoinc.com.", 0], + "eea44bc2560ecbd36239eae85b9d813fe7f0d36b0715e52d0a60dea4e1fd6608": ["hexun.com.", 0], + "eea63689b262b4e8ba9a3118c25ef5bece6c5048cd63bf8a9b5a54b01ba4cd01": ["ziraatbank.com.tr.", 0], + "eeace9cb9ef82f4f228ae5531634684bf7e2d9fc20d575599ee06fbaabbe7ceb": ["unity3d.com.", 0], + "eebdae6b8bf103dfa14d6887bda67b0237033b0c4de25021786953e6e2a03417": ["haveibeenpwned.com.", 0], + "eec4e7f1858f3cc0e91875a9710aed08f266238dd0f592b52724fb88cf0e14df": ["vcommission.com.", 0], + "eec6b23de41d5b826325c568ee2299c1894a93c6f7bea1769f972764a53b9b45": ["cuisineaz.com.", 0], + "eec9ee4428e28c2f1085ccb337fae03dab61d66a859f2639b96ec26d7b0d0939": ["lex.uz.", 0], + "eed55605ed046574c0a9c8744989e71f4a832828247206cf6eb24b16a3a7459b": ["cug.edu.cn.", 0], + "eee53bd0a4b957c04ba2e9b07c2886b6da0c3bd59e767811d5095f27f52cae75": ["disqus.com.", 0], + "eee703f7d896e2815840c96a2fd7d38bac43c8221f0195e10ebfc5602b5f9c66": ["iscorp.com.", 0], + "eeec625d57b1880d508931be0044ff7558b93514e499abe0df09b6dbb6fc20a7": ["jumia.com.ng.", 0], + "eeedaf377008435860a56d7939e097531c43c12d61a83b19fdbb31d15d1f1dbe": ["youtube-nocookie.com.", 0], + "eef35036ab6766dc508231a4761ecceddfb1c7b79d20fef96ea6f72b4ecc91ed": ["paradisehill.cc.", 0], + "eef4fcf569c518c9cf40fd7b60440aa3fd45e93314ce7de2ebc2499a4b32d00d": ["agency.", 1], + "eef6bd744a1a52a58dce9f6257bc571adea7360c686a92f6e8640d134f9b2a97": ["science.", 1], + "eefa0879836daf056781a6edd5239a5b44b49e11f47db512d577d95993e789c9": ["graphicriver.net.", 0], + "eeffa10aa1ff4839dd4a731ea1d940cfc207b86551b66407e45836d36c212ecb": ["erp321.com.", 0], + "ef0c983b5ae13390234bfc70af73ba05b5be905f0f8ed69743594bc25d0c64d3": ["now.", 1], + "ef0d7d590e5a6080a42b7b51fffba8cb334973afa88e951b67b27523e40bae25": ["evaair.com.", 0], + "ef1440e5c75d0c3cf35ef80d975dab3b84a191b7473ded50b55de898950775ce": ["nz.", 1], + "ef1c8278ff712f1e588bdc0f1ac32c3e1d4bb7f3182a900c986abace0c050562": ["farnell.com.", 0], + "ef1e2c8a3bf90be1312a0178f9d85c9c951e07961d1754b5c07b6fe3b0e89546": ["iledefrance.fr.", 0], + "ef1ee2d445b26a5ac1c20f08962ce2e4b1817d1f8b9e6796c48e8b3fff920457": ["rossko.ru.", 0], + "ef271b815e191ac07728bd9b9b5c4bf97bf441545f3b4cc0de82749fa458df9a": ["lighting.", 1], + "ef33ae66b53bbc0171b2ab6bcea3d13ddae95dfa8a144c11df913457312eeb4e": ["macworld.com.", 0], + "ef34285c780509c83feb3b8097b436592c23cf9557acd8039b4ba626f16f6146": ["date.", 1], + "ef3c53beeccb62c1e09e9b454a66a1c20cb6c038f5df49f0ae7d43fbcc0caa62": ["bitmart.com.", 0], + "ef4661d9d91f8797205d796f29bd8ccacc6b17df24a3fbd17fc7f91e6850abf9": ["chinaunicom.cn.", 0], + "ef4aa07a912ed02ea9e1122b5e39630f0c8b502271b47a734ac83fc1ac4e42f8": ["onedrive.com.", 0], + "ef4d16731f25617ae6d65a6e78a92854049c299b8ad8f410929cd4e4a72e7e08": ["corel.com.", 0], + "ef4d5bb65bb9f5ce875fd7c1dea0420c531f4092f88ed8c1233659d3a62faf65": ["centos.org.", 0], + "ef4dad73af5cc7d98ea69309941476819ee3f9d52048a26c2be6606b0ee60b92": ["ipinfo.io.", 0], + "ef4dc09e117502f18ec7e11aa549575b2c517c621dbe4e6cfafb64dc47d5625a": ["empower-retirement.com.", 0], + "ef5372e7306bb74657517b4e93f8ac898e154a50d6ed874f3543a5c8c41f24f4": ["dianping.com.", 0], + "ef54612b249f3b166e53b1d3377e573a0700bff0658fb3d2a575d6b9a602ba9f": ["12go.asia.", 0], + "ef58e9e2543941d368faef39808a494793e46dc6ce99fc0f6477c6c4189d373c": ["tellonym.me.", 0], + "ef5c6b42ad81803ab182c3681fc4c2348d8e708efb1ce12eb9afcf257c3893d3": ["prosv.ru.", 0], + "ef5d64c169bb6ae2cc7f53ac2820023c791bc904ee48cb08eac8ea53e5e6d5a4": ["runoob.com.", 0], + "ef5ea30631d22e353e6e25fdb226b5cce414d69b26391eb450a93751d240a847": ["spectrumsurveys.com.", 0], + "ef62168b5aeb510d61c60130f659543e6623c4547cf98c3867c80432cbcd8cca": ["fit.", 1], + "ef62254f937d767c9c621ab1fa0e77cc73cb917589f154c343564c9f412bb527": ["iq.", 1], + "ef63290d06dea2cf1b5e50334872f976cb71eb5dba2172177b1481746716cfb8": ["redhat.com.", 0], + "ef64cbae886e1ac0adc9d54b8ad46de98c23e9cc63d2a45ce866223b58c8817f": ["videocelebs.net.", 0], + "ef65a6f39a0d6a23bc9e16784cbd46529c5caac858c0e3c4434eed1d868ed785": ["taptap.cn.", 0], + "ef666a41c0f4a7fd7ff68cce525619405d4fca1d6bcce4d92d00d44f06664459": ["tangedco.gov.in.", 0], + "ef6913d5dc6d27437a06128901029cc3f32ac9a72071489a8d48b435ecbd20a1": ["be.", 1], + "ef6987b05338ec54bdde9ad8283512c0f98a3f598eee9bd13d66fe29eb87b6ba": ["trendmicro.com.", 0], + "ef6beb3c7fe5dddab8d9eccaa6c2798d6e3da818a244bb94a5e6c477a3a96df5": ["lipsum.com.", 0], + "ef7209cf8bcc6d99bb6a2d6e2f543bd22a6da4e753d31e2bb447eb67e28dd806": ["tuttocampo.it.", 0], + "ef767748a26bd65c6f9a691fb48259b6850dfc4761ad725e17070120b261acb2": ["anime1.me.", 0], + "ef7798d2b12382c5a70df06d169e99fa6637d6e6057016559f0578c2ddf002da": ["drishtiias.com.", 0], + "ef7de741d56d65bc987ded2e304d5ee72ebb1204e17f0602c4aa1051caea0311": ["5173.com.", 0], + "ef8362720c2d922c77c50ad6f38184b9061664f66fea57ac311e35597f3c8207": ["3dnews.ru.", 0], + "ef8c0053f9e47478d1f078951aa279dcb14fa408ca6f53bae02b3e81f48f8cb3": ["joom.com.", 0], + "ef937b32a583c98ee9c69080d7498008908cce0d7382ead5e4e3c2d068ff9341": ["netpnb.com.", 0], + "efa0355a83062acdb929ec9ffdce16df258aa19f6eae77cdc73057b4622b3bbb": ["opengroup.org.", 0], + "efa065cf285874bfd30fc1ac146307cb3970bf6027c3d4d9a6b47396864c4981": ["hp.com.", 0], + "efa4dcc35682b75e28f17cff824bbd24b94ee2f3db6d302508754142cfa46044": ["driveridentifier.com.", 0], + "efa783d11d2353f5f28ee43606bf13b129986171a8127c4842b7da4596040128": ["99166.com.", 0], + "efac1058ea4d9fdb6f4a0bd6908a40c129cf20a06cee38a3e1dff79566606d92": ["iimjobs.com.", 0], + "efac325d4bd9b2461d8d98bca4eeb5ac7d6e5d92d8e709145a87ef97087b9bf0": ["atlassian.net.", 0], + "efaec503fd2bdc26970e0b5045e2e000fdbe1e0d5f23c3e6e9ad2cd06f607a91": ["rtl.de.", 0], + "efaf01fd7a227681c3282fdadff77bc367251c7372211eb45c6824eb176e3892": ["appleinsider.com.", 0], + "efb332c6641a9897266bb951bb714f875da2efda960347fe33d7d4fb6a299629": ["isb.az.", 0], + "efb8a2b81f47fe71dd4bffa7a085094cd9005a499d2a4950481db5c90cdf79b6": ["connexus.com.", 0], + "efbb353c41a480a02cefd220bdc66b4817762adc791aa1c8024559e4751dfb89": ["trading.", 1], + "efbc2767cf551f9d63ba53d77c48f8bee44ba7e4aa63a461e14e04df935b9e35": ["baby.", 1], + "efbd8bf1939079743354fca65cb601ab5ee78a9c68ff313cb38dc242fd77b5d7": ["wantedly.com.", 0], + "efbe62e6ada7ddfa8d889b9da9e351880278391f2a81fa71595e4abee128107b": ["hdqwalls.com.", 0], + "efc64a9b8c9b9c1843ca82e134d67e20e85db61e13236fff73b02cbfefdf0285": ["showup.tv.", 0], + "efc91392068348b5a5f8a520e151367a929df86a15b6089a362b1b0f3bd1e919": ["daz3d.com.", 0], + "efcc113af4c0c6df4c59ac10c58ab403dea0dc56cb097af39d0a0c451a7f6353": ["space.", 1], + "efd292f6bb9a01cc6e07ee12574652093813b24035fa40ac060d3957e6abeb63": ["la.", 1], + "efd441307518a5346250e11c06d3ead71b44b8505d9abc317a19008a0620a615": ["btdx8.com.", 0], + "efd5f22540c00cd5e76c6bcbb5b05ea80fc2354e4ef9e0a0ce89f7c3f4efc575": ["apachefriends.org.", 0], + "efda893aa850b0c0e61f33325615b9d93bcf6b42d60d8f5d37ebc720fd4e3daf": ["data.", 1], + "efe722019380773af5602051036d683a231f1a4ac0ddff439abb1fc0ed59e5ef": ["sale.", 1], + "efea4e47dff4f7007ab8b02cbb47c07eebd38cec0fc722f8f4936c325c162073": ["oilprice.com.", 0], + "efec9826e2cb3f614fe043afc877b98e07596aa1b08c8e7258bf2ad9cb067330": ["riyadbank.com.", 0], + "efeee9b5a8c9b5cd15684cbd52683266dda72306d8cb2bdbb5ebee98d47d4a37": ["kyokugen.info.", 0], + "eff79eae4f678dfeb07951dafed9cd8cc1bc19bffdb905715ffc7a6e35157d65": ["unal.edu.co.", 0], + "effd056fcca8af77689fa3d6dd16fd30fe6214d69ffd5143c59c868e7d9e4072": ["bigw.com.au.", 0], + "f005d458eaf217c7463c31fef1904ab46cc7adfb5870f130c4919c508b0db5c6": ["klaviyo.com.", 0], + "f006941e6c059ecd77ad7d80d2661e580476ef3214bd46bad2dc56346b962cea": ["tubxporn.xxx.", 0], + "f017b67a77664748bc5de3332d16821a2e1d66fc14b6dd59349d874722230aa0": ["uploadhaven.com.", 0], + "f01fbd888398b8bfb956cf47afef869d1fd091b0150e29faa0dc4e2d9f9de833": ["digitaltrends.com.", 0], + "f024455bc9be50c13dc276ba2cae81321edd25f6ee02219a9b802a79e54bae26": ["jumpcloud.com.", 0], + "f0254ff5af53b2cfb480d7995e8e6537fc50b90b3c830c2f5954f01d7f799067": ["rikunabi.com.", 0], + "f028533f83d4e975005c0d57192f7e420c1322957c0b1be097f974cf06b47bf8": ["51miz.com.", 0], + "f02ba5c931f6db8c99702d105afd6b0d7ee0ed9d61bf09aaa0a7338298589c98": ["gizmodo.com.", 0], + "f03135f318b8bb628fbee8bbe1139f6d36bbc924aaac62bdf1b848e3c9bebe8f": ["1password.com.", 0], + "f03195043a9f533b9173dcfd0b574f3e1ff13e35524f826ad3de466514da3c38": ["wipro.com.", 0], + "f03699848a9e3a3d6c2a6edc875993d1acb2b0da121d2cc2a1e63bfe5fb38348": ["intercars.eu.", 0], + "f03df7f98b63c29c3ae71e0fb5de65f37dc7fb91feb0a98cab12d2f2f2cf0c5b": ["echosign.com.", 0], + "f04788af96bb730ef89a6689af656b02c230aff8f9d10a50779691eebd5611b7": ["rollingstone.com.", 0], + "f04d18a37663e97b1488230245e69ca51cd39e1323ea69299ca46327440cf667": ["kariyer.net.", 0], + "f05698c7356ba6a30c7dcc7c85f65b238c624e6e46527d64a2294f8ef5a1dbe5": ["ibanking-services.com.", 0], + "f056ff036524f2b86346c417d0d34fc2f91f526b433f9afee5a7ff36870630f6": ["globo.", 1], + "f05da108cbd1dfc8d1219c56ed4ee81db6cca97e20bd0a28a7a5a47c5fd22073": ["bankhapoalim.co.il.", 0], + "f05e74c3d9044f02259b0f82a1b6279c533eda9f76ab00177cc364ced2c95602": ["mozilla.com.", 2], + "f0635d6c4e7eabb0144276a82ac880a608c7d28b641293012ccabd96e1112e24": ["infinitynewtab.com.", 0], + "f0642372059adb537a8bbf537e7b7ff1db59c4c84f8dbd8da9162e088ca7058e": ["fantasynamegenerators.com.", 0], + "f0662a448b43442f6159a0c51255dc9cfe0541f5c3dd7bc2f62db26d8d9bc9b4": ["sofifa.com.", 0], + "f070670f391a596b652608a03900334fac6d572fa8c17e523188717fd8961cac": ["jiji.ng.", 0], + "f07b6e29671fc43c658c63b63bd17a694dbf21f6b6987b92ad540eac7b43fb3d": ["pageuppeople.com.", 0], + "f081fa8e90e84d6a60ac98f14679aa5c71c216f1d41c700d743e17d97294dff6": ["collegedunia.com.", 0], + "f0823879223e58ebe0012d4a83ebca141e452f41b02a5ac058064d3de82a7466": ["yarnpkg.com.", 0], + "f0890328709241cd3f7c26636262f16a2dc11a1e81677e564947298b573af23f": ["dvr.", 1], + "f089a328f693844a92f948f571d1888086e3a256569335e2479a4a3a189b2242": ["motherless.com.", 0], + "f08e0f1af6f2afa812136b26465e218f379f54c99511a2dbc38fec0816bec9ab": ["wiley.com.", 0], + "f09073a2fd9bc13cc4fb5192a18ef2eb76d24f4d7a9197eaa61e50a57fbb9dc5": ["cmnw.jp.", 0], + "f09560d8892f2b85186e35ec49a3e653129eeccba39e818934153e43f72e33a9": ["etimad.sa.", 0], + "f09a7cb04fd7fc8e1c7e9bc099f7de3832cd8b4ee690ce9da98e62b1768537ad": ["pontofrio.com.br.", 0], + "f09e50ad3dbb133122e69e6c4557f464f3a0b109fcf03bf02df80cc41bec3a05": ["bricodepot.fr.", 0], + "f0a46f3d388c034e8637588f2432ee1ecc427063c7f473377a749f0bd0acb412": ["liquipedia.net.", 0], + "f0a6bec8c99affbfc3bb2d6c2bb7a776b4aa0f7ee22eaed635106c0b4d3e0664": ["rpgbot.net.", 0], + "f0b1e420c6bcded391a7fc005e662c0ce3b0588ad395fada40cc7f15bfdf45f2": ["goldpoint.", 1], + "f0c6afb4bd53277c9d84378da46d29d0c88a947e6021297352fe557e6a9975a0": ["nalog-nalog.ru.", 0], + "f0cd8c6b92408d02d1972c38fa05bf4757eb9278a4b0fba7a945695b9ca0a2e2": ["mihoyo.com.", 0], + "f0d466697d2c67676793fb716907f43d5a870dcf43ed693e52d035c620a35139": ["chinaacc.com.", 0], + "f0e349defc9c1320248bbd052d31664c932b87596c44c9c70afe271f63460318": ["eenadu.net.", 0], + "f0e78cddbeba53e7792e91280fdb1e46736f71b2cf1c98e99f98c2cbb0b456f0": ["winrar.es.", 0], + "f0eeacb3107e2a9fbe1eabaedefeac48e58a211d292ecce3c85a2c62ea3afe16": ["mudah.my.", 0], + "f0efbb340da05722b5dacfa274603a2c37aa2953723c775693c56e89150bc481": ["zipformplus.com.", 0], + "f0f339b6c1d1deba10973d910df0ca3ffd4b1c52f46c1da42638f6b0703a6e72": ["wongnai.com.", 0], + "f0fe3859f55d0397aeb71488a855dde6c7e187d1ee20d680e80c62636f39d2ca": ["fitness.", 1], + "f10e8d4189afbce7245d263fa3d990637f1935106099b7dabf68746d42b5572e": ["travelzoo.com.", 0], + "f110d9fd955f2d04e8db399d6fe8c8456d563146e62123b6fbcba5491209267e": ["trt1.jus.br.", 0], + "f11330beb423c26925be9a0d6523aa28939245242e539b64beeebfb3e7838b4d": ["sto.cx.", 0], + "f118a0812fe7fa579563d38aa49288b39772f2a5c44548a8f789ced4c5070911": ["mcar.vip.", 0], + "f11b8a6209fd3d0f244c95d151801741e85ce7e5b397358cdea955d3eff45278": ["sps-system.com.", 0], + "f12051dd03da85495cdf4c6ddc81f22af8ba193e1ffe1b202152b18c6269d9dc": ["ygoprodeck.com.", 0], + "f1295f5ad353d06ba6fcb447ab05fbc7a48cd804f6ff88cb0da6285dbda5f3f7": ["xhamsterlive.com.", 0], + "f1358e9e64aced07788e5125bc16db5664e91b9fc56787e2be28d2e5a5da9b45": ["huobi.com.", 0], + "f137e474c0c3b627beb6d1d65b05cf94c516723d87d68a41703cf988ce21677c": ["wuyou.net.", 0], + "f13840039387a758daf492cb7f372f0fdb67a3c7203feb4f8018304dd4e66326": ["ratp.fr.", 0], + "f13fcfd31748e3fbf10193d146f59f93e27c672215452bdaab0752221f945c9a": ["mopo.de.", 0], + "f14fbc8c63669b65afd51bb07f6a3f0df3c458aa0f348388c487133845a0c4ae": ["sumtotal.host.", 0], + "f1569ee6ab6a51e89c82ce8632bd6811a4906740ef97c697d70736dfcb865658": ["the-saleroom.com.", 0], + "f16943b684a9550b9c77253bbc34f4f48716cbc38398d4792a9f7f8c5f7fe54a": ["infura.io.", 0], + "f169b52d097643488f4272a997e3b13aaf4848ae0fe760f6355502bb09ed93b0": ["bajalogratis.com.", 0], + "f171525a99526456090e848541e65444cc7905d4d5ff4b3c8ce42fa78e0680dd": ["sf-express.com.", 0], + "f17424221854b8b98685904453040436b30a663cdf8822350743838d19922314": ["bibliocommons.com.", 0], + "f17df59981ee31d6edac2a7577cb8ac42153533ccdbe555509e948ab2a1ff948": ["solutions.", 1], + "f187028577f46e409c35992d533b2d0d54c04e3ad792d372366747a09fc2b3eb": ["muscleandfitness.com.", 0], + "f18995ad4a9525daa1003600e407cea7df2ab14102c88d643de1227ec55aa1a6": ["yiyaojd.com.", 0], + "f18cf82a0424dfb8aa6d669763a82c6cafd95e645fbd2219d6f86419f8b33459": ["deepl.com.", 0], + "f1905aa09751e3cee4d6859f3125f01c43d89ee2354d3cb2157cb84432a25cd7": ["bubblestudent.co.uk.", 0], + "f1960da4deb74e3fa1c6b25afafab0414f5da42996555706c82ede8e1d0ed9c3": ["wpnovin.com.", 0], + "f197aaf5197350ad3995947c6c611636115747bc8b3ffd761136e2039a2791cd": ["windows.", 1], + "f1983769f77d2098bee629d54115d16b290856e2dc7a0c7069064be9b5527250": ["braintreegateway.com.", 0], + "f1a9a3bbbc6f6c8e368138132592f8eee387fd52f67a1d5106579ebea98a329e": ["plotek.pl.", 0], + "f1ab99f34f3b27d9634ae90fc439c8e08e415179f6a4ccc0b5ab51ecbe73da7d": ["barrons.com.", 0], + "f1b2ee9a3600b07af849332735889217400c02db484d1f5842e658d1a317ae53": ["mykhel.com.", 0], + "f1b573353b12672596200bd9c5e5cf69cf304cb9ba589e035fcc2f172988f737": ["autobild.de.", 0], + "f1be4c39d05e6905e84ff39357e90b4b2e3f9c423da1a8562dbca253500332fc": ["perfectgirls.net.", 0], + "f1c634f67e8498f31079c0f77f5d3a1d1409f7a4ab0f8e0df2ae093ec4807742": ["okazurand.net.", 0], + "f1c722bfcf7260a7c27548bd9d6627dfa1ab9c66e6ebd2d08acc3821c2052b40": ["h1g.jp.", 0], + "f1c77c4b3f1c6a70dd295a99047935f7d1304688d434997c76f94c9c3405b6e9": ["chinadegrees.cn.", 0], + "f1dcd042f3c4ec914ac7618e514cca24f5a1854abfb282f455e268ae30d09264": ["fast.", 1], + "f1dd4dd769bf70182caecded99f28183c62bcf0a9e069d489d682b8f4ae2f1a2": ["javjunkies.com.", 0], + "f1dda756270562aec419c31a79a362576f46438074041e07297c4547bfc4971b": ["codester.com.", 0], + "f1e1e1cbec32abe1d46b9c5437732034c108b7ebce30d32bfb997b4dc435727b": ["propertyguru.com.sg.", 0], + "f1e34af6381a00a43d9ae228503002a94ab7f0da79ce31270b827218bad155fc": ["migros.com.tr.", 0], + "f1eb36e57ae4d4ed782d3f4ceda572bae683117b8922e2137b4fdbda5668577b": ["interpals.net.", 0], + "f1f45c41fa14b5a7bc67483fa8ed35a607dab89ea33a4392faaa6a518d6d7ba0": ["tpp-uk.com.", 0], + "f1fb7ce1a0d6f0edcb909f1453a2ad756121175af566229bc7033353d2f16486": ["artistapirata.com.", 0], + "f1fbae20a3e707db05a92e6ac358d02161c0e76506f7f9e7fbcc6b5c9e7142ee": ["kiotviet.vn.", 0], + "f1fc31518bf4a19e614c8a391ef9262485fe75999cc762dd2b01076ff9fd3358": ["villas.", 1], + "f1ff328599b7b689f34c03492acd1b6bb05b2a7ff7e99caada800400326cd9a7": ["camera.", 1], + "f1ff901d95cec9d4583e945d1e4d854d7173a0409bbe04bb31d2bdcf54b9989b": ["coocan.jp.", 0], + "f200089a1a4a0b52933da2ae276791a254769f3277808452253c7f3e1ade02cc": ["mchs.gov.ru.", 0], + "f211a4928f788d335dc88f105dfd90c4b215b9a0f2c30f6aece147a2ed8f3b29": ["trabajando.cl.", 0], + "f21623e252e128acdd50c4a21632dc1a67c70188ba4d39b0539cd9232c1571e5": ["hentai4daily.com.", 0], + "f21bb2d6b9bcc477fc1d4db0ef2fef608de0df92c3a87eafe1b3a9e3b5cf606a": ["level3.com.", 0], + "f2230eb82ecf634ad13fdf2384cba29ef8dfc602f7e534c2d065a482f848a4a9": ["seesaawiki.jp.", 0], + "f224a9ec8189e5ae63feae0466a744492a14d3504eafb3640c8dade0f538b037": ["yunexpress.cn.", 0], + "f2292192347fd48435f864f5c8eac7cf0f4eb6d66400c506ad6955523acaa946": ["bizon365.ru.", 0], + "f2303fd74a14212abc629ceb013b5b73159e12b42dac89dc93c6aef188b3a4fc": ["maisfontes.com.", 0], + "f235cd3ee2a36639888672be858a7f803ddb8eeb311b7d2e1dd3271d21a063f5": ["xjtu.edu.cn.", 0], + "f23cca3cd61dc8e3a753b8ba619e93a570f7903429206beef25581a1acafe770": ["epson.", 1], + "f241e85e826fcb5262a64ff3562b88be16b09c59b5f95705eb924315bec02298": ["uma.es.", 0], + "f242fceae74ec375a2dbcbd9067e082338752778576bcfed44df2035803cddd0": ["valuecommerce.com.", 0], + "f245b3b8e49324d1ca0a9acbf9896b95b32b62ec0edd17c0f4cf1da5cdbd560e": ["hk01.com.", 0], + "f24ecb13ace1df92ab21c34478e53a77b4ba68d11727a66c7cb001a0e5d3b809": ["ilgazzettino.it.", 0], + "f25104b0d88ae5307c942009556cd6ccb8ba1c5be5950554ae4a0585b9cdc2f1": ["samurai-gamers.com.", 0], + "f253a7094019af4d09732327a3976a30db82e949b5b5a769a07aa676c1112c8f": ["microfocus.com.", 0], + "f2573ed80993bde6d4c383517e8601d62f9f4148d6fca0d354b09ff5009ecd0d": ["gdeposylka.ru.", 0], + "f2619e5a16fd136a38e4b7ea64d01a1f714e05930eb71234437fbf60321d5fde": ["macrumors.com.", 0], + "f261db94d240522db4d1c35a3c013492fdb259c909340f7c16eb28a350ccafe0": ["tamilyogi.love.", 0], + "f26237c514f80f61bced549fc14a131670347b42cb6283f94a7937004b9a0612": ["forvo.com.", 0], + "f263df0644d43d3c385f47266d44dae7a054d4401a5cc397dde403249cead00b": ["infobae.com.", 0], + "f268672548206fa0a8bc6ea6d233e655e05b4e86e1cf7e8a7f01ccaa75d98548": ["sd.", 1], + "f278a817f2b648375249a6918f453adeb51c94bedc628907e4ff56a51b911be2": ["sageone.co.za.", 0], + "f27d5f70933598d2c0642308eb98b7201e190404f8d157407a90cdc5cd3c6d39": ["convertkit.com.", 0], + "f280c69a383bf921852f01f09d2a3bf4f124e6f823c2001eb57d50782b99cdd1": ["pe.", 1], + "f2860d9aad740bbf83810068dfdb0b28d546b4b0f08f8afa277c5df1c422f933": ["getapp.com.", 0], + "f288084f66c28c61c54d23f28028194264f0af02c5475b5dbaddc07855d32efb": ["hundsun.com.", 0], + "f2892314c6012731e8f187ebd070ef3280673e7a1248d48277eabe8bb192d4aa": ["instant-gaming.com.", 0], + "f28a53de5a23b736bcafb103e9e8938a7596bd4f007a0d6eeed1bbd2ddf788ba": ["pxhere.com.", 0], + "f28a9969d6e90790933394da762034cd7bd5d8d86b73ed74954a957bd478976f": ["hotnigerianjobs.com.", 0], + "f28b80a95a6d29898ad10b92c097a116892b361777900cb4b20127edfc1417ff": ["saraba1st.com.", 0], + "f28c8665f6d101ece6747dfec725f0890d4059368241cdcde636f6f457c84f9f": ["uhaul.com.", 0], + "f28fc5132cbd4b70d800c5d679ff1892d659e2bf28af8d28dacf30f87ec629c3": ["knoji.com.", 0], + "f29020170e1d7e4098332f8204e4ea10ee84ec5cd00f949aa0587bf46986e0ec": ["uidai.gov.in.", 0], + "f293316f080994b0034b8984933f45c1489b06c969c744500acc6bbdecc6fe44": ["bancodevenezuela.com.", 0], + "f2979695eb7bd22e01dd0cbc6ae157a4565a5d6513f112674d12c59ca1b37d49": ["paymentus.com.", 0], + "f299fb07c7eebe8d5967b7e0d22b4e6b95f89c728c45311c40497284149bedbf": ["seiya-saiga.com.", 0], + "f29c8a02c1250f8208e95df905b2214145b3ea96225227e4fa0730d3b2f1e955": ["wawacity.tech.", 0], + "f2a2e63ac812ad0a4fdcccc9049b4101d56b00e0cdc476099d5f52bf86fe9602": ["ee.", 1], + "f2a3261fa3dc2f3977197bc88299a6bd6ec0854e5df81ba00a9172bb1839fea8": ["figma.com.", 0], + "f2a93e473dddfc1b529aacc17342e8ac19c2046bbb646b49365fd4e9bfd7bfa2": ["levi.com.", 0], + "f2ab92e991063cd9e53a8eadc33388120b14a77b6f43ac3591b424e1051df014": ["tplinkwifi.net.", 0], + "f2b0104b87abc5909ee3beb4f2a312d2d8a2ec239391bd66e5c811f57e368c14": ["kurir.rs.", 0], + "f2bc227286d483f064e4029dcd3880cc6e2972beb5b897e473b28bbb9058c712": ["liaoxuefeng.com.", 0], + "f2c872e7337310033f68ccd7fd61fbf941f09489e288e9b3274118e3f642968f": ["jnnews.tv.", 0], + "f2ce3c13e9b1c1df9827c5e5df2445a131ad652d70e2229f6b6cb0d41480cbf7": ["upwork.com.", 0], + "f2d29f06e6204b53c6003342db968ae0e83ec796fcc80d863ede34a800a11b9c": ["moneysupermarket.com.", 0], + "f2d7f1a1630036f44913f9f7305febab26ba64b94137dac09ee851c1a66dd87f": ["ascii2d.net.", 0], + "f2de7ffc2d3bf63befabaf2ecd322b30175771dbf0127b01c2f9c10930021b5e": ["1616.net.", 0], + "f2e016829e1b3ece47a3fa0c2f3d1368d5c41380e9604a49c9e1b739bf000556": ["xrea.com.", 0], + "f2e088ec0f315fc211b5be3843c28fe325bf874636d88009d60796ee73e6193c": ["quizizz.com.", 0], + "f2e3eb90c5b18f241708d6cf026d834d220e01b3b65453632191f62a448364d2": ["inmanga.com.", 0], + "f2e4e8890c6130123f5ef8f34dbd4b640b82e4a6a9796363b4ef9c085744046b": ["proxer.me.", 0], + "f2e5ccc13d88083de9ea6e70b232c9712199c6dbd86f9d7dfcf42f445e8aaae1": ["iplaysoft.com.", 0], + "f2e6bdf60b4ebc0d36ecc32be6c102e2916e01399da7590ebf378a8c4d19e22a": ["dailymotion.com.", 0], + "f2e8c7a8c3f9c5b354bbcd4bbcc935a9b03116acfcd848c652d892a19d6a24cc": ["run.", 1], + "f2ed5ebfc9cd54d9687337d5869482953d8e4aaa8a3a6a7a33870fa31d2b7051": ["supplies.", 1], + "f2f1c2833af932bd9039b2f0d562861c77d8812b11f5d519b0bba092aff3ffe6": ["hyundaiusa.com.", 0], + "f2f32bc78572bda48a88a715b7aefd0652ccdc8a6e120248b8c9ab13ba51c1f2": ["pastebin.com.", 0], + "f2f5d27dbd9652bb4ae654b9e4a4a0e2456323de152aeb3b37437e56da221f2d": ["quikpayasp.com.", 0], + "f2f7194a0a176cdd3c56d79df21c03068382fac52435a139047bf3967c37e265": ["indihome.co.id.", 0], + "f2f9ddd03e54d42902dd265413aa5da40e1bb85980618064953f454e753c9e36": ["kolesa.kz.", 0], + "f304b43b2d74b2041a2faa4834041900edfed94d9bd7eea0247eb7bc74de91af": ["inaproc.id.", 0], + "f30bc3d7fbb25c797d0684a41d1e1f04d9d58e17389804c091b26a4c1bd32584": ["kudosporn.com.", 0], + "f30c4390d64a42706c33a00c9fe4f2191b64ba0096195d39db334c7c45703530": ["amsterdam.", 1], + "f30e453b740b11c9b14984f377271ea910faa7c2140af7a279b5e9fef48cfff4": ["amundi-ee.com.", 0], + "f322bc01cf6e0b191caef23a4957703d331a5a803e275254cace0043914a5ace": ["zhonghongwang.com.", 0], + "f325cf64475b16b0f7b97ad316ac316b82dd6d444ec12e785abab3de2ae26125": ["bato.to.", 0], + "f32699cecb347119511f64ac8fe7ba24685dc13e069f85daf54a6c9ca1b6f5bb": ["itsnicethat.com.", 0], + "f3292f3b2cce579aef0551dfaef4ddb9c0def04a2b50b318fb8e919dedb6d4a3": ["cheshi.com.", 0], + "f32a64de4c7ad3e94194f5be27100d0bb3a6b73298c47230463165816dd43f67": ["airbus.", 1], + "f32caf7b1bfa09a1f8fd0d8ef81c5a4b63069fd4d658d8778e1c9caaddd3a4eb": ["fullprogramlarindir.net.", 0], + "f332f097c2109b4add98fed33411056a06d9bee6ee91bfd36bdf2a96b0d487fb": ["mundodeportivo.com.", 0], + "f33369419b5bc8f00d7b335dbe291aa2c5b74cf5d3cad27ed072273532d48ea7": ["diyanet.gov.tr.", 0], + "f3375e1abde4f80e0b70bf320851b1d40f79905024ea5353cfb029d717b77c0c": ["adquan.com.", 0], + "f344e9456066046cc8a81ff1ed930eab4eb39d855a1d8ff76455db07c553a688": ["hukoomi.gov.qa.", 0], + "f347f38e32cd9359ffbaad7c55e1431894b1ea62004bd5201f90c3d8d29a83f2": ["guanajuato.gob.mx.", 0], + "f34da3d40f4f846ae41643175892d54ea271699c9933d05da90633eb1dbc912e": ["holiday.", 1], + "f355fa1761d415995126c277d4c24cf7c48611b30044835f73f1ddad4b8b04f8": ["247sports.com.", 0], + "f35dba1ff1e5a0ab7e9243dc92c6560fb72f63f88ee69891a58e07cfa6ac0b10": ["romhacking.net.", 0], + "f368f3053d199c944a3babb310116b2cc1e5111f1aa44b8614dd2f93bc520f11": ["rsvp.", 1], + "f36b95349f6466c829283d8da9d70e6ceb3fa111f3b1967ec3c9fef5ef20f5c3": ["americanas.com.br.", 0], + "f37519c1dfc4fe41ea4380505a2e6af42ed5ac34030e729a2d2d808d8c6a1125": ["ga.", 1], + "f376b1d6dfcd6e52cd3e7713cb3cb7a05d58809691d2f59ba276b67d18798be1": ["mintmanga.live.", 0], + "f37e082801e48df3dfebc805b67a8bea6cddc017f2d2ff40cb0eb1ffc26019c0": ["togetter.com.", 0], + "f37f49fa9d4c847df4fb331789eddc75ec24bcc87a6764ebba493831995f387d": ["engineer.", 1], + "f38527c7e9cf71787304d6612005b16aa6b35779d78864cdf65855d859587d08": ["enjoei.com.br.", 0], + "f39597dce3aed80bee3dfa7d6cba69f65f0aa24f80281f23c378a56d0c9b8fd9": ["shop.", 1], + "f3a90d044b7fe35435d6845ec2dc2325e857b5584bfc9a334aea24dd77406c29": ["meneame.net.", 0], + "f3b0c9bee55d7b0c17638b42c7b5cfee3627dbf93b2fb6af070476848881f8aa": ["hesaplama.net.", 0], + "f3b51610f5544349a5fbf9723c82086666b31f797b32c4fb35e6ae34ec8edfdf": ["1geki.jp.", 0], + "f3b58dc85f773cd73de34fd7f4c6f14e6e76263e2fcf8519ef89eca4dcff035a": ["vestiairecollective.com.", 0], + "f3bd8da9c7ceb86b70d470f34733629dc3546345dfcacd71cee1dab1a739ee60": ["hikvision.com.", 0], + "f3c6d335f9672299e349aa7a3cf4e20efa25914eb8f96462160cb5d91fbd10b5": ["freshdesk.com.", 0], + "f3cc50998087709321a47f7bdaec9a0367c5dbe92f9bb9e3ad3a1fc96ba96c1f": ["m24.ru.", 0], + "f3d84b054bbc80ec2afbe7f399bf97433e69b30fbdf41689c108e00d7d081c46": ["torproject.org.", 0], + "f3daf1c5d9cfd24bfd14053769f85cb9c51b435f5114c0d4ae333cebc001a7ed": ["detmir.ru.", 0], + "f3db4818fd64a9e9f15e7bc3b2c7e7a83902bfa01efdc7106387e3ef6258a1a3": ["hatla2ee.com.", 0], + "f3dd9aad349988e247adf6f192e5a7154651c31718de2a99cfc1446af070adde": ["ftvgirls.com.", 0], + "f3e93bb4ff2cb7e26d8141df05b0a2fe712fd0a6e02fca16952f1a1187f1e151": ["gazeta.pl.", 0], + "f3e9ce8c71cb68dae91667a4b589ff3424bc8e2d54dafbd558aaa519db14040e": ["obsproject.com.", 0], + "f3eaf0e4520110ccb9315a31e7f14e594198c0d9e37b506a405432f05d0c2747": ["googlevideo.com.", 0], + "f3f012b3f41d38f93197f4bb59c812e105506e0fcbbfb80988d323c0268959ff": ["silk.", 1], + "f3f2d96ee239b6e2c579cc790171fbaf393af8bb051bf62f65f298e344823a16": ["army.", 1], + "f3fadcbe6e8c3ffe7dcf3860331a6d7f42dd3dd76aaf9c3edddd5375dae86455": ["mobinnet.ir.", 0], + "f3fe9125d528fb6d4b069680911c5e11e077273fffd998f022e763c9185f9886": ["lanrentuku.com.", 0], + "f40e443ed54e6219473a729ea5dceee4af088762184531fc7e3b78ad53d8c35b": ["klix.ba.", 0], + "f41228feab6b23361e9bd7234dd8e2cd584ba508a51490d99bc87416acfca378": ["sinochem.com.", 0], + "f41673ea5a0c08cfde18b9de981da5a5dc61abc68b264ecd4f5a0c8a5c57b4c2": ["bna.com.ar.", 0], + "f417b87eca674148386c846490c5421446770c1f2cf3f25e60c2a79ea9d74e06": ["zodgame.xyz.", 0], + "f41da7b1d1d223972d03d6508476880a568ac9770b8667f8e3c8e44856a2aa72": ["mojim.com.", 0], + "f41f115d6dc7da3edc1d97f3b948e2e61be0bfaca4d653c214456e5eb28e3f33": ["rs.", 1], + "f41f1457984422770ef86c86d45c57ad0948ceb7634eab2a49ecf6f30df56eb2": ["locanto.com.", 0], + "f421cb382cc3b4373a4bed18beb1be24a2bdeda6ef20cdf4d00d62160ac910e4": ["arizona.edu.", 0], + "f423a1153d48bee800328e93825ed446113ddbc0e42b3853dade4b2d635b6eae": ["getpostman.com.", 0], + "f4268bf273b25c0b9110ff8e355535dc52c7df8f8a0ef23d2a0109bff7c83a0b": ["1010jiajiao.com.", 0], + "f428ab89c04667a63a91befe409fe31f13aa9261fa64b3ac64d36e57d28e792b": ["dbw.cn.", 0], + "f42c9cd804d7349eb55cd9b27cc88aa2fa1fff4bf82a9331562e93b9fd1d8770": ["ppy.sh.", 0], + "f430185549f79d22a4fd8a1a521733ad34ca6e96c68893fab6e877bd5ff19171": ["unipus.cn.", 0], + "f43109bbd5baf7d7d068c44539eed3d5dcfaa19b942c6fde13920d3e187a1c47": ["addgene.org.", 0], + "f43d72cd60502f4d5194372724a2352439416e5c3933a7fe45851fdba3e9e719": ["snapdeal.com.", 0], + "f4419eb00f5192071853192943d75d2b6d85345c3a6964293196cdd43ea44b61": ["ahnegao.com.br.", 0], + "f44968a960b119b1d056ef6097d25e199babed7eb1a19f55fdd2b3f733dcfcef": ["unibocconi.it.", 0], + "f44b8d04c504728669584e4f7aa43e27a30efdc316c3c0d57a6c40f85f076840": ["hentaitube.win.", 0], + "f44f9e939454ed69a2e1f21105cf8e2e8ab9732ca6a331146329132e66a920d6": ["worten.pt.", 0], + "f4511dca9e252e04fc562b842faa1aeae5079c2ef939ccf26cf64833ff656b5d": ["cheapoair.com.", 0], + "f457da02e963a0b52777fcef5a355175185f4218c5ee5d5c1de80f26858ab4fd": ["stockholm.", 1], + "f45bc30a09f41495e34d787745d8ec3bd76459c0353122b4def0689d60aa8448": ["afl.", 1], + "f46366083ab745d7e3c06d0baafb0f19c95013457a84309263a501b49b8c9818": ["bootsnipp.com.", 0], + "f468396001134890e87993a30dee94cfc0fbf0dd8e61b569dd7e4f6cc14ee619": ["gaijin.net.", 0], + "f46ab39760bcdab2d2b0368bfa8696c005b2940669c3dc3a579fbc7327f64d67": ["minecraft.net.", 0], + "f4712a123d8c14b5a83cc35a41f7897bbe51de66dc4f449f214cbcab3ba1a9a4": ["findagrave.com.", 0], + "f472f71865556826321706d9e532bef40029654e3e695bb46466d76a934b28a6": ["fobshanghai.com.", 0], + "f48206c68d8b5e3fad3223c2e6290e76fa97e562982cba21be51edaf94aca7e4": ["mui.com.", 0], + "f488f6ab3cfa1984c2c6b9a3a84637819a7de9a3d4d9236c3af85584ffbae2ff": ["srvtrck.com.", 0], + "f49c2d1fe4eba3f0c106d2691eaf1c8883d0bd6d739e45e11b60eba3beab7885": ["youmath.it.", 0], + "f49cb0f2ad75d4433fce5c5f923bdce52da1ba58ee06737f01c32acfed353397": ["moi.", 1], + "f4a9f910a418e92981aaf9ad0cad6f7b83ffbf5aac4127f7d91b81c0a03fdf05": ["nudevista.com.", 0], + "f4b37b6856a6143b419396c5dd444b2447cba127234e1f839a328d2603614e82": ["perfectmoney.is.", 0], + "f4b44f2b71778b8af15917f760b7966fc9f53f5b527fb5c7cf77c119905d51be": ["instyle.com.", 0], + "f4b4e9ac4e26ded3114c1f9ce5da12aa0a4894cab19ddb64be6c57a374d35f74": ["yabook.org.", 0], + "f4b4fd01de7189b75eba159ff83a5f3f1b75b5e166d0d58c29c419cebe5eab4b": ["jo.", 1], + "f4bbc9adcd88e1b1903e261c5da563d3c5bf14425613122255568d4a0651d3b9": ["list-org.com.", 0], + "f4beb8f19dc092c2e249cd83125109eb5a71f5be0ff364d9202b50aa8bd10d73": ["godaddy.", 1], + "f4c0beb05567bed298d8e86439af9c64dbbb86e0804ce527992945db1f873bca": ["website.", 1], + "f4c69396ec7122c43f2a9a9ce1418a1d1f3708ae9e34d1322599bfbc6d876ca2": ["sweetwater.com.", 0], + "f4ce48e3afc4adb055d74aefbb17d4891a486a78fa064604cccf257faca3f6d2": ["farfetch.com.", 0], + "f4d0225b2569df0153ca5df990d84cb3182938bbb4e0784cc69db493f0a9e1fd": ["cheatengine.org.", 0], + "f4d27f5818ee5bd9decb41bceab25cb15570a110135c25f151080c8825de07d5": ["goo.", 1], + "f4d9d8683c5e2c1d532a6afb97734e332b61662a6091478286df3f5ca37126e6": ["jrj.com.cn.", 0], + "f4da922126797e50ef6bffd06c8479e62d3b0d425b7e5f646e023c81b8cad5b4": ["mfa.gov.az.", 0], + "f4f58b2730cff2d449b305f0257a27ac229c29801d56f094235462c17ac86c55": ["alaraby.co.uk.", 0], + "f50079f9c29ce9938633809b63167d4df87da8e36170444857bc8b563cc7ad86": ["dogdrip.net.", 0], + "f507a4bc9af97a59bf81531f6fb2a9e1103f494bc388370a6b9c9e1061f1864b": ["tmohentai.com.", 0], + "f50de7e91f8aa12cf5a26cbff31350d9ac7c55dcfe4c6acb2f9400784c2c1231": ["truthfinder.com.", 0], + "f5125b9510c34500459cf10d456eacea623b0d9b19f7a678fc868b781c4c66c9": ["tureng.com.", 0], + "f5134550a2e70a55e4b911f3d137a40505f42fd272d1cb468d29c6008b7eec6a": ["szfszf.com.", 0], + "f51bc2cfc6ae04fbb22beb04014f507286d97e1496febab6f5e367ced76552d2": ["aptoide.com.", 0], + "f51db12df67c741a1a3377ae9caf167889602905cd88ade52172ad04e7442665": ["i-doxs.net.", 0], + "f5275ab2b0bc32bb47f01e23e9a677687fe880cccc169ef2f9bb0108e2918eaf": ["soundtrap.com.", 0], + "f52f4bcfbe563823a7d414d622759b864beca116990cf94b2e6f9084665ea527": ["mponline.gov.in.", 0], + "f53df0ead901cff1413b138ae3e8cf0d9294fbed7f64282f6f32b624f5e766a7": ["banvenez.com.", 0], + "f5481db4eb161b82310f5a9d45071da7c5fdbf2257e5a6ddd8ccb09dfc896e5b": ["bigbadtoystore.com.", 0], + "f54b3734b9f90e1eb27eb52b5e9e779bcc733f19265f7c26d3baff167a7b170c": ["iltalehti.fi.", 0], + "f54cc88d9031cbadbe79907c8b9b0e1133ba174a788f059e9f7c8df925782093": ["clip-studio.com.", 0], + "f55136eceb152e202b8b6436495a06ef75ef1ef28ca6ab503182f3082aaab351": ["secretchina.com.", 0], + "f55329db92744ea9ee69894dda46ca08b34ef5e9fd67250acde93db3ade737b2": ["porndig.com.", 0], + "f55a0cc26f073e77f3c4e856a4bec3903bf2362840adcab496ffb4db5bc54ea4": ["addevent.com.", 0], + "f56062ab94c32a0948f726844e3b115d366e583b62517ef5cca36a1be97136fb": ["sheknows.com.", 0], + "f562a776b81c4e77272baade893563500520b936951302fc50c64f2bd19fd8ce": ["lifeinsurance.", 1], + "f56812f378ec801950714ff2f662ce9220e020aeda6574c4336f873de48321f7": ["aldi-nord.de.", 0], + "f5684a98ed5e90c7900cc45e95b1091158e5ebe43b64d2994d834a662259fa7a": ["paperyy.com.", 0], + "f56ba251228faf4eefe305de41fb2671083525b05bd01a9946699cd66b716633": ["6vhao.tv.", 0], + "f56ba918e45573cdaa9539536e43219611b315cf4ee46787bad6aab0520872ce": ["eurobank.gr.", 0], + "f5741bdc0edeb6720dd8f395d3cb867c278889e3c65cacbb5cb505c319ddd10c": ["komikcast.site.", 0], + "f57930021bd65d0472a68d725240b7ae15e3779b0768564749efe3a9782d295f": ["elte.hu.", 0], + "f57e177abf595247e2c913a1f5f9d83c02daa4692d992978bc8de782f0578276": ["ws.", 1], + "f582ebf7c36a7dec2c2a688b6cc3b44683a669b5e8565ab749e8d60863470504": ["llss.ooo.", 0], + "f5891018f261b33faca97c27ba452e0fc2ce81514fb30ffcde14a24275d7b513": ["financial.", 1], + "f58bc1903aa3169c16179ddef971570edf97ad789a1a18473e1ef59114446c09": ["eastgame.org.", 0], + "f58c8d2e08173ac48a42fcf4d49378c3991bbd3dd7d237a462726d51334ba910": ["nuist.edu.cn.", 0], + "f5903c5609407b2c3a3f588facd1a1e26a002709fc2cabaf7d9854c6a6acffa5": ["partis.si.", 0], + "f5972983c7f787a40b214d194b203bfa517c0dabb558f473bbb0e2597aca7cb8": ["deakin.edu.au.", 0], + "f59c9fe7783b2b87ae5aa874593bfae8d9ca350a5d109156a297f67d7e44bdd6": ["hattrick.org.", 0], + "f59fa8bac375285c251a1945eb04b04a4426fd5044d1dc40e74e212910cb2a17": ["mobinsb.ir.", 0], + "f5b3e50bc26b2ad452cd2898023218a820c5df1d50b8fb737348206dffc1b02b": ["flippa.com.", 0], + "f5b446ee7a4a7d3da94caf832a78b39ee9deb920d417faf4259047c77a093e6e": ["myket.ir.", 0], + "f5bf651082eaee16d8d90681859b85219fb525a31c51436075fe3971e30d33f2": ["uz.", 1], + "f5c0d5ac295df28563addfffcfa00bb92a06e4bfbbb53ba665d4dab6fe1b70ca": ["ppt-online.org.", 0], + "f5c34f57c091804c51553e6b91a1d073153b5b3a4c7592fa4647e5400fa2031a": ["odoo.com.", 0], + "f5c40337f9095a231c20cd42249a4d834061a0eb69e96871e20c40329b6a72bc": ["tunefind.com.", 0], + "f5cb5ca39f6adc5f7edc4cec1be82e6ff6fc2b7bb494f5c704099e71a2a16c15": ["qatarsale.com.", 0], + "f5cd6bdc026bfa1043e85c0660d3ce3a80b8ecd2cdebdd402030817aca4b4ade": ["ufpr.br.", 0], + "f5d249a4142c70c911fdc5ff61fd2ec32cf701ea6f4514079a829bb1ec9b4c1e": ["microstrategy.com.", 0], + "f5d6a7f7884e99e5719c2f40dc6fda0bbf3cc171041d1bb3f69b217c18145912": ["sfgate.com.", 0], + "f5d6b7f00df0a39c7272348940d5fc78ad457cbe89af14418d2fd36d31c94659": ["twpornstars.com.", 0], + "f5d840e2040dc378ff945a720315603344ee9bcb9c74cfbf34bbacf85a6611da": ["10086.cn.", 0], + "f5dc92f002fe38a5349b5345a468a217e273517100d2682d3d2bfd93f49e4b77": ["wallpaperflare.com.", 0], + "f5dd4ca8708e95c5ab0342234ec1ace59df438f6dabd540b400354517bea0f82": ["wifi4games.com.", 0], + "f5dec61fa29a2b28e3585056cb7ec36dbb831c9555bb7ad514595153c3135a84": ["ericsson.", 1], + "f5e2fe080fea095ea8c758ccc2b25d3cc11342686ee3aee6906b59af0737e2e9": ["traveloka.com.", 0], + "f5e94f39909ae65bfd9b1542142588b52e3b3eb0a503ab1b02eca02d7b2c8cc5": ["telegram.org.", 0], + "f5ea1820b838b14750565bef8516b0d86d22da11ef959145f470b9d22105ffc5": ["newsbugz.com.", 0], + "f5f471b53572ae97c2bcdfe29430602db7e5f11aed74772634e86c80253a4d15": ["gives.", 1], + "f5fd2c84bc667f1c24d2c7dd363ae71a7a3dfae5be6cffab741a9cc59b39e3db": ["badmintoncn.com.", 0], + "f604b3117eb6e2fef296f93890a6ea6686b2d62c9ce432ed7438369495bd6f6c": ["creditcard.", 1], + "f6062af10cc5192f7ae3c9652d3fffaa231e3af9ba5e7fcd2a37d7d592845628": ["kakaku.com.", 0], + "f60fff08a6fde0c044fa9c14b4fd111a1fb88468e85e86a393e5222043128ab7": ["tmtpost.com.", 0], + "f610f5e911c46571413c16e0b5a82b0adb213bc4cbc878dc92d2b3b80ca10537": ["usac.edu.gt.", 0], + "f612c2f1e85d8a090b22a658ef5e13e7de54f2cc417140f6e400f005b85b7366": ["rqbank.ir.", 0], + "f616571f83806ee1b6e7e82f624999c6be4a42bd9844e245cf84c54b5463cc49": ["nr.", 1], + "f61a42456bd2acf16bb495136d1faf359ca1b0d4bd27b0022faa0f68d3da6ec1": ["xn--unup4y.", 1], + "f61a831a33fa5b727e0f20182abcbf9bf672824f5fef229ae78d7c1d4b8e9544": ["lpsnmedia.net.", 0], + "f61ce76685a19cf5c7b121a8aff786e94bb0b1eeafcd5e551eb9ef8ef62d1726": ["creativemarket.com.", 0], + "f625a4b424a4f46e6e62676b8d46f475b21c8941f3f54cde9e6deda3f16a7f7d": ["securebanklogin.com.", 0], + "f62819500c600265ff3443af1f56c6a45e5e79220a5047f6d82c023e1eadfb54": ["wang.", 1], + "f62d37995bfb7604459ade799e6e89389acaff59df1b48c909054f1a4f9c11f1": ["indiatimes.com.", 0], + "f62d8c4b063cb089112e46d8d1396d4c2978cc896445c147650d32f35a6fcfa1": ["limited.", 1], + "f62e19db1886108287d7625e292e50dd3b36d1f157b7df478a067b0cf1d3fba9": ["financialexpress.com.", 0], + "f62e66798f1813e820014359bb133822cff1f6317f6cf992d51ed5dcd35196e9": ["nzz.ch.", 0], + "f6325ff2aed5dcb586b7726bb608967f1ea4335797648bac793a539b261d9844": ["accenture.", 1], + "f63aa96d23d9bd54a9c52337a2f289a0e6cca26d239d64ffb1cddabb2f0fb913": ["tabelog.com.", 0], + "f63e28c6c96c2f5c2e488e31811fcd57eaed823ef6d7bc024e6dccc411d35913": ["hentaistube.com.", 0], + "f641e1d0c43f481b88df090ab08b94e34c008045fa1c4c95c647513fd8f49765": ["dumpert.nl.", 0], + "f643cea5b7722450f169b7fa0ea8cdee6bb6465de87130a0fb25d473062eb992": ["enfamily.cn.", 0], + "f648c62fac944d279e35615919fa61e11d95111926ed92fa8622ef036a2ab1bd": ["ikea.com.", 0], + "f649068a641ccf7ba2659349e8f1059aba1cce71a691bdf9db88f08a33da248a": ["iso.org.", 0], + "f649c2a957a615dfcfe1622ad56196adc9622c60ca9222d7277adb2158a223e8": ["drom.ru.", 0], + "f64bee55f67214aeae3aa013e7ddba0c7b27a38f5759d536c16f7de06e97992b": ["escortbabylon.net.", 0], + "f652f0e2ec6b64c0d1da723cbc40298afa05194608399b0d136f0a4425563b6c": ["163.com.", 0], + "f65a429cd5ed81adfdd0a87d13aa1112e29cdc1604b937cf17042f17d9d45ca5": ["mysql.com.", 0], + "f660c1038e079d213469d14cbcb8532d9e76802129a80429cc2763c848a62b6e": ["freebitcoin.io.", 0], + "f661b00fccf44eaba0265ef9afa68a5d7a44ee336d696142dee352170550f55a": ["softbank.", 1], + "f663dc80058fff11b47f5b0978a0a7c04ea7d13a1daf3e815abd4975f10aa6d1": ["jtb.co.jp.", 0], + "f666eb6c21f0d4d513d2f486eba15ea5b9eabab27978ff7143870af12435099e": ["love.", 1], + "f66fb973118b3ff75c8b6d9f4a75648d2a65f3381b7f0cd1604eacc90d3ff9d9": ["myqnapcloud.com.", 0], + "f672ad252151e9e08f19b6dc6a0270300ebd51c5b2146ab2fa6d355d71588118": ["redeszone.net.", 0], + "f67d842df5785a94d236699976d9781a6f5dfbc386394566707db08818180fa1": ["nitrotype.com.", 0], + "f67de09aefe4ff324879e24954ba867d6b007fa08fc614cec065336884a43f70": ["dropbox.com.", 0], + "f67f08d5da7a29a30a2fdd2af7eb1e67e4b032123069797772a427c9937112a8": ["indiatyping.com.", 0], + "f680cb12dd51fa60f1cf918d5cc39c7a8498fb9dbd8b63b2120c5e12be0fe6f8": ["comicat.org.", 0], + "f6893a57e500a1ac92cd7c98613c2645bbcaac9eaae81fe85dc476d32875832c": ["hair.", 1], + "f6959b90014524efcd53b2942dbe6c2e6892202f7fac65ff9313a86836935384": ["mondaq.com.", 0], + "f69b297d3ba07004fd3487a2b9f3a6709845dd81258dd7a156b4005988f5f56a": ["sport5.co.il.", 0], + "f6a4ac645ee994308681e01cd5b6f5d7e6cdaf6df7600d1efdd833dcde80ee20": ["gooyaabitemplates.com.", 0], + "f6a8e4cb9e0f5026e81263dbbf18ef8d99f58a2d71a5fb6e9a9e1ae79d22a06f": ["xrysoi.pro.", 0], + "f6ac8d68430b2f228a90660bf535c049699b334bf0d3538ca559e2353d89f7be": ["songkick.com.", 0], + "f6ad65f1cef11b8d17c6ab5ed1d83ace352a5ad666a497d3de541ce47dd2dcc4": ["pornsos.com.", 0], + "f6aecec49bf05fd25764bebb0cb3a7ff623fadf6fc34026f91f9920da1ee5c50": ["nginx.com.", 0], + "f6bbb92dee4db879d4004b5cc492a6c6d598c1f61274657c6ec3c7c12f6d846a": ["googleblog.com.", 0], + "f6c027c96be763300857c69b58f1f6626106037ac5fe1f279f9ec737b51b4106": ["fanatik.com.tr.", 0], + "f6c6dd084d11ac6e6db967dbb121eb1c8a522ddab43f317a883fe3878722987a": ["skroutz.gr.", 0], + "f6d3b72b5d3568746bb54090e3310751bfad30a251e4a42b01e514e0a7614066": ["goodinfo.tw.", 0], + "f6d4bf3d1991e5a1399de0f27399ca7b03d40d2394c51c0054ca899ee6fc74a1": ["starrezhousing.com.", 0], + "f6dc42c139a6687090e1e7095b0ad68a3f685b814b79d6f54884d2b5af0c5f0a": ["doczj.com.", 0], + "f6df0f8ce3b9482babdac733a34f7f25f78c28b9780bccfa954ca78f2b0705dd": ["theupsstore.com.", 0], + "f6e204f7a35510a068b11578afd0cad4bf0d25c2843381047b99e248b5fc0b68": ["konsoleh.co.za.", 0], + "f6f26464ebba1d86188e419ec65fbd939721f2d5bcbcf114c76cd2f896256249": ["amateri.com.", 0], + "f6fb73e741e6c9ac5fe04eb52aad383d3245b4f3e0e5cc5fe0abdb7c2cc568f9": ["bizrate.com.", 0], + "f6fe9d737a46a9f06fbc0716eed0d96cf482f0f6b53a41e0fed19bdae353c121": ["xn--h2breg3eve.", 1], + "f7170d4ea08566e740408776ebbaed0b6ab2a19885c221e5032bc4d286663fc7": ["leclerc.", 1], + "f71b14ddc90ffc99a88f488bd7719c64ea0c9361837e1db6c3f7e9254bfdb3d1": ["sabb.com.", 0], + "f71db197702e23f1288b60113ae3150f05b4afe0b992730f0e1c59e3268e8827": ["mabinogiworld.com.", 0], + "f71ea5625fcc982f3287e3cfdcc61a288bd1d865abd10bef3fbfbfc5435a887f": ["bobibanking.com.", 0], + "f728f18678c01f88e7dee2bfe143321eda44f08c50caea5adf2a3f9081a0ff5a": ["forobeta.com.", 0], + "f73e0c796017b7a8b0d7b667dc27a305f16297127b8348b7f6e06183bc7f8bdc": ["physicsandmathstutor.com.", 0], + "f740f6eed8bc3472c57803ce9a391171efec48a1ec89552e3bbd67e27bf9b5f2": ["uni-koeln.de.", 0], + "f7412cc5d95a5dae1740d93a1120fa3f719ab5c90d8a151a3a801245f2520c67": ["mgronline.com.", 0], + "f742de85b49c0f954f5f966ff55f82da8f69c6374049874e8e9a7119acb66361": ["myuhc.com.", 0], + "f746d4f4ef60a9c8156b2ea9c06c0e60db9ee7eb939705c9cb08dad1b9c3559a": ["isciii.es.", 0], + "f7478bcbcb29897fdd098eee6f421f435f6864b1783ebb1610066ccaa612ab54": ["toronto.ca.", 0], + "f74b98ee2cdd785756a51f4c68620853c43378237fff026597e649ee663dd43f": ["xn--c2br7g.", 1], + "f74c4463bd72cafccaf5ca7e00c586812f467f5ec19716654b288cdbde00863f": ["javtorrent.me.", 0], + "f7501f22635bb3040696f8caf7651c98b7f10fe00fadcc76032542bcc9fa6d8a": ["duplichecker.com.", 0], + "f75eaf2b08819bd553f5136feb3f8f259d840634e0782990c7487d4784ac9750": ["nbcsports.com.", 0], + "f75f5dbfe39924e63a2f90686cc116b69b9ff339b589d00983c127a9c351b630": ["chinanews.com.", 0], + "f763e486d28bc1404747d9005252063991a35c1ee44168a18a1d89006e852144": ["crntt.com.", 0], + "f764091ab59ee095cee1f5fd059d24def9d0cb3523a5fedd3dd1a409c04a2957": ["icourse163.org.", 0], + "f7799bf27f794904b5ee4473a477c9ca55986cec7ee94f1c3bb541e2410a231b": ["cda.pl.", 0], + "f7884087477eca2e87dd57e6dcbc2d7d08c008454cc3efe2c27240c26168a782": ["ultrasurfing.com.", 0], + "f78b71495166383d971a03654a03cce69ff365f629e0b3b94bd00ea50e642037": ["kukuw.com.", 0], + "f78e7aa95f95fc1a984790d8e100223cbaf7c0f6c46198251dff5c090261d596": ["freshbooks.com.", 0], + "f790ac35b5f370c175221874049ae0d4f898b260eff849f656a7ad1c6a61b344": ["ozbargain.com.au.", 0], + "f79fc712ba67d0aa1411d8b93a2682e3f6e7d971ff0be608b064d2213d477917": ["cologne.", 1], + "f79ffe5057a141729160c674f22692847324ea2a50e925ce80bbeee73f2fe5d2": ["rent.", 1], + "f7a53126e083d926e830d16a11c72a818cec1fdfe879f1eecd49d0d1e1b5d59a": ["altinn.no.", 0], + "f7a7f997e9e5d87af078f7fcde1b0bc27b4ab9afc9d50ecc90fb30f5fb409d40": ["venmo.com.", 0], + "f7a8aad7da673ae49150d4440ef7d8c96282d3b7227bf7e36f66644a3309556e": ["gvm.com.tw.", 0], + "f7b1a4677d0e191347fec52de797bf249c9d9c6cb6198efa37dc37abf17a8e37": ["akb48matomemory.com.", 0], + "f7b3dc04a546a51914f9a81c698a668ee865f3137ae0052e922fe1a445db86bd": ["almubasher.com.sa.", 0], + "f7b75641cf98a717bb7c9bea3b93801ba089b3f1fc4d09bbb3eeb66687ad1dc4": ["polovniautomobili.com.", 0], + "f7be00e869c51da2c014df91392d604efa166524b70f9a14d6a9c345861b991c": ["elcorteingles.es.", 0], + "f7c040ebcdf0c5aa8eba385d57369852f0d0457a7dc8fd7846011b214bee8d79": ["surveyjunkie.com.", 0], + "f7c229e7bdd83bb334297624fa2f4b6fe5056f81597216a12a0352a9e3ce1285": ["my-best.com.", 0], + "f7c4c8532fd7824831602d0a78b678ea47947af1d55b4220163ada07f8707d23": ["cooltext.com.", 0], + "f7ceb5f3ef16afbbc1b6e98958286b6764d643f78270abe3ef9f6b4a20a8ed10": ["crutchfield.com.", 0], + "f7dd2ff5e4154c101047ebeebc79bb2e4036e3c0b4cb2c3977d0cbd95c8273d7": ["dof.gob.mx.", 0], + "f7e1901334a5ad25fd2e5042deb67216e35bff37c366852a9612921885d746cd": ["msd.", 1], + "f7e37773635facb58c898ab32234cb515444a7f96fe0f92f030e7cae21d18725": ["sc.", 1], + "f7e6463653871da122895a0da9fd20f59b0322ea54f4e635f2baadab277cff0d": ["altex.ro.", 0], + "f7e7090188f67e61070b35b1a80b1b1e46903e11c07e60c71d93d5a189a04dd5": ["lincoln.", 1], + "f7ebbffad762258198c66143b6e16577d310ecb4d7a7373e74e3e3d27160fbca": ["publico.es.", 0], + "f7f34707fa05f53bfd4c532d315464184711e38b536bc88aec97b545055743ed": ["bermuda.gov.bm.", 0], + "f7f4853e5956e8e59ed4dbb9c52f8a02bcff952f0193a730a61de2c243efac77": ["universityofcalifornia.edu.", 0], + "f7fb98c093a38281ff55a20758c88a4ec6ed22c81d1a8d71c204b978eb81e2d4": ["cpa.", 1], + "f7fe6f0f82e0f13c944e5bdc53083e8aa28616e15d6eba68628a1562e33fe62f": ["docsend.com.", 0], + "f806445b84ef4fd46cb43e20a4d2391d9e9adea2b7f2e8d90bf29e70b36722c8": ["sportngin.com.", 0], + "f81741bf04484c4b204658fec4fe7a3bde74b575a393ff910c658823f90a39d0": ["oe24.at.", 0], + "f823da897a0f2296094527d1ae47a84235e8fe2e12adffc4ca914c4a093f96be": ["softonic.com.", 0], + "f828093d5881b4a0615a3a06a940eb2f25ef3e183db044e073ed67609c783f08": ["idrlabs.com.", 0], + "f8363704ea47b903f2df0d3950d6ead71a7177059f04d6202f9ab2b86e754eff": ["gop.", 1], + "f8388f298c687161417705dc4307e24593da18a00097c938d03765016e5a2092": ["microsoftonline.com.", 0], + "f83b8b8dce93fcd749554f8b10886b4ea7c4db0304db55cc3a071b4ac334742a": ["hani.co.kr.", 0], + "f83b92dc0704b809a47830ec525b22cebbbaef3ff108cd2a96b86e927e18f386": ["neobux.com.", 0], + "f83cb40cbb86a23fa3a36f0607fb6d8106726f8f244ec7b644031d7388241512": ["altium.com.cn.", 0], + "f83f163703743d69524f8112b391e1f3e7815436de14a5e005d44b6710730bad": ["tdx.com.cn.", 0], + "f83fdf0eb6446c880c04a479725b99b161f3b8877f29bc01a991c068d211593f": ["travelers.", 1], + "f84185260b0e865f7bafa35aeeed8bb1ffdaaa91ce624092a9b20a27511a161e": ["com.", 1], + "f84353224eff1cbcfeb3cfd0fb998eba3818d046d16e9f2ca32ad63d3e173efc": ["virdocs.com.", 0], + "f84a81c09a2babb378a77cd3f86f0df2a22ed40661b151c993674c97c7d87598": ["archdaily.com.", 0], + "f84ab3ed161bd3f336620f1da3c99620f635d6dbdc158d38b829fe1e2270fbbe": ["e-himart.co.kr.", 0], + "f850f9f161f37bfdebe3e5336ccf46f25fd7e0552e5eb7b8984a842eeb6a9d68": ["softpedia.com.", 0], + "f8565094214292e009ff25323ce2fcbbf33fae4dca25231328b039e22b730e8d": ["mw.", 1], + "f8567157725e7b5d8232c72b805308d47bfb6640c17fa1a2d5c0077dca1318fe": ["alamy.com.", 0], + "f85d0bc2763c64545880fa541e3437c608a660fc3b5b08c1a178ebe33b881a32": ["saksoff5th.com.", 0], + "f85feecc3f5190887db7ef1b87dd14902c47748fe3c62186059b6a58d24a37b3": ["kanzhun.com.", 0], + "f864896d28b6c19780281b5b722d4ca78186ecbb0e96137c4e1d0ce65c230f13": ["cafebazaar.ir.", 0], + "f8670be6137ceb20067316548979981b2feadd453a20d95cd4229651b8f15428": ["mumsnet.com.", 0], + "f867e00ad29cbe5e6f8fa40f97e054cb7ed91e1a19873e1c89a30294ca8b6b17": ["newyorker.com.", 0], + "f86a61b2a931fdd9b09fe8a15f882bd1cec053caf50eedbd7bbb49558b644313": ["sondakika.com.", 0], + "f87582212ce1a18dd90e676c07123e3fbdff2a9ad70955f1f748012f2e51d7cd": ["bnonline.fi.cr.", 0], + "f87b295903e25a75d1768e290b52ce6ae9e72f6b69df3da4fb03a4839cdbd7d3": ["bitchute.com.", 0], + "f87e1b94acf19f201e4d45dbcab1fee3c39e09f92685ce840de1d919eadba50b": ["zol.com.cn.", 0], + "f887a0d5b156d086cc8de3e581b8d5c563f2ab378a96c96874aebe7f5ee98c68": ["pdf2doc.com.", 0], + "f88951d1472b8fce534527cf83971cee79267b67a506836f569c012c04467518": ["fiocruz.br.", 0], + "f891229ddb45343a52e90ddd647b78d22a1c915d721ba3591d85c4ebba6cd206": ["free-freecell-solitaire.com.", 0], + "f892b2a43ab510fe3693ed26392ea8a99f54ed39a581e8b73aede3b7bdacba70": ["xn--w4rs40l.", 1], + "f897a6f2e5cfc11ab0291d3ebcab72cbd50d05afe8cde3adc9eb682aedae3217": ["peekyou.com.", 0], + "f8a80558b2334b5f98389899d94d9f58895e80e2df883006817cb2dd25b2b957": ["morazzia.com.", 0], + "f8afdc63b069b2314a317245d3874c4f067a91308278f3f6d9a92890b33fa8ed": ["microcenter.com.", 0], + "f8b179a9b1b86cd836915641f7f545c8c1acd7ed24f316f8272731d2dfdc75ee": ["dualshockers.com.", 0], + "f8beb6eb750d5ebe8bf810f2c0ec73b393ea2c2329da35482823df2305e770d1": ["huijiwiki.com.", 0], + "f8c16295f81b51425853b08570c3fd69d9d37c9bcb3145448b315c15c6d0cb94": ["stardewvalleywiki.com.", 0], + "f8d8ffaa346adc5a504e3b2a2fa400478241e108e0fa4c8d209d295e1c04c724": ["hnu.edu.cn.", 0], + "f8dccc3fcf5601ad8cccf44d4d4994508379beafd455fe7db343ca8e023eaaa9": ["rb24.ir.", 0], + "f8e03a7756922f302d1e58bdb2544b18bf5d414d413397e0d0d6e93185628236": ["ceskatelevize.cz.", 0], + "f8e088d50c3ec9baeb889f9955eed6da9f8ada0a1e02fe8662a145c294b89e96": ["unsee.cc.", 0], + "f8e1b570c0545bb2a360edb6067b2994c1e83a07ba579efdaa0ebd3cd6ebc2c9": ["bosch-home.com.", 0], + "f8f2170f1b4a9a08c280638c439e306d14ed906b626b74c13dffb66440261694": ["ikman.lk.", 0], + "f8f28c137dc42a56de939e5045bbdd84a3b2c7846d2fe50b740daa8b81370250": ["predictz.com.", 0], + "f8f2ee5be230ed6c264d0d089304a3b6c337080555d578f88127cb5a2877a85f": ["utorrent.com.", 0], + "f8f384f4ca99486aa5d96f60065a5883216f00416052c3b0e0be48a1c93b599d": ["gl.", 1], + "f8f7356d44b5a5245a2107cce81e5dde474ed2be6c6eca546f49a8c9ac92faa6": ["loopnet.com.", 0], + "f8fab2766787df6d3fdbeadd9fc64c54a9d8e8e4d6452717251f517897ecaf9f": ["mvnrepository.com.", 0], + "f8fd5b8c7bef49a71b75b4942f77ee5c2bd791f54ea910f7949455abb0b270a1": ["xserver.ne.jp.", 0], + "f9047205371ee12aec40d51b16d7dcf655f4821d8eadd9625c348d15b9305668": ["candybar.co.", 0], + "f9069043dd307efcc97e9e474ebd42c8c83087425d858c8d195eee3ceec89cac": ["perueduca.pe.", 0], + "f90b297fcd7791281e003594f45e79569091abc397a6eb0e823770843c896d8c": ["paradoxplaza.com.", 0], + "f91324a2f7c85c0a2658010a3f0ca74461ae104fee333a608b02b4f96b1244f3": ["sb.", 1], + "f914a3600816cb165875bfaf207d8cb4f88f2c4b43ede19e1a3c081deb18df84": ["tube.", 1], + "f91841c9c615cbfc67ff130558cc4fe5db9b9244b35b1cc0c5faacb93c2f7d4d": ["pcgarage.ro.", 0], + "f919ae3359e694328754faafdd47123ad7b4cd015cd5be9b41b102c57d6c5dee": ["fatalmodel.com.", 0], + "f926295bb5d61cfacac7e9ee073b84fb0c20db4b4e635ececa797bf4aad0fbe8": ["wyylde.com.", 0], + "f928c3169a6a8e7602c76962110460499a20063c8562d0e2d109cea30c2a9b0b": ["miamioh.edu.", 0], + "f92c52c56d660aeb5e6566713675a865f156ae016d79058b73e8b8afe8b03440": ["animenewsnetwork.com.", 0], + "f92c98ab0d3699415943e424c043813b812e18dda7f0e57f62364c6d333789cb": ["eglobal.com.mx.", 0], + "f92ffae9199b5abe796e22b53b967e0b1e8883027662f7fdf1da094c7bd6b9fe": ["fotomac.com.tr.", 0], + "f9304a76be4131b86ded8a184938766e22366424efd9687e1141a7741bfced55": ["livemaster.ru.", 0], + "f934635d804c1cfb4e64c75c8aabccfecb1df9497036a8db0552609a367e3479": ["onejav.com.", 0], + "f9373c63eda488c535800cfdbef88debb1a3033341dd0e3d58f5049ba71f971e": ["confirmit.com.", 0], + "f9477872b4278e68b935117f40b707caaa3eeddaaebb1468b67aeb421a165da3": ["shimano.com.", 0], + "f9546e385ac94e89b5dfead57cf151a739f19c051ffc5913dab7de2bb45a4beb": ["commerceinspector.com.", 0], + "f954fdbe86193b0efebdda87be62635cba2e5bf93928d113becdb2116902d75c": ["ato.gov.au.", 0], + "f95d67fe56584b4b9dc7b976cb611cfea77b0269e99e0a53f7d085eb85d4d16a": ["pornicom.com.", 0], + "f9647b2062e483c472bac544a863703a2d09a49364d3baebe2d32b6415c61e6e": ["office.", 1], + "f96495fd628ab414b759ce75a3a7ea5e652eace858dc4a2af9078217602d6e86": ["sexart.com.", 0], + "f9697604cd58f8b8e3f9c955bc3db851948bc4f080f440359a7ce87d6b4d822b": ["glovoapp.com.", 0], + "f969951444234b993937d94a0e6841d6321266e503b19f7b6654e0bc976bc30e": ["ar.", 1], + "f96c9a52d030b5a979666d4e9f7a3568b6d3d8d7ab5cdee32bfa5afea4387ce9": ["lojasrenner.com.br.", 0], + "f983827ace0bff5aa7b4d8f323325d8639977fe03f4b923a9dc9bc8cb02c4fee": ["contact.", 1], + "f98575cd179abed282b78166185103e3280d29f3efc1d4e1257459317c572bea": ["fcbarcelona.com.", 0], + "f98675f9ec93d4c24249f73b8cae1e18abc24f97a11ff64fac7499aed51ccbf8": ["wuso.me.", 0], + "f98acdabfa301c291484c8b5e884ccded6f1e8afc618e466ab35ccf28e4da8c7": ["activision.com.", 0], + "f98e8e2f6dc02c9494a7ff40220480b598a4eb0a3f606c507d98b67492d2be13": ["lasexta.com.", 0], + "f9921c7e5e18c08e479969083ed8a7313b575d25dc8fb1884ff1e37d7ad974a4": ["anghami.com.", 0], + "f99aa909f5600efdf3dc62174c066420d76f878fe80ffb0883a11b0d808e9220": ["temasek.", 1], + "f9a28e9e5155db8f8827b11c03ea37f4fe66ee3a723db44dbe4a4955f1e4d4b0": ["officedepot.com.", 0], + "f9a5ee61c3cfc9ba06620e7fb4ab57c02fca291d65711c091a4719887f564fb5": ["tokyomotion.net.", 0], + "f9a756e9e54b16dd658170316ffa9537377cfbd3e308a58b373c4158bfd5dbf8": ["nvi.gov.tr.", 0], + "f9ac7903bdf3411ea0c2a4dbc88f1ce9b7b60e17624b061b4e6d8fa3c8e8bbf8": ["aeroflot.ru.", 0], + "f9af4982f62907633b42cfdecbc9691d2bf7d44e114e37f45ebbe0a91a2e9914": ["ipsw.me.", 0], + "f9afe6c2982f80dc4d43240191de350d84c20a6a2b87e2b38a71f969c0028d1f": ["hifishark.com.", 0], + "f9b3a404cb0b5e57509dfa3e737e86a3bd5e4bdd04ca401277a5f978da20858e": ["getintopc.com.", 0], + "f9be90f39653a53675acf5bdbce28c6fa68d64be0d711021a92ee7517ebc721c": ["sothebys.com.", 0], + "f9bea3c9e4b4ee33070ec5c922f6bb8d32e24c9bb864594d311b96784b8560e8": ["defacto.com.tr.", 0], + "f9ce603a3ba6eb390969d5fc7508809240e2adf793674b17caa3660ee772521d": ["flickr.", 1], + "f9d73b31ce0db6230260aa402c406ab9b4be521c11bd96399df2d2b2e7d9e925": ["unb.br.", 0], + "f9de7f827c17f3021dc53bcb7958baadaea1e32af80cec81f87b41e40f1c4686": ["wattpad.com.", 0], + "f9df0f7efcd5a70cdcac10d1ff0111c7598d7a712cb0af52c543af39ee43883a": ["systems.", 1], + "f9df1224f1b8433f196e69b86ea591c20fd5842cf317b1782d7b8fcaaa4f8ece": ["drudgereport.com.", 0], + "f9e89a41bbd3f7a232308032f9b5c2f627f1bb2e4ff5c731cf6ecc8787890791": ["bakufu.jp.", 0], + "f9e9b39c3cc8c78ce1073f7ae10f9e33ae8af4f4b31f2e6765579c7f0f46bc45": ["resourcepack.net.", 0], + "f9edbf4da32e69ade4f2ed24502dbad147901d9984915f9a228a9e9a0a571367": ["mootanroo.com.", 0], + "f9f1ce465179b2c76faf52be16c452551a8b77a9e787ad3b2a896421412841c6": ["fragrancenet.com.", 0], + "f9f814c732e367d1778d41e7a83870ae2ae936cb92934f697a6210ae06a03df4": ["trulia.com.", 0], + "fa032bd22f8be6b8d4abfc4fdbf39988acc96d8bef4929fd0cfc16db27d3fb5a": ["wordstream.com.", 0], + "fa08d7aa26f300674a6e7103f8c91fb25f498b8cf7388dd1a86a25f4d44a55da": ["divar.ir.", 0], + "fa0f8a5a59b344b131cb72a229b8281e68f8201c3e07ace3faba54a67e75d371": ["tienda.", 1], + "fa11f357c4349a7aa3274c12ad302df439207e1953afbcaf31dc6b0a40cc50e0": ["corrieredellosport.it.", 0], + "fa12f5a9a2afa9fc9454457f54514eabdb5f9dd62bfd790bcc6743065004d2e0": ["lendingclub.com.", 0], + "fa144da1d7113e342585a4f892c6c76f17c53c8402e023a4ecf0eae815e001e2": ["aftonbladet.se.", 0], + "fa169a28cbade9a84e1fe53d5922f9742eac729378e203b0747b884f5afe6e4d": ["tass.ru.", 0], + "fa1a18b1bfbbbce8e4e249df3c4a702e2b747589f8bc06319601c1111e129719": ["o2online.de.", 0], + "fa1e9bbabe9d73bcd870c862ddef9d62ecea878f8003e4943c70cd9c13d5bd5c": ["famitsu.com.", 0], + "fa1f3ad65a81951e257daa2b05339ec21aff010aad647256a2d5d83fb926f808": ["zbporn.com.", 0], + "fa232a8ac526b53d0dabbbed990ed817ecbabe34399ee5e4d5005999861da05a": ["anadolu.edu.tr.", 0], + "fa235ab78f3b0fa414be9dbb0c9fbfa93ac5f165df326379b7dafb2e949c58bc": ["d20pfsrd.com.", 0], + "fa2e6c6bb2cf5f267876d40cd94f21c895c1c8c00eef8e4ed33bf53c61e9813b": ["almayadeen.net.", 0], + "fa2f49c23f0af9fb54ae3a6c22cd76844c05454334f29257eda92f6537952461": ["zeit.de.", 0], + "fa3113509d2b2f79ff7d20c18367009cc9c985ef170a0528302b871068e72927": ["jenkins.io.", 0], + "fa36f38db2950da918053eb8e55cf9d1371f135b67658f3e4a38cd6de44bbee3": ["gogoanime.bid.", 0], + "fa44874d66604f8be1fc1c98e95453e907a0da26969122f98d65deb2fb247468": ["wwe.com.", 0], + "fa45390c92dbd846845b6039f8b08e2d6120028a7ec1a5ab1f66df3068d96423": ["rightstufanime.com.", 0], + "fa45a3dd60cf05fd6b7ca39058e40871533e7cf579f9529bc4b76608376dc2f6": ["weidian.com.", 0], + "fa45a72ecc4a58dd88dc33ec8dc582f587aa99e793e118caf4bd7ca2cb32178e": ["cdbao.net.", 0], + "fa473113a73795b750708837709ff95ebad5c12acb55e0453c0b384beccbd6b8": ["wizcase.com.", 0], + "fa47912e4de01ffff36216dfe50d1542ba7136f1da1f696ec7377e998ff73398": ["boxofficeindia.com.", 0], + "fa49ed37a1898bec4d8a2a90097e3c75573ee4e05e1605a1c593530138f0c9b3": ["creately.com.", 0], + "fa530b62581bf7765e139c7d0b30b8be53663dfa50bb80193d7dc94683ad8119": ["acethinker.com.", 0], + "fa547d140759c7198e09d07a52f4ea157701b4166b2bae3c1ccf7390a04d7ffe": ["bakeca.it.", 0], + "fa556f7e76203908167c872f7b5f0dd3ec942ed653db8d3072e99f0fbc678f72": ["zarinpal.com.", 0], + "fa5d43eb42c15d7aa2bec1cc3c7ad34addfa89f56ff6ac79c6291aafb14c8e3e": ["borsaitaliana.it.", 0], + "fa5ed8844d28cfed60955b9830acd9243df80355d96dc4fe8f8130c1973939ac": ["kaola.com.", 0], + "fa5f6cbdd5246489cdf205ace3908aff270af42a90da41cf29239637d655b6b5": ["fabswingers.com.", 0], + "fa6e907a118f7a669bc255faaeb47af327109e4ddc0f63600c33a405b38be429": ["porngrand.com.", 0], + "fa7483b18ee66d3d8241bb8c8b2d8774e3b178de269c699dd63faa5f39c701bb": ["tipranks.com.", 0], + "fa76f7aa8410f792d1d01c40cd201ee8956d20ef7c77b1d43c9291efb155ceab": ["habitica.com.", 0], + "fa7b6093b827b0507cc12e3aa366b6636f8b7baa20bb21eed98518b98dccdaa3": ["university.", 1], + "fa80cac8db44efa88f30d29b62671f913393e31febdceca4c4a68fd2de81d47d": ["ph.", 1], + "fa838d170f36bea4ac822f5176c2a879fbf13e967a11a237919309bdf7671dc2": ["onepiece-tube.com.", 0], + "fa87d53accc230b742c67eff5e504f7c6eee0475839940bb286d4140a9ebbe1f": ["sanguosha.com.", 0], + "fa8904a1d401a3d7085f9dff2e13aa395f3f3be975b9db4b9036ccf78eaff57c": ["mackup.glop.org.", 0], + "fa9946b4793d0584834684f0bf0b7d3102b13893d7764d6d85a8883f16ed7cc3": ["point2homes.com.", 0], + "faa08b00bd1e92a30286d095f06d48cf23588e9b31999b3b8a479341b1b27e23": ["fanruan.com.", 0], + "faa2ea1cb467ef6ed104ca0f9f391070df37b08e615f4b6d531012a37a7c1cf5": ["openbible.info.", 0], + "faa6841ad068a365e438983a23f1a8325189e2067525847681ddca182bcefd3a": ["theculturetrip.com.", 0], + "faa8a7fa3f886e01f40d5b4c816321d72417d754274f589b5fc10bb60b7df74a": ["jeanmarcmorandini.com.", 0], + "faab5bab2fedc3934d9fc0290066e3ea34956e6a95b71beb61f495d7c3c70649": ["origo.hu.", 0], + "fab07132523341b623f4f85d7b55f3c23ff93db747133bd904ee80825c35eb4a": ["producthunt.com.", 0], + "fab3b787b67d37f29ead5b4b5d89e178dc986d24a8ebf49b9a6a597db6f9408f": ["honeybook.com.", 0], + "fab3cb66ca0169a2bc545f9c4f181a3c0f0292a1c299bdf0611db5b339d3008b": ["xinmin.cn.", 0], + "fab5b905ad82d4a545832758a80499c02ed3676b8d423f48f9ad7a83c0e42953": ["volkskrant.nl.", 0], + "fabde6664ad16231fea8d5dce8b489e338181ba5d9a004026b133a2d117cdd6c": ["shitaraba.net.", 0], + "fabf46dfc6dd6f74fdff9681836aabeb7bde897500e24422079bec7d3a27d6af": ["mentalfloss.com.", 0], + "fac506bc18350df88dfaecae71f3c9331a2b713a76e932308997e1b782e95f81": ["nf.", 1], + "fad8071a01df506e1099c4a82e1c3723121f0565327b50ec86be6a0397193071": ["boston.", 1], + "faeb8ecb698ad5b8a0cfa95c8a1a0c318daa8cd53177709642f58c5f10ff87ab": ["amica.", 1], + "faed333845861c79b2aefd2a319d99bc99199fef7fe0153f73e673c1ec5cc028": ["bancofalabella.cl.", 0], + "faf38f6c4dd8e0deb4c3cb114578d1ba18a401e1b63d15cb46e2c69a581521cc": ["exness.com.", 0], + "faf7b73d7346503162c4708334cb183a9cb785461740e25ea65eed30bf2be0f4": ["spanishdict.com.", 0], + "faf7c779afc97534a52551b631494956f29732cf18cec64fbd74bd9e7aecd0c2": ["cryptobrowser.site.", 0], + "faff5b1ed355e352fe435ee692ada56990d01d94ef178d9ba2023989c9e5c488": ["ontraport.com.", 0], + "faffb781b5289f0b1edde9c12ba5eb1ee4160d0f542bbaacdcf9ef980f51ec9c": ["km.", 1], + "fb0383cecc76952e2ae50df734fa088f456d19d9f5346f2ac3953c9f8a44c38b": ["wappalyzer.com.", 0], + "fb06ebcda108d986893b9a200b13a974725f55af92b4e06e0866e13fbc22f248": ["yzu.edu.cn.", 0], + "fb09f35a7b53852305c23349f86fac1348b33375faa2d85f46e4587853c5dc12": ["pbc.gov.cn.", 0], + "fb0ee94304221e823dcff3f9301b0334474d43f340571e2f4febc40cdd446dc4": ["clinique.", 1], + "fb135c54919560e012c6bc2cc9468911c3d99f74f85f98e2521f73d77700f724": ["furniture.", 1], + "fb15f0dc8d2ba721610b6f9093fc22cf02e121e231ee399fd8175c7698f14d42": ["gelderlander.nl.", 0], + "fb1adafafb85c875dbdddff7baaf457f97a315305d8e58be976a0720fd67beaa": ["goodrx.com.", 0], + "fb1fadb6317687f6693fd3f4aa1fbe6f11bb941e12ffc4c44967f045d150beb9": ["fastcompany.com.", 0], + "fb2a49185ff85e19b80a5046bca671f1c2f39a41eb2b94de092724f5e90d7a40": ["vodafone.de.", 0], + "fb32ad49a00c0a8795f66bb2e21a6268cf657b4916ae42eb200d1cb2d7e7a74c": ["modland.net.", 0], + "fb3891e04ee474db221c1afa6dbaeb151f04ce071f6c72884202f89fa2f9e229": ["culonudo.com.", 0], + "fb3c2ee138c2f5261ce34fc6bc09826a96558941b125f1ea745c71e2e66322de": ["englishclub.com.", 0], + "fb3cef69c0e67f4930be657acc295b5cfed3d6e11fcf9fd05d8aeb0ffe631bec": ["kuai8.com.", 0], + "fb3eb2d4b38d4461324c534da94b3b67227af29e40f843cf5023540353f9211c": ["tecmilenio.mx.", 0], + "fb4befca5363e4636e6b64bafa899755855809eef1e837085d6db7cb1fe9032e": ["brainyquote.com.", 0], + "fb4cac08d39221dc89e517d5c7eb434bd4558620db579c5fc905d7a93c34fb41": ["sdsu.edu.", 0], + "fb4f11d34d676c2b44a149f7a0b88879ee5dd36dc6b0039b5f1d576efd229aa1": ["hardreset.info.", 0], + "fb5b400e64fb171579a5a4aa39fffd5f8568acf1fd7de16d93f5cf863429d836": ["legal.", 1], + "fb5f05480d45246b0ad82be1c71238d9da5392d20e2a31e7fdd474dadc0acad1": ["merrickbank.com.", 0], + "fb6ded3251bb42237f9ea0c1f5368aec34714a6bf9894657f05a506ec876d8f5": ["ukr.net.", 0], + "fb6eeebf1dcbb83d399a471f4ecf6851f734e79c9b4fc6345f40f5ebaf667515": ["seb.lt.", 0], + "fb796fc3b5b9f707d13e2bb88a05ede8cf2d78d5f65d4125ed454a4d0cfd5425": ["letsencrypt.org.", 0], + "fb7f16775eb0af8d9bf2e3a68e80d80a0d5e8402306a0aa0d46a48db8a9dff26": ["wickedwhimsmod.com.", 0], + "fb856b66a047f5dae05de362c29743a4dfc64ca4115fbecc0ee75bf508aa8013": ["wikiru.jp.", 0], + "fb889abfd28ec62f00b4964dfb5c5c8da3d59da5080797ee38d29661951cdf91": ["athleta.", 1], + "fb8ca3486bedc896daaaeff1ab2ecf41c0006ef740c1ce3ccb3f526976979b0d": ["liberoquotidiano.it.", 0], + "fb8cf80b3e109f5f1d083216a09a9f3c90809a61755e3e57d54b5c94a0421526": ["mail-archive.com.", 0], + "fb8fe164f662555516f47dbdf0caa3e9aac5aa55b9466b2275fb230498a785dd": ["uhc.com.", 0], + "fb93722bfcc058fddc582e95e854dcae2ef62f305b9f79959e6bd2a360eda86c": ["noor-book.com.", 0], + "fb9a608357f4414b86adb0355c318b2b3cbeeb0a9795ae49af6de7a70c20eb38": ["bayut.com.", 0], + "fb9addfc06abb76ea427e6b9538f88499ed9035fdb701025e5526f73845b796c": ["privat24.ua.", 0], + "fb9f26f752d05e4251469160f84fda4f19a088c88c4ed77e2c9905f525a72d93": ["arkadium.com.", 0], + "fba39ea5f4ab9c594c669e0ecd67fe04562f7c4f33d7de030b604ed5487521f6": ["nta.go.jp.", 0], + "fbbd05f8a791b9ba1745b62ad10fdbcb27cfc2b9f461a6ee37228bc8278c7e7a": ["cinemay.buzz.", 0], + "fbc9027d194b06a8f251668d8cdad8bafbea91fb7044d877a3296e63577b7f82": ["uptvs.com.", 0], + "fbcb7bac23d8d7d1caef73539284483ee9b16a9d093a49eb00f47b874e3d5625": ["xvideo-jp.com.", 0], + "fbcdcbbe180c1e88976ced8c0bdffcb67f0acf6370c4f07e003fec3285e10765": ["sketchupbar.com.", 0], + "fbda082e79787c1f87491e8a2aa9c2d3505883a1fb6f0c7b526a4663d4b85cc1": ["dy2018.com.", 0], + "fbdb4ad4797f8c4fe32fdbb36409addbf062d9c9508e5a39e4b47ce80ae898ca": ["svuonline.org.", 0], + "fbdd5314df89da858a970c94cc8741a96ea17352f2a9c396662eaef7152f27f3": ["futbol.", 1], + "fbdf2c214ee86d87087014f4758158cd742fbf47059aa9e449ed0ed155e848aa": ["karnataka.gov.in.", 0], + "fbe2d19962eefba5004915893953b92acf5707314fea4530dccd1592b73bb8ff": ["livedoor.jp.", 0], + "fbe38937458aa14e01093704b500a25e1bfc2b0cea7083005ae8167f3ce9e7f8": ["tierion.com.", 0], + "fbe9bbf02daa8b23692842c339f00a1c1cd19a4f7c5a2402d321119f367b30d5": ["wf.", 1], + "fc0201a3ec706dcc09a629303efaf2667c8f29a433055cce2afc4f9a4dd240fa": ["fotmob.com.", 0], + "fc02c1fd297f460a12ecde885952e63db4ad88a3819af63ea50cf19e50e2845f": ["amboss.com.", 0], + "fc0613eae031eaf91df2e76758af4bf61a11b5afd0d60bc6a1238ed8c734d1c8": ["uber.com.", 0], + "fc0859986f03df2a1c373e98e983ba74c226b10fe709e728ef7f8e3395ce057e": ["ge.", 1], + "fc0926453180af2cc4826ae3eda114f7af745747d062377f0d5d5d66c581e872": ["mappy.com.", 0], + "fc100d4a0b16f820c27771bcb5964fcfca088ade5b9af5762c2795bcea9990d2": ["rezultati.com.", 0], + "fc128ce00e331c4c3e0bcd5beff51d0db240f0649a0a1be540d428690ff21ccf": ["douyin.com.", 0], + "fc1a988235015a7997520869e352f6337e96936fdd21c3cdf2da7cccc616635e": ["shaparak.ir.", 0], + "fc295aa9fe84483670e392e973bb07513201efa29a595427b93538815d892fa2": ["smallpdf.com.", 0], + "fc2d5b6a954b62858becb893165f7076cce4eb257a7f278768e1236ad8c5ffae": ["bankwest.com.au.", 0], + "fc31f35f41cf5a42dc97631d137157893913b2110224069c2781436a9281b67f": ["voegol.com.br.", 0], + "fc3377e2b8778fca3004d2aaab1a0eeb344455fa41dd5ca44fdf01da9ad5fe3a": ["ncss.cn.", 0], + "fc337bfd0abb71ff0028b1fba2749e000454bc58cf7af74876768341f05bf51c": ["granbluefantasy.jp.", 0], + "fc338b2e836cca11e1598e2c6f0e20a72447feb5fca59bb4c1af3fd0c5dc0243": ["forumfree.it.", 0], + "fc35f429c84d8aeb02e3eadf31a81f9ff969fb0f9619eb2b9fed8f2bc9bee45d": ["thewindowsclub.com.", 0], + "fc36dfc696c07995b53348e427c46752e960bbf611b430b6099373e0c38b3706": ["jeurissen.co.", 0], + "fc3b00acde0a55aa64d9b40db5b7963d26357d8cf931f02060f45198daf731a1": ["pearson-intl.com.", 0], + "fc3de6f39d13bbaa71f90dfa1e658f9e5ba37453e990848472b9f4e8316025e3": ["xn--8y0a063a.", 1], + "fc3ed1d6099e3d24049a10b6472595487fc611d53d69477c3fe42fc030f29ed8": ["workfront.com.", 0], + "fc4259cd790c955a2b37f9b534326a90cb4101d0270b15e79943b583868d446e": ["appmedia.jp.", 0], + "fc4b3a75ab20fccc26aeceab14384c14cf29d7608319cc4c29ba1c6338a2d3bb": ["fang.com.", 0], + "fc4e5efb5c15f25f519352ddb2f04625221d6aabf68a44ce4749afc3597ee711": ["utp.edu.pe.", 0], + "fc4eb985ecaa4ce1e27459215abf86f1593aad96575573b0ce33fd34abaf8509": ["benesse.ne.jp.", 0], + "fc4f838c2ff1605675eb03c0fdcc9921ebcd2889b14879fdd90f0b7db601d74b": ["gyazo.com.", 0], + "fc57bc975baf00fd73bb36c80859dd50d93f3e0b4e34e39d36f0de28830ba80f": ["mercadoshops.com.br.", 0], + "fc597f83b3d6276caf46ee5bac5657343f2438d8223b72c0e08955ca59c65126": ["pdftoimage.com.", 0], + "fc764f7426f19b490168539b8a7a6b5c9696e3506bd52b2069f166fc993335e3": ["pelando.com.br.", 0], + "fc81050bc4c9122aa023d6ab0175eb421a8dce5f7a78d870a61baf05ddb53118": ["bookmyshow.com.", 0], + "fc896568605d031231c656b9729c7f355db7a00f2fcd96191e1745e1fc990ba4": ["caradisiac.com.", 0], + "fc8b1843cc8ab969ae75a16ec17f4695267e4e6bb55a8c14681e3335f6bae77a": ["adyen.com.", 0], + "fc8d5c0c5dcaf52e3f8edc891d278f84ad4f23cb73ed207b6f04e628238871f2": ["srf.ch.", 0], + "fc9d13f6ae51f9ca49b72b2b81b7bcc291bf364aacd496ce1544edffc6cfae74": ["pcfinancial.ca.", 0], + "fca0c32c1c005323e5d9cc813d1bc5e742d291f73391314c89a9a29e7653af16": ["pnas.org.", 0], + "fcaa72f1b4f8716fbbda1133cb706cbc87bc2125324124a8b8ef3b337a684e6d": ["123-reg.co.uk.", 0], + "fcb5470b533e15e5a88813b3b4c6218bcc71e9273b74b8a0112b2241617c6a3f": ["worldoftanks.com.", 0], + "fcbd88088db02c2212ee66b26012184ee119e4088fb5fa9072752edd424a2db8": ["douyu.com.", 0], + "fcc161836f1af2879061d9bd6db822b80dd7bce40b26f0014c44f3bb1acf7143": ["khaleejtimes.com.", 0], + "fcc20a737b9bc068017126d39f0fca63b8703735080eb46b35b75f16862e3142": ["thalys.com.", 0], + "fcc3390e9d4f9e941b7a146766f2d89ef50a5fa9309e797c78a819e900742697": ["sii.cl.", 0], + "fcc35add4ef2ab03950e9c6ef40597a2361b1c1c82a4ed87ffe6398165bb7e24": ["24sata.hr.", 0], + "fcd03267241a1baf3f2c0f47177690e1b7678cf9b597884e1cbccfd1fd1b8977": ["digiposte.fr.", 0], + "fcd0cd61ed6fb1675bcf0b8fcff4875ab7b98efc8656dff54121db2dac2a3fb9": ["royalcaribbean.com.", 0], + "fcd3e9749f77751a1bf99f27b5e1d72561c6a6b8b81e4e28bd469e9d1efbda00": ["tidex.com.", 0], + "fcd7bb25eef4dff48f9f271c82070fc8515a690240b5f5a0a001e9254c6e1fd7": ["worldfn.net.", 0], + "fcdc6e23e94270a0f5aa2e8159220bac77f3de92a6292b43b630304461271fb0": ["sbi.", 1], + "fce0a68e5d37988090ea42eb3180045970c79ede072122bc6e2fafb3a683d6c5": ["oyunskor.com.", 0], + "fce9c8c3cdaf7505c8285c97aa19d7d3d5a5746d4daceae63de65c436c98020f": ["webnovel.com.", 0], + "fcf496533924a65534f5bb2dd3cdce16bd3ec8445e03b215780638b27b05449f": ["dm.", 1], + "fcf7d3230c31a4465e78ac00a8ab760920773ef5f63df6bc1415a721b4ec08f9": ["cimri.com.", 0], + "fcfe1df1f08cbe65281f735885fd9442870d965f70a3c16cb9f1dbc83c7652ce": ["fixya.com.", 0], + "fd07ed708905400fb36afcff1209b42a6725acf05d3d507dc1cc52363b4406ac": ["mlsmatrix.com.", 0], + "fd09ec2fcda3834687e85451903c7273c0785f657e1ca3b957c75951c3c23aab": ["androidfilehost.com.", 0], + "fd0cafd942f338f62d87de86d103883bbccf0a97668d97e19baf29c5f7998075": ["eeworld.com.cn.", 0], + "fd0d4f0ad72cbcdb63fa6ce1c1c126feaac11308fe2fa01c52074ea6ad5a2a70": ["vingle.net.", 0], + "fd1b95689f202c2e236b105621bd6d0e9fadd7d2a2415da2b1dd6d435bde4084": ["unext.jp.", 0], + "fd1dcc53e2bbdbc712ceff894862bf2d8302a78cca5a665a1e4d623221fa418d": ["rsshub.app.", 0], + "fd23649db8886667c8a47c57737f9abab2fec19d64b3318e7d9f10499dbbf1ea": ["careers360.com.", 0], + "fd24e8f52f6470719519db4781c56f6e29da08fde96b59a14afa3519bf166d15": ["cu.", 1], + "fd26da9f1e5cd971353d111d9a8db46bb001d75b4eacd2f1208c0f962ef7fe8a": ["pointtown.com.", 0], + "fd2d71573c9a35794a014ef8983e7e2a549ea78748a3b13844907f85b48d3739": ["afilias.info.", 2], + "fd3d2b0bebf3347f4258b261c735d9b02adf203be9515c76c79de0d521bea744": ["cllmcmaster.ca.", 0], + "fd3f198d4a4137128a6855cd348c57b2d13fdb70159e84cb554d240f49d57915": ["laposte.fr.", 0], + "fd45a8fca5d3a430ef8a4adc6d6996cb614362b5906fd6bc662fc8aa9bacd881": ["samplicio.us.", 0], + "fd4bdbec2845ffbcb6686e358581cf441aa8b4a743b4f47202deac6519db8305": ["giveawayoftheday.com.", 0], + "fd53661e2258554cb094a249e3bae38a0345cb99cda51e7b6ed001fd283bed0b": ["evilangel.com.", 0], + "fd5a71a5fecae3102bf192afa6e4c07566bd8f41fbc6934cc20fd12e5595e121": ["pw.", 1], + "fd6048316a9b1a13a36d0a8c6425126f3fd29a43a259ae9d3516a02168fb6f17": ["athenahealth.com.", 0], + "fd6b8c265d1f34412c6b6f1ffb0cb3429f8ab4e2574a72c228eea1143b6233c1": ["unieuro.it.", 0], + "fd6d104457bedd8807eb49d28bdda5713240fbbedce616d460c3d90b8131153a": ["lse.ac.uk.", 0], + "fd72ce0d05375c700aab4bdbe3bcbda9cc96fdba017232989c57321bcf1ba674": ["kinobar.vip.", 0], + "fd786ac21715d5f076b0a30f466f434e255255890213377c612e576664e0cff8": ["williamhill.", 1], + "fd832e5239fc43c45a97bf23b8581aa2e76b2ec903587f6f95dc9f61b143f5f4": ["tg.", 1], + "fd8ba2f3126362939f532506bfd9fb45f57f8b63d4ac2753bcddd8779016b7b3": ["nowruz.", 1], + "fd9054b41e6e8cc8ae42af65735f4a2056f8089cc3d769437fc7c282413ee880": ["advcash.com.", 0], + "fd907c1a7564c2f307ba2d2d24d44d6e2486ca40d316bda1277ae720e7f5621d": ["dezeen.com.", 0], + "fd9164465e080b7b39448b004e08a3127b0a6228efd8c4be596a4d3bbd55c902": ["7-zip.org.", 0], + "fd92d4cc1021aeaee5c02450f7c24feecaa83ab6b1d8fee0bf0cf7ed16ba4409": ["te5.com.", 0], + "fd933a260a6c2a547dba28356dbf2b70dc63462d88eb8976442a75d6adf7822e": ["kingtrans.net.", 0], + "fd961e07c90896dc26f4a78d5175d5d4dfa7db22c22673d9c45b24f2c0f7f31f": ["pcgamesn.com.", 0], + "fd98b230f6f91d0a0df58d88a4f37e35e3275796e211cf1220b493e6582b3611": ["czc.cz.", 0], + "fd99322465971e0b726a3f49e30eaddf9eb9a37a51b484a9a4b9c409a17b99bd": ["lacoste.com.", 0], + "fd9a8bc4b000ff2bb987fdc17f166aafbd3324d01776ad6c4692bf10300668e9": ["totalwine.com.", 0], + "fd9ba187a0d5dd524840a78ef8a8d67d56f499e561488e375af38dd44245f523": ["populiweb.com.", 0], + "fda4ea06507d617c0ed1094b0e0877c3ce6d815cd82873d76387083ccc993177": ["net-a-porter.com.", 0], + "fdb4afba15b531cbd99e6599405cdd71be21088c745583664e2e6cb6273b4695": ["xl720.com.", 0], + "fdb8725aaffd76f5d97fe2f180745efc4a20e35ecef89b7944f69df8df5409ca": ["clicktripz.com.", 0], + "fdd125e7acd741c65ce064a247402ec121c108e0fb2f84aedcf5c4d4d5c1516b": ["startlap.hu.", 0], + "fdd308677e0c7aade47bf080888fdcb1d1ea2acdfc78854e433d640a4d256dba": ["actblue.com.", 0], + "fde4854edee740f1896bdbdcf52ac757715870b4fa4d3631ff4c4277b13d198d": ["lucidchart.com.", 0], + "fde9e5017d1d1342ce5ea299e4604a5a0329113a9dd63ddf9707ec3854e451ee": ["adafruit.com.", 0], + "fdea545123691727d0217269b4c51e9162df01358f4a491a58dd41024ab6d33d": ["sharefile.com.", 0], + "fded83de2dfd26829038480c6f35923efab1db0f16a449756095cad51d1643e5": ["mca.gov.in.", 0], + "fdfac571bd580c4865c49e252d3834de7d8395b58adb42f01ee38111d039f436": ["wrestlinginc.com.", 0], + "fdff254024228be1e951b46a209eb6fbe7a10e597b11ef50a457f8e8521a90aa": ["tongji.edu.cn.", 0], + "fe0249b9ed54f650836d7b580bb51febd6c0ca6ed091c27ab087ad04b87dd4d6": ["pressplay.cc.", 0], + "fe0421872eca0b0bdeca4dd71c5f169e2bb6ce9ecccbef72406100ed4962d596": ["qulishi.com.", 0], + "fe069e7c687e88287f67828fccb362bc28c215862bcf0ddbcd66ca16ad101c1d": ["dealspotr.com.", 0], + "fe1225906cffac5a38072b1fc54c462b2f3ae6916d8aa644bdaad76a12e2e926": ["camsoda.com.", 0], + "fe1d98893dcb9692e54e9ea9c54e619842ebaf3cce30d8956346a93003e11926": ["hltv.org.", 0], + "fe1ec5affc121362b4e381a55b130cb090e115394f4d8f6f845013dcd26de2a5": ["herbeauty.co.", 0], + "fe32a4d4a0e7c8f9bfbaf8e8729a6b2b38ce5079119ce5193cea6b93ce97e55c": ["lefrak.", 1], + "fe33ccb82b85cafbf7bc65b2f5971e582eeb73bb2d53cdd4b3056ef4c420ad34": ["edreams.com.", 0], + "fe3bc2ad5dd724183aa3727cd3f0bd3014f2f5b65b78a19aab93280ed4f08b1d": ["incometax.gov.in.", 0], + "fe3bca77451b01dca2f35c55de5967a7403f8d5429af8491c96a0f8b0d69ee41": ["resume-now.com.", 0], + "fe4a7e05fc8f0440cf0af45ad2110a48b635dc0e59807f54c1873238f8fa3c7a": ["capitalone.", 1], + "fe4ab0bfb52ecabed2ceabc77216d1b9f43c2d3c52ae03f1fdb9a4985cd40a54": ["dmed.kz.", 0], + "fe51b297db87f4ef68bf76884b54c0442c9c292729c4394d7bed8f7e6e679bd9": ["bricklink.com.", 0], + "fe5359b0ba025139978acc9f9df7be35ecdc297696a1ea34aca84ba0a455a616": ["ke.", 1], + "fe57cac19f47bae55c51dad7daa590378983d7d5cee6d97d758121b002bbd8b4": ["photo.", 1], + "fe6853bd18aa0e28b84ce8847cb672b005a3ce7c84739e690dcf01304b99471b": ["swedbank.lt.", 0], + "fe68e2efc9bf3de8ed0a0ec85bc1034bcbc3655e168b13688ea6ddb15e100d49": ["elperuano.pe.", 0], + "fe69c607fe9908aba7e015e86ecf7170f621d3398d44fa7d3a9818f67dda0ee0": ["citibank.co.in.", 0], + "fe720d44005c2eca5a0a324cbbecc3d3137da4ac31b07d8ad9f22eaba556ec23": ["jijidown.com.", 0], + "fe76ff36ce85945c4ae9d4c5d8579c0e1110f402bb167182bfe08667e146cab6": ["becu.org.", 0], + "fe8e9fede27b57ab4396b685ee7c99c8cb20511e49635b19c0fdfa95b3188390": ["pivigames.blog.", 0], + "fe924908b4fcb7f5bc5b4cc42692e7805ea4718e8c59202b3a7407847d451660": ["intuit.", 1], + "fe94264a6679150ce2a3bb250b09d042a57243b0953264593a37e1fda61fab25": ["free-powerpoint-templates-design.com.", 0], + "fe99b0232885ee603674f5096d3f6e1cd965138f27553d02d1b55ea5555b1118": ["jdwx.info.", 0], + "fe9bab7e0273f8819617ed0be5f34a036ddc67e826d514e6495d8e578375f398": ["hku.hk.", 0], + "fe9fd9250c3771bf2befd04b9b0149073ffa1e6ffb66d7ae16281ae4c7fab305": ["china-airlines.com.", 0], + "feaea8734deb987a3d146413f0e407b0dc094c0ef96e0c59f36e6e69c24075e3": ["sjtu.edu.cn.", 0], + "feb7babdf98540b277b3d5929204d1257d0bbb3d8a3fffbfbfae22398b9f17a5": ["zoho.com.", 0], + "fec529ffb2e6e1ab1c3fbed2f240f065b37d14be61f2eb83c38291b04cc8bd01": ["labnol.org.", 0], + "fec66a719ff97f2fef3bdb22f37608950a8aa3693b8aa3facb441f885d0b2706": ["vimeo.com.", 0], + "fecba468e8ce82b3b179f5a5691ba82392cabf110cbf2be17b93b6ddabcfa009": ["mangascan.ws.", 0], + "fecbe612e7bd80c9cf5bd8190c1413416c227a46624fcb195511813b7fe1dd87": ["worldfirst.com.cn.", 0], + "fecca8ac12b392afd60573a9eecd9bf8c7dd99a784c0de1995ef1ece243aa131": ["skyroom.online.", 0], + "fecfcbdad3f97d11af7a68b9e38fd9d2524d1ec287f6ed4bc3434e7d079fce5d": ["xn--q7ce6a.", 1], + "fed0e7221f93b53f46ffd0136afff1c85b0eafbdcce9774844f3c5545470a9da": ["telekom.de.", 0], + "fedf8a94d645025a94f34cb39666191552e03d549559eb8b4d87444af484e01f": ["themeisle.com.", 0], + "fedfea45a2e743c0bd1b97507b3df4eb1b8733b48fdb91eadc369fd98b8d142b": ["findlaw.cn.", 0], + "fee107479a366f723904ba4677061814f3f1072977592ae0edd15e9d84c4efc6": ["enbdev.com.", 0], + "fee3044acfbbaaf72eeb18956f84a7cb45f21e3fc1646939f398772bc12e3ccf": ["momon-ga.com.", 0], + "fee882184d316cb7cc80104dc518c5454cb23542a47a66113ad248cb9301d63b": ["portaleargo.it.", 0], + "feeaf8c651028773a316a2d0c037cefa02300f5251119b2c8dccee6323416614": ["sony.", 1], + "fef1b00f2129b1828fcae425f543d297df2284edaad41f77b76ba24d9db56ae2": ["secureinternetbank.com.", 0], + "fef1f1a09e954d88468a237d9ca667de1f2c3d60b07a985ac3110b16e31d75b3": ["artofproblemsolving.com.", 0], + "fef50c3e0399c686fa1723d49a8fb9d46b8b5849a1ac8345c104c6fbb86c5ab4": ["registrar-servers.com.", 0], + "fef654d19588a37367de55e4cdaaa5c802ed1b5888617d9e1f8782b3ab111638": ["clicksud.biz.", 0], + "feff046bbf68f8dc15f1ac9fb95f788e1d4890d613d1f99894f18bb76632c31b": ["tokopedia.com.", 0], + "feff779face5e18ea6e81ba0995d240e065e4cbf02d0c5896bcaa4bf78b417bd": ["cn-healthcare.com.", 0], + "ff03a28b82129d1244f0fb83e6ad2000749aeed43e3cbfdc143a4ce6b9b836cb": ["ryanair.com.", 0], + "ff0bab58c7ad0d195fffbfb25e6ec9c582c096b2ffc3b2f0a600bf857567cb04": ["fravega.com.", 0], + "ff15b5ea64ad9f9fdb7a87f919cf406a6156ac11fb0e8a22029fe1a554658a04": ["saisoncard.co.jp.", 0], + "ff1caff32f4cd4ebe7c7b4bbb6e9cd8f5b12b587b029e3bef90a004950a160d6": ["fluentu.com.", 0], + "ff21ba1fba13b5ae89bbda4536c2028dd20305398742de094abd12d1657337cb": ["kujiale.com.", 0], + "ff26e68c48dfdb4381bc12452c6c2f2ba1c2fc69275bad19ba52bf99878f5ebf": ["cvs.com.", 0], + "ff2909b02cf3c2e87d748f95b49e5dbc2f4a2b056eaba83a1ac94418f31a83f2": ["xn--cg4bki.", 1], + "ff2a38e08e83c154ee48ee672f0b5de00cdb915a5cf1d7cfc0a3576c736d8d00": ["haronbouchannel.com.", 0], + "ff34f44cf2d085be9ee33188287c5e108ff26013e4e070ce076d58b816829733": ["tesco.com.", 0], + "ff389bc2cf6a1e2fb671e1b58f31199aadd15b9bfa5e0e0d40786112f248ac6f": ["mcneel.com.", 0], + "ff3c69f14ca82bbe9d318f2bf9dfe8882476d84ee6e0a81853c9873059ea68cd": ["mindmeister.com.", 0], + "ff4922c52e8f610d6ae6ed9744949d05e0dcc06ccc4a79987a29bdceeb0529e5": ["wikiloc.com.", 0], + "ff4f028fc75f6417f77ebbe468d4fd2b13e5fbad94bdda424fbdb7f8d958d625": ["pt.", 1], + "ff526248f7540dd2e783eff1df82e3f07fd48e3890e7d5c8fd08d6d1e345c58e": ["vet.", 1], + "ff55d0a8c6efd0e617ab027198c4add904ed445532a773ebcdc911ba670d66f5": ["schwab.com.", 0], + "ff5a9b4a1d80083a77e9f986de62b1a561e9599cd6feaddcfc6f2138d950333c": ["php.net.", 0], + "ff5bd022485141fd48fab18bab9af764ee0b9c8e928d42c04c6e90bdf9d26747": ["clickpay.com.", 0], + "ff60cf851c968e1f5bb8a30117640aeb06d001754f94d656be15c7fb71f2f98f": ["vu.", 1], + "ff6a8c10debd1759158d5bf3dad93b1e8d3da34ba5505f1e68454a3cc72de460": ["flowers.", 1], + "ff8aed5538d1af2e503754c75dcb0d29195509c8e04a115136b225c0e7bd4046": ["registraduria.gov.co.", 0], + "ff8d079396a367faea5d5cfd37c9da260ee8a752df0ad961838bf4327ea48827": ["internetdownloadmanager.com.", 0], + "ff9db6a56d8ace5559dcafa33f2ab448544fea1344e03f5b3b135ae8f4303ad7": ["kerryproperties.", 1], + "ffa54d260b73925ceba9a94d855dcd7785e2897b5aac206ad7933326570f8f59": ["rockpapershotgun.com.", 0], + "ffb10143dfd15f566399f0a0ca0fcc108e1b98f46aa7167b84136baa280e722e": ["auone.jp.", 0], + "ffbbb2ea16387dcedc747ba5b39f0d4efee71794fdfc0d47486ce3f981c1fb5e": ["smbc-card.com.", 0], + "ffbe274ac0b543f70bcbe4590d39c4ca5694de65b339b3dcc6c31c60f4fc94dc": ["itbazar.com.", 0], + "ffc29dbfdc69ad45c74732c7080b0d3a25b99bbcca96753e692846a6091a14e3": ["tubangzhu.net.", 0], + "ffd20db44e992661f0ee293c9b2da2b0c94ba42468e0543b4af9c95195e1b311": ["usgs.gov.", 0], + "ffd248e73f10194e06e48f9179a6f31ff356a582bc61400d750dca95bb5d3393": ["pexels.com.", 0], + "ffd6c294d4ace1512e559d74c8fc42104e156e19b0d2d85267e132c4c4835a87": ["eus.", 1], + "ffd949396ed1c5b707e73fc21c00611201655e44b4850784f38950ada3df9b0d": ["panasonic.", 1], + "ffdc1aac83ace34726a5fc877d427981effad738d49c65021ebfaeda10048855": ["frl.", 1], + "fff44835db539db2f4aaaf06ee6cddc445cf6392f0f3ef7d00a080487ab7bfe4": ["glavkniga.ru.", 0], + "fff7ccb3652a4f9255ea4068cbcfc5e07611c3dfcff1bbd5ed0633144c17ff3a": ["salamnews.org.", 0], + "ffff905f688460a72442e8292f0f826ec4455f0f0912d79863425c44c00cade5": ["postype.com.", 0] +} diff --git a/lib/covenants/namestate.js b/lib/covenants/namestate.js index a79996265..0b4a99573 100644 --- a/lib/covenants/namestate.js +++ b/lib/covenants/namestate.js @@ -213,7 +213,7 @@ class NameState extends bio.Struct { if (this.isClaimable(height, network)) return false; - // If we haven't been renewed in a year, start over. + // If we haven't been renewed in two years, start over. if (height >= this.renewal + network.names.renewalWindow) return true; @@ -728,7 +728,8 @@ class NameState extends bio.Struct { assert((height >>> 0) === height); assert(network && network.names); - const spacing = network.pow.targetSpacing; + const {blocksPerDay} = network.pow; + const blocksPerHour = blocksPerDay / 24; const { treeInterval, @@ -737,103 +738,127 @@ class NameState extends bio.Struct { revealPeriod, renewalWindow, auctionMaturity, - transferLockup + transferLockup, + claimPeriod } = network.names; const openPeriod = treeInterval + 1; const stats = {}; - if (this.isOpening(height, network)) { - const start = this.height; - const end = this.height + openPeriod; - const blocks = end - height; - const hours = ((blocks * spacing) / 60 / 60); + let state = this.state(height, network); - stats.openPeriodStart = start; - stats.openPeriodEnd = end; - - stats.blocksUntilBidding = blocks; - stats.hoursUntilBidding = Number(hours.toFixed(2)); - } - - if (this.isLocked(height, network)) { - const start = this.height; - const end = this.height + lockupPeriod; - const blocks = end - height; - const hours = ((blocks * spacing) / 60 / 60); - - stats.lockupPeriodStart = start; - stats.lockupPeriodEnd = end; - - stats.blocksUntilClosed = blocks; - stats.hoursUntilClosed = Number(hours.toFixed(2)); - } - - if (this.isBidding(height, network)) { - const start = this.height + openPeriod; - const end = start + biddingPeriod; - const blocks = end - height; - const hours = ((blocks * spacing) / 60 / 60); - - stats.bidPeriodStart = start; - stats.bidPeriodEnd = end; - - stats.blocksUntilReveal = blocks; - stats.hoursUntilReveal = Number(hours.toFixed(2)); - } - - if (this.isReveal(height, network)) { - const start = this.height + openPeriod + biddingPeriod; - const end = start + revealPeriod; - const blocks = end - height; - const hours = ((blocks * spacing) / 60 / 60); - - stats.revealPeriodStart = start; - stats.revealPeriodEnd = end; - - stats.blocksUntilClose = blocks; - stats.hoursUntilClose = Number(hours.toFixed(2)); - } - - if (this.isClosed(height, network)) { - const start = this.renewal; - const end = start + renewalWindow; - const blocks = end - height; - const days = ((blocks * spacing) / 60 / 60 / 24); - - stats.renewalPeriodStart = start; - stats.renewalPeriodEnd = end; - - stats.blocksUntilExpire = blocks; - stats.daysUntilExpire = Number(days.toFixed(2)); - } - - if (this.isRevoked(height, network)) { - const start = this.revoked; - const end = start + auctionMaturity; - const blocks = end - height; - const hours = ((blocks * spacing) / 60 / 60); - - stats.revokePeriodStart = start; - stats.revokePeriodEnd = end; + // Special case for a state that is not a state: + // EXPIRED but not revoked. + const EXPIRED = -1; + if (this.isExpired(height, network)) { + if (this.owner.isNull()) + return null; - stats.blocksUntilReopen = blocks; - stats.hoursUntilReopen = Number(hours.toFixed(2)); + if (state !== states.REVOKED) + state = EXPIRED; } - // Add these details if name is in mid-transfer - if (this.transfer !== 0) { - const start = this.transfer; - const end = start + transferLockup; - const blocks = end - height; - const hours = ((blocks * spacing) / 60 / 60); - - stats.transferLockupStart = start; - stats.transferLockupEnd = end; - - stats.blocksUntilValidFinalize = blocks; - stats.hoursUntilValidFinalize = Number(hours.toFixed(2)); + switch (state) { + case states.OPENING: { + const start = this.height; + const end = this.height + openPeriod; + const blocks = end - height; + const hours = blocks / blocksPerHour; + + stats.openPeriodStart = start; + stats.openPeriodEnd = end; + + stats.blocksUntilBidding = blocks; + stats.hoursUntilBidding = Number(hours.toFixed(2)); + break; + } + case states.LOCKED: { + const start = this.height; + const end = this.height + lockupPeriod; + const blocks = end - height; + const hours = blocks / blocksPerHour; + + stats.lockupPeriodStart = start; + stats.lockupPeriodEnd = end; + + stats.blocksUntilClosed = blocks; + stats.hoursUntilClosed = Number(hours.toFixed(2)); + break; + } + case states.BIDDING: { + const start = this.height + openPeriod; + const end = start + biddingPeriod; + const blocks = end - height; + const hours = blocks / blocksPerHour; + + stats.bidPeriodStart = start; + stats.bidPeriodEnd = end; + + stats.blocksUntilReveal = blocks; + stats.hoursUntilReveal = Number(hours.toFixed(2)); + break; + } + case states.REVEAL: { + const start = this.height + openPeriod + biddingPeriod; + const end = start + revealPeriod; + const blocks = end - height; + const hours = blocks / blocksPerHour; + + stats.revealPeriodStart = start; + stats.revealPeriodEnd = end; + + stats.blocksUntilClose = blocks; + stats.hoursUntilClose = Number(hours.toFixed(2)); + break; + } + case states.CLOSED: { + const start = this.renewal; + const normalEnd = start + renewalWindow; + const end = this.claimed ? Math.max(claimPeriod, normalEnd) : normalEnd; + const blocks = end - height; + const days = blocks / blocksPerDay; + + stats.renewalPeriodStart = start; + stats.renewalPeriodEnd = end; + + stats.blocksUntilExpire = blocks; + assert(stats.blocksUntilExpire >= 0); + stats.daysUntilExpire = Number(days.toFixed(2)); + + // Add these details if name is in mid-transfer + if (this.transfer !== 0) { + const start = this.transfer; + const end = start + transferLockup; + const blocks = end - height; + const hours = blocks / blocksPerHour; + + stats.transferLockupStart = start; + stats.transferLockupEnd = end; + + stats.blocksUntilValidFinalize = blocks; + stats.hoursUntilValidFinalize = Number(hours.toFixed(2)); + } + break; + } + case states.REVOKED: { + const start = this.revoked; + const end = start + auctionMaturity; + const blocks = end - height; + const hours = blocks / blocksPerHour; + + stats.revokePeriodStart = start; + stats.revokePeriodEnd = end; + + stats.blocksUntilReopen = blocks; + stats.hoursUntilReopen = Number(hours.toFixed(2)); + break; + } + case EXPIRED: { + const expired = this.renewal + renewalWindow; + stats.blocksSinceExpired = height - expired; + break; + } } return stats; diff --git a/lib/covenants/ownership.js b/lib/covenants/ownership.js index 466b0bbba..5f7a9f29a 100644 --- a/lib/covenants/ownership.js +++ b/lib/covenants/ownership.js @@ -18,12 +18,15 @@ const Network = require('../protocol/network'); const reserved = require('./reserved'); const {Proof: BNSProof} = BNSOwnership; +/** @typedef {import('../types').NetworkType} NetworkType */ + /* * Constants */ const EMPTY = Buffer.alloc(0); +/** @type {Ownership} */ let ownership = null; /** @@ -35,6 +38,11 @@ class Proof extends BNSProof { super(); } + /** + * @param {Buffer} data + * @returns {this} + */ + decode(data) { const br = bio.read(data); @@ -49,6 +57,10 @@ class Proof extends BNSProof { return this; } + /** + * @returns {String[]} + */ + getNames() { const target = this.getTarget(); @@ -58,6 +70,10 @@ class Proof extends BNSProof { return [util.label(target, 0), target]; } + /** + * @returns {String} + */ + getName() { return this.getNames()[0]; } @@ -258,9 +274,6 @@ class Ownership extends BNSOwnership { } } -Ownership.Proof = Proof; -Ownership.OwnershipProof = Proof; - /** * ProofData */ @@ -291,4 +304,9 @@ ownership = new Ownership(); * Expose */ -module.exports = ownership; +Ownership.Proof = Proof; +Ownership.OwnershipProof = Proof; +Ownership.ProofData = ProofData; +Ownership.ownership = ownership; + +module.exports = Ownership; diff --git a/lib/covenants/reserved-browser.js b/lib/covenants/reserved-browser.js index 6d76acdbe..897519e46 100644 --- a/lib/covenants/reserved-browser.js +++ b/lib/covenants/reserved-browser.js @@ -77,7 +77,10 @@ class Reserved { hash, target, value, - root + root, + top100, + custom, + zero }; } diff --git a/lib/covenants/reserved.js b/lib/covenants/reserved.js index c7d600fe6..2e1df7dad 100644 --- a/lib/covenants/reserved.js +++ b/lib/covenants/reserved.js @@ -113,7 +113,10 @@ class Reserved { hash, target, value, - root + root, + top100, + custom, + zero }; } diff --git a/lib/covenants/rules.js b/lib/covenants/rules.js index 716e84740..9156d8d90 100644 --- a/lib/covenants/rules.js +++ b/lib/covenants/rules.js @@ -13,10 +13,18 @@ const blake2b = require('bcrypto/lib/blake2b'); const sha3 = require('bcrypto/lib/sha3'); const consensus = require('../protocol/consensus'); const reserved = require('./reserved'); -const {OwnershipProof} = require('./ownership'); +const {locked} = require('./locked'); +const OwnershipProof = require('./ownership').OwnershipProof; const AirdropProof = require('../primitives/airdropproof'); const rules = exports; +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../protocol/network')} Network */ +/** @typedef {import('../primitives/tx')} TX */ +/** @typedef {import('../coins/coinview')} CoinView */ +/** @typedef {import('./ownership').OwnershipProof} OwnershipProof */ + /* * Constants */ @@ -106,6 +114,28 @@ rules.MAX_NAME_SIZE = 63; rules.MAX_RESOURCE_SIZE = 512; +/** + * Consensus name verification flags (used for block validation). + * @enum {Number} + * @default + */ + +rules.nameFlags = { + VERIFY_COVENANTS_NONE: 0, + // Activated when hardening soft fork activates. + VERIFY_COVENANTS_HARDENED: 1 << 0, + // Activated when ICANN lockup soft fork activates. + VERIFY_COVENANTS_LOCKUP: 1 << 1 +}; + +/** + * Standard verify flags for covenants. + * @const {NameFlags} + * @default + */ + +rules.nameFlags.MANDATORY_VERIFY_COVENANT_FLAGS = 0; + /** * Maximum covenant size. * @const {Number} @@ -121,7 +151,7 @@ rules.MAX_COVENANT_SIZE = (0 /** * Hash a domain name. * @param {String|Buffer} name - * @returns {Buffer} + * @returns {Hash} */ rules.hashName = function hashName(name) { @@ -133,7 +163,7 @@ rules.hashName = function hashName(name) { /** * Hash a domain name. * @param {String} name - * @returns {Buffer} + * @returns {Hash} */ rules.hashString = function hashString(name) { @@ -176,7 +206,7 @@ rules.verifyName = function verifyName(name) { /** * Verify a domain name meets handshake requirements. - * @param {String} name + * @param {String} str * @returns {Boolean} */ @@ -223,7 +253,7 @@ rules.verifyString = function verifyString(str) { /** * Verify a domain name meets handshake requirements. - * @param {Buffer} name + * @param {Buffer} buf * @returns {Boolean} */ @@ -374,9 +404,45 @@ rules.isReserved = function isReserved(nameHash, height, network) { return reserved.has(nameHash); }; +/** + * Test whether a name is locked up. + * ICANNLOCKUP soft fork. + * @param {Buffer} nameHash + * @param {Number} height + * @param {Network} network + * @returns {Boolean} + */ + +rules.isLockedUp = function isLockedUp(nameHash, height, network) { + assert(Buffer.isBuffer(nameHash)); + assert((height >>> 0) === height); + assert(network && network.names); + + if (network.names.noReserved) + return false; + + if (height < network.names.claimPeriod) + return false; + + const item = locked.get(nameHash); + + if (!item) + return false; + + // ICANN Names are always LOCKED. + if (item.root) + return true; + + // Alexa names will expire after 4 years, after claim period ends. + if (height < network.names.alexaLockupPeriod) + return true; + + return false; +}; + /** * Create a blind bid hash from a value and nonce. - * @param {Amount} value + * @param {AmountValue} value * @param {Buffer} nonce * @returns {Buffer} */ @@ -706,6 +772,7 @@ rules.hasSaneCovenants = function hasSaneCovenants(tx) { if (!key.equals(nameHash)) return false; + /** @type {OwnershipProof} */ let proof; try { @@ -1114,6 +1181,7 @@ rules.verifyCovenants = function verifyCovenants(tx, view, height, network) { if (witness.items.length !== 1) return -1; + /** @type {OwnershipProof} */ let proof; try { proof = OwnershipProof.decode(witness.items[0]); diff --git a/lib/covenants/view.js b/lib/covenants/view.js index 7302f33e4..462c7a83b 100644 --- a/lib/covenants/view.js +++ b/lib/covenants/view.js @@ -5,11 +5,20 @@ const {BufferMap} = require('buffer-map'); const NameState = require('./namestate'); const NameUndo = require('./undo'); +/** @typedef {import('../types').Hash} Hash */ + class View { constructor() { + /** @type {BufferMap} */ this.names = new BufferMap(); } + /** + * @param {Object} db + * @param {Hash} nameHash + * @returns {NameState} + */ + getNameStateSync(db, nameHash) { assert(db && typeof db.getNameState === 'function'); assert(Buffer.isBuffer(nameHash)); @@ -19,6 +28,7 @@ class View { if (cache) return cache; + /** @type {NameState?} */ const ns = db.getNameState(nameHash); if (!ns) { @@ -33,6 +43,12 @@ class View { return ns; } + /** + * @param {Object} db + * @param {Hash} nameHash + * @returns {Promise} + */ + async getNameState(db, nameHash) { assert(db && typeof db.getNameState === 'function'); assert(Buffer.isBuffer(nameHash)); @@ -42,6 +58,7 @@ class View { if (cache) return cache; + /** @type {NameState?} */ const ns = await db.getNameState(nameHash); if (!ns) { diff --git a/lib/dns/resource.js b/lib/dns/resource.js index a4d7869f1..1478d0466 100644 --- a/lib/dns/resource.js +++ b/lib/dns/resource.js @@ -877,7 +877,7 @@ function typeToClass(type) { function stringToClass(type) { assert(typeof type === 'string'); - if (!hsTypes.hasOwnProperty(type)) + if (!Object.prototype.hasOwnProperty.call(hsTypes, type)) return null; return typeToClass(hsTypes[type]); diff --git a/lib/hd/common.js b/lib/hd/common.js index 507f6608c..d5b8482cd 100644 --- a/lib/hd/common.js +++ b/lib/hd/common.js @@ -10,6 +10,9 @@ const assert = require('bsert'); const LRU = require('blru'); const common = exports; +/** @typedef {import('./private')} HDPrivateKey */ +/** @typedef {import('./public')} HDPublicKey */ + /** * Index at which hardening begins. * @const {Number} @@ -36,7 +39,7 @@ common.MAX_ENTROPY = 512; /** * LRU cache to avoid deriving keys twice. - * @type {LRU} + * @type {LRU} */ common.cache = new LRU(500); diff --git a/lib/hd/hd.js b/lib/hd/hd.js index 0ca02ec3c..264233738 100644 --- a/lib/hd/hd.js +++ b/lib/hd/hd.js @@ -13,6 +13,10 @@ const HDPrivateKey = require('./private'); const HDPublicKey = require('./public'); const wordlist = require('./wordlist'); +/** @typedef {import('../protocol/network')} Network */ +/** @typedef {import('../types').Base58String} Base58String */ +/** @typedef {import('../types').NetworkType} NetworkType */ + /** * @exports hd */ @@ -22,7 +26,7 @@ const HD = exports; /** * Instantiate an HD key (public or private) from an base58 string. * @param {Base58String} xkey - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {HDPrivateKey|HDPublicKey} */ @@ -58,7 +62,7 @@ HD.fromSeed = function fromSeed(options) { /** * Instantiate an hd private key from a mnemonic. * @param {Mnemonic} mnemonic - * @param {String?} bip39Passphrase + * @param {String?} [bip39Passphrase] * @returns {HDPrivateKey} */ @@ -82,7 +86,7 @@ HD.fromJSON = function fromJSON(json, network) { /** * Instantiate an HD key from serialized data. * @param {Buffer} data - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {HDPrivateKey|HDPublicKey} */ @@ -121,7 +125,7 @@ HD.from = function from(options, network) { /** * Test whether an object is in the form of a base58 hd key. * @param {String} data - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {Boolean} */ @@ -133,8 +137,8 @@ HD.isBase58 = function isBase58(data, network) { /** * Test whether an object is in the form of a serialized hd key. * @param {Buffer} data - * @param {Network?} network - * @returns {NetworkType} + * @param {(Network|NetworkType)?} [network] + * @returns {Boolean} */ HD.isRaw = function isRaw(data, network) { diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index 819ff2592..7028d7caa 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -17,12 +17,22 @@ const wordlist = require('./wordlist'); const common = require('./common'); const nfkd = require('./nfkd'); +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /* * Constants */ const wordlistCache = Object.create(null); +/** + * @typedef {Object} MnemonicOptions + * @property {Number?} [bits] + * @property {Buffer?} [entropy] + * @property {String?} [phrase] + * @property {String?} [language] + */ + /** * HD Mnemonic * @alias module:hd.Mnemonic @@ -32,15 +42,7 @@ class Mnemonic extends bio.Struct { /** * Create a mnemonic. * @constructor - * @param {Object} options - * @param {Number?} options.bit - Bits of entropy (Must - * be a multiple of 8) (default=256). - * @param {Buffer?} options.entropy - Entropy bytes. Will - * be generated with `options.bits` bits of entropy - * if not present. - * @param {String?} options.phrase - Mnemonic phrase (will - * be generated if not present). - * @param {String?} options.language - Language. + * @param {String|MnemonicOptions} [options] */ constructor(options) { @@ -56,8 +58,7 @@ class Mnemonic extends bio.Struct { /** * Inject properties from options object. - * @private - * @param {Object} options + * @param {String|MnemonicOptions} options */ fromOptions(options) { @@ -109,7 +110,7 @@ class Mnemonic extends bio.Struct { /** * Generate the seed. - * @param {String?} passphrase + * @param {String?} [passphrase] * @returns {Buffer} pbkdf2 seed. */ @@ -169,7 +170,9 @@ class Mnemonic extends bio.Struct { // 11 bit indexes from the entropy. const list = Mnemonic.getWordlist(this.language); - let phrase = []; + /** @type {String[]} */ + const words = []; + for (let i = 0; i < wbits / 11; i++) { let index = 0; for (let j = 0; j < 11; j++) { @@ -179,14 +182,15 @@ class Mnemonic extends bio.Struct { index <<= 1; index |= (data[oct] >>> (7 - bit)) & 1; } - phrase.push(list.words[index]); + words.push(list.words[index]); } + let phrase; // Japanese likes double-width spaces. if (this.language === 'japanese') - phrase = phrase.join('\u3000'); + phrase = words.join('\u3000'); else - phrase = phrase.join(' '); + phrase = words.join(' '); this.phrase = phrase; @@ -195,7 +199,6 @@ class Mnemonic extends bio.Struct { /** * Inject properties from phrase. - * @private * @param {String} phrase */ @@ -284,7 +287,7 @@ class Mnemonic extends bio.Struct { * Inject properties from entropy. * @private * @param {Buffer} entropy - * @param {String?} lang + * @param {String?} [lang] */ fromEntropy(entropy, lang) { @@ -334,7 +337,7 @@ class Mnemonic extends bio.Struct { /** * Retrieve the wordlist for a language. * @param {String} lang - * @returns {Object} + * @returns {WordList} */ static getWordlist(lang) { @@ -367,7 +370,6 @@ class Mnemonic extends bio.Struct { /** * Inject properties from json object. - * @private * @param {Object} json */ @@ -404,7 +406,8 @@ class Mnemonic extends bio.Struct { /** * Write the mnemonic to a buffer writer. - * @params {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -421,8 +424,7 @@ class Mnemonic extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -498,7 +500,7 @@ class WordList { * Create word list. * @constructor * @ignore - * @param {Array} words + * @param {String[]} words */ constructor(words) { diff --git a/lib/hd/private.js b/lib/hd/private.js index 17dcaafa4..dfe34a4ec 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -21,12 +21,25 @@ const common = require('./common'); const Mnemonic = require('./mnemonic'); const HDPublicKey = require('./public'); +/** @typedef {import('../types').Base58String} Base58String */ +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /* * Constants */ const SEED_SALT = Buffer.from('Bitcoin seed', 'ascii'); +/** + * @typedef {Object} HDPrivateKeyOptions + * @property {Number} depth + * @property {Number} parentFingerPrint + * @property {Number} childIndex + * @property {Buffer} chainCode + * @property {Buffer} privateKey + */ + /** * HDPrivateKey * @alias module:hd.PrivateKey @@ -41,12 +54,7 @@ class HDPrivateKey extends bio.Struct { /** * Create an hd private key. * @constructor - * @param {Object|String} options - * @param {Number?} options.depth - * @param {Number?} options.parentFingerPrint - * @param {Number?} options.childIndex - * @param {Buffer?} options.chainCode - * @param {Buffer?} options.privateKey + * @param {HDPrivateKeyOptions} [options] */ constructor(options) { @@ -69,8 +77,7 @@ class HDPrivateKey extends bio.Struct { /** * Inject properties from options object. - * @private - * @param {Object} options + * @param {HDPrivateKeyOptions} options */ fromOptions(options) { @@ -114,6 +121,7 @@ class HDPrivateKey extends bio.Struct { /** * Get cached base58 xprivkey. + * @param {(NetworkType|Network)?} [network] * @returns {Base58String} */ @@ -123,6 +131,7 @@ class HDPrivateKey extends bio.Struct { /** * Get cached base58 xpubkey. + * @param {(NetworkType|Network)?} [network] * @returns {Base58String} */ @@ -156,7 +165,7 @@ class HDPrivateKey extends bio.Struct { /** * Derive a child key. * @param {Number} index - Derivation index. - * @param {Boolean?} hardened - Whether the derivation should be hardened. + * @param {Boolean?} [hardened] - Whether the derivation should be hardened. * @returns {HDPrivateKey} */ @@ -175,7 +184,10 @@ class HDPrivateKey extends bio.Struct { } const id = this.getID(index); - const cache = common.cache.get(id); + + /** @type {HDPrivateKey} */ + // @ts-ignore + const cache = common.cache.get(id); if (cache) return cache; @@ -209,6 +221,8 @@ class HDPrivateKey extends bio.Struct { this.fingerPrint = fp.readUInt32BE(0, true); } + /** @type {HDPrivateKey} */ + // @ts-ignore const child = new this.constructor(); child.depth = this.depth + 1; child.parentFingerPrint = this.fingerPrint; @@ -275,7 +289,7 @@ class HDPrivateKey extends bio.Struct { /** * Test whether an object is in the form of a base58 xprivkey. * @param {String} data - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {Boolean} */ @@ -299,7 +313,7 @@ class HDPrivateKey extends bio.Struct { /** * Test whether a buffer has a valid network prefix. * @param {Buffer} data - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {Boolean} */ @@ -310,7 +324,7 @@ class HDPrivateKey extends bio.Struct { if (data.length < 4) return false; - const version = data.readUInt32BE(0, true); + const version = data.readUInt32BE(0); try { Network.fromPrivate(version, network); @@ -345,6 +359,7 @@ class HDPrivateKey extends bio.Struct { derivePath(path) { const indexes = common.parsePath(path, true); + /** @type {HDPrivateKey} */ let key = this; for (const index of indexes) @@ -355,7 +370,7 @@ class HDPrivateKey extends bio.Struct { /** * Compare a key against an object. - * @param {Object} obj + * @param {HDPrivateKey} obj * @returns {Boolean} */ @@ -371,8 +386,8 @@ class HDPrivateKey extends bio.Struct { /** * Compare a key against an object. - * @param {Object} obj - * @returns {Boolean} + * @param {HDPrivateKey} key + * @returns {Number} */ compare(key) { @@ -450,9 +465,8 @@ class HDPrivateKey extends bio.Struct { /** * Inject properties from a mnemonic. - * @private * @param {Mnemonic} mnemonic - * @param {String?} passphrase + * @param {String?} [passphrase] */ fromMnemonic(mnemonic, passphrase) { @@ -463,7 +477,7 @@ class HDPrivateKey extends bio.Struct { /** * Instantiate an hd private key from a mnemonic. * @param {Mnemonic} mnemonic - * @param {String?} passphrase + * @param {String?} [passphrase] * @returns {HDPrivateKey} */ @@ -473,8 +487,7 @@ class HDPrivateKey extends bio.Struct { /** * Inject properties from a mnemonic. - * @private - * @param {String} mnemonic + * @param {String} phrase */ fromPhrase(phrase) { @@ -538,7 +551,7 @@ class HDPrivateKey extends bio.Struct { * Inject properties from base58 key. * @private * @param {Base58String} xkey - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] */ fromBase58(xkey, network) { @@ -548,9 +561,8 @@ class HDPrivateKey extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {BufferReader} br - * @param {(Network|NetworkType)?} network + * @param {bio.BufferReader} br + * @param {(Network|NetworkType)?} [network] */ read(br, network) { @@ -592,8 +604,9 @@ class HDPrivateKey extends bio.Struct { /** * Write the key to a buffer writer. - * @param {BufferWriter} bw - * @param {(Network|NetworkType)?} network + * @param {BufioWriter} bw + * @param {(Network|NetworkType)?} [network] + * @returns {BufioWriter} */ write(bw, network) { @@ -614,7 +627,7 @@ class HDPrivateKey extends bio.Struct { /** * Instantiate an HD private key from a base58 string. * @param {Base58String} xkey - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {HDPrivateKey} */ @@ -624,6 +637,7 @@ class HDPrivateKey extends bio.Struct { /** * Convert key to a more json-friendly object. + * @param {(Network|NetworkType)?} [network] * @returns {Object} */ @@ -635,9 +649,8 @@ class HDPrivateKey extends bio.Struct { /** * Inject properties from json object. - * @private * @param {Object} json - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] */ fromJSON(json, network) { diff --git a/lib/hd/public.js b/lib/hd/public.js index 032a0e4fc..f8f263056 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -18,6 +18,20 @@ const Network = require('../protocol/network'); const consensus = require('../protocol/consensus'); const common = require('./common'); +/** @typedef {import('../types').Base58String} Base58String */ +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {String} HDPublicKeyID */ + +/** + * @typedef HDPublicKeyOptions + * @property {Number} depth + * @property {Number} parentFingerPrint + * @property {Number} childIndex + * @property {Buffer} chainCode + * @property {Buffer} publicKey + */ + /** * HDPublicKey * @alias module:hd.PublicKey @@ -32,13 +46,7 @@ class HDPublicKey extends bio.Struct { /** * Create an HD public key. * @constructor - * @param {Object|Base58String} options - * @param {Base58String?} options.xkey - Serialized base58 key. - * @param {Number?} options.depth - * @param {Number?} options.parentFingerPrint - * @param {Number?} options.childIndex - * @param {Buffer?} options.chainCode - * @param {Buffer?} options.publicKey + * @param {HDPublicKeyOptions} [options] */ constructor(options) { @@ -58,8 +66,7 @@ class HDPublicKey extends bio.Struct { /** * Inject properties from options object. - * @private - * @param {Object} options + * @param {HDPublicKeyOptions} options */ fromOptions(options) { @@ -90,6 +97,7 @@ class HDPublicKey extends bio.Struct { /** * Get cached base58 xprivkey (always null here). + * @param {(NetworkType|Network)?} [network] * @returns {null} */ @@ -124,9 +132,9 @@ class HDPublicKey extends bio.Struct { /** * Derive a child key. * @param {Number} index - Derivation index. - * @param {Boolean?} hardened - Whether the derivation + * @param {Boolean?} [hardened] - Whether the derivation * should be hardened (throws if true). - * @returns {HDPrivateKey} + * @returns {HDPublicKey} * @throws on `hardened` */ @@ -143,6 +151,8 @@ class HDPublicKey extends bio.Struct { throw new Error('Depth too high.'); const id = this.getID(index); + /** @type {HDPublicKey} */ + // @ts-ignore const cache = common.cache.get(id); if (cache) @@ -171,6 +181,8 @@ class HDPublicKey extends bio.Struct { this.fingerPrint = fp.readUInt32BE(0, true); } + /** @type {HDPublicKey} */ + // @ts-ignore const child = new this.constructor(); child.depth = this.depth + 1; child.parentFingerPrint = this.fingerPrint; @@ -236,7 +248,6 @@ class HDPublicKey extends bio.Struct { /** * Test whether a string is a valid path. * @param {String} path - * @param {Boolean?} hardened * @returns {Boolean} */ @@ -260,6 +271,7 @@ class HDPublicKey extends bio.Struct { derivePath(path) { const indexes = common.parsePath(path, false); + /** @type {HDPublicKey} */ let key = this; for (const index of indexes) @@ -270,7 +282,7 @@ class HDPublicKey extends bio.Struct { /** * Compare a key against an object. - * @param {Object} obj + * @param {HDPublicKey} obj * @returns {Boolean} */ @@ -286,8 +298,8 @@ class HDPublicKey extends bio.Struct { /** * Compare a key against an object. - * @param {Object} obj - * @returns {Boolean} + * @param {HDPublicKey} key + * @returns {Number} */ compare(key) { @@ -323,6 +335,7 @@ class HDPublicKey extends bio.Struct { /** * Convert key to a more json-friendly object. + * @param {(Network|NetworkType)?} [network] * @returns {Object} */ @@ -334,9 +347,8 @@ class HDPublicKey extends bio.Struct { /** * Inject properties from json object. - * @private * @param {Object} json - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] */ fromJSON(json, network) { @@ -372,8 +384,8 @@ class HDPublicKey extends bio.Struct { /** * Test whether a buffer has a valid network prefix. * @param {Buffer} data - * @param {(Network|NetworkType)?} network - * @returns {NetworkType} + * @param {(Network|NetworkType)?} [network] + * @returns {Boolean} */ static isRaw(data, network) { @@ -383,7 +395,7 @@ class HDPublicKey extends bio.Struct { if (data.length < 4) return false; - const version = data.readUInt32BE(0, true); + const version = data.readUInt32BE(0); try { Network.fromPublic(version, network); @@ -395,9 +407,8 @@ class HDPublicKey extends bio.Struct { /** * Inject properties from a base58 key. - * @private * @param {Base58String} xkey - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] */ fromBase58(xkey, network) { @@ -407,8 +418,7 @@ class HDPublicKey extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br * @param {(Network|NetworkType)?} network */ @@ -440,8 +450,9 @@ class HDPublicKey extends bio.Struct { /** * Write the key to a buffer writer. - * @param {BufferWriter} bw - * @param {(Network|NetworkType)?} network + * @param {BufioWriter} bw + * @param {(Network|NetworkType)?} [network] + * @returns {BufioWriter} */ write(bw, network) { @@ -470,7 +481,7 @@ class HDPublicKey extends bio.Struct { /** * Instantiate an HD public key from a base58 string. * @param {Base58String} xkey - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {HDPublicKey} */ diff --git a/lib/hsd.js b/lib/hsd.js index e518d326d..14abb159b 100644 --- a/lib/hsd.js +++ b/lib/hsd.js @@ -56,6 +56,11 @@ hsd.define('blockchain', './blockchain'); hsd.define('Chain', './blockchain/chain'); hsd.define('ChainEntry', './blockchain/chainentry'); +// Client +hsd.define('client', './client'); +hsd.define('WalletClient', './client/wallet'); +hsd.define('NodeClient', './client/node'); + // Coins hsd.define('coins', './coins'); hsd.define('Coins', './coins/coins'); diff --git a/lib/mempool/airdropentry.js b/lib/mempool/airdropentry.js index 2803d37cc..337416e67 100644 --- a/lib/mempool/airdropentry.js +++ b/lib/mempool/airdropentry.js @@ -12,6 +12,11 @@ const policy = require('../protocol/policy'); const util = require('../utils/util'); const Address = require('../primitives/address'); +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../primitives/airdropproof')} AirdropProof */ + /* * Constants */ @@ -73,7 +78,6 @@ class AirdropEntry extends bio.Struct { /** * Inject properties from airdrop. - * @private * @param {AirdropProof} proof * @param {Number} height */ @@ -99,7 +103,6 @@ class AirdropEntry extends bio.Struct { /** * Create a mempool entry from an airdrop proof. * @param {AirdropProof} proof - * @param {Object} data * @param {Number} height - Entry height. * @returns {AirdropEntry} */ @@ -110,7 +113,7 @@ class AirdropEntry extends bio.Struct { /** * Get fee. - * @returns {Amount} + * @returns {AmountValue} */ getFee() { @@ -160,7 +163,8 @@ class AirdropEntry extends bio.Struct { /** * Serialize entry to a buffer. - * @returns {Buffer} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -180,9 +184,8 @@ class AirdropEntry extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {Buffer} data - * @returns {AirdropEntry} + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { diff --git a/lib/mempool/claimentry.js b/lib/mempool/claimentry.js index ac664191d..1da6d4ef9 100644 --- a/lib/mempool/claimentry.js +++ b/lib/mempool/claimentry.js @@ -13,6 +13,11 @@ const util = require('../utils/util'); const Address = require('../primitives/address'); const rules = require('../covenants/rules'); +/** @typedef {import('../primitives/claim')} Claim */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /* * Constants */ @@ -58,7 +63,6 @@ class ClaimEntry extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -84,7 +88,6 @@ class ClaimEntry extends bio.Struct { /** * Inject properties from claim. - * @private * @param {Claim} claim * @param {Object} data * @param {Number} height @@ -128,7 +131,7 @@ class ClaimEntry extends bio.Struct { /** * Get fee. - * @returns {Amount} + * @returns {AmountValue} */ getFee() { @@ -178,7 +181,8 @@ class ClaimEntry extends bio.Struct { /** * Serialize entry to a buffer. - * @returns {Buffer} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -204,9 +208,8 @@ class ClaimEntry extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {Buffer} data - * @returns {ClaimEntry} + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { diff --git a/lib/mempool/contractstate.js b/lib/mempool/contractstate.js index 102733899..cbc2948bc 100644 --- a/lib/mempool/contractstate.js +++ b/lib/mempool/contractstate.js @@ -14,6 +14,10 @@ const CoinView = require('../coins/coinview'); const {types} = rules; const {states} = NameState; +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../protocol/network')} Network */ +/** @typedef {import('../primitives/tx')} TX */ + /* * Constants */ @@ -25,6 +29,11 @@ const EMPTY = Buffer.alloc(0); */ class ContractState { + /** + * @constructor + * @param {Network} network + */ + constructor(network) { assert(network); @@ -35,18 +44,23 @@ class ContractState { this.unique = new BufferSet(); // Reference counter. + /** @type {BufferMap} */ this.refs = new BufferMap(); // Map of nameHash->set-of-txids. + /** @type {BufferMap} */ this.opens = new BufferMap(); // Map of nameHash->set-of-txids. + /** @type {BufferMap} */ this.bids = new BufferMap(); // Map of nameHash->set-of-txids. + /** @type {BufferMap} */ this.reveals = new BufferMap(); // Map of nameHash->set-of-txids. + /** @type {BufferMap} */ this.updates = new BufferMap(); // Current on-chain state @@ -65,24 +79,51 @@ class ContractState { return this; } + /** + * @param {Hash} nameHash + * @returns {Boolean} + */ + hasName(nameHash) { return this.unique.has(nameHash); } + /** + * @param {Hash} nameHash + * @returns {ContractState} + */ + addName(nameHash) { this.unique.add(nameHash); return this; } + /** + * @param {Hash} nameHash + * @returns {ContractState} + */ + removeName(nameHash) { this.unique.delete(nameHash); return this; } + /** + * @param {TX} tx + * @returns {Boolean} + */ + hasNames(tx) { return rules.hasNames(tx, this.unique); } + /** + * @param {BufferMap} map + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + addMap(map, nameHash, hash) { let set = map.get(nameHash); @@ -96,6 +137,13 @@ class ContractState { return this; } + /** + * @param {BufferMap} map + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + removeMap(map, nameHash, hash) { const set = map.get(nameHash); @@ -110,38 +158,91 @@ class ContractState { return this; } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + addOpen(nameHash, hash) { return this.addMap(this.opens, nameHash, hash); } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + removeOpen(nameHash, hash) { return this.removeMap(this.opens, nameHash, hash); } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + addBid(nameHash, hash) { return this.addMap(this.bids, nameHash, hash); } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + removeBid(nameHash, hash) { return this.removeMap(this.bids, nameHash, hash); } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + addReveal(nameHash, hash) { return this.addMap(this.reveals, nameHash, hash); } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + removeReveal(nameHash, hash) { return this.removeMap(this.reveals, nameHash, hash); } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + addUpdate(nameHash, hash) { return this.addMap(this.updates, nameHash, hash); } + /** + * @param {Hash} nameHash + * @param {Hash} hash + * @returns {ContractState} + */ + removeUpdate(nameHash, hash) { return this.removeMap(this.updates, nameHash, hash); } + /** + * @param {Hash} nameHash + * @returns {ContractState} + */ + reference(nameHash) { let count = this.refs.get(nameHash); @@ -155,6 +256,11 @@ class ContractState { return this; } + /** + * @param {Hash} nameHash + * @returns {ContractState} + */ + dereference(nameHash) { let count = this.refs.get(nameHash); @@ -176,6 +282,12 @@ class ContractState { return this; } + /** + * @param {TX} tx + * @param {CoinView} view + * @returns {ContractState} + */ + track(tx, view) { const hash = tx.hash(); @@ -233,6 +345,11 @@ class ContractState { return this; } + /** + * @param {TX} tx + * @returns {ContractState} + */ + untrack(tx) { const hash = tx.hash(); const names = new BufferSet(); @@ -270,6 +387,11 @@ class ContractState { return this; } + /** + * @param {CoinView} view + * @returns {ContractState} + */ + merge(view) { for (const [nameHash, ns] of view.names) { if (!this.refs.has(nameHash)) @@ -284,6 +406,13 @@ class ContractState { return this; } + /** + * @param {BufferMap} map + * @param {Hash} nameHash + * @param {BufferSet} items + * @returns {ContractState} + */ + toSet(map, nameHash, items) { const hashes = map.get(nameHash); @@ -296,26 +425,65 @@ class ContractState { return this; } + /** + * @param {Hash} nameHash + * @param {BufferSet} items + * @returns {ContractState} + */ + handleExpired(nameHash, items) { - return this.toSet(this.updates, nameHash, items); + this.toSet(this.updates, nameHash, items); + this.toSet(this.reveals, nameHash, items); + return this; } + /** + * @param {Hash} nameHash + * @param {BufferSet} items + * @returns {ContractState} + */ + handleOpen(nameHash, items) { return this.toSet(this.updates, nameHash, items); } + /** + * @param {Hash} nameHash + * @param {BufferSet} items + * @returns {ContractState} + */ + handleBidding(nameHash, items) { return this.toSet(this.opens, nameHash, items); } + /** + * @param {Hash} nameHash + * @param {BufferSet} items + * @returns {ContractState} + */ + handleReveal(nameHash, items) { return this.toSet(this.bids, nameHash, items); } + /** + * @param {Hash} nameHash + * @param {BufferSet} items + * @returns {ContractState} + */ + handleClosed(nameHash, items) { return this.toSet(this.reveals, nameHash, items); } + /** + * Invalidate transactions in the mempool. + * @param {Number} height + * @param {Boolean} hardened + * @returns {BufferSet} - list of invalidated tx hashes. + */ + invalidate(height, hardened) { const nextHeight = height + 1; const network = this.network; @@ -335,7 +503,7 @@ class ContractState { const state = ns.state(nextHeight, network); switch (state) { - case states.OPEN: + case states.OPENING: this.handleOpen(nameHash, invalid); break; case states.BIDDING: diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 8b8426c2b..afcd75745 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -10,7 +10,7 @@ const assert = require('bsert'); const path = require('path'); const EventEmitter = require('events'); const bdb = require('bdb'); -const {RollingFilter} = require('bfilter'); +const {RollingFilter} = require('@handshake-org/bfilter'); const Heap = require('bheep'); const {BufferMap, BufferSet} = require('buffer-map'); const random = require('bcrypto/lib/random'); @@ -116,7 +116,7 @@ class Mempool extends EventEmitter { if (this.options.persistent) { const entries = await this.cache.getEntries(); - const hardened = await this.hasHardening(); + const state = await this.getNextState(); const height = this.chain.height + 1; for (const entry of entries) @@ -132,7 +132,14 @@ class Mempool extends EventEmitter { this.indexEntry(entry, view); } - assert(await this.verifyCovenants(entry.tx, view, height, hardened)); + const validCovenants = await this.verifyCovenants( + entry.tx, + view, + height, + state.nameFlags + ); + + assert(validCovenants); this.contracts.track(entry.tx, view); } @@ -202,6 +209,7 @@ class Mempool extends EventEmitter { */ async _addBlock(block, txs, view) { + const nextHeight = block.height + 1; const entries = []; const cb = txs[0]; @@ -266,7 +274,8 @@ class Mempool extends EventEmitter { // for a now expired name. Another // example is a stale BID for a name // which has now reached the REVEAL state. - const hardened = await this.hasHardening(); + const state = await this.getNextState(); + const hardened = state.hasHardening(); const invalid = this.contracts.invalidate(block.height, hardened); for (const hash of invalid) { @@ -284,11 +293,17 @@ class Mempool extends EventEmitter { this.untrackClaim(entry); } + // At the chain.tip.height of claimPeriod - 1, we no longer + // accept new claims as they will be invalid in the next block. + // We also need to clean them up from the mempool. + if (nextHeight === this.network.names.claimPeriod) + this.dropClaims(); + // Remove all GooSig based airdrops from the mempool // when the block one before the height that disables // GooSig is added to the mempool to prevent the // mining of invalid blocks. - if (block.height + 1 === this.network.goosigStop) { + if (nextHeight === this.network.goosigStop) { for (const [hash, entry] of this.airdrops.entries()) { const airdrop = this.getAirdrop(hash); const key = airdrop.getKey(); @@ -430,7 +445,7 @@ class Mempool extends EventEmitter { async _handleReorg() { const height = this.chain.height + 1; - const hardened = await this.hasHardening(); + const state = await this.getNextState(); const mtp = await this.chain.getMedianTime(this.chain.tip); const remove = []; const lockFlags = common.lockFlags.STANDARD_LOCKTIME_FLAGS; @@ -493,8 +508,14 @@ class Mempool extends EventEmitter { if (hasCovenants) { const view = new CoinView(); - - if (!await this.verifyCovenants(entry.tx, view, height, hardened)) { + const validCovenants = await this.verifyCovenants( + entry.tx, + view, + height, + state.nameFlags + ); + + if (!validCovenants) { remove.push(hash); continue; } @@ -526,16 +547,6 @@ class Mempool extends EventEmitter { return this.nextState; } - /** - * Test whether the chain state is hardened. - * @returns {Boolean} - */ - - async hasHardening() { - const state = await this.getNextState(); - return state.hasHardening(); - } - /** * Reset the mempool. * @method @@ -1101,7 +1112,7 @@ class Mempool extends EventEmitter { 0); } - const hardened = await this.hasHardening(); + const state = await this.getNextState(); const proof = claim.getProof(); if (!proof || !proof.isSane()) @@ -1152,7 +1163,7 @@ class Mempool extends EventEmitter { const view = new CoinView(); // Covenant verification. - if (!await this.verifyCovenants(tx, view, height, hardened)) { + if (!await this.verifyCovenants(tx, view, height, state.nameFlags)) { throw new VerifyError(tx, 'invalid', 'invalid-covenant', @@ -1340,7 +1351,9 @@ class Mempool extends EventEmitter { } if (proof.isWeak()) { - if (await this.hasHardening()) + const state = await this.getNextState(); + + if (state.hasHardening()) throw new VerifyError(proof, 'invalid', 'bad-airdrop-rsa1024', 0); } @@ -1590,7 +1603,7 @@ class Mempool extends EventEmitter { async verify(entry, view) { const network = this.network; const height = this.chain.height + 1; - const hardened = await this.hasHardening(); + const state = await this.getNextState(); const lockFlags = common.lockFlags.STANDARD_LOCKTIME_FLAGS; const tx = entry.tx; @@ -1692,7 +1705,7 @@ class Mempool extends EventEmitter { } // Covenant verification. - if (!await this.verifyCovenants(tx, view, height, hardened)) { + if (!await this.verifyCovenants(tx, view, height, state.nameFlags)) { throw new VerifyError(tx, 'invalid', 'invalid-covenant', @@ -1757,12 +1770,12 @@ class Mempool extends EventEmitter { * @param {TX} tx * @param {CoinView} view * @param {Number} height - * @param {Boolean} hardened + * @param {NameFlags} nameFlags */ - async verifyCovenants(tx, view, height, hardened) { + async verifyCovenants(tx, view, height, nameFlags) { try { - await this.chain.verifyCovenants(tx, view, height, hardened); + await this.chain.verifyCovenants(tx, view, height, nameFlags); } catch (e) { if (e.type !== 'VerifyError') throw e; diff --git a/lib/mempool/mempoolentry.js b/lib/mempool/mempoolentry.js index 6749543b9..08043fc1c 100644 --- a/lib/mempool/mempoolentry.js +++ b/lib/mempool/mempoolentry.js @@ -11,6 +11,13 @@ const policy = require('../protocol/policy'); const util = require('../utils/util'); const TX = require('../primitives/tx'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').HexHash} HexHash */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../coins/coinview')} CoinView */ + /** * Mempool Entry * Represents a mempool entry. @@ -26,12 +33,12 @@ class MempoolEntry extends bio.Struct { /** * Create a mempool entry. * @constructor - * @param {Object} options + * @param {Object} [options] * @param {TX} options.tx - Transaction in mempool. * @param {Number} options.height - Entry height. * @param {Number} options.priority - Entry priority. * @param {Number} options.time - Entry time. - * @param {Amount} options.value - Value of on-chain coins. + * @param {AmountValue} options.value - Value of on-chain coins. */ constructor(options) { @@ -57,7 +64,6 @@ class MempoolEntry extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -80,8 +86,8 @@ class MempoolEntry extends bio.Struct { /** * Inject properties from transaction. - * @private * @param {TX} tx + * @param {CoinView} view * @param {Number} height */ @@ -123,6 +129,7 @@ class MempoolEntry extends bio.Struct { /** * Create a mempool entry from a TX. * @param {TX} tx + * @param {CoinView} view * @param {Number} height - Entry height. * @returns {MempoolEntry} */ @@ -142,7 +149,7 @@ class MempoolEntry extends bio.Struct { /** * Calculate reverse transaction hash. - * @returns {Hash} + * @returns {HexHash} */ txid() { @@ -171,7 +178,7 @@ class MempoolEntry extends bio.Struct { /** * Get fee. - * @returns {Amount} + * @returns {AmountValue} */ getFee() { @@ -180,7 +187,7 @@ class MempoolEntry extends bio.Struct { /** * Get delta fee. - * @returns {Amount} + * @returns {AmountValue} */ getDeltaFee() { @@ -284,7 +291,8 @@ class MempoolEntry extends bio.Struct { /** * Serialize entry to a buffer. - * @returns {Buffer} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -303,9 +311,8 @@ class MempoolEntry extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {Buffer} data - * @returns {MempoolEntry} + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { diff --git a/lib/migrations/README.md b/lib/migrations/README.md index 7f556ed44..06064b407 100644 --- a/lib/migrations/README.md +++ b/lib/migrations/README.md @@ -64,6 +64,24 @@ migration to run specific to that db. incremented from there. (Currently, wallet and chain both have one migration) ## Writing migrations +### Migration requirements + 1. Migration must not depend on the methods, but instead interact + with the database directly. This makes sure that future code changes + to the chain/wallet db do not affect old migrations. Especially if someone + is upgrading multiple versions at once. + 2. Migration must allow for interruption. Using batch provided to the migration + is useful for this purpose. If migration does not utilize the provided batch, + it should be possible to stop the migration and resume or restart it later. + This makes sure that interruption by user or the system does not corrupt + the database. + 3. Each migration should copy the existing db keys and use those + in the migration, instead of depending on layout. Future updates may modify + the layout and break old migrations. + 4. Migration Tests should also follow the same 3 rules. + +NOTE: Migration tests can use database dumps from the previous/current versions. +NOTE: Migration test db generation must be deterministic. + ### Databases and migrations HSD has two separate databases with their own migrations: ChainDB and WalletDB. Depending which database your migration affects, you will need to diff --git a/lib/mining/common.js b/lib/mining/common.js index dcdc1bd28..27128563d 100644 --- a/lib/mining/common.js +++ b/lib/mining/common.js @@ -34,8 +34,8 @@ const B0 = 0x1; common.swap32 = function swap32(data) { for (let i = 0; i < data.length; i += 4) { - const field = data.readUInt32LE(i, true); - data.writeUInt32BE(field, i, true); + const field = data.readUInt32LE(i); + data.writeUInt32BE(field, i); } return data; @@ -53,20 +53,20 @@ common.double256 = function double256(target) { assert(target.length === 32); - hi = target.readUInt32BE(0, true); - lo = target.readUInt32BE(4, true); + hi = target.readUInt32BE(0); + lo = target.readUInt32BE(4); n += (hi * 0x100000000 + lo) * B192; - hi = target.readUInt32BE(8, true); - lo = target.readUInt32BE(12, true); + hi = target.readUInt32BE(8); + lo = target.readUInt32BE(12); n += (hi * 0x100000000 + lo) * B128; - hi = target.readUInt32BE(16, true); - lo = target.readUInt32BE(20, true); + hi = target.readUInt32BE(16); + lo = target.readUInt32BE(20); n += (hi * 0x100000000 + lo) * B64; - hi = target.readUInt32BE(24, true); - lo = target.readUInt32BE(28, true); + hi = target.readUInt32BE(24); + lo = target.readUInt32BE(28); n += (hi * 0x100000000 + lo) * B0; return n; @@ -113,7 +113,7 @@ common.getTarget = function getTarget(bits) { /** * Get bits from target. * @param {Buffer} data - * @returns {Buffer} + * @returns {Number} */ common.getBits = function getBits(data) { diff --git a/lib/mining/miner.js b/lib/mining/miner.js index daa067eeb..9a8913c78 100644 --- a/lib/mining/miner.js +++ b/lib/mining/miner.js @@ -469,7 +469,7 @@ class MinerOptions { this.chain = null; this.mempool = null; - this.version = 0; + this.version = -1; this.addresses = []; this.coinbaseFlags = Buffer.from(`mined by ${pkg.name}`, 'ascii'); this.preverify = false; diff --git a/lib/mining/template.js b/lib/mining/template.js index 57bd8033d..41dfa7d91 100644 --- a/lib/mining/template.js +++ b/lib/mining/template.js @@ -23,6 +23,14 @@ const CoinView = require('../coins/coinview'); const rules = require('../covenants/rules'); const common = require('./common'); +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../primitives/claim')} Claim */ +/** @typedef {import('../primitives/airdropproof')} AirdropProof */ +/** @typedef {import('../mempool/airdropentry')} AirdropEntry */ +/** @typedef {import('../mempool/claimentry')} ClaimEntry */ +/** @typedef {import('../mempool/mempoolentry')} MempoolEntry */ + /* * Constants */ @@ -38,7 +46,7 @@ class BlockTemplate { /** * Create a block template. * @constructor - * @param {Object} options + * @param {Object} [options] */ constructor(options) { @@ -63,7 +71,7 @@ class BlockTemplate { this.witnessRoot = consensus.ZERO_HASH; this.treeRoot = consensus.ZERO_HASH; this.reservedRoot = consensus.ZERO_HASH; - this.coinbase = DUMMY; + this.coinbase = new TX(); this.items = []; this.claims = []; this.airdrops = []; @@ -74,7 +82,6 @@ class BlockTemplate { /** * Inject properties from options. - * @private * @param {Object} options * @returns {BlockTemplate} */ @@ -233,7 +240,7 @@ class BlockTemplate { /** * Calculate the block reward. - * @returns {Amount} + * @returns {AmountValue} */ getReward() { @@ -294,13 +301,14 @@ class BlockTemplate { output.value = claim.value - claim.fee; output.address = claim.address; - output.covenant.type = rules.types.CLAIM; - output.covenant.pushHash(claim.nameHash); - output.covenant.pushU32(this.height); - output.covenant.push(claim.name); - output.covenant.pushU8(flags); - output.covenant.pushHash(claim.commitHash); - output.covenant.pushU32(claim.commitHeight); + output.covenant.setClaim( + claim.nameHash, + this.height, + claim.name, + flags, + claim.commitHash, + claim.commitHeight + ); cb.outputs.push(output); } @@ -363,9 +371,10 @@ class BlockTemplate { /** * Create raw block header with given parameters. - * @param {Buffer} extraNonce - * @param {Number} time * @param {Number} nonce + * @param {Number} time + * @param {Buffer} extraNonce + * @param {Buffer} mask * @returns {Buffer} */ @@ -389,10 +398,10 @@ class BlockTemplate { /** * Calculate proof with given parameters. - * @param {Number} nonce1 - * @param {Number} nonce2 + * @param {Number} nonce * @param {Number} time - * @param {Buffer} nonce + * @param {Buffer} extraNonce + * @param {Buffer} mask * @returns {BlockProof} */ @@ -485,6 +494,7 @@ class BlockTemplate { * Add a transaction to the template. * @param {TX} tx * @param {CoinView} view + * @returns {Boolean} */ addTX(tx, view) { @@ -532,7 +542,8 @@ class BlockTemplate { * Add a transaction to the template * (less verification than addTX). * @param {TX} tx - * @param {CoinView?} view + * @param {CoinView?} [view] + * @returns {Boolean} */ pushTX(tx, view) { @@ -565,6 +576,7 @@ class BlockTemplate { * Add a claim to the template. * @param {Claim} claim * @param {Object} data + * @returns {Boolean} */ addClaim(claim, data) { @@ -581,6 +593,7 @@ class BlockTemplate { /** * Add a claim to the template. * @param {AirdropProof} proof + * @returns {Boolean} */ addAirdrop(proof) { @@ -800,9 +813,9 @@ class BlockAirdrop { } /** - * Instantiate block entry from mempool entry. - * @param {ClaimEntry} entry - * @returns {BlockClaim} + * Instantiate block airdrop from mempool airdropentry. + * @param {AirdropEntry} entry + * @returns {BlockAirdrop} */ static fromEntry(entry) { @@ -836,15 +849,27 @@ class BlockProof { this.mask = consensus.ZERO_HASH; } + /** + * @returns {Hash} + */ + hash() { return this.powHash(); } + /** + * @returns {Hash} + */ + shareHash() { const hdr = Headers.fromMiner(this.hdr); return hdr.shareHash(); } + /** + * @returns {Hash} + */ + powHash() { const hash = this.shareHash(); @@ -854,10 +879,20 @@ class BlockProof { return hash; } - verify(target, network) { + /** + * @param {Buffer} target + * @returns {Boolean} + */ + + verify(target) { return this.powHash().compare(target) <= 0; } + /** + * Calculate the target difficulty. + * @returns {Number} + */ + getDifficulty() { return common.getDifficulty(this.powHash()); } diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 3cd88c0cf..d6fb3474b 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -22,6 +22,10 @@ const Block = require('../primitives/block'); const common = require('./common'); const {encoding} = bio; +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../mempool/mempool')} Mempool */ + const { MAX_BLOCK_SIZE, HEADER_SIZE @@ -45,7 +49,7 @@ class CompactBlock extends AbstractBlock { /** * Create a compact block. * @constructor - * @param {Object?} options + * @param {Object?} [options] */ constructor(options) { @@ -68,7 +72,6 @@ class CompactBlock extends AbstractBlock { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -111,8 +114,7 @@ class CompactBlock extends AbstractBlock { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -173,8 +175,7 @@ class CompactBlock extends AbstractBlock { /** * Serialize block to buffer writer. - * @private - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -308,7 +309,6 @@ class CompactBlock extends AbstractBlock { /** * Initialize compact block and short id map. - * @private */ init() { @@ -389,7 +389,6 @@ class CompactBlock extends AbstractBlock { /** * Inject properties from block. - * @private * @param {Block} block * @param {Buffer?} nonce * @returns {CompactBlock} @@ -462,7 +461,7 @@ class TXRequest extends bio.Struct { /** * TX Request * @constructor - * @param {Object?} options + * @param {Object?} [options] */ constructor(options) { @@ -477,9 +476,8 @@ class TXRequest extends bio.Struct { /** * Inject properties from options. - * @private * @param {Object} options - * @returns {TXRequest} + * @returns {this} */ fromOptions(options) { @@ -493,7 +491,6 @@ class TXRequest extends bio.Struct { /** * Inject properties from compact block. - * @private * @param {CompactBlock} block * @returns {TXRequest} */ @@ -521,9 +518,8 @@ class TXRequest extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br - * @returns {TXRequest} + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { @@ -575,7 +571,8 @@ class TXRequest extends bio.Struct { /** * Write serialized request to buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -608,7 +605,7 @@ class TXResponse extends bio.Struct { /** * Create a tx response. * @constructor - * @param {Object?} options + * @param {Object?} [options] */ constructor(options) { @@ -623,9 +620,8 @@ class TXResponse extends bio.Struct { /** * Inject properties from options. - * @private * @param {Object} options - * @returns {TXResponse} + * @returns {this} */ fromOptions(options) { @@ -639,9 +635,8 @@ class TXResponse extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br - * @returns {TXResponse} + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { @@ -657,8 +652,8 @@ class TXResponse extends bio.Struct { /** * Inject properties from block. - * @private * @param {Block} block + * @param {TXRequest} req * @returns {TXResponse} */ @@ -678,6 +673,7 @@ class TXResponse extends bio.Struct { /** * Instantiate response from block. * @param {Block} block + * @param {TXRequest} req * @returns {TXResponse} */ @@ -704,8 +700,7 @@ class TXResponse extends bio.Struct { /** * Write serialized response to buffer writer. - * @private - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { diff --git a/lib/net/hostlist.js b/lib/net/hostlist.js index fe9f9dd31..595815374 100644 --- a/lib/net/hostlist.js +++ b/lib/net/hostlist.js @@ -219,8 +219,6 @@ class HostList { /** * Read and initialize from hosts file. - * @method - * @returns {Promise} */ injectSeeds() { @@ -464,7 +462,7 @@ class HostList { * Get fresh bucket for host. * @private * @param {HostEntry} entry - * @param {NetAddress?} src + * @param {NetAddress?} [src] * @returns {Map} */ @@ -534,7 +532,7 @@ class HostList { /** * Add host to host list. * @param {NetAddress} addr - * @param {NetAddress?} src + * @param {NetAddress?} [src] * @returns {Boolean} */ @@ -889,7 +887,7 @@ class HostList { addSeed(host) { const ip = IP.fromHostname(host); - if (ip.type === IP.types.DNS) { + if (ip.type === IP.types.NONE) { // Defer for resolution. this.dnsSeeds.push(ip); return null; @@ -914,7 +912,7 @@ class HostList { addNode(host) { const ip = IP.fromHostname(host); - if (ip.type === IP.types.DNS) { + if (ip.type === IP.types.NONE) { // Defer for resolution. this.dnsNodes.push(ip); return null; @@ -1163,7 +1161,7 @@ class HostList { async populate(target) { const addrs = []; - assert(target.type === IP.types.DNS, 'Resolved host passed.'); + assert(target.type === IP.types.NONE, 'Resolved host passed.'); this.logger.info('Resolving host: %s.', target.host); @@ -1465,8 +1463,8 @@ class HostEntry { /** * Create a host entry. * @constructor - * @param {NetAddress} addr - * @param {NetAddress} src + * @param {NetAddress} [addr] + * @param {NetAddress} [src] */ constructor(addr, src) { @@ -1841,7 +1839,5 @@ function random(max) { * Expose */ -exports = HostList; -exports.HostEntry = HostEntry; - -module.exports = exports; +HostList.HostEntry = HostEntry; +module.exports = HostList; diff --git a/lib/net/netaddress.js b/lib/net/netaddress.js index f949df677..79a12fdbf 100644 --- a/lib/net/netaddress.js +++ b/lib/net/netaddress.js @@ -14,6 +14,10 @@ const Network = require('../protocol/network'); const util = require('../utils/util'); const common = require('./common'); +/** @typedef {import('net').Socket} NetSocket */ +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /* * Constants */ @@ -34,7 +38,7 @@ class NetAddress extends bio.Struct { /** * Create a network address. * @constructor - * @param {Object} options + * @param {Object} [options] * @param {Number?} options.time - Timestamp. * @param {Number?} options.services - Service bits. * @param {String?} options.host - IP address (IPv6 or IPv4). @@ -58,7 +62,6 @@ class NetAddress extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -217,6 +220,7 @@ class NetAddress extends bio.Struct { /** * Compare against another network address. + * @param {NetAddress} addr * @returns {Boolean} */ @@ -226,6 +230,7 @@ class NetAddress extends bio.Struct { /** * Compare against another network address. + * @param {NetAddress} addr * @returns {Number} */ @@ -326,10 +331,10 @@ class NetAddress extends bio.Struct { /** * Inject properties from host, port, and network. - * @private * @param {String} host * @param {Number} port - * @param {(Network|NetworkType)?} network + * @param {Buffer} [key] + * @param {(Network|NetworkType)?} [network] */ fromHost(host, port, key, network) { @@ -355,7 +360,8 @@ class NetAddress extends bio.Struct { * from a host and port. * @param {String} host * @param {Number} port - * @param {(Network|NetworkType)?} network + * @param {Buffer} [key] + * @param {(Network|NetworkType)?} [network] * @returns {NetAddress} */ @@ -365,9 +371,8 @@ class NetAddress extends bio.Struct { /** * Inject properties from hostname and network. - * @private * @param {String} hostname - * @param {(Network|NetworkType)?} network + * @param {(Network|NetworkType)?} [network] */ fromHostname(hostname, network) { @@ -385,7 +390,7 @@ class NetAddress extends bio.Struct { * Instantiate a network address * from a hostname (i.e. 127.0.0.1:8333). * @param {String} hostname - * @param {(Network|NetworkType)?} network + * @param {(Network|NetworkType)?} [network] * @returns {NetAddress} */ @@ -395,8 +400,8 @@ class NetAddress extends bio.Struct { /** * Inject properties from socket. - * @private - * @param {net.Socket} socket + * @param {NetSocket} socket + * @param {(Network|NetworkType)?} [network] */ fromSocket(socket, network) { @@ -410,12 +415,13 @@ class NetAddress extends bio.Struct { /** * Instantiate a network address * from a socket. - * @param {net.Socket} socket + * @param {NetSocket} socket + * @param {(Network|NetworkType)?} [network] * @returns {NetAddress} */ - static fromSocket(hostname, network) { - return new this().fromSocket(hostname, network); + static fromSocket(socket, network) { + return new this().fromSocket(socket, network); } /** @@ -429,8 +435,8 @@ class NetAddress extends bio.Struct { /** * Write network address to a buffer writer. - * @param {BufferWriter} bw - * @returns {Buffer} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -442,13 +448,12 @@ class NetAddress extends bio.Struct { bw.fill(0, 20); // reserved bw.writeU16(this.port); bw.writeBytes(this.key); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -493,9 +498,8 @@ class NetAddress extends bio.Struct { /** * Inject properties from json object. - * @private * @param {Object} json - * @returns {NetAddress} + * @returns {this} */ fromJSON(json) { @@ -544,7 +548,7 @@ NetAddress.DEFAULT_SERVICES = 0 /** * @param {NetAddress} addr - * @returns {Number} + * @returns {Buffer} */ function groupKey(addr) { diff --git a/lib/net/packets.js b/lib/net/packets.js index cb3f321f2..0876e09fa 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -14,7 +14,7 @@ const assert = require('bsert'); const bio = require('bufio'); const blake2b = require('bcrypto/lib/blake2b'); const UrkelProof = require('urkel').Proof; -const {BloomFilter} = require('bfilter'); +const {BloomFilter} = require('@handshake-org/bfilter'); const common = require('./common'); const util = require('../utils/util'); const bip152 = require('./bip152'); @@ -30,6 +30,12 @@ const AirdropProof = require('../primitives/airdropproof'); const {encoding} = bio; const DUMMY = Buffer.alloc(0); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../protocol/errors').VerifyError} VerifyError */ +/** @typedef {import('../primitives/block')} Block */ + /** * Packet types. * @enum {Number} @@ -156,15 +162,15 @@ class VersionPacket extends Packet { /** * Create a version packet. * @constructor - * @param {Object?} options - * @param {Number} options.version - Protocol version. - * @param {Number} options.services - Service bits. - * @param {Number} options.time - Timestamp of discovery. - * @param {NetAddress} options.remote - Their address. - * @param {Buffer} options.nonce - * @param {String} options.agent - User agent string. - * @param {Number} options.height - Chain height. - * @param {Boolean} options.noRelay - Whether transactions + * @param {Object} [options] + * @param {Number?} options.version - Protocol version. + * @param {Number?} options.services - Service bits. + * @param {Number?} options.time - Timestamp of discovery. + * @param {NetAddress?} options.remote - Their address. + * @param {Buffer?} options.nonce + * @param {String?} options.agent - User agent string. + * @param {Number?} options.height - Chain height. + * @param {Boolean?} options.noRelay - Whether transactions * should be relayed immediately. */ @@ -173,6 +179,7 @@ class VersionPacket extends Packet { this.type = exports.types.VERSION; + /** @type {Number} */ this.version = common.PROTOCOL_VERSION; this.services = common.LOCAL_SERVICES; this.time = util.now(); @@ -188,7 +195,6 @@ class VersionPacket extends Packet { /** * Inject properties from options. - * @private * @param {Object} options */ @@ -238,7 +244,8 @@ class VersionPacket extends Packet { /** * Write version packet to buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -252,13 +259,13 @@ class VersionPacket extends Packet { bw.writeString(this.agent, 'ascii'); bw.writeU32(this.height); bw.writeU8(this.noRelay ? 1 : 0); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { @@ -329,18 +336,18 @@ class PingPacket extends Packet { /** * Serialize ping packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { bw.writeBytes(this.nonce); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -381,18 +388,18 @@ class PongPacket extends Packet { /** * Serialize pong packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { bw.writeBytes(this.nonce); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -454,7 +461,8 @@ class AddrPacket extends Packet { /** * Serialize addr packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -463,13 +471,12 @@ class AddrPacket extends Packet { for (const item of this.items) item.write(bw); - return this; + return bw; } /** * Inject properties from serialized data. - * @private - * @param {Buffer} data + * @param {bio.BufferReader} br */ read(br) { @@ -500,6 +507,7 @@ class InvPacket extends Packet { this.type = exports.types.INV; + /** @type {InvItem[]} */ this.items = items || []; } @@ -517,7 +525,8 @@ class InvPacket extends Packet { /** * Serialize inv packet to writer. - * @param {Buffer} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -528,13 +537,12 @@ class InvPacket extends Packet { for (const item of this.items) item.write(bw); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -624,7 +632,8 @@ class GetBlocksPacket extends Packet { /** * Serialize getblocks packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -637,13 +646,12 @@ class GetBlocksPacket extends Packet { bw.writeHash(this.stop); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -718,7 +726,8 @@ class HeadersPacket extends Packet { /** * Serialize headers packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -729,13 +738,12 @@ class HeadersPacket extends Packet { for (const item of this.items) item.write(bw); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -799,7 +807,7 @@ class BlockPacket extends Packet { /** * Serialize block packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -808,8 +816,7 @@ class BlockPacket extends Packet { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -850,7 +857,7 @@ class TXPacket extends Packet { /** * Serialize tx packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -859,8 +866,7 @@ class TXPacket extends Packet { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -876,13 +882,14 @@ class TXPacket extends Packet { * (see {@link RejectPacket.codes}). * @property {String?} msg - Message. * @property {String?} reason - Reason. - * @property {(Hash|Buffer)?} data - Transaction or block hash. + * @property {(Hash|Buffer)?} hash - Transaction or block hash. */ class RejectPacket extends Packet { /** * Create reject packet. * @constructor + * @param {Object} [options] */ constructor(options) { @@ -893,6 +900,7 @@ class RejectPacket extends Packet { this.message = 0; this.code = RejectPacket.codes.INVALID; this.reason = ''; + /** @type {Hash?} */ this.hash = null; if (options) @@ -901,7 +909,6 @@ class RejectPacket extends Packet { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -965,7 +972,7 @@ class RejectPacket extends Packet { /** * Serialize reject packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -977,13 +984,12 @@ class RejectPacket extends Packet { if (this.hash) bw.writeHash(this.hash); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1008,11 +1014,10 @@ class RejectPacket extends Packet { /** * Inject properties from reason message and object. - * @private - * @param {Number} code + * @param {(String|Number)?} code * @param {String} reason - * @param {String?} msg - * @param {Hash?} hash + * @param {Number?} [msg] + * @param {Hash?} [hash] */ fromReason(code, reason, msg, hash) { @@ -1040,10 +1045,10 @@ class RejectPacket extends Packet { /** * Instantiate reject packet from reason message. - * @param {Number} code + * @param {(String|Number)?} code * @param {String} reason - * @param {String?} msg - * @param {Hash?} hash + * @param {Number?} [msg] + * @param {Hash?} [hash] * @returns {RejectPacket} */ @@ -1054,7 +1059,7 @@ class RejectPacket extends Packet { /** * Instantiate reject packet from verify error. * @param {VerifyError} err - * @param {(TX|Block)?} obj + * @param {(TX|Block)?} [obj] * @returns {RejectPacket} */ @@ -1172,7 +1177,7 @@ class FilterLoadPacket extends Packet { /** * Serialize filterload packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -1181,8 +1186,7 @@ class FilterLoadPacket extends Packet { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1232,18 +1236,17 @@ class FilterAddPacket extends Packet { /** * Serialize filteradd packet to writer. - * @returns {BufferWriter} bw + * @returns {BufioWriter} bw */ write(bw) { bw.writeVarBytes(this.data); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1301,7 +1304,7 @@ class MerkleBlockPacket extends Packet { /** * Serialize merkleblock packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -1310,8 +1313,7 @@ class MerkleBlockPacket extends Packet { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1352,18 +1354,17 @@ class FeeFilterPacket extends Packet { /** * Serialize feefilter packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { bw.writeI64(this.rate); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1407,19 +1408,18 @@ class SendCmpctPacket extends Packet { /** * Serialize sendcmpct packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { bw.writeU8(this.mode); bw.writeU64(this.version); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1447,12 +1447,13 @@ class CmpctBlockPacket extends Packet { this.type = exports.types.CMPCTBLOCK; + /** @type {Block|bip152.CompactBlock} */ this.block = block || new bip152.CompactBlock(); } /** * Serialize cmpctblock packet. - * @returns {Buffer} + * @returns {Number} */ getSize() { @@ -1461,7 +1462,7 @@ class CmpctBlockPacket extends Packet { /** * Serialize cmpctblock packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -1470,8 +1471,7 @@ class CmpctBlockPacket extends Packet { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1483,14 +1483,14 @@ class CmpctBlockPacket extends Packet { /** * GetBlockTxn Packet * @extends Packet - * @property {TXRequest} request + * @property {bip152.TXRequest} request */ class GetBlockTxnPacket extends Packet { /** * Create a `getblocktxn` packet. * @constructor - * @param {TXRequest?} request + * @param {bip152.TXRequest?} [request] */ constructor(request) { @@ -1512,7 +1512,7 @@ class GetBlockTxnPacket extends Packet { /** * Serialize getblocktxn packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -1521,8 +1521,7 @@ class GetBlockTxnPacket extends Packet { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1534,14 +1533,14 @@ class GetBlockTxnPacket extends Packet { /** * BlockTxn Packet * @extends Packet - * @property {TXResponse} response + * @property {bip152.TXResponse} response */ class BlockTxnPacket extends Packet { /** * Create a `blocktxn` packet. * @constructor - * @param {TXResponse?} response + * @param {bip152.TXResponse?} [response] */ constructor(response) { @@ -1563,7 +1562,7 @@ class BlockTxnPacket extends Packet { /** * Serialize blocktxn packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { @@ -1572,8 +1571,7 @@ class BlockTxnPacket extends Packet { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1593,8 +1591,8 @@ class GetProofPacket extends Packet { /** * Create a `getproof` packet. * @constructor - * @param {Buffer?} root - * @param {Buffer?} key + * @param {Buffer?} [root] + * @param {Buffer?} [key] */ constructor(root, key) { @@ -1616,19 +1614,18 @@ class GetProofPacket extends Packet { /** * Serialize getproof packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { bw.writeHash(this.root); bw.writeBytes(this.key); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1648,9 +1645,9 @@ class ProofPacket extends Packet { /** * Create a `proof` packet. * @constructor - * @param {Buffer} root - * @param {Buffer} key - * @param {UrkelProof} proof + * @param {Buffer} [root] + * @param {Buffer} [key] + * @param {UrkelProof} [proof] */ constructor(root, key, proof) { @@ -1675,20 +1672,19 @@ class ProofPacket extends Packet { /** * Serialize proof packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { bw.writeHash(this.root); bw.writeBytes(this.key); this.proof.writeBW(bw, blake2b, 256); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1708,7 +1704,7 @@ class ClaimPacket extends Packet { /** * Create a `proof` packet. * @constructor - * @param {Claim?} claim + * @param {Claim?} [claim] */ constructor(claim) { @@ -1729,18 +1725,17 @@ class ClaimPacket extends Packet { /** * Serialize proof packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { this.claim.write(bw); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1758,7 +1753,7 @@ class AirdropPacket extends Packet { /** * Create a `proof` packet. * @constructor - * @param {AirdropProof?} proof + * @param {AirdropProof?} [proof] */ constructor(proof) { @@ -1779,18 +1774,18 @@ class AirdropPacket extends Packet { /** * Serialize proof packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { this.proof.write(bw); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1810,8 +1805,8 @@ class UnknownPacket extends Packet { /** * Create an unknown packet. * @constructor - * @param {Number|null} type - * @param {Buffer|null} data + * @param {Number?} [type] + * @param {Buffer?} [data] */ constructor(type, data) { @@ -1837,18 +1832,17 @@ class UnknownPacket extends Packet { /** * Serialize unknown packet to writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw */ write(bw) { bw.writeBytes(this.data); - return this; + return bw; } /** * Inject properties from serialized data. - * @private - * @param {BufferReader} data + * @param {bio.BufferReader} br * @param {Number} type */ diff --git a/lib/net/peer.js b/lib/net/peer.js index 04644c1c3..f3eb28a41 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -13,7 +13,7 @@ const {format} = require('util'); const tcp = require('btcp'); const dns = require('bdns'); const Logger = require('blgr'); -const {RollingFilter} = require('bfilter'); +const {RollingFilter} = require('@handshake-org/bfilter'); const {BufferMap} = require('buffer-map'); const Parser = require('./parser'); const Framer = require('./framer'); @@ -34,6 +34,11 @@ const services = common.services; const invTypes = InvItem.types; const packetTypes = packets.types; +/** @typedef {import('net').Socket} NetSocket */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../protocol/errors').VerifyError} VerifyError */ + /** * Represents a network peer. * @alias module:net.Peer @@ -141,7 +146,8 @@ class Peer extends EventEmitter { /** * Create inbound peer from socket. * @param {PeerOptions} options - * @param {net.Socket} socket + * @param {NetSocket} socket + * @param {Boolean} encrypted * @returns {Peer} */ @@ -620,7 +626,7 @@ class Peer extends EventEmitter { // Check the peer's bloom // filter if they're using spv. if (this.spvFilter) { - if (!tx.test(this.spvFilter)) + if (!tx.testAndMaybeUpdate(this.spvFilter)) continue; } diff --git a/lib/net/pool.js b/lib/net/pool.js index d12d6f42c..a0b72cfd5 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -17,7 +17,7 @@ const List = require('blst'); const base32 = require('bcrypto/lib/encoding/base32'); const {BufferMap, BufferSet} = require('buffer-map'); const blake2b = require('bcrypto/lib/blake2b'); -const {BloomFilter, RollingFilter} = require('bfilter'); +const {BloomFilter, RollingFilter} = require('@handshake-org/bfilter'); const rng = require('bcrypto/lib/random'); const secp256k1 = require('bcrypto/lib/secp256k1'); const {siphash} = require('bcrypto/lib/siphash'); diff --git a/lib/net/seeds/main.js b/lib/net/seeds/main.js index 40885710b..c84ab4d81 100644 --- a/lib/net/seeds/main.js +++ b/lib/net/seeds/main.js @@ -18,11 +18,11 @@ module.exports = [ // chjj 'aoihqqagbhzz6wxg43itefqvmgda4uwtky362p22kbimcyg5fdp54@172.104.214.189', // chjj - 'ajk57wutnhfdzvqwqrgab3wwh4wxoqgnkz4avbln54pgj5jwefcts@172.104.177.177', - // chjj 'am2lsmbzzxncaptqjo22jay3mztfwl33bxhkp7icfx7kmi5rvjaic@139.162.183.168', // chjj 'ap5vuwabzwyz6akhesanada4skhetd2jsvpkwuqxzuaoovn5ez4xg@45.79.134.225', + // handshake-enthusiast + 'aiwykdz37okry3pb2lzdsgbxeg72uky2zckxmiapzstpqqmb2hnge@35.154.209.88', // Same nodes as above, but clearnet '165.22.151.242', @@ -33,54 +33,42 @@ module.exports = [ '173.255.209.126', '74.207.247.120', '172.104.214.189', - '172.104.177.177', '139.162.183.168', '45.79.134.225', + '35.154.209.88', // Other nodes discovered by seed.easyhandshake.com and htools-org/hnsnodes '104.254.246.153', - '66.94.118.127', - '165.227.93.117', - '98.47.90.111', - '85.214.33.200', - '178.18.254.92', - '152.69.186.119', - '89.58.17.86', - '76.217.158.15', - '149.102.152.193', + '108.175.4.26', + '129.153.166.244', + '139.177.198.45', '139.59.211.187', '147.124.228.198', - '108.175.4.26', - '66.29.151.42', + '152.69.186.119', + '162.248.93.246', + '165.227.93.117', + '18.197.233.236', + '193.122.196.124', + '202.61.201.15', '205.250.112.133', + '212.227.68.123', + '3.211.218.12', '44.229.138.206', - '81.6.58.121', - '154.26.133.109', - '140.238.164.155', - '81.169.159.30', + '46.137.240.49', '50.112.123.184', - '185.232.71.108', - '207.244.233.76', - '140.238.196.136', - '193.41.237.153', - '82.223.165.215', - '212.227.68.123', - '202.61.201.15', - '168.138.31.136', - '107.152.32.250', - '108.61.206.38', - '204.99.129.8', - '73.185.40.66', - '129.153.166.244', - '137.184.156.222', '5.161.64.49', - '176.96.136.143', - '150.136.231.120', '54.184.104.94', - '47.108.194.84', - '45.79.95.228', - '89.163.154.217', - '139.178.67.226', - '47.242.86.29', + '66.29.151.42', + '70.161.64.119', + '73.185.40.66', + '74.91.115.209', + '81.169.159.30', + '82.223.165.215', + '85.214.33.200', + '91.218.58.116', + '91.218.58.176', + '91.218.58.224', + '91.218.58.24', + '91.218.58.58', '99.92.204.3' ]; diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 1754180d2..0e8bbdcb8 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -316,6 +316,7 @@ class FullNode extends Node { } this.logger.info('Node is loaded.'); + this.emit('open'); } /** @@ -348,12 +349,13 @@ class FullNode extends Node { this.logger.info('Node is closed.'); this.emit('closed'); + this.emit('close'); } /** * Rescan for any missed transactions. * @param {Number|Hash} start - Start block. - * @param {Bloom} filter + * @param {BloomFilter} filter * @param {Function} iter - Iterator. * @returns {Promise} */ @@ -362,6 +364,20 @@ class FullNode extends Node { return this.chain.scan(start, filter, iter); } + /** + * Interactive rescan for any missed transactions. + * @param {Number|Hash} start - Start block. + * @param {BloomFilter} filter + * @param {Function} iter - Iterator. + * @param {Boolean} [fullLock=false] - lock the whole chain instead of per + * scan. + * @returns {Promise} + */ + + scanInteractive(start, filter, iter, fullLock = false) { + return this.chain.scanInteractive(start, filter, iter, fullLock); + } + /** * Broadcast a transaction. * @param {TX|Block|Claim|AirdropProof} item @@ -670,8 +686,7 @@ class FullNode extends Node { async getNameStatus(nameHash) { const height = this.chain.height + 1; - const hardened = await this.mempool.hasHardening(); - return this.chain.db.getNameStatus(nameHash, height, hardened); + return this.chain.db.getNameStatus(nameHash, height); } } diff --git a/lib/node/http.js b/lib/node/http.js index b6f425b1f..c30db1ff1 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -11,7 +11,7 @@ const path = require('path'); const {Server} = require('bweb'); const Validator = require('bval'); const base58 = require('bcrypto/lib/encoding/base58'); -const {BloomFilter} = require('bfilter'); +const {BloomFilter} = require('@handshake-org/bfilter'); const sha256 = require('bcrypto/lib/sha256'); const random = require('bcrypto/lib/random'); const {safeEqual} = require('bcrypto/lib/safe'); @@ -20,6 +20,7 @@ const TX = require('../primitives/tx'); const Claim = require('../primitives/claim'); const Address = require('../primitives/address'); const Network = require('../protocol/network'); +const scanActions = require('../blockchain/common').scanActions; const pkg = require('../pkg'); /** @@ -132,6 +133,39 @@ class HTTP extends Server { pub.brontidePort = brontide.port; } + const treeInterval = this.network.names.treeInterval; + const prevHeight = this.chain.height - 1; + const treeRootHeight = this.chain.height === 0 ? 0 : + prevHeight - (prevHeight % treeInterval) + 1; + + const treeCompaction = { + compacted: false, + compactOnInit: false, + compactInterval: null, + lastCompaction: null, + nextCompaction: null + }; + + if (!this.chain.options.spv) { + const chainOptions = this.chain.options; + const { + compactionHeight, + compactFrom + } = await this.chain.getCompactionHeights(); + + treeCompaction.compactOnInit = chainOptions.compactTreeOnInit; + + if (chainOptions.compactTreeOnInit) { + treeCompaction.compactInterval = chainOptions.compactTreeInitInterval; + treeCompaction.nextCompaction = compactFrom; + } + + if (compactionHeight > 0) { + treeCompaction.compacted = true; + treeCompaction.lastCompaction = compactionHeight; + } + } + res.json(200, { version: pkg.version, network: this.network.type, @@ -139,7 +173,17 @@ class HTTP extends Server { height: this.chain.height, tip: this.chain.tip.hash.toString('hex'), treeRoot: this.chain.tip.treeRoot.toString('hex'), + treeRootHeight: treeRootHeight, progress: this.chain.getProgress(), + indexers: { + indexTX: this.chain.options.indexTX, + indexAddress: this.chain.options.indexAddress + }, + options: { + spv: this.chain.options.spv, + prune: this.chain.options.prune + }, + treeCompaction: treeCompaction, state: { tx: this.chain.db.state.tx, coin: this.chain.db.state.coin, @@ -399,7 +443,7 @@ class HTTP extends Server { const valid = Validator.fromRequest(req); const hash = valid.bhash('hash'); - assert(hash, 'Must pass hash.'); + enforce(hash, 'Must pass hash.'); const invalid = this.mempool.rejects.test(hash, 'hex'); @@ -663,6 +707,22 @@ class HTTP extends Server { return this.scan(socket, start); }); + + socket.hook('rescan interactive', (...args) => { + const valid = new Validator(args); + const start = valid.uintbhash(0); + const rawFilter = valid.buf(1); + const fullLock = valid.bool(2, false); + let filter = socket.filter; + + if (start == null) + throw new Error('Invalid parameter.'); + + if (rawFilter) + filter = BloomFilter.fromRaw(rawFilter); + + return this.scanInteractive(socket, start, filter, fullLock); + }); } /** @@ -770,7 +830,7 @@ class HTTP extends Server { if (!socket.filter) return false; - return tx.test(socket.filter); + return tx.testAndMaybeUpdate(socket.filter); } /** @@ -791,8 +851,84 @@ class HTTP extends Server { return socket.call('block rescan', block, raw); }); + return null; } + + /** + * Scan using a socket's filter (interactive). + * @param {WebSocket} socket + * @param {Hash} start + * @param {BloomFilter} filter + * @param {Boolean} [fullLock=false] + * @returns {Promise} + */ + + async scanInteractive(socket, start, filter, fullLock = false) { + const iter = async (entry, txs) => { + const block = entry.encode(); + const raw = []; + + for (const tx of txs) + raw.push(tx.encode()); + + const action = await socket.call('block rescan interactive', block, raw); + const valid = new Validator(action); + const actionType = valid.i32('type'); + + switch (actionType) { + case scanActions.NEXT: + case scanActions.ABORT: + case scanActions.REPEAT: { + return { + type: actionType + }; + } + case scanActions.REPEAT_SET: { + // NOTE: This is operation is on the heavier side, + // because it sends the whole Filter that can be quite + // big depending on the situation. + // NOTE: In HTTP Context REPEAT_SET wont modify socket.filter + // but instead setup new one for the rescan. Further REPEAT_ADDs will + // modify this filter instead of the socket.filter. + const rawFilter = valid.buf('filter'); + let filter = null; + + if (rawFilter != null) + filter = BloomFilter.fromRaw(rawFilter); + + return { + type: scanActions.REPEAT_SET, + filter: filter + }; + } + case scanActions.REPEAT_ADD: { + // NOTE: This operation depending on the filter + // that was provided can be either modifying the + // socket.filter or the filter provided by REPEAT_SET. + const chunks = valid.array('chunks'); + + if (!chunks) + throw new Error('Invalid parameter.'); + + return { + type: scanActions.REPEAT_ADD, + chunks: chunks + }; + } + + default: + throw new Error('Unknown action.'); + } + }; + + try { + await this.node.scanInteractive(start, filter, iter, fullLock); + } catch (err) { + await socket.call('block rescan interactive abort', err.message); + throw err; + } + } } class HTTPOptions { diff --git a/lib/node/node.js b/lib/node/node.js index dea3d5bad..5cd6672b0 100644 --- a/lib/node/node.js +++ b/lib/node/node.js @@ -14,7 +14,7 @@ const Config = require('bcfg'); const secp256k1 = require('bcrypto/lib/secp256k1'); const Network = require('../protocol/network'); const WorkerPool = require('../workers/workerpool'); -const ownership = require('../covenants/ownership'); +const {ownership} = require('../covenants/ownership'); /** * Node diff --git a/lib/node/rpc.js b/lib/node/rpc.js index d7f1bd115..905e84dae 100644 --- a/lib/node/rpc.js +++ b/lib/node/rpc.js @@ -37,7 +37,7 @@ const pkg = require('../pkg'); const rules = require('../covenants/rules'); const {Resource} = require('../dns/resource'); const NameState = require('../covenants/namestate'); -const ownership = require('../covenants/ownership'); +const {ownership} = require('../covenants/ownership'); const AirdropProof = require('../primitives/airdropproof'); const {EXP} = consensus; const RPCBase = bweb.RPC; @@ -2432,9 +2432,11 @@ class RPC extends RPCBase { const nameHash = rules.hashName(name); const reserved = rules.isReserved(nameHash, height + 1, network); const [start, week] = rules.getRollout(nameHash, network); + const state = await this.chain.getNextState(); const ns = await this.getNameState(nameHash, safe); + let locked = undefined; let info = null; if (ns) { @@ -2442,11 +2444,15 @@ class RPC extends RPCBase { info = ns.getJSON(height, network); } + if (state.hasICANNLockup()) + locked = rules.isLockedUp(nameHash, height + 1, network); + return { start: { reserved: reserved, week: week, - start: start + start: start, + locked: locked }, info }; @@ -2978,17 +2984,21 @@ class RPC extends RPCBase { hex: undefined, type: Script.typesByVal[type], reqSigs: 1, + totalSigs: 1, p2sh: undefined }; if (hex) json.hex = script.toJSON(); - const [m] = script.getMultisig(); + const [m, n] = script.getMultisig(); if (m !== -1) json.reqSigs = m; + if (n !== -1) + json.totalSigs = n; + return json; } diff --git a/lib/node/spvnode.js b/lib/node/spvnode.js index 87548ab44..5145325d8 100644 --- a/lib/node/spvnode.js +++ b/lib/node/spvnode.js @@ -73,7 +73,6 @@ class SPVNode extends Node { createSocket: this.config.func('create-socket'), memory: this.config.bool('memory'), agent: this.config.str('agent'), - selfish: true, listen: false }); @@ -191,6 +190,7 @@ class SPVNode extends Node { await this.handleOpen(); this.logger.info('Node is loaded.'); + this.emit('open'); } /** @@ -219,16 +219,31 @@ class SPVNode extends Node { this.logger.info('Node is closed.'); this.emit('closed'); + this.emit('close'); } /** * Scan for any missed transactions. * Note that this will replay the blockchain sync. * @param {Number|Hash} start - Start block. + * @param {BloomFilter} filter + * @param {Function} iter * @returns {Promise} */ - async scan(start) { + async scan(start, filter, iter) { + throw new Error('Not implemented.'); + } + + /** + * Interactive scan for any missed transactions. + * @param {Number|Hash} start + * @param {BloomFilter} filter + * @param {Function} iter + * @returns {Promise} + */ + + scanInteractive(start, filter, iter) { throw new Error('Not implemented.'); } diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index d1395f7f1..fdba21184 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -14,6 +14,9 @@ const InvItem = require('./invitem'); const consensus = require('../protocol/consensus'); const util = require('../utils/util'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /** * Abstract Block * The class which all block-like objects inherit from. @@ -50,13 +53,14 @@ class AbstractBlock extends bio.Struct { this.mutable = false; + /** @type {Buffer?} */ this._hash = null; + /** @type {Buffer?} */ this._maskHash = null; } /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -97,7 +101,6 @@ class AbstractBlock extends bio.Struct { /** * Inject properties from json object. - * @private * @param {Object} json */ @@ -152,7 +155,7 @@ class AbstractBlock extends bio.Struct { /** * Hash the block header. - * @returns {Hash} hash + * @returns {Hash} */ hash() { @@ -195,8 +198,8 @@ class AbstractBlock extends bio.Struct { /** * Inject properties from serialized data. - * @private * @param {Buffer} data + * @returns {this} */ fromHead(data) { @@ -341,7 +344,7 @@ class AbstractBlock extends bio.Struct { /** * Calculate share hash. - * @returns {Buffer} + * @returns {Hash} */ shareHash() { @@ -414,7 +417,8 @@ class AbstractBlock extends bio.Struct { /** * Serialize the block headers. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ writeHead(bw) { @@ -440,7 +444,7 @@ class AbstractBlock extends bio.Struct { /** * Parse the block headers. - * @param {BufferReader} br + * @param {bio.BufferReader} br */ readHead(br) { diff --git a/lib/primitives/address.js b/lib/primitives/address.js index d4c72bb21..c1b07e8bf 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -14,12 +14,24 @@ const sha3 = require('bcrypto/lib/sha3'); const Network = require('../protocol/network'); const consensus = require('../protocol/consensus'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../script/script')} Script */ +/** @typedef {import('../script/witness')} Witness */ + /* * Constants */ const ZERO_HASH160 = Buffer.alloc(20, 0x00); +/** + * @typedef {Object} AddressOptions + * @property {Hash} hash + * @property {Number} version + */ + /** * Address * Represents an address. @@ -32,7 +44,8 @@ class Address extends bio.Struct { /** * Create an address. * @constructor - * @param {Object?} options + * @param {AddressOptions|String} [options] + * @param {(NetworkType|Network)?} [network] */ constructor(options, network) { @@ -47,8 +60,8 @@ class Address extends bio.Struct { /** * Inject properties from options object. - * @private - * @param {Object} options + * @param {AddressOptions|String} options + * @param {(NetworkType|Network)?} [network] */ fromOptions(options, network) { @@ -136,7 +149,7 @@ class Address extends bio.Struct { /** * Compare against another address. * @param {Address} addr - * @returns {Boolean} + * @returns {Number} */ compare(addr) { @@ -153,7 +166,7 @@ class Address extends bio.Struct { /** * Inject properties from another address. * @param {Address} addr - * @returns {Boolean} + * @returns {this} */ inject(addr) { @@ -164,16 +177,17 @@ class Address extends bio.Struct { /** * Clone address. - * @returns {Address} + * @returns {this} */ clone() { + // @ts-ignore return new this.constructor().inject(this); } /** * Compile the address object to a bech32 address. - * @param {{NetworkType|Network)?} network + * @param {(NetworkType|Network)?} [network] * @returns {String} * @throws Error on bad hash/prefix. */ @@ -216,9 +230,8 @@ class Address extends bio.Struct { /** * Inject properties from bech32 address. - * @private * @param {String} data - * @param {Network?} network + * @param {(NetworkType|Network)?} [network] * @throws Parse error */ @@ -234,8 +247,8 @@ class Address extends bio.Struct { /** * Inject properties from witness. - * @private * @param {Witness} witness + * @returns {Address|null} */ fromWitness(witness) { @@ -260,9 +273,8 @@ class Address extends bio.Struct { /** * Inject properties from a hash. - * @private - * @param {Buffer|Hash} hash - * @param {Number} [version=-1] + * @param {Hash} hash + * @param {Number} [version=0] * @throws on bad hash size */ @@ -289,7 +301,6 @@ class Address extends bio.Struct { /** * Inject properties from witness pubkeyhash. - * @private * @param {Buffer} hash * @returns {Address} */ @@ -301,7 +312,6 @@ class Address extends bio.Struct { /** * Inject properties from witness scripthash. - * @private * @param {Buffer} hash * @returns {Address} */ @@ -313,7 +323,6 @@ class Address extends bio.Struct { /** * Inject properties from witness program. - * @private * @param {Number} version * @param {Buffer} hash * @returns {Address} @@ -404,8 +413,8 @@ class Address extends bio.Struct { /** * Write address to buffer writer. - * @param {BufferWriter} bw - * @returns {BufferWriter} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -417,8 +426,8 @@ class Address extends bio.Struct { /** * Read address from buffer reader. - * @param {BufferReader} br - * @returns {Address} + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { @@ -435,7 +444,7 @@ class Address extends bio.Struct { /** * Inspect the Address. - * @returns {Object} + * @returns {String} */ format() { @@ -469,7 +478,7 @@ class Address extends bio.Struct { * Create an Address from a witness. * Attempt to extract address * properties from a witness. - * @param {Witness} + * @param {Witness} witness * @returns {Address|null} */ @@ -480,7 +489,7 @@ class Address extends bio.Struct { /** * Create a naked address from hash/version. * @param {Hash} hash - * @param {Number} [version=-1] + * @param {Number} [version=0] * @returns {Address} * @throws on bad hash size */ @@ -532,18 +541,16 @@ class Address extends bio.Struct { /** * Get the hash of a base58 address or address-related object. - * @param {String|Address|Hash} data - * @param {Network?} network + * @param {Address|Hash} data * @returns {Hash} */ - static getHash(data, network) { + static getHash(data) { if (!data) throw new Error('Object is not an address.'); - if (Buffer.isBuffer(data)) { + if (Buffer.isBuffer(data)) return data; - } if (data instanceof Address) return data.hash; diff --git a/lib/primitives/airdropkey.js b/lib/primitives/airdropkey.js index 3f4427632..5e121a0bb 100644 --- a/lib/primitives/airdropkey.js +++ b/lib/primitives/airdropkey.js @@ -14,6 +14,10 @@ const ed25519 = require('bcrypto/lib/ed25519'); const {countLeft} = require('bcrypto/lib/encoding/util'); const Goo = require('goosig'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /* * Goo */ @@ -62,6 +66,11 @@ class AirdropKey extends bio.Struct { this.tweak = null; } + /** + * @param {AirdropKey} key + * @returns {this} + */ + inject(key) { assert(key instanceof AirdropKey); @@ -107,6 +116,10 @@ class AirdropKey extends bio.Struct { return countLeft(this.n) < 2048 - 7; } + /** + * @returns {Boolean} + */ + validate() { switch (this.type) { case keyTypes.RSA: { @@ -147,6 +160,12 @@ class AirdropKey extends bio.Struct { } } + /** + * @param {Buffer} msg + * @param {Buffer} sig + * @returns {Boolean} + */ + verify(msg, sig) { assert(Buffer.isBuffer(msg)); assert(Buffer.isBuffer(sig)); @@ -186,6 +205,10 @@ class AirdropKey extends bio.Struct { } } + /** + * @returns {Hash} + */ + hash() { const bw = bio.pool(this.getSize()); this.write(bw); @@ -232,6 +255,11 @@ class AirdropKey extends bio.Struct { return size; } + /** + * @param {BufioWriter} bw + * @returns {BufioWriter} + */ + write(bw) { bw.writeU8(this.type); @@ -265,6 +293,11 @@ class AirdropKey extends bio.Struct { return bw; } + /** + * @param {bio.BufferReader} br + * @returns {this} + */ + read(br) { this.type = br.readU8(); @@ -309,6 +342,13 @@ class AirdropKey extends bio.Struct { return this; } + /** + * @param {String} addr + * @param {AmountValue} value + * @param {Boolean} sponsor + * @returns {this} + */ + fromAddress(addr, value, sponsor = false) { assert(typeof addr === 'string'); assert(Number.isSafeInteger(value) && value >= 0); @@ -360,18 +400,24 @@ class AirdropKey extends bio.Struct { }; } + /** + * @param {Object} json + * @returns {this} + */ + fromJSON(json) { assert(json && typeof json === 'object'); assert(typeof json.type === 'string'); - assert(keyTypes.hasOwnProperty(json.type)); + assert(Object.prototype.hasOwnProperty.call(keyTypes, json.type)); this.type = keyTypes[json.type]; + console.log(base16.decode.toString()); switch (this.type) { case keyTypes.RSA: { this.n = base16.decode(json.n); this.e = base16.decode(json.e); - this.nonce = base16.decode(json.nonce, 32); + this.nonce = base16.decode(json.nonce); break; } @@ -381,14 +427,14 @@ class AirdropKey extends bio.Struct { } case keyTypes.P256: { - this.point = base16.decode(json.point, 33); - this.nonce = base16.decode(json.nonce, 32); + this.point = base16.decode(json.point); + this.nonce = base16.decode(json.nonce); break; } case keyTypes.ED25519: { - this.point = base16.decode(json.point, 32); - this.nonce = base16.decode(json.nonce, 32); + this.point = base16.decode(json.point); + this.nonce = base16.decode(json.nonce); break; } @@ -411,6 +457,13 @@ class AirdropKey extends bio.Struct { return this; } + /** + * @param {String} addr + * @param {AmountValue} value + * @param {Boolean} sponsor + * @returns {AirdropKey} + */ + static fromAddress(addr, value, sponsor) { return new this().fromAddress(addr, value, sponsor); } diff --git a/lib/primitives/airdropproof.js b/lib/primitives/airdropproof.js index eed3d00ff..00dabf097 100644 --- a/lib/primitives/airdropproof.js +++ b/lib/primitives/airdropproof.js @@ -11,6 +11,9 @@ const InvItem = require('./invitem'); const consensus = require('../protocol/consensus'); const {keyTypes} = AirdropKey; +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /* * Constants */ @@ -45,6 +48,8 @@ const TREE_LEAVES = AIRDROP_LEAVES + FAUCET_LEAVES; const MAX_PROOF_SIZE = 3400; // 3253 +/** @typedef {ReturnType} AirdropProofJSON */ + /** * AirdropProof */ @@ -53,8 +58,10 @@ class AirdropProof extends bio.Struct { constructor() { super(); this.index = 0; + /** @type {Hash[]} */ this.proof = []; this.subindex = 0; + /** @type {Hash[]} */ this.subproof = []; this.key = EMPTY; this.version = 0; @@ -87,6 +94,12 @@ class AirdropProof extends bio.Struct { return size; } + /** + * @param {BufioWriter} bw + * @param {Boolean} [sighash=false] + * @returns {BufioWriter} + */ + write(bw, sighash = false) { if (sighash) bw.writeBytes(CONTEXT); @@ -115,6 +128,11 @@ class AirdropProof extends bio.Struct { return bw; } + /** + * @param {Buffer} data + * @returns {this} + */ + decode(data) { const br = bio.read(data); @@ -129,6 +147,11 @@ class AirdropProof extends bio.Struct { return this; } + /** + * @param {bio.BufferReader} br + * @returns {this} + */ + read(br) { this.index = br.readU32(); assert(this.index < AIRDROP_LEAVES); @@ -169,12 +192,21 @@ class AirdropProof extends bio.Struct { return this; } + /** + * @returns {Buffer} + */ + hash() { const bw = bio.pool(this.getSize()); this.write(bw); return blake2b.digest(bw.render()); } + /** + * @param {Hash} [expect] + * @returns {Boolean} + */ + verifyMerkle(expect) { if (expect == null) { expect = this.isAddress() @@ -201,6 +233,10 @@ class AirdropProof extends bio.Struct { return root.equals(expect); } + /** + * @returns {Buffer} + */ + signatureData() { const size = this.getSize(true); const bw = bio.pool(size); @@ -210,10 +246,18 @@ class AirdropProof extends bio.Struct { return bw.render(); } + /** + * @returns {Buffer} + */ + signatureHash() { return sha256.digest(this.signatureData()); } + /** + * @returns {AirdropKey|null} + */ + getKey() { try { return AirdropKey.decode(this.key); @@ -222,6 +266,10 @@ class AirdropProof extends bio.Struct { } } + /** + * @returns {Boolean} + */ + verifySignature() { const key = this.getKey(); @@ -244,6 +292,10 @@ class AirdropProof extends bio.Struct { return key.verify(msg, this.signature); } + /** + * @returns {Number} + */ + position() { let index = this.index; @@ -378,6 +430,11 @@ class AirdropProof extends bio.Struct { return true; } + /** + * @param {Hash} [expect] + * @returns {Boolean} + */ + verify(expect) { if (!this.isSane()) return false; @@ -407,6 +464,11 @@ class AirdropProof extends bio.Struct { }; } + /** + * @param {AirdropProofJSON} json + * @returns {this} + */ + fromJSON(json) { assert(json && typeof json === 'object'); assert((json.index >>> 0) === json.index); @@ -422,12 +484,12 @@ class AirdropProof extends bio.Struct { this.index = json.index; for (const hash of json.proof) - this.proof.push(base16.decode(hash, 32)); + this.proof.push(base16.decode(hash)); this.subindex = json.subindex; for (const hash of json.subproof) - this.subproof.push(base16.decode(hash, 32)); + this.subproof.push(base16.decode(hash)); if (json.key) this.key = AirdropKey.fromJSON(json.key).encode(); diff --git a/lib/primitives/block.js b/lib/primitives/block.js index 33af82c16..ab6102c1a 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -20,6 +20,13 @@ const Network = require('../protocol/network'); const util = require('../utils/util'); const {encoding} = bio; +/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').RawBlock} RawBlock */ +/** @typedef {import('../coins/coinview')} CoinView */ + /** * Block * Represents a full block. @@ -31,15 +38,18 @@ class Block extends AbstractBlock { /** * Create a block. * @constructor - * @param {Object} options + * @param {Object} [options] */ constructor(options) { super(); + /** @type {TX[]} */ this.txs = []; + /** @type {Buffer?} */ this._raw = null; + /** @type {Sizes?} */ this._sizes = null; if (options) @@ -48,7 +58,6 @@ class Block extends AbstractBlock { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -68,7 +77,8 @@ class Block extends AbstractBlock { /** * Clear any cached values. - * @param {Boolean?} all - Clear transactions. + * @param {Boolean?} [all] - Clear transactions. + * @returns {this} */ refresh(all) { @@ -286,7 +296,7 @@ class Block extends AbstractBlock { /** * Get the "claimed" reward by the coinbase. - * @returns {Amount} claimed + * @returns {AmountValue} claimed */ getClaimed() { @@ -317,8 +327,8 @@ class Block extends AbstractBlock { /** * Inspect the block and return a more * user-friendly representation of the data. - * @param {CoinView} view - * @param {Number} height + * @param {CoinView} [view] + * @param {Number} [height] * @returns {Object} */ @@ -349,10 +359,10 @@ class Block extends AbstractBlock { /** * Convert the block to an object suitable * for JSON serialization. - * @param {Network} network - * @param {CoinView} view - * @param {Number} height - * @param {Number} depth + * @param {Network} [network] + * @param {CoinView} [view] + * @param {Number} [height] + * @param {Number} [depth] * @returns {Object} */ @@ -381,7 +391,6 @@ class Block extends AbstractBlock { /** * Inject properties from json object. - * @private * @param {Object} json */ @@ -399,8 +408,7 @@ class Block extends AbstractBlock { /** * Inject properties from serialized data. - * @private - * @param {Buffer} data + * @param {bio.BufferReader} br */ read(br) { @@ -430,7 +438,7 @@ class Block extends AbstractBlock { /** * Convert the Block to a MerkleBlock. - * @param {Bloom} filter - Bloom filter for transactions + * @param {BloomFilter} filter - Bloom filter for transactions * to match. The merkle block will contain only the * matched transactions. * @returns {MerkleBlock} @@ -440,6 +448,11 @@ class Block extends AbstractBlock { return MerkleBlock.fromBlock(this, filter); } + /** + * @param {BufioWriter} bw + * @returns {BufioWriter} + */ + write(bw) { if (this._raw) { bw.writeBytes(this._raw); @@ -456,6 +469,10 @@ class Block extends AbstractBlock { return bw; } + /** + * @returns {Buffer} + */ + encode() { if (this.mutable) return super.encode(); @@ -477,7 +494,7 @@ class Block extends AbstractBlock { /** * Get real block size with witness. - * @returns {RawBlock} + * @returns {Sizes} */ getSizes() { diff --git a/lib/primitives/claim.js b/lib/primitives/claim.js index 008efe6c7..6b4d4ed4a 100644 --- a/lib/primitives/claim.js +++ b/lib/primitives/claim.js @@ -12,13 +12,18 @@ const blake2b = require('bcrypto/lib/blake2b'); const consensus = require('../protocol/consensus'); const policy = require('../protocol/policy'); const rules = require('../covenants/rules'); -const ownership = require('../covenants/ownership'); +const Ownership = require('../covenants/ownership'); const InvItem = require('./invitem'); const TX = require('./tx'); const Input = require('./input'); const Output = require('./output'); -const {types} = rules; -const {OwnershipProof} = ownership; +const {OwnershipProof} = Ownership; + +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../protocol/network')} Network */ /* * Constants @@ -28,7 +33,7 @@ const EMPTY = Buffer.alloc(0); /** * Claim - * @extends {bufio.Struct} + * @extends {bio.Struct} */ class Claim extends bio.Struct { @@ -37,16 +42,25 @@ class Claim extends bio.Struct { this.blob = EMPTY; + /** @type {Hash?} */ this._hash = null; this._data = null; } + /** + * @returns {this} + */ + refresh() { this._hash = null; this._data = null; return this; } + /** + * @returns {Hash} + */ + hash() { if (!this._hash) this._hash = blake2b.digest(this.blob); @@ -54,10 +68,19 @@ class Claim extends bio.Struct { return this._hash; } + /** + * @returns {String} + */ + hashHex() { return this.hash().toString('hex'); } + /** + * @param {Network} network + * @returns {Object} + */ + getData(network) { if (!this._data) { const proof = this.getProof(); @@ -76,16 +99,30 @@ class Claim extends bio.Struct { return this._data; } + /** + * @returns {Number} + */ + getSize() { return 2 + this.blob.length; } + /** + * @param {BufioWriter} bw + * @returns {BufioWriter} + */ + write(bw) { bw.writeU16(this.blob.length); bw.writeBytes(this.blob); return bw; } + /** + * @param {Buffer} data + * @returns {this} + */ + decode(data) { const br = bio.read(data); @@ -100,6 +137,11 @@ class Claim extends bio.Struct { return this; } + /** + * @param {bio.BufferReader} br + * @returns {this} + */ + read(br) { const size = br.readU16(); @@ -111,19 +153,37 @@ class Claim extends bio.Struct { return this; } + /** + * @returns {InvItem} + */ + toInv() { return new InvItem(InvItem.types.CLAIM, this.hash()); } + /** + * @returns {Number} + */ + getWeight() { return this.getSize(); } + /** + * @returns {Number} + */ + getVirtualSize() { const scale = consensus.WITNESS_SCALE_FACTOR; return (this.getWeight() + scale - 1) / scale | 0; } + /** + * @param {Number} [size] + * @param {Number} [rate] + * @returns {AmountValue} + */ + getMinFee(size, rate) { if (size == null) size = this.getVirtualSize(); @@ -131,12 +191,23 @@ class Claim extends bio.Struct { return policy.getMinFee(size, rate); } + /** + * @param {Network} [network] + * @returns {AmountValue} + */ + getFee(network) { const data = this.getData(network); assert(data); return data.fee; } + /** + * @param {Number} [size] + * @param {Network} [network] + * @returns {Rate} + */ + getRate(size, network) { const fee = this.getFee(network); @@ -146,6 +217,12 @@ class Claim extends bio.Struct { return policy.getRate(size, fee); } + /** + * @param {Network} network + * @param {Number} height + * @returns {TX} + */ + toTX(network, height) { const data = this.getData(network); assert(data); @@ -170,13 +247,14 @@ class Claim extends bio.Struct { if (data.weak) flags |= 1; - output.covenant.type = types.CLAIM; - output.covenant.pushHash(rules.hashName(data.name)); - output.covenant.pushU32(height); - output.covenant.pushString(data.name); - output.covenant.pushU8(flags); - output.covenant.pushHash(data.commitHash); - output.covenant.pushU32(data.commitHeight); + output.covenant.setClaim( + rules.hashName(data.name), + height, + Buffer.from(data.name, 'binary'), + flags, + data.commitHash, + data.commitHeight + ); tx.inputs.push(input); tx.outputs.push(output); @@ -186,6 +264,10 @@ class Claim extends bio.Struct { return tx; } + /** + * @returns {OwnershipProof} + */ + getProof() { try { return this.toProof(); @@ -194,35 +276,70 @@ class Claim extends bio.Struct { } } + /** + * @returns {OwnershipProof} + */ + toProof() { return OwnershipProof.decode(this.blob); } + /** + * @returns {Buffer} + */ + toBlob() { return this.blob; } + /** + * @returns {Object} + */ + getJSON() { const proof = this.getProof(); return proof.toJSON(); } + /** + * Inject properties from blob. + * @param {Buffer} blob + * @returns {this} + */ + fromBlob(blob) { assert(Buffer.isBuffer(blob)); this.blob = blob; return this; } + /** + * @param {OwnershipProof} proof + * @returns {this} + */ + fromProof(proof) { assert(proof instanceof OwnershipProof); this.blob = proof.encode(); return this; } + /** + * Instantiate claim from raw proof. + * @param {Buffer} blob + * @returns {Claim} + */ + static fromBlob(blob) { return new this().fromBlob(blob); } + /** + * Instantiate claim from proof. + * @param {OwnershipProof} proof + * @returns {Claim} + */ + static fromProof(proof) { return new this().fromProof(proof); } diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js index 3816cc90a..cb83b7f36 100644 --- a/lib/primitives/coin.js +++ b/lib/primitives/coin.js @@ -14,6 +14,12 @@ const consensus = require('../protocol/consensus'); const Outpoint = require('./outpoint'); const util = require('../utils/util'); +/** @typedef {import('bufio').BufferReader} BufferReader */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../types').HexHash} HexHash */ +/** @typedef {import('./tx')} TX */ + /** * Coin * Represents an unspent output. @@ -32,7 +38,7 @@ class Coin extends Output { /** * Create a coin. * @constructor - * @param {Object} options + * @param {Object?} [options] */ constructor(options) { @@ -50,8 +56,7 @@ class Coin extends Output { /** * Inject options into coin. - * @private - * @param {Object} options + * @param {Object} [options] */ fromOptions(options) { @@ -107,17 +112,17 @@ class Coin extends Output { /** * Clone the coin. - * @private - * @returns {Coin} + * @returns {this} */ clone() { assert(false, 'Coins are not cloneable.'); + return this; } /** * Calculate number of confirmations since coin was created. - * @param {Number?} height - Current chain height. Network + * @param {Number} height - Current chain height. Network * height is used if not passed in. * @return {Number} */ @@ -140,7 +145,7 @@ class Coin extends Output { /** * Serialize coin to a key * suitable for a hash table. - * @returns {String} + * @returns {Buffer} */ toKey() { @@ -149,8 +154,7 @@ class Coin extends Output { /** * Inject properties from hash table key. - * @private - * @param {String} key + * @param {Buffer} key * @returns {Coin} */ @@ -163,7 +167,7 @@ class Coin extends Output { /** * Instantiate coin from hash table key. - * @param {String} key + * @param {Buffer} key * @returns {Coin} */ @@ -173,7 +177,7 @@ class Coin extends Output { /** * Get little-endian hash. - * @returns {Hash} + * @returns {HexHash?} */ txid() { @@ -203,8 +207,8 @@ class Coin extends Output { /** * Convert the coin to an object suitable * for JSON serialization. - * @param {Network} network - * @param {Boolean} minimal + * @param {Network} [network] + * @param {Boolean} [minimal] * @returns {Object} */ @@ -225,8 +229,8 @@ class Coin extends Output { /** * Inject JSON properties into coin. - * @private * @param {Object} json + * @param {(NetworkType|Network)?} [network] */ fromJSON(json, network) { @@ -266,7 +270,8 @@ class Coin extends Output { /** * Write the coin to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -287,7 +292,6 @@ class Coin extends Output { /** * Inject properties from serialized buffer writer. - * @private * @param {BufferReader} br */ @@ -309,6 +313,7 @@ class Coin extends Output { * Inject properties from TX. * @param {TX} tx * @param {Number} index + * @param {Number} height */ fromTX(tx, index, height) { @@ -330,6 +335,7 @@ class Coin extends Output { * Instantiate a coin from a TX * @param {TX} tx * @param {Number} index - Output index. + * @param {Number} height - Chain height. * @returns {Coin} */ diff --git a/lib/primitives/covenant.js b/lib/primitives/covenant.js index 56250d5ea..db983e980 100644 --- a/lib/primitives/covenant.js +++ b/lib/primitives/covenant.js @@ -14,6 +14,13 @@ const consensus = require('../protocol/consensus'); const {encoding} = bio; const {types, typesByVal} = rules; +/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('./address')} Address */ + +/** @typedef {ReturnType} CovenantJSON */ + /** * Covenant * @alias module:primitives.Covenant @@ -26,6 +33,8 @@ class Covenant extends bio.Struct { /** * Create a covenant. * @constructor + * @param {rules.types|Object} [type] + * @param {Buffer[]} [items] */ constructor(type, items) { @@ -40,8 +49,9 @@ class Covenant extends bio.Struct { /** * Inject properties from options object. - * @private - * @param {Object} options + * @param {rules.types|Object} [type] + * @param {Buffer[]} [items] + * @returns {this} */ fromOptions(type, items) { @@ -84,7 +94,7 @@ class Covenant extends bio.Struct { * Set an item. * @param {Number} index * @param {Buffer} item - * @returns {Buffer} + * @returns {Covenant} */ set(index, item) { @@ -102,6 +112,7 @@ class Covenant extends bio.Struct { /** * Push an item. * @param {Buffer} item + * @returns {this} */ push(item) { @@ -125,6 +136,7 @@ class Covenant extends bio.Struct { /** * Push a uint8. * @param {Number} num + * @returns {Covenant} */ pushU8(num) { @@ -150,6 +162,7 @@ class Covenant extends bio.Struct { /** * Push a uint32. * @param {Number} num + * @returns {Covenant} */ pushU32(num) { @@ -163,7 +176,7 @@ class Covenant extends bio.Struct { /** * Get a hash. * @param {Number} index - * @returns {Buffer} + * @returns {Hash} */ getHash(index) { @@ -174,7 +187,8 @@ class Covenant extends bio.Struct { /** * Push a hash. - * @param {Buffer} hash + * @param {Hash} hash + * @returns {Covenant} */ pushHash(hash) { @@ -199,6 +213,7 @@ class Covenant extends bio.Struct { /** * Push a string. * @param {String} str + * @returns {Covenant} */ pushString(str) { @@ -334,6 +349,232 @@ class Covenant extends bio.Struct { return this.type === types.REVOKE; } + /** + * Build helpers + */ + + /** + * Set covenant to NONE. + * @returns {Covenant} + */ + + setNone() { + this.type = types.NONE; + this.items = []; + return this; + } + + /** + * Set covenant to OPEN. + * @param {Hash} nameHash + * @param {Buffer} rawName + * @returns {Covenant} + */ + + setOpen(nameHash, rawName) { + this.type = types.OPEN; + this.items = []; + this.pushHash(nameHash); + this.pushU32(0); + this.push(rawName); + return this; + } + + /** + * Set covenant to BID. + * @param {Hash} nameHash + * @param {Number} start + * @param {Buffer} rawName + * @param {Hash} blind + * @returns {Covenant} + */ + + setBid(nameHash, start, rawName, blind) { + this.type = types.BID; + this.items = []; + this.pushHash(nameHash); + this.pushU32(start); + this.push(rawName); + this.pushHash(blind); + + return this; + } + + /** + * Set covenant to REVEAL. + * @param {Hash} nameHash + * @param {Number} height + * @param {Hash} nonce + * @returns {Covenant} + */ + + setReveal(nameHash, height, nonce) { + this.type = types.REVEAL; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + this.pushHash(nonce); + + return this; + } + + /** + * Set covenant to REDEEM. + * @param {Hash} nameHash + * @param {Number} height + * @returns {Covenant} + */ + + setRedeem(nameHash, height) { + this.type = types.REDEEM; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + + return this; + } + + /** + * Set covenant to REGISTER. + * @param {Hash} nameHash + * @param {Number} height + * @param {Buffer} record + * @param {Hash} blockHash + * @returns {Covenant} + */ + + setRegister(nameHash, height, record, blockHash) { + this.type = types.REGISTER; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + this.push(record); + this.pushHash(blockHash); + + return this; + } + + /** + * Set covenant to UPDATE. + * @param {Hash} nameHash + * @param {Number} height + * @param {Buffer} resource + * @returns {Covenant} + */ + + setUpdate(nameHash, height, resource) { + this.type = types.UPDATE; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + this.push(resource); + + return this; + } + + /** + * Set covenant to RENEW. + * @param {Hash} nameHash + * @param {Number} height + * @param {Hash} blockHash + * @returns {Covenant} + */ + + setRenew(nameHash, height, blockHash) { + this.type = types.RENEW; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + this.pushHash(blockHash); + + return this; + } + + /** + * Set covenant to TRANSFER. + * @param {Hash} nameHash + * @param {Number} height + * @param {Address} address + * @returns {Covenant} + */ + + setTransfer(nameHash, height, address) { + this.type = types.TRANSFER; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + this.pushU8(address.version); + this.push(address.hash); + + return this; + } + + /** + * Set covenant to REVOKE. + * @param {Hash} nameHash + * @param {Number} height + * @param {Buffer} rawName + * @param {Number} flags + * @param {Number} claimed + * @param {Number} renewals + * @param {Hash} blockHash + * @returns {Covenant} + */ + + setFinalize(nameHash, height, rawName, flags, claimed, renewals, blockHash) { + this.type = types.FINALIZE; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + this.push(rawName); + this.pushU8(flags); + this.pushU32(claimed); + this.pushU32(renewals); + this.pushHash(blockHash); + + return this; + } + + /** + * Set covenant to REVOKE. + * @param {Hash} nameHash + * @param {Number} height + * @returns {Covenant} + */ + + setRevoke(nameHash, height) { + this.type = types.REVOKE; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + + return this; + } + + /** + * Set covenant to CLAIM. + * @param {Hash} nameHash + * @param {Number} height + * @param {Buffer} rawName + * @param {Number} flags + * @param {Hash} commitHash + * @param {Number} commitHeight + * @returns {Covenant} + */ + + setClaim(nameHash, height, rawName, flags, commitHash, commitHeight) { + this.type = types.CLAIM; + this.items = []; + this.pushHash(nameHash); + this.pushU32(height); + this.push(rawName); + this.pushU8(flags); + this.pushHash(commitHash); + this.pushU32(commitHeight); + + return this; + } + /** * Test whether the covenant is name-related. * @returns {Boolean} @@ -433,9 +674,8 @@ class Covenant extends bio.Struct { /** * Inject properties from covenant. * Used for cloning. - * @private - * @param {Covenant} covenant - * @returns {Covenant} + * @param {this} covenant + * @returns {this} */ inject(covenant) { @@ -447,7 +687,7 @@ class Covenant extends bio.Struct { /** * Test the covenant against a bloom filter. - * @param {Bloom} filter + * @param {BloomFilter} filter * @returns {Boolean} */ @@ -505,7 +745,8 @@ class Covenant extends bio.Struct { /** * Write covenant to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -531,7 +772,6 @@ class Covenant extends bio.Struct { /** * Convert covenant to a hex string. - * @returns {String} */ getJSON() { @@ -549,8 +789,8 @@ class Covenant extends bio.Struct { /** * Inject properties from json object. - * @private - * @param {String} json + * @param {CovenantJSON} json + * @returns {this} */ fromJSON(json) { @@ -570,8 +810,8 @@ class Covenant extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { @@ -590,8 +830,8 @@ class Covenant extends bio.Struct { /** * Inject items from string. - * @private * @param {String|String[]} items + * @returns {this} */ fromString(items) { @@ -618,7 +858,7 @@ class Covenant extends bio.Struct { */ format() { - return ``; + return ``; } /** diff --git a/lib/primitives/headers.js b/lib/primitives/headers.js index 1a6c94952..1733eef7d 100644 --- a/lib/primitives/headers.js +++ b/lib/primitives/headers.js @@ -9,6 +9,15 @@ const util = require('../utils/util'); const AbstractBlock = require('./abstractblock'); +/** @typedef {import('bufio').BufferReader} BufferReader */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../protocol/network')} Network */ +/** @typedef {import('../blockchain/chainentry')} ChainEntry */ +/** @typedef {import('../coins/coinview')} CoinView */ +/** @typedef {import('./block')} Block */ +/** @typedef {import('./merkleblock')} MerkleBlock */ + /** * Headers * Represents block headers obtained @@ -21,7 +30,7 @@ class Headers extends AbstractBlock { /** * Create headers. * @constructor - * @param {Object} options + * @param {Object} [options] */ constructor(options) { @@ -52,7 +61,8 @@ class Headers extends AbstractBlock { /** * Serialize the headers to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -62,8 +72,7 @@ class Headers extends AbstractBlock { /** * Inject properties from buffer reader. - * @private - * @param {Buffer} data + * @param {BufferReader} br */ read(br) { @@ -128,9 +137,9 @@ class Headers extends AbstractBlock { /** * Convert the block to an object suitable * for JSON serialization. - * @param {Network} network - * @param {CoinView} view - * @param {Number} height + * @param {(NetworkType|Network)?} [network] + * @param {CoinView} [view] + * @param {Number} [height] * @returns {Object} */ @@ -154,7 +163,6 @@ class Headers extends AbstractBlock { /** * Inject properties from json object. - * @private * @param {Object} json */ @@ -166,8 +174,8 @@ class Headers extends AbstractBlock { /** * Inspect the headers and return a more * user-friendly representation of the data. - * @param {CoinView} view - * @param {Number} height + * @param {CoinView} [view] + * @param {Number} [height] * @returns {Object} */ diff --git a/lib/primitives/input.js b/lib/primitives/input.js index e920cfe1d..6fce81a39 100644 --- a/lib/primitives/input.js +++ b/lib/primitives/input.js @@ -12,6 +12,16 @@ const Network = require('../protocol/network'); const Witness = require('../script/witness'); const Outpoint = require('./outpoint'); +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('./tx')} TX */ +/** @typedef {import('./coin')} Coin */ +/** @typedef {import('./address')} Address */ +/** @typedef {import('../wallet/path')} Path */ + +/** @typedef {ReturnType} InputJSON */ + /** * Input * Represents a transaction input. @@ -26,7 +36,7 @@ class Input extends bio.Struct { /** * Create transaction input. * @constructor - * @param {Object} options + * @param {Object?} [options] */ constructor(options) { @@ -42,7 +52,6 @@ class Input extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -65,7 +74,8 @@ class Input extends bio.Struct { /** * Clone the input. - * @returns {Input} + * @param {this} input + * @returns {this} */ inject(input) { @@ -101,7 +111,7 @@ class Input extends bio.Struct { * Get the previous output script's address. Will "guess" * based on the input script and/or witness if coin * is not available. - * @param {Coin?} coin + * @param {Coin?} [coin] * @returns {Address?} addr */ @@ -117,8 +127,8 @@ class Input extends bio.Struct { /** * Get the address hash. - * @param {Coin?} coin - * @returns {Hash} hash + * @param {Coin?} [coin] + * @returns {Hash?} hash */ getHash(coin) { @@ -167,10 +177,9 @@ class Input extends bio.Struct { /** * Convert the input to an object suitable * for JSON serialization. - * @param {Network} network - * @param {Coin} coin - * @param {Path} path - * @returns {Object} + * @param {NetworkType|Network} [network] + * @param {Coin} [coin] + * @param {Path} [path] */ getJSON(network, coin, path) { @@ -195,8 +204,8 @@ class Input extends bio.Struct { /** * Inject properties from a JSON object. - * @private - * @param {Object} json + * @param {InputJSON} json + * @returns {this} */ fromJSON(json) { @@ -220,7 +229,8 @@ class Input extends bio.Struct { /** * Write the input to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -231,8 +241,8 @@ class Input extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { @@ -243,8 +253,8 @@ class Input extends bio.Struct { /** * Inject properties from outpoint. - * @private * @param {Outpoint} outpoint + * @returns {this} */ fromOutpoint(outpoint) { @@ -257,7 +267,7 @@ class Input extends bio.Struct { /** * Instantiate input from outpoint. - * @param {Outpoint} + * @param {Outpoint} outpoint * @returns {Input} */ @@ -281,7 +291,7 @@ class Input extends bio.Struct { /** * Instantiate input from coin. - * @param {Coin} + * @param {Coin} coin * @returns {Input} */ @@ -291,7 +301,6 @@ class Input extends bio.Struct { /** * Inject properties from transaction. - * @private * @param {TX} tx * @param {Number} index */ diff --git a/lib/primitives/invitem.js b/lib/primitives/invitem.js index 13b19d5b6..e4aeac3ba 100644 --- a/lib/primitives/invitem.js +++ b/lib/primitives/invitem.js @@ -8,6 +8,9 @@ const bio = require('bufio'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /** * Inv Item * @alias module:primitives.InvItem @@ -20,7 +23,7 @@ class InvItem extends bio.Struct { /** * Create an inv item. * @constructor - * @param {Number} type + * @param {InvItem.types} type * @param {Hash} hash */ @@ -32,7 +35,7 @@ class InvItem extends bio.Struct { /** * Write inv item to buffer writer. - * @param {BufferWriter} bw + * @returns {Number} */ getSize() { @@ -41,19 +44,20 @@ class InvItem extends bio.Struct { /** * Write inv item to buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { bw.writeU32(this.type); bw.writeHash(this.hash); - return this; + return bw; } /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { diff --git a/lib/primitives/keyring.js b/lib/primitives/keyring.js index 8b9909a21..902015b8d 100644 --- a/lib/primitives/keyring.js +++ b/lib/primitives/keyring.js @@ -17,6 +17,12 @@ const Address = require('./address'); const Output = require('./output'); const secp256k1 = require('bcrypto/lib/secp256k1'); +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../types').Base58String} Base58String */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('./tx')} TX */ + /* * Constants */ @@ -33,19 +39,25 @@ class KeyRing extends bio.Struct { /** * Create a key ring. * @constructor - * @param {Object} options + * @param {Object?} [options] */ constructor(options) { super(); this.publicKey = ZERO_KEY; + /** @type {Buffer?} */ this.privateKey = null; + /** @type {Script?} */ this.script = null; + /** @type {Hash?} */ this._keyHash = null; + /** @type {Address?} */ this._keyAddress = null; + /** @type {Hash?} */ this._scriptHash = null; + /** @type {Address?} */ this._scriptAddress = null; if (options) @@ -54,7 +66,6 @@ class KeyRing extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -94,7 +105,6 @@ class KeyRing extends bio.Struct { /** * Inject data from private key. - * @private * @param {Buffer} key */ @@ -120,7 +130,6 @@ class KeyRing extends bio.Struct { /** * Inject data from public key. - * @private * @param {Buffer} key */ @@ -134,7 +143,6 @@ class KeyRing extends bio.Struct { /** * Generate a keyring. - * @private * @returns {KeyRing} */ @@ -158,28 +166,27 @@ class KeyRing extends bio.Struct { * @returns {KeyRing} */ - static fromPublic(key) { - return new this().fromPublic(key); + static fromPublic(publicKey) { + return new this().fromPublic(publicKey); } /** * Inject data from public key. - * @private - * @param {Buffer} privateKey + * @param {Buffer} key */ fromKey(key) { assert(Buffer.isBuffer(key), 'Key must be a buffer.'); if (key.length === 32) - return this.fromPrivate(key, true); + return this.fromPrivate(key); return this.fromPublic(key); } /** * Instantiate keyring from a public key. - * @param {Buffer} publicKey + * @param {Buffer} key * @returns {KeyRing} */ @@ -255,9 +262,8 @@ class KeyRing extends bio.Struct { /** * Inject properties from serialized secret. - * @private - * @param {Base58String} secret - * @param {(Network|NetworkType)?} network + * @param {Base58String} data + * @param {(Network|NetworkType)?} [network] */ fromSecret(data, network) { @@ -277,7 +283,7 @@ class KeyRing extends bio.Struct { /** * Instantiate a keyring from a serialized secret. - * @param {Base58String} secret + * @param {Base58String} data * @param {(Network|NetworkType)?} network * @returns {KeyRing} */ @@ -424,7 +430,7 @@ class KeyRing extends bio.Struct { /** * Check whether transaction output belongs to this address. * @param {TX|Output} tx - Transaction or Output. - * @param {Number?} index - Output index. + * @param {Number?} [index] - Output index. * @returns {Boolean} */ @@ -499,6 +505,7 @@ class KeyRing extends bio.Struct { /** * Convert an KeyRing to a more json-friendly object. + * @param {(NetworkType|Network)?} [network] * @returns {Object} */ @@ -512,7 +519,6 @@ class KeyRing extends bio.Struct { /** * Inject properties from json object. - * @private * @param {Object} json */ @@ -524,7 +530,7 @@ class KeyRing extends bio.Struct { this.publicKey = Buffer.from(json.publicKey, 'hex'); if (json.script) - this.script = Buffer.from(json.script, 'hex'); + this.script = Script.fromHex(json.script); return this; } @@ -552,7 +558,8 @@ class KeyRing extends bio.Struct { /** * Write the keyring to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -574,8 +581,7 @@ class KeyRing extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { diff --git a/lib/primitives/memblock.js b/lib/primitives/memblock.js index c27805c37..24d519fcd 100644 --- a/lib/primitives/memblock.js +++ b/lib/primitives/memblock.js @@ -16,6 +16,8 @@ const Output = require('./output'); const consensus = require('../protocol/consensus'); const DUMMY = Buffer.alloc(0); +/** @typedef {import('../types').BufioWriter} BufioWriter */ + /** * Mem Block * A block object which is essentially a "placeholder" @@ -177,8 +179,7 @@ class MemBlock extends AbstractBlock { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -193,7 +194,6 @@ class MemBlock extends AbstractBlock { /** * Inject properties from serialized data. - * @private * @param {Buffer} data */ @@ -204,7 +204,8 @@ class MemBlock extends AbstractBlock { /** * Return serialized block data. - * @returns {Buffer} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index fbf004e19..e1ff939a1 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -18,6 +18,14 @@ const Headers = require('./headers'); const DUMMY = Buffer.from([0]); const {encoding} = bio; +/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../coins/coinview')} CoinView */ +/** @typedef {import('../protocol/network')} Network */ +/** @typedef {import('./block')} Block */ +/** @typedef {import('./tx')} TX */ + /** * Merkle Block * Represents a merkle (filtered) block. @@ -35,7 +43,9 @@ class MerkleBlock extends AbstractBlock { constructor(options) { super(); + /** @type {TX[]} */ this.txs = []; + /** @type {Hash[]} */ this.hashes = []; this.flags = DUMMY; @@ -48,7 +58,6 @@ class MerkleBlock extends AbstractBlock { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -82,7 +91,7 @@ class MerkleBlock extends AbstractBlock { /** * Clear any cached values. - * @param {Boolean?} all - Clear transactions. + * @param {Boolean?} [all] - Clear transactions. */ refresh(all) { @@ -126,7 +135,6 @@ class MerkleBlock extends AbstractBlock { /** * Verify the partial merkletree. - * @private * @returns {Boolean} */ @@ -275,8 +283,8 @@ class MerkleBlock extends AbstractBlock { /** * Inspect the block and return a more * user-friendly representation of the data. - * @param {CoinView} view - * @param {Number} height + * @param {CoinView} [view] + * @param {Number} [height] * @returns {Object} */ @@ -324,7 +332,8 @@ class MerkleBlock extends AbstractBlock { /** * Write the merkleblock to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -344,8 +353,7 @@ class MerkleBlock extends AbstractBlock { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -366,9 +374,9 @@ class MerkleBlock extends AbstractBlock { /** * Convert the block to an object suitable * for JSON serialization. - * @param {Network} network - * @param {CoinView} view - * @param {Number} height + * @param {Network} [network] + * @param {CoinView} [view] + * @param {Number} [height] * @returns {Object} */ @@ -397,7 +405,6 @@ class MerkleBlock extends AbstractBlock { /** * Inject properties from json object. - * @private * @param {Object} json */ @@ -424,7 +431,7 @@ class MerkleBlock extends AbstractBlock { * it through a filter first. This will build the partial * merkle tree. * @param {Block} block - * @param {Bloom} filter + * @param {BloomFilter} filter * @returns {MerkleBlock} */ @@ -432,7 +439,7 @@ class MerkleBlock extends AbstractBlock { const matches = []; for (const tx of block.txs) - matches.push(tx.test(filter) ? 1 : 0); + matches.push(tx.testAndMaybeUpdate(filter) ? 1 : 0); return this.fromMatches(block, matches); } diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index ced667492..cfea27a89 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -27,6 +27,14 @@ const rules = require('../covenants/rules'); const util = require('../utils/util'); const {types} = rules; +/** @typedef {import('../types').SighashType} SighashType */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').VerifyFlags} VerifyFlags */ +/** @typedef {import('../protocol/network')} Network */ +/** @typedef {import('../workers/workerpool')} WorkerPool */ +/** @typedef {import('./keyring')} KeyRing */ + /** * MTX * A mutable transaction object. @@ -41,7 +49,7 @@ class MTX extends TX { * Create a mutable transaction. * @alias module:primitives.MTX * @constructor - * @param {Object} options + * @param {Object?} [options] */ constructor(options) { @@ -57,7 +65,6 @@ class MTX extends TX { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -102,7 +109,8 @@ class MTX extends TX { /** * Clone the transaction. Note that * this will not carry over the view. - * @returns {MTX} + * @param {this} mtx + * @returns {this} */ inject(mtx) { @@ -197,7 +205,7 @@ class MTX extends TX { /** * Add an output. * @param {Address|Output|Object} addr - Address or output options. - * @param {Amount?} value + * @param {AmountValue?} [value] * @returns {Output} * * @example @@ -221,8 +229,7 @@ class MTX extends TX { /** * Verify all transaction inputs. - * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS] - * @returns {Boolean} Whether the inputs are valid. + * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] * @throws {ScriptError} on invalid inputs */ @@ -234,7 +241,7 @@ class MTX extends TX { * Verify the transaction inputs on the worker pool * (if workers are enabled). * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] - * @param {WorkerPool?} pool + * @param {WorkerPool?} [pool] * @returns {Promise} */ @@ -244,7 +251,7 @@ class MTX extends TX { /** * Verify all transaction inputs. - * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS] + * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] * @returns {Boolean} Whether the inputs are valid. */ @@ -263,7 +270,7 @@ class MTX extends TX { * Verify the transaction inputs on the worker pool * (if workers are enabled). * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] - * @param {WorkerPool?} pool + * @param {WorkerPool?} [pool] * @returns {Promise} */ @@ -280,7 +287,7 @@ class MTX extends TX { /** * Calculate the fee for the transaction. - * @returns {Amount} fee (zero if not all coins are available). + * @returns {AmountValue} fee (zero if not all coins are available). */ getFee() { @@ -289,7 +296,7 @@ class MTX extends TX { /** * Calculate the total input value. - * @returns {Amount} value + * @returns {AmountValue} value */ getInputValue() { @@ -344,7 +351,6 @@ class MTX extends TX { /** * Calculate virtual sigop count. - * @param {VerifyFlags?} flags * @returns {Number} sigop count */ @@ -371,6 +377,7 @@ class MTX extends TX { * @param {Number} height - Height at which the * transaction is being spent. In the mempool this is * the chain height plus one at the time it entered the pool. + * @param {Network} network * @returns {Boolean} */ @@ -388,6 +395,7 @@ class MTX extends TX { * @param {Number} height - Height at which the * transaction is being spent. In the mempool this is * the chain height plus one at the time it entered the pool. + * @param {Network} network * @returns {Array} [fee, reason, score] */ @@ -454,8 +462,8 @@ class MTX extends TX { * Build script for a single vector * based on a previous script. * @param {Script} prev - * @param {Buffer} ring - * @return {Boolean} + * @param {KeyRing} ring + * @return {Stack} */ scriptVector(prev, ring) { @@ -516,7 +524,7 @@ class MTX extends TX { * @param {Coin|Output} coin * @param {KeyRing} ring * @param {SighashType?} type - * @param {WorkerPool?} pool + * @param {WorkerPool?} [pool] * @returns {Promise} */ @@ -524,7 +532,7 @@ class MTX extends TX { if (!pool) return this.signInput(index, coin, ring, type); - return await pool.signInput(this, index, coin, ring, type, pool); + return await pool.signInput(this, index, coin, ring, type); } /** @@ -595,7 +603,7 @@ class MTX extends TX { * @param {Stack} vector * @param {Buffer} sig * @param {KeyRing} ring - * @return {Boolean} + * @return {Stack?} */ signVector(prev, vector, sig, ring) { @@ -939,7 +947,7 @@ class MTX extends TX { /** * Estimate maximum possible size. * @param {Function?} estimate - Input script size estimator. - * @returns {Number} + * @returns {Promise} */ async estimateSize(estimate) { @@ -1011,7 +1019,7 @@ class MTX extends TX { * Select necessary coins based on total output value. * @param {Coin[]} coins * @param {Object?} options - * @returns {CoinSelection} + * @returns {Promise} * @throws on not enough funds available. */ @@ -1023,7 +1031,7 @@ class MTX extends TX { /** * Attempt to subtract a fee from a single output. * @param {Number} index - * @param {Amount} fee + * @param {AmountValue} fee */ subtractIndex(index, fee) { @@ -1043,7 +1051,7 @@ class MTX extends TX { /** * Attempt to subtract a fee from all outputs evenly. - * @param {Amount} fee + * @param {AmountValue} fee */ subtractFee(fee) { @@ -1105,7 +1113,7 @@ class MTX extends TX { * Select coins and fill the inputs. * @param {Coin[]} coins * @param {Object} options - See {@link MTX#selectCoins} options. - * @returns {CoinSelector} + * @returns {Promise} */ async fund(coins, options) { @@ -1163,8 +1171,11 @@ class MTX extends TX { assert(changeOutput.covenant.type === 0); } + /** @type {Input[]} */ const inputs = []; + /** @type {Output[]} */ const outputs = []; + // [Input, Output][] const linked = []; let i = 0; @@ -1216,7 +1227,7 @@ class MTX extends TX { /** * Avoid fee sniping. - * @param {Number} - Current chain height. + * @param {Number} height - Current chain height. * @see bitcoin/src/wallet/wallet.cpp */ @@ -1236,7 +1247,7 @@ class MTX extends TX { /** * Set locktime and sequences appropriately. * @param {Number} locktime - * @param {Boolean?} seconds + * @param {Boolean?} [seconds] */ setLocktime(locktime, seconds) { @@ -1409,12 +1420,13 @@ class CoinSelector { /** * Create a coin selector. * @constructor - * @param {TX} tx + * @param {MTX} tx * @param {Object?} options */ constructor(tx, options) { this.tx = tx.clone(); + this.view = tx.view; this.coins = []; this.outputValue = 0; this.index = 0; @@ -1538,11 +1550,13 @@ class CoinSelector { if (options.inputs) { assert(Array.isArray(options.inputs)); + + const lastIndex = this.inputs.size; for (let i = 0; i < options.inputs.length; i++) { const prevout = options.inputs[i]; assert(prevout && typeof prevout === 'object'); const {hash, index} = prevout; - this.inputs.set(Outpoint.toKey(hash, index), i); + this.inputs.set(Outpoint.toKey(hash, index), lastIndex + i); } } @@ -1595,7 +1609,7 @@ class CoinSelector { /** * Calculate total value required. - * @returns {Amount} + * @returns {AmountValue} */ total() { @@ -1655,7 +1669,7 @@ class CoinSelector { /** * Get the current fee based on a size. * @param {Number} size - * @returns {Amount} + * @returns {AmountValue} */ getFee(size) { @@ -1676,31 +1690,7 @@ class CoinSelector { fund() { // Ensure all preferred inputs first. - if (this.inputs.size > 0) { - const coins = []; - - for (let i = 0; i < this.inputs.size; i++) - coins.push(null); - - for (const coin of this.coins) { - const {hash, index} = coin; - const key = Outpoint.toKey(hash, index); - const i = this.inputs.get(key); - - if (i != null) { - coins[i] = coin; - this.inputs.delete(key); - } - } - - if (this.inputs.size > 0) - throw new Error('Could not resolve preferred inputs.'); - - for (const coin of coins) { - this.tx.addCoin(coin); - this.chosen.push(coin); - } - } + this.resolveInputCoins(); if (this.isFull()) return; @@ -1725,7 +1715,7 @@ class CoinSelector { /** * Initiate selection from `coins`. * @param {Coin[]} coins - * @returns {CoinSelector} + * @returns {Promise} */ async select(coins) { @@ -1803,6 +1793,56 @@ class CoinSelector { this.fee = this.hardFee; this.fund(); } + + resolveInputCoins() { + if (this.inputs.size === 0) + return; + + const coins = []; + + for (let i = 0 ; i < this.inputs.size; i++) { + coins.push(null); + } + + // first resolve from coinview if possible. + for (const key of this.inputs.keys()) { + const prevout = Outpoint.fromKey(key); + + if (this.view.hasEntry(prevout)) { + const coinEntry = this.view.getEntry(prevout); + const i = this.inputs.get(key); + + if (i != null) { + assert(!coins[i]); + coins[i] = coinEntry.toCoin(prevout); + this.inputs.delete(key); + } + } + } + + // Now try to resolve from the passed coins array. + if (this.inputs.size > 0) { + for (const coin of this.coins) { + const {hash, index} = coin; + const key = Outpoint.toKey(hash, index); + const i = this.inputs.get(key); + + if (i != null) { + assert(!coins[i]); + coins[i] = coin; + this.inputs.delete(key); + } + } + } + + if (this.inputs.size > 0) + throw new Error('Could not resolve preferred inputs.'); + + for (const coin of coins) { + this.tx.addCoin(coin); + this.chosen.push(coin); + } + } } /** @@ -1838,8 +1878,8 @@ class FundingError extends Error { * Create a funding error. * @constructor * @param {String} msg - * @param {Amount} available - * @param {Amount} required + * @param {AmountValue} [available] + * @param {AmountValue} [required] */ constructor(msg, available, required) { diff --git a/lib/primitives/outpoint.js b/lib/primitives/outpoint.js index f4bc3b611..5ae9e401b 100644 --- a/lib/primitives/outpoint.js +++ b/lib/primitives/outpoint.js @@ -11,6 +11,17 @@ const bio = require('bufio'); const consensus = require('../protocol/consensus'); const util = require('../utils/util'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').HexHash} HexHash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('./tx')} TX */ + +/** + * @typedef {Object} OutpointJSON + * @property {HexHash} hash + * @property {Number} index + */ + /** * Outpoint * Represents a COutPoint. @@ -23,8 +34,8 @@ class Outpoint extends bio.Struct { /** * Create an outpoint. * @constructor - * @param {Hash?} hash - * @param {Number?} index + * @param {Hash?} [hash] + * @param {Number?} [index] */ constructor(hash, index) { @@ -43,7 +54,6 @@ class Outpoint extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -58,7 +68,8 @@ class Outpoint extends bio.Struct { /** * Clone the outpoint. - * @returns {Outpoint} + * @param {this} prevout + * @returns {this} */ inject(prevout) { @@ -70,7 +81,7 @@ class Outpoint extends bio.Struct { /** * Test equality against another outpoint. - * @param {Outpoint} prevout + * @param {this} prevout * @returns {Boolean} */ @@ -82,7 +93,7 @@ class Outpoint extends bio.Struct { /** * Compare against another outpoint (BIP69). - * @param {Outpoint} prevout + * @param {this} prevout * @returns {Number} */ @@ -109,7 +120,7 @@ class Outpoint extends bio.Struct { /** * Get little-endian hash. - * @returns {Hash} + * @returns {HexHash} */ txid() { @@ -119,7 +130,7 @@ class Outpoint extends bio.Struct { /** * Serialize outpoint to a key * suitable for a hash table. - * @returns {String} + * @returns {Buffer} */ toKey() { @@ -128,8 +139,7 @@ class Outpoint extends bio.Struct { /** * Inject properties from hash table key. - * @private - * @param {String} key + * @param {Buffer} key * @returns {Outpoint} */ @@ -142,7 +152,7 @@ class Outpoint extends bio.Struct { /** * Instantiate outpoint from hash table key. - * @param {String} key + * @param {Buffer} key * @returns {Outpoint} */ @@ -152,7 +162,8 @@ class Outpoint extends bio.Struct { /** * Write outpoint to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -172,8 +183,7 @@ class Outpoint extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -184,8 +194,7 @@ class Outpoint extends bio.Struct { /** * Inject properties from json object. - * @private - * @params {Object} json + * @param {OutpointJSON} json */ fromJSON(json) { @@ -200,7 +209,7 @@ class Outpoint extends bio.Struct { /** * Convert the outpoint to an object suitable * for JSON serialization. - * @returns {Object} + * @returns {OutpointJSON} */ getJSON() { @@ -241,7 +250,7 @@ class Outpoint extends bio.Struct { * suitable for a hash table. * @param {Hash} hash * @param {Number} index - * @returns {String} + * @returns {Buffer} */ static toKey(hash, index) { diff --git a/lib/primitives/output.js b/lib/primitives/output.js index 76fd5c54f..2c955fe27 100644 --- a/lib/primitives/output.js +++ b/lib/primitives/output.js @@ -16,10 +16,23 @@ const policy = require('../protocol/policy'); const util = require('../utils/util'); const Covenant = require('./covenant'); +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('./covenant').CovenantJSON} CovenantJSON */ + +/** + * @typedef {Object} OutputJSON + * @property {AmountValue} value + * @property {String} address + * @property {CovenantJSON} covenant + */ + /** * Represents a transaction output. * @alias module:primitives.Output - * @property {Amount} value + * @property {AmountValue} value * @property {Address} address */ @@ -27,7 +40,7 @@ class Output extends bio.Struct { /** * Create an output. * @constructor - * @param {Object?} options + * @param {Object?} [options] */ constructor(options) { @@ -43,7 +56,6 @@ class Output extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -66,9 +78,8 @@ class Output extends bio.Struct { /** * Inject properties from address/value pair. - * @private * @param {Address} address - * @param {Amount} value + * @param {AmountValue} value * @returns {Output} */ @@ -84,7 +95,7 @@ class Output extends bio.Struct { /** * Instantiate output from address/value pair. * @param {Address} address - * @param {Amount} value + * @param {AmountValue} value * @returns {Output} */ @@ -94,7 +105,8 @@ class Output extends bio.Struct { /** * Clone the output. - * @returns {Output} + * @param {this} output + * @returns {this} */ inject(output) { @@ -145,7 +157,7 @@ class Output extends bio.Struct { /** * Get the address hash. - * @returns {Hash} hash + * @returns {Hash} */ getHash() { @@ -168,8 +180,8 @@ class Output extends bio.Struct { /** * Convert the output to an object suitable * for JSON serialization. - * @param {Network} network - * @returns {Object} + * @param {Network} [network] + * @returns {OutputJSON} */ getJSON(network) { @@ -185,8 +197,8 @@ class Output extends bio.Struct { /** * Calculate the dust threshold for this * output, based on serialize size and rate. - * @param {Rate?} rate - * @returns {Amount} + * @param {Rate?} [rate] + * @returns {AmountValue} */ getDustThreshold(rate) { @@ -216,7 +228,7 @@ class Output extends bio.Struct { /** * Test whether the output should be considered dust. - * @param {Rate?} rate + * @param {Rate?} [rate] * @returns {Boolean} */ @@ -235,8 +247,7 @@ class Output extends bio.Struct { /** * Inject properties from a JSON object. - * @private - * @param {Object} json + * @param {OutputJSON} json */ fromJSON(json) { @@ -254,7 +265,8 @@ class Output extends bio.Struct { /** * Write the output to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -266,8 +278,8 @@ class Output extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 9d340def7..49dca5e86 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -28,6 +28,22 @@ const AirdropProof = require('../primitives/airdropproof'); const {encoding} = bio; const {hashType} = Script; +/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('../types').SighashType} SighashType */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Rate} Rate */ +/** @typedef {import('../types').VerifyFlags} VerifyFlags */ +/** @typedef {import('../types').HexHash} HexHash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../blockchain/chainentry')} ChainEntry */ +/** @typedef {import('../covenants/ownership').OwnershipProof} OwnershipProof */ +/** @typedef {import('../workers/workerpool')} WorkerPool */ +/** @typedef {import('../coins/coinview')} CoinView */ +/** @typedef {import('./covenant')} Covenant */ +/** @typedef {import('./coin')} Coin */ +/** @typedef {import('./address')} Address */ + /** * TX * A static transaction object. @@ -42,28 +58,38 @@ class TX extends bio.Struct { /** * Create a transaction. * @constructor - * @param {Object?} options + * @param {Object?} [options] */ constructor(options) { super(); this.version = 0; + /** @type {Input[]} */ this.inputs = []; + /** @type {Output[]} */ this.outputs = []; this.locktime = 0; this.mutable = false; + /** @type {Hash?} */ this._hash = null; + /** @type {Hash?} */ this._wdhash = null; + /** @type {Hash?} */ this._whash = null; + /** @type {Buffer?} */ this._raw = null; + /** @type {Sizes?} */ this._sizes = null; + /** @type {Hash?} */ this._hashPrevouts = null; + /** @type {Hash?} */ this._hashSequence = null; + /** @type {Hash?} */ this._hashOutputs = null; if (options) @@ -72,7 +98,6 @@ class TX extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -109,9 +134,8 @@ class TX extends bio.Struct { /** * Inject properties from tx. * Used for cloning. - * @private - * @param {TX} tx - * @returns {TX} + * @param {this} tx + * @returns {this} */ inject(tx) { @@ -149,7 +173,7 @@ class TX extends bio.Struct { /** * Hash the transaction with the non-witness serialization. - * @returns {Hash} hash + * @returns {Hash} */ hash() { @@ -259,7 +283,7 @@ class TX extends bio.Struct { * @param {Script} prev - Previous output script or redeem script * (in the case of witnesspubkeyhash, this should be the generated * p2pkh script). - * @param {Amount} value - Previous output value. + * @param {AmountValue} value - Previous output value. * @param {SighashType} type - Sighash type. * @returns {Buffer} Signature hash. */ @@ -379,7 +403,7 @@ class TX extends bio.Struct { * Verify signature. * @param {Number} index * @param {Script} prev - * @param {Amount} value + * @param {AmountValue} value * @param {Buffer} sig * @param {Buffer} key * @returns {Boolean} @@ -401,7 +425,7 @@ class TX extends bio.Struct { * @param {Script} prev - Previous output script or redeem script * (in the case of witnesspubkeyhash, this should be the generated * p2pkh script). - * @param {Amount} value - Previous output value. + * @param {AmountValue} value - Previous output value. * @param {Buffer} key * @param {SighashType} type * @returns {Buffer} Signature in DER format. @@ -471,6 +495,7 @@ class TX extends bio.Struct { continue; } + /** @type {OwnershipProof} */ let proof; try { proof = OwnershipProof.decode(witness.items[0]); @@ -529,7 +554,7 @@ class TX extends bio.Struct { * (if workers are enabled). * @param {CoinView} view * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] - * @param {WorkerPool?} pool + * @param {WorkerPool?} [pool] * @returns {Promise} */ @@ -551,7 +576,7 @@ class TX extends bio.Struct { * verified. * @param {Coin|Output} coin - Previous output. * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS] - * @param {WorkerPool?} pool + * @param {WorkerPool?} [pool] * @returns {Promise} */ @@ -612,7 +637,7 @@ class TX extends bio.Struct { * (if workers are enabled). * @param {CoinView} view * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] - * @param {WorkerPool?} pool + * @param {WorkerPool?} [pool] * @returns {Promise} */ @@ -633,13 +658,13 @@ class TX extends bio.Struct { * verified. * @param {Coin|Output} coin - Previous output. * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS] - * @param {WorkerPool?} pool + * @param {WorkerPool?} [pool] * @returns {Promise} */ async verifyInputAsync(index, coin, flags, pool) { try { - await this.checkInput(index, coin, flags, pool); + await this.checkInputAsync(index, coin, flags, pool); } catch (e) { if (e.type === 'ScriptError') return false; @@ -661,7 +686,7 @@ class TX extends bio.Struct { /** * Calculate the fee for the transaction. * @param {CoinView} view - * @returns {Amount} fee (zero if not all coins are available). + * @returns {AmountValue} fee (zero if not all coins are available). */ getFee(view) { @@ -674,7 +699,7 @@ class TX extends bio.Struct { /** * Calculate the total input value. * @param {CoinView} view - * @returns {Amount} value + * @returns {AmountValue} value */ getInputValue(view) { @@ -694,7 +719,7 @@ class TX extends bio.Struct { /** * Calculate the total output value. - * @returns {Amount} value + * @returns {AmountValue} value */ getOutputValue() { @@ -1294,6 +1319,7 @@ class TX extends bio.Struct { * @param {Number} height - Height at which the * transaction is being spent. In the mempool this is * the chain height plus one at the time it entered the pool. + * @param {Network} network * @returns {Boolean} */ @@ -1312,6 +1338,7 @@ class TX extends bio.Struct { * @param {Number} height - Height at which the * transaction is being spent. In the mempool this is * the chain height plus one at the time it entered the pool. + * @param {Network} network * @returns {Array} [fee, reason, score] */ @@ -1382,7 +1409,8 @@ class TX extends bio.Struct { * any contextual covenants rules. * @param {CoinView} view * @param {Number} height - * @returns {Boolean} + * @param {Network} network + * @returns {Number} */ verifyCovenants(view, height, network) { @@ -1414,7 +1442,7 @@ class TX extends bio.Struct { * Calculate the transaction priority. * @param {CoinView} view * @param {Number} height - * @param {Number?} size - Size to calculate priority + * @param {Number?} [size] - Size to calculate priority * based on. If not present, virtual size will be used. * @returns {Number} */ @@ -1485,9 +1513,9 @@ class TX extends bio.Struct { * passed this test is most likely relayable * without a fee. * @param {CoinView} view - * @param {Number?} height - If not present, tx + * @param {Number} height - If not present, tx * height or network height will be used. - * @param {Number?} size - If not present, modified + * @param {Number?} [size] - If not present, modified * size will be calculated and used. * @returns {Boolean} */ @@ -1500,10 +1528,10 @@ class TX extends bio.Struct { /** * Calculate minimum fee in order for the transaction * to be relayable (not the constant min relay fee). - * @param {Number?} size - If not present, max size + * @param {Number?} [size] - If not present, max size * estimation will be calculated and used. - * @param {Rate?} rate - Rate of dollarydoo per kB. - * @returns {Amount} fee + * @param {Rate?} [rate] - Rate of dollarydoo per kB. + * @returns {AmountValue} fee */ getMinFee(size, rate) { @@ -1517,10 +1545,10 @@ class TX extends bio.Struct { * Calculate the minimum fee in order for the transaction * to be relayable, but _round to the nearest kilobyte * when taking into account size. - * @param {Number?} size - If not present, max size + * @param {Number?} [size] - If not present, max size * estimation will be calculated and used. - * @param {Rate?} rate - Rate of dollarydoo per kB. - * @returns {Amount} fee + * @param {Rate?} [rate] - Rate of dollarydoo per kB. + * @returns {AmountValue} fee */ getRoundFee(size, rate) { @@ -1534,7 +1562,7 @@ class TX extends bio.Struct { * Calculate the transaction's rate based on size * and fees. Size will be calculated if not present. * @param {CoinView} view - * @param {Number?} size + * @param {Number?} [size] * @returns {Rate} */ @@ -1574,6 +1602,31 @@ class TX extends bio.Struct { */ test(filter) { + if (filter.test(this.hash())) + return true; + + for (let i = 0; i < this.outputs.length; i++) { + const {address, covenant} = this.outputs[i]; + + if (filter.test(address.hash) || covenant.test(filter)) + return true; + } + + for (const {prevout} of this.inputs) { + if (filter.test(prevout.encode())) + return true; + } + + return false; + } + + /** + * Test a transaction against a bloom filter. + * @param {BloomFilter} filter + * @returns {Boolean} + */ + + testAndMaybeUpdate(filter) { let found = false; if (filter.test(this.hash())) @@ -1602,7 +1655,7 @@ class TX extends bio.Struct { /** * Get little-endian tx hash. - * @returns {Hash} + * @returns {HexHash} */ txid() { @@ -1611,7 +1664,7 @@ class TX extends bio.Struct { /** * Get little-endian wtx hash. - * @returns {Hash} + * @returns {HexHash} */ wtxid() { @@ -1676,9 +1729,9 @@ class TX extends bio.Struct { /** * Inspect the transaction and return a more * user-friendly representation of the data. - * @param {CoinView} view - * @param {ChainEntry} entry - * @param {Number} index + * @param {CoinView?} [view] + * @param {ChainEntry?} [entry] + * @param {Number?} [index] * @returns {Object} */ @@ -1736,10 +1789,10 @@ class TX extends bio.Struct { /** * Convert the transaction to an object suitable * for JSON serialization. - * @param {Network} network - * @param {CoinView} view - * @param {ChainEntry} entry - * @param {Number} index + * @param {Network} [network] + * @param {CoinView} [view] + * @param {ChainEntry} [entry] + * @param {Number} [index] * @returns {Object} */ @@ -1791,7 +1844,6 @@ class TX extends bio.Struct { /** * Inject properties from a json object. - * @private * @param {Object} json */ @@ -1819,8 +1871,7 @@ class TX extends bio.Struct { /** * Inject properties from serialized * buffer reader (witness serialization). - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -1900,9 +1951,8 @@ class TX extends bio.Struct { /** * Serialize transaction with witness. Calculates the witness * size as it is framing (exposed on return value as `witness`). - * @private - * @param {BufferWriter} bw - * @returns {BufferWriter} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { diff --git a/lib/primitives/txmeta.js b/lib/primitives/txmeta.js index 2ca1421e2..a2a18e147 100644 --- a/lib/primitives/txmeta.js +++ b/lib/primitives/txmeta.js @@ -11,6 +11,12 @@ const bio = require('bufio'); const util = require('../utils/util'); const TX = require('./tx'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../blockchain/chainentry')} ChainEntry */ +/** @typedef {import('../coins/coinview')} CoinView */ +/** @typedef {import('../protocol/network')} Network */ + /** * TXMeta * An extended transaction object. @@ -21,7 +27,7 @@ class TXMeta extends bio.Struct { /** * Create an extended transaction. * @constructor - * @param {Object?} options + * @param {Object?} [options] */ constructor(options) { @@ -30,6 +36,7 @@ class TXMeta extends bio.Struct { this.tx = new TX(); this.mtime = util.now(); this.height = -1; + /** @type {Hash} */ this.block = null; this.time = 0; this.index = -1; @@ -40,7 +47,6 @@ class TXMeta extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options */ @@ -81,8 +87,9 @@ class TXMeta extends bio.Struct { /** * Inject properties from options object. - * @private - * @param {Object} options + * @param {TX} tx + * @param {ChainEntry} entry + * @param {Number} index */ fromTX(tx, entry, index) { @@ -98,7 +105,9 @@ class TXMeta extends bio.Struct { /** * Instantiate TXMeta from options. - * @param {Object} options + * @param {TX} tx + * @param {ChainEntry} entry + * @param {Number} index * @returns {TXMeta} */ @@ -108,6 +117,7 @@ class TXMeta extends bio.Struct { /** * Inspect the transaction. + * @param {CoinView} view * @returns {Object} */ @@ -123,8 +133,9 @@ class TXMeta extends bio.Struct { /** * Convert the transaction to an object suitable * for JSON serialization. - * @param {Network} network - * @param {CoinView} view + * @param {Network} [network] + * @param {CoinView} [view] + * @param {Number} [chainHeight] * @returns {Object} */ @@ -144,7 +155,6 @@ class TXMeta extends bio.Struct { /** * Inject properties from a json object. - * @private * @param {Object} json */ @@ -193,7 +203,8 @@ class TXMeta extends bio.Struct { * to store transactions in the database. The extended * serialization includes the height, block hash, index, * timestamp, and pending-since time. - * @returns {Buffer} + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -216,8 +227,7 @@ class TXMeta extends bio.Struct { /** * Inject properties from "extended" serialization format. - * @private - * @param {Buffer} data + * @param {bio.BufferReader} br */ read(br) { diff --git a/lib/protocol/consensus.js b/lib/protocol/consensus.js index 39b5e85fe..808129653 100644 --- a/lib/protocol/consensus.js +++ b/lib/protocol/consensus.js @@ -13,6 +13,9 @@ const assert = require('bsert'); const BN = require('bcrypto/lib/bn.js'); +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('../types').Amount} Amount */ + /** * Coin exponent. * @const {Number} @@ -477,6 +480,7 @@ exports.verifyPOW = function verifyPOW(hash, bits) { /** * Calculate block subsidy. * @param {Number} height - Reward era by height. + * @param {Number} interval - halving interval. * @returns {Amount} */ diff --git a/lib/protocol/errors.js b/lib/protocol/errors.js index e0ddbd266..135bcee8e 100644 --- a/lib/protocol/errors.js +++ b/lib/protocol/errors.js @@ -1,3 +1,4 @@ +// @ts-check /*! * errors.js - error objects for hsd * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License). @@ -12,6 +13,11 @@ const assert = require('bsert'); +/** @typedef {import('../primitives/block')} Block */ +/** @typedef {import('../primitives/tx')} TX */ +/** @typedef {import('../primitives/claim')} Claim */ +/** @typedef {import('../types').Hash} Hash */ + /** * Verify Error * An error thrown during verification. Can be either @@ -19,24 +25,24 @@ const assert = require('bsert'); * block verification error. Ultimately used to send * `reject` packets to peers. * @extends Error - * @param {Block|TX} msg - * @param {String} code - Reject packet code. - * @param {String} reason - Reject packet reason. - * @param {Number} score - Ban score increase + * @property {Block|TX|Claim} msg + * @property {String} code - Reject packet code. + * @property {String} reason - Reject packet reason. + * @property {Number} score - Ban score increase * (can be -1 for no reject packet). - * @param {Boolean} malleated + * @property {Boolean} malleated */ class VerifyError extends Error { /** * Create a verify error. * @constructor - * @param {Block|TX} msg + * @param {Block|TX|Claim} msg * @param {String} code - Reject packet code. * @param {String} reason - Reject packet reason. * @param {Number} score - Ban score increase * (can be -1 for no reject packet). - * @param {Boolean} malleated + * @param {Boolean} [malleated=false] */ constructor(msg, code, reason, score, malleated) { diff --git a/lib/protocol/network.js b/lib/protocol/network.js index b286033ee..307a8e8ef 100644 --- a/lib/protocol/network.js +++ b/lib/protocol/network.js @@ -11,6 +11,8 @@ const binary = require('../utils/binary'); const networks = require('./networks'); const TimeData = require('./timedata'); +/** @typedef {import('../types').NetworkType} NetworkType */ + /** * Network * Represents a network. @@ -71,8 +73,6 @@ class Network { /** * Get a deployment by bit index. - * @param {Number} bit - * @returns {Object} */ init() { @@ -217,7 +217,7 @@ class Network { * @private * @param {Object} value * @param {Function} compare - * @param {Network|null} network + * @param {(NetworkType|Network)?} network * @param {String} name * @returns {Network} */ @@ -242,7 +242,7 @@ class Network { /** * Get a network by its magic number. * @param {Number} value - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {Network} */ @@ -252,8 +252,8 @@ class Network { /** * Get a network by its WIF prefix. - * @param {Number} value - * @param {Network?} network + * @param {Number} prefix + * @param {(Network|NetworkType)?} [network] * @returns {Network} */ @@ -263,8 +263,8 @@ class Network { /** * Get a network by its xpubkey prefix. - * @param {Number} value - * @param {Network?} network + * @param {Number} prefix + * @param {(Network|NetworkType)?} [network] * @returns {Network} */ @@ -274,8 +274,8 @@ class Network { /** * Get a network by its xprivkey prefix. - * @param {Number} value - * @param {Network?} network + * @param {Number} prefix + * @param {(Network|NetworkType)?} [network] * @returns {Network} */ @@ -286,7 +286,7 @@ class Network { /** * Get a network by its xpubkey base58 prefix. * @param {String} prefix - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {Network} */ @@ -297,7 +297,7 @@ class Network { /** * Get a network by its xprivkey base58 prefix. * @param {String} prefix - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {Network} */ @@ -308,7 +308,7 @@ class Network { /** * Get a network by its bech32 address prefix. * @param {String} hrp - * @param {Network?} network + * @param {(Network|NetworkType)?} [network] * @returns {Network} */ @@ -366,13 +366,13 @@ Network.type = null; Network.main = null; Network.testnet = null; Network.regtest = null; -Network.segnet4 = null; Network.simnet = null; /* * Set initial network. */ +// @ts-ignore Network.set(process.env.HSD_NETWORK || 'main'); /* diff --git a/lib/protocol/networks.js b/lib/protocol/networks.js index 45f5fc528..bfbbc30fc 100644 --- a/lib/protocol/networks.js +++ b/lib/protocol/networks.js @@ -12,6 +12,8 @@ * @module protocol/networks */ +/** @typedef {import('../types').NetworkType} NetworkType */ + const BN = require('bcrypto/lib/bn.js'); const genesis = require('./genesis'); const network = exports; @@ -19,7 +21,8 @@ const network = exports; /** * Network type list. * @memberof module:protocol/networks - * @const {String[]} + * @const {NetworkType[]} + * @type {NetworkType[]} * @default */ @@ -104,7 +107,9 @@ main.checkpointMap = { 100000: Buffer.from( '000000000000000136d7d3efa688072f40d9fdd71bd47bb961694c0f38950246', 'hex'), 130000: Buffer.from( - '0000000000000005ee5106df9e48bcd232a1917684ac344b35ddd9b9e4101096', 'hex') + '0000000000000005ee5106df9e48bcd232a1917684ac344b35ddd9b9e4101096', 'hex'), + 160000: Buffer.from( + '00000000000000021e723ce5aedc021ab4f85d46a6914e40148f01986baa46c9', 'hex') }; /** @@ -113,7 +118,7 @@ main.checkpointMap = { * @default */ -main.lastCheckpoint = 100000; +main.lastCheckpoint = 160000; /** * Reward halving interval. @@ -258,8 +263,6 @@ main.txStart = 14 * main.pow.blocksPerDay; /** * Name-related constants. - * @enum {Number} - * @default */ main.names = { @@ -316,6 +319,14 @@ main.names = { claimPeriod: (4 * 365) * main.pow.blocksPerDay, + /** + * The time window which the + * names will be locked before release or hard fork. + * @const {Number} + */ + + alexaLockupPeriod: (8 * 365) * main.pow.blocksPerDay, + /** * Amount of time required in between * replacement claims. @@ -352,14 +363,6 @@ main.names = { transferLockup: 2 * main.pow.blocksPerDay, - /** - * Amount of time before a transfer - * or revocation is possible. - * @const {Number} - */ - - revocationDelay: 14 * main.pow.blocksPerDay, - /** * Sum of total period and revocation delay. * @const {Number} @@ -384,8 +387,6 @@ main.names = { /** * Block constants. - * @enum {Number} - * @default */ main.block = { @@ -463,6 +464,16 @@ main.deployments = { required: false, force: false }, + icannlockup: { + name: 'icannlockup', + bit: 1, + startTime: 1691625600, // August 10, 2023 + timeout: 1703980800, // December 31, 2023 + threshold: -1, + window: -1, + required: false, + force: false + }, testdummy: { name: 'testdummy', bit: 28, @@ -483,12 +494,13 @@ main.deployments = { main.deploys = [ main.deployments.hardening, + main.deployments.icannlockup, main.deployments.testdummy ]; /** * Key prefixes. - * @enum {Number} + * @enum {Number|String} * @default */ @@ -668,12 +680,12 @@ testnet.names = { renewalPeriod: 7 * testnet.pow.blocksPerDay, renewalMaturity: 1 * testnet.pow.blocksPerDay, claimPeriod: 90 * testnet.pow.blocksPerDay, + alexaLockupPeriod: 180 * testnet.pow.blocksPerDay, claimFrequency: 2 * testnet.pow.blocksPerDay, biddingPeriod: 1 * testnet.pow.blocksPerDay, revealPeriod: 2 * testnet.pow.blocksPerDay, treeInterval: testnet.pow.blocksPerDay >>> 2, transferLockup: 2 * testnet.pow.blocksPerDay, - revocationDelay: 4 * testnet.pow.blocksPerDay, auctionMaturity: (1 + 2 + 4) * testnet.pow.blocksPerDay, noRollout: false, noReserved: false @@ -703,6 +715,16 @@ testnet.deployments = { required: false, force: false }, + icannlockup: { + name: 'icannlockup', + bit: 1, + startTime: 1691625600, // August 10, 2023 + timeout: 1703980800, // December 31, 2023 + threshold: -1, + window: -1, + required: false, + force: false + }, testdummy: { name: 'testdummy', bit: 28, @@ -717,6 +739,7 @@ testnet.deployments = { testnet.deploys = [ testnet.deployments.hardening, + testnet.deployments.icannlockup, testnet.deployments.testdummy ]; @@ -811,12 +834,12 @@ regtest.names = { renewalPeriod: 2500, renewalMaturity: 50, claimPeriod: 250000, + alexaLockupPeriod: 500000, claimFrequency: 0, biddingPeriod: 5, revealPeriod: 10, treeInterval: 5, transferLockup: 10, - revocationDelay: 50, auctionMaturity: 5 + 10 + 50, noRollout: false, noReserved: false @@ -846,6 +869,16 @@ regtest.deployments = { required: false, force: false }, + icannlockup: { + name: 'icannlockup', + bit: 1, + startTime: 1691625600, // August 10, 2023 + timeout: 1703980800, // December 31, 2023 + threshold: -1, + window: -1, + required: false, + force: false + }, testdummy: { name: 'testdummy', bit: 28, @@ -860,6 +893,7 @@ regtest.deployments = { regtest.deploys = [ regtest.deployments.hardening, + regtest.deployments.icannlockup, regtest.deployments.testdummy ]; @@ -958,12 +992,12 @@ simnet.names = { renewalPeriod: 1250, renewalMaturity: 25, claimPeriod: 75000, + alexaLockupPeriod: 150000, claimFrequency: 0, biddingPeriod: 25, revealPeriod: 50, treeInterval: 2, transferLockup: 5, - revocationDelay: 25, auctionMaturity: 25 + 50 + 25, noRollout: false, noReserved: false @@ -993,6 +1027,16 @@ simnet.deployments = { required: false, force: false }, + icannlockup: { + name: 'icannlockup', + bit: 1, + startTime: 1691625600, // August 10, 2023 + timeout: 1703980800, // December 31, 2023 + threshold: -1, + window: -1, + required: false, + force: false + }, testdummy: { name: 'testdummy', bit: 28, @@ -1007,6 +1051,7 @@ simnet.deployments = { simnet.deploys = [ simnet.deployments.hardening, + simnet.deployments.icannlockup, simnet.deployments.testdummy ]; diff --git a/lib/protocol/policy.js b/lib/protocol/policy.js index 60cafd6c4..9e688ebc7 100644 --- a/lib/protocol/policy.js +++ b/lib/protocol/policy.js @@ -13,6 +13,9 @@ const assert = require('bsert'); const consensus = require('./consensus'); +/** @typedef {import('../types').Amount} Amount */ +/** @typedef {import('../types').Rate} Rate */ + /** * Maximum transaction version (policy). * @const {Number} @@ -177,8 +180,8 @@ exports.BLOCK_PRIORITY_THRESHOLD = exports.FREE_THRESHOLD; /** * Calculate minimum fee based on rate and size. - * @param {Number?} size - * @param {Rate?} rate - Rate of dollarydoo per kB. + * @param {Number} size + * @param {Rate?} [rate] - Rate of dollarydoo per kB. * @returns {Amount} fee */ @@ -204,8 +207,8 @@ exports.getMinFee = function getMinFee(size, rate) { * Calculate the minimum fee in order for the transaction * to be relayable, but _round to the nearest kilobyte * when taking into account size. - * @param {Number?} size - * @param {Rate?} rate - Rate of dollarydoo per kB. + * @param {Number} size + * @param {Rate?} [rate] - Rate of dollarydoo per kB. * @returns {Amount} fee */ diff --git a/lib/script/common.js b/lib/script/common.js index 2fdb5fd56..2b003d3d3 100644 --- a/lib/script/common.js +++ b/lib/script/common.js @@ -14,6 +14,9 @@ const assert = require('bsert'); const secp256k1 = require('bcrypto/lib/secp256k1'); const ScriptNum = require('./scriptnum'); +/** @typedef {import('../types').SighashType} SighashType */ +/** @typedef {import('../types').VerifyFlags} VerifyFlags */ + /** * Opcodes by value. * @const {Object} @@ -342,7 +345,7 @@ exports.small = [ /** * Script and locktime flags. See {@link VerifyFlags}. - * @enum {Number} + * @enum {VerifyFlags} */ exports.flags = { diff --git a/lib/script/opcode.js b/lib/script/opcode.js index 9ec15ef42..ab6a8d63b 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -12,6 +12,9 @@ const ScriptNum = require('./scriptnum'); const common = require('./common'); const opcodes = common.opcodes; +/** @typedef {import('../types').BufioWriter} BufioWriter */ + +/** @type {Opcode[]} */ const opCache = []; let PARSE_ERROR = null; @@ -31,7 +34,7 @@ class Opcode { * Note: this should not be called directly. * @constructor * @param {Number} value - Opcode. - * @param {Buffer?} data - Pushdata buffer. + * @param {Buffer?} [data] - Pushdata buffer. */ constructor(value, data) { @@ -175,8 +178,8 @@ class Opcode { /** * Get string for opcode. - * @param {String?} enc - * @returns {Buffer|null} + * @param {String?} [enc] + * @returns {String|null} */ toString(enc) { @@ -205,8 +208,8 @@ class Opcode { /** * Convert opcode to script number. - * @param {Boolean?} minimal - * @param {Number?} limit + * @param {Boolean?} [minimal] + * @param {Number?} [limit] * @returns {ScriptNum|null} */ @@ -296,7 +299,8 @@ class Opcode { /** * Encode the opcode to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -498,7 +502,7 @@ class Opcode { /** * Instantiate an opcode from a ScriptNum. - * @param {ScriptNumber} num + * @param {ScriptNum} num * @returns {Opcode} */ @@ -574,7 +578,7 @@ class Opcode { /** * Instantiate opcode from buffer reader. - * @param {BufferReader} br + * @param {bio.BufferReader} br * @returns {Opcode} */ diff --git a/lib/script/script.js b/lib/script/script.js index 06bad4ccb..6ce9148b6 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -27,6 +27,16 @@ const opcodes = common.opcodes; const scriptTypes = common.types; const {encoding} = bio; +/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ +/** @typedef {import('../types').SighashType} SighashType */ +/** @typedef {import('../types').VerifyFlags} VerifyFlags */ +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').Hash} Hash */ +/** @typedef {import('./witness')} Witness */ +/** @typedef {import('../primitives/address')} Address */ +/** @typedef {import('../primitives/tx')} TX */ + /* * Constants */ @@ -46,13 +56,14 @@ class Script extends bio.Struct { /** * Create a script. * @constructor - * @param {Buffer|Array|Object} code + * @param {Object?} [options] */ constructor(options) { super(); this.raw = EMPTY_BUFFER; + /** @type {Opcode[]} */ this.code = []; if (options) @@ -79,8 +90,8 @@ class Script extends bio.Struct { /** * Inject properties from options object. - * @private * @param {Object} options + * @returns {this} */ fromOptions(options) { @@ -111,7 +122,6 @@ class Script extends bio.Struct { /** * Instantiate a value-only iterator. - * @returns {ScriptIterator} */ values() { @@ -120,7 +130,6 @@ class Script extends bio.Struct { /** * Instantiate a key and value iterator. - * @returns {ScriptIterator} */ entries() { @@ -129,7 +138,6 @@ class Script extends bio.Struct { /** * Instantiate a value-only iterator. - * @returns {ScriptIterator} */ [Symbol.iterator]() { @@ -150,9 +158,8 @@ class Script extends bio.Struct { /** * Inject properties from an array of * of buffers and numbers. - * @private - * @param {Array} code - * @returns {Script} + * @param {Opcode[]} code + * @returns {this} */ fromArray(code) { @@ -169,7 +176,7 @@ class Script extends bio.Struct { /** * Instantiate script from an array * of buffers and numbers. - * @param {Array} code + * @param {Opcode[]} code * @returns {Script} */ @@ -199,9 +206,8 @@ class Script extends bio.Struct { /** * Inject data from stack items. - * @private * @param {Buffer[]} items - * @returns {Script} + * @returns {this} */ fromItems(items) { @@ -236,9 +242,8 @@ class Script extends bio.Struct { /** * Inject data from stack. - * @private * @param {Stack} stack - * @returns {Script} + * @returns {this} */ fromStack(stack) { @@ -258,9 +263,8 @@ class Script extends bio.Struct { /** * Inject properties from script. * Used for cloning. - * @private - * @param {Script} script - * @returns {Script} + * @param {this} script + * @returns {this} */ inject(script) { @@ -293,7 +297,7 @@ class Script extends bio.Struct { /** * Clear the script. - * @returns {Script} + * @returns {this} */ clear() { @@ -343,7 +347,6 @@ class Script extends bio.Struct { /** * Re-encode the script internally. Useful if you * changed something manually in the `code` array. - * @returns {Script} */ compile() { @@ -367,7 +370,8 @@ class Script extends bio.Struct { /** * Write the script to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -377,8 +381,7 @@ class Script extends bio.Struct { /** * Encode the script to a Buffer. See {@link Script#encode}. - * @param {String} enc - Encoding, either `'hex'` or `null`. - * @returns {Buffer|String} Serialized script. + * @returns {Buffer} Serialized script. */ encode() { @@ -396,7 +399,6 @@ class Script extends bio.Struct { /** * Inject properties from json object. - * @private * @param {String} json */ @@ -408,13 +410,14 @@ class Script extends bio.Struct { /** * Get the script's "subscript" starting at a separator. * @param {Number} index - The last separator to sign/verify beyond. - * @returns {Script} Subscript. + * @returns {this} Subscript. */ getSubscript(index) { if (index === 0) return this.clone(); + /** @type {this} */ const script = new Script(); for (let i = index; i < this.code.length; i++) { @@ -434,7 +437,7 @@ class Script extends bio.Struct { * Remove all OP_CODESEPARATORs if present. This bizarre * behavior is necessary for signing and verification when * code separators are present. - * @returns {Script} Subscript. + * @returns {this}. */ removeSeparators() { @@ -458,6 +461,7 @@ class Script extends bio.Struct { // Uncommon case: someone actually // has a code separator. Go through // and remove them all. + /** @type {this} */ const script = new Script(); for (const op of this.code) { @@ -477,7 +481,7 @@ class Script extends bio.Struct { * @param {Number?} flags - Script standard flags. * @param {TX?} tx - Transaction being verified. * @param {Number?} index - Index of input being verified. - * @param {Amount?} value - Previous output value. + * @param {AmountValue?} value - Previous output value. * @throws {ScriptError} Will be thrown on VERIFY failures. */ @@ -1323,8 +1327,8 @@ class Script extends bio.Struct { /** * Inject properties from a pay-to-pubkey script. - * @private * @param {Buffer} key + * @returns {this} */ fromPubkey(key) { @@ -1435,7 +1439,7 @@ class Script extends bio.Struct { /** * Get the standard script type. - * @returns {ScriptType} + * @returns {common.types} */ getType() { @@ -1655,7 +1659,7 @@ class Script extends bio.Struct { /** * Test the script against a bloom filter. - * @param {Bloom} filter + * @param {BloomFilter} filter * @returns {Boolean} */ @@ -2171,8 +2175,7 @@ class Script extends bio.Struct { /** * Inject properties from bitcoind test string. - * @private - * @param {String} items - Script string. + * @param {String} code - Script string. * @throws Parse error. */ @@ -2238,7 +2241,7 @@ class Script extends bio.Struct { * @param {Address} addr * @param {TX} tx * @param {Number} index - * @param {Amount} value + * @param {AmountValue} value * @param {VerifyFlags} flags * @throws {ScriptError} */ @@ -2304,8 +2307,7 @@ class Script extends bio.Struct { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -2314,8 +2316,8 @@ class Script extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {Buffer} + * @param {Buffer} data + * @returns {this} */ decode(data) { diff --git a/lib/script/scripterror.js b/lib/script/scripterror.js index 1ba91ea37..99ef17947 100644 --- a/lib/script/scripterror.js +++ b/lib/script/scripterror.js @@ -6,6 +6,8 @@ 'use strict'; +/** @typedef {import('./opcode')} Opcode */ + /** * Script Error * An error thrown from the scripting system, @@ -23,8 +25,8 @@ class ScriptError extends Error { * Create an error. * @constructor * @param {String} code - Error code. - * @param {Opcode} op - Opcode. - * @param {Number?} ip - Instruction pointer. + * @param {Opcode|String} [op] - Opcode. + * @param {Number?} [ip] - Instruction pointer. */ constructor(code, op, ip) { diff --git a/lib/script/scriptnum.js b/lib/script/scriptnum.js index 2c91dd6cb..b94238607 100644 --- a/lib/script/scriptnum.js +++ b/lib/script/scriptnum.js @@ -29,8 +29,8 @@ class ScriptNum extends I64 { /** * Create a script number. * @constructor - * @param {(Number|String|Buffer|Object)?} num - * @param {(String|Number)?} base + * @param {(Number|String|Buffer|Object)?} [num] + * @param {(String|Number)?} [base] */ constructor(num, base) { @@ -170,8 +170,8 @@ class ScriptNum extends I64 { * Decode and verify script number. * @private * @param {Buffer} data - * @param {Boolean?} minimal - Require minimal encoding. - * @param {Number?} limit - Size limit. + * @param {Boolean?} [minimal] - Require minimal encoding. + * @param {Number?} [limit] - Size limit. * @returns {ScriptNum} */ @@ -223,8 +223,8 @@ class ScriptNum extends I64 { /** * Decode and verify script number. * @param {Buffer} data - * @param {Boolean?} minimal - Require minimal encoding. - * @param {Number?} limit - Size limit. + * @param {Boolean?} [minimal] - Require minimal encoding. + * @param {Number?} [limit] - Size limit. * @returns {ScriptNum} */ diff --git a/lib/script/sigcache.js b/lib/script/sigcache.js index f256cfd2a..d0e814431 100644 --- a/lib/script/sigcache.js +++ b/lib/script/sigcache.js @@ -10,6 +10,8 @@ const assert = require('bsert'); const {BufferMap} = require('buffer-map'); const secp256k1 = require('bcrypto/lib/secp256k1'); +/** @typedef {import('../types').Hash} Hash */ + /** * Signature cache. * @alias module:script.SigCache @@ -93,7 +95,7 @@ class SigCache { /** * Verify a signature, testing * it against the cache first. - * @param {Buffer} hash + * @param {Hash} hash * @param {Buffer} sig * @param {Buffer} key * @returns {Boolean} diff --git a/lib/script/stack.js b/lib/script/stack.js index 06ccf184d..1a27b850c 100644 --- a/lib/script/stack.js +++ b/lib/script/stack.js @@ -23,7 +23,7 @@ class Stack extends bio.Struct { /** * Create a stack. * @constructor - * @param {Buffer[]?} items - Stack items. + * @param {Buffer[]?} [items] - Stack items. */ constructor(items) { @@ -51,7 +51,7 @@ class Stack extends bio.Struct { /** * Instantiate a value-only iterator. - * @returns {StackIterator} + * @returns {IterableIterator} */ [Symbol.iterator]() { @@ -60,7 +60,7 @@ class Stack extends bio.Struct { /** * Instantiate a value-only iterator. - * @returns {StackIterator} + * @returns {IterableIterator} */ values() { @@ -69,7 +69,6 @@ class Stack extends bio.Struct { /** * Instantiate a key and value iterator. - * @returns {StackIterator} */ entries() { @@ -116,7 +115,8 @@ class Stack extends bio.Struct { /** * Clone the stack. - * @returns {Stack} Cloned stack. + * @param {this} stack + * @returns {this} Cloned stack. */ inject(stack) { @@ -196,8 +196,8 @@ class Stack extends bio.Struct { /** * Set stack item at index. * @param {Number} index - * @param {Buffer} value - * @returns {Buffer} + * @param {Buffer} item + * @returns {this} */ set(index, item) { @@ -216,7 +216,7 @@ class Stack extends bio.Struct { * Push item onto stack. * @see Array#push * @param {Buffer} item - * @returns {Number} Stack size. + * @returns {this} */ push(item) { @@ -229,7 +229,7 @@ class Stack extends bio.Struct { * Unshift item from stack. * @see Array#unshift * @param {Buffer} item - * @returns {Number} + * @returns {this} */ unshift(item) { @@ -242,7 +242,7 @@ class Stack extends bio.Struct { * Insert an item. * @param {Number} index * @param {Buffer} item - * @returns {Buffer} + * @returns {this} */ insert(index, item) { @@ -271,7 +271,7 @@ class Stack extends bio.Struct { if (end < 0) end = this.items.length + end; - this.items.splice(start, end - start); + return this.items.splice(start, end - start); } /** diff --git a/lib/script/witness.js b/lib/script/witness.js index 768f148b1..cff666bc3 100644 --- a/lib/script/witness.js +++ b/lib/script/witness.js @@ -17,6 +17,17 @@ const Stack = require('./stack'); const {encoding} = bio; const scriptTypes = common.types; +/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */ +/** @typedef {import('../types').ScriptType} ScriptType */ +/** @typedef {import('../types').BufioWriter} BufioWriter */ + +/** + * @typedef {Object} WitnessOptions + * @property {Buffer[]} items + * @property {Script?} redeem + * @property {Number} length + */ + /** * Witness * Refers to the witness vector of @@ -33,11 +44,8 @@ class Witness extends Stack { * Create a witness. * @alias module:script.Witness * @constructor - * @param {Buffer[]|Object} items - Array of + * @param {Buffer[]|WitnessOptions} [options] - Array of * stack items. - * @property {Buffer[]} items - * @property {Script?} redeem - * @property {Number} length */ constructor(options) { @@ -49,8 +57,8 @@ class Witness extends Stack { /** * Inject properties from options object. - * @private - * @param {Object} options + * @param {Buffer[]|WitnessOptions} options + * @returns {this} */ fromOptions(options) { @@ -138,7 +146,6 @@ class Witness extends Stack { /** * Inject properties from a stack. - * @private * @param {Stack} stack */ @@ -168,9 +175,8 @@ class Witness extends Stack { /** * Inject properties from witness. * Used for cloning. - * @private - * @param {Witness} witness - * @returns {Witness} + * @param {this} witness + * @returns {this} */ inject(witness) { @@ -303,7 +309,7 @@ class Witness extends Stack { /** * Test the witness against a bloom filter. - * @param {Bloom} filter + * @param {BloomFilter} filter * @returns {Boolean} */ @@ -321,7 +327,7 @@ class Witness extends Stack { /** * Grab and deserialize the redeem script from the witness. - * @returns {Script} Redeem script. + * @returns {Script?} Redeem script. */ getRedeem() { @@ -378,7 +384,8 @@ class Witness extends Stack { /** * Write witness to a buffer writer. - * @param {BufferWriter} bw + * @param {BufioWriter} bw + * @returns {BufioWriter} */ write(bw) { @@ -403,7 +410,7 @@ class Witness extends Stack { /** * Convert witness to a hex string. - * @returns {String} + * @returns {String[]} */ getJSON() { @@ -417,8 +424,7 @@ class Witness extends Stack { /** * Inject properties from json object. - * @private - * @param {String} json + * @param {String[]} json */ fromJSON(json) { @@ -434,8 +440,7 @@ class Witness extends Stack { /** * Inject properties from buffer reader. - * @private - * @param {BufferReader} br + * @param {bio.BufferReader} br */ read(br) { @@ -452,7 +457,6 @@ class Witness extends Stack { /** * Inject items from string. - * @private * @param {String|String[]} items */ diff --git a/lib/types.js b/lib/types.js index 55579afab..f8ef93123 100644 --- a/lib/types.js +++ b/lib/types.js @@ -32,6 +32,12 @@ * @global */ +/** + * A bitfield containing name flags. + * @typedef {Number} NameFlags + * @global + */ + /** * Base58 string. * @typedef {String} Base58String @@ -51,15 +57,21 @@ */ /** - * Buffer or hex-string hash. - * @typedef {Buffer|String} Hash + * 32 byte buffer. + * @typedef {Buffer} Hash + * @global + */ + +/** + * Hex-string hash. + * @typedef {String} HexHash * @global */ /** * Signature hash type. One of `all`, `single`, `none`, or * one of {@link constants.hashType}. - * @typedef {String|Number} SighashType + * @typedef {Number} SighashType * @global */ @@ -92,14 +104,29 @@ */ /** - * One of `main`, `testnet`, `regtest`, `segnet3`, `segnet4`. - * @typedef {String} NetworkType - * @see {module:network.types} + * One of `main`, `testnet`, `regtest`, `simnet`. + * @typedef {'main'|'testnet'|'regtest'|'simnet'} NetworkType + * @see {network.types} * @global */ /** * One of `doo`, `uhns`, `mhns`, `hns`, `handshake`. - * @typedef {String} AmountUnitType + * @typedef {'doo'|'uhns'|'mhns'|'hns'|'handshake'} AmountUnitType * @global */ + +/** + * Raw block data. + * @typedef {Buffer} RawBlock + * @global + */ + +/** @typedef {import('bufio').StaticWriter} StaticWriter */ +/** @typedef {import('bufio').BufferWriter} BufferWriter */ + +/** + * @typedef {StaticWriter|BufferWriter} BufioWriter + */ + +module.exports = {}; diff --git a/lib/ui/amount.js b/lib/ui/amount.js index 15ceea9cb..36b486a33 100644 --- a/lib/ui/amount.js +++ b/lib/ui/amount.js @@ -11,6 +11,9 @@ const fixed = require('../utils/fixed'); const {EXP} = require('../protocol/consensus'); const pkg = require('../pkg'); +/** @typedef {import('../types').AmountUnitType} AmountUnitType */ +/** @typedef {import('../types').Amount} AmountValue */ + /** * Amount * Represents a currency amount (base unit internally). @@ -22,7 +25,7 @@ class Amount { /** * Create an amount. * @constructor - * @param {(String|Number)?} value + * @param {(String|Number)?} [value] * @param {AmountUnitType} [unit=doo] */ @@ -35,7 +38,6 @@ class Amount { /** * Inject properties from options. - * @private * @param {String|Number} value * @param {AmountUnitType} [unit=doo] * @returns {Amount} @@ -53,7 +55,7 @@ class Amount { /** * Get base unit value. - * @returns {Amount} + * @returns {AmountValue} */ toValue() { @@ -63,7 +65,7 @@ class Amount { /** * Get base unit string or value. * @param {Boolean} [num=false] - Return a number. - * @returns {String|Amount} + * @returns {String|AmountValue} */ toBase(num) { @@ -76,7 +78,7 @@ class Amount { /** * Get mhns string or value. * @param {Boolean} [num=false] - Return a number. - * @returns {String|Amount} + * @returns {String|AmountValue} */ toMilli(num) { @@ -86,7 +88,7 @@ class Amount { /** * Get currency string or value. * @param {Boolean} [num=false] - Return a number. - * @returns {String|Amount} + * @returns {String|AmountValue} */ toCoins(num) { @@ -97,7 +99,7 @@ class Amount { * Get unit string or value. * @param {AmountUnitType} unit * @param {Boolean} [num=false] - Return a number. - * @returns {String|Amount} + * @returns {String|AmountValue} * @throws on incorrect unit type. */ @@ -117,7 +119,7 @@ class Amount { /** * Convert amount to currency string. - * @returns {String} + * @returns {String|AmountValue} */ toString() { @@ -126,8 +128,7 @@ class Amount { /** * Inject properties from value. - * @private - * @param {Amount} value + * @param {AmountValue} value * @returns {Amount} */ @@ -140,7 +141,6 @@ class Amount { /** * Inject properties from base unit. - * @private * @param {Number|String} value * @returns {Amount} */ @@ -152,7 +152,6 @@ class Amount { /** * Inject properties from mhns. - * @private * @param {Number|String} value * @returns {Amount} */ @@ -164,7 +163,6 @@ class Amount { /** * Inject properties from value. - * @private * @param {Number|String} value * @returns {Amount} */ @@ -176,7 +174,6 @@ class Amount { /** * Inject properties from unit. - * @private * @param {AmountUnitType} unit * @param {Number|String} value * @returns {Amount} @@ -210,8 +207,7 @@ class Amount { /** * Instantiate amount from value. - * @private - * @param {Amount} value + * @param {AmountValue} value * @returns {Amount} */ @@ -273,7 +269,7 @@ class Amount { * Safely convert base unit to a currency string. * This function explicitly avoids any * floating point arithmetic. - * @param {Amount} value - Base unit. + * @param {AmountValue} value - Base unit. * @param {Boolean} [num=false] - Return a number. * @returns {String|Number} Currency string. */ @@ -288,7 +284,7 @@ class Amount { /** * Safely convert a currency string to base unit. * @param {String} str - * @returns {Amount} Base unit. + * @returns {AmountValue} Base unit. * @throws on parse error */ @@ -301,7 +297,7 @@ class Amount { /** * Safely convert base unit to a currency string. - * @param {Amount} value + * @param {AmountValue} value * @param {Number} exp - Exponent. * @param {Boolean} [num=false] - Return a number. * @returns {String|Number} @@ -317,7 +313,7 @@ class Amount { * Safely convert a currency string to base unit. * @param {String|Number} value * @param {Number} exp - Exponent. - * @returns {Amount} Base unit. + * @returns {AmountValue} Base unit. * @throws on parse error */ diff --git a/lib/ui/uri.js b/lib/ui/uri.js index cb0ab1757..e95cf51f0 100644 --- a/lib/ui/uri.js +++ b/lib/ui/uri.js @@ -11,12 +11,25 @@ const Address = require('../primitives/address'); const Amount = require('./amount'); const pkg = require('../pkg'); +/** @typedef {import('../types').Amount} AmountValue */ +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('../protocol/network')} Network */ + +/** + * @typedef {Object} URIOptions + * @property {Address} address + * @property {AmountValue?} [amount] + * @property {String?} [label] + * @property {String?} [message] + * @property {String?} [request] + */ + /** * URI * Represents a handshake URI. * @alias module:ui.URI * @property {Address} address - * @property {Amount} amount + * @property {AmountValue} amount * @property {String|null} label * @property {String|null} message * @property {String|null} request @@ -27,14 +40,17 @@ class URI { * Create a handshake URI. * @alias module:ui.URI * @constructor - * @param {Object|String} options + * @param {(URIOptions|String)?} [options] */ constructor(options) { this.address = new Address(); this.amount = -1; + /** @type {String?} */ this.label = null; + /** @type {String?} */ this.message = null; + /** @type {String?} */ this.request = null; if (options) @@ -43,8 +59,7 @@ class URI { /** * Inject properties from options object. - * @private - * @param {Object|String} options + * @param {URIOptions|String} options * @returns {URI} */ @@ -81,7 +96,7 @@ class URI { /** * Instantiate URI from options. - * @param {Object|String} options + * @param {URIOptions|String} options * @returns {URI} */ @@ -93,7 +108,7 @@ class URI { * Parse and inject properties from string. * @private * @param {String} str - * @param {Network?} network + * @param {(NetworkType|Network)?} [network] * @returns {URI} */ @@ -148,7 +163,7 @@ class URI { /** * Instantiate uri from string. * @param {String} str - * @param {Network?} network + * @param {(NetworkType|Network)?} [network] * @returns {URI} */ diff --git a/lib/utils/binary.js b/lib/utils/binary.js index ad3b19abd..8e4cafcf7 100644 --- a/lib/utils/binary.js +++ b/lib/utils/binary.js @@ -11,7 +11,7 @@ * @param {Array} items * @param {Object} key * @param {Function} compare - * @param {Boolean?} insert + * @param {Boolean} [insert=false] * @returns {Number} Index. */ @@ -43,6 +43,7 @@ exports.search = function search(items, key, compare, insert) { * @param {Array} items * @param {Object} item * @param {Function} compare + * @param {Boolean} [uniq=false] * @returns {Number} index */ @@ -87,6 +88,11 @@ exports.remove = function remove(items, item, compare) { * Helpers */ +/** + * @param {Array} list + * @param {Number} i + */ + function splice(list, i) { if (i === 0) { list.shift(); diff --git a/lib/utils/fixed.js b/lib/utils/fixed.js index 5c3eee094..884fa7c0a 100644 --- a/lib/utils/fixed.js +++ b/lib/utils/fixed.js @@ -28,11 +28,11 @@ exports.encode = function encode(num, exp) { const mult = pow10(exp); - let lo = num % mult; - let hi = (num - lo) / mult; + const nlo = num % mult; + const nhi = (num - nlo) / mult; - lo = lo.toString(10); - hi = hi.toString(10); + let lo = nlo.toString(10); + const hi = nhi.toString(10); while (lo.length < exp) lo = '0' + lo; @@ -100,17 +100,17 @@ exports.decode = function decode(str, exp) { assert(/^\d+$/.test(hi) && /^\d+$/.test(lo), 'Non-numeric characters in fixed number string.'); - hi = parseInt(hi, 10); - lo = parseInt(lo, 10); + const nhi = parseInt(hi, 10); + const nlo = parseInt(lo, 10); const mult = pow10(exp); const maxLo = modSafe(mult); const maxHi = divSafe(mult); - assert(hi < maxHi || (hi === maxHi && lo <= maxLo), + assert(nhi < maxHi || (nhi === maxHi && nlo <= maxLo), 'Fixed number string exceeds 2^53-1.'); - return sign * (hi * mult + lo); + return sign * (nhi * mult + nlo); }; /** diff --git a/lib/utils/hashlist.js b/lib/utils/hashlist.js index a605be033..4430ae3f6 100644 --- a/lib/utils/hashlist.js +++ b/lib/utils/hashlist.js @@ -19,6 +19,10 @@ const DUMMY = Buffer.allocUnsafe(0); */ class HashList { + /** + * @param {Number} size + */ + constructor(size) { assert((size >>> 0) === size); assert((size & 1) === 0); @@ -51,7 +55,12 @@ class HashList { yield this.data.slice(i, i + this.size); } + /** + * @returns {HashList} + */ + clone() { + // @ts-ignore const list = new this.constructor(); list.data = copy(this.data); list.pos = this.pos; diff --git a/lib/utils/util.js b/lib/utils/util.js index e26471793..5da606da5 100644 --- a/lib/utils/util.js +++ b/lib/utils/util.js @@ -14,10 +14,12 @@ const assert = require('bsert'); const util = exports; +/** @typedef {ReturnType} HRTime */ + /** * Return hrtime (shim for browser). - * @param {Array} time - * @returns {Array} [seconds, nanoseconds] + * @param {HRTime?} [time] + * @returns {HRTime|Number} [seconds, nanoseconds] */ util.bench = function bench(time) { @@ -90,7 +92,7 @@ util.time = function time(date) { if (date == null) return util.now(); - return new Date(date) / 1000 | 0; + return Number(new Date(date)) / 1000 | 0; }; /** @@ -101,24 +103,24 @@ util.time = function time(date) { util.hex32 = function hex32(num) { assert((num >>> 0) === num); - num = num.toString(16); - switch (num.length) { + const numStr = num.toString(16); + switch (numStr.length) { case 1: - return `0000000${num}`; + return `0000000${numStr}`; case 2: - return `000000${num}`; + return `000000${numStr}`; case 3: - return `00000${num}`; + return `00000${numStr}`; case 4: - return `0000${num}`; + return `0000${numStr}`; case 5: - return `000${num}`; + return `000${numStr}`; case 6: - return `00${num}`; + return `00${numStr}`; case 7: - return `0${num}`; + return `0${numStr}`; case 8: - return `${num}`; + return `${numStr}`; default: throw new Error(); } diff --git a/lib/wallet/account.js b/lib/wallet/account.js index be18c4d26..582d3a747 100644 --- a/lib/wallet/account.js +++ b/lib/wallet/account.js @@ -512,7 +512,7 @@ class Account extends bio.Struct { * Allocate new lookahead addresses if necessary. * @param {Number} receiveDepth * @param {Number} changeDepth - * @returns {Promise} - Returns {@link WalletKey}. + * @returns {Promise} */ async syncDepth(b, receive, change) { @@ -563,6 +563,8 @@ class Account extends bio.Struct { */ async setLookahead(b, lookahead) { + assert((lookahead >>> 0) === lookahead, 'Lookahead must be a number.'); + if (lookahead === this.lookahead) return; diff --git a/lib/wallet/client.js b/lib/wallet/client.js index c73f14e9c..0f7e3a729 100644 --- a/lib/wallet/client.js +++ b/lib/wallet/client.js @@ -7,15 +7,17 @@ 'use strict'; const assert = require('bsert'); -const {NodeClient} = require('hs-client'); +const NodeClient = require('../client/node'); const TX = require('../primitives/tx'); const Coin = require('../primitives/coin'); const NameState = require('../covenants/namestate'); +const {encoding} = require('bufio'); const parsers = { 'block connect': (entry, txs) => parseBlock(entry, txs), 'block disconnect': entry => [parseEntry(entry)], 'block rescan': (entry, txs) => parseBlock(entry, txs), + 'block rescan interactive': (entry, txs) => parseBlock(entry, txs), 'chain reset': entry => [parseEntry(entry)], 'tx': tx => [TX.decode(tx)] }; @@ -74,10 +76,27 @@ class WalletClient extends NodeClient { return super.setFilter(filter.encode()); } + /** + * Rescan for any missed transactions. + * @param {Number|Hash} start - Start block. + * @returns {Promise} + */ + async rescan(start) { return super.rescan(start); } + /** + * Rescan interactive for any missed transactions. + * @param {Number|Hash} start - Start block. + * @param {Boolean} [fullLock=false] + * @returns {Promise} + */ + + async rescanInteractive(start, fullLock) { + return super.rescanInteractive(start, null, fullLock); + } + async getNameStatus(nameHash) { const json = await super.getNameStatus(nameHash); return NameState.fromJSON(json); @@ -94,6 +113,9 @@ class WalletClient extends NodeClient { */ function parseEntry(data) { + if (!data) + return null; + // 32 hash // 4 height // 4 nonce @@ -112,17 +134,24 @@ function parseEntry(data) { assert(Buffer.isBuffer(data)); // Just enough to read the three data below - assert(data.length >= 44); + assert(data.length >= 80); + + const hash = data.slice(0, 32); + const height = encoding.readU32(data, 32); + const time = encoding.readU64(data, 40); + const prevBlock = data.slice(48, 80); return { - hash: data.slice(0, 32), - height: data.readUInt32LE(32), - time: data.readUInt32LE(40) + hash, + height, + time, + prevBlock }; } function parseBlock(entry, txs) { const block = parseEntry(entry); + assert(block); const out = []; for (const tx of txs) diff --git a/lib/wallet/http.js b/lib/wallet/http.js index 859d77005..f11fcac34 100644 --- a/lib/wallet/http.js +++ b/lib/wallet/http.js @@ -362,6 +362,22 @@ class HTTP extends Server { res.json(200, account.getJSON(balance)); }); + // Modify account + this.patch('/wallet/:id/account/:account', async (req, res) => { + const valid = Validator.fromRequest(req); + const passphrase = valid.str('passphrase'); + const acct = valid.str('account'); + + const options = { + lookahead: valid.u32('lookahead') + }; + + const account = await req.wallet.modifyAccount(acct, options, passphrase); + const balance = await req.wallet.getBalance(account.accountIndex); + + res.json(200, account.getJSON(balance)); + }); + // Change passphrase this.post('/wallet/:id/passphrase', async (req, res) => { const valid = Validator.fromRequest(req); @@ -441,10 +457,9 @@ class HTTP extends Server { // Send TX this.post('/wallet/:id/send', async (req, res) => { const valid = Validator.fromRequest(req); - const passphrase = valid.str('passphrase'); const options = TransactionOptions.fromValidator(valid); - const tx = await req.wallet.send(options, passphrase); + const tx = await req.wallet.send(options); const details = await req.wallet.getDetails(tx.hash()); @@ -454,16 +469,22 @@ class HTTP extends Server { // Create TX this.post('/wallet/:id/create', async (req, res) => { const valid = Validator.fromRequest(req); - const passphrase = valid.str('passphrase'); const sign = valid.bool('sign', true); + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. const options = TransactionOptions.fromValidator(valid); const tx = await req.wallet.createTX(options); if (sign) - await req.wallet.sign(tx, passphrase); + await req.wallet.sign(tx, options.passphrase); - res.json(200, tx.getJSON(this.network)); + const json = tx.getJSON(this.network); + + if (options.paths) + await this.addOutputPaths(json, tx, req.wallet); + + res.json(200, json); }); // Sign TX @@ -838,8 +859,8 @@ class HTTP extends Server { const valid = Validator.fromRequest(req); const name = valid.str('name'); - assert(name, 'Must pass name.'); - assert(rules.verifyName(name), 'Must pass valid name.'); + enforce(name, 'Must pass name.'); + enforce(rules.verifyName(name), 'Must pass valid name.'); const height = this.wdb.height; const network = this.network; @@ -884,8 +905,8 @@ class HTTP extends Server { const valid = Validator.fromRequest(req); const name = valid.str('name'); - assert(name, 'Must pass name.'); - assert(rules.verifyName(name), 'Must pass valid name.'); + enforce(name, 'Must pass name.'); + enforce(rules.verifyName(name), 'Must pass valid name.'); const height = this.wdb.height; const network = this.network; @@ -934,7 +955,7 @@ class HTTP extends Server { let own = valid.bool('own', false); if (name) - assert(rules.verifyName(name), 'Must pass valid name.'); + enforce(rules.verifyName(name), 'Must pass valid name.'); if (!name) own = true; @@ -973,7 +994,7 @@ class HTTP extends Server { let own = valid.bool('own', false); if (name) - assert(rules.verifyName(name), 'Must pass valid name.'); + enforce(rules.verifyName(name), 'Must pass valid name.'); if (!name) own = true; @@ -994,8 +1015,8 @@ class HTTP extends Server { const valid = Validator.fromRequest(req); const name = valid.str('name'); - assert(name, 'Must pass name.'); - assert(rules.verifyName(name), 'Must pass valid name.'); + enforce(name, 'Must pass name.'); + enforce(rules.verifyName(name), 'Must pass valid name.'); const ns = await req.wallet.getNameStateByName(name); @@ -1017,10 +1038,10 @@ class HTTP extends Server { const addr = valid.str('address'); const bid = valid.ufixed('bid'); - assert(name, 'Name is required.'); - assert(rules.verifyName(name), 'Valid name is required.'); - assert(addr, 'Address is required.'); - assert(bid != null, 'Bid is required.'); + enforce(name, 'Name is required.'); + enforce(rules.verifyName(name), 'Valid name is required.'); + enforce(addr, 'Address is required.'); + enforce(bid != null, 'Bid is required.'); let address; try { @@ -1047,26 +1068,33 @@ class HTTP extends Server { this.post('/wallet/:id/open', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); - const force = valid.bool('force', false); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(name, 'Name is required.'); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Name is required.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createOpen(name, force, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendOpen(name, options); return res.json(200, tx.getJSON(this.network)); } + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. + const mtx = await req.wallet.createOpen(name, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); + + const json = mtx.getJSON(this.network); - return res.json(200, mtx.getJSON(this.network)); + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); + + return res.json(200, json); }); // Create Bid @@ -1075,27 +1103,35 @@ class HTTP extends Server { const name = valid.str('name'); const bid = valid.u64('bid'); const lockup = valid.u64('lockup'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(name, 'Name is required.'); - assert(bid != null, 'Bid is required.'); - assert(lockup != null, 'Lockup is required.'); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Name is required.'); + enforce(bid != null, 'Bid is required.'); + enforce(lockup != null, 'Lockup is required.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createBid(name, bid, lockup, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendBid(name, bid, lockup, options); return res.json(200, tx.getJSON(this.network)); } + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. + const mtx = await req.wallet.createBid(name, bid, lockup, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); + + const json = mtx.getJSON(this.network); + + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); - return res.json(200, mtx.getJSON(this.network)); + return res.json(200, json); }); // Create auction-related transactions in advance (bid and reveal for now) @@ -1108,32 +1144,41 @@ class HTTP extends Server { const sign = valid.bool('sign', true); const broadcastBid = valid.bool('broadcastBid'); - assert(name, 'Name is required.'); - assert(bid != null, 'Bid is required.'); - assert(lockup != null, 'Lockup is required.'); - assert(broadcastBid != null, 'broadcastBid is required.'); - assert(broadcastBid ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Name is required.'); + enforce(bid != null, 'Bid is required.'); + enforce(lockup != null, 'Lockup is required.'); + enforce(broadcastBid != null, 'broadcastBid is required.'); + enforce(broadcastBid ? sign : true, 'Must sign when broadcasting.'); const options = TransactionOptions.fromValidator(valid); - const auctionTxs = await req.wallet.createAuctionTxs( + const auctionTXs = await req.wallet.createAuctionTXs( name, bid, lockup, options ); + if (broadcastBid) + auctionTXs.bid = await req.wallet.sendMTX(auctionTXs.bid, passphrase); + if (sign) { - if (broadcastBid) { - auctionTxs.bid = await req.wallet.sendMTX(auctionTxs.bid, passphrase); - } else { - await req.wallet.sign(auctionTxs.bid, passphrase); - } - await req.wallet.sign(auctionTxs.reveal, passphrase); + if (!broadcastBid) + await req.wallet.sign(auctionTXs.bid, passphrase); + + await req.wallet.sign(auctionTXs.reveal, passphrase); + } + + const jsonBid = auctionTXs.bid.getJSON(this.network); + const jsonReveal = auctionTXs.reveal.getJSON(this.network); + + if (options.paths) { + await this.addOutputPaths(jsonBid, auctionTXs.bid, req.wallet); + await this.addOutputPaths(jsonReveal, auctionTXs.reveal, req.wallet); } return res.json(200, { - bid: auctionTxs.bid.getJSON(this.network), - reveal: auctionTxs.reveal.getJSON(this.network) + bid: jsonBid, + reveal: jsonReveal }); }); @@ -1141,60 +1186,92 @@ class HTTP extends Server { this.post('/wallet/:id/reveal', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); - - if (!name) { - const tx = await req.wallet.sendRevealAll(); - return res.json(200, tx.getJSON(this.network)); - } + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createReveal(name, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + let tx; + + if (name) { + // TODO: Add abort signal to close when request closes. + tx = await req.wallet.sendReveal(name, options); + } else { + // TODO: Add abort signal to close when request closes. + tx = await req.wallet.sendRevealAll(options); + } + return res.json(200, tx.getJSON(this.network)); } + let mtx; + + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. + if (name) { + mtx = await req.wallet.createReveal(name, options); + } else { + mtx = await req.wallet.createRevealAll(options); + } + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); - return res.json(200, mtx.getJSON(this.network)); + const json = mtx.getJSON(this.network); + + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); + + return res.json(200, json); }); // Create Redeem this.post('/wallet/:id/redeem', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); const options = TransactionOptions.fromValidator(valid); + if (broadcast) { + let tx; + + if (name) { + // TODO: Add abort signal to close when request closes. + tx = await req.wallet.sendRedeem(name, options); + } else { + // TODO: Add abort signal to close when request closes. + tx = await req.wallet.sendRedeemAll(options); + } + + return res.json(200, tx.getJSON(this.network)); + } + let mtx; + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. if (!name) { mtx = await req.wallet.createRedeemAll(options); } else { mtx = await req.wallet.createRedeem(name, options); } - if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); - return res.json(200, tx.getJSON(this.network)); - } - if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); + + const json = mtx.getJSON(this.network); - return res.json(200, mtx.getJSON(this.network)); + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); + + return res.json(200, json); }); // Create Update @@ -1202,13 +1279,12 @@ class HTTP extends Server { const valid = Validator.fromRequest(req); const name = valid.str('name'); const data = valid.obj('data'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); - assert(name, 'Must pass name.'); - assert(data, 'Must pass data.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Must pass name.'); + enforce(data, 'Must pass data.'); let resource; try { @@ -1218,42 +1294,57 @@ class HTTP extends Server { } const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createUpdate(name, resource, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendUpdate(name, resource, options); return res.json(200, tx.getJSON(this.network)); } + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. + const mtx = await req.wallet.createUpdate(name, resource, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); - return res.json(200, mtx.getJSON(this.network)); + const json = mtx.getJSON(this.network); + + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); + + return res.json(200, json); }); // Create Renewal this.post('/wallet/:id/renewal', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); - assert(name, 'Must pass name.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Must pass name.'); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createRenewal(name, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendRenewal(name, options); return res.json(200, tx.getJSON(this.network)); } + const mtx = await req.wallet.createRenewal(name, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); + + const json = mtx.getJSON(this.network); - return res.json(200, mtx.getJSON(this.network)); + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); + + return res.json(200, json); }); // Create Transfer @@ -1261,105 +1352,154 @@ class HTTP extends Server { const valid = Validator.fromRequest(req); const name = valid.str('name'); const address = valid.str('address'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); - assert(name, 'Must pass name.'); - assert(address, 'Must pass address.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Must pass name.'); + enforce(address, 'Must pass address.'); const addr = Address.fromString(address, this.network); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createTransfer(name, addr, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendTransfer(name, addr, options); return res.json(200, tx.getJSON(this.network)); } + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. + const mtx = await req.wallet.createTransfer(name, addr, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); + + const json = mtx.getJSON(this.network); + + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); - return res.json(200, mtx.getJSON(this.network)); + return res.json(200, json); }); // Create Cancel this.post('/wallet/:id/cancel', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); - assert(name, 'Must pass name.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Must pass name.'); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createCancel(name, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendCancel(name, options); return res.json(200, tx.getJSON(this.network)); } + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. + const mtx = await req.wallet.createCancel(name, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); - return res.json(200, mtx.getJSON(this.network)); + const json = mtx.getJSON(this.network); + + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); + + return res.json(200, json); }); // Create Finalize this.post('/wallet/:id/finalize', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); - assert(name, 'Must pass name.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Must pass name.'); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createFinalize(name, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendFinalize(name, options); return res.json(200, tx.getJSON(this.network)); } + const mtx = await req.wallet.createFinalize(name, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); + + const json = mtx.getJSON(this.network); - return res.json(200, mtx.getJSON(this.network)); + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); + + return res.json(200, json); }); // Create Revoke this.post('/wallet/:id/revoke', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); - const passphrase = valid.str('passphrase'); const broadcast = valid.bool('broadcast', true); const sign = valid.bool('sign', true); - assert(broadcast ? sign : true, 'Must sign when broadcasting.'); - assert(name, 'Must pass name.'); + enforce(broadcast ? sign : true, 'Must sign when broadcasting.'); + enforce(name, 'Must pass name.'); const options = TransactionOptions.fromValidator(valid); - const mtx = await req.wallet.createRevoke(name, options); if (broadcast) { - const tx = await req.wallet.sendMTX(mtx, passphrase); + // TODO: Add abort signal to close when request closes. + const tx = await req.wallet.sendRevoke(name, options); return res.json(200, tx.getJSON(this.network)); } + // TODO: Add create TX with locks for used Coins and/or + // adds to the pending list. + const mtx = await req.wallet.createRevoke(name, options); + if (sign) - await req.wallet.sign(mtx, passphrase); + await req.wallet.sign(mtx, options.passphrase); + + const json = mtx.getJSON(this.network); + + if (options.paths) + await this.addOutputPaths(json, mtx, req.wallet); - return res.json(200, mtx.getJSON(this.network)); + return res.json(200, json); }); } + /** + * Add wallet path information to JSON outputs + * @private + */ + + async addOutputPaths(json, tx, wallet) { + for (let i = 0; i < tx.outputs.length; i++) { + const {address} = tx.outputs[i]; + const path = await wallet.getPath(address); + + if (!path) + continue; + + json.outputs[i].path = path.getJSON(this.network); + } + + return json; + } + /** * Initialize websockets. * @private @@ -1714,6 +1854,8 @@ class TransactionOptions { this.subtractIndex = valid.i32('subtractIndex'); this.depth = valid.u32(['confirmations', 'depth']); this.paths = valid.bool('paths'); + this.passphrase = valid.str('passphrase'); + this.hardFee = valid.u64('hardFee'), this.outputs = []; if (valid.has('outputs')) { diff --git a/lib/wallet/layout.js b/lib/wallet/layout.js index 98ea3cdab..11059d3b8 100644 --- a/lib/wallet/layout.js +++ b/lib/wallet/layout.js @@ -10,97 +10,161 @@ const bdb = require('bdb'); /* * Wallet Database Layout: + * WDB State + * --------- * V -> db version * O -> flags - * R -> chain sync state * D -> wallet id depth - * p[addr-hash] -> wallet ids - * P[wid][addr-hash] -> path data - * r[wid][index][hash] -> path account index + * M -> migration state + * + * Chain Sync + * ---------- + * R -> chain sync state + * h[height] -> block hash + * + * WID mappings + * -------- + * b[height] -> block->wid map + * T[tx-hash] -> tx->wid map + * o[tx-hash][index] -> outpoint->wid map + * p[addr-hash] -> address->wid map + * N[name-hash] -> name->wid map + * + * Wallet + * ------ + * l[id] -> wid * w[wid] -> wallet * W[wid] -> wallet id - * l[id] -> wid + * + * Wallet Account + * -------------- * a[wid][index] -> account * i[wid][name] -> account index * n[wid][index] -> account name - * h[height] -> recent block hash - * b[height] -> block->wid map - * o[hash][index] -> outpoint->wid map - * T[hash] -> tx->wid map + * + * Wallet Path + * ----------- + * P[wid][addr-hash] -> path data + * r[wid][index][addr-hash] -> dummy (addr by account) + * + * TXDB + * ---- * t[wid]* -> txdb - * N[hash256] -> name map - * M -> migration state */ exports.wdb = { + // WDB State V: bdb.key('V'), O: bdb.key('O'), - R: bdb.key('R'), D: bdb.key('D'), + M: bdb.key('M'), + + // Chain Sync + R: bdb.key('R'), + h: bdb.key('h', ['uint32']), + + // WID Mappings + b: bdb.key('b', ['uint32']), + T: bdb.key('T', ['hash256']), p: bdb.key('p', ['hash']), - P: bdb.key('P', ['uint32', 'hash']), - r: bdb.key('r', ['uint32', 'uint32', 'hash']), + o: bdb.key('o', ['hash256', 'uint32']), + N: bdb.key('N', ['hash256']), + + // Wallet + l: bdb.key('l', ['ascii']), w: bdb.key('w', ['uint32']), W: bdb.key('W', ['uint32']), - l: bdb.key('l', ['ascii']), + + // Wallet Account a: bdb.key('a', ['uint32', 'uint32']), i: bdb.key('i', ['uint32', 'ascii']), n: bdb.key('n', ['uint32', 'uint32']), - h: bdb.key('h', ['uint32']), - b: bdb.key('b', ['uint32']), - o: bdb.key('o', ['hash256', 'uint32']), - T: bdb.key('T', ['hash256']), - t: bdb.key('t', ['uint32']), - N: bdb.key('N', ['hash256']), - M: bdb.key('M') + + // Wallet Path + P: bdb.key('P', ['uint32', 'hash']), + r: bdb.key('r', ['uint32', 'uint32', 'hash']), + + // TXDB + t: bdb.key('t', ['uint32']) }; /* * TXDB Database Layout: + * Balance + * ------- * R -> wallet balance * r[account] -> account balance - * t[hash] -> extended tx - * c[hash][index] -> coin - * d[hash][index] -> undo coin - * s[hash][index] -> spent by hash - * p[hash] -> dummy (pending flag) - * m[time][hash] -> dummy (tx by time) - * h[height][hash] -> dummy (tx by height) - * T[account][hash] -> dummy (tx by account) - * P[account][hash] -> dummy (pending tx by account) - * M[account][time][hash] -> dummy (tx by time + account) - * H[account][height][hash] -> dummy (tx by height + account) - * C[account][hash][index] -> dummy (coin by account) + * + * Coin + * ---- + * c[tx-hash][index] -> coin + * C[account][tx-hash][index] -> dummy (coin by account) + * d[tx-hash][index] -> undo coin + * s[tx-hash][index] -> spent by hash + * + * Transaction + * ----------- + * t[tx-hash] -> extended tx + * T[account][tx-hash] -> dummy (tx by account) + * m[time][tx-hash] -> dummy (tx by time) + * M[account][time][tx-hash] -> dummy (tx by time + account) + * + * Confirmed + * --------- * b[height] -> block record + * h[height][tx-hash] -> dummy (tx by height) + * H[account][height][tx-hash] -> dummy (tx by height + account) + * + * Unconfirmed + * ----------- + * p[hash] -> dummy (pending tx) + * P[account][tx-hash] -> dummy (pending tx by account) + * + * Names + * ----- + * A[name-hash] -> name record (name record by name hash) + * U[tx-hash] -> name undo record (name undo record by tx hash) + * i[name-hash][tx-hash][index] -> bid (BlindBid by name + tx + index) + * B[name-hash][tx-hash][index] -> reveal (BidReveal by name + tx + index) + * E[name-hash][tx-hash][index] - bid to reveal out (by bid txhash + index) + * v[blind-hash] -> blind (Blind Value by blind hash) + * o[name-hash] -> tx hash OPEN only (tx hash by name hash) */ exports.txdb = { prefix: bdb.key('t', ['uint32']), + + // Balance R: bdb.key('R'), r: bdb.key('r', ['uint32']), - t: bdb.key('t', ['hash256']), + + // Coin c: bdb.key('c', ['hash256', 'uint32']), + C: bdb.key('C', ['uint32', 'hash256', 'uint32']), d: bdb.key('d', ['hash256', 'uint32']), s: bdb.key('s', ['hash256', 'uint32']), - p: bdb.key('p', ['hash256']), - m: bdb.key('m', ['uint32', 'hash256']), - h: bdb.key('h', ['uint32', 'hash256']), + + // Transaction + t: bdb.key('t', ['hash256']), T: bdb.key('T', ['uint32', 'hash256']), - P: bdb.key('P', ['uint32', 'hash256']), + m: bdb.key('m', ['uint32', 'hash256']), M: bdb.key('M', ['uint32', 'uint32', 'hash256']), - H: bdb.key('H', ['uint32', 'uint32', 'hash256']), - C: bdb.key('C', ['uint32', 'hash256', 'uint32']), + + // Confirmed b: bdb.key('b', ['uint32']), - // Name records + h: bdb.key('h', ['uint32', 'hash256']), + H: bdb.key('H', ['uint32', 'uint32', 'hash256']), + + // Unconfirmed + p: bdb.key('p', ['hash256']), + P: bdb.key('P', ['uint32', 'hash256']), + + // Names A: bdb.key('A', ['hash256']), - // Name undo records U: bdb.key('U', ['hash256']), - // Bids i: bdb.key('i', ['hash256', 'hash256', 'uint32']), - // Reveals B: bdb.key('B', ['hash256', 'hash256', 'uint32']), - // Blinds + E: bdb.key('E', ['hash256', 'hash256', 'uint32']), v: bdb.key('v', ['hash256']), - // Opens o: bdb.key('o', ['hash256']) }; diff --git a/lib/wallet/migrations.js b/lib/wallet/migrations.js index 1964c50b3..9b49bd4c9 100644 --- a/lib/wallet/migrations.js +++ b/lib/wallet/migrations.js @@ -8,7 +8,19 @@ const assert = require('bsert'); const Logger = require('blgr'); +const bdb = require('bdb'); +const bio = require('bufio'); +const {HDPublicKey} = require('../hd/hd'); +const binary = require('../utils/binary'); +const {encoding} = bio; const Network = require('../protocol/network'); +const Account = require('./account'); +const WalletKey = require('./walletkey'); +const Path = require('./path'); +const Script = require('../script/script'); +const MapRecord = require('./records').MapRecord; +const Outpoint = require('../primitives/outpoint'); +const TX = require('../primitives/tx'); const AbstractMigration = require('../migrations/migration'); const { MigrationResult, @@ -16,9 +28,11 @@ const { types, oldLayout } = require('../migrations/migrator'); -const MigrationState = require('../migrations/state'); const layouts = require('./layout'); -const layout = layouts.wdb; +const wlayout = layouts.wdb; + +/** @typedef {import('bdb').DB} DB */ +/** @typedef {import('./walletdb')} WalletDB */ /** * Switch to new migrations layout. @@ -37,7 +51,7 @@ class MigrateMigrations extends AbstractMigration { this.logger = options.logger.context('wallet-migrations-migrate'); this.db = options.db; this.ldb = options.ldb; - this.layout = options.layout; + this.layout = MigrateMigrations.layout(); } async check() { @@ -46,22 +60,33 @@ class MigrateMigrations extends AbstractMigration { /** * Actual migration - * @param {Batch} b + * @param {DB.Batch} b * @returns {Promise} */ async migrate(b) { this.logger.info('Migrating migrations..'); - const state = new MigrationState(); - state.nextMigration = 1; + let nextMigration = 1; - if (await this.ldb.get(oldLayout.M.encode(0))) { - b.del(oldLayout.M.encode(0)); - state.nextMigration = 2; + if (await this.ldb.get(this.layout.oldLayout.wdb.M.encode(0))) { + b.del(this.layout.oldLayout.wdb.M.encode(0)); + nextMigration = 2; } this.db.writeVersion(b, 1); - b.put(this.layout.M.encode(), state.encode()); + b.put( + this.layout.newLayout.wdb.M.encode(), + this.encodeMigrationState(nextMigration) + ); + } + + encodeMigrationState(nextMigration) { + const size = 4 + 1 + 1; + const encoded = Buffer.alloc(size); + + encoding.writeVarint(encoded, nextMigration, 4); + + return encoded; } static info() { @@ -70,6 +95,21 @@ class MigrateMigrations extends AbstractMigration { description: 'Wallet migration layout has changed.' }; } + + static layout() { + return { + oldLayout: { + wdb: { + M: bdb.key('M', ['uint32']) + } + }, + newLayout: { + wdb: { + M: bdb.key('M') + } + } + }; + } } /** @@ -91,6 +131,7 @@ class MigrateChangeAddress extends AbstractMigration { this.logger = options.logger.context('change-address-migration'); this.db = options.db; this.ldb = options.ldb; + this.layout = MigrateChangeAddress.layout(); } /** @@ -110,37 +151,76 @@ class MigrateChangeAddress extends AbstractMigration { */ async migrate(b, pending) { + const wlayout = this.layout.wdb; const wids = await this.ldb.keys({ - gte: layout.W.min(), - lte: layout.W.max(), - parse: key => layout.W.decode(key)[0] + gte: wlayout.W.min(), + lte: wlayout.W.max(), + parse: key => wlayout.W.decode(key)[0] }); let total = 0; for (const wid of wids) { - const wallet = await this.db.get(wid); - - this.logger.info('Checking wallet (id=%s, wid=%d).', - wallet.id, wid); - - total += await this.migrateWallet(b, wallet); + this.logger.info('Checking wallet (wid=%d).', wid); + total += await this.migrateWallet(b, wid); } if (total > 0) pending.rescan = true; } - async migrateWallet(b, wallet) { + async migrateWallet(b, wid) { + const accounts = this.ldb.iterator({ + gte: this.layout.wdb.a.min(wid), + lte: this.layout.wdb.a.max(wid), + values: true + }); + let total = 0; - for (let i = 0; i < wallet.accountDepth; i++) { - const account = await wallet.getAccount(i); + for await (const {key, value} of accounts) { + const [awid, aindex] = this.layout.wdb.a.decode(key); + const name = await this.ldb.get(this.layout.wdb.n.encode(wid, aindex)); + assert(awid === wid); + const br = bio.read(value); + const initialized = br.readU8(); + + if (!initialized) + continue; + + const type = br.readU8(); + const m = br.readU8(); + const n = br.readU8(); + br.seek(4); // skip receive + const changeDepth = br.readU32(); + const lookahead = br.readU8(); + const accountKey = this.readKey(br); + const count = br.readU8(); + assert(br.left() === count * 74); + + const keys = []; + + for (let i = 0; i < count; i++) { + const key = this.readKey(br); + const cmp = (a, b) => a.compare(b); + binary.insert(keys, key, cmp, true); + } + + for (let i = 0; i < changeDepth + lookahead; i++) { + const key = this.deriveKey({ + accountName: name, + accountIndex: aindex, + accountKey: accountKey, + type: type, + m: m, + n: n, + branch: 1, + index: i, + keys: keys + }); - for (let j = 0; j < account.changeDepth + account.lookahead; j++) { - const key = account.deriveChange(j); const path = key.toPath(); - if (!await this.db.hasPath(account.wid, path.hash)) { - await this.db.savePath(b, account.wid, path); + if (!await this.hasPath(wid, path.hash)) { + await this.savePath(b, wid, path); total += 1; } } @@ -149,6 +229,63 @@ class MigrateChangeAddress extends AbstractMigration { return total; } + deriveKey(options) { + const key = options.accountKey.derive(options.branch).derive(options.index); + const wkey = new WalletKey(); + wkey.keyType = Path.types.HD; + wkey.name = options.accountName; + wkey.account = options.accountIndex; + wkey.branch = options.branch; + wkey.index = options.index; + wkey.publicKey = key.publicKey; + + const keys = []; + switch (options.type) { + case Account.types.PUBKEYHASH: + break; + + case Account.types.MULTISIG: + keys.push(wkey.publicKey); + + for (const shared of options.keys) { + const key = shared.derive(options.branch).derive(options.index); + keys.push(key.publicKey); + } + + wkey.script = Script.fromMultisig(options.m, options.n, keys); + + break; + } + + return wkey; + } + + readKey(br) { + const key = new HDPublicKey(); + key.depth = br.readU8(); + key.parentFingerPrint = br.readU32BE(); + key.childIndex = br.readU32BE(); + key.chainCode = br.readBytes(32); + key.publicKey = br.readBytes(33); + return key; + } + + async hasPath(wid, hash) { + return this.ldb.has(this.layout.wdb.P.encode(wid, hash)); + } + + async savePath(b, wid, path) { + const wlayout = this.layout.wdb; + + const data = await this.ldb.get(wlayout.p.encode(path.hash)); + const map = data ? MapRecord.decode(data) : new MapRecord(); + + map.add(wid); + b.put(wlayout.p.encode(path.hash), map.encode()); + b.put(wlayout.P.encode(wid, path.hash), path.encode()); + b.put(wlayout.r.encode(wid, path.account, path.hash), null); + } + /** * Return info about the migration. * @returns {String} @@ -160,6 +297,35 @@ class MigrateChangeAddress extends AbstractMigration { description: 'Wallet is corrupted.' }; } + + /** + * Get layout that migration is going to affect. + * @returns {Object} + */ + + static layout() { + return { + wdb: { + // W[wid] -> wallet id + W: bdb.key('W', ['uint32']), + + // n[wid][index] -> account name + n: bdb.key('n', ['uint32', 'uint32']), + + // a[wid][index] -> account + a: bdb.key('a', ['uint32', 'uint32']), + + // p[addr-hash] -> address->wid map + p: bdb.key('p', ['hash']), + + // P[wid][addr-hash] -> path data + P: bdb.key('P', ['uint32', 'hash']), + + // r[wid][index][addr-hash] -> dummy (addr by account) + r: bdb.key('r', ['uint32', 'uint32', 'hash']) + } + }; + } } /** @@ -180,6 +346,7 @@ class MigrateAccountLookahead extends AbstractMigration { this.logger = options.logger.context('account-lookahead-migration'); this.db = options.db; this.ldb = options.ldb; + this.layout = MigrateAccountLookahead.layout(); } /** @@ -197,10 +364,11 @@ class MigrateAccountLookahead extends AbstractMigration { */ async migrate(b) { + const wlayout = this.layout.wdb; const wids = await this.ldb.keys({ - gte: layout.W.min(), - lte: layout.W.max(), - parse: key => layout.W.decode(key)[0] + gte: wlayout.W.min(), + lte: wlayout.W.max(), + parse: key => wlayout.W.decode(key)[0] }); for (const wid of wids) @@ -210,14 +378,15 @@ class MigrateAccountLookahead extends AbstractMigration { } async migrateWallet(b, wid) { + const wlayout = this.layout.wdb; const accounts = await this.ldb.keys({ - gte: layout.a.min(wid), - lte: layout.a.max(wid), - parse: key => layout.a.decode(key)[1] + gte: wlayout.a.min(wid), + lte: wlayout.a.max(wid), + parse: key => wlayout.a.decode(key)[1] }); for (const accID of accounts) { - const key = layout.a.encode(wid, accID); + const key = wlayout.a.encode(wid, accID); const rawAccount = await this.ldb.get(key); const newRaw = this.accountEncode(rawAccount); b.put(key, newRaw); @@ -232,7 +401,7 @@ class MigrateAccountLookahead extends AbstractMigration { const post = raw.slice(preLen + 1); const newLookahead = Buffer.alloc(4, 0x00); - newLookahead.writeUInt32LE(lookahead[0], 0); + encoding.writeU32(newLookahead, lookahead[0], 0); return Buffer.concat([ pre, @@ -247,6 +416,305 @@ class MigrateAccountLookahead extends AbstractMigration { description: 'Account lookahead now supports up to 2^32 - 1' }; } + + static layout() { + return { + wdb: { + // W[wid] -> wallet id + W: bdb.key('W', ['uint32']), + + // a[wid][index] -> account + a: bdb.key('a', ['uint32', 'uint32']) + } + }; + } +} + +class MigrateTXDBBalances extends AbstractMigration { + /** + * Create TXDB Balance migration object. + * @param {WalletMigratorOptions} options + * @constructor + */ + + constructor(options) { + super(options); + + this.options = options; + this.logger = options.logger.context('txdb-balance-migration'); + this.db = options.db; + this.ldb = options.ldb; + } + + /** + * We always migrate. + * @returns {Promise} + */ + + async check() { + return types.MIGRATE; + } + + /** + * Actual migration + * @param {Batch} b + * @param {WalletMigrationResult} pending + * @returns {Promise} + */ + + async migrate(b, pending) { + pending.recalculateTXDB = true; + } + + static info() { + return { + name: 'TXDB balance refresh', + description: 'Refresh balances for TXDB after txdb updates' + }; + } +} + +/** + * Applies to WalletDB v2 + * Migrate bid reveal entries. + * - Adds height to the blind bid entries. + * - NOTE: This can not be recovered if the bid is not owned by the wallet. + * Wallet does not store transactions for not-owned bids. + * - Add Bid Outpoint information to the reveal (BidReveal) entries. + * - NOTE: This information can not be recovered for not-owned reveals. + * Wallet does not store transactions for not-owned reveals. + * - Add new BID -> REVEAL index. (layout.E) + * - NOTE: This information can not be recovered for not-owned reveals. + * Wallet does not store transactions for not-owned reveals. + * + */ + +class MigrateBidRevealEntries extends AbstractMigration { + /** + * Create Bid Reveal Entries migration object. + * @param {WalletMigratorOptions} options + * @constructor + */ + + constructor(options) { + super(options); + + this.options = options; + this.logger = options.logger.context('bid-reveal-entries-migration'); + this.db = options.db; + this.ldb = options.ldb; + this.layout = MigrateBidRevealEntries.layout(); + } + + /** + * We always migrate. + * @returns {Promise} + */ + + async check() { + return types.MIGRATE; + } + + /** + * Actual migration + * @param {DB.Batch} b + * @param {WalletMigrationResult} pending + * @returns {Promise} + */ + + async migrate(b, pending) { + /** @type {Number[]} */ + const wids = await this.ldb.keys({ + gte: wlayout.W.min(), + lte: wlayout.W.max(), + parse: key => wlayout.W.decode(key)[0] + }); + + for (const wid of wids) { + await this.migrateReveals(wid); + await this.migrateBids(wid); + } + + this.db.writeVersion(b, 3); + } + + /** + * Migrate reveals and index Bid2Reveal + * @param {Number} wid + * @returns {Promise} + */ + + async migrateReveals(wid) { + const txlayout = this.layout.txdb; + const prefix = txlayout.prefix.encode(wid); + const bucket = this.ldb.bucket(prefix); + const emptyOutpoint = new Outpoint(); + + const reveals = bucket.iterator({ + gte: txlayout.B.min(), + lte: txlayout.B.max(), + values: true + }); + + for await (const {key, value} of reveals) { + const b = bucket.batch(); + const [nameHash, txHash, txIndex] = txlayout.B.decode(key); + const nameLen = value[0]; + const totalOld = nameLen + 1 + 13; + const totalNew = nameLen + 1 + 13 + 36; + + // allow migration to be interrupted in the middle. + assert(value.length === totalOld || value.length === totalNew); + + // skip if already migrated. + if (value.length === totalNew) + continue; + + const owned = value[nameLen + 1 + 12]; + const rawTXRecord = await bucket.get(txlayout.t.encode(txHash)); + assert(owned && rawTXRecord || !owned); + + // We can not index the bid link and bid2reveal index if + // the transaction is not owned by the wallet. + // But we need to put null outpoint to the reveal for serialization. + if (!owned) { + const newReveal = Buffer.concat([value, emptyOutpoint.encode()]); + assert(newReveal.length === totalNew); + b.put(key, newReveal); + await b.write(); + continue; + } + + const reader = bio.read(rawTXRecord); + const tx = TX.fromReader(reader); + assert(tx.inputs[txIndex]); + + const bidPrevout = tx.inputs[txIndex].prevout; + const bidKey = txlayout.i.encode( + nameHash, bidPrevout.hash, bidPrevout.index); + const bidRecord = await bucket.get(bidKey); + // ensure bid exists. + assert(bidRecord); + + const newReveal = Buffer.concat([value, bidPrevout.encode()]); + assert(newReveal.length === totalNew); + // update reveal with bid outpoint. + b.put(key, newReveal); + // index bid to reveal. + b.put(txlayout.E.encode(nameHash, bidPrevout.hash, bidPrevout.index), + (new Outpoint(txHash, txIndex)).encode()); + await b.write(); + } + } + + /** + * Migrate bids, add height to the entries. + * @param {Number} wid + * @returns {Promise} + */ + + async migrateBids(wid) { + const txlayout = this.layout.txdb; + const prefix = txlayout.prefix.encode(wid); + const bucket = this.ldb.bucket(prefix); + + const bids = bucket.iterator({ + gte: txlayout.i.min(), + lte: txlayout.i.max(), + values: true + }); + + /** + * @param {Buffer} blindBid + * @param {Number} height + * @returns {Buffer} + */ + + const reencodeBlindBid = (blindBid, height) => { + const nameLen = blindBid[0]; + const totalOld = nameLen + 1 + 41; + const totalNew = nameLen + 1 + 41 + 4; + assert(blindBid.length === totalOld); + + const newBlindBid = Buffer.alloc(totalNew); + // copy everything before expected height place. + blindBid.copy(newBlindBid, 0, 0, totalOld - 1); + // copy height. + bio.encoding.writeU32(newBlindBid, height, totalOld - 1); + // copy last byte (owned flag). + blindBid.copy(newBlindBid, totalNew - 1, totalOld - 1); + + return newBlindBid; + }; + + for await (const {key, value} of bids) { + const b = bucket.batch(); + const [,txHash] = txlayout.i.decode(key); + const nameLen = value[0]; + const totalNew = nameLen + 1 + 41 + 4; + + // allow migration to be interrupted in the middle. + if (totalNew === value.length) + continue; + + const owned = value[nameLen + 1 + 40]; + if (!owned) { + const height = 0xffffffff; // -1 + const newValue = reencodeBlindBid(value, height); + b.put(key, newValue); + await b.write(); + continue; + } + + const rawTXRecord = await bucket.get(txlayout.t.encode(txHash)); + assert(rawTXRecord); + + const br = bio.read(rawTXRecord); + TX.fromReader(br); + // skip mtime. + br.seek(4); + + const hasBlock = br.readU8() === 1; + // We only index the bid in blocks, not in mempool. + assert(hasBlock); + + // skip hash. + br.seek(32); + const height = br.readU32(); + const newValue = reencodeBlindBid(value, height); + b.put(key, newValue); + + await b.write(); + } + } + + static info() { + return { + name: 'Bid reveal entries migration', + description: 'Migrate bids and reveals to link each other.' + }; + } + + static layout() { + return { + wdb: { + V: bdb.key('V'), + // W[wid] -> wallet id + W: bdb.key('W', ['uint32']) + }, + txdb: { + prefix: bdb.key('t', ['uint32']), + // t[tx-hash] -> extended tx (Read only) + t: bdb.key('t', ['hash256']), + // i[name-hash][tx-hash][index] -> txdb.BlindBid + i: bdb.key('i', ['hash256', 'hash256', 'uint32']), + // B[name-hash][tx-hash][index] -> txdb.BidReveal + B: bdb.key('B', ['hash256', 'hash256', 'uint32']), + // E[name-hash][tx-hash][index] -> bid to reveal out. + E: bdb.key('E', ['hash256', 'hash256', 'uint32']) + } + }; + } } /** @@ -259,6 +727,7 @@ class WalletMigrationResult extends MigrationResult { super(); this.rescan = false; + this.recalculateTXDB = false; } } @@ -328,12 +797,14 @@ class WalletMigratorOptions { this.migrateFlag = -1; this.dbVersion = 0; + /** @type {WalletDB} */ this.db = null; + /** @type {DB} */ this.ldb = null; this.layout = layouts.wdb; - if (options) - this.fromOptions(options); + assert(options); + this.fromOptions(options); } /** @@ -382,12 +853,16 @@ exports.WalletMigrationResult = WalletMigrationResult; exports.migrations = { 0: MigrateMigrations, 1: MigrateChangeAddress, - 2: MigrateAccountLookahead + 2: MigrateAccountLookahead, + 3: MigrateTXDBBalances, + 4: MigrateBidRevealEntries }; // Expose migrations exports.MigrateChangeAddress = MigrateChangeAddress; exports.MigrateMigrations = MigrateMigrations; exports.MigrateAccountLookahead = MigrateAccountLookahead; +exports.MigrateTXDBBalances = MigrateTXDBBalances; +exports.MigrateBidRevealEntries = MigrateBidRevealEntries; module.exports = exports; diff --git a/lib/wallet/node.js b/lib/wallet/node.js index e28894ef1..ff4816320 100644 --- a/lib/wallet/node.js +++ b/lib/wallet/node.js @@ -52,7 +52,9 @@ class WalletNode extends Node { wipeNoReally: this.config.bool('wipe-no-really'), spv: this.config.bool('spv'), walletMigrate: this.config.uint('migrate'), - checkLookahead: this.config.bool('check-lookahead', false) + icannlockup: this.config.bool('icannlockup', true), + migrateNoRescan: this.config.bool('migrate-no-rescan', false), + preloadAll: this.config.bool('preload-all', false) }); this.rpc = new RPC(this); @@ -107,9 +109,11 @@ class WalletNode extends Node { await this.openPlugins(); await this.http.open(); + await this.wdb.connect(); await this.handleOpen(); this.logger.info('Wallet node is loaded.'); + this.emit('open'); } /** @@ -128,8 +132,10 @@ class WalletNode extends Node { this.rpc.wallet = null; + await this.wdb.disconnect(); await this.wdb.close(); await this.handleClose(); + this.emit('close'); } } diff --git a/lib/wallet/nodeclient.js b/lib/wallet/nodeclient.js index 7144c3073..5f181bcbc 100644 --- a/lib/wallet/nodeclient.js +++ b/lib/wallet/nodeclient.js @@ -7,6 +7,7 @@ 'use strict'; const assert = require('bsert'); +const blacklist = require('bsock/lib/blacklist'); const AsyncEmitter = require('bevent'); /** @@ -27,6 +28,7 @@ class NodeClient extends AsyncEmitter { this.network = node.network; this.filter = null; this.opened = false; + this.hooks = new Map(); this.init(); } @@ -98,13 +100,46 @@ class NodeClient extends AsyncEmitter { } /** - * Add a listener. - * @param {String} type + * Add a hook. + * @param {String} event * @param {Function} handler */ - hook(type, handler) { - return this.on(type, handler); + hook(event, handler) { + assert(typeof event === 'string', 'Event must be a string.'); + assert(typeof handler === 'function', 'Handler must be a function.'); + assert(!this.hooks.has(event), 'Hook already bound.'); + assert(!Object.prototype.hasOwnProperty.call(blacklist, event), + 'Blacklisted event.'); + this.hooks.set(event, handler); + } + + /** + * Remove a hook. + * @param {String} event + */ + + unhook(event) { + assert(typeof event === 'string', 'Event must be a string.'); + assert(!Object.prototype.hasOwnProperty.call(blacklist, event), + 'Blacklisted event.'); + this.hooks.delete(event); + } + + /** + * Call a hook. + * @param {String} event + * @param {...Object} args + * @returns {Promise} + */ + + handleCall(event, ...args) { + const hook = this.hooks.get(event); + + if (!hook) + throw new Error('No hook available.'); + + return hook(...args); } /** @@ -175,7 +210,7 @@ class NodeClient extends AsyncEmitter { // `data` is ignored because pool.spvFilter === walletDB.filter // and therefore is already updated. // Argument is kept here to be consistent with API in - // wallet/client.js (hs-client NodeClient) and wallet/nullclient.js + // wallet/client.js (client/node.js) and wallet/nullclient.js this.node.pool.queueFilterLoad(); } @@ -215,8 +250,6 @@ class NodeClient extends AsyncEmitter { /** * Rescan for any missed transactions. * @param {Number|Hash} start - Start block. - * @param {Bloom} filter - * @param {Function} iter - Iterator. * @returns {Promise} */ @@ -225,10 +258,38 @@ class NodeClient extends AsyncEmitter { return this.node.chain.reset(start); return this.node.chain.scan(start, this.filter, (entry, txs) => { - return this.emitAsync('block rescan', entry, txs); + return this.handleCall('block rescan', entry, txs); }); } + /** + * Rescan interactive for any missed transactions. + * @param {Number|Hash} start - Start block. + * @param {Boolean} [fullLock=false] + * @returns {Promise} + */ + + async rescanInteractive(start, fullLock = true) { + if (this.node.spv) + return this.node.chain.reset(start); + + const iter = async (entry, txs) => { + return await this.handleCall('block rescan interactive', entry, txs); + }; + + try { + return await this.node.scanInteractive( + start, + this.filter, + iter, + fullLock + ); + } catch (e) { + await this.handleCall('block rescan interactive abort', e.message); + throw e; + } + } + /** * Get name state. * @param {Buffer} nameHash diff --git a/lib/wallet/nullclient.js b/lib/wallet/nullclient.js index abc30f266..0dcdde0b7 100644 --- a/lib/wallet/nullclient.js +++ b/lib/wallet/nullclient.js @@ -165,8 +165,6 @@ class NullClient extends EventEmitter { /** * Rescan for any missed transactions. * @param {Number|Hash} start - Start block. - * @param {Bloom} filter - * @param {Function} iter - Iterator. * @returns {Promise} */ @@ -174,6 +172,17 @@ class NullClient extends EventEmitter { ; } + /** + * Rescan interactive for any missed transactions. + * @param {Number|Hash} start - Start block. + * @param {Boolean} [fullLock=false] + * @returns {Promise} + */ + + async rescanInteractive(start, fullLock) { + ; + } + /** * Get opening bid height. * @param {Buffer} nameHash diff --git a/lib/wallet/path.js b/lib/wallet/path.js index f3e9f9195..6b498945a 100644 --- a/lib/wallet/path.js +++ b/lib/wallet/path.js @@ -12,6 +12,9 @@ const Address = require('../primitives/address'); const Network = require('../protocol/network'); const {encoding} = bio; +/** @typedef {import('../types').NetworkType} NetworkType */ +/** @typedef {import('./account')} Account */ + /** * Path * @alias module:wallet.Path @@ -51,9 +54,8 @@ class Path extends bio.Struct { /** * Instantiate path from options object. - * @private * @param {Object} options - * @returns {Path} + * @returns {this} */ fromOptions(options) { @@ -75,7 +77,8 @@ class Path extends bio.Struct { /** * Clone the path object. - * @returns {Path} + * @param {this} path + * @returns {this} */ inject(path) { @@ -97,8 +100,8 @@ class Path extends bio.Struct { /** * Inject properties from serialized data. - * @private - * @param {Buffer} data + * @param {bio.BufferReader} br + * @returns {this} */ read(br) { @@ -151,7 +154,8 @@ class Path extends bio.Struct { /** * Serialize path. - * @returns {Buffer} + * @param {bio.BufferWriter} bw + * @returns {bio.BufferWriter} */ write(bw) { @@ -213,7 +217,7 @@ class Path extends bio.Struct { /** * Convert path object to string derivation path. - * @param {(String|Network)?} network - Network type. + * @param {(NetworkType|Network)?} [network] - Network type. * @returns {String} */ diff --git a/lib/wallet/plugin.js b/lib/wallet/plugin.js index f0a7b7434..2864166b5 100644 --- a/lib/wallet/plugin.js +++ b/lib/wallet/plugin.js @@ -33,7 +33,11 @@ class Plugin extends EventEmitter { constructor(node) { super(); - this.config = node.config.filter('wallet'); + this.config = node.config.filter('wallet', { + // Allow configurations to propagate from the hsd.conf + // with 'wallet-' prefix. + data: true + }); this.config.open('hsw.conf'); this.network = node.network; @@ -53,7 +57,9 @@ class Plugin extends EventEmitter { wipeNoReally: this.config.bool('wipe-no-really'), spv: node.spv, walletMigrate: this.config.uint('migrate'), - checkLookahead: this.config.bool('check-lookahead', false) + icannlockup: this.config.bool('icannlockup', true), + migrateNoRescan: this.config.bool('migrate-no-rescan', false), + preloadAll: this.config.bool('preload-all', false) }); this.rpc = new RPC(this); @@ -86,11 +92,13 @@ class Plugin extends EventEmitter { await this.wdb.open(); this.rpc.wallet = this.wdb.primary; await this.http.open(); + await this.wdb.connect(); } async close() { await this.http.close(); this.rpc.wallet = null; + await this.wdb.disconnect(); await this.wdb.close(); } } diff --git a/lib/wallet/records.js b/lib/wallet/records.js index a81ccb8ab..a36f39d0e 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -217,16 +217,20 @@ class TXRecord extends bio.Struct { /** * Create tx record. * @constructor - * @param {TX} tx - * @param {BlockMeta?} block + * @param {Number} mtime + * @param {TX} [tx] + * @param {BlockMeta} [block] */ - constructor(tx, block) { + constructor(mtime, tx, block) { super(); + if (mtime == null) + mtime = util.now(); + this.tx = null; this.hash = null; - this.mtime = util.now(); + this.mtime = mtime; this.height = -1; this.block = null; this.index = -1; @@ -240,7 +244,7 @@ class TXRecord extends bio.Struct { * Inject properties from tx and block. * @private * @param {TX} tx - * @param {Block?} block + * @param {Block} [block] * @returns {TXRecord} */ @@ -256,13 +260,14 @@ class TXRecord extends bio.Struct { /** * Instantiate tx record from tx and block. - * @param {TX} tx - * @param {Block?} block + * @param {TX} [tx] + * @param {Block} [block] + * @param {Number} [mtime] * @returns {TXRecord} */ - static fromTX(tx, block) { - return new this().fromTX(tx, block); + static fromTX(tx, block, mtime) { + return new this(mtime).fromTX(tx, block); } /** @@ -422,6 +427,10 @@ class MapRecord extends bio.Struct { return this.wids.delete(wid); } + has(wid) { + return this.wids.has(wid); + } + write(bw) { bw.writeU32(this.wids.size); diff --git a/lib/wallet/rpc.js b/lib/wallet/rpc.js index fbe249c39..f85bf5596 100644 --- a/lib/wallet/rpc.js +++ b/lib/wallet/rpc.js @@ -2062,7 +2062,7 @@ class RPC extends RPCBase { async sendOpen(args, help) { const opts = this._validateOpen(args, help, 'sendopen'); const wallet = this.wallet; - const tx = await wallet.sendOpen(opts.name, opts.force, { + const tx = await wallet.sendOpen(opts.name, { account: opts.account }); @@ -2072,7 +2072,7 @@ class RPC extends RPCBase { async createOpen(args, help) { const opts = this._validateOpen(args, help, 'createopen'); const wallet = this.wallet; - const mtx = await wallet.createOpen(opts.name, opts.force, { + const mtx = await wallet.createOpen(opts.name, { paths: true, account: opts.account }); @@ -2081,20 +2081,19 @@ class RPC extends RPCBase { } _validateOpen(args, help, method) { - const msg = `${method} "name" ( force "account" )`; + const msg = `${method} "name" ( "account" )`; if (help || args.length < 1 || args.length > 3) throw new RPCError(errs.MISC_ERROR, msg); const valid = new Validator(args); const name = valid.str(0); - const force = valid.bool(1, false); - const account = valid.str(2); + const account = valid.str(1); if (!name || !rules.verifyName(name)) throw new RPCError(errs.TYPE_ERROR, 'Invalid name.'); - return {name, force, account}; + return {name, account}; } async sendBid(args, help) { @@ -2467,6 +2466,7 @@ class RPC extends RPCBase { return mtx.getJSON(this.network); } + _validateRevoke(args, help, method) { if (help || args.length < 1 || args.length > 2) throw new RPCError(errs.MISC_ERROR, `${method} "name" ( "account" )`); diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index d2adf816e..9ad1a4cf3 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -14,14 +14,13 @@ const Amount = require('../ui/amount'); const CoinView = require('../coins/coinview'); const Coin = require('../primitives/coin'); const Outpoint = require('../primitives/outpoint'); -const records = require('./records'); const layout = require('./layout').txdb; const consensus = require('../protocol/consensus'); const policy = require('../protocol/policy'); const rules = require('../covenants/rules'); const NameState = require('../covenants/namestate'); const NameUndo = require('../covenants/undo'); -const {TXRecord} = records; +const {TXRecord} = require('./records'); const {types} = rules; /* @@ -46,6 +45,7 @@ class TXDB { this.wdb = wdb; this.db = wdb.db; this.logger = wdb.logger; + this.nowFn = wdb.options.nowFn || util.now; this.wid = wid || 0; this.bucket = null; @@ -82,7 +82,7 @@ class TXDB { /** * Get wallet path for output. * @param {Output} output - * @returns {Promise} - Returns {@link Path}. + * @returns {Promise} */ getPath(output) { @@ -97,7 +97,7 @@ class TXDB { /** * Test whether path exists for output. * @param {Output} output - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ hasPath(output) { @@ -166,6 +166,17 @@ class TXDB { b.del(layout.d.encode(spender.hash, spender.index)); } + /** + * Spend credit by spender/input record. + * Add undo coin to the input record. + * @param {Credit} credit + * @param {Outpoint} spender + */ + + addUndoToInput(b, credit, spender) { + b.put(layout.d.encode(spender.hash, spender.index), credit.coin.encode()); + } + /** * Write input record. * @param {TX} tx @@ -205,8 +216,9 @@ class TXDB { /** * Update account balance. + * @param {Batch} b * @param {Number} acct - * @param {Balance} delta + * @param {BalanceDelta} delta */ async updateAccountBalance(b, acct, delta) { @@ -220,7 +232,7 @@ class TXDB { * Test a whether a coin has been spent. * @param {Hash} hash * @param {Number} index - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ async getSpent(hash, index) { @@ -236,7 +248,7 @@ class TXDB { * Test a whether a coin has been spent. * @param {Hash} hash * @param {Number} index - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ isSpent(hash, index) { @@ -329,7 +341,7 @@ class TXDB { /** * List block records. - * @returns {Promise} + * @returns {Promise} */ getBlocks() { @@ -343,7 +355,7 @@ class TXDB { /** * Get block record. * @param {Number} height - * @returns {Promise} + * @returns {Promise} */ async getBlock(height) { @@ -440,7 +452,7 @@ class TXDB { /** * Test whether we have a name. * @param {Buffer} nameHash - * @returns {Boolean} + * @returns {Promise} */ async hasNameState(nameHash) { @@ -450,7 +462,7 @@ class TXDB { /** * Get a name state if present. * @param {Buffer} nameHash - * @returns {NameState} + * @returns {Promise} */ async getNameState(nameHash) { @@ -467,7 +479,7 @@ class TXDB { /** * Get all names. - * @returns {NameState[]} + * @returns {Promise} */ async getNames() { @@ -493,7 +505,7 @@ class TXDB { * Test whether we have a bid. * @param {Buffer} nameHash * @param {Outpoint} outpoint - * @returns {Boolean} + * @returns {Promise} */ async hasBid(nameHash, outpoint) { @@ -505,7 +517,7 @@ class TXDB { * Get a bid if present. * @param {Buffer} nameHash * @param {Outpoint} outpoint - * @returns {BlindBid} + * @returns {Promise} */ async getBid(nameHash, outpoint) { @@ -538,6 +550,7 @@ class TXDB { bb.lockup = options.lockup; bb.blind = options.blind; bb.own = options.own; + bb.height = options.height; b.put(layout.i.encode(nameHash, hash, index), bb.encode()); } @@ -556,7 +569,7 @@ class TXDB { /** * Get all bids for name. * @param {Buffer} nameHash - * @returns {BlindBid[]} + * @returns {Promise} */ async getBids(nameHash) { @@ -589,6 +602,7 @@ class TXDB { /** * Remove all bids for name. * @param {Buffer} nameHash + * @returns {Promise} */ async removeBids(b, nameHash) { @@ -604,7 +618,7 @@ class TXDB { * Test whether we have a reveal. * @param {Buffer} nameHash * @param {Outpoint} outpoint - * @returns {Boolean} + * @returns {Promise} */ async hasReveal(nameHash, outpoint) { @@ -616,7 +630,7 @@ class TXDB { * Get a reveal if present. * @param {Buffer} nameHash * @param {Outpoint} outpoint - * @returns {BidReveal} + * @returns {Promise} */ async getReveal(nameHash, outpoint) { @@ -633,23 +647,67 @@ class TXDB { return brv; } + /** + * Get reveal by bid outpoint. + * @param {Buffer} nameHash + * @param {Outpoint} bidOut + * @returns {Promise} + */ + + async getRevealByBid(nameHash, bidOut) { + const rawOutpoint = await this.bucket.get( + layout.E.encode(nameHash, bidOut.hash, bidOut.index)); + + if (!rawOutpoint) + return null; + + const outpoint = Outpoint.decode(rawOutpoint); + return this.getReveal(nameHash, outpoint); + } + + /** + * Get bid by reveal outpoint. + * @param {Buffer} nameHash + * @param {Outpoint} revealOut + * @returns {Promise} + */ + + async getBidByReveal(nameHash, revealOut) { + const reveal = await this.getReveal(nameHash, revealOut); + + if (!reveal) + return null; + + return this.getBid(nameHash, reveal.bidPrevout); + } + /** * Write a reveal. * @param {Object} b * @param {Buffer} nameHash * @param {Outpoint} outpoint * @param {Object} options + * @param {String} options.name + * @param {Amount} options.value + * @param {Number} options.height + * @param {Boolean} options.own + * @param {Outpoint} options.bidPrevout + * @returns {void} */ putReveal(b, nameHash, outpoint, options) { const {hash, index} = outpoint; + const {bidPrevout} = options; const brv = new BidReveal(); brv.nameHash = nameHash; brv.name = options.name; brv.value = options.value; brv.height = options.height; brv.own = options.own; + brv.bidPrevout = bidPrevout; b.put(layout.B.encode(nameHash, hash, index), brv.encode()); + b.put(layout.E.encode(nameHash, bidPrevout.hash, bidPrevout.index), + outpoint.encode()); } /** @@ -657,17 +715,19 @@ class TXDB { * @param {Object} b * @param {Buffer} nameHash * @param {Outpoint} outpoint + * @param {Outpoint} bidPrevout */ - removeReveal(b, nameHash, outpoint) { + removeReveal(b, nameHash, outpoint, bidPrevout) { const {hash, index} = outpoint; b.del(layout.B.encode(nameHash, hash, index)); + b.del(layout.E.encode(nameHash, bidPrevout.hash, bidPrevout.index)); } /** * Get all reveals by name. * @param {Buffer} nameHash - * @returns {BidReveal[]} + * @returns {Promise} */ async getReveals(nameHash) { @@ -694,6 +754,7 @@ class TXDB { * Remove all reveals by name. * @param {Object} b * @param {Buffer} nameHash + * @returns {Promise} */ async removeReveals(b, nameHash) { @@ -708,7 +769,7 @@ class TXDB { /** * Test whether a blind value is present. * @param {Buffer} blind - Blind hash. - * @returns {Boolean} + * @returns {Promise} */ async hasBlind(blind) { @@ -718,7 +779,7 @@ class TXDB { /** * Get a blind value if present. * @param {Buffer} blind - Blind hash. - * @returns {BlindValue} + * @returns {Promise} */ async getBlind(blind) { @@ -749,6 +810,7 @@ class TXDB { * Save blind value. * @param {Buffer} blind * @param {Object} options + * @returns {Promise} */ async saveBlind(blind, options) { @@ -769,9 +831,9 @@ class TXDB { /** * Add transaction without a batch. - * @private * @param {TX} tx - * @returns {Promise} + * @param {BlockMeta} [block] + * @returns {Promise} */ async add(tx, block) { @@ -794,7 +856,8 @@ class TXDB { return this.confirm(existing, block); } - const wtx = TXRecord.fromTX(tx, block); + const now = this.nowFn(); + const wtx = TXRecord.fromTX(tx, block, now); if (!block) { // Potentially remove double-spenders. @@ -817,7 +880,7 @@ class TXDB { * Test whether the transaction * has a duplicate open. * @param {TX} - * @returns {Boolean} + * @returns {Promise} */ async isDoubleOpen(tx) { @@ -849,6 +912,7 @@ class TXDB { * Remove duplicate opens. * @private * @param {TX} tx + * @returns {Promise} */ async removeDoubleOpen(tx) { @@ -863,6 +927,11 @@ class TXDB { if (!hash) continue; + const wtx = await this.getTX(hash); + + if (wtx.height !== -1) + return; + await this.remove(hash); } } @@ -909,8 +978,8 @@ class TXDB { * Insert transaction. * @private * @param {TXRecord} wtx - * @param {BlockMeta} block - * @returns {Promise} + * @param {BlockMeta} [block] + * @returns {Promise
} */ async insert(wtx, block) { @@ -957,18 +1026,7 @@ class TXDB { state.tx(path, 1); state.coin(path, -1); state.unconfirmed(path, -coin.value); - - // FINALIZE is a special case: locked coins _leave_ the wallet. - if (tx.output(i) && tx.covenant(i).isFinalize()) { - if (!block) { - state.ulocked(path, -tx.outputs[i].value); - } else { - state.clocked(path, -tx.outputs[i].value); - // This is the first time we've seen this tx and it is in a block - // (probably from a rescan). Update unconfirmed locked balance also. - state.ulocked(path, -tx.outputs[i].value); - } - } + this.unlockBalances(state, credit, path, -1); if (!block) { // If the tx is not mined, we do not @@ -988,6 +1046,8 @@ class TXDB { // coin so it can be reconnected // later during a reorg. state.confirmed(path, -coin.value); + this.unlockBalances(state, credit, path, height); + await this.removeCredit(b, credit, path); view.addCoin(coin); @@ -1005,14 +1065,6 @@ class TXDB { if (!path) continue; - // If the first time we see a TX is in a block - // (i.e. during a rescan) update the "unconfirmed" locked balance - // before updating the "confirmed" locked balance. - if (height !== -1) - await this.lockBalances(b, state, tx, i, path, -1); - - await this.lockBalances(b, state, tx, i, path, height); - details.setOutput(i, path); const credit = Credit.fromTX(tx, i, height); @@ -1021,11 +1073,27 @@ class TXDB { state.tx(path, 1); state.coin(path, 1); state.unconfirmed(path, output.value); + this.lockBalances(state, credit, path, -1); - if (block) + if (block) { state.confirmed(path, output.value); + this.lockBalances(state, credit, path, height); + } + + const spender = await this.getSpent(hash, i); + + if (spender) { + credit.spent = true; + this.addUndoToInput(b, credit, spender); + + // TODO: emit 'missed credit' + state.coin(path, -1); + state.unconfirmed(path, -output.value); + this.unlockBalances(state, credit, path, -1); + } await this.saveCredit(b, credit, path); + await this.watchOpensEarly(b, output); } // Handle names. @@ -1106,7 +1174,7 @@ class TXDB { * @private * @param {TXRecord} wtx * @param {BlockMeta} block - * @returns {Promise} + * @returns {Promise
} */ async confirm(wtx, block) { @@ -1137,6 +1205,9 @@ class TXDB { if (!credits[i]) { await this.removeInput(b, tx, i); + // NOTE: This check has been moved to the outputs + // processing in insert(pending), insert(block) and confirm. + // But will still be here just in case. const credit = await this.getCredit(hash, index); if (!credit) @@ -1167,17 +1238,15 @@ class TXDB { if (resolved) { state.coin(path, -1); state.unconfirmed(path, -coin.value); + this.unlockBalances(state, credit, path, -1); } + state.confirmed(path, -coin.value); + this.unlockBalances(state, credit, path, height); + // We can now safely remove the credit // entirely, now that we know it's also // been removed on-chain. - state.confirmed(path, -coin.value); - - // FINALIZE is a special case: locked coins _leave_ the wallet. - if (tx.output(i) && tx.covenant(i).isFinalize()) - state.clocked(path, -tx.outputs[i].value); - await this.removeCredit(b, credit, path); view.addCoin(coin); @@ -1192,13 +1261,13 @@ class TXDB { if (!path) continue; - await this.lockBalances(b, state, tx, i, path, height); - details.setOutput(i, path); let credit = await this.getCredit(hash, i); if (!credit) { + // TODO: Emit 'missed credit' event. + // This credit didn't belong to us the first time we // saw the transaction (before confirmation or rescan). // Create new credit for database. @@ -1208,9 +1277,17 @@ class TXDB { // meaning if it becomes unconfirmed, we can still confidently spend it. credit.own = own; - // Add coin to "unconfirmed" balance (which includes confirmed coins) - state.coin(path, 1); - state.unconfirmed(path, credit.coin.value); + const spender = await this.getSpent(hash, i); + + if (spender) { + credit.spent = true; + this.addUndoToInput(b, credit, spender); + } else { + // Add coin to "unconfirmed" balance (which includes confirmed coins) + state.coin(path, 1); + state.unconfirmed(path, credit.coin.value); + this.lockBalances(state, credit, path, -1); + } } // Credits spent in the mempool add an @@ -1223,6 +1300,8 @@ class TXDB { // Update coin height and confirmed // balance. Save once again. state.confirmed(path, output.value); + this.lockBalances(state, credit, path, height); + credit.coin.height = height; await this.saveCredit(b, credit, path); @@ -1252,6 +1331,8 @@ class TXDB { // Commit the new state. The balance has updated. const balance = await this.updateBalance(b, state); + this.unindexOpens(b, tx); + await b.write(); this.unlockTX(tx); @@ -1266,7 +1347,7 @@ class TXDB { * Recursively remove a transaction * from the database. * @param {Hash} hash - * @returns {Promise} + * @returns {Promise} */ async remove(hash) { @@ -1283,7 +1364,8 @@ class TXDB { * database. Disconnect inputs. * @private * @param {TXRecord} wtx - * @returns {Promise} + * @param {BlockMeta} [block] + * @returns {Promise
} */ async erase(wtx, block) { @@ -1320,18 +1402,12 @@ class TXDB { state.tx(path, -1); state.coin(path, 1); state.unconfirmed(path, coin.value); + this.lockBalances(state, credit, path, -1); - // FINALIZE is a special case: locked coins _leave_ the wallet. - // In this case a TX is erased, adding them back. - if (tx.output(i) && tx.covenant(i).isFinalize()) { - if (!block) - state.ulocked(path, tx.outputs[i].value); - else - state.clocked(path, tx.outputs[i].value); - } - - if (block) + if (block) { state.confirmed(path, coin.value); + this.lockBalances(state, credit, path, height); + } this.unspendCredit(b, tx, i); @@ -1349,18 +1425,24 @@ class TXDB { if (!path) continue; - await this.unlockBalances(b, state, tx, i, path, height); + const credit = await this.getCredit(hash, i); + + // If we don't have credit for the output, then we don't need + // to do anything, because they were getting erased anyway. + if (!credit) + continue; details.setOutput(i, path); - const credit = Credit.fromTX(tx, i, height); - state.tx(path, -1); state.coin(path, -1); state.unconfirmed(path, -output.value); + this.unlockBalances(state, credit, path, -1); - if (block) + if (block) { state.confirmed(path, -output.value); + this.unlockBalances(state, credit, path, height); + } await this.removeCredit(b, credit, path); } @@ -1420,7 +1502,7 @@ class TXDB { * remove all of its spenders. * @private * @param {TXRecord} wtx - * @returns {Promise} + * @returns {Promise} */ async removeRecursive(wtx) { @@ -1450,7 +1532,7 @@ class TXDB { /** * Revert a block. * @param {Number} height - * @returns {Promise} + * @returns {Promise} - number of txs removed. */ async revert(height) { @@ -1474,7 +1556,7 @@ class TXDB { * @private * @param {Hash} hash * @param {Number} height - * @returns {Promise} + * @returns {Promise} */ async unconfirm(hash, height) { @@ -1501,13 +1583,27 @@ class TXDB { if (wtx.height === -1) return null; + const tx = wtx.tx; + + if (tx.isCoinbase()) + return this.removeRecursive(wtx); + + // On unconfirm, if we already have OPEN txs in the pending list we + // remove transaction and it's descendants instead of storing them in + // the pending list. This follows the mempool behaviour where the first + // entries in the mempool will be the ones left, instead of txs coming + // from the block. This ensures consistency with the double open rules. + if (await this.isDoubleOpen(tx)) + return this.removeRecursive(wtx); + return this.disconnect(wtx, wtx.getBlock()); } /** * Unconfirm a transaction. Necessary after a reorg. * @param {TXRecord} wtx - * @returns {Promise} + * @param {BlockMeta} block + * @returns {Promise
} */ async disconnect(wtx, block) { @@ -1515,6 +1611,7 @@ class TXDB { const {tx, hash, height} = wtx; const details = new Details(wtx, block); const state = new BalanceDelta(); + let own = false; assert(block); @@ -1544,15 +1641,12 @@ class TXDB { details.setInput(i, path, coin); state.confirmed(path, coin.value); - - // FINALIZE is a special case: locked coins _leave_ the wallet. - // In this case a TX is reversed, adding them back. - if (tx.output(i) && tx.covenant(i).isFinalize()) - state.clocked(path, tx.outputs[i].value); + this.lockBalances(state, credit, path, height); // Resave the credit and mark it // as spent in the mempool instead. credit.spent = true; + own = true; await this.saveCredit(b, credit, path); } } @@ -1566,18 +1660,40 @@ class TXDB { if (!path) continue; - await this.unlockBalances(b, state, tx, i, path, height); - - const credit = await this.getCredit(hash, i); + let credit = await this.getCredit(hash, i); + let resolved = false; // Potentially update undo coin height. if (!credit) { - await this.updateSpentCoin(b, tx, i, height); - continue; - } + // TODO: Emit 'missed credit' event. - if (credit.spent) - await this.updateSpentCoin(b, tx, i, height); + // This credit didn't belong to us the first time we + // saw the transaction (after confirmation). + // Create new credit for database. + credit = Credit.fromTX(tx, i, height); + + // If this tx spent any of our own coins, we "own" this output, + // meaning if it becomes unconfirmed, we can still confidently spend it. + credit.own = own; + resolved = true; + + const spender = await this.getSpent(hash, i); + + if (spender) { + credit.spent = true; + this.addUndoToInput(b, credit, spender); + } else { + // If the newly discovered Coin is not spent, + // we need to add these to the balance. + state.coin(path, 1); + state.unconfirmed(path, credit.coin.value); + this.lockBalances(state, credit, path, -1); + } + } else if (credit.spent) { + // The coin height of this output becomes -1 + // as it is being unconfirmed. + await this.updateSpentCoin(b, tx, i, -1); + } details.setOutput(i, path); @@ -1585,11 +1701,22 @@ class TXDB { // balance. Save once again. credit.coin.height = -1; - state.confirmed(path, -output.value); + // If the coin was not discovered now, it means + // we need to subtract the values as they were part of + // the balance. + // If the credit is new, confirmed balances did not account for it. + if (!resolved) { + state.confirmed(path, -output.value); + this.unlockBalances(state, credit, path, height); + } await this.saveCredit(b, credit, path); } + // Unconfirm will also index OPENs as the transaction is now part of the + // wallet pending transactions. + this.indexOpens(b, tx); + // Undo name state. await this.undoNameState(b, tx); @@ -1632,7 +1759,7 @@ class TXDB { * @private * @param {Hash} hash * @param {TX} ref - Reference tx, the tx that double-spent. - * @returns {Promise} - Returns Boolean. + * @returns {Promise} - Returns Boolean. */ async removeConflict(wtx) { @@ -1642,6 +1769,9 @@ class TXDB { const details = await this.removeRecursive(wtx); + if (!details) + return null; + this.logger.warning('Removed conflict: %x.', tx.hash()); // Emit the _removed_ transaction. @@ -1655,7 +1785,8 @@ class TXDB { * double spenders, and verify inputs. * @private * @param {TX} tx - * @returns {Promise} + * @param {Boolean} conf + * @returns {Promise} */ async removeConflicts(tx, conf) { @@ -1700,180 +1831,105 @@ class TXDB { } /** - * Lock balances according to covenants. - * @param {Object} b + * Lock balances according to covenant. + * Inserting or confirming: TX outputs. + * Removing or undoing: Coins spent by the wallet in tx inputs. * @param {State} state - * @param {TX} tx - * @param {Number} i + * @param {Credit} credit * @param {Path} path * @param {Number} height */ - async lockBalances(b, state, tx, i, path, height) { - const output = tx.outputs[i]; - const covenant = output.covenant; + lockBalances(state, credit, path, height) { + const {value, covenant} = credit.coin; switch (covenant.type) { - case types.CLAIM: - case types.BID: { - if (height === -1) - state.ulocked(path, output.value); - else - state.clocked(path, output.value); - break; - } - - case types.REVEAL: { - assert(i < tx.inputs.length); - - const nameHash = covenant.getHash(0); - const prevout = tx.inputs[i].prevout; - - const bb = await this.getBid(nameHash, prevout); - if (!bb) - break; - - if (height === -1) { - state.ulocked(path, -bb.lockup); - state.ulocked(path, output.value); - } else { - state.clocked(path, -bb.lockup); - state.clocked(path, output.value); - } - - break; - } - - case types.REDEEM: { + case types.CLAIM: // output is locked until REGISTER + case types.BID: // output is locked until REVEAL + case types.REVEAL: // output is locked until REDEEM + case types.REGISTER: // output is now locked or "burned" + case types.UPDATE: // output has been locked since REGISTER + case types.RENEW: + case types.TRANSFER: + case types.FINALIZE: + case types.REVOKE: + { if (height === -1) - state.ulocked(path, -output.value); + state.ulocked(path, value); else - state.clocked(path, -output.value); - break; - } - - case types.REGISTER: { - assert(i < tx.inputs.length); - - const prevout = tx.inputs[i].prevout; - - const coin = await this.getCoin(prevout.hash, prevout.index); - assert(coin); - assert(coin.covenant.isReveal() || coin.covenant.isClaim()); - - if (height === -1) { - state.ulocked(path, -coin.value); - state.ulocked(path, output.value); - } else { - state.clocked(path, -coin.value); - state.clocked(path, output.value); - } - + state.clocked(path, value); break; } - case types.FINALIZE: { - if (height === -1) - state.ulocked(path, output.value); - else - state.clocked(path, output.value); + case types.REDEEM: // noop: already unlocked by the BID in the input break; - } } } /** * Unlock balances according to covenants. - * @param {Object} b + * Inserting or confirming: Coins spent by the wallet in TX inputs. + * Removing or undoing: TX outputs. * @param {State} state - * @param {TX} tx - * @param {Number} i + * @param {Credit} credit * @param {Path} path * @param {Number} height */ - async unlockBalances(b, state, tx, i, path, height) { - const output = tx.outputs[i]; - const covenant = output.covenant; + unlockBalances(state, credit, path, height) { + const {value, covenant} = credit.coin; switch (covenant.type) { - case types.CLAIM: - case types.BID: { + case types.CLAIM: // output is locked until REGISTER + case types.BID: // output is locked until REVEAL + case types.REVEAL: // output is locked until REDEEM + case types.REGISTER: // output is now locked or "burned" + case types.UPDATE: // output has been locked since REGISTER + case types.RENEW: + case types.TRANSFER: + case types.FINALIZE: + case types.REVOKE: + { if (height === -1) - state.ulocked(path, -output.value); + state.ulocked(path, -value); else - state.clocked(path, -output.value); - break; - } - - case types.REVEAL: { - assert(i < tx.inputs.length); - - const nameHash = covenant.getHash(0); - const prevout = tx.inputs[i].prevout; - - const bb = await this.getBid(nameHash, prevout); - if (!bb) - break; - - if (height === -1) { - state.ulocked(path, bb.lockup); - state.ulocked(path, -output.value); - } else { - state.clocked(path, bb.lockup); - state.clocked(path, -output.value); - } - + state.clocked(path, -value); break; } - - case types.REDEEM: { - if (height === -1) - state.ulocked(path, output.value); - else - state.clocked(path, output.value); + case types.REDEEM: // noop: already unlocked by the BID in the input break; - } + } + } - case types.REGISTER: { - assert(i < tx.inputs.length); + /** + * Start tracking OPENs right away. + * This does not check if the name is owned by the wallet. + * @private + * @param {Batch} b + * @param {Output} tx + * @param {Path} path + * @returns {Promise} + */ - const coins = await this.getSpentCoins(tx); - const coin = coins[i]; - assert(coin); - assert(coin.covenant.isReveal() || coin.covenant.isClaim()); + async watchOpensEarly(b, output) { + const {covenant} = output; - if (height === -1) { - state.ulocked(path, coin.value); - state.ulocked(path, -output.value); - } else { - state.clocked(path, coin.value); - state.clocked(path, -output.value); - } + if (!covenant.isOpen()) + return; - break; - } + const nameHash = covenant.getHash(0); - case types.FINALIZE: { - if (height === -1) - state.ulocked(path, -output.value); - else - state.clocked(path, -output.value); - break; - } - } + if (!await this.wdb.hasNameMap(nameHash, this.wid)) + await this.addNameMap(b, nameHash); } /** * Handle incoming covenant. * @param {Object} b * @param {TX} tx - * @param {Number} i - * @param {Path} path + * @param {CoinView} view * @param {Number} height - * @returns {Object} out - * @returns {Boolean} out.updated - * @returns {Boolean} out.index + * @returns {Promise} updated */ async connectNames(b, tx, view, height) { @@ -1930,8 +1986,7 @@ class TXDB { case types.OPEN: { if (!path) { // Are we "watching" this name? - const map = await this.wdb.getNameMap(nameHash); - if (!map || !map.wids.has(this.wid)) + if (!await this.wdb.hasNameMap(nameHash, this.wid)) break; const name = covenant.get(2); @@ -1967,6 +2022,7 @@ class TXDB { name, lockup, blind, + height, own: false }); @@ -1984,6 +2040,7 @@ class TXDB { name, lockup, blind, + height, own: true }); @@ -2004,33 +2061,39 @@ class TXDB { ns.setValue(output.value); } + const {prevout} = tx.inputs[i]; + if (!path) { this.putReveal(b, nameHash, outpoint, { name: ns.name, value: output.value, height: height, - own: false + own: false, + bidPrevout: prevout }); updated = true; break; } - const {prevout} = tx.inputs[i]; const coin = view.getOutput(prevout); - const uc = coin.covenant; - const blind = uc.getHash(3); - const nonce = covenant.getHash(2); - this.putBlind(b, blind, { - value: output.value, - nonce: nonce - }); + if (coin) { + const uc = coin.covenant; + const blind = uc.getHash(3); + const nonce = covenant.getHash(2); + + this.putBlind(b, blind, { + value: output.value, + nonce: nonce + }); + } this.putReveal(b, nameHash, outpoint, { name: ns.name, value: output.value, height: height, - own: true + own: true, + bidPrevout: prevout }); updated = true; @@ -2194,6 +2257,7 @@ class TXDB { * @param {Number} i * @param {Path} path * @param {Number} height + * @returns {Promise} applied undo. */ async undoNameState(b, tx) { @@ -2213,8 +2277,9 @@ class TXDB { break; } case types.REVEAL: { + const input = tx.inputs[i]; const nameHash = covenant.getHash(0); - this.removeReveal(b, nameHash, tx.outpoint(i)); + this.removeReveal(b, nameHash, tx.outpoint(i), input.prevout); break; } } @@ -2230,6 +2295,7 @@ class TXDB { * that is in the nameMap but does not involve wallet addresses. * @param {Object} b * @param {Hash} hash + * @returns {Promise} - applied undo. */ async applyNameUndo(b, hash) { @@ -2265,6 +2331,65 @@ class TXDB { return true; } + /** + * Recalculate wallet balances. + * @returns {Promise} + */ + + async recalculateBalances() { + const state = new BalanceDelta(); + + const creditIter = this.bucket.iterator({ + gte: layout.c.min(), + lte: layout.c.max(), + values: true + }); + + await creditIter.each(async (key, raw) => { + const credit = Credit.decode(raw); + const coin = credit.coin; + const value = coin.value; + const path = await this.getPath(coin); + + assert(path); + + state.coin(path, 1); + state.unconfirmed(path, value); + this.lockBalances(state, credit, path, -1); + + // Unconfirmed coins + if (coin.height !== -1) { + state.confirmed(path, value); + this.lockBalances(state, credit, path, coin.height); + } + + if (credit.spent) { + state.coin(path, -1); + state.unconfirmed(path, -value); + this.unlockBalances(state, credit, path, -1); + } + }); + + const batch = this.bucket.batch(); + + for (const [acct, delta] of state.accounts) { + const oldAccountBalance = await this.getAccountBalance(acct); + const finalAcctBalance = new Balance(); + finalAcctBalance.tx = oldAccountBalance.tx; + + delta.applyTo(finalAcctBalance); + batch.put(layout.r.encode(acct), finalAcctBalance.encode()); + } + + const walletBalance = await this.getWalletBalance(); + const finalWalletBalance = new Balance(); + finalWalletBalance.tx = walletBalance.tx; + state.applyTo(finalWalletBalance); + batch.put(layout.R.encode(), finalWalletBalance.encode()); + + await batch.write(); + } + /** * Lock all coins in a transaction. * @param {TX} tx @@ -2304,6 +2429,7 @@ class TXDB { /** * Unlock a single coin. * @param {Coin|Outpoint} coin + * @returns {Boolean} - whether the coin was locked. */ unlockCoin(coin) { @@ -2323,6 +2449,7 @@ class TXDB { /** * Test locked status of a single coin. * @param {Coin|Outpoint} coin + * @returns {Boolean} */ isLocked(coin) { @@ -2334,7 +2461,7 @@ class TXDB { * Filter array of coins or outpoints * for only unlocked ones. * @param {Coin[]|Outpoint[]} - * @returns {Array} + * @returns {Coin[]|Outpoint[]} */ filterLocked(coins) { @@ -2365,7 +2492,7 @@ class TXDB { /** * Get hashes of all transactions in the database. * @param {Number} acct - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getAccountHistoryHashes(acct) { @@ -2385,7 +2512,7 @@ class TXDB { * @param {Number} acct * @param {Hash} hash * @param {Index} number - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ hasCoinByAccount(acct, hash, index) { @@ -2397,7 +2524,7 @@ class TXDB { /** * Get hashes of all transactions in the database. * @param {Number} acct - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getHistoryHashes(acct) { @@ -2416,7 +2543,7 @@ class TXDB { /** * Get hashes of all unconfirmed transactions in the database. * @param {Number} acct - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getAccountPendingHashes(acct) { @@ -2434,7 +2561,7 @@ class TXDB { /** * Get hashes of all unconfirmed transactions in the database. * @param {Number} acct - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getPendingHashes(acct) { @@ -2453,7 +2580,7 @@ class TXDB { /** * Test whether the database has a pending transaction. * @param {Hash} hash - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ async hasPending(hash) { @@ -2463,7 +2590,7 @@ class TXDB { /** * Get all coin hashes in the database. * @param {Number} acct - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getAccountOutpoints(acct) { @@ -2481,7 +2608,7 @@ class TXDB { /** * Get all coin hashes in the database. * @param {Number} acct - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getOutpoints(acct) { @@ -2508,7 +2635,7 @@ class TXDB { * @param {Number} options.end - End height. * @param {Number?} options.limit - Max number of records. * @param {Boolean?} options.reverse - Reverse order. - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getAccountHeightRangeHashes(acct, options) { @@ -2537,7 +2664,7 @@ class TXDB { * @param {Number} options.end - End height. * @param {Number?} options.limit - Max number of records. * @param {Boolean?} options.reverse - Reverse order. - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getHeightRangeHashes(acct, options) { @@ -2564,7 +2691,7 @@ class TXDB { /** * Get TX hashes by height. * @param {Number} height - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getHeightHashes(height) { @@ -2579,7 +2706,7 @@ class TXDB { * @param {Number} options.end - End height. * @param {Number?} options.limit - Max number of records. * @param {Boolean?} options.reverse - Reverse order. - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getAccountRangeHashes(acct, options) { @@ -2608,7 +2735,7 @@ class TXDB { * @param {Number} options.end - End height. * @param {Number?} options.limit - Max number of records. * @param {Boolean?} options.reverse - Reverse order. - * @returns {Promise} - Returns {@link Hash}[]. + * @returns {Promise} */ getRangeHashes(acct, options) { @@ -2640,7 +2767,7 @@ class TXDB { * @param {Number} options.end - End time. * @param {Number?} options.limit - Max number of records. * @param {Boolean?} options.reverse - Reverse order. - * @returns {Promise} - Returns {@link TX}[]. + * @returns {Promise} */ async getRange(acct, options) { @@ -2660,7 +2787,7 @@ class TXDB { * Get last N transactions. * @param {Number} acct * @param {Number} limit - Max number of transactions. - * @returns {Promise} - Returns {@link TX}[]. + * @returns {Promise} */ getLast(acct, limit) { @@ -2675,7 +2802,7 @@ class TXDB { /** * Get all transactions. * @param {Number} acct - * @returns {Promise} - Returns {@link TX}[]. + * @returns {Promise} */ getHistory(acct) { @@ -2696,7 +2823,7 @@ class TXDB { /** * Get all acct transactions. * @param {Number} acct - * @returns {Promise} - Returns {@link TX}[]. + * @returns {Promise} */ async getAccountHistory(acct) { @@ -2715,7 +2842,7 @@ class TXDB { /** * Get unconfirmed transactions. * @param {Number} acct - * @returns {Promise} - Returns {@link TX}[]. + * @returns {Promise} */ async getPending(acct) { @@ -2734,7 +2861,7 @@ class TXDB { /** * Get coins. * @param {Number} acct - * @returns {Promise} - Returns {@link Coin}[]. + * @returns {Promise} */ getCredits(acct) { @@ -2761,7 +2888,7 @@ class TXDB { /** * Get coins by account. * @param {Number} acct - * @returns {Promise} - Returns {@link Coin}[]. + * @returns {Promise} */ async getAccountCredits(acct) { @@ -2780,7 +2907,7 @@ class TXDB { /** * Fill a transaction with coins (all historical coins). * @param {TX} tx - * @returns {Promise} - Returns {@link TX}. + * @returns {Promise>} */ async getSpentCredits(tx) { @@ -2813,7 +2940,7 @@ class TXDB { /** * Get coins. * @param {Number} acct - * @returns {Promise} - Returns {@link Coin}[]. + * @returns {Promise} */ async getCoins(acct) { @@ -2833,7 +2960,7 @@ class TXDB { /** * Get coins by account. * @param {Number} acct - * @returns {Promise} - Returns {@link Coin}[]. + * @returns {Promise} */ async getAccountCoins(acct) { @@ -2853,7 +2980,7 @@ class TXDB { /** * Get historical coins for a transaction. * @param {TX} tx - * @returns {Promise} - Returns {@link TX}. + * @returns {Promise} */ async getSpentCoins(tx) { @@ -2878,7 +3005,7 @@ class TXDB { /** * Get a coin viewpoint. * @param {TX} tx - * @returns {Promise} - Returns {@link CoinView}. + * @returns {Promise} */ async getCoinView(tx) { @@ -2903,7 +3030,7 @@ class TXDB { /** * Get historical coin viewpoint. * @param {TX} tx - * @returns {Promise} - Returns {@link CoinView}. + * @returns {Promise} */ async getSpentView(tx) { @@ -2927,7 +3054,7 @@ class TXDB { /** * Get transaction. * @param {Hash} hash - * @returns {Promise} - Returns {@link TX}. + * @returns {Promise} */ async getTX(hash) { @@ -2942,7 +3069,7 @@ class TXDB { /** * Get transaction details. * @param {Hash} hash - * @returns {Promise} - Returns {@link TXDetails}. + * @returns {Promise} */ async getDetails(hash) { @@ -2956,8 +3083,8 @@ class TXDB { /** * Convert transaction to transaction details. - * @param {TXRecord[]} wtxs - * @returns {Promise} + * @param {TXRecord[]|TXRecord} wtxs + * @returns {Promise} */ async toDetails(wtxs) { @@ -2982,7 +3109,7 @@ class TXDB { * Convert transaction to transaction details. * @private * @param {TXRecord} wtx - * @returns {Promise} + * @returns {Promise
} */ async _toDetails(wtx) { @@ -3014,7 +3141,7 @@ class TXDB { /** * Test whether the database has a transaction. * @param {Hash} hash - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ hasTX(hash) { @@ -3025,7 +3152,7 @@ class TXDB { * Get coin. * @param {Hash} hash * @param {Number} index - * @returns {Promise} - Returns {@link Coin}. + * @returns {Promise} */ async getCoin(hash, index) { @@ -3038,10 +3165,10 @@ class TXDB { } /** - * Get coin. + * Get credit. * @param {Hash} hash * @param {Number} index - * @returns {Promise} - Returns {@link Coin}. + * @returns {Promise} */ async getCredit(hash, index) { @@ -3061,7 +3188,7 @@ class TXDB { * Get spender coin. * @param {Outpoint} spent * @param {Outpoint} prevout - * @returns {Promise} - Returns {@link Coin}. + * @returns {Promise} */ async getSpentCoin(spent, prevout) { @@ -3081,7 +3208,7 @@ class TXDB { /** * Test whether the database has a spent coin. * @param {Outpoint} spent - * @returns {Promise} - Returns {@link Coin}. + * @returns {Promise} */ hasSpentCoin(spent) { @@ -3116,7 +3243,7 @@ class TXDB { /** * Test whether the database has a transaction. * @param {Hash} hash - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ async hasCoin(hash, index) { @@ -3126,7 +3253,7 @@ class TXDB { /** * Calculate balance. * @param {Number?} account - * @returns {Promise} - Returns {@link Balance}. + * @returns {Promise} */ async getBalance(acct) { @@ -3140,7 +3267,7 @@ class TXDB { /** * Calculate balance. - * @returns {Promise} - Returns {@link Balance}. + * @returns {Promise} */ async getWalletBalance() { @@ -3155,7 +3282,7 @@ class TXDB { /** * Calculate balance by account. * @param {Number} acct - * @returns {Promise} - Returns {@link Balance}. + * @returns {Promise} */ async getAccountBalance(acct) { @@ -3173,13 +3300,13 @@ class TXDB { * Zap pending transactions older than `age`. * @param {Number} acct * @param {Number} age - Age delta. - * @returns {Promise} + * @returns {Promise} - zapped tx hashes. */ async zap(acct, age) { assert((age >>> 0) === age); - const now = util.now(); + const now = this.nowFn(); const txs = await this.getRange(acct, { start: 0, @@ -3208,7 +3335,7 @@ class TXDB { /** * Abandon transaction. * @param {Hash} hash - * @returns {Promise} + * @returns {Promise
} - removed tx details. */ async abandon(hash) { @@ -3296,7 +3423,7 @@ class Balance extends bio.Struct { * Inject properties from serialized data. * @private * @param {Buffer} data - * @returns {TXDBState} + * @returns {Balance} */ read(br) { @@ -3311,7 +3438,7 @@ class Balance extends bio.Struct { /** * Convert balance to a more json-friendly object. - * @param {Boolean?} minimal + * @param {Boolean} [minimal=false] * @returns {Object} */ @@ -3886,18 +4013,25 @@ class BlindBid extends bio.Struct { this.value = -1; this.lockup = 0; this.blind = consensus.ZERO_HASH; + this.height = -1; this.own = false; } getSize() { - return 1 + this.name.length + 41; + return 1 + this.name.length + 45; } write(bw) { + let height = this.height; + + if (height === -1) + height = 0xffffffff; + bw.writeU8(this.name.length); bw.writeBytes(this.name); bw.writeU64(this.lockup); bw.writeBytes(this.blind); + bw.writeU32(height); bw.writeU8(this.own ? 1 : 0); return bw; } @@ -3906,7 +4040,12 @@ class BlindBid extends bio.Struct { this.name = br.readBytes(br.readU8()); this.lockup = br.readU64(); this.blind = br.readBytes(32); + this.height = br.readU32(); this.own = br.readU8() === 1; + + if (this.height === 0xffffffff) + this.height = -1; + return this; } @@ -3918,6 +4057,7 @@ class BlindBid extends bio.Struct { value: this.value === -1 ? undefined : this.value, lockup: this.lockup, blind: this.blind.toString('hex'), + height: this.height, own: this.own }; } @@ -3968,13 +4108,14 @@ class BidReveal extends bio.Struct { this.name = EMPTY; this.nameHash = consensus.ZERO_HASH; this.prevout = new Outpoint(); + this.bidPrevout = new Outpoint(); this.value = 0; this.height = -1; this.own = false; } getSize() { - return 1 + this.name.length + 13; + return 1 + this.name.length + 13 + 36; } write(bw) { @@ -3988,6 +4129,7 @@ class BidReveal extends bio.Struct { bw.writeU64(this.value); bw.writeU32(height); bw.writeU8(this.own ? 1 : 0); + this.bidPrevout.write(bw); return bw; } @@ -3997,6 +4139,7 @@ class BidReveal extends bio.Struct { this.value = br.readU64(); this.height = br.readU32(); this.own = br.readU8() === 1; + this.bidPrevout.read(br); if (this.height === 0xffffffff) this.height = -1; @@ -4009,6 +4152,7 @@ class BidReveal extends bio.Struct { name: this.name.toString('ascii'), nameHash: this.nameHash.toString('hex'), prevout: this.prevout.toJSON(), + bidPrevout: this.bidPrevout.isNull() ? null : this.bidPrevout.toJSON(), value: this.value, height: this.height, own: this.own @@ -4020,4 +4164,14 @@ class BidReveal extends bio.Struct { * Expose */ +TXDB.Balance = Balance; +TXDB.BalanceDelta = BalanceDelta; +TXDB.Credit = Credit; +TXDB.Details = Details; +TXDB.DetailsMember = DetailsMember; +TXDB.BlockRecord = BlockRecord; +TXDB.BlindBid = BlindBid; +TXDB.BlindValue = BlindValue; +TXDB.BidReveal = BidReveal; + module.exports = TXDB; diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index aad0bc37f..97f4884d7 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -32,7 +32,7 @@ const rules = require('../covenants/rules'); const {Resource} = require('../dns/resource'); const Claim = require('../primitives/claim'); const reserved = require('../covenants/reserved'); -const ownership = require('../covenants/ownership'); +const {ownership} = require('../covenants/ownership'); const {states} = require('../covenants/namestate'); const {types} = rules; const {Mnemonic} = HD; @@ -46,6 +46,12 @@ const Outpoint = require('../primitives/outpoint'); const EMPTY = Buffer.alloc(0); +/** + * @typedef {Object} AddResult + * @property {Details} details + * @property {WalletKey[]} derived + */ + /** * Wallet * @alias module:wallet.Wallet @@ -199,9 +205,9 @@ class Wallet extends EventEmitter { const account = await this._createAccount(options, passphrase); assert(account); - this.logger.info('Wallet initialized (%s).', this.id); + await this.txdb.open(this); - return this.txdb.open(this); + this.logger.info('Wallet initialized (%s).', this.id); } /** @@ -215,9 +221,8 @@ class Wallet extends EventEmitter { if (!account) throw new Error('Default account not found.'); + await this.txdb.open(this); this.logger.info('Wallet opened (%s).', this.id); - - return this.txdb.open(this); } /** @@ -659,6 +664,50 @@ class Wallet extends EventEmitter { return account; } + /** + * Modify an account. Requires passphrase if master key is encrypted. + * @param {String|Number} acct + * @param {Object} options + * @param {String} [passphrase] + * @returns {Promise} + */ + + async modifyAccount(acct, options, passphrase) { + const unlock = await this.writeLock.lock(); + try { + return await this._modifyAccount(acct, options, passphrase); + } finally { + unlock(); + } + } + + /** + * Create an account without a lock. + * @param {String|Number} acct + * @param {Object} options + * @param {String} [passphrase] + * @returns {Promise} + */ + + async _modifyAccount(acct, options, passphrase) { + if (!await this.hasAccount(acct)) + throw new Error(`Account ${acct} does not exist.`); + + await this.unlock(passphrase); + + const account = await this.getAccount(acct); + assert(account); + + const b = this.db.batch(); + + if (options.lookahead != null) + await account.setLookahead(b, options.lookahead); + + await b.write(); + + return account; + } + /** * Ensure an account. Requires passphrase if master key is encrypted. * @param {Object} options - See {@link Account} options. @@ -714,7 +763,7 @@ class Wallet extends EventEmitter { /** * Retrieve an account from the database. * @param {Number|String} acct - * @returns {Promise} - Returns {@link Account}. + * @returns {Promise} */ async getAccount(acct) { @@ -1584,14 +1633,12 @@ class Wallet extends EventEmitter { * Make a open MTX. * @param {String} name * @param {Number|String} acct - * @param {Boolean} force * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ - async makeOpen(name, force, acct, mtx) { + async makeOpen(name, acct, mtx) { assert(typeof name === 'string'); - assert(typeof force === 'boolean'); assert((acct >>> 0) === acct || typeof acct === 'string'); if (!rules.verifyName(name)) @@ -1601,11 +1648,15 @@ class Wallet extends EventEmitter { const nameHash = rules.hashName(rawName); const height = this.wdb.height + 1; const network = this.network; + const {icannlockup} = this.wdb.options; // TODO: Handle expired behavior. if (rules.isReserved(nameHash, height, network)) throw new Error(`Name is reserved: ${name}.`); + if (icannlockup && rules.isLockedUp(nameHash, height, network)) + throw new Error(`Name is locked up: ${name}.`); + if (!rules.hasRollout(nameHash, height, network)) throw new Error(`Name not yet available: ${name}.`); @@ -1629,10 +1680,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = addr; output.value = 0; - output.covenant.type = types.OPEN; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(0); - output.covenant.push(rawName); + output.covenant.setOpen(nameHash, rawName); if (!mtx) mtx = new MTX(); @@ -1648,14 +1696,13 @@ class Wallet extends EventEmitter { * Create and finalize an open * MTX without a lock. * @param {String} name - * @param {Boolean} force * @param {Object} options - * @returns {MTX} + * @returns {Promise} */ - async _createOpen(name, force, options) { + async _createOpen(name, options) { const acct = options ? options.account || 0 : 0; - const mtx = await this.makeOpen(name, force, acct); + const mtx = await this.makeOpen(name, acct); await this.fill(mtx, options); return this.finalize(mtx, options); } @@ -1664,15 +1711,14 @@ class Wallet extends EventEmitter { * Create and finalize an open * MTX with a lock. * @param {String} name - * @param {Boolean} force * @param {Object} options - * @returns {MTX} + * @returns {Promise} */ - async createOpen(name, force, options) { + async createOpen(name, options) { const unlock = await this.fundLock.lock(); try { - return await this._createOpen(name, force, options); + return await this._createOpen(name, options); } finally { unlock(); } @@ -1682,13 +1728,13 @@ class Wallet extends EventEmitter { * Create and send an open * MTX without a lock. * @param {String} name - * @param {Boolean} force * @param {Object} options + * @returns {Promise} */ - async _sendOpen(name, force, options) { + async _sendOpen(name, options) { const passphrase = options ? options.passphrase : null; - const mtx = await this._createOpen(name, force, options); + const mtx = await this._createOpen(name, options); return this.sendMTX(mtx, passphrase); } @@ -1696,14 +1742,14 @@ class Wallet extends EventEmitter { * Create and send an open * MTX with a lock. * @param {String} name - * @param {Boolean} force * @param {Object} options + * @returns {Promise} */ - async sendOpen(name, force, options) { + async sendOpen(name, options) { const unlock = await this.fundLock.lock(); try { - return await this._sendOpen(name, force, options); + return await this._sendOpen(name, options); } finally { unlock(); } @@ -1716,14 +1762,16 @@ class Wallet extends EventEmitter { * @param {Number} lockup * @param {Number|String} acct * @param {MTX?} mtx - * @returns {MTX} + * @param {Address?} addr + * @returns {Promise} */ - async makeBid(name, value, lockup, acct, mtx) { + async makeBid(name, value, lockup, acct, mtx, addr) { assert(typeof name === 'string'); assert(Number.isSafeInteger(value) && value >= 0); assert(Number.isSafeInteger(lockup) && lockup >= 0); assert((acct >>> 0) === acct || typeof acct === 'string'); + assert(addr == null || addr instanceof Address); if (!rules.verifyName(name)) throw new Error(`Invalid name: ${name}.`); @@ -1733,12 +1781,6 @@ class Wallet extends EventEmitter { const height = this.wdb.height + 1; const network = this.network; - if (rules.isReserved(nameHash, height, network)) - throw new Error(`Name is reserved: ${name}.`); - - if (!rules.hasRollout(nameHash, height, network)) - throw new Error(`Name not yet available: ${name}.`); - let ns = await this.getNameState(nameHash); if (!ns) @@ -1759,17 +1801,15 @@ class Wallet extends EventEmitter { `Bid (${value}) exceeds lockup value (${lockup}): ${name}.` ); - const addr = await this.receiveAddress(acct); + if (!addr) + addr = await this.receiveAddress(acct); + const blind = await this.generateBlind(nameHash, addr, value); const output = new Output(); output.address = addr; output.value = lockup; - output.covenant.type = types.BID; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(start); - output.covenant.push(rawName); - output.covenant.pushHash(blind); + output.covenant.setBid(nameHash, start, rawName, blind); if (!mtx) mtx = new MTX(); @@ -1857,10 +1897,10 @@ class Wallet extends EventEmitter { * @returns {MTX} output.reveal */ - async createAuctionTxs(name, value, lockup, options) { + async createAuctionTXs(name, value, lockup, options) { const unlock = await this.fundLock.lock(); try { - return await this._createAuctionTxs(name, value, lockup, options); + return await this._createAuctionTXs(name, value, lockup, options); } finally { unlock(); } @@ -1878,7 +1918,7 @@ class Wallet extends EventEmitter { * @returns {MTX} output.reveal */ - async _createAuctionTxs(name, value, lockup, options) { + async _createAuctionTXs(name, value, lockup, options) { const bid = await this._createBid(name, value, lockup, options); const bidOuputIndex = bid.outputs.findIndex(o => o.covenant.isBid()); @@ -1902,10 +1942,8 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = bidCoin.address; output.value = value; - output.covenant.type = types.REVEAL; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(height); - output.covenant.pushHash(nonce); + output.covenant.setReveal(nameHash, height, nonce); + reveal.addOutpoint(Outpoint.fromTX(bid, bidOuputIndex)); reveal.outputs.push(output); @@ -1924,7 +1962,7 @@ class Wallet extends EventEmitter { * @param {String} name * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeReveal(name, acct, mtx) { @@ -1968,7 +2006,7 @@ class Wallet extends EventEmitter { continue; const {hash, index} = prevout; - const coin = await this.getCoin(hash, index); + const coin = await this.getUnspentCoin(hash, index); if (!coin) continue; @@ -1993,10 +2031,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = value; - output.covenant.type = types.REVEAL; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushHash(nonce); + output.covenant.setReveal(nameHash, ns.height, nonce); mtx.addOutpoint(prevout); mtx.outputs.push(output); @@ -2072,7 +2107,7 @@ class Wallet extends EventEmitter { * Make a reveal MTX. * @param {MTX?} mtx * @param {Number?} witnessSize - * @returns {MTX} + * @returns {Promise} */ async makeRevealAll(mtx, witnessSize) { @@ -2123,10 +2158,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = value; - output.covenant.type = types.REVEAL; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushHash(nonce); + output.covenant.setReveal(nameHash, ns.height, nonce); mtx.addOutpoint(prevout); mtx.outputs.push(output); @@ -2207,7 +2239,7 @@ class Wallet extends EventEmitter { * @param {String} name * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeRedeem(name, acct, mtx) { @@ -2252,7 +2284,7 @@ class Wallet extends EventEmitter { if (prevout.equals(ns.owner)) continue; - const coin = await this.getCoin(hash, index); + const coin = await this.getUnspentCoin(hash, index); if (!coin) continue; @@ -2269,9 +2301,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.REDEEM; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); + output.covenant.setRedeem(nameHash, ns.height); mtx.outputs.push(output); pushed++; @@ -2349,7 +2379,7 @@ class Wallet extends EventEmitter { * @param {String} name * @param {MTX?} mtx * @param {Number?} witnessSize - * @returns {MTX} + * @returns {Promise} */ async makeRedeemAll(mtx, witnessSize) { @@ -2394,9 +2424,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.REDEEM; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); + output.covenant.setRedeem(nameHash, ns.height); mtx.addOutpoint(prevout); mtx.outputs.push(output); @@ -2500,14 +2528,19 @@ class Wallet extends EventEmitter { throw new Error(`Auction not found: ${name}.`); const {hash, index} = ns.owner; - const coin = await this.getCoin(hash, index); + const credit = await this.getCredit(hash, index); - if (!coin) + if (!credit) throw new Error(`Wallet did not win the auction: ${name}.`); + if (credit.spent) + throw new Error(`Credit is already pending for: ${name}.`); + if (ns.isExpired(height, network)) throw new Error(`Name has expired: ${name}.`); + const coin = credit.coin; + // Is local? if (coin.height < ns.height) throw new Error(`Wallet did not win the auction: ${name}.`); @@ -2527,9 +2560,7 @@ class Wallet extends EventEmitter { output.address = coin.address; output.value = ns.value; - output.covenant.type = types.REGISTER; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); + let rawResource = EMPTY; if (resource) { const raw = resource.encode(); @@ -2540,12 +2571,12 @@ class Wallet extends EventEmitter { `(${rules.MAX_RESOURCE_SIZE}) for name: ${name}.` ); - output.covenant.push(raw); - } else { - output.covenant.push(EMPTY); + rawResource = raw; } - output.covenant.pushHash(await this.wdb.getRenewalBlock()); + const blockHash = await this.wdb.getRenewalBlock(); + + output.covenant.setRegister(nameHash, ns.height, rawResource, blockHash); if (!mtx) mtx = new MTX(); @@ -2561,7 +2592,7 @@ class Wallet extends EventEmitter { * @param {Resource} resource * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeUpdate(name, resource, acct, mtx) { @@ -2586,14 +2617,19 @@ class Wallet extends EventEmitter { throw new Error(`Auction not found: ${name}.`); const {hash, index} = ns.owner; - const coin = await this.getCoin(hash, index); + const credit = await this.getCredit(hash, index); - if (!coin) + if (!credit) throw new Error(`Wallet does not own name: ${name}.`); + if (credit.spent) + throw new Error(`Credit is already pending for: ${name}.`); + if (acct != null && !await this.txdb.hasCoinByAccount(acct, hash, index)) throw new Error(`Account does not own name: ${name}.`); + const coin = credit.coin; + if (coin.covenant.isReveal() || coin.covenant.isClaim()) return this._makeRegister(name, resource, mtx); @@ -2622,10 +2658,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.UPDATE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.push(raw); + output.covenant.setUpdate(nameHash, ns.height, raw); if (!mtx) mtx = new MTX(); @@ -2706,7 +2739,7 @@ class Wallet extends EventEmitter { * @param {String} name * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeRenewal(name, acct, mtx) { @@ -2730,14 +2763,20 @@ class Wallet extends EventEmitter { throw new Error(`Auction not found: ${name}.`); const {hash, index} = ns.owner; - const coin = await this.getCoin(hash, index); + const credit = await this.getCredit(hash, index); - if (!coin) + if (!credit) throw new Error(`Wallet does not own name: ${name}.`); + if (credit.spent) { + throw new Error(`Credit is already pending for: ${name}.`); + } + if (ns.isExpired(height, network)) throw new Error(`Name has expired: ${name}.`); + const coin = credit.coin; + // Is local? if (coin.height < ns.height) throw new Error(`Wallet does not own name: ${name}.`); @@ -2761,10 +2800,8 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.RENEW; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushHash(await this.wdb.getRenewalBlock()); + const blockHash = await this.wdb.getRenewalBlock(); + output.covenant.setRenew(nameHash, ns.height, blockHash); if (!mtx) mtx = new MTX(); @@ -2778,7 +2815,7 @@ class Wallet extends EventEmitter { * Make a renewal MTX for all expiring names. * @param {MTX?} mtx * @param {Number?} witnessSize - * @returns {MTX} + * @returns {Promise} */ async makeRenewalAll(mtx, witnessSize) { @@ -2839,10 +2876,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.RENEW; - output.covenant.pushHash(ns.nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushHash(renewalBlock); + output.covenant.setRenew(ns.nameHash, ns.height, renewalBlock); mtx.addOutpoint(new Outpoint(coin.hash, coin.index)); mtx.outputs.push(output); @@ -2925,7 +2959,7 @@ class Wallet extends EventEmitter { * @param {Address} address * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeTransfer(name, address, acct, mtx) { @@ -2950,14 +2984,19 @@ class Wallet extends EventEmitter { throw new Error(`Auction not found: ${name}.`); const {hash, index} = ns.owner; - const coin = await this.getCoin(hash, index); + const credit = await this.getCredit(hash, index); - if (!coin) + if (!credit) throw new Error(`Wallet does not own name: ${name}.`); + if (credit.spent) + throw new Error(`Credit is already pending for: ${name}.`); + if (ns.isExpired(height, network)) throw new Error(`Name has expired: ${name}.`); + const coin = credit.coin; + // Is local? if (coin.height < ns.height) throw new Error(`Wallet does not own name: ${name}.`); @@ -2968,6 +3007,9 @@ class Wallet extends EventEmitter { if (!ns.isClosed(height, network)) throw new Error(`Auction is not yet closed: ${name}.`); + if (coin.covenant.isTransfer()) + throw new Error(`Name is already being transferred: ${name}.`); + if (!coin.covenant.isRegister() && !coin.covenant.isUpdate() && !coin.covenant.isRenew() @@ -2978,11 +3020,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.TRANSFER; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushU8(address.version); - output.covenant.push(address.hash); + output.covenant.setTransfer(nameHash, ns.height, address); if (!mtx) mtx = new MTX(); @@ -3067,7 +3105,7 @@ class Wallet extends EventEmitter { * @param {String} name * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeCancel(name, acct, mtx) { @@ -3115,10 +3153,7 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.UPDATE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.push(EMPTY); + output.covenant.setUpdate(nameHash, ns.height, EMPTY); if (!mtx) mtx = new MTX(); @@ -3195,7 +3230,7 @@ class Wallet extends EventEmitter { * @param {String} name * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeFinalize(name, acct, mtx) { @@ -3219,14 +3254,19 @@ class Wallet extends EventEmitter { throw new Error(`Auction not found: ${name}.`); const {hash, index} = ns.owner; - const coin = await this.getCoin(hash, index); + const credit = await this.getCredit(hash, index); - if (!coin) + if (!credit) throw new Error(`Wallet does not own name: ${name}.`); + if (credit.spent) + throw new Error(`Credit is already pending for: ${name}.`); + if (ns.isExpired(height, network)) throw new Error(`Name has expired: ${name}.`); + const coin = credit.coin; + // Is local? if (coin.height < ns.height) throw new Error(`Wallet does not own name: ${name}.`); @@ -3255,14 +3295,15 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = address; output.value = coin.value; - output.covenant.type = types.FINALIZE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.push(rawName); - output.covenant.pushU8(flags); - output.covenant.pushU32(ns.claimed); - output.covenant.pushU32(ns.renewals); - output.covenant.pushHash(await this.wdb.getRenewalBlock()); + output.covenant.setFinalize( + nameHash, + ns.height, + rawName, + flags, + ns.claimed, + ns.renewals, + await this.wdb.getRenewalBlock() + ); if (!mtx) mtx = new MTX(); @@ -3277,7 +3318,7 @@ class Wallet extends EventEmitter { * @private * @param {MTX?} mtx * @param {Number?} witnessSize - * @returns {MTX} + * @returns {Promise} */ async makeFinalizeAll(mtx, witnessSize) { @@ -3320,14 +3361,15 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = address; output.value = coin.value; - output.covenant.type = types.FINALIZE; - output.covenant.pushHash(ns.nameHash); - output.covenant.pushU32(ns.height); - output.covenant.push(Buffer.from(ns.name, 'ascii')); - output.covenant.pushU8(flags); - output.covenant.pushU32(ns.claimed); - output.covenant.pushU32(ns.renewals); - output.covenant.pushHash(await this.wdb.getRenewalBlock()); + output.covenant.setFinalize( + ns.nameHash, + ns.height, + Buffer.from(ns.name, 'ascii'), + flags, + ns.claimed, + ns.renewals, + await this.wdb.getRenewalBlock() + ); mtx.addOutpoint(new Outpoint(coin.hash, coin.index)); mtx.outputs.push(output); @@ -3415,7 +3457,7 @@ class Wallet extends EventEmitter { * @param {String} name * @param {(Number|String)?} acct * @param {MTX?} mtx - * @returns {MTX} + * @returns {Promise} */ async makeRevoke(name, acct, mtx) { @@ -3439,14 +3481,19 @@ class Wallet extends EventEmitter { throw new Error(`Auction not found: ${name}.`); const {hash, index} = ns.owner; - const coin = await this.getCoin(hash, index); + const credit = await this.getCredit(hash, index); - if (!coin) + if (!credit) throw new Error(`Wallet does not own name: ${name}.`); + if (credit.spent) + throw new Error(`Credit is already pending for: ${name}.`); + if (acct != null && !await this.txdb.hasCoinByAccount(acct, hash, index)) throw new Error(`Account does not own name: ${name}.`); + const coin = credit.coin; + // Is local? if (coin.height < ns.height) throw new Error(`Wallet does not own name: ${name}.`); @@ -3468,9 +3515,8 @@ class Wallet extends EventEmitter { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.REVOKE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); + + output.covenant.setRevoke(nameHash, ns.height); if (!mtx) mtx = new MTX(); @@ -3544,7 +3590,7 @@ class Wallet extends EventEmitter { /** * Get account by address. * @param {Address} address - * @returns {Account} + * @returns {Promise} */ async getAccountByAddress(address) { @@ -3568,7 +3614,7 @@ class Wallet extends EventEmitter { * vsize. Input data like prevout and sequence count as base data * and must be added in outside this function. * @param {Address} addr - * @returns {Number} + * @returns {Promise} */ async estimateSize(addr) { @@ -3613,27 +3659,19 @@ class Wallet extends EventEmitter { return size; } - /** - * Build a transaction, fill it with outputs and inputs, - * sort the members according to BIP69 (set options.sort=false - * to avoid sorting), set locktime, and template it. - * @param {Object} options - See {@link Wallet#fund options}. - * @param {Object[]} options.outputs - See {@link MTX#addOutput}. - * @param {Object[]} options.outputs - See {@link MTX#addOutput}. - * @param {MTX?} mtx - * @returns {Promise} - Returns {@link MTX}. - */ + /** + * Make a transaction with normal outputs. + * @param {Object[]} outputs - See {@link MTX#addOutput} + * @param {MTX} [mtx=null] - MTX to modify instead of new one. + * @returns {MTX} - MTX with populated outputs. + */ - async createTX(options, force, mtx) { - const outputs = options.outputs; - let finish = false; - if (!mtx) { - finish = true; - mtx = new MTX(); - } + makeTX(outputs, mtx) { + assert(Array.isArray(outputs), 'output must be an array.'); + assert(outputs.length > 0, 'At least one output is required.'); - assert(Array.isArray(outputs), 'Outputs must be an array.'); - assert(outputs.length > 0, 'At least one output required.'); + if (!mtx) + mtx = new MTX(); // Add the outputs for (const obj of outputs) { @@ -3654,32 +3692,49 @@ class Wallet extends EventEmitter { mtx.outputs.push(output); } - // If a MTX was passed in to this function as an argument, - // we assume the caller is constructing a bigger TX and - // may still have more ins/out to add to it. - // That caller will have to call fund() and finalize() - // on their own when they are ready. - if (!finish) - return mtx; + return mtx; + } - // Fill the inputs with unspents - await this.fund(mtx, options, force); + /** + * Build a transaction, fill and finalize without a lock. + * @param {Object} options - See {@link Wallet#fund options}. + * @param {Object[]} options.outputs - See {@link MTX#addOutput}. + * @returns {Promise} - MTX with populated inputs and outputs. + */ + async _createTX(options) { + const mtx = this.makeTX(options.outputs); + await this.fill(mtx, options); return this.finalize(mtx, options); } + /** + * Build a transaction, fill and finalize with a lock. + * @param {Object} options - See {@link Wallet#fund options}. + * @param {Object[]} options.outputs - See {@link MTX#addOutput}. + * @returns {Promise} - Returns {@link MTX}. + */ + + async createTX(options) { + const unlock = await this.fundLock.lock(); + try { + return await this._createTX(options); + } finally { + unlock(); + } + } + /** * Make a batch transaction with multiple actions. * @param {Array} actions * @param {Object} options - * @returns {MTX} + * @returns {Promise} */ async makeBatch(actions, options) { assert(Array.isArray(actions)); assert(actions.length, 'Batches require at least one action.'); - const force = false; const acct = options ? options.account || 0 : 0; const mtx = new MTX(); @@ -3713,6 +3768,11 @@ class Wallet extends EventEmitter { } }); + // Some actions accept output addresses to avoid address reuse. + // We track that by bumping receiveIndex. + const account = await this.getAccount(acct); + let receiveIndex = account.receiveDepth - 1; + // "actions" are arrays that start with a covenant type (or meta-type) // followed by the arguments expected by the corresponding "make" function. for (const action of actions) { @@ -3720,73 +3780,86 @@ class Wallet extends EventEmitter { assert(typeof type === 'string'); switch (type) { - case 'NONE': + case 'NONE': { assert(action.length === 2); - await this.createTX( - {outputs: [{ - address: action[0], - value: action[1] - }]}, - force, - mtx - ); + this.makeTX([{ + address: action[0], + value: action[1] + }], mtx); + break; - case 'OPEN': + } + case 'OPEN': { assert(action.length === 1, 'Bad arguments for OPEN.'); - await this.makeOpen(...action, force, acct, mtx); + await this.makeOpen(...action, acct, mtx); break; - case 'BID': + } + case 'BID': { assert(action.length === 3, 'Bad arguments for BID.'); - await this.makeBid(...action, acct, mtx); + const address = account.deriveReceive(receiveIndex++).getAddress(); + await this.makeBid(...action, acct, mtx, address); break; - case 'REVEAL': + } + case 'REVEAL': { if (action.length === 1) { await this.makeReveal(...action, acct, mtx); - } else { - assert(action.length === 0, 'Bad arguments for REVEAL.'); - await this.makeRevealAll(mtx, witnessSize); + break; } + + assert(action.length === 0, 'Bad arguments for REVEAL.'); + await this.makeRevealAll(mtx, witnessSize); break; - case 'REDEEM': + } + case 'REDEEM': { if (action.length === 1) { await this.makeRedeem(...action, acct, mtx); - } else { - assert(action.length === 0, 'Bad arguments for REDEEM.'); - await this.makeRedeemAll(mtx, witnessSize); + break; } + + assert(action.length === 0, 'Bad arguments for REDEEM.'); + await this.makeRedeemAll(mtx, witnessSize); break; - case 'UPDATE': + } + case 'UPDATE': { assert(action.length === 2, 'Bad arguments for UPDATE.'); await this.makeUpdate(...action, acct, mtx); break; - case 'RENEW': + } + case 'RENEW': { if (action.length === 1) { await this.makeRenewal(...action, acct, mtx); - } else { - assert(action.length === 0, 'Bad arguments for RENEW.'); - await this.makeRenewalAll(mtx, witnessSize); + break; } + + assert(action.length === 0, 'Bad arguments for RENEW.'); + await this.makeRenewalAll(mtx, witnessSize); break; - case 'TRANSFER': + } + case 'TRANSFER': { assert(action.length === 2, 'Bad arguments for TRANSFER.'); await this.makeTransfer(...action, acct, mtx); break; - case 'FINALIZE': + } + case 'FINALIZE': { if (action.length === 1) { await this.makeFinalize(...action, acct, mtx); - } else { - assert(action.length === 0, 'Bad arguments for FINALIZE.'); - await this.makeFinalizeAll(mtx, witnessSize); + break; } + + assert(action.length === 0, 'Bad arguments for FINALIZE.'); + await this.makeFinalizeAll(mtx, witnessSize); break; - case 'CANCEL': + } + case 'CANCEL': { assert(action.length === 1, 'Bad arguments for CANCEL.'); await this.makeCancel(...action, acct, mtx); break; - case 'REVOKE': + } + case 'REVOKE': { assert(action.length === 1, 'Bad arguments for REVOKE.'); await this.makeRevoke(...action, acct, mtx); break; + } default: throw new Error(`Unknown action type: ${type}`); } @@ -3807,10 +3880,6 @@ class Wallet extends EventEmitter { // Clean up. // 1. Some actions MUST be the ONLY action for a name. // i.e. no duplicate OPENs or REVOKE/FINALIZE for same name in one tx. - // 2. Some outputs may reuse same address from this.receieveAddress(acct) - // We can bump those to the next receive address, - const account = await this.getAccount(acct); - let receiveIndex = account.receiveDepth - 1; const set = new BufferSet(); for (const output of mtx.outputs) { const {covenant} = output; @@ -3823,12 +3892,10 @@ class Wallet extends EventEmitter { case types.CLAIM: case types.OPEN: output.address = account.deriveReceive(receiveIndex++).getAddress(); - assert(!set.has(nameHash), 'Duplicate name with exclusive action.'); set.add(nameHash); break; case types.BID: - output.address = account.deriveReceive(receiveIndex++).getAddress(); case types.REVEAL: case types.REDEEM: break; @@ -3854,7 +3921,7 @@ class Wallet extends EventEmitter { * Make a batch transaction with multiple actions. * @param {Array} actions * @param {Object} options - * @returns {MTX} + * @returns {Promise} */ async _createBatch(actions, options) { @@ -3936,7 +4003,7 @@ class Wallet extends EventEmitter { * Finalize and template an MTX. * @param {MTX} mtx * @param {Object} options - * @returns {MTX} + * @returns {Promise} */ async finalize(mtx, options) { @@ -3980,10 +4047,10 @@ class Wallet extends EventEmitter { * @returns {Promise} - Returns {@link TX}. */ - async send(options, passphrase) { + async send(options) { const unlock = await this.fundLock.lock(); try { - return await this._send(options, passphrase); + return await this._send(options); } finally { unlock(); } @@ -3997,8 +4064,9 @@ class Wallet extends EventEmitter { * @returns {Promise} - Returns {@link TX}. */ - async _send(options, passphrase) { - const mtx = await this.createTX(options, true); + async _send(options) { + const passphrase = options ? options.passphrase : null; + const mtx = await this._createTX(options); return this.sendMTX(mtx, passphrase); } @@ -4006,6 +4074,7 @@ class Wallet extends EventEmitter { * Sign and send a (templated) mutable transaction. * @param {MTX} mtx * @param {String} passphrase + * @returns {Promise} */ async sendMTX(mtx, passphrase) { @@ -4326,47 +4395,12 @@ class Wallet extends EventEmitter { return paths; } - /** - * Increase lookahead for account. - * @param {(Number|String)?} account - * @param {Number} lookahead - * @returns {Promise} - */ - - async setLookahead(acct, lookahead) { - const unlock = await this.writeLock.lock(); - try { - return this._setLookahead(acct, lookahead); - } finally { - unlock(); - } - } - - /** - * Increase lookahead for account (without a lock). - * @private - * @param {(Number|String)?} account - * @param {Number} lookahead - * @returns {Promise} - */ - - async _setLookahead(acct, lookahead) { - const account = await this.getAccount(acct); - - if (!account) - throw new Error('Account not found.'); - - const b = this.db.batch(); - await account.setLookahead(b, lookahead); - await b.write(); - } - /** * Sync address depths based on a transaction's outputs. * This is used for deriving new addresses when * a confirmed transaction is seen. * @param {TX} tx - * @returns {Promise} + * @returns {Promise} - derived rings. */ async syncOutputDepth(tx) { @@ -4632,6 +4666,17 @@ class Wallet extends EventEmitter { return credit.coin; } + /** + * Get credit from the wallet. + * @param {Hash} hash + * @param {Number} index + * @returns {Promise} + */ + + getCredit(hash, index) { + return this.txdb.getCredit(hash, index); + } + /** * Get a transaction from the wallet. * @param {Hash} hash @@ -4663,7 +4708,7 @@ class Wallet extends EventEmitter { /** * Get all names. - * @returns {NameState[]} + * @returns {Promise} */ async getNames() { @@ -4700,10 +4745,21 @@ class Wallet extends EventEmitter { return this.txdb.getBlind(blind); } + /** + * Get bid + * @param {Buffer} nameHash + * @param {Outpoint} outpoint + * @returns {Promise} + */ + + async getBid(nameHash, outpoint) { + return this.txdb.getBid(nameHash, outpoint); + } + /** * Get all bids for name. * @param {Buffer} nameHash - * @returns {BlindBid[]} + * @returns {Promise} */ async getBids(nameHash) { @@ -4720,6 +4776,28 @@ class Wallet extends EventEmitter { return this.txdb.getBids(name ? rules.hashName(name) : null); } + /** + * Get bid by reveal. + * @param {Buffer} nameHash + * @param {Outpoint} outpoint - reveal outpoint + * @returns {Promise} + */ + + async getBidByReveal(nameHash, outpoint) { + return this.txdb.getBidByReveal(nameHash, outpoint); + } + + /** + * Get reveal. + * @param {Buffer} nameHash + * @param {Outpoint} outpoint + * @returns {BidReveal?} + */ + + async getReveal(nameHash, outpoint) { + return this.txdb.getReveal(nameHash, outpoint); + } + /** * Get all reveals by name. * @param {Buffer} nameHash @@ -4740,10 +4818,21 @@ class Wallet extends EventEmitter { return this.txdb.getReveals(name ? rules.hashName(name) : null); } + /** + * Get reveal for bid. + * @param {Buffer} nameHash + * @param {Outpoint} outpoint - bid outpoint + * @returns {Promise} + */ + + async getRevealByBid(nameHash, outpoint) { + return this.txdb.getRevealByBid(nameHash, outpoint); + } + /** * Add a transaction to the wallets TX history. * @param {TX} tx - * @returns {Promise} + * @returns {Promise} */ async add(tx, block) { @@ -4760,27 +4849,32 @@ class Wallet extends EventEmitter { * Potentially resolves orphans. * @private * @param {TX} tx - * @returns {Promise} + * @returns {Promise} */ async _add(tx, block) { const details = await this.txdb.add(tx, block); - if (details) { - const derived = await this.syncOutputDepth(tx); - if (derived.length > 0) { - this.wdb.emit('address', this, derived); - this.emit('address', derived); - } + if (!details) + return null; + + const derived = await this.syncOutputDepth(tx); + + if (derived.length > 0) { + this.wdb.emit('address', this, derived); + this.emit('address', derived); } - return details; + return { + details, + derived + }; } /** * Revert a block. * @param {Number} height - * @returns {Promise} + * @returns {Promise} - number of txs removed. */ async revert(height) { @@ -4807,6 +4901,23 @@ class Wallet extends EventEmitter { } } + /** + * Recalculate balances + * @returns {Promise} + */ + + async recalculateBalances() { + const unlock1 = await this.writeLock.lock(); + const unlock2 = await this.fundLock.lock(); + + try { + return await this.txdb.recalculateBalances(); + } finally { + unlock2(); + unlock1(); + } + } + /** * Zap stale TXs from wallet. * @param {(Number|String)?} acct @@ -5068,7 +5179,7 @@ class Wallet extends EventEmitter { /** * Get current receive address. * @param {Number} [acct=0] - * @returns {Address} + * @returns {Promise
} */ async receiveAddress(acct = 0) { @@ -5081,7 +5192,7 @@ class Wallet extends EventEmitter { /** * Get current change address. * @param {Number} [acct=0] - * @returns {Address} + * @returns {Promise
} */ async changeAddress(acct = 0) { diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index bf65c048d..5fb9d8b16 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -10,7 +10,7 @@ const assert = require('bsert'); const path = require('path'); const EventEmitter = require('events'); const bio = require('bufio'); -const {BloomFilter} = require('bfilter'); +const {BloomFilter} = require('@handshake-org/bfilter'); const {Lock, MapLock} = require('bmutex'); const bdb = require('bdb'); const Logger = require('blgr'); @@ -29,6 +29,11 @@ const WalletMigrator = require('./migrations'); const layout = layouts.wdb; const tlayout = layouts.txdb; const {states} = require('../covenants/namestate'); +const util = require('../utils/util'); +const {scanActions} = require('../blockchain/common'); + +/** @typedef {import('../primitives/tx')} TX */ +/** @typedef {import('../blockchain/common').ScanAction} ScanAction */ const { ChainState, @@ -37,6 +42,17 @@ const { MapRecord } = records; +/** + * @typedef {Object} AddBlockResult + * @property {Number} txs - Number of transactions added on this add. + * @property {Boolean} filterUpdated - Whether the bloom filter was updated. + */ + +/** + * @typedef {Object} AddTXResult + * @property {Number} wids - Wallet IDs affected. + * @property {Boolean} filterUpdated - Whether the bloom filter was updated. + /** * WalletDB * @alias module:wallet.WalletDB @@ -62,14 +78,20 @@ class WalletDB extends EventEmitter { this.feeRate = this.options.feeRate; this.db = bdb.create(this.options); this.name = 'wallet'; - this.version = 2; + this.version = 3; - this.primary = null; + // chain state. + this.hasStateCache = false; this.state = new ChainState(); - this.confirming = false; this.height = 0; + + // wallets + this.primary = null; this.wallets = new Map(); this.depth = 0; + + // guards + this.confirming = false; this.rescanning = false; this.filterSent = false; @@ -122,18 +144,28 @@ class WalletDB extends EventEmitter { }); this.client.on('connect', async () => { + this.emit('connect'); try { await this.syncNode(); + this.emit('sync done', this.state); } catch (e) { this.emit('error', e); } }); this.client.on('disconnect', async () => { + this.emit('disconnect'); this.filterSent = false; }); this.client.bind('block connect', async (entry, txs) => { + // If we are rescanning or doing initial sync we ignore + // block connect events. This avoids deadlocks when using + // nodeclient, but also skips unnecessary addBlock calls + // that would just repeat after the txLock is unlocked. + if (this.rescanning) + return; + try { await this.addBlock(entry, txs); } catch (e) { @@ -142,6 +174,9 @@ class WalletDB extends EventEmitter { }); this.client.bind('block disconnect', async (entry) => { + if (this.rescanning) + return; + try { await this.removeBlock(entry); } catch (e) { @@ -157,6 +192,21 @@ class WalletDB extends EventEmitter { } }); + this.client.hook('block rescan interactive', async (entry, txs) => { + try { + return await this.rescanBlockInteractive(entry, txs); + } catch (e) { + this.emit('error', e); + return { + type: scanActions.ABORT + }; + } + }); + + this.client.hook('block rescan interactive abort', async (message) => { + this.emit('error', new Error(message)); + }); + this.client.bind('tx', async (tx) => { try { await this.addTX(tx); @@ -199,11 +249,10 @@ class WalletDB extends EventEmitter { if (this.options.wipeNoReally) await this.wipe(); - await this.watch(); - await this.connect(); + await this.loadState(); this.logger.info( - 'WalletDB loaded (depth=%d, height=%d, start=%d).', + 'WalletDB is loading (depth=%d, height=%d, start=%d).', this.depth, this.state.height, this.state.startHeight); @@ -221,11 +270,23 @@ class WalletDB extends EventEmitter { this.primary = wallet; if (migrationResult.rescan) { - this.logger.info('Rescanning...'); - await this.scan(0); + if (!this.options.migrateNoRescan) { + this.logger.info('Migration rollback...'); + await this.rollback(0); + } else { + this.logger.warning( + 'Migration rescan skipped, state may be incorrect.'); + } } - await this.checkLookahead(); + if (migrationResult.recalculateTXDB) + await this.recalculateBalances(); + + await this.preloadAll(); + await this.watch(); + + this.logger.info('WalletDB opened.'); + this.emit('open'); } /** @@ -244,38 +305,20 @@ class WalletDB extends EventEmitter { } /** - * Update account lookahead & depth + * Preload all wallets. * @returns {Promise} */ - async checkLookahead() { - if (!this.options.checkLookahead) + async preloadAll() { + if (!this.options.preloadAll) return; - const b = this.db.batch(); - - const wids = await this.db.keys({ - gte: layout.W.min(), - lte: layout.W.max(), - parse: key => layout.W.decode(key)[0] - }); + this.logger.info('Preloading all wallets...'); + const wallets = await this.getWallets(); - for (const wid of wids) { - const wallet = await this.get(wid); - - for (let i = 0; i < wallet.accountDepth; i++) { - this.logger.warning( - 'Setting lookahead for wallet %s account %d', - wallet.id, - i - ); - const account = await wallet.getAccount(i); - await account.setLookahead(b, Account.MAX_LOOKAHEAD); - } - } - - await b.write(); - } + for (const wname of wallets) + await this.get(wname); + } /** * Verify network. @@ -305,14 +348,17 @@ class WalletDB extends EventEmitter { */ async close() { - await this.disconnect(); + if (this.client.opened) + await this.disconnect(); for (const wallet of this.wallets.values()) { await wallet.destroy(); this.unregister(wallet); } - return this.db.close(); + await this.db.close(); + this.logger.info('WalletDB Closed.'); + this.emit('close'); } /** @@ -401,30 +447,45 @@ class WalletDB extends EventEmitter { async syncNode() { const unlock = await this.txLock.lock(); + this.rescanning = true; try { this.logger.info('Resyncing from server...'); - await this.syncState(); + await this.syncInitState(); await this.syncFilter(); await this.syncChain(); await this.resend(); } finally { + this.rescanning = false; unlock(); } } /** - * Initialize and write initial sync state. + * Recover state from the cache. * @returns {Promise} */ - async syncState() { + async loadState() { const cache = await this.getState(); - if (cache) { - this.state = cache; - this.height = cache.height; - return undefined; - } + if (!cache) + return; + + this.logger.info('Initialized chain state from the database.'); + this.hasStateCache = true; + this.state = cache; + this.height = cache.height; + } + + /** + * Initialize and write initial sync state. + * @returns {Promise} + */ + + async syncInitState() { + // We have recovered from the cache. + if (this.hasStateCache) + return; this.logger.info('Initializing database state from server.'); @@ -455,11 +516,12 @@ class WalletDB extends EventEmitter { this.state = state; this.height = state.height; - return undefined; + return; } /** * Connect and sync with the chain server. + * Part of syncNode. * @private * @returns {Promise} */ @@ -480,22 +542,30 @@ class WalletDB extends EventEmitter { height -= 1; } - return this.scan(height); + // syncNode sets the rescanning to true. + return this.scanInteractive(height); } /** * Rescan blockchain from a given height. + * Needs this.rescanning = true to be set from the caller. * @private - * @param {Number?} height + * @param {Number} [height=this.state.startHeight] * @returns {Promise} */ async scan(height) { + assert(this.rescanning, 'WDB: Rescanning guard not set.'); + if (height == null) height = this.state.startHeight; assert((height >>> 0) === height, 'WDB: Must pass in a height.'); + this.logger.info( + 'Rolling back %d blocks.', + this.height - height + 1); + await this.rollback(height); this.logger.info( @@ -504,12 +574,39 @@ class WalletDB extends EventEmitter { const tip = await this.getTip(); - try { - this.rescanning = true; - await this.client.rescan(tip.hash); - } finally { - this.rescanning = false; - } + return this.client.rescan(tip.hash); + } + + /** + * Interactive scan blockchain from a given height. + * Expect this.rescanning to be set to true. + * @private + * @param {Number} [height=this.state.startHeight] + * @param {Boolean} [fullLock=true] + * @returns {Promise} + */ + + async scanInteractive(height, fullLock = true) { + assert(this.rescanning, 'WDB: Rescanning guard not set.'); + + if (height == null) + height = this.state.startHeight; + + assert((height >>> 0) === height, 'WDB: Must pass in a height.'); + + this.logger.info( + 'Rolling back %d blocks.', + this.height - height + 1); + + await this.rollback(height); + + this.logger.info( + 'WalletDB is scanning %d blocks.', + this.state.height - height + 1); + + const tip = await this.getTip(); + + return this.client.rescanInteractive(tip.hash, fullLock); } /** @@ -568,22 +665,18 @@ class WalletDB extends EventEmitter { lte: layout.T.max() }); - const wids = await this.db.keys({ - gte: layout.W.min(), - lte: layout.W.max(), - parse: key => layout.W.decode(key)[0] - }); + const wnames = await this.getWallets(); - for (const wid of wids) { - const wallet = await this.get(wid); + for (const wname of wnames) { + const wallet = await this.get(wname); this.logger.warning( 'Clearing all tx history for wallet: %s (%d)', - wallet.id, wid + wallet.id, wallet.wid ); // remove all txdb data *except* blinds ('v') const key = 'v'.charCodeAt(); - const prefix = layout.t.encode(wid); + const prefix = layout.t.encode(wallet.wid); await removeRange({ gte: Buffer.concat([prefix, Buffer.alloc(1)]), lt: Buffer.concat([prefix, Buffer.from([key])]) @@ -607,6 +700,7 @@ class WalletDB extends EventEmitter { async rescan(height) { const unlock = await this.txLock.lock(); + try { return await this._rescan(height); } finally { @@ -622,7 +716,42 @@ class WalletDB extends EventEmitter { */ async _rescan(height) { - return this.scan(height); + this.rescanning = true; + + try { + return await this.scanInteractive(height); + } finally { + this.rescanning = false; + } + } + + /** + * Recalculate balances from the coins. + * @returns {Promise} + */ + + async recalculateBalances() { + const unlock = await this.txLock.lock(); + + try { + return await this._recalculateBalances(); + } finally { + unlock(); + } + } + + /** + * Recalculate balances from the coins (without a lock). + * @returns {Promise} + */ + + async _recalculateBalances() { + const wnames = await this.getWallets(); + + for (const wname of wnames) { + const wallet = await this.get(wname); + await wallet.recalculateBalances(); + } } /** @@ -877,7 +1006,7 @@ class WalletDB extends EventEmitter { /** * Map wallet id to wid. * @param {String|Number} id - * @returns {Promise} - Returns {Number}. + * @returns {Promise} */ async ensureWID(id) { @@ -893,7 +1022,7 @@ class WalletDB extends EventEmitter { /** * Map wallet id to wid. * @param {String} id - * @returns {Promise} - Returns {Number}. + * @returns {Promise} */ async getWID(id) { @@ -910,7 +1039,7 @@ class WalletDB extends EventEmitter { /** * Map wallet wid to id. * @param {Number} wid - * @returns {Promise} - Returns {String}. + * @returns {Promise} */ async getID(wid) { @@ -925,7 +1054,7 @@ class WalletDB extends EventEmitter { /** * Get a wallet from the database, setup watcher. * @param {Number|String} id - * @returns {Promise} - Returns {@link Wallet}. + * @returns {Promise} */ async get(id) { @@ -947,7 +1076,7 @@ class WalletDB extends EventEmitter { * Get a wallet from the database without a lock. * @private * @param {Number} wid - * @returns {Promise} - Returns {@link Wallet}. + * @returns {Promise} */ async _get(wid) { @@ -1582,8 +1711,8 @@ class WalletDB extends EventEmitter { } /** - * Get all wallet ids. - * @returns {Promise} + * Get all wallet names. + * @returns {Promise} */ async getWallets() { @@ -1803,13 +1932,24 @@ class WalletDB extends EventEmitter { /** * Sync the current chain state to tip. * @param {BlockMeta} tip + * @param {Boolean} checkMark - should we check startHeight/mark. This should + * only happen if we are progressing forward in history and have txs. * @returns {Promise} */ - async setTip(tip) { + async setTip(tip, checkMark = false) { const b = this.db.batch(); const state = this.state.clone(); + // mark state if state has not been marked, we are moving forward + // and we have txs. If state is marked, it means we already found + // first tx for the whole wdb, so no longer move it forward. + if (checkMark && !state.marked) { + state.startHeight = tip.height; + state.startHash = tip.hash; + state.marked = true; + } + if (tip.height < state.height) { // Hashes ahead of our new tip // that we need to delete. @@ -1855,38 +1995,34 @@ class WalletDB extends EventEmitter { } /** - * Mark current state. - * @param {BlockMeta} block + * Get a wallet map. + * @param {Buffer} key * @returns {Promise} */ - async markState(block) { - const state = this.state.clone(); - state.startHeight = block.height; - state.startHash = block.hash; - state.marked = true; + async getMap(key) { + const data = await this.db.get(key); - const b = this.db.batch(); - b.put(layout.R.encode(), state.encode()); - await b.write(); + if (!data) + return null; - this.state = state; - this.height = state.height; + return MapRecord.decode(data); } /** - * Get a wallet map. + * Does wdb have wallet map. * @param {Buffer} key - * @returns {Promise} + * @param {Number} wid + * @returns {Promise} */ - async getMap(key) { - const data = await this.db.get(key); + async hasMap(key, wid) { + const map = await this.getMap(key); - if (!data) - return null; + if (!map) + return false; - return MapRecord.decode(data); + return map.has(wid); } /** @@ -2055,7 +2191,7 @@ class WalletDB extends EventEmitter { */ async addOutpointMap(b, hash, index, wid) { - await this.addOutpoint(hash, index); + this.addOutpoint(hash, index); return this.addMap(b, layout.o.encode(hash, index), wid); } @@ -2080,6 +2216,17 @@ class WalletDB extends EventEmitter { return this.getMap(layout.N.encode(nameHash)); } + /** + * Has wid in the wallet map. + * @param {Buffer} nameHash + * @param {Number} wid + * @returns {Promise} + */ + + async hasNameMap(nameHash, wid) { + return this.hasMap(layout.N.encode(nameHash), wid); + } + /** * Add wid to a wallet map. * @param {Wallet} wallet @@ -2105,8 +2252,8 @@ class WalletDB extends EventEmitter { /** * Get a wallet block meta. - * @param {Hash} hash - * @returns {Promise} + * @param {Number} height + * @returns {Promise} */ async getBlock(height) { @@ -2121,7 +2268,7 @@ class WalletDB extends EventEmitter { /** * Get wallet tip. * @param {Hash} hash - * @returns {Promise} + * @returns {Promise} */ async getTip() { @@ -2173,13 +2320,13 @@ class WalletDB extends EventEmitter { assert(tip); await this.revert(tip.height); - await this.setTip(tip); + await this.setTip(tip, false); } /** * Revert TXDB to an older state. * @param {Number} target - * @returns {Promise} + * @returns {Promise} */ async revert(target) { @@ -2195,6 +2342,7 @@ class WalletDB extends EventEmitter { await iter.each(async (key, value) => { const [height] = layout.b.decode(key); const block = MapRecord.decode(value); + this.logger.info('Reverting block: %d', height); for (const wid of block.wids) { const wallet = await this.get(wid); @@ -2204,16 +2352,19 @@ class WalletDB extends EventEmitter { }); this.logger.info('Rolled back %d WalletDB transactions.', total); + return total; } /** * Add a block's transactions and write the new best hash. * @param {ChainEntry} entry - * @returns {Promise} + * @param {TX[]} txs + * @returns {Promise} */ async addBlock(entry, txs) { const unlock = await this.txLock.lock(); + try { return await this._addBlock(entry, txs); } finally { @@ -2226,7 +2377,7 @@ class WalletDB extends EventEmitter { * @private * @param {ChainEntry} entry * @param {TX[]} txs - * @returns {Promise} + * @returns {Promise} */ async _addBlock(entry, txs) { @@ -2236,7 +2387,18 @@ class WalletDB extends EventEmitter { this.logger.warning( 'WalletDB is connecting low blocks (%d).', tip.height); - return 0; + + const block = await this.getBlock(tip.height); + assert(block); + + if (!entry.hash.equals(block.hash)) { + // Maybe we run syncChain here. + this.logger.warning( + 'Unusual reorg at low height (%d).', + tip.height); + } + + return null; } if (tip.height >= this.network.block.slowHeight) @@ -2250,12 +2412,41 @@ class WalletDB extends EventEmitter { // updated before the block was fully // processed (in the case of a crash). this.logger.warning('Already saw WalletDB block (%d).', tip.height); + + const block = await this.getBlock(tip.height); + assert(block); + + if (!entry.hash.equals(block.hash)) { + this.logger.warning( + 'Unusual reorg at the same height (%d).', + tip.height); + + // Maybe we can run syncChain here. + return null; + } } else if (tip.height !== this.state.height + 1) { - await this.scan(this.state.height); - return 0; + await this._rescan(this.state.height); + return null; } - const walletTxs = []; + let block; + + if (tip.height > 2) { + block = await this.getBlock(tip.height - 1); + assert(block); + } + + if (block && !block.hash.equals(entry.prevBlock)) { + // We can trigger syncChain here as well. + this.logger.warning( + 'Unusual reorg at height (%d).', + tip.height); + + return null; + } + + const walletTXs = []; + let filterUpdated = false; try { // We set the state as confirming so that @@ -2263,33 +2454,43 @@ class WalletDB extends EventEmitter { // increment by one until the block is fully // added and the height is updated. this.confirming = true; + for (const tx of txs) { - if (await this._addTX(tx, tip)) { - walletTxs.push(tx); + const txadded = await this._addTX(tx, tip); + + if (txadded) { + walletTXs.push(tx); + + if (txadded.filterUpdated) + filterUpdated = true; } } // Sync the state to the new tip. - await this.setTip(tip); + // If we encountered wallet txs, we also trigger mark check. + await this.setTip(tip, walletTXs.length > 0); } finally { this.confirming = false; } - if (walletTxs.length > 0) { + if (walletTXs.length > 0) { this.logger.info('Connected WalletDB block %x (tx=%d).', - tip.hash, walletTxs.length); + tip.hash, walletTXs.length); } - this.emit('block connect', entry, walletTxs); + this.emit('block connect', entry, walletTXs); - return walletTxs.length; + return { + txs: walletTXs.length, + filterUpdated: filterUpdated + }; } /** * Unconfirm a block's transactions * and write the new best hash (SPV version). * @param {ChainEntry} entry - * @returns {Promise} + * @returns {Promise} - number of txs removed. */ async removeBlock(entry) { @@ -2305,7 +2506,7 @@ class WalletDB extends EventEmitter { * Unconfirm a block's transactions. * @private * @param {ChainEntry} entry - * @returns {Promise} + * @returns {Promise} - number of txs removed. */ async _removeBlock(entry) { @@ -2331,7 +2532,7 @@ class WalletDB extends EventEmitter { const map = await this.getBlockMap(tip.height); if (!map) { - await this.setTip(prev); + await this.setTip(prev, false); this.emit('block disconnect', entry); return 0; } @@ -2345,7 +2546,7 @@ class WalletDB extends EventEmitter { } // Sync the state to the previous tip. - await this.setTip(prev); + await this.setTip(prev, false); this.logger.warning('Disconnected wallet block %x (tx=%d).', tip.hash, total); @@ -2382,12 +2583,47 @@ class WalletDB extends EventEmitter { } } + /** + * Rescan a block interactively. + * @param {ChainEntry} entry + * @param {TX[]} txs + * @returns {Promise} - interactive action + */ + + async rescanBlockInteractive(entry, txs) { + if (!this.rescanning) + throw new Error(`WDB: Unsolicited rescan block: ${entry.height}.`); + + if (entry.height > this.state.height + 1) + throw new Error(`WDB: Rescan block too high: ${entry.height}.`); + + const blockAdded = await this._addBlock(entry, txs); + + if (!blockAdded) + throw new Error('WDB: Block not added.'); + + if (blockAdded.filterUpdated) { + // We remove block, because adding the same block twice, will ignore + // already indexed transactions. This handles the case where single + // transaction has undiscovered outputs. + await this._removeBlock(entry); + + return { + type: scanActions.REPEAT + }; + } + + return { + type: scanActions.NEXT + }; + } + /** * Add a transaction to the database, map addresses * to wallet IDs, potentially store orphans, resolve * orphans, or confirm a transaction. * @param {TX} tx - * @returns {Promise} + * @returns {Promise} */ async addTX(tx) { @@ -2404,7 +2640,7 @@ class WalletDB extends EventEmitter { * @private * @param {TX} tx * @param {BlockMeta} block - * @returns {Promise} + * @returns {Promise} */ async _addTX(tx, block) { @@ -2415,14 +2651,12 @@ class WalletDB extends EventEmitter { if (!wids) return null; - if (block && !this.state.marked) - await this.markState(block); - this.logger.info( 'Incoming transaction for %d wallets in WalletDB (%s).', wids.size, tx.txid()); let result = false; + let filterUpdated = false; // Insert the transaction // into every matching wallet. @@ -2431,18 +2665,27 @@ class WalletDB extends EventEmitter { assert(wallet); - if (await wallet.add(tx, block)) { + const wadded = await wallet.add(tx, block); + + if (wadded) { + result = true; + + if (wadded.derived.length > 0) + filterUpdated = true; + this.logger.info( 'Added transaction to wallet in WalletDB: %s (%d).', wallet.id, wid); - result = true; } } if (!result) return null; - return wids; + return { + wids, + filterUpdated + }; } /** @@ -2504,7 +2747,11 @@ class WalletOptions { this.spv = false; this.wipeNoReally = false; this.walletMigrate = -1; - this.checkLookahead = false; + this.icannlockup = true; + this.migrateNoRescan = false; + this.preloadAll = false; + + this.nowFn = util.now; if (options) this.fromOptions(options); @@ -2587,9 +2834,24 @@ class WalletOptions { this.walletMigrate = options.walletMigrate; } - if (options.checkLookahead != null) { - assert(typeof options.checkLookahead === 'boolean'); - this.checkLookahead = options.checkLookahead; + if (options.icannlockup != null) { + assert(typeof options.icannlockup === 'boolean'); + this.icannlockup = options.icannlockup; + } + + if (options.migrateNoRescan != null) { + assert(typeof options.migrateNoRescan === 'boolean'); + this.migrateNoRescan = options.migrateNoRescan; + } + + if (options.preloadAll != null) { + assert(typeof options.preloadAll === 'boolean'); + this.preloadAll = options.preloadAll; + } + + if (options.nowFn != null) { + assert(typeof options.nowFn === 'function'); + this.nowFn = options.nowFn; } return this; @@ -2623,6 +2885,11 @@ function fromString(str) { return buf; } +/** + * @param {Buffer} buf + * @returns {String} + */ + function toString(buf) { assert(buf.length > 0); assert(buf[0] === buf.length - 1); diff --git a/lib/workers/master.js b/lib/workers/master.js index 8b5ecf61f..0016ebb79 100644 --- a/lib/workers/master.js +++ b/lib/workers/master.js @@ -15,7 +15,7 @@ const Parser = require('./parser'); const Framer = require('./framer'); const packets = require('./packets'); const Parent = require('./parent'); -const ownership = require('../covenants/ownership'); +const {ownership} = require('../covenants/ownership'); /** * Master diff --git a/lib/workers/workerpool.js b/lib/workers/workerpool.js index adb31031d..c252b308e 100644 --- a/lib/workers/workerpool.js +++ b/lib/workers/workerpool.js @@ -17,7 +17,7 @@ const jobs = require('./jobs'); const Parser = require('./parser'); const Framer = require('./framer'); const packets = require('./packets'); -const ownership = require('../covenants/ownership'); +const {ownership} = require('../covenants/ownership'); /** * Worker Pool @@ -234,7 +234,7 @@ class WorkerPool extends EventEmitter { * Execute the tx signing job (default timeout). * @method * @param {MTX} tx - * @param {KeyRing[]} ring + * @param {KeyRing|KeyRing[]} ring * @param {SighashType} type * @returns {Promise} */ diff --git a/package-lock.json b/package-lock.json index 541f4a222..0bdccc137 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,41 +1,41 @@ { "name": "hsd", - "version": "5.99.0", - "lockfileVersion": 2, + "version": "6.99.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hsd", - "version": "5.99.0", + "version": "6.99.0", "license": "MIT", "dependencies": { - "bcfg": "~0.1.7", + "@handshake-org/bfilter": "~2.3.0", + "bcfg": "~0.2.2", "bcrypto": "~5.4.0", - "bdb": "~1.4.0", + "bcurl": "~0.2.1", + "bdb": "~1.6.0", "bdns": "~0.1.5", - "bevent": "~0.1.5", - "bfile": "~0.2.2", - "bfilter": "~1.0.5", - "bheep": "~0.1.5", - "binet": "~0.3.7", - "blgr": "~0.2.0", - "blru": "~0.1.6", - "blst": "~0.1.5", - "bmutex": "~0.1.6", + "bevent": "~0.1.6", + "bfile": "~0.2.3", + "bheep": "~0.1.6", + "binet": "~0.3.9", + "blgr": "~0.2.1", + "blru": "~0.1.7", + "blst": "~0.1.6", + "bmutex": "~0.1.7", "bns": "~0.15.0", - "bsert": "~0.0.10", - "bsock": "~0.1.9", + "bsert": "~0.0.13", + "bsock": "~0.1.11", "bsocks": "~0.2.6", "btcp": "~0.1.5", "buffer-map": "~0.0.7", - "bufio": "~1.2.0", + "bufio": "~1.2.1", "bupnp": "~0.2.6", - "bval": "~0.1.6", - "bweb": "~0.1.11", + "bval": "~0.1.8", + "bweb": "~0.2.0", "goosig": "~0.10.0", - "hs-client": "~0.0.13", "n64": "~0.2.10", - "urkel": "~1.0.2" + "urkel": "~1.0.3" }, "bin": { "hs-seeder": "bin/hs-seeder", @@ -43,27 +43,42 @@ "hsd": "bin/hsd", "hsd-cli": "bin/hsd-cli", "hsd-node": "bin/node", + "hsd-rpc": "bin/hsd-rpc", "hsd-spvnode": "bin/spvnode", - "hsw-cli": "bin/hsw-cli" + "hsw-cli": "bin/hsw-cli", + "hsw-rpc": "bin/hsw-rpc" }, "devDependencies": { - "bmocha": "^2.1.8" + "bmocha": "^2.2.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/bcfg": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/bcfg/-/bcfg-0.1.7.tgz", - "integrity": "sha512-+4beq5bXwfmxdcEoHYQsaXawh1qFzjLcRvPe5k5ww/NEWzZTm56Jk8LuPmfeGB7X584jZ8xGq6UgMaZnNDa5Ww==", + "node_modules/@handshake-org/bfilter": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@handshake-org/bfilter/-/bfilter-2.3.0.tgz", + "integrity": "sha512-vTKTVJvLHz2knpdnYMT0idb6R+HlOCbYKlw2L9Bk9oKOAXwjOIFUp6hnZKIVb87rYW8eEfUROrFG3+DcYwxm7w==", "dependencies": { - "bsert": "~0.0.10" + "bcrypto": "~5.4.0", + "bsert": "~0.0.12", + "bufio": "~1.2.1" }, "engines": { "node": ">=8.0.0" } }, + "node_modules/bcfg": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/bcfg/-/bcfg-0.2.2.tgz", + "integrity": "sha512-xa7hYK8ZgEV/Wjh+EJiKLLd+h8A0HGyhyntNMvKCeXIGepLqKUL3KYOE5zFz8EBv8sS3XruD5YPmYIjtwFOrZA==", + "dependencies": { + "bsert": "~0.0.12" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/bcrypto": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/bcrypto/-/bcrypto-5.4.0.tgz", @@ -86,29 +101,29 @@ } }, "node_modules/bcurl": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bcurl/-/bcurl-0.2.0.tgz", - "integrity": "sha512-uBWc/w3AqjAfo6/+ODoRSoY/w/C7UaU/9AYcXjxgObTyUf3lvV5jCuAU/dSZyWysDyWBQkPzllOd7KZkwJHnwg==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/bcurl/-/bcurl-0.2.1.tgz", + "integrity": "sha512-7L00pT9SQEDzRIsRn53Il5pYHkeFlIoH0SdNIAbXdqsUMnq4IteTRuSZwC2f/uUKiRE5oW40MBhW72mquO9sQg==", "dependencies": { - "brq": "~0.1.8", - "bsert": "~0.0.10", - "bsock": "~0.1.9" + "brq": "~0.1.10", + "bsert": "~0.0.12", + "bsock": "~0.1.10" }, "engines": { "node": ">=8.0.0" } }, "node_modules/bdb": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/bdb/-/bdb-1.4.0.tgz", - "integrity": "sha512-NjsvznNQSW419u/VlitEioAglJd44n6MrOI+6Rf9JqlyF6DQytBh8bwCT3axUw095aUlGtvoscJG3C56pIPQ7Q==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/bdb/-/bdb-1.6.0.tgz", + "integrity": "sha512-xsStfCpiTCr4vae+wrfQQVIZVS7peLEZwUu8HyHYIwoauFXgqxh7QatIptp2lgg8CjpIGXwwHE3TZO0+uZSsXA==", "hasInstallScript": true, "dependencies": { - "bsert": "~0.0.10", + "bsert": "~0.0.13", "loady": "~0.0.5" }, "engines": { - "node": ">=8.6.0" + "node": ">=12.0.0" } }, "node_modules/bdns": { @@ -123,105 +138,84 @@ } }, "node_modules/bevent": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/bevent/-/bevent-0.1.5.tgz", - "integrity": "sha512-hs6T3BjndibrAmPSoKTHmKa3tz/c6Qgjv9iZw+tAoxuP6izfTCkzfltBQrW7SuK5xnY22gv9jCEf51+mRH+Qvg==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bevent/-/bevent-0.1.6.tgz", + "integrity": "sha512-vu1MYZIbZhz8s4/QDuqJ06L8BloELnMJxf/bGfp5ifqNpKpdCvgDw3ymsyzu27JJinDiGvx775V5NnTmWR7tFA==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" } }, "node_modules/bfile": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/bfile/-/bfile-0.2.2.tgz", - "integrity": "sha512-X205SsJ7zFAnjeJ/pBLqDqF10x/4Su3pBy8UdVKw4hdGJk7t5pLoRi+uG4rPaDAClGbrEfT/06PGUbYiMYKzTg==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/bfilter": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bfilter/-/bfilter-1.0.5.tgz", - "integrity": "sha512-GupIidtCvLbKhXnA1sxvrwa+gh95qbjafy7P1U1x/2DHxNabXq4nGW0x3rmgzlJMYlVl+c8fMxoMRIwpKYlgcQ==", - "dependencies": { - "bsert": "~0.0.10", - "bufio": "~1.0.6", - "mrmr": "~0.1.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/bfilter/node_modules/bufio": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", - "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/bfile/-/bfile-0.2.3.tgz", + "integrity": "sha512-BhbmCLqDC+u8rPSeB/I8bRC8luQoUt+wD326CECXYXtE5GyTWL/q/OkNp58aH7XEREguEItvqM18s9vXLvg6fw==", "engines": { "node": ">=8.0.0" } }, "node_modules/bheep": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/bheep/-/bheep-0.1.5.tgz", - "integrity": "sha512-0KR5Zi8hgJBKL35+aYzndCTtgSGakOMxrYw2uszd5UmXTIfx3+drPGoETlVbQ6arTdAzSoQYA1j35vbaWpQXBg==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bheep/-/bheep-0.1.6.tgz", + "integrity": "sha512-u44Xgb0Rr+Y/1chKqiUSY44mpBCglDxnapyfA6mssbJE+C7iyAPY4r8WMPqmdvZPYk7EU3ogbt4pKDItDMU87A==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" } }, "node_modules/binet": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/binet/-/binet-0.3.7.tgz", - "integrity": "sha512-GF+QD4ajs3GWabaVzso7Kn9aZEbwI0e54FKU2ID8bM/7rIk7BpSJytB1KS7SMpix+fWAi9MAGkOgSFljl0aaKg==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/binet/-/binet-0.3.9.tgz", + "integrity": "sha512-htptPuT5YTTRThIQAuWyCo+rIvXwAC+CrUq40ldhKHBPbZoMb76SBkI8NtvLkWussJr+lnR5Mc0rh1dpNBrBPg==", "dependencies": { - "bs32": "~0.1.5", - "bsert": "~0.0.10" + "bs32": "~0.1.7", + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" } }, "node_modules/blgr": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/blgr/-/blgr-0.2.0.tgz", - "integrity": "sha512-2jZdqajYCGD5rwGdOooQpxgjKsiAAV2g8LapwSnbTjAYTZAqmqBAS+GsVGFi+/y7t1Pspidv/5HsWBbJrsEuFw==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/blgr/-/blgr-0.2.1.tgz", + "integrity": "sha512-ShNWkj8qxjf0JEwDt1h675Lgx9EV8a2lRWVWtZrptqg/tIrISv0TM7s+NVNXEwyIlQYYIu4lQqjlGRYWUn4g5Q==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" } }, "node_modules/blru": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/blru/-/blru-0.1.6.tgz", - "integrity": "sha512-34+xZ2u4ys/aUzWCU9m6Eee4nVuN1ywdxbi8b3Z2WULU6qvnfeHvCWEdGzlVfRbbhimG2xxJX6R77GD2cuVO6w==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/blru/-/blru-0.1.7.tgz", + "integrity": "sha512-FzSf/LgZp0wu2kzT2xt7C73pKfyJurCzKkhkVdbYS5GgO8rNxQjadyZcFAGwNAcHfjxV48HPhdh5vn4S7rr3aA==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" } }, "node_modules/blst": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/blst/-/blst-0.1.5.tgz", - "integrity": "sha512-TPl04Cx3CHdPFAJ2x9Xx1Z1FOfpAzmNPfHkfo+pGAaNH4uLhS58ExvamVkZh3jadF+B7V5sMtqvrqdf9mHINYA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/blst/-/blst-0.1.6.tgz", + "integrity": "sha512-P88SBVTdjKsvltTGXPayu9ACSZ36CNpECE8eZjPg+Mj++EB/VHOFUAnVIZ142NceSWYkVMBKiVENH49YHKHjFg==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" } }, "node_modules/bmocha": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/bmocha/-/bmocha-2.1.8.tgz", - "integrity": "sha512-bog23Ckl9lRyBxrsi4FmX1rTz4d1WhHRpIA+q2lpoiXmNuroMHr1JUOU5sPiMZwvhLCxqffvWv3xCJC7PC126w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bmocha/-/bmocha-2.2.0.tgz", + "integrity": "sha512-f0o/na7RLjoUzcrx3wqyGaOXsPcAlAo7Q2NybKfcUM3udmaDTXMr6Zbo0IZup+BG7ah8jYjk7AK+5tiboirH+g==", "dev": true, "bin": { "_bmocha": "bin/_bmocha", @@ -232,11 +226,11 @@ } }, "node_modules/bmutex": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/bmutex/-/bmutex-0.1.6.tgz", - "integrity": "sha512-nXWOXtQHbfPaMl6jyEF/rmRMrcemj2qn+OCAI/uZYurjfx7Dg3baoXdPzHOL0U8Cfvn8CWxKcnM/rgxL7DR4zw==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/bmutex/-/bmutex-0.1.7.tgz", + "integrity": "sha512-czuDM58Y1LCT7V0t9NHd0j4l7A3IBovXNvWrmmCcdKsulEgGcahTGqrWNidvWFv2dhiuvQmUzaLf6AT00+FLFw==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" @@ -286,20 +280,20 @@ } }, "node_modules/brq": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/brq/-/brq-0.1.8.tgz", - "integrity": "sha512-6SDY1lJMKXgt5TZ6voJQMH2zV1XPWWtm203PSkx3DSg9AYNYuRfOPFSBDkNemabzgpzFW9/neR4YhTvyJml8rQ==", + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/brq/-/brq-0.1.10.tgz", + "integrity": "sha512-iil4TtQWw9Wb2G+mEP0iHqM8Q16mHINJzR5wHTsfKZTtcOVoEGj6yX3ed7yLQ92KR4QO9KjlrlO7/Y7766i7Tw==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" } }, "node_modules/bs32": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/bs32/-/bs32-0.1.6.tgz", - "integrity": "sha512-usjDesQqZ8ihHXOnOEQuAdymBHnJEfSd+aELFSg1jN/V3iAf12HrylHlRJwIt6DTMmXpBDQ+YBg3Q3DIYdhRgQ==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/bs32/-/bs32-0.1.7.tgz", + "integrity": "sha512-I0aKZCBneFTkcVNUIALzsOevqPIF93ynGPX6wj6pXmIBpXVGCqTgdvMx2QyR/NwgOIqMLH++Ovyum28abVG6QA==", "dependencies": { "bsert": "~0.0.10" }, @@ -308,19 +302,19 @@ } }, "node_modules/bsert": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/bsert/-/bsert-0.0.10.tgz", - "integrity": "sha512-NHNwlac+WPy4t2LoNh8pXk8uaIGH3NSaIUbTTRXGpE2WEbq0te/tDykYHkFK57YKLPjv/aGHmbqvnGeVWDz57Q==", + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/bsert/-/bsert-0.0.13.tgz", + "integrity": "sha512-gYzSj8I2lDTKvl4aRSYs2CZIpeJugq7RjGhLRG+Jl//gEW5B2u1MKB6exVCL09FqYj6JRQAAgRwQHMOWvr7A8A==", "engines": { "node": ">=8.0.0" } }, "node_modules/bsock": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/bsock/-/bsock-0.1.9.tgz", - "integrity": "sha512-/l9Kg/c5o+n/0AqreMxh2jpzDMl1ikl4gUxT7RFNe3A3YRIyZkiREhwcjmqxiymJSRI/Qhew357xGn1SLw/xEw==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/bsock/-/bsock-0.1.11.tgz", + "integrity": "sha512-4COhlKKBfOQOomNvz1hjoPtN5ytpqbxkuCPvIPbYMvaZwNBKpo8dZa1LJjcN3wuAkjIIJLF/fc4o3e1DYiceQw==", "dependencies": { - "bsert": "~0.0.10" + "bsert": "~0.0.12" }, "engines": { "node": ">=8.0.0" @@ -363,11 +357,11 @@ } }, "node_modules/bufio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.2.0.tgz", - "integrity": "sha512-UlFk8z/PwdhYQTXSQQagwGAdtRI83gib2n4uy4rQnenxUM2yQi8lBDzF230BNk+3wAoZDxYRoBwVVUPgHa9MCA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.2.1.tgz", + "integrity": "sha512-9oR3zNdupcg/Ge2sSHQF3GX+kmvL/fTPvD0nd5AGLq8SjUYnTz+SlFjK/GXidndbZtIj+pVKXiWeR9w6e9wKCA==", "engines": { - "node": ">=8.0.0" + "node": ">=14.0.0" } }, "node_modules/bupnp": { @@ -384,9 +378,9 @@ } }, "node_modules/bval": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/bval/-/bval-0.1.6.tgz", - "integrity": "sha512-jxNH9gSx7g749hQtS+nTxXYz/bLxwr4We1RHFkCYalNYcj12RfbW6qYWsKu0RYiKAdFcbNoZRHmWrIuXIyhiQQ==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/bval/-/bval-0.1.8.tgz", + "integrity": "sha512-38WQyq94sgKaJbHSmkOwZqba6Ac0KIKPO0SMDNg/mCcwUos2NIrMg5Bb2LkzIer+RzS186IYusNeSrJrKdaqhA==", "dependencies": { "bsert": "~0.0.10" }, @@ -395,12 +389,12 @@ } }, "node_modules/bweb": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/bweb/-/bweb-0.1.11.tgz", - "integrity": "sha512-zi9FtNpPD+Mv9UAZFRzV3jnUcEDPJTeUS8JDvsR9Nlq4fwc93SBJUui0PrC1U1GO8zzopoXtoOZ5Do9L46Rv2Q==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bweb/-/bweb-0.2.0.tgz", + "integrity": "sha512-JfpXemYqylNySwrhR7b4HZTrxnDhbOzNiIXCPBVQU6O8rTZ1wFDLFDr/7uQqkwzjyNZ4ZWTp5wP/pJY2IizfDA==", "dependencies": { "bsert": "~0.0.10", - "bsock": "~0.1.8" + "bsock": "~0.1.9" }, "bin": { "bweb": "bin/bweb" @@ -423,25 +417,6 @@ "node": ">=8.0.0" } }, - "node_modules/hs-client": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/hs-client/-/hs-client-0.0.13.tgz", - "integrity": "sha512-3Vm/4S0TDstbOW+OfdTeP2EQ4dolPNqMulTSr31RihwX8cX1DyT4il1Fc9STXXToXTsZuFro2WD/+1m0MWi5Ag==", - "dependencies": { - "bcfg": "~0.1.7", - "bcurl": "~0.2.0", - "bsert": "~0.0.10" - }, - "bin": { - "hsd-cli": "bin/hsd-cli", - "hsd-rpc": "bin/hsd-rpc", - "hsw-cli": "bin/hsw-cli", - "hsw-rpc": "bin/hsw-rpc" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/loady": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/loady/-/loady-0.0.5.tgz", @@ -450,19 +425,6 @@ "node": ">=8.0.0" } }, - "node_modules/mrmr": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/mrmr/-/mrmr-0.1.10.tgz", - "integrity": "sha512-NJRJs+yJyRWwcTqLRf7O32n56UP1+UQoTrGVEoB3LMj0h2jlon790drDbxKvi5mK5k4HfC0cpNkxqHcrJK/evg==", - "hasInstallScript": true, - "dependencies": { - "bsert": "~0.0.10", - "loady": "~0.0.5" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/n64": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/n64/-/n64-0.2.10.tgz", @@ -485,9 +447,9 @@ } }, "node_modules/urkel": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/urkel/-/urkel-1.0.2.tgz", - "integrity": "sha512-Y5UXbgBr6pczrD08N0SYJkWjtdtTTpmZsOvuftdrEHLnTjuxwSNjKsXYLQkICTptvnHAJ2OjI6XdAxtYTyOHew==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/urkel/-/urkel-1.0.3.tgz", + "integrity": "sha512-L2M46WWSaz1LpyUYFgnQg7WSOWtNcRx3uH+4GwHK1jbmYj6phLuIwirTVMlhfcZ0o/CWn5Y04UWLhmlvijZiDg==", "dependencies": { "bfile": "~0.2.1", "bmutex": "~0.1.6", @@ -497,310 +459,5 @@ "node": ">=8.0.0" } } - }, - "dependencies": { - "bcfg": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/bcfg/-/bcfg-0.1.7.tgz", - "integrity": "sha512-+4beq5bXwfmxdcEoHYQsaXawh1qFzjLcRvPe5k5ww/NEWzZTm56Jk8LuPmfeGB7X584jZ8xGq6UgMaZnNDa5Ww==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bcrypto": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/bcrypto/-/bcrypto-5.4.0.tgz", - "integrity": "sha512-KDX2CR29o6ZoqpQndcCxFZAtYA1jDMnXU3jmCfzP44g++Cu7AHHtZN/JbrN/MXAg9SLvtQ8XISG+eVD9zH1+Jg==", - "requires": { - "bufio": "~1.0.7", - "loady": "~0.0.5" - }, - "dependencies": { - "bufio": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", - "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==" - } - } - }, - "bcurl": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bcurl/-/bcurl-0.2.0.tgz", - "integrity": "sha512-uBWc/w3AqjAfo6/+ODoRSoY/w/C7UaU/9AYcXjxgObTyUf3lvV5jCuAU/dSZyWysDyWBQkPzllOd7KZkwJHnwg==", - "requires": { - "brq": "~0.1.8", - "bsert": "~0.0.10", - "bsock": "~0.1.9" - } - }, - "bdb": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/bdb/-/bdb-1.4.0.tgz", - "integrity": "sha512-NjsvznNQSW419u/VlitEioAglJd44n6MrOI+6Rf9JqlyF6DQytBh8bwCT3axUw095aUlGtvoscJG3C56pIPQ7Q==", - "requires": { - "bsert": "~0.0.10", - "loady": "~0.0.5" - } - }, - "bdns": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/bdns/-/bdns-0.1.5.tgz", - "integrity": "sha512-LNVkfM7ynlAD0CvPvO9cKxW8YXt1KOCRQZlRsGZWeMyymUWVdHQpZudAzH9chaFAz6HiwAnQxwDemCKDPy6Mag==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bevent": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/bevent/-/bevent-0.1.5.tgz", - "integrity": "sha512-hs6T3BjndibrAmPSoKTHmKa3tz/c6Qgjv9iZw+tAoxuP6izfTCkzfltBQrW7SuK5xnY22gv9jCEf51+mRH+Qvg==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bfile": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/bfile/-/bfile-0.2.2.tgz", - "integrity": "sha512-X205SsJ7zFAnjeJ/pBLqDqF10x/4Su3pBy8UdVKw4hdGJk7t5pLoRi+uG4rPaDAClGbrEfT/06PGUbYiMYKzTg==" - }, - "bfilter": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bfilter/-/bfilter-1.0.5.tgz", - "integrity": "sha512-GupIidtCvLbKhXnA1sxvrwa+gh95qbjafy7P1U1x/2DHxNabXq4nGW0x3rmgzlJMYlVl+c8fMxoMRIwpKYlgcQ==", - "requires": { - "bsert": "~0.0.10", - "bufio": "~1.0.6", - "mrmr": "~0.1.6" - }, - "dependencies": { - "bufio": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", - "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==" - } - } - }, - "bheep": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/bheep/-/bheep-0.1.5.tgz", - "integrity": "sha512-0KR5Zi8hgJBKL35+aYzndCTtgSGakOMxrYw2uszd5UmXTIfx3+drPGoETlVbQ6arTdAzSoQYA1j35vbaWpQXBg==", - "requires": { - "bsert": "~0.0.10" - } - }, - "binet": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/binet/-/binet-0.3.7.tgz", - "integrity": "sha512-GF+QD4ajs3GWabaVzso7Kn9aZEbwI0e54FKU2ID8bM/7rIk7BpSJytB1KS7SMpix+fWAi9MAGkOgSFljl0aaKg==", - "requires": { - "bs32": "~0.1.5", - "bsert": "~0.0.10" - } - }, - "blgr": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/blgr/-/blgr-0.2.0.tgz", - "integrity": "sha512-2jZdqajYCGD5rwGdOooQpxgjKsiAAV2g8LapwSnbTjAYTZAqmqBAS+GsVGFi+/y7t1Pspidv/5HsWBbJrsEuFw==", - "requires": { - "bsert": "~0.0.10" - } - }, - "blru": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/blru/-/blru-0.1.6.tgz", - "integrity": "sha512-34+xZ2u4ys/aUzWCU9m6Eee4nVuN1ywdxbi8b3Z2WULU6qvnfeHvCWEdGzlVfRbbhimG2xxJX6R77GD2cuVO6w==", - "requires": { - "bsert": "~0.0.10" - } - }, - "blst": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/blst/-/blst-0.1.5.tgz", - "integrity": "sha512-TPl04Cx3CHdPFAJ2x9Xx1Z1FOfpAzmNPfHkfo+pGAaNH4uLhS58ExvamVkZh3jadF+B7V5sMtqvrqdf9mHINYA==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bmocha": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/bmocha/-/bmocha-2.1.8.tgz", - "integrity": "sha512-bog23Ckl9lRyBxrsi4FmX1rTz4d1WhHRpIA+q2lpoiXmNuroMHr1JUOU5sPiMZwvhLCxqffvWv3xCJC7PC126w==", - "dev": true - }, - "bmutex": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/bmutex/-/bmutex-0.1.6.tgz", - "integrity": "sha512-nXWOXtQHbfPaMl6jyEF/rmRMrcemj2qn+OCAI/uZYurjfx7Dg3baoXdPzHOL0U8Cfvn8CWxKcnM/rgxL7DR4zw==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bns": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/bns/-/bns-0.15.0.tgz", - "integrity": "sha512-iJWQVE399vQzPfhalFMJGEQ7k5Ot2D6Mz8dkoPeLO8huWAMOiJNJ1tHzOu5j+ZyNNew6ITgG/LsSyaRPxvkXuw==", - "requires": { - "bcrypto": "~5.4.0", - "bfile": "~0.2.2", - "bheep": "~0.1.5", - "binet": "~0.3.6", - "bs32": "~0.1.6", - "bsert": "~0.0.10", - "btcp": "~0.1.5", - "budp": "~0.1.6", - "bufio": "~1.0.7", - "unbound": "~0.4.3" - }, - "dependencies": { - "bufio": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", - "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==" - } - } - }, - "brq": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/brq/-/brq-0.1.8.tgz", - "integrity": "sha512-6SDY1lJMKXgt5TZ6voJQMH2zV1XPWWtm203PSkx3DSg9AYNYuRfOPFSBDkNemabzgpzFW9/neR4YhTvyJml8rQ==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bs32": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/bs32/-/bs32-0.1.6.tgz", - "integrity": "sha512-usjDesQqZ8ihHXOnOEQuAdymBHnJEfSd+aELFSg1jN/V3iAf12HrylHlRJwIt6DTMmXpBDQ+YBg3Q3DIYdhRgQ==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bsert": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/bsert/-/bsert-0.0.10.tgz", - "integrity": "sha512-NHNwlac+WPy4t2LoNh8pXk8uaIGH3NSaIUbTTRXGpE2WEbq0te/tDykYHkFK57YKLPjv/aGHmbqvnGeVWDz57Q==" - }, - "bsock": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/bsock/-/bsock-0.1.9.tgz", - "integrity": "sha512-/l9Kg/c5o+n/0AqreMxh2jpzDMl1ikl4gUxT7RFNe3A3YRIyZkiREhwcjmqxiymJSRI/Qhew357xGn1SLw/xEw==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bsocks": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bsocks/-/bsocks-0.2.6.tgz", - "integrity": "sha512-66UkjoB9f7lhT+WKgYq8MQa6nkr96mlX64JYMlIsXe/X4VeqNwvsx7UOE3ZqD6lkwg8GvBhapRTWj0qWO3Pw8w==", - "requires": { - "binet": "~0.3.5", - "bsert": "~0.0.10" - } - }, - "btcp": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/btcp/-/btcp-0.1.5.tgz", - "integrity": "sha512-tkrtMDxeJorn5p0KxaLXELneT8AbfZMpOFeoKYZ5qCCMMSluNuwut7pGccLC5YOJqmuk0DR774vNVQLC9sNq/A==" - }, - "budp": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/budp/-/budp-0.1.6.tgz", - "integrity": "sha512-o+a8NPq3DhV91j4nInjht2md6mbU1XL+7ciPltP66rw5uD3KP1m5r8lA94LZVaPKcFdJ0l2HVVzRNxnY26Pefg==" - }, - "buffer-map": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/buffer-map/-/buffer-map-0.0.7.tgz", - "integrity": "sha512-95try3p/vMRkIAAnJDaGkFhGpT/65NoeW6XelEPjAomWYR58RQtW4khn0SwKj34kZoE7uxL7w2koZSwbnszvQQ==" - }, - "bufio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.2.0.tgz", - "integrity": "sha512-UlFk8z/PwdhYQTXSQQagwGAdtRI83gib2n4uy4rQnenxUM2yQi8lBDzF230BNk+3wAoZDxYRoBwVVUPgHa9MCA==" - }, - "bupnp": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bupnp/-/bupnp-0.2.6.tgz", - "integrity": "sha512-J6ykzJhZMxXKN78K+1NzFi3v/51X2Mvzp2hW42BWwmxIVfau6PaN99gyABZ8x05e8MObWbsAis23gShhj9qpbw==", - "requires": { - "binet": "~0.3.5", - "brq": "~0.1.7", - "bsert": "~0.0.10" - } - }, - "bval": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/bval/-/bval-0.1.6.tgz", - "integrity": "sha512-jxNH9gSx7g749hQtS+nTxXYz/bLxwr4We1RHFkCYalNYcj12RfbW6qYWsKu0RYiKAdFcbNoZRHmWrIuXIyhiQQ==", - "requires": { - "bsert": "~0.0.10" - } - }, - "bweb": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/bweb/-/bweb-0.1.11.tgz", - "integrity": "sha512-zi9FtNpPD+Mv9UAZFRzV3jnUcEDPJTeUS8JDvsR9Nlq4fwc93SBJUui0PrC1U1GO8zzopoXtoOZ5Do9L46Rv2Q==", - "requires": { - "bsert": "~0.0.10", - "bsock": "~0.1.8" - } - }, - "goosig": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/goosig/-/goosig-0.10.0.tgz", - "integrity": "sha512-+BVVLfxmawAmGVjjJpXzu5LNcFIOfgXgP7kWEyc3qu/xn9RMqbPbNfYDdHBZKfZkDMIO7Q4vD790iNYQAXhoFA==", - "requires": { - "bcrypto": "~5.4.0", - "bsert": "~0.0.10", - "loady": "~0.0.5" - } - }, - "hs-client": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/hs-client/-/hs-client-0.0.13.tgz", - "integrity": "sha512-3Vm/4S0TDstbOW+OfdTeP2EQ4dolPNqMulTSr31RihwX8cX1DyT4il1Fc9STXXToXTsZuFro2WD/+1m0MWi5Ag==", - "requires": { - "bcfg": "~0.1.7", - "bcurl": "~0.2.0", - "bsert": "~0.0.10" - } - }, - "loady": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/loady/-/loady-0.0.5.tgz", - "integrity": "sha512-uxKD2HIj042/HBx77NBcmEPsD+hxCgAtjEWlYNScuUjIsh/62Uyu39GOR68TBR68v+jqDL9zfftCWoUo4y03sQ==" - }, - "mrmr": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/mrmr/-/mrmr-0.1.10.tgz", - "integrity": "sha512-NJRJs+yJyRWwcTqLRf7O32n56UP1+UQoTrGVEoB3LMj0h2jlon790drDbxKvi5mK5k4HfC0cpNkxqHcrJK/evg==", - "requires": { - "bsert": "~0.0.10", - "loady": "~0.0.5" - } - }, - "n64": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/n64/-/n64-0.2.10.tgz", - "integrity": "sha512-uH9geV4+roR1tohsrrqSOLCJ9Mh1iFcDI+9vUuydDlDxUS1UCAWUfuGb06p3dj3flzywquJNrGsQ7lHP8+4RVQ==" - }, - "unbound": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/unbound/-/unbound-0.4.3.tgz", - "integrity": "sha512-2ISqZLXtzp1l9f1V8Yr6S+zuhXxEwE1CjKHjXULFDHJcfhc9Gm3mn19hdPp4rlNGEdCivKYGKjYe3WRGnafYdA==", - "optional": true, - "requires": { - "loady": "~0.0.5" - } - }, - "urkel": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/urkel/-/urkel-1.0.2.tgz", - "integrity": "sha512-Y5UXbgBr6pczrD08N0SYJkWjtdtTTpmZsOvuftdrEHLnTjuxwSNjKsXYLQkICTptvnHAJ2OjI6XdAxtYTyOHew==", - "requires": { - "bfile": "~0.2.1", - "bmutex": "~0.1.6", - "bsert": "~0.0.10" - } - } } } diff --git a/package.json b/package.json index 7656b074e..494688377 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hsd", - "version": "5.99.0", + "version": "6.99.0", "description": "Cryptocurrency bike-shed", "license": "MIT", "repository": "git://github.com/handshake-org/hsd.git", @@ -20,36 +20,36 @@ "node": ">=14.0.0" }, "dependencies": { - "bcfg": "~0.1.7", + "bcfg": "~0.2.2", "bcrypto": "~5.4.0", - "bdb": "~1.4.0", + "bcurl": "~0.2.1", + "bdb": "~1.6.0", "bdns": "~0.1.5", - "bevent": "~0.1.5", - "bfile": "~0.2.2", - "bfilter": "~1.0.5", - "bheep": "~0.1.5", - "binet": "~0.3.7", - "blgr": "~0.2.0", - "blru": "~0.1.6", - "blst": "~0.1.5", - "bmutex": "~0.1.6", + "bevent": "~0.1.6", + "bfile": "~0.2.3", + "@handshake-org/bfilter": "~2.3.0", + "bheep": "~0.1.6", + "binet": "~0.3.9", + "blgr": "~0.2.1", + "blru": "~0.1.7", + "blst": "~0.1.6", + "bmutex": "~0.1.7", "bns": "~0.15.0", - "bsert": "~0.0.10", - "bsock": "~0.1.9", + "bsert": "~0.0.13", + "bsock": "~0.1.11", "bsocks": "~0.2.6", "btcp": "~0.1.5", "buffer-map": "~0.0.7", - "bufio": "~1.2.0", + "bufio": "~1.2.1", "bupnp": "~0.2.6", - "bval": "~0.1.6", - "bweb": "~0.1.11", + "bval": "~0.1.8", + "bweb": "~0.2.0", "goosig": "~0.10.0", - "hs-client": "~0.0.13", "n64": "~0.2.10", - "urkel": "~1.0.2" + "urkel": "~1.0.3" }, "devDependencies": { - "bmocha": "^2.1.8" + "bmocha": "^2.2.0" }, "main": "./lib/hsd.js", "bin": { @@ -59,7 +59,9 @@ "hs-seeder": "./bin/hs-seeder", "hs-wallet": "./bin/hsw", "hsd-cli": "./bin/hsd-cli", - "hsw-cli": "./bin/hsw-cli" + "hsd-rpc": "./bin/hsd-rpc", + "hsw-cli": "./bin/hsw-cli", + "hsw-rpc": "./bin/hsw-rpc" }, "scripts": { "build-docs": "jsdoc -c jsdoc.json", @@ -73,6 +75,7 @@ }, "browser": { "./lib/covenants/reserved": "./lib/covenants/reserved-browser.js", + "./lib/covenants/locked": "./lib/covenants/locked-browser.js", "./lib/hd/nfkd": "./lib/hd/nfkd-compat.js", "./lib/hd/wordlist": "./lib/hd/wordlist-browser.js", "./lib/workers/child": "./lib/workers/child-browser.js", diff --git a/scripts/gen-hsclient.js b/scripts/gen-hsclient.js new file mode 100755 index 000000000..e058d2772 --- /dev/null +++ b/scripts/gen-hsclient.js @@ -0,0 +1,268 @@ +#!/usr/bin/env node + +'use strict'; + +const assert = require('bsert'); +const path = require('path'); +const os = require('os'); +const util = require('util'); +const cp = require('child_process'); +const exec = util.promisify(cp.exec); +const fs = require('bfile'); +const Config = require('bcfg'); + +const ROOT = path.dirname(__dirname); +const NAME = 'hs-client'; +const HSD_PKG = require(path.join(ROOT, 'package.json')); + +const REMOTE = 'git@github.com:handshake-org/hs-client.git'; + +const INIT_HS_CLIENT_PKG = { + name: 'hs-client', + description: 'HSD node and wallet client', + keywords: [ + 'http', + 'request', + 'socket.io', + 'websockets' + ], + main: './lib/client/index.js', + bin: { + 'hsd-cli': './bin/hsd-cli', + 'hsd-rpc': './bin/hsd-rpc', + 'hsw-cli': './bin/hsw-cli', + 'hsw-rpc': './bin/hsw-rpc' + }, + engines: { + node: '>=8.0.0' + } +}; + +const INHERIT_PROPERTIES = [ + 'version', + 'license', + 'repository', + 'homepage', + 'bugs', + 'author' +]; + +const DEPENDENCIES = [ + 'bcfg', + 'bcurl', + 'bsert' +]; + +const COPY_FILES = [ + '.npmignore', + '.gitignore', + 'bin/hsd-cli', + 'bin/hsw-cli', + 'bin/hsd-rpc', + 'bin/hsw-rpc', + 'lib/client/index.js', + 'lib/client/wallet.js', + 'lib/client/node.js', + 'LICENSE', + 'SECURITY.md' +]; + +const README = ` +hs-client +========= + +Autogenerated from https://github.com/handshake-org/hsd. + +REST and RPC client for handshake. + +## Usage + +\`\`\` js +const {NodeClient, WalletClient} = require('hs-client'); +\`\`\` +`; + +const HELP = `Usage: gen-hsclient [DIRECTORY] [OPTIONS]... +Generate hs-client package in DIRECTORY. + +OPTIONS: + --no-git - Don't setup git. + --verbose - Verbose output. + --no-steps - Don't print next steps. +`; + +async function ensureDir(dir) { + dir = path.resolve(dir); + + if (dir.startsWith(ROOT)) + throw new Error(`${NAME} needs to be outside of the hsd. ${dir}`); + + if (await fs.exists(dir)) { + throw new Error( + `Directory ${dir} already exists.` + + ' Please remove to proceed or choose different directory.' + ); + } + + await fs.mkdir(dir); + return dir; +} + +async function setupPackageContent(dir) { + for (const file of COPY_FILES) { + const src = path.join(ROOT, file); + const dst = path.join(dir, file); + const dstDir = path.dirname(dst); + + if (!await fs.exists(dstDir)) + await fs.mkdirp(dstDir); + + await fs.copy(src, dst); + } + + const hsClientPkg = { + ...INIT_HS_CLIENT_PKG + }; + + for (const name of INHERIT_PROPERTIES) { + assert(HSD_PKG[name]); + hsClientPkg[name] = HSD_PKG[name]; + } + + hsClientPkg.dependencies = {}; + for (const dep of DEPENDENCIES) { + assert(HSD_PKG.dependencies[dep], `Dependency "${dep}" not found for hsd.`); + hsClientPkg.dependencies[dep] = HSD_PKG.dependencies[dep]; + } + + await fs.writeJSON(path.join(dir, 'package.json'), hsClientPkg); + await fs.writeFile(path.join(dir, 'README.md'), README); + + return hsClientPkg; +} + +async function setupGit(dir, log) { + const commands = [ + 'git init -b master', + `git remote add origin ${REMOTE}`, + 'git fetch -q origin master', + 'git pull -q origin master', + 'git rm -r .' + ]; + + log('Setting up git: ', dir); + + for (const cmd of [...commands]) { + log(` > ${cmd} in ${dir}.`); + log(await execCmd(cmd, dir, 20000)); + commands.shift(); + } +} + +async function finalGit(dir, version, log) { + const commands = [ + 'git add .', + `git commit --gpg-sign -m "v${version}"`, + `git tag --sign v${version} -m "v${version}"` + ]; + + const manualCommands = [ + 'git push origin master', + `git push origin v${version}` + ]; + + for (const cmd of [...commands]) { + log(` > ${cmd} in ${dir}.`); + + try { + log(await execCmd(cmd, dir)); + } catch (e) { + console.log(`Failed to execute: ${cmd}.`); + console.log(e.message); + console.log('You need to proceed manually.'); + break; + } + + commands.shift(); + } + + return [...commands, ...manualCommands]; +} + +(async () => { + const config = new Config('hsd', { + alias: { + 'v': 'verbose' + } + }); + + config.load({ + argv: true, + env: true + }); + + if (config.bool('help')) { + console.log(HELP); + process.exit(0); + } + + const dir = config.str(0, tmpdir()); + const verbose = config.bool('verbose', false); + const noGit = config.bool('no-git', false); + const noSteps = config.bool('no-steps', false); + + const log = verbose ? console.log : () => {}; + + const pkgDir = await ensureDir(dir); + + if (!noSteps && !noGit) + await setupGit(pkgDir, log); + + log(`Copying files to ${pkgDir}...`); + const pkg = await setupPackageContent(pkgDir); + + if (noSteps) + return; + + let gitNext; + + if (!noGit) + gitNext = await finalGit(pkgDir, pkg.version, log); + + console.log(`Generated ${pkgDir}.`); + console.log('Next steps:'); + console.log(` $ cd ${pkgDir}`); + + if (!noGit) { + assert(gitNext); + console.log('Git Next:'); + for (const cmd of gitNext) + console.log(` $ ${cmd}`); + } + + console.log('NPM Next:'); + console.log(' $ npm publish --dry-run # check if everything is ok'); + console.log(' $ npm publish'); +})().catch((err) => { + console.log('Try passing --help for help.'); + console.error(err.stack); + process.exit(1); +}); + +function tmpdir() { + return path.join(os.tmpdir(), NAME + '-' + Date.now()); +} + +async function execCmd(cmd, cwd, timeout = 2000) { + assert(cwd, 'CWD is required.'); + + const {stdout, stderr} = await exec(cmd, { + cwd, + timeout + }); + + if (stderr.length !== 0) + throw new Error(stderr); + + return stdout; +} diff --git a/test/anyone-can-renew-test.js b/test/anyone-can-renew-test.js index 57b9eff03..ee039bc4a 100644 --- a/test/anyone-can-renew-test.js +++ b/test/anyone-can-renew-test.js @@ -105,7 +105,7 @@ describe('Anyone-can-renew address', function() { it('should win name with Alice\'s wallet', async () => { heightBeforeOpen = node.chain.height; - await alice.sendOpen(name, false); + await alice.sendOpen(name); await mineBlocks(network.names.treeInterval + 1); await alice.sendBid(name, 100000, 200000); @@ -161,12 +161,12 @@ describe('Anyone-can-renew address', function() { }); it('should not be owned by either wallet', async () => { - assert.rejects( + await assert.rejects( alice.sendTransfer(name, aliceReceive), {message: `Wallet does not own name: ${name}.`} ); - assert.rejects( + await assert.rejects( bob.sendTransfer(name, bobReceive), {message: `Auction not found: ${name}.`} ); @@ -184,10 +184,12 @@ describe('Anyone-can-renew address', function() { value: coin.value, address: coin.address })); - mtx.output(0).covenant.type = rules.types.RENEW; - mtx.output(0).covenant.pushHash(nameHash); - mtx.output(0).covenant.pushU32(heightBeforeOpen + 1); - mtx.output(0).covenant.pushHash(node.chain.tip.hash); + + mtx.output(0).covenant.setRenew( + nameHash, + heightBeforeOpen + 1, + node.chain.tip.hash + ); await alice.fund(mtx, {coins: [coin]}); await alice.finalize(mtx, {coins: [coin]}); @@ -212,10 +214,12 @@ describe('Anyone-can-renew address', function() { value: coin.value, address: coin.address })); - mtx.output(0).covenant.type = rules.types.UPDATE; - mtx.output(0).covenant.pushHash(nameHash); - mtx.output(0).covenant.pushU32(heightBeforeOpen + 1); - mtx.output(0).covenant.push(Buffer.alloc(1)); + + mtx.output(0).covenant.setUpdate( + nameHash, + heightBeforeOpen + 1, + Buffer.alloc(1) + ); await alice.fund(mtx, {coins: [coin]}); await alice.finalize(mtx, {coins: [coin]}); @@ -240,11 +244,12 @@ describe('Anyone-can-renew address', function() { value: coin.value, address: coin.address })); - mtx.output(0).covenant.type = rules.types.TRANSFER; - mtx.output(0).covenant.pushHash(nameHash); - mtx.output(0).covenant.pushU32(heightBeforeOpen + 1); - mtx.output(0).covenant.pushU8(0); - mtx.output(0).covenant.push(Buffer.alloc(20)); + + mtx.output(0).covenant.setTransfer( + nameHash, + heightBeforeOpen + 1, + new Address({ version: 0, hash: Buffer.alloc(20) }) + ); await alice.fund(mtx, {coins: [coin]}); await alice.finalize(mtx, {coins: [coin]}); @@ -269,10 +274,12 @@ describe('Anyone-can-renew address', function() { value: coin.value, address: coin.address })); - mtx.output(0).covenant.type = rules.types.RENEW; - mtx.output(0).covenant.pushHash(nameHash); - mtx.output(0).covenant.pushU32(heightBeforeOpen + 1); - mtx.output(0).covenant.pushHash(node.chain.tip.hash); + + mtx.output(0).covenant.setRenew( + nameHash, + heightBeforeOpen + 1, + node.chain.tip.hash + ); await alice.fund(mtx, {coins: [coin]}); await alice.finalize(mtx, {coins: [coin]}); @@ -294,10 +301,12 @@ describe('Anyone-can-renew address', function() { value: coin.value, address: coin.address })); - mtx.output(0).covenant.type = rules.types.RENEW; - mtx.output(0).covenant.pushHash(nameHash); - mtx.output(0).covenant.pushU32(heightBeforeOpen + 1); - mtx.output(0).covenant.pushHash(node.chain.tip.hash); + + mtx.output(0).covenant.setRenew( + nameHash, + heightBeforeOpen + 1, + node.chain.tip.hash + ); await bob.fund(mtx, {coins: [coin]}); await bob.finalize(mtx, {coins: [coin]}); diff --git a/test/auction-reorg-test.js b/test/auction-reorg-test.js index 5969c1d6b..d1e8d5cb7 100644 --- a/test/auction-reorg-test.js +++ b/test/auction-reorg-test.js @@ -11,7 +11,8 @@ const Miner = require('../lib/mining/miner'); const MemWallet = require('./util/memwallet'); const Network = require('../lib/protocol/network'); const rules = require('../lib/covenants/rules'); -const ownership = require('../lib/covenants/ownership'); +const {ownership} = require('../lib/covenants/ownership'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); const network = Network.get('regtest'); const {treeInterval} = network.names; @@ -62,9 +63,7 @@ function createNode() { wallet.getNameStatus = async (nameHash) => { assert(Buffer.isBuffer(nameHash)); const height = chain.height + 1; - const state = await chain.getNextState(); - const hardened = state.hasHardening(); - return chain.db.getNameStatus(nameHash, height, hardened); + return chain.db.getNameStatus(nameHash, height); }; return wallet; @@ -75,6 +74,19 @@ function createNode() { describe('Auction Reorg', function() { this.timeout(20000); + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + + before(() => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; + }); + + after(() => { + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + }); + describe('Vickrey Auction Reorg', function() { const node = createNode(); const orig = createNode(); @@ -141,8 +153,8 @@ describe('Auction Reorg', function() { assert(await chain.add(block)); }); - it('should mine 10 blocks', async () => { - for (let i = 0; i < 10; i++) { + it('should mine through the bidding period', async () => { + for (let i = 0; i < network.names.biddingPeriod; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -163,8 +175,8 @@ describe('Auction Reorg', function() { assert(await chain.add(block)); }); - it('should mine 20 blocks', async () => { - for (let i = 0; i < 20; i++) { + it('should mine through the reveal period', async () => { + for (let i = 0; i < network.names.revealPeriod; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -188,14 +200,6 @@ describe('Auction Reorg', function() { assert(await chain.add(block)); }); - it('should mine 10 blocks', async () => { - for (let i = 0; i < 10; i++) { - const block = await cpu.mineBlock(); - assert(block); - assert(await chain.add(block)); - } - }); - it('should register again and update tree', async () => { const mtx = await winner.createUpdate(NAME1, Buffer.from([1,2,4])); @@ -220,6 +224,14 @@ describe('Auction Reorg', function() { assert(await chain.add(block)); }); + it('should mine past tree interval', async () => { + for (let i = 0; i < network.names.treeInterval; i++) { + const block = await cpu.mineBlock(); + assert(block); + assert(await chain.add(block)); + } + }); + it('should renew', async () => { const mtx = await winner.createRenewal(NAME1); @@ -253,7 +265,7 @@ describe('Auction Reorg', function() { assert.strictEqual(err.reason, 'bad-renewal-premature'); }); - it('should mine 10 blocks', async () => { + it('should mine through tree txn commitment', async () => { const left = treeInterval - (chain.height % treeInterval); for (let i = 0; i < left; i++) { @@ -409,6 +421,8 @@ describe('Auction Reorg', function() { }); describe('Claim Reorg', function() { + this.timeout(10000); + const node = createNode(); const {chain, miner, cpu, blocks} = node; @@ -546,8 +560,8 @@ describe('Auction Reorg', function() { }); */ - it('should mine 20 blocks', async () => { - for (let i = 0; i < 20; i++) { + it('should mine a tree interval blocks', async () => { + for (let i = 0; i < network.names.treeInterval; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -620,8 +634,8 @@ describe('Auction Reorg', function() { assert.strictEqual(err.reason, 'bad-finalize-maturity'); }); - it('should mine 20 blocks', async () => { - for (let i = 0; i < 20; i++) { + it('should mine through the transfer lock period', async () => { + for (let i = 0; i < network.names.transferLockup; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); diff --git a/test/auction-rpc-test.js b/test/auction-rpc-test.js index e152cf343..d55742655 100644 --- a/test/auction-rpc-test.js +++ b/test/auction-rpc-test.js @@ -2,113 +2,60 @@ const assert = require('bsert'); const bio = require('bufio'); -const plugin = require('../lib/wallet/plugin'); const rules = require('../lib/covenants/rules'); const common = require('./util/common'); const { ChainEntry, - FullNode, - SPVNode, KeyRing, MTX, Network, Path } = require('..'); -const {NodeClient, WalletClient} = require('hs-client'); const {forValue} = require('./util/common'); +const NodeContext = require('./util/node-context'); class TestUtil { - constructor(options) { - if (!options) - options = Object.create(null); - - if (!options.host) - options.host = '127.0.0.1'; - - if (!options.nport) - options.nport = 14037; - - if (!options.wport) - options.wport = 14039; - - this.network = Network.get('regtest'); - - this.txs = {}; - - this.blocks = {}; - - this.node = new FullNode({ + constructor() { + this.nodeCtx = new NodeContext({ memory: true, workers: true, - network: this.network.type, listen: true, - bip37: true + bip37: true, + wallet: true }); - this.node.use(plugin); + this.nodeCtx.init(); - this.nclient = new NodeClient({ - timeout: 15000, - host: options.host, - port: options.nport - }); + this.network = this.nodeCtx.network; + this.txs = {}; + this.blocks = {}; - this.wclient = new WalletClient({ - host: options.host, - port: options.wport - }); + this.node = this.nodeCtx.node; + } + + get nclient() { + return this.nodeCtx.nclient; } - /** - * Execute an RPC using the wallet client. - * @param {String} method - RPC method - * @param {Array} params - method parameters - * @returns {Promise} - Returns a two item array with the RPC's return value - * or null as the first item and an error or null as the second item. - */ - - async wrpc(method, params = []) { - return this.wclient.execute(method, params) - .then(data => data) - .catch((err) => { - throw new Error(err); - }); + get wclient() { + return this.nodeCtx.wclient; } - /** - * Execute an RPC using the node client. - * @param {String} method - RPC method - * @param {Array} params - method parameters - * @returns {Promise} - Returns a two item array with the - * RPC's return value or null as the first item and an error or - * null as the second item. - */ - - async nrpc(method, params = []) { - return this.nclient.execute(method, params) - .then(data => data) - .catch((err) => { - throw new Error(err); - }); + wrpc(method, params = []) { + return this.nodeCtx.wrpc(method, params); } - /** - * Open the util and all its child objects. - */ + nrpc(method, params = []) { + return this.nodeCtx.nrpc(method, params); + } async open() { assert(!this.opened, 'TestUtil is already open.'); this.opened = true; - await this.node.ensure(); - await this.node.open(); - await this.node.connect(); - this.node.startSync(); - - await this.nclient.open(); - await this.wclient.open(); + await this.nodeCtx.open(); - this.node.plugins.walletdb.wdb.on('confirmed', ((details, tx) => { + this.nodeCtx.wdb.on('confirmed', ((details, tx) => { const txid = tx.txid(); if (!this.txs[txid]) @@ -125,17 +72,8 @@ class TestUtil { }); } - /** - * Close util and all its child objects. - */ - async close() { - assert(this.opened, 'TestUtil is not open.'); - this.opened = false; - - await this.nclient.close(); - await this.wclient.close(); - await this.node.close(); + await this.nodeCtx.close(); } async confirmTX(txid, timeout = 5000) { @@ -480,43 +418,32 @@ describe('Auction RPCs', function() { }); describe('SPV', function () { - const spvNode = new SPVNode({ - memory: true, - network: 'regtest', - port: 10000, - brontidePort: 20000, + const spvCtx = new NodeContext({ httpPort: 30000, only: '127.0.0.1', - noDns: true - }); + noDns: true, - const spvClient = new NodeClient({ - port: 30000 + spv: true }); before(async () => { await util.node.connect(); - await spvNode.open(); - await spvNode.connect(); - await spvNode.startSync(); - - await forValue(spvNode.chain, 'height', util.node.chain.height); + await spvCtx.open(); - await spvClient.open(); + await forValue(spvCtx.chain, 'height', util.node.chain.height); }); after(async () => { - await spvClient.close(); - await spvNode.close(); + await spvCtx.close(); }); it('should not get current namestate', async () => { - const {info} = await spvClient.execute('getnameinfo', [name]); + const {info} = await spvCtx.nrpc('getnameinfo', [name]); assert.strictEqual(info, null); }); it('should get historcial namestate at safe height', async () => { - const {info} = await spvClient.execute('getnameinfo', [name, true]); + const {info} = await spvCtx.nrpc('getnameinfo', [name, true]); assert.strictEqual(info.name, name); assert.strictEqual(info.state, 'CLOSED'); assert.strictEqual(info.value, loserBid.bid * COIN); @@ -524,12 +451,12 @@ describe('Auction RPCs', function() { }); it('should not get current resource', async () => { - const json = await spvClient.execute('getnameresource', [name]); + const json = await spvCtx.nrpc('getnameresource', [name]); assert.strictEqual(json, null); }); it('should get historcial resource at safe height', async () => { - const json = await spvClient.execute('getnameresource', [name, true]); + const json = await spvCtx.nrpc('getnameresource', [name, true]); assert.deepStrictEqual( json, { @@ -546,7 +473,7 @@ describe('Auction RPCs', function() { it('should not verifymessagewithname', async () => { // No local Urkel tree, namestate is always null await assert.rejects( - spvClient.execute('verifymessagewithname', [name, signSig, signMsg]), + spvCtx.nrpc('verifymessagewithname', [name, signSig, signMsg]), {message: /Cannot find the name owner/} ); }); @@ -555,7 +482,7 @@ describe('Auction RPCs', function() { // This time we do have a valid namestate to work with, but // SPV nodes still don't have a UTXO set to get addresses from await assert.rejects( - spvClient.execute('verifymessagewithname', [name, signSig, signMsg, true]), + spvCtx.nrpc('verifymessagewithname', [name, signSig, signMsg, true]), {message: /Cannot find the owner's address/} ); }); diff --git a/test/auction-test.js b/test/auction-test.js index a224d9ad3..155a17262 100644 --- a/test/auction-test.js +++ b/test/auction-test.js @@ -8,7 +8,8 @@ const Miner = require('../lib/mining/miner'); const MemWallet = require('./util/memwallet'); const Network = require('../lib/protocol/network'); const rules = require('../lib/covenants/rules'); -const ownership = require('../lib/covenants/ownership'); +const {ownership} = require('../lib/covenants/ownership'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); const network = Network.get('regtest'); const GNAME_SIZE = 10; @@ -58,9 +59,7 @@ function createNode() { wallet.getNameStatus = async (nameHash) => { assert(Buffer.isBuffer(nameHash)); const height = chain.height + 1; - const state = await chain.getNextState(); - const hardened = state.hasHardening(); - return chain.db.getNameStatus(nameHash, height, hardened); + return chain.db.getNameStatus(nameHash, height); }; return wallet; @@ -71,6 +70,19 @@ function createNode() { describe('Auction', function() { this.timeout(15000); + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + + before(() => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; + }); + + after(() => { + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + }); + describe('Vickrey Auction', function() { const node = createNode(); const orig = createNode(); @@ -139,8 +151,8 @@ describe('Auction', function() { assert(await chain.add(block)); }); - it('should mine 10 blocks', async () => { - for (let i = 0; i < 10; i++) { + it('should mine through the bidding period', async () => { + for (let i = 0; i < network.names.biddingPeriod; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -161,8 +173,8 @@ describe('Auction', function() { assert(await chain.add(block)); }); - it('should mine 20 blocks', async () => { - for (let i = 0; i < 20; i++) { + it('should mine through the reveal period', async () => { + for (let i = 0; i < network.names.revealPeriod; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -186,8 +198,8 @@ describe('Auction', function() { assert(await chain.add(block)); }); - it('should mine 10 blocks', async () => { - for (let i = 0; i < 10; i++) { + it('should mine a tree interval', async () => { + for (let i = 0; i < network.names.treeInterval; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -251,8 +263,8 @@ describe('Auction', function() { assert.strictEqual(err.reason, 'bad-renewal-premature'); }); - it('should mine 10 blocks', async () => { - for (let i = 0; i < 10; i++) { + it('should mine a tree interval', async () => { + for (let i = 0; i < network.names.treeInterval; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -483,6 +495,8 @@ describe('Auction', function() { }); describe('Claim', function() { + this.timeout(10000); + const node = createNode(); const {chain, miner, cpu, blocks} = node; @@ -500,8 +514,8 @@ describe('Auction', function() { miner.addAddress(wallet.getReceive()); }); - it('should mine 20 blocks', async () => { - for (let i = 0; i < 20; i++) { + it('should mine a tree interval', async () => { + for (let i = 0; i < network.names.treeInterval; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -725,8 +739,8 @@ describe('Auction', function() { }); */ - it('should mine 20 blocks', async () => { - for (let i = 0; i < 20; i++) { + it('should mine a tree interval', async () => { + for (let i = 0; i < network.names.treeInterval; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); @@ -799,8 +813,8 @@ describe('Auction', function() { assert.strictEqual(err.reason, 'bad-finalize-maturity'); }); - it('should mine 20 blocks', async () => { - for (let i = 0; i < 20; i++) { + it('should mine through the transfer lockup period', async () => { + for (let i = 0; i < network.names.transferLockup; i++) { const block = await cpu.mineBlock(); assert(block); assert(await chain.add(block)); diff --git a/test/blockstore-test.js b/test/blockstore-test.js index b09e875ac..7be973dd0 100644 --- a/test/blockstore-test.js +++ b/test/blockstore-test.js @@ -93,7 +93,7 @@ describe('BlockStore', function() { for (const method of methods) { it(`${method}`, async () => { assert(store[method]); - assert.rejects(async () => { + await assert.rejects(async () => { await store[method](); }, { name: 'Error', diff --git a/test/brontide-test.js b/test/brontide-test.js index c14ee7abd..9e54065d9 100644 --- a/test/brontide-test.js +++ b/test/brontide-test.js @@ -1,15 +1,169 @@ 'use strict'; -const assert = require('assert'); -const {Brontide} = require('../lib/net/brontide'); +const assert = require('bsert'); +const {CipherState, Brontide} = require('../lib/net/brontide'); const HELLO = Buffer.from('hello', 'ascii'); const PROLOGUE = 'hns'; +const ROTATION_INTERVAL = 1000; /* * Tests */ +describe('CipherState', function() { + const cipher = new CipherState(); + const key = Buffer.from( + '2121212121212121212121212121212121212121212121212121212121212121', + 'hex' + ); + const salt = Buffer.from( + '1111111111111111111111111111111111111111111111111111111111111111', + 'hex' + ); + + it('should initalize with the key and salt', () => { + cipher.initSalt(key, salt); + + assert.bufferEqual(cipher.key, key); + + assert.bufferEqual(cipher.salt, salt); + }); + + it('should rotate the secret key', () => { + cipher.rotateKey(); + + assert.bufferEqual( + cipher.key, + Buffer.from( + '0b579ba44366e4d49ac7a44a8203925cb6d610e950aee7a23c47a5448173af11', + 'hex' + ) + ); + + assert.bufferEqual( + cipher.salt, + Buffer.from( + 'be23775b41e7c67d1ec6dcfc21299f32461e145d4164f65943b4b99fcaff6dee', + 'hex' + ) + ); + + assert.strictEqual(cipher.nonce, 0); + }); + + it('should properly encrypt given text and empty ad', () => { + // Reset the cipher + const cipher = new CipherState(); + cipher.initSalt(key, salt); + + const hello = Buffer.from('hello', 'ascii'); + + const tag = cipher.encrypt(hello); + + assert.bufferEqual( + tag, + Buffer.from('f11ae60b9df4c6ea25aea58ce1b6df83', 'hex') + ); + assert.bufferEqual(hello, Buffer.from('0935b4c530', 'hex')); + + // Round 2 + const hello2 = Buffer.from('hello', 'ascii'); + + const tag2 = cipher.encrypt(hello2); + + assert.bufferEqual( + tag2, + Buffer.from('d840242a1e817cd8374d45fb5621a5fc', 'hex') + ); + assert.bufferEqual(hello2, Buffer.from('74898781da', 'hex')); + }); + + it('should properly encrypt given text and ad', () => { + // Reset the cipher + const cipher = new CipherState(); + cipher.initSalt(key, salt); + + const hello = Buffer.from('hello', 'ascii'); + const ad = Buffer.from('222222222222222222222222222222222222', 'hex'); + + const tag = cipher.encrypt(hello, ad); + + assert.bufferEqual( + tag, + Buffer.from('81ad416f62157481c8af8ace16b64e15', 'hex') + ); + assert.bufferEqual(hello, Buffer.from('0935b4c530', 'hex')); + + const hello2 = Buffer.from('hello', 'ascii'); + + const tag2 = cipher.encrypt(hello2, ad); + + assert.bufferEqual( + tag2, + Buffer.from('df3f8257977dfb8d283c6fb149d2d49d', 'hex') + ); + assert.bufferEqual(hello2, Buffer.from('74898781da', 'hex')); + }); + + it('should rotate key after encryption', () => { + const cipher = new CipherState(); + cipher.initSalt(key, salt); + + const hello = Buffer.from('hello', 'ascii'); + + cipher.nonce = 999; + + cipher.encrypt(hello); + + assert.strictEqual(cipher.nonce, 0); + + assert.bufferEqual( + cipher.key, + Buffer.from( + '0b579ba44366e4d49ac7a44a8203925cb6d610e950aee7a23c47a5448173af11', + 'hex' + ) + ); + + assert.bufferEqual( + cipher.salt, + Buffer.from( + 'be23775b41e7c67d1ec6dcfc21299f32461e145d4164f65943b4b99fcaff6dee', + 'hex' + ) + ); + }); + + it('should decrypt encrypted text', () => { + const encryptionCipher = new CipherState(); + encryptionCipher.initSalt(key, salt); + + const decryptionCipher = new CipherState(); + decryptionCipher.initSalt(key, salt); + + const hello = Buffer.from('hello', 'ascii'); + + const tag = encryptionCipher.encrypt(hello); + + assert(decryptionCipher.decrypt(hello, tag)); + }); + + it('should decrypt encrypted text throughout key rotation', () => { + const encryptionCipher = new CipherState(); + encryptionCipher.initSalt(key, salt); + + const decryptionCipher = new CipherState(); + decryptionCipher.initSalt(key, salt); + + for (let i = 0; i <= ROTATION_INTERVAL + 1; i++) { + const hello = Buffer.from('hello', 'ascii'); + const tag = encryptionCipher.encrypt(hello); + assert(decryptionCipher.decrypt(hello, tag)); + } + }); +}); + describe('Brontide', function() { it('should test brontide exchange', () => { const epriv1 = diff --git a/test/chain-blockstore-test.js b/test/chain-blockstore-test.js index 4cd0960d6..67ae85a76 100644 --- a/test/chain-blockstore-test.js +++ b/test/chain-blockstore-test.js @@ -1,7 +1,7 @@ 'use strict'; const assert = require('bsert'); -const {BloomFilter} = require('bfilter'); +const {BloomFilter} = require('@handshake-org/bfilter'); const Network = require('../lib/protocol/network'); const {FileBlockStore, LevelBlockStore} = require('../lib/blockstore'); const Chain = require('../lib/blockchain/chain'); diff --git a/test/chain-checkpoints-test.js b/test/chain-checkpoints-test.js index 124fa59b3..09f25dbf8 100644 --- a/test/chain-checkpoints-test.js +++ b/test/chain-checkpoints-test.js @@ -7,12 +7,13 @@ const Chain = require('../lib/blockchain/chain'); const BlockStore = require('../lib/blockstore/level'); const Miner = require('../lib/mining/miner'); const MemWallet = require('./util/memwallet'); -const ownership = require('../lib/covenants/ownership'); +const {ownership} = require('../lib/covenants/ownership'); const Address = require('../lib/primitives/address'); const Network = require('../lib/protocol/network'); const rules = require('../lib/covenants/rules'); const {Resource} = require('../lib/dns/resource'); const AirdropProof = require('../lib/primitives/airdropproof'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); const network = Network.get('regtest'); @@ -91,8 +92,13 @@ async function mineBlocks(n, label) { } describe('Checkpoints', function() { + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + before(async () => { ownership.ignore = true; + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; await blocks.open(); await chainGenerator.open(); @@ -100,11 +106,13 @@ describe('Checkpoints', function() { }); after(async () => { + ownership.ignore = false; + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + await miner.close(); await chainGenerator.close(); await blocks.close(); - - ownership.ignore = false; }); it('should add addrs to miner', async () => { @@ -119,6 +127,8 @@ describe('Checkpoints', function() { }); it('should CLAIM and REGISTER a reserved name', async () => { + this.timeout(10000); + const claim = await wallet.fakeClaim('cloudflare'); await mineBlock(null, [claim], null, 'claim'); diff --git a/test/chain-full-test.js b/test/chain-full-test.js index d6b45d1b1..483096fc7 100644 --- a/test/chain-full-test.js +++ b/test/chain-full-test.js @@ -737,9 +737,7 @@ describe('Chain', function() { wallet.getNameStatus = async (nameHash) => { assert(Buffer.isBuffer(nameHash)); const height = chain.height + 1; - const state = await chain.getNextState(); - const hardened = state.hasHardening(); - return chain.db.getNameStatus(nameHash, height, hardened); + return chain.db.getNameStatus(nameHash, height); }; }); diff --git a/test/chain-icann-lockup-test.js b/test/chain-icann-lockup-test.js new file mode 100644 index 000000000..56c68d8c2 --- /dev/null +++ b/test/chain-icann-lockup-test.js @@ -0,0 +1,1111 @@ +'use strict'; + +const assert = require('bsert'); +const {NodeClient, WalletClient} = require('../lib/client'); +const {ownership} = require('../lib/covenants/ownership'); +const Network = require('../lib/protocol/network'); +const FullNode = require('../lib/node/fullnode'); +const {forEvent} = require('./util/common'); +const chainCommon = require('../lib/blockchain/common'); +const {BufferMap} = require('buffer-map'); +const {thresholdStates} = chainCommon; +const {isReserved, isLockedUp, hashName} = require('../lib/covenants/rules'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); + +const SOFT_FORK_NAME = 'icannlockup'; + +const network = Network.get('regtest'); +const deployments = network.deployments; +const activationThreshold = network.activationThreshold; +const minerWindow = network.minerWindow; + +const ACTUAL_START = deployments[SOFT_FORK_NAME].startTime; +const ACTUAL_TIMEOUT = deployments[SOFT_FORK_NAME].timeout; +const ACTUAL_CLAIM_PERIOD = network.names.claimPeriod; +const ACTUAL_RENEWAL_WINDOW = network.names.renewalWindow; + +/* + * Test ICANN LOCKUP activation paths. + * It includes test for bip9 activation for the + * `icannlockup` soft fork - when it fails + * and the names become auctionable as well + * as the path where it succeeds and auctions + * become illegal "forever". + * Soft-fork in regtest will be setup to activate after 3 windows and one + * window for DEFINED -> STARTED state. + * Soft-fork voting will only happen in 2 windows. + * In regtest this means: 144 + 144 + 144 + 144 (4 window) blocks will be set + * for claimPeriod end. + * Soft forking period for: 144 + 144 (2 window) blocks. + * Steps: + * - 144 defined + * - 144 + 144 started (Active voting) + * - 144 locked in or failed end of claim period. + * + * Test will run failure and success paths and make sure both give + * results soft-fork expects: + * - on failure: names can be auctioned. + * - on success: root and top10k + * unauctionable via mempool and blocks, for those running + * the node with updated software. + */ + +describe('BIP9 - ICANN lockup (integration)', function() { + this.timeout(20000); + + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + + before(() => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; + }); + + after(() => { + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + }); + + const CUSTOM = [ + 'cloudflare', + 'nlnetlabs', + 'dnscrypt' + ]; + + // Names from 10k alexa. + const TOP10k = [ + 'paypal', + 'steamdb', + 'nvidia', + 'docker' + ]; + + const ROOT = [ + 'nl', + 'fr', + 'aw', + 'pl', + 'baidu' + ]; + + // These will get unreserved for because they fall out of 10k. + const OTHER = [ + 'web', + 'ishares', + 'bforbank', + 'raspbian-france' + ]; + + const checkBIP9Info = (info, expected) => { + expected = expected || {}; + expected.startTime = expected.startTime || deployments[SOFT_FORK_NAME].startTime; + expected.timeout = expected.timeout || deployments[SOFT_FORK_NAME].timeout; + + assert(info, 'BIP9 info should be returned'); + assert.strictEqual(info.status, expected.status); + assert.strictEqual(info.bit, deployments[SOFT_FORK_NAME].bit); + assert.strictEqual(info.startTime, expected.startTime); + assert.strictEqual(info.timeout, expected.timeout); + }; + + const checkBIP9Statistcs = (stats, expected) => { + expected = expected || {}; + + assert.strictEqual(stats.period, expected.period || minerWindow); + assert.strictEqual(stats.threshold, expected.threshold || activationThreshold); + assert.strictEqual(stats.elapsed, expected.elapsed); + assert.strictEqual(stats.count, expected.count); + assert.strictEqual(stats.possible, expected.possible); + }; + + describe('Rules', function() { + const main = Network.get('main'); + const {claimPeriod, alexaLockupPeriod} = main.names; + + const testCases = []; + + for (const name of [...ROOT, ...TOP10k, ...CUSTOM, ...OTHER]) { + testCases.push({ + name, + lockup: false, + reserved: true, + height: claimPeriod - 1, + testName: `should not lockup before claim period ends (${name}), ` + + 'and be reserved (ALL)' + }); + } + + for (const name of [...ROOT, ...TOP10k, ...CUSTOM]) { + testCases.push({ + name, + lockup: true, + reserved: false, + height: claimPeriod, + testName: 'should get locked after claim period ends and ' + + `before alexaLockupPeriod ends (ROOT, TOP 10k) (${name})` + }); + } + + for (const name of [...ROOT, ...TOP10k, ...CUSTOM]) { + testCases.push({ + name, + lockup: true, + reserved: false, + height: alexaLockupPeriod - 1, + testName: 'should get locked after claim period ends and ' + + `before alexaLockupPeriod ends (ROOT, TOP 10k) (${name}) (last)` + }); + } + + for (const name of [...ROOT]) { + testCases.push({ + name, + lockup: true, + reserved: false, + height: alexaLockupPeriod, + testName: `should get locked even after alexaLockupPeriod (ROOT) (${name})` + }); + } + + // after another 4 years all names will become tradeable. + for (const name of [...TOP10k, ...CUSTOM, ...OTHER]) { + testCases.push({ + name, + lockup: false, + reserved: false, + height: alexaLockupPeriod, + testName: `should get unlocked after alexaLockupPeriod (NON-ROOT) (${name})` + }); + } + + for (const {name, lockup, reserved, height, testName} of testCases) { + it(testName, () => { + const hash = hashName(name); + + assert.strictEqual(lockup, isLockedUp(hash, height, main)); + assert.strictEqual(reserved, isReserved(hash, height, main)); + }); + } + }); + + describe('BIP9 - ICANN lockup - failure (integration)', function() { + this.timeout(20000); + + let node, chain; + let nodeClient, walletClient; + let wdb, wallet; + + const FROOT = ROOT.slice(); + const FTOP10k = TOP10k.slice(); + const FCUSTOM = CUSTOM.slice(); + const FOTHER = OTHER.slice(); + const CLAIMED = []; + const CLAIMED_ROOT = []; + const CLAIMED_OTHER = []; + + before(async () => { + node = new FullNode({ + memory: true, + network: network.type, + // We don't want wallet to check lockup names for this test. + walletIcannlockup: false, + plugins: [require('../lib/wallet/plugin')] + }); + + await node.ensure(); + await node.open(); + + chain = node.chain; + + deployments[SOFT_FORK_NAME].startTime = 0; + deployments[SOFT_FORK_NAME].timeout = 0xffffffff; + network.names.claimPeriod = minerWindow * 4; + network.names.renewalWindow = minerWindow * 6; + + // Ignore claim validation + ownership.ignore = true; + + nodeClient = new NodeClient({ + port: network.rpcPort, + timeout: 10000 + }); + + walletClient = new WalletClient({ + port: network.walletPort, + timeout: 10000 + }); + + const walletPlugin = node.require('walletdb'); + wdb = walletPlugin.wdb; + wallet = await wdb.get('primary'); + + const account = await walletClient.getAccount('primary', 'default'); + const receive = account.receiveAddress; + node.miner.addAddress(receive); + + await walletClient.execute('selectwallet', ['primary']); + }); + + after(async () => { + // Enable claim validation + ownership.ignore = false; + + deployments[SOFT_FORK_NAME].startTime = ACTUAL_START; + deployments[SOFT_FORK_NAME].timeout = ACTUAL_TIMEOUT; + network.names.claimPeriod = ACTUAL_CLAIM_PERIOD; + network.names.renewalWindow = ACTUAL_RENEWAL_WINDOW; + + await node.close(); + }); + + it('should get deployment stats', async () => { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + + assert.strictEqual(state, thresholdStates.DEFINED); + checkBIP9Info(bip9info, { status: 'defined' }); + }); + + it('should start the soft-fork', async () => { + for (let i = 0; i < minerWindow - 2; i++) + await mineBlock(node); + + // We are now at the threshold of the window. + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.DEFINED); + checkBIP9Info(bip9info, { status: 'defined' }); + } + + // go into new window and change the state to started. + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + assert.deepStrictEqual(bip9info.statistics, { + period: minerWindow, + threshold: activationThreshold, + elapsed: 0, + count: 0, + possible: true + }); + } + }); + + it('should fail to OPEN for the claimable names', async () => { + let err; + try { + await walletClient.createOpen('primary', { + name: FROOT[0] + }); + } catch (e) { + err = e; + } + + assert(err); + assert(err.message, `Name is reserved: ${FROOT[0]}`); + }); + + it('should be possible to claim for now', async () => { + const root = FROOT.shift(); + const other = FOTHER.shift(); + + const mempoolClaim = forEvent(node.mempool, 'claim', 2, 20000); + + { + const claim = await wallet.makeFakeClaim(root); + await wdb.sendClaim(claim); + CLAIMED.push(root); + CLAIMED_ROOT.push(root); + } + + { + const claim = await wallet.makeFakeClaim(other); + await wdb.sendClaim(claim); + CLAIMED.push(other); + CLAIMED_OTHER.push(other); + } + + await mempoolClaim; + + assert.strictEqual(node.mempool.claims.size, 2); + }); + + it('should fail first window right away', async () => { + const maxFailures = minerWindow - activationThreshold; + + for (let i = 0; i < maxFailures; i++) + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: maxFailures, + count: 0, + possible: true + }); + } + + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: maxFailures + 1, + count: 0, + possible: false + }); + } + + // finish the whole window. + for (let i = 0; i < activationThreshold - 1; i++) + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: 0, + count: 0, + possible: true + }); + } + }); + + it('should fail second window by 1 vote', async () => { + // Because we want this new window to be the last one, + // here we manipulate the deployment timeout. + // Because the deployment state in the window gets + // cached, we can safely modify timeout in the beginning of the + // window. + deployments[SOFT_FORK_NAME].timeout = 1; + + for (let i = 0; i < activationThreshold - 1; i++) + await mineBlock(node, { setICANNLockup: true }); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: activationThreshold - 1, + count: activationThreshold - 1, + possible: true + }); + } + + // mine everything else w/o a vote. + for (let i = 0; i < minerWindow - activationThreshold; i++) { + await mineBlock(node); + + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: activationThreshold + i, + count: activationThreshold - 1, + possible: true + }); + } + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: minerWindow - 1, + count: activationThreshold - 1, + possible: true + }); + } + + // After this it should go to the FAILED state. + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.FAILED); + checkBIP9Info(bip9info, { status: 'failed' }); + + assert(!bip9info.statistics); + } + }); + + it('should still allow claims before claimPeriod', async () => { + // Just go on the edge of the claim period. + // Leave a room for the next test. + while (chain.tip.height < network.names.claimPeriod - 4) + await mineBlock(node); + + const custom = FCUSTOM.shift(); + const top10k = FTOP10k.shift(); + + const names = [custom, top10k]; + + const mempoolClaim = forEvent(node.mempool, 'claim', names.length, 20000); + + for (const name of names) { + const claim = await wallet.makeFakeClaim(name); + await node.mempool.insertClaim(claim); + } + + await mempoolClaim; + assert.strictEqual(node.mempool.claims.size, names.length); + await mineBlock(node); + }); + + it('should fail to claim and invalidate', async () => { + const root = FROOT.shift(); + const other = FOTHER.shift(); + + const rootClaim = await wallet.makeFakeClaim(root); + const otherClaim = await wallet.makeFakeClaim(other); + + // Should insert one claim in the mempool. + await node.mempool.insertClaim(rootClaim); + assert.strictEqual(node.mempool.claims.size, 1); + + await mineBlock(node, { ignoreClaims: true }); + assert.strictEqual(node.mempool.claims.size, 1); + + while (chain.tip.height < network.names.claimPeriod - 1) { + assert.strictEqual(node.mempool.claims.size, 1); + await mineBlock(node, { ignoreClaims: true }); + } + + // Claim should get invalidated. + assert.strictEqual(node.mempool.claims.size, 0); + + let err; + + try { + await node.mempool.insertClaim(otherClaim); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.type, 'VerifyError'); + assert.strictEqual(err.reason, 'invalid-covenant'); + + assert.strictEqual(node.mempool.claims.size, 0); + }); + + it('should open the auction', async () => { + const root = FROOT.shift(); + const custom = FCUSTOM.shift(); + const top10k = FTOP10k.shift(); + const other = FOTHER.shift(); + + const names = [root, custom, top10k, other]; + + const opens = forEvent(node.mempool, 'tx', names.length, 20000); + + for (const name of names) { + const mtx = await wallet.createOpen(name); + await wallet.sign(mtx); + const tx = mtx.toTX(); + await wdb.addTX(tx); + await node.mempool.addTX(tx); + } + + await opens; + + await mineBlock(node); + + for (const name of names) { + const ns = await nodeClient.execute('getnameinfo', [name]); + assert(!ns.start.locked); + assert.strictEqual(ns.info.state, 'OPENING'); + } + + for (let i = 0; i < network.names.treeInterval + 1; i++) + await mineBlock(node); + + for (const name of names) { + const ns = await nodeClient.execute('getnameinfo', [name]); + assert(!ns.start.locked); + assert.strictEqual(ns.info.state, 'BIDDING'); + } + }); + + it('should open expired claims', async () => { + const rootName = CLAIMED_ROOT[0]; + const otherName = CLAIMED_OTHER[0]; + const names = [rootName, otherName]; + + const root = await nodeClient.execute('getnameinfo', [rootName]); + const other = await nodeClient.execute('getnameinfo', [otherName]); + const commitHeight = root.info.height; + const expireHeight = commitHeight + network.names.renewalWindow; + + // They were claimed in the same block. + assert.strictEqual(root.info.height, other.info.height); + assert.ok(expireHeight > network.names.claimPeriod); + + // let them expire. + while (chain.tip.height < expireHeight) + await mineBlock(node); + + for (const name of names) { + const nameExp = await nodeClient.execute('getnameinfo', [name]); + assert.strictEqual(nameExp.info, null); + } + + // Only OTHER open gets added. + const opens = forEvent(node.mempool, 'tx', names.length, 20000); + + for (const name of names) { + const mtx = await wallet.createOpen(name); + await wallet.sign(mtx); + const tx = await mtx.toTX(); + await wdb.addTX(tx); + await node.mempool.addTX(tx); + } + + await opens; + await mineBlock(node); + + for (const name of names) { + const afterOpen = await nodeClient.execute('getnameinfo', [name]); + assert.strictEqual(afterOpen.info.state, 'OPENING'); + } + + await mineNBlocks(network.names.treeInterval + 1, node); + + for (const name of names) { + const nameAfterInterval = await nodeClient.execute('getnameinfo', [name]); + assert.strictEqual(nameAfterInterval.info.state, 'BIDDING'); + } + }); + }); + + describe('BIP9 - ICANN lockup - success (integration)', function() { + this.timeout(20000); + + let node, chain; + let nodeClient, walletClient; + let wdb, wallet; + + const FROOT = ROOT.slice(); + const FTOP10k = TOP10k.slice(); + const FCUSTOM = CUSTOM.slice(); + const FOTHER = OTHER.slice(); + const CLAIMED = []; + const CLAIMED_ROOT = []; + const CLAIMED_OTHER = []; + + before(async () => { + node = new FullNode({ + memory: true, + network: network.type, + // We don't want wallet to check lockup names for this test. + walletIcannlockup: false, + plugins: [require('../lib/wallet/plugin')] + }); + + await node.ensure(); + await node.open(); + + chain = node.chain; + + deployments[SOFT_FORK_NAME].startTime = 0; + deployments[SOFT_FORK_NAME].timeout = 0xffffffff; + network.names.claimPeriod = minerWindow * 4; + network.names.renewalWindow = minerWindow * 6; + + // Ignore claim validation + ownership.ignore = true; + + nodeClient = new NodeClient({ + port: network.rpcPort, + timeout: 10000 + }); + + walletClient = new WalletClient({ + port: network.walletPort, + timeout: 10000 + }); + + const walletPlugin = node.require('walletdb'); + wdb = walletPlugin.wdb; + wallet = await wdb.get('primary'); + + const account = await walletClient.getAccount('primary', 'default'); + const receive = account.receiveAddress; + node.miner.addAddress(receive); + + await walletClient.execute('selectwallet', ['primary']); + }); + + after(async () => { + // Enable claim validation + ownership.ignore = false; + + // Enable claim validation + // ownership.ignore = false; + deployments[SOFT_FORK_NAME].startTime = ACTUAL_START; + deployments[SOFT_FORK_NAME].timeout = ACTUAL_TIMEOUT; + network.names.claimPeriod = ACTUAL_CLAIM_PERIOD; + network.names.renewalWindow = ACTUAL_RENEWAL_WINDOW; + + await node.close(); + }); + + it('should get deployment stats', async () => { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + + assert.strictEqual(state, thresholdStates.DEFINED); + checkBIP9Info(bip9info, { status: 'defined' }); + }); + + it('should start the soft-fork', async () => { + await mineNBlocks(minerWindow - 2, node); + + // We are now at the threshold of the window. + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.DEFINED); + checkBIP9Info(bip9info, { status: 'defined' }); + } + + // go into new window and change the state to started. + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + assert.deepStrictEqual(bip9info.statistics, { + period: minerWindow, + threshold: activationThreshold, + elapsed: 0, + count: 0, + possible: true + }); + } + }); + + it('should fail to OPEN for the claimable names', async () => { + let err; + try { + await walletClient.createOpen('primary', { + name: FROOT[0] + }); + } catch (e) { + err = e; + } + + assert(err); + assert(err.message, `Name is reserved: ${FROOT[0]}`); + }); + + it('should be possible to claim for now', async () => { + const root = FROOT.shift(); + const other = FOTHER.shift(); + + const mempoolClaim = forEvent(node.mempool, 'claim', 2, 20000); + + { + // send ICANN TLD. + const claim = await wallet.makeFakeClaim(root); + await wdb.sendClaim(claim); + CLAIMED.push(root); + CLAIMED_ROOT.push(root); + } + + { + // send OTHER. + const claim = await wallet.makeFakeClaim(other); + await wdb.sendClaim(claim); + CLAIMED.push(other); + CLAIMED_OTHER.push(other); + } + + await mempoolClaim; + + assert.strictEqual(node.mempool.claims.size, 2); + }); + + it('should fail first window right away', async () => { + const maxFailures = minerWindow - activationThreshold; + + await mineNBlocks(maxFailures, node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: maxFailures, + count: 0, + possible: true + }); + } + + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: maxFailures + 1, + count: 0, + possible: false + }); + } + + // finish the whole window. + await mineNBlocks(activationThreshold - 1, node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: 0, + count: 0, + possible: true + }); + } + }); + + it('should succeed second window by 1 vote', async () => { + await mineNBlocks(activationThreshold, node, { setICANNLockup: true }); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: activationThreshold, + count: activationThreshold, + possible: true + }); + } + + // mine everything else w/o a vote. + for (let i = 0; i < minerWindow - activationThreshold - 1; i++) { + await mineBlock(node); + + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: activationThreshold + i + 1, + count: activationThreshold, + possible: true + }); + } + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + assert.strictEqual(state, thresholdStates.STARTED); + checkBIP9Info(bip9info, { status: 'started' }); + + checkBIP9Statistcs(bip9info.statistics, { + elapsed: minerWindow - 1, + count: activationThreshold, + possible: true + }); + } + + // After this it should go to the ACTIVE state. + await mineBlock(node); + + { + const state = await getICANNLockupState(chain); + const bip9info = await getBIP9Info(nodeClient, SOFT_FORK_NAME); + + assert.strictEqual(state, thresholdStates.LOCKED_IN); + checkBIP9Info(bip9info, { status: 'locked_in' }); + + assert(!bip9info.statistics); + } + }); + + it('should still allow claims before claimPeriod', async () => { + // Just go on the edge of the claim period. + // Leave a room for the next test. + while (chain.tip.height < network.names.claimPeriod - 4) + await mineBlock(node); + + const custom = FCUSTOM.shift(); + const top10k = FTOP10k.shift(); + + const names = [custom, top10k]; + + const mempoolClaim = forEvent(node.mempool, 'claim', names.length, 20000); + + for (const name of names) { + const claim = await wallet.makeFakeClaim(name); + await node.mempool.insertClaim(claim); + } + + await mempoolClaim; + assert.strictEqual(node.mempool.claims.size, names.length); + await mineBlock(node); + }); + + it('should fail to claim and invalidate', async () => { + const root = FROOT.shift(); + const other = FOTHER.shift(); + + const rootClaim = await wallet.makeFakeClaim(root); + const otherClaim = await wallet.makeFakeClaim(other); + + await node.mempool.insertClaim(rootClaim); + assert.strictEqual(node.mempool.claims.size, 1); + + await mineBlock(node, { ignoreClaims: true }); + assert.strictEqual(node.mempool.claims.size, 1); + + while (chain.tip.height < network.names.claimPeriod - 1) { + assert.strictEqual(node.mempool.claims.size, 1); + await mineBlock(node, { ignoreClaims: true }); + } + + assert.strictEqual(node.chain.tip.height + 1, network.names.claimPeriod); + // Claim should get invalidated. + assert.strictEqual(node.mempool.claims.size, 0); + + let err; + + try { + await node.mempool.insertClaim(otherClaim); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.type, 'VerifyError'); + assert.strictEqual(err.reason, 'invalid-covenant'); + + assert.strictEqual(node.mempool.claims.size, 0); + }); + + it('should fail to open the auction for ICANN TLDs', async () => { + const root = FROOT.shift(); + const custom = FCUSTOM.shift(); + const top10k = FTOP10k.shift(); + + const names = [root, custom, top10k]; + + for (const name of names) { + const mtx = await wallet.createOpen(name); + await wallet.sign(mtx); + const tx = mtx.toTX(); + await wdb.addTX(tx); + + let err; + try { + await node.mempool.addTX(tx); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.type, 'VerifyError'); + assert.strictEqual(err.reason, 'invalid-covenant'); + assert.strictEqual(node.mempool.claims.size, 0); + } + + await mineBlock(node); + + for (const name of names) { + const ns = await nodeClient.execute('getnameinfo', [name]); + assert.strictEqual(ns.start.locked, true); + assert.strictEqual(ns.info, null); + } + }); + + it('should open auction for OTHERs', async () => { + const names = [...FOTHER]; + + const opens = forEvent(node.mempool, 'tx', names.length, 20000); + + for (const name of names) { + const tx = await wallet.sendOpen(name); + assert(tx); + } + + await opens; + await mineBlock(node); + + for (const name of names) { + const ns = await nodeClient.execute('getnameinfo', [name]); + assert.strictEqual(ns.start.locked, false); + assert.strictEqual(ns.info.state, 'OPENING'); + } + + await mineNBlocks(network.names.treeInterval + 1, node); + + for (const name of names) { + const ns = await nodeClient.execute('getnameinfo', [name]); + assert(!ns.start.locked); + assert.strictEqual(ns.info.state, 'BIDDING'); + } + }); + + it('should fail to open expired TLDs, but open for OTHERs', async () => { + const rootName = CLAIMED_ROOT[0]; + const otherName = CLAIMED_OTHER[0]; + const root = await nodeClient.execute('getnameinfo', [rootName]); + const other = await nodeClient.execute('getnameinfo', [otherName]); + const commitHeight = root.info.height; + const expireHeight = commitHeight + network.names.renewalWindow; + + // They were claimed in the same block. + assert.strictEqual(root.info.height, other.info.height); + assert.ok(expireHeight > network.names.claimPeriod); + + // let them expire. + while (chain.tip.height < expireHeight) + await mineBlock(node); + + const rootExp0 = await nodeClient.execute('getnameinfo', [rootName]); + const otherExp = await nodeClient.execute('getnameinfo', [otherName]); + assert.strictEqual(rootExp0.info, null); + assert.strictEqual(otherExp.info, null); + + // Only OTHER open gets added. + const opens = forEvent(node.mempool, 'tx', 1, 20000); + + // Fail for the TLD. + let err; + + { + const mtx = await wallet.createOpen(rootName); + await wallet.sign(mtx); + const tx = await mtx.toTX(); + await wdb.addTX(tx); + + try { + await node.mempool.addTX(tx); + } catch (e) { + err = e; + } + } + + assert(err); + assert.strictEqual(err.type, 'VerifyError'); + assert.strictEqual(err.reason, 'invalid-covenant'); + + { + // Should not fail for OTHER (as they are auctionable) + const mtx = await wallet.createOpen(otherName); + await wallet.sign(mtx); + const tx = await mtx.toTX(); + await wdb.addTX(tx); + await node.mempool.addTX(tx); + } + + await opens; + await mineBlock(node); + + const rootAfterOpen = await nodeClient.execute('getnameinfo', [rootName]); + assert.strictEqual(rootAfterOpen.start.locked, true); + assert.strictEqual(rootAfterOpen.info, null); + + const otherAfterOpen = await nodeClient.execute('getnameinfo', [otherName]); + assert.strictEqual(otherAfterOpen.info.state, 'OPENING'); + + await mineNBlocks(network.names.treeInterval + 1, node); + const otherAfterInterval = await nodeClient.execute('getnameinfo', [otherName]); + assert.strictEqual(otherAfterInterval.info.state, 'BIDDING'); + }); + }); +}); + +async function mineNBlocks(n, node, opts = {}) { + for (let i = 0; i < n; i++) + await mineBlock(node, opts); +} + +async function mineBlock(node, opts = {}) { + assert(node); + const chain = node.chain; + const miner = node.miner; + + const setICANNLockup = opts.setICANNLockup || false; + const ignoreClaims = opts.ignoreClaims || false; + + const forBlock = forEvent(node, 'block', 1, 2000); + + let backupClaims = null; + + if (ignoreClaims) { + backupClaims = node.mempool.claims; + node.mempool.claims = new BufferMap(); + } + + const job = await miner.cpu.createJob(chain.tip); + + // opt out of all (esp. `hardening`) as + // some domains in this test still use RSA-1024 + job.attempt.version = 0; + + if (setICANNLockup) + job.attempt.version |= (1 << deployments[SOFT_FORK_NAME].bit); + + job.refresh(); + + if (ignoreClaims) + node.mempool.claims = backupClaims; + + const block = await job.mineAsync(); + await chain.add(block); + await forBlock; + + return block; +} + +async function getICANNLockupState(chain) { + const prev = chain.tip; + const state = await chain.getState(prev, deployments.icannlockup); + return state; +} + +async function getBIP9Info(nodeClient, name) { + const info = await nodeClient.execute('getblockchaininfo'); + return info.softforks[name]; +} diff --git a/test/chain-migration-test.js b/test/chain-migration-test.js index 5183e951d..81fa706f1 100644 --- a/test/chain-migration-test.js +++ b/test/chain-migration-test.js @@ -3,13 +3,14 @@ const assert = require('bsert'); const fs = require('bfile'); const {encoding} = require('bufio'); +const Logger = require('blgr'); const {ZERO_HASH} = require('../lib/protocol/consensus'); const Network = require('../lib/protocol/network'); const WorkerPool = require('../lib/workers/workerpool'); const Miner = require('../lib/mining/miner'); const Chain = require('../lib/blockchain/chain'); const BlockStore = require('../lib/blockstore'); -const layout = require('../lib/blockchain/layout'); +const chLayout = require('../lib/blockchain/layout'); const ChainMigrator = require('../lib/blockchain/migrations'); const MigrationState = require('../lib/migrations/state'); const AbstractMigration = require('../lib/migrations/migration'); @@ -17,8 +18,15 @@ const { types, oldLayout } = require('../lib/migrations/migrator'); -const {migrationError} = require('./util/migrations'); -const {rimraf, testdir} = require('./util/common'); +const { + migrationError, + writeVersion, + getVersion, + fillEntries, + checkEntries +} = require('./util/migrations'); +const common = require('./util/common'); +const {rimraf, testdir} = common; const network = Network.get('regtest'); @@ -80,7 +88,7 @@ describe('Chain Migrations', function() { }); it('should initialize fresh chain migration state', async () => { - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.lastMigration, lastMigrationID); @@ -96,10 +104,10 @@ describe('Chain Migrations', function() { const genesisUndo = await chainDB.getUndoCoins(genesisHash); const b = ldb.batch(); - b.del(layout.M.encode()); - b.put(layout.b.encode(genesisHash), genesisBlock.encode()); - b.put(layout.u.encode(genesisHash), genesisUndo.encode()); - writeVersion(b, 'chain', 1); + b.del(chLayout.M.encode()); + b.put(chLayout.b.encode(genesisHash), genesisBlock.encode()); + b.put(chLayout.u.encode(genesisHash), genesisUndo.encode()); + writeVersion(b, chLayout.V.encode(), 'chain', 1); await b.write(); await chain.close(); @@ -117,11 +125,11 @@ describe('Chain Migrations', function() { chainFlagError(lastMigrationID)); assert.strictEqual(error.message, expected); - const versionData = await ldb.get(layout.V.encode()); + const versionData = await ldb.get(chLayout.V.encode()); const version = getVersion(versionData, 'chain'); assert.strictEqual(version, 1); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.lastMigration, -1); @@ -133,10 +141,10 @@ describe('Chain Migrations', function() { // special case in migrations it('should not migrate last old migration state w/o flag', async () => { const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); b.put(oldLayout.M.encode(0), null); b.put(oldLayout.M.encode(1), null); - writeVersion(b, 'chain', 1); + writeVersion(b, chLayout.V.encode(), 'chain', 1); await b.write(); await chain.close(); @@ -154,11 +162,11 @@ describe('Chain Migrations', function() { chainFlagError(lastMigrationID)); assert.strictEqual(error.message, expected); - const versionData = await ldb.get(layout.V.encode()); + const versionData = await ldb.get(chLayout.V.encode()); const version = getVersion(versionData, 'chain'); assert.strictEqual(version, 1); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.nextMigration, 0); @@ -175,27 +183,27 @@ describe('Chain Migrations', function() { const genesisUndo = await chainDB.getUndoCoins(genesisHash); const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); // Migration blockstore - b.put(layout.b.encode(genesisHash), genesisBlock.encode()); - b.put(layout.u.encode(genesisHash), genesisUndo.encode()); + b.put(chLayout.b.encode(genesisHash), genesisBlock.encode()); + b.put(chLayout.u.encode(genesisHash), genesisUndo.encode()); // migration 3 - MigrateTreeState - b.put(layout.s.encode(), Buffer.alloc(32, 0)); + b.put(chLayout.s.encode(), Buffer.alloc(32, 0)); - writeVersion(b, 'chain', 1); + writeVersion(b, chLayout.V.encode(), 'chain', 1); await b.write(); await chain.close(); chain.options.chainMigrate = lastMigrationID; await chain.open(); - const versionData = await ldb.get(layout.V.encode()); + const versionData = await ldb.get(chLayout.V.encode()); const version = getVersion(versionData, 'chain'); assert.strictEqual(version, chainDB.version); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.lastMigration, lastMigrationID); @@ -205,10 +213,10 @@ describe('Chain Migrations', function() { }); it('should check chaindb flags if there are migrations', async () => { - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); state.nextMigration -= 1; - await ldb.put(layout.M.encode(), state.encode()); + await ldb.put(chLayout.M.encode(), state.encode()); await chain.close(); chain.options.spv = true; @@ -307,7 +315,7 @@ describe('Chain Migrations', function() { it('should initialize fresh chain migration state', async () => { await chain.open(); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.lastMigration, 1); @@ -324,10 +332,10 @@ describe('Chain Migrations', function() { const genesisUndo = await chainDB.getUndoCoins(genesisHash); const b = ldb.batch(); - b.del(layout.M.encode()); - b.put(layout.b.encode(genesisHash), genesisBlock.encode()); - b.put(layout.u.encode(genesisHash), genesisUndo.encode()); - writeVersion(b, 'chain', 1); + b.del(chLayout.M.encode()); + b.put(chLayout.b.encode(genesisHash), genesisBlock.encode()); + b.put(chLayout.u.encode(genesisHash), genesisUndo.encode()); + writeVersion(b, chLayout.V.encode(), 'chain', 1); await b.write(); await chain.close(); @@ -344,11 +352,11 @@ describe('Chain Migrations', function() { chainFlagError(1)); assert.strictEqual(error.message, expected); - const versionData = await ldb.get(layout.V.encode()); + const versionData = await ldb.get(chLayout.V.encode()); const version = getVersion(versionData, 'chain'); assert.strictEqual(version, 1); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.lastMigration, -1); @@ -366,22 +374,22 @@ describe('Chain Migrations', function() { const genesisUndo = await chainDB.getUndoCoins(genesisHash); const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); b.put(oldLayout.M.encode(0), null); - b.put(layout.b.encode(genesisHash), genesisBlock.encode()); - b.put(layout.u.encode(genesisHash), genesisUndo.encode()); - writeVersion(b, 'chain', 1); + b.put(chLayout.b.encode(genesisHash), genesisBlock.encode()); + b.put(chLayout.u.encode(genesisHash), genesisUndo.encode()); + writeVersion(b, chLayout.V.encode(), 'chain', 1); await b.write(); await chain.close(); chain.options.chainMigrate = 1; await chain.open(); - const versionData = await ldb.get(layout.V.encode()); + const versionData = await ldb.get(chLayout.V.encode()); const version = getVersion(versionData, 'chain'); assert.strictEqual(version, 2); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.lastMigration, 1); @@ -394,10 +402,10 @@ describe('Chain Migrations', function() { await chain.open(); const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); b.put(oldLayout.M.encode(0), null); b.put(oldLayout.M.encode(1), null); - writeVersion(b, 'chain', 1); + writeVersion(b, chLayout.V.encode(), 'chain', 1); await b.write(); await chain.close(); @@ -413,11 +421,11 @@ describe('Chain Migrations', function() { chainFlagError(1)); assert.strictEqual(error.message, expected); - const versionData = await ldb.get(layout.V.encode()); + const versionData = await ldb.get(chLayout.V.encode()); const version = getVersion(versionData, 'chain'); assert.strictEqual(version, 1); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.nextMigration, 0); @@ -430,20 +438,20 @@ describe('Chain Migrations', function() { await chain.open(); const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); b.put(oldLayout.M.encode(0), null); b.put(oldLayout.M.encode(1), null); - writeVersion(b, 'chain', 1); + writeVersion(b, chLayout.V.encode(), 'chain', 1); await b.write(); await chain.close(); chain.options.chainMigrate = 1; await chain.open(); - const versionData = await ldb.get(layout.V.encode()); + const versionData = await ldb.get(chLayout.V.encode()); const version = getVersion(versionData, 'chain'); assert.strictEqual(version, 2); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.nextMigration, 2); @@ -484,7 +492,7 @@ describe('Chain Migrations', function() { await chain.open(); const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); b.put(oldLayout.M.encode(0), null); b.put(oldLayout.M.encode(1), null); await b.write(); @@ -502,7 +510,7 @@ describe('Chain Migrations', function() { chainFlagError(2)); assert.strictEqual(error.message, expected); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.nextMigration, 0); @@ -538,7 +546,7 @@ describe('Chain Migrations', function() { await chain.open(); const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); b.put(oldLayout.M.encode(0), null); b.put(oldLayout.M.encode(1), null); await b.write(); @@ -550,7 +558,7 @@ describe('Chain Migrations', function() { assert.strictEqual(migrated1, false); assert.strictEqual(migrated2, true); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.nextMigration, 3); @@ -564,7 +572,7 @@ describe('Chain Migrations', function() { await chain.open(); const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(chLayout.M.encode()); b.put(oldLayout.M.encode(0), null); b.put(oldLayout.M.encode(1), null); await b.write(); @@ -573,7 +581,7 @@ describe('Chain Migrations', function() { chain.options.chainMigrate = 1; await chain.open(); - const rawState = await ldb.get(layout.M.encode()); + const rawState = await ldb.get(chLayout.M.encode()); const state = MigrationState.decode(rawState); assert.strictEqual(state.lastMigration, 1); @@ -583,129 +591,166 @@ describe('Chain Migrations', function() { }); }); - describe('Migration ChainState (integration)', function() { - const location = testdir('migrate-chain-state'); + describe('Migration #0 & #1 (data)', function() { + const location = testdir('migrate-chain-0-1-data'); + const data = require('./data/migrations/chain-0-migrate-migrations.json'); const migrationsBAK = ChainMigrator.migrations; + const Migration = ChainMigrator.MigrateMigrations; const store = BlockStore.create({ memory: true, network }); - const workers = new WorkerPool({ - enabled: true, - size: 2 - }); - const chainOptions = { prefix: location, memory: false, blocks: store, - network, - workers + logger: Logger.global, + network }; - let chain, miner, cpu; - before(async () => { - ChainMigrator.migrations = {}; + let chain; + beforeEach(async () => { await fs.mkdirp(location); - await workers.open(); + await store.open(); }); - after(async () => { + afterEach(async () => { ChainMigrator.migrations = migrationsBAK; + await store.close(); + + if (chain.opened) { + await chain.close(); + } + await rimraf(location); - await workers.close(); }); - beforeEach(async () => { - chain = new Chain(chainOptions); - miner = new Miner({ chain }); - cpu = miner.cpu; + for (const tcase of data.cases) { + it(`should migrate ${tcase.description}`, async () => { + const before = tcase.before; + const after = tcase.after; + const version = tcase.dbVersion; + const mustMigrate1 = tcase.migrate1; + assert(typeof version === 'number'); + assert(typeof mustMigrate1 === 'boolean'); + + chain = new Chain({ + ...chainOptions, + ...tcase.options + }); + let ldb = chain.db.db; + + await ldb.open(); + await fillEntries(ldb, before); + const batch = ldb.batch(); + writeVersion(batch, chLayout.V.encode(), 'chain', version); + await batch.write(); + await ldb.close(); + + let migrated = false; + ChainMigrator.migrations = { + 0: Migration, + 1: class extends AbstractMigration { + async check() { + if (tcase.options.spv) + return types.FAKE_MIGRATE; + + if (tcase.options.prune) + return types.SKIP; + + return types.MIGRATE; + } + + async migrate() { + migrated = true; + } + } + }; - await miner.open(); - await store.open(); - }); + chain.options.chainMigrate = 1; + chain.db.version = 2; - afterEach(async () => { - if (chain.opened) - await chain.close(); + try { + await chain.open(); + } catch (e) { + ; + } - await store.close(); - await miner.close(); - }); + ldb = chain.db.db; - let correctState; - it('should mine 10 blocks', async () => { - await chain.open(); + if (mustMigrate1) + assert(migrated, 'Migration 1 did not run.'); + await checkEntries(ldb, after); + }); + } + }); - for (let i = 0; i < 10; i++) { - const block = await cpu.mineBlock(); - assert(block); - assert(await chain.add(block)); - } + describe('Migrate ChainState (data)', function() { + const location = testdir('migrate-chainstate-data'); + const data = require('./data/migrations/chain-1-chainstate.json'); + const migrationsBAK = ChainMigrator.migrations; + const Migration = ChainMigrator.MigrateChainState; + const store = BlockStore.create({ + memory: true, + network }); - it('should move blocks to pre-blockstore state', async () => { - await chain.open(); - const chainDB = chain.db; - const ldb = chainDB.db; + const chainOptions = { + prefix: location, + memory: false, + blocks: store, + logger: Logger.global, + network + }; - const state = await chainDB.getState(); - const tipHeight = await chainDB.getHeight(state.tip); + let chain, ldb; + before(async () => { + ChainMigrator.migrations = {}; + await fs.mkdirp(location); + await store.open(); + chain = new Chain(chainOptions); + await chain.open(); + ldb = chain.db.db; - const b = ldb.batch(); - for (let i = 0; i <= tipHeight; i++) { - const block = await chainDB.getBlock(i); - const hash = block.hash(); - const undo = await chainDB.getUndoCoins(hash); + await fillEntries(ldb, data.before); - b.put(layout.b.encode(hash), block.encode()); - b.put(layout.u.encode(hash), undo.encode()); - } + await chain.close(); + await store.close(); + }); - await b.write(); + after(async () => { + ChainMigrator.migrations = migrationsBAK; + await rimraf(location); }); - it('should set incorrect chaindb state', async () => { - await chain.open(); - const state = chain.db.state.clone(); - correctState = state.clone(); + beforeEach(async () => { + await fs.mkdirp(location); + await store.open(); + }); - state.coin = 0; - state.value = 0; - state.burned = 0; + afterEach(async () => { + await store.close(); - await chain.db.db.put(layout.R.encode(), state.encode()); + if (chain.opened) { + await chain.close(); + } }); - it('should enable chain state migration', () => { + it('should migrate', async () => { ChainMigrator.migrations = { - 0: ChainMigrator.MigrateChainState + 0: Migration }; - }); - it('should throw error when new migration is available', async () => { - const expected = migrationError(ChainMigrator.migrations, [0], - chainFlagError(0)); - - let error; + chain.options.chainMigrate = 0; try { await chain.open(); } catch (e) { - error = e; + ; } - assert(error, 'Chain must throw an error.'); - assert.strictEqual(error.message, expected); - }); - - it('should migrate chain state', async () => { - chain.options.chainMigrate = 0; - - await chain.open(); - - assert.bufferEqual(chain.db.state.encode(), correctState.encode(), - 'Chain State did not properly migrate.'); + await checkEntries(ldb, data.after); + await chain.close(); }); }); @@ -782,8 +827,8 @@ describe('Chain Migrations', function() { // we don't actually have undo blocks with those blocks. const undoData = Buffer.alloc(100, 1); - ldbBatch.put(layout.b.encode(hash), block.encode()); - ldbBatch.put(layout.u.encode(hash), undoData); + ldbBatch.put(chLayout.b.encode(hash), block.encode()); + ldbBatch.put(chLayout.u.encode(hash), undoData); blocksBatch.pruneBlock(hash); } @@ -824,6 +869,118 @@ describe('Chain Migrations', function() { }); }); + describe('Migrate BlockStore (data)', function() { + const location = testdir('migrate-blockstore-data'); + const data = require('./data/migrations/chain-2-blockstore.json'); + const migrationsBAK = ChainMigrator.migrations; + const Migration = ChainMigrator.MigrateBlockStore; + const layout = Migration.layout(); + const store = BlockStore.create({ + memory: true, + network + }); + + const chainOptions = { + prefix: location, + memory: false, + blocks: store, + logger: Logger.global, + network + }; + + const full = { + name: 'full', + options: chainOptions, + data: data.full + }; + const pruned = { + name: 'pruned', + options: { + ...chainOptions, + prune: true + }, + data: data.pruned + }; + + let chain, ldb; + beforeEach(async () => { + await fs.mkdirp(location); + await store.open(); + }); + + afterEach(async () => { + await rimraf(location); + await store.close(); + + if (chain.opened) + await chain.close(); + }); + + after(async () => { + ChainMigrator.migrations = migrationsBAK; + }); + + for (const tcase of [full, pruned]) { + it(`should migrate ${tcase.name} node`, async () => { + ChainMigrator.migrations = {}; + chain = new Chain(tcase.options); + await chain.open(); + ldb = chain.db.db; + + await fillEntries(ldb, tcase.data); + await chain.close(); + + ChainMigrator.migrations = { + 0: Migration + }; + + chain.options.chainMigrate = 0; + + await chain.open(); + + // do we have block entries. + const blocks = await ldb.range({ + gte: layout.b.min(), + lte: layout.b.max() + }); + + assert.strictEqual(blocks.length, 0); + + const undos = await ldb.range({ + gte: layout.u.min(), + lte: layout.u.max() + }); + + assert.strictEqual(undos.length, 0); + + for (const [hexKey, hexValue] of Object.entries(data.full)) { + // block. + if (hexKey.slice(0, 2) === 'b'.charCodeAt(0).toString(16)) { + const key = layout.b.decode(Buffer.from(hexKey, 'hex')); + const hash = key[0]; + const value = Buffer.from(hexValue, 'hex'); + + const block = await store.readBlock(hash); + assert(block); + assert.bufferEqual(block, value); + continue; + } + + // undo block + if (hexKey.slice(0, 2) === 'u'.charCodeAt(0).toString(16)) { + const key = layout.u.decode(Buffer.from(hexKey, 'hex')); + const hash = key[0]; + const value = Buffer.from(hexValue, 'hex'); + + const undo = await store.readUndo(hash); + assert(undo); + assert.bufferEqual(undo, value); + } + } + }); + } + }); + describe('Migration Tree State (integration)', function() { const location = testdir('migrate-tree-state'); const migrationsBAK = ChainMigrator.migrations; @@ -895,8 +1052,8 @@ describe('Chain Migrations', function() { // Previous state await chain.open(); const b = ldb.batch(); - b.put(layout.s.encode(), Buffer.alloc(32, 0x00)); - writeVersion(b, 'chain', 2); + b.put(chLayout.s.encode(), Buffer.alloc(32, 0x00)); + writeVersion(b, chLayout.V.encode(), 'chain', 2); await b.write(); await chain.close(); @@ -946,17 +1103,17 @@ describe('Chain Migrations', function() { it('should migrate tree state (2)', async () => { await chain.open(); - const state = MigrationState.decode(await ldb.get(layout.M.encode())); + const state = MigrationState.decode(await ldb.get(chLayout.M.encode())); state.nextMigration = 0; // revert migration const b = ldb.batch(); const root = Buffer.alloc(32, 0x01); // revert version in DB. - writeVersion(b , 'chain', 2); + writeVersion(b, chLayout.V.encode(), 'chain', 2); // encode wrong tree state (non default) - b.put(layout.s.encode(), root); - b.put(layout.M.encode(), state.encode()); + b.put(chLayout.s.encode(), root); + b.put(chLayout.M.encode(), state.encode()); await b.write(); await chain.close(); @@ -973,7 +1130,7 @@ describe('Chain Migrations', function() { assert(error, 'Chain must throw an error.'); assert.strictEqual(error.message, `Missing node: ${root.toString('hex')}.`); - const version = getVersion(await ldb.get(layout.V.encode()), 'chain'); + const version = getVersion(await ldb.get(chLayout.V.encode()), 'chain'); assert.strictEqual(version, 3); assert.bufferEqual(chaindb.treeState.treeRoot, root); assert.bufferEqual(chaindb.treeState.compactionRoot, ZERO_HASH); @@ -1028,7 +1185,7 @@ describe('Chain Migrations', function() { // Previous state await chain.open(); const b = ldb.batch(); - writeVersion(b, 'chain', 2); + writeVersion(b, chLayout.V.encode(), 'chain', 2); await b.write(); await chain.close(); @@ -1075,25 +1232,75 @@ describe('Chain Migrations', function() { assert.bufferEqual(state.encode(), encoded); }); }); -}); -function writeVersion(b, name, version) { - const value = Buffer.alloc(name.length + 4); + describe('Migrate Tree State (data)', function() { + const location = testdir('migrate-treestate-data'); + const data = require('./data/migrations/chain-3-treestate.json'); + const migrationsBAK = ChainMigrator.migrations; + const Migration = ChainMigrator.MigrateTreeState; + const store = BlockStore.create({ + memory: true, + network + }); + + const chainOptions = { + prefix: location, + memory: false, + blocks: store, + logger: Logger.global, + network + }; + + let chain, ldb; + before(async () => { + ChainMigrator.migrations = {}; + await fs.mkdirp(location); + await store.open(); + chain = new Chain(chainOptions); + chain.db.version = 2; + await chain.open(); + ldb = chain.db.db; + + await fillEntries(ldb, data.before); + + await chain.close(); + await store.close(); + }); + + after(async () => { + ChainMigrator.migrations = migrationsBAK; + await rimraf(location); + }); - value.write(name, 0, 'ascii'); - value.writeUInt32LE(version, name.length); + beforeEach(async () => { + await fs.mkdirp(location); + await store.open(); + }); + + afterEach(async () => { + await store.close(); - b.put(layout.V.encode(), value); -} + if (chain.opened) { + await chain.close(); + } + }); -function getVersion(data, name) { - const error = 'version mismatch'; + it('should migrate', async () => { + ChainMigrator.migrations = { + 0: Migration + }; - if (data.length !== name.length + 4) - throw new Error(error); + chain.options.chainMigrate = 0; + chain.db.version = 3; + try { + await chain.open(); + } catch (e) { + ; + } - if (data.toString('ascii', 0, name.length) !== name) - throw new Error(error); + await checkEntries(ldb, data.after); + await chain.close(); + }); + }); +}); - return data.readUInt32LE(name.length); -} diff --git a/test/chain-reset-reorg-test.js b/test/chain-reset-reorg-test.js index 0dfb93e06..132c9fbfa 100644 --- a/test/chain-reset-reorg-test.js +++ b/test/chain-reset-reorg-test.js @@ -9,16 +9,16 @@ const { openChainBundle, closeChainBundle, syncChain, - chainTreeHas, - chainTxnHas + chainTreeHasName, + chainTxnHasName } = require('./util/chain'); const network = Network.get('regtest'); describe('Chain reorg/reset test', function() { let wallet; - let chainb1, chain, mainMiner; - let chainb2, altChain, altMiner; + let chainBundle1, chain, mainMiner; + let chainBundle2, altChain, altMiner; let tipHeight = 0; const mineBlocksOpens = async (miner, n) => { @@ -58,38 +58,38 @@ describe('Chain reorg/reset test', function() { wallet = new MemWallet({ network }); - chainb1 = getChainBundle({ + chainBundle1 = getChainBundle({ memory: true, workers: true, address: wallet.getReceive() }); - chainb2 = getChainBundle({ + chainBundle2 = getChainBundle({ memory: true, workers: true, address: wallet.getReceive() }); - chainb1.chain.on('connect', (entry, block) => { + chainBundle1.chain.on('connect', (entry, block) => { wallet.addBlock(entry, block.txs); }); - chainb1.chain.on('disconnect', (entry, block) => { + chainBundle1.chain.on('disconnect', (entry, block) => { wallet.removeBlock(entry, block.txs); }); - await openChainBundle(chainb1); - await openChainBundle(chainb2); + await openChainBundle(chainBundle1); + await openChainBundle(chainBundle2); - chain = chainb1.chain; - mainMiner = chainb1.miner; - altChain = chainb2.chain; - altMiner = chainb2.miner; + chain = chainBundle1.chain; + mainMiner = chainBundle1.miner; + altChain = chainBundle2.chain; + altMiner = chainBundle2.miner; }; const afterHook = async () => { - await closeChainBundle(chainb1); - await closeChainBundle(chainb2); + await closeChainBundle(chainBundle1); + await closeChainBundle(chainBundle2); }; describe('Chain reorg', function() { @@ -124,8 +124,8 @@ describe('Chain reorg/reset test', function() { tipHeight++; for (const name of names0) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } const root = await chain.db.treeRoot(); @@ -136,8 +136,8 @@ describe('Chain reorg/reset test', function() { assert.bufferEqual(chain.db.treeRoot(), root); for (const name of [...names0, ...names1]) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } // mine 3 blocks on alt chain @@ -148,14 +148,14 @@ describe('Chain reorg/reset test', function() { assert.bufferEqual(chain.db.treeRoot(), root); for (const name of [...names0, ...names2]) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } // these got reorged. for (const name of names1) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), false); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), false); } assert.strictEqual(chain.tip.height, tipHeight); @@ -167,13 +167,13 @@ describe('Chain reorg/reset test', function() { tipHeight++; for (const name of [...names0, ...names2, ...names3]) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of names1) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), false); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), false); } assert.notBufferEqual(chain.db.treeRoot(), root); @@ -195,8 +195,8 @@ describe('Chain reorg/reset test', function() { tipHeight += 3; for (const name of names0) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } assert.notBufferEqual(chain.db.txn.rootHash(), root); @@ -208,30 +208,30 @@ describe('Chain reorg/reset test', function() { assert.strictEqual(chain.tip.height, tipHeight + 3); for (const name of [...names0, ...names1.slice(0, -1)]) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } - assert.strictEqual(await chainTreeHas(chain, names1[names1.length - 1]), false); - assert.strictEqual(await chainTxnHas(chain, names1[names1.length - 1]), true); + assert.strictEqual(await chainTreeHasName(chain, names1[names1.length - 1]), false); + assert.strictEqual(await chainTxnHasName(chain, names1[names1.length - 1]), true); const names2 = await mineBlocksOpens(altMiner, 4); await syncChain(altChain, chain, tipHeight); tipHeight += 4; for (const name of [...names0, ...names2.slice(0, -2)]) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of [...names2.slice(-2)]) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of names1) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), false); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), false); } }); @@ -244,13 +244,13 @@ describe('Chain reorg/reset test', function() { assert.strictEqual(chain.tip.height, tipHeight + 15); for (const name of [...names1.slice(0, -2)]) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of [...names1.slice(-2)]) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } // mine 16 on alt chain. @@ -262,18 +262,18 @@ describe('Chain reorg/reset test', function() { assert.strictEqual(chain.tip.height, tipHeight); for (const name of [...names2.slice(0, -3)]) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of [...names2.slice(-3)]) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of names1) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), false); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), false); } }); }); @@ -313,8 +313,8 @@ describe('Chain reorg/reset test', function() { tipHeight += 2; for (const name of names0) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } const root = await chain.db.treeRoot(); @@ -327,26 +327,26 @@ describe('Chain reorg/reset test', function() { assert.bufferEqual(chain.db.treeRoot(), root); for (const name of [...names0, ...resetNames]) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } await chain.reset(tipHeight - 2); for (const name of names0) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of resetNames) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), false); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), false); } await syncChain(altChain, chain, tipHeight - 2); for (const name of [...names0, ...resetNames]) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } }); @@ -358,8 +358,8 @@ describe('Chain reorg/reset test', function() { tipHeight += 3; for (const name of names0) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } const resetNames = await mineBlocksOpens(mainMiner, 3); @@ -367,34 +367,34 @@ describe('Chain reorg/reset test', function() { tipHeight += 3; for (const name of [...names0, ...resetNames.slice(0, -1)]) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } const txnName = resetNames[resetNames.length - 1]; - assert.strictEqual(await chainTreeHas(chain, txnName), false); - assert.strictEqual(await chainTxnHas(chain, txnName), true); + assert.strictEqual(await chainTreeHasName(chain, txnName), false); + assert.strictEqual(await chainTxnHasName(chain, txnName), true); await chain.reset(tipHeight - 3); for (const name of names0) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of resetNames) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), false); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), false); } await syncChain(altChain, chain, tipHeight - 3); for (const name of [...names0, ...resetNames.slice(0, -1)]) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } - assert.strictEqual(await chainTreeHas(chain, txnName), false); - assert.strictEqual(await chainTxnHas(chain, txnName), true); + assert.strictEqual(await chainTreeHasName(chain, txnName), false); + assert.strictEqual(await chainTxnHasName(chain, txnName), true); }); it('should mine 18 blocks, reset and resync', async () => { @@ -411,13 +411,13 @@ describe('Chain reorg/reset test', function() { const txnNames = names.slice(-3); for (const name of treeNames) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of txnNames) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } await chain.reset(tipHeight - 18); @@ -427,13 +427,13 @@ describe('Chain reorg/reset test', function() { assert.strictEqual(altChain.tip.height, tipHeight); for (const name of treeNames) { - assert.strictEqual(await chainTreeHas(chain, name), true); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), true); + assert.strictEqual(await chainTxnHasName(chain, name), true); } for (const name of txnNames) { - assert.strictEqual(await chainTreeHas(chain, name), false); - assert.strictEqual(await chainTxnHas(chain, name), true); + assert.strictEqual(await chainTreeHasName(chain, name), false); + assert.strictEqual(await chainTxnHasName(chain, name), true); } }); }); diff --git a/test/claim-test.js b/test/claim-test.js index f14364e61..26cbc1392 100644 --- a/test/claim-test.js +++ b/test/claim-test.js @@ -6,9 +6,10 @@ const Address = require('../lib/primitives/address'); const Claim = require('../lib/primitives/claim'); const FullNode = require('../lib/node/fullnode'); const consensus = require('../lib/protocol/consensus'); -const ownership = require('../lib/covenants/ownership'); +const {ownership} = require('../lib/covenants/ownership'); const reserved = require('../lib/covenants/reserved'); const {Resource} = require('../lib/dns/resource'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); const network = Network.get('regtest'); @@ -46,7 +47,14 @@ async function mineBlocks(n, addr) { describe('Reserved Name Claims', function() { this.timeout(10000); + + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + before(async () => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; + await node.open(); wallet = await wdb.create(); @@ -54,6 +62,8 @@ describe('Reserved Name Claims', function() { }); after(async () => { + ownership.Resolver = originalResolver; + ownership.servers = originalServers; await node.close(); }); diff --git a/test/contractstate-test.js b/test/contractstate-test.js index 3dbd65409..03bee42f1 100644 --- a/test/contractstate-test.js +++ b/test/contractstate-test.js @@ -23,30 +23,17 @@ function nameContext(name, type) { switch (type) { case types.OPEN: - output.covenant.type = types.OPEN; - output.covenant.pushHash(nameHash); // nameHash - output.covenant.pushU32(0); // start - output.covenant.push(rawName); // rawName + output.covenant.setOpen(nameHash, rawName); break; case types.BID: - output.covenant.type = types.BID; - output.covenant.pushHash(nameHash); // nameHash - output.covenant.pushU32(0); // start - output.covenant.push(rawName); // rawName - output.covenant.pushHash(Buffer.alloc(32)); // blind + output.covenant.setBid(nameHash, 0, rawName, Buffer.alloc(32)); break; case types.REVEAL: - output.covenant.type = types.REVEAL; - output.covenant.pushHash(nameHash); // nameHash - output.covenant.pushU32(100); // height - output.covenant.pushHash(Buffer.alloc(32)); // nonce + output.covenant.setReveal(nameHash, 100, Buffer.alloc(32)); break; case types.UPDATE: { const data = Buffer.from('hello world', 'ascii'); - output.covenant.type = types.UPDATE; - output.covenant.pushHash(nameHash); // nameHash - output.covenant.pushU32(100); // height - output.covenant.push(data); // record + output.covenant.setUpdate(nameHash, 100, data); break; } } diff --git a/test/data/migrations/README.md b/test/data/migrations/README.md new file mode 100644 index 000000000..4bee7730f --- /dev/null +++ b/test/data/migrations/README.md @@ -0,0 +1,11 @@ +Migration data with before/after entries. + +It contains generator scripts and manually assembled jsons for the migrations. + +## Notes + - wallet-4-bid-reveal.json (wallet-4-bid-reveal-gen.js) + - `fullAfter` is db dump with new version, `after` is filtered out things + that can not be recovered. + - Removed not-owned BID <-> Reveal bidings, they are not possible to + recover. We don't have transactions for those. We can't recover height of + the bids either. diff --git a/test/data/migrations/chain-0-migrate-migrations-gen.js b/test/data/migrations/chain-0-migrate-migrations-gen.js new file mode 100644 index 000000000..244cfe52c --- /dev/null +++ b/test/data/migrations/chain-0-migrate-migrations-gen.js @@ -0,0 +1,73 @@ +'use strict'; + +const Logger = require('blgr'); +const Network = require('../../../lib/protocol/network'); +const ChainDB = require('../../../lib/blockchain/chaindb'); +const mutils = require('../../util/migrations'); + +const NETWORK = Network.get('regtest'); +let blockstore = null; + +try { + blockstore = require('../../../lib/blockstore'); +} catch (e) { + ; +} + +async function dumpMigration(options) { + let blocks = null; + + if (blockstore && !options.spv) { + blocks = blockstore.create({ + memory: true, + network: NETWORK, + logger: Logger.global + }); + + await blocks.open(); + } + + const chainDB = new ChainDB({ + logger: Logger.global, + network: NETWORK, + memory: true, + prune: options.prune, + spv: options.spv, + entryCache: 5000, + blocks + }); + + await chainDB.open(); + const data = await getMigrationDump(chainDB); + + await chainDB.close(); + + if (blocks) + await blocks.close(); + + return data; +} + +(async () => { + const full = await dumpMigration({ prune: false, spv: false }); + const prune = await dumpMigration({ prune: true, spv: false }); + const spv = await dumpMigration({ prune: false, spv: true }); + + console.log(JSON.stringify({ + full, + prune, + spv + }, null, 2)); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); + +async function getMigrationDump(chaindb) { + const prefixes = [ + 'O', + 'M' + ]; + + return mutils.dumpChainDB(chaindb, prefixes.map(mutils.prefix2hex)); +} diff --git a/test/data/migrations/chain-0-migrate-migrations.json b/test/data/migrations/chain-0-migrate-migrations.json new file mode 100644 index 000000000..454d372e4 --- /dev/null +++ b/test/data/migrations/chain-0-migrate-migrations.json @@ -0,0 +1,158 @@ +{ + "description": "Migrate migrations. Affects chaindb layout M.", + "cases": [ + { + "description": "Migration after migration flag was set (full) < v3.0.0.", + "options": { + "spv": false, + "prune": false + }, + "dbVersion": 1, + "migrate1": false, + "before": { + "4d00000000": "00", + "4d00000001": "00", + "4f": "cf9538ae0000000000000000" + }, + "after": { + "4d": "000000000200", + "4f": "cf9538ae0000000000000000" + } + }, + { + "description": "Migration after migration flag was set (pruned) < v3.0.0.", + "options": { + "spv": false, + "prune": true + }, + "dbVersion": 1, + "migrate1": false, + "before": { + "4d00000000": "00", + "4d00000001": "00", + "4f": "cf9538ae0200000000000000" + }, + "after": { + "4d": "00000000020101", + "4f": "cf9538ae0200000000000000" + } + }, + { + "description": "Migration after migration flag was set (spv) from < v3.0.0.", + "options": { + "spv": true, + "prune": false + }, + "dbVersion": 1, + "migrate1": false, + "before": { + "4d00000000": "00", + "4d00000001": "00", + "4f": "cf9538ae0100000000000000" + }, + "after": { + "4d": "000000000200", + "4f": "cf9538ae0100000000000000" + } + }, + { + "description": "Migration before flag was set (full) from 2.3.0 to v2.4.0", + "options": { + "spv": false, + "prune": false + }, + "dbVersion": 0, + "migrate1": true, + "before": { + "4d00000000": "00", + "4f": "cf9538ae0000000000000000" + }, + "after": { + "4d": "000000000200", + "4f": "cf9538ae0000000000000000" + } + }, + { + "description": "Migration before flag was set (pruned) from 2.3.0 to v2.4.0.", + "options": { + "spv": false, + "prune": true + }, + "dbVersion": 0, + "migrate1": false, + "before": { + "4d00000000": "00", + "4f": "cf9538ae0200000000000000" + }, + "after": { + "4d": "00000000020101", + "4f": "cf9538ae0200000000000000" + } + }, + { + "description": "Migration before flag was set (spv) from 2.3.0 to v2.4.0.", + "options": { + "spv": true, + "prune": false + }, + "dbVersion": 0, + "migrate1": false, + "before": { + "4d00000000": "00", + "4f": "cf9538ae0100000000000000" + }, + "after": { + "4d": "000000000200", + "4f": "cf9538ae0100000000000000" + } + }, + { + "description": "Migration before flag was set (full) from < v2.3.0", + "options": { + "spv": false, + "prune": false + }, + "dbVersion": 0, + "migrate1": true, + "before": { + "4f": "cf9538ae0000000000000000" + }, + "after": { + "4d": "000000000200", + "4f": "cf9538ae0000000000000000" + } + }, + { + "description": "Migration before flag was set (pruned) from < v2.3.0.", + "options": { + "spv": false, + "prune": true + }, + "dbVersion": 0, + "migrate1": false, + "before": { + "4f": "cf9538ae0200000000000000" + }, + "after": { + "4d": "00000000020101", + "4f": "cf9538ae0200000000000000" + } + }, + { + "description": "Migration before flag was set (spv) from < v2.3.0", + "options": { + "spv": true, + "prune": false + }, + "dbVersion": 0, + "migrate1": false, + "before": { + "4f": "cf9538ae0100000000000000" + }, + "after": { + "4d": "000000000200", + "4f": "cf9538ae0100000000000000" + } + } + ] +} diff --git a/test/data/migrations/chain-1-chainstate-gen.js b/test/data/migrations/chain-1-chainstate-gen.js new file mode 100644 index 000000000..ffc8805d9 --- /dev/null +++ b/test/data/migrations/chain-1-chainstate-gen.js @@ -0,0 +1,198 @@ +'use strict'; + +/** + * Migration from 2.1.2 to 2.1.3 + * NOTE, patches necessary to run this migration gen: +diff --git a/test/util/memwallet.js b/test/util/memwallet.js +index 02cce343..c4831893 100644 +--- a/test/util/memwallet.js ++++ b/test/util/memwallet.js +@@ -284,8 +284,8 @@ class MemWallet { + if (height == null) + height = -1; + +- if (this.map.has(hash)) +- return true; ++ // if (this.map.has(hash)) ++ // return true; + + const view = new CoinView(); + * + */ + +const Logger = require('blgr'); +const Network = require('../../../lib/protocol/network'); +const Mempool = require('../../../lib/mempool/mempool'); +const Miner = require('../../../lib/mining/miner'); +const Chain = require('../../../lib/blockchain/chain'); +const MemWallet = require('../../util/memwallet'); +const HD = require('../../../lib/hd'); +// const rules = require('../../../lib/covenants/rules'); +const mutils = require('../../util/migrations'); + +const NETWORK = Network.get('regtest'); +let blockstore = null; + +try { + blockstore = require('../../../lib/blockstore'); +} catch (e) { + ; +} + +const wallet1priv = 'rprvKE8qsHtkmUxUSPQdn2sFKFUcKyUQz9pKQhxjEWecnXg9hgJMsmJXcw' + + 'J77SqmHT1R6mcuNqVPzgT2EoGStsXaUN92VJKhQWUB6uZdL8gAZvez'; + +let txID = 0; + +async function dumpMigration() { + const commonOptions = { + memory: true, + network: NETWORK, + logger: Logger.global + }; + + let blocks = null; + + if (blockstore) { + blocks = blockstore.create(commonOptions); + + await blocks.open(); + } + + const chain = new Chain({ + ...commonOptions, + entryCache: 5000, + blocks + }); + + const mempool = new Mempool({ + ...commonOptions, + chain + }); + + const miner = new Miner({ + ...commonOptions, + mempool, + chain + }); + + const master = HD.HDPrivateKey.fromBase58(wallet1priv, NETWORK); + const wallet = new MemWallet({ + network: NETWORK, + master + }); + + const address = wallet.getAddress(); + miner.addAddress(address); + + mempool.on('tx', (tx) => { + miner.cpu.notifyEntry(); + wallet.addTX(tx); + }); + + chain.on('connect', async (entry, block, view) => { + try { + await mempool._addBlock(entry, block.txs, view); + wallet.addBlock(entry, block.txs); + } catch (e) { + ; + } + }); + + chain.on('disconnect', async (entry, block) => { + try { + await mempool._removeBlock(entry, block.txs); + } catch (e) { + ; + } + }); + + await chain.open(); + await mempool.open(); + await miner.open(); + + miner.createBlock = async (tip, address) => { + return mutils.createBlock({ + txno: txID++, + chain, + miner, + tip, + address + }); + }; + + const mineBlock = async () => { + const block = await miner.mineBlock(chain.tip, address); + await chain.add(block); + }; + + // 10 blocks + for (let i = 0; i < 20; i++) { + await mineBlock(); + } + + // full auction from start to finish. + // const name = rules.grindName(10, chain.tip.height + 1, NETWORK); + const name = 'nypvzvlxha'; + const openTX = await wallet.createOpen(name); + await mempool.addTX(openTX.toTX()); + + for (let i = 0; i < NETWORK.names.treeInterval + 1; i++) + await mineBlock(); + + const bidTX1 = await wallet.createBid(name, 10000, 20000); + await mempool.addTX(bidTX1.toTX()); + const bidTX2 = await wallet.createBid(name, 10000, 20000); + await mempool.addTX(bidTX2.toTX()); + + for (let i = 0; i < NETWORK.names.biddingPeriod; i++) + await mineBlock(); + + const reveal = await wallet.createReveal(name); + await mempool.addTX(reveal.toTX()); + + for (let i = 0; i < NETWORK.names.revealPeriod + 1; i++) + await mineBlock(); + + const register = await wallet.createRegister(name, Buffer.from([1,2,3])); + await mempool.addTX(register.toTX()); + await mineBlock(); + + const update = await wallet.createUpdate(name, Buffer.from([1,2,3,4])); + await mempool.addTX(update.toTX()); + await mineBlock(); + + const data = await getMigrationDump(chain); + + await miner.close(); + await mempool.close(); + await chain.close(); + + if (blocks) + await blocks.close(); + + return data; +} + +(async () => { + const full = await dumpMigration(); + + console.log(JSON.stringify({ + full + }, null, 2)); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); + +async function getMigrationDump(chain) { + const prefixes = [ + 'R', + 'h', + 'H', + 'b', + 'u' + ]; + + return mutils.dumpChainDB(chain.db, prefixes.map(mutils.prefix2hex)); +} diff --git a/test/data/migrations/chain-1-chainstate.json b/test/data/migrations/chain-1-chainstate.json new file mode 100644 index 000000000..18239db57 --- /dev/null +++ b/test/data/migrations/chain-1-chainstate.json @@ -0,0 +1,289 @@ +{ + "description": "Migration fo the chainstate from before v2.1.3", + "before": { + "52": "07244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d33000000000000003000000000000000b06e8cf4140000001027000000000000", + "4800000000": "ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5", + "4800000001": "5a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3", + "4800000002": "61c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed", + "4800000003": "6a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49", + "4800000004": "5a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135", + "4800000005": "023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86", + "4800000006": "2ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81", + "4800000007": "218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80", + "4800000008": "0d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637", + "4800000009": "52de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c", + "480000000a": "796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e", + "480000000b": "41a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801", + "480000000c": "4ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972", + "480000000d": "19621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69", + "480000000e": "63a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f", + "480000000f": "20cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31", + "4800000010": "195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e", + "4800000011": "5fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575", + "4800000012": "25ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f", + "4800000013": "680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a", + "4800000014": "63b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac", + "4800000015": "62dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8", + "4800000016": "2e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa", + "4800000017": "23403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e", + "4800000018": "381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd63", + "4800000019": "5a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91", + "480000001a": "3e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c", + "480000001b": "792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d", + "480000001c": "58c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7", + "480000001d": "32b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b", + "480000001e": "7992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec", + "480000001f": "50215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256", + "4800000020": "44c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40", + "4800000021": "0ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee", + "4800000022": "507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2", + "4800000023": "35947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d", + "4800000024": "78f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea2", + "4800000025": "73e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf", + "4800000026": "576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde6", + "4800000027": "7407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f", + "4800000028": "2a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe48672", + "4800000029": "0ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd", + "480000002a": "2c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a980282303", + "480000002b": "60f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1", + "480000002c": "07244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d", + "62023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "000000002e4d385e000000005a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d7821350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e1b13c02e1a1a62973bd11419b8ae101cc1d5673ac4367d2d5887ced610a7ffbbcdd88e0f14469f736ff4dbb17f3ae4632964d093efa6f9362d48581cc4f5300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000500000003124d696e657220666f7220646174612067656e080400000000000000080000000000000000", + "6207244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d": "0300000096a8385e0000000060f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da16c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060e9c7bb6f48fa01972ccb737b8a3b7ad6a86a693c3a63e9c9b34a2331f1c7d0bc594e1078e3e121e0379a60f09acb2a102d52d0070372a4621743f724770e9600000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2b00000001ba9d3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002c00000003124d696e657220666f7220646174612067656e082b00000000000000080000000000000000000000000217a1d4c313e25990c1d5955fd8f1f2dbaceb9b89f11daacad4066ce631c3026a00000000ffffffff528c8499ebe12e2ef754d1752dc8e8b6fea7efecc2ddb43a77e04301f7f4dbd400000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5007032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000401020304468a35770000000000147a98b6621e4ccc528560645f6a7682633a24fad80000000000000241775c6ee7167f573270b29450a141cfdca3fec0dbb880621360b2cc51b82cb83c6068cfe6d030267cff7099ebc785b97efff854ef11c42671f476e5238893fe320121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd530241279501bc9ab9b5732ac2211228e7943abaed43f0a5a4f383c8a8d2a69f77d4d774076bd85c1711fa55187ff248637a6bda1ff9544d1f3a291e815c3aa9cd5870012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "620ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd": "000000008ea1385e000000002a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe486726c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2a81c8b6fa6c23724a6f75c264b33274ce0bf81b6c7c96e941f6ecf0214466030f18d522ec485998e9c7e6d8dd56450c50e9e8718256aef1c2ebf7b1ddb4a0100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff280000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002900000003124d696e657220666f7220646174612067656e082800000000000000080000000000000000", + "620d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "010000003654385e00000000218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561ba8e15b6446ae574bd94831f78c34e3d36f891b499ea318c92d6c158cbc7062c56c8f928267cc7889f18ccd50a413daaba98fba47a74da65e46746a1b914400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff070000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000800000003124d696e657220666f7220646174612067656e080700000000000000080000000000000000", + "620ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee": "00000000ce8e385e0000000044c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e0179700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008fb43c4c4bd13d9e5cc08ca76c3407a94111097d4855759fb45abc0633c5d668f0e5935ac5f4058b276772434da60262d4b96fec397563d622819e4a02b40e0c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff200000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002100000003124d696e657220666f7220646174612067656e082000000000000000080000000000000000", + "62195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e": "00000000f666385e0000000020cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee6a7f23a0f57830311a30c3c8445b673587e120ea99558ea8c32faf66236d4fdec7715d616580a8008487f6df0133e0a101981f6b22fa007e45025a04c9628900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001000000003124d696e657220666f7220646174612067656e080f00000000000000080000000000000000", + "6219621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69": "03000000ee5f385e000000004ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a8acc94a096d21eb0dc87bdfb0cfcd4191d63c157af264c9aa5a3bc61154762f71621f91895fd7ce1c76bcd6ca7942803e1df39636b8f5df0541b601538903700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000d00000003124d696e657220666f7220646174612067656e080c00000000000000080000000000000000", + "6220cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31": "000000009e64385e0000000063a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000faf9826f22ab9e5fd8850a72e6d526c99774645b2da88d668acfc516ccf6a7bd1cfa41793cd6f68255eda0b063e35c68980a389ffd731d877bedc14b3d9a2e200000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000f00000003124d696e657220666f7220646174612067656e080e00000000000000080000000000000000", + "62218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "01000000de51385e000000002ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb8100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0fa2412035ea50aad5dfa552200a584f3c8b5f5ed0395a3ba9f61ffcdfaf03c10d7a31250c9abd77aa5f7ea994b191de4980200322f7017a60851612adc5d8d00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000700000003124d696e657220666f7220646174612067656e080600000000000000080000000000000000", + "6223403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e": "020000005e77385e000000002e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6a4069d1f79ea506489fa3ce6f2ea392a88ce03f5eca8da136d37f8173adbd5266d25967ca47f116f14b53c6a19befaec491fad22a84b37d38532b71b2f033e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff160000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001700000003124d696e657220666f7220646174612067656e081600000000000000080000000000000000", + "6225ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f": "00000000a66b385e000000005fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be185750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099440cecc52f2703578666b0adcfab3ca236ebb4c29e8d53648bf7fabf897fbbd8c894203ead6810c16ef6136d1cafc12a51e369dbd515727c16f6f6d745e74f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff110000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001200000003124d696e657220666f7220646174612067656e081100000000000000080000000000000000", + "622a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe48672": "00000000369f385e000000007407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a427d9fd0a8fc3c3425254b9fecc84d46e816b84490e5e5365ff71c648907da92c3e15b47fd4a34691dad929e94a579cc1708d5d6f0c4f25467fc9624dc15cd100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002800000003124d696e657220666f7220646174612067656e082700000000000000080000000000000000", + "622c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a980282303": "00000000e6a3385e000000000ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000253216431886015813a2c3265167c0b470bc033872d45199fe1b929fc226889e030f1ef09799fd5be8064fb4d730aab74a6b9aad1096d416e71b041917c1604500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff290000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002a00000003124d696e657220666f7220646174612067656e082900000000000000080000000000000000", + "622ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "00000000864f385e00000000023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009166e55997f82e200b1075b7a1cc9a12e40827d7ccac1b643c2dd690e09761b4a9cd7eeb8b6a7d937080f36c2a5804906b4b5bc875ed04435be54f5aad1a188a00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000600000003124d696e657220666f7220646174612067656e080500000000000000080000000000000000", + "622e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa": "010000000675385e0000000062dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c69c31b4a90751aa313b77c18784c64eb23ddc8e15463285873ea9df84f36ad5bd4b5b87eddf93c41b4098d936e3d237a9bae3d74a37161b02e2d8add70b2ec600000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff150000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001600000003124d696e657220666f7220646174612067656e081500000000000000080000000000000000", + "6232b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b": "010000006e85385e0000000058c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c65b77f0a82a02d8d54b6ec3e0b531681a3f4928de15d8818a8acda026c524257698df8ca2f1c793f77a0b4b53d2d2518cf2fef78bb76b1e3704ec8826fd81e800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001d00000003124d696e657220666f7220646174612067656e081c00000000000000080000000000000000", + "6235947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d": "000000007e93385e00000000507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e0179700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cecf592e30040c20d6efface2d5ff9323fa5cd8517471310ebbd4d2a40a7de5d75c1edfeedb93d1a88ee1bac8af79c4dfe6b89b2894afe202c2f243870e71ef00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff220000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002300000003124d696e657220666f7220646174612067656e082200000000000000080000000000000000", + "62381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd63": "01000000b679385e0000000023403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015dba10ca4e6573021176899859a904458c33fe013243535cd54991f4cff6c16b03a7a4872432985c6bb44420ca04fa251c5cdd3b32524d3b5c50f461c75f6500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff170000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001800000003124d696e657220666f7220646174612067656e081700000000000000080000000000000000", + "623e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c": "00000000667e385e000000005a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6d037463fcb5dcd1f39f9477d3e9a8bb1297bf2ebc0ba8e3a9c31fc89f6adc9d88940d30b8754686427f6425da074bbefafadfeacb8c1144acc2d4e2a89aefc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff190000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001a00000003124d696e657220666f7220646174612067656e081900000000000000080000000000000000", + "6241a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801": "010000003e5b385e00000000796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f666f45583cba91c8647a6f503f904e111bd60fa68c61461132ee2091f63582f7dce7d52e000f8bce8458275be81055f5e71b725863a60330a11185f5dd31f6b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000b00000003124d696e657220666f7220646174612067656e080a00000000000000080000000000000000", + "6244c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40": "00000000768c385e0000000050215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e01797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047533e120e5cbc1583a855125b7375090f97727bb378c41764939538223793daf5f8988619721c3c34adf81114ac7f1e6c976f689ff3c773c428c91bf720169f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f00000001d8a23577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002000000003124d696e657220666f7220646174612067656e081f000000000000000800000000000000000000000002190359c5e09ddc532432118e50528e8a09f4fc74a753874294d24319e399d95c00000000ffffffffc85aac696ab665bbddf16f7d746726b6daecac257f07bdff750514601c1863a900000000ffffffff0310270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65041500000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc0410270000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57504032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65041500000020c632c00673f5b777cb5275ed44da6747c0ba74bd8bf1e3170a041700b6638567483f0000000000000014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec700000000000002417b1d6a3ce8efc4190824af5dcbd3f4a3cb3dc6891c4e8a60383b5dae689803d8187791b052eb15db82616d6883ae4b58f3985acce24df72839ef1c6b50153d620121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd5302416c58f31f513f232b000a3840e0f812d6868f5d9bde8b1735f6504db040d388506d462b665d318a9d62f1fdbd9bce2d5196089e538c8713a972e3f085d79ce6ae0121031dfb31228b957bfa7e5bb71c8bd51eb5c17802b2ddd915767fdcb36c5a4fa83d", + "624ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972": "03000000965d385e0000000041a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009952c7481f1ebd82b54ffa2e29f268f5a1f646f0756ab5a9422deec9983a00a893c8556d944a646b8640201df462dc4acbdb3bea36cbe2f48933d0bc5fa680cb00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000c00000003124d696e657220666f7220646174612067656e080b00000000000000080000000000000000", + "6250215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256": "000000001e8a385e000000007992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e01797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019a0bdab3dff23fd435494b4944a0daeddd082e6ff7f05b4eccae067ac7fd1e45284a1dc05f01642f98bcb183abb13f1859d691307a69092bdc588ad0bd6df5e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001f00000003124d696e657220666f7220646174612067656e081e00000000000000080000000000000000", + "62507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2": "000000002691385e000000000ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4ef0d083671d3f7ba76de62f92e9a958c8fff5820726915369e95f6f8ddba8bee7496b1fb12fdc3dd4dd506f859f193e534d17cda07fa73b1668b1ed9f0b6fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff210000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002200000003124d696e657220666f7220646174612067656e082100000000000000080000000000000000", + "6252de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "000000008e56385e000000000d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e63700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000390e4b36e542e82c0e923ca4d1932f3f5347a16efb8902c61a4e60673cb3d8205b949d1715598766a84a6832a000ecc8c8d0af5d21e17d34334fb72d72a6f5fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff080000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000900000003124d696e657220666f7220646174612067656e080800000000000000080000000000000000", + "62576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde6": "04000000869a385e0000000073e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005557ec8eba2a8e148d090ee12ece24d76143ba3e7f74ed99e780db64b8bc3ba98bef5892e3207f2dd220d7bea608b574821caf899cbfe27ab46215987280b9e700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff250000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002600000003124d696e657220666f7220646174612067656e082500000000000000080000000000000000", + "6258c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7": "010000001683385e00000000792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000932ee74fa91d151c09263a2689da2248ee78297979f4986287787075bf22c419c607707b6dc2c599174227943c14f311f6861cea884ec78699f4558235e1c80f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001c00000003124d696e657220666f7220646174612067656e081b00000000000000080000000000000000", + "625a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91": "000000000e7c385e00000000381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd6300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a749890b54e0a02f69f70896c0028c0986d84faf11bbb313412130c0478a9efa78d1802b3dba2622f345986c97f8a5abb47e1b888763c1d95d83441ab924593900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff180000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001900000003124d696e657220666f7220646174612067656e081800000000000000080000000000000000", + "625a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "00000000ce43385e00000000ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083e6df7f577fa1444dca368383c7ac8b7c4cdaef03acfcaaa832c6a3a9b5b8e3b359fe257aaba2e2d74dfb3dcfdde5c270463fd1a56bf960955627e68a499a5100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff000000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000100000003124d696e657220666f7220646174612067656e080000000000000000080000000000000000", + "625a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "00000000d64a385e000000006a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd4900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043c0463823b12f4d83446c43b376c1342ededcc1da92d52246030f769a7e46ffb4d5d8c03171ba22ec589da0044aaa801b09bbedfa53b5e62b96a4fd90d993e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff030000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000400000003124d696e657220666f7220646174612067656e080300000000000000080000000000000000", + "625fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575": "020000004e69385e00000000195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009694eb45717668a57ec854436adb1900356b744741c79b65fad7645cdb377b471d83d7454586a56ab022cc65d158727c381fb135c869aa2e4cba66fc5f57afcc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff100000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001100000003124d696e657220666f7220646174612067656e081000000000000000080000000000000000", + "6260f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1": "020000003ea6385e000000002c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a9802823036c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fbad0df4d7a368e397126693ba2b9e57c8a1fdf4e2d92d0414c80789c199249e924154315a8fd52682500ae3aa1ed67737fef60a3e0671ae5f0f63e36cfa61e000000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2a00000001fa9e3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002b00000003124d696e657220666f7220646174612067656e082a0000000000000008000000000000000000000000020f2ff53543d3d9bbb9098544b3acf6f5c17b63e06047725d5ec45d7cdc44dfe100000000fffffffff213a9f7e04dda5bf839d3cf2d6ae6bf45860e3c435d47b1d75a5dc2be96126700000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50689357700000000001460976bb97754c7ab48bac4c894d27a4126bc23d10000000000000241353a23ba46e229685aafb85f6a76c8e93633d08637973a0714801c681667e0ab7e9bc1572c0ee7e841f15a0f69e0d96bb36fc0b0090057d84fa63c34a35d169e0121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd530241a9c56e8283769a96834d8cbef135abfeb2fc96f3dd3173bb466522bef55af2d2160f60a483855b527f3ce45f33b1c47b973219a5d929601b06d1729d3d655f40012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "6261c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "020000002646385e000000005a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5b3f00155bfcbcf9a5474b24b2502cfc04fb044618e080d0de01f169287019228f94d91b51f4ae612804fecadfb0f036b60bc52a92d83168153a5622ceeb38e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff010000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000200000003124d696e657220666f7220646174612067656e080100000000000000080000000000000000", + "6262dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8": "00000000ae72385e0000000063b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de43806f8990af0f90386c2e0fbae4dfd5d9a2e36c7b8d8aacd61e345e39d80dbffde28f2fd4c034ffd6dfa47c26f8cea86fceeb6500c5a0fd8582552f5f65bd00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1400000001629b3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001500000003124d696e657220666f7220646174612067656e0814000000000000000800000000000000000000000001f09e1b55be758049061eb1c5667d55f83ea2a5f85770521169545c0a8e70234d00000000ffffffff02000000000000000000142f6d31b494bdfa42034719ca28894496cdb8935902032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504000000000a6e7970767a766c7868619e8c35770000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000241ba9d3c7c0671b34249000b8f0d1d14e0bc84ff2e8a86c010c7c03beadcbf623b01b1fae5f06884bc8d675707901f748c77fa20e933773b3b74e3e41ab4c4c512012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "6263a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f": "020000004662385e0000000019621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f3b952fe99137d91bad3e8eff8c4e8ffdc1402988a71fc415dec1695561378ca7e17046f1ed6fc462c6d2363bb8ff188d11bb7f7f43dcc06443471dae2e956c400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000e00000003124d696e657220666f7220646174612067656e080d00000000000000080000000000000000", + "6263b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac": "000000005670385e00000000680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d33f379231913136bf301b5ed38cf5f2d3dae390bcc6c58e7ff3305f48eea8d7e8f9eb6e41344d13c30c63e3018266d5685e39b4adcdd805d356c70904df6cef00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff130000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001400000003124d696e657220666f7220646174612067656e081300000000000000080000000000000000", + "62680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a": "00000000fe6d385e0000000025ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000095e6537bbc29a59e564f19c258ec19a7c821b6ff001ec4ad47604861f150b89763f90b1e4f1fe82e89119cf8e2966cacb2562a1dc236168463b577b55d4d82ae00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff120000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001300000003124d696e657220666f7220646174612067656e081200000000000000080000000000000000", + "626a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "000000007e48385e0000000061c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac280bf1231e5aeaee10ce20f93393769452cf00abc404ac83ebcda710cc3dcb171727d58f15b3e6666de2efbe6c5a57de0ed3f64ac6a1cbd5d9800a2d2e974300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000300000003124d696e657220666f7220646174612067656e080200000000000000080000000000000000", + "6273e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf": "000000002e98385e0000000078f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea26c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b4a56e342513350812ec1166791f76c96e3b2391060474db6f11b892d62c5aefa8607ed5d859a8a6864b1dc4c3e453049589a42eb8f1dfdb6af537481ae6e1e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff240000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002500000003124d696e657220666f7220646174612067656e082400000000000000080000000000000000", + "627407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f": "04000000de9c385e00000000576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde66c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d992736510e384198ceea78cd9b574a8c447fd90a01c275b3c1e3c52155be651c03e4040df295c4883f4d599b9849389c6f3d270c8c9f408867f0ff03131ba900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff260000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002700000003124d696e657220666f7220646174612067656e082600000000000000080000000000000000", + "6278f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea2": "00000000d695385e0000000035947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063073cd9466141c84aeea40a3f9ec4d88279b675d7aa5cf0371808fbbe8e79fabf3cd9760a08ae1bb5072d65b1bec43eea01fd0898832bc6a5494f4cd3193fcb00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff230000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002400000003124d696e657220666f7220646174612067656e082300000000000000080000000000000000", + "62792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d": "00000000be80385e000000003e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e0179700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e02e9612d9dd3ef50fc22e905109cfc76dd0d845e7ad48ef3c654fdcee8f7694ce8c02e6ee76cf15c2c61d3d1a331d9d3d053488c7b78ec8d3069b40df0444e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000300000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1a0000000158a53577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001b00000003124d696e657220666f7220646174612067656e081a000000000000000800000000000000000000000001400819f704335fca6ce65693f9666beb0b0798062c47a5a14d9701e5e850d29500000000ffffffff02204e0000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df343d3577000000000014222740511246fe2b10957049ae10e0bbe7fa996c00000000000002417f01a99d973309f23fd407c2d58cedb0cf6256ade320c127db034b45c7d07eb70f7250aa73421a97b2d3f30b3963adea382dffecd7305c7906188abd68f8b353012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f200000000019b487bc7de42e644488cc6baa13a3b2c48a5f446596028010f26f840d62e8f9d00000000ffffffff02204e0000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe343d35770000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc0000000000000241b5206787bd07379af91e4d45a9cf4f19b8b1d930fefeaaa64e39ab428c11274a610c427e06312b0ae288107de92a3e31c4b0b6cb7bf744ad38065051fe58e162012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "62796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "00000000e658385e0000000052de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0c13adfe3ae131ba76a105a819e3a11bfd4aefc66c537aa54567f766ee3bb0c57d1d97ddbd3641748c818adf8967b100a5a3b480a029ccbb077ed50e7455ae100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff090000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000a00000003124d696e657220666f7220646174612067656e080900000000000000080000000000000000", + "627992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec": "00000000c687385e0000000032b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa8d227f12e1ffc326c92e376be83ae054b5e47fce08d82f37cf871cab641229254915b59b8273cd840b1687a1aa807bba79c415632f404685a239b2b0bb166500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001e00000003124d696e657220666f7220646174612067656e081d00000000000000080000000000000000", + "62ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "000000007841385e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a2c60b9439206938f8d7823782abdb8b211a57431e9c9b6a6365d8d428933518e4c9756fef2ad10375f360e0560fcc7587eb5223ddf8cd7c7e06e60a1140b1500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff01d04c5777000000000014f0237ae2e8f860f7d79124fc513f012e5aaa8d23000000000000042050b8937fc5def08f9f3cbda7e5f08c706edb80aba5880c000000000000000000202d5de58609d4970fb548f85ad07a87db40e054e34cc81c951ca995a58f674db72010d748eda1b9c67b94d3244e0211677618a9b4b329e896ad90431f9f48034bad20e2c0299a1e466773516655f09a64b1e16b2579530de6c4a59ce5654dea45180f", + "68023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "05000000", + "6807244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d": "2c000000", + "680ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd": "29000000", + "680d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "08000000", + "680ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee": "21000000", + "68195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e": "10000000", + "6819621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69": "0d000000", + "6820cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31": "0f000000", + "68218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "07000000", + "6823403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e": "17000000", + "6825ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f": "12000000", + "682a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe48672": "28000000", + "682c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a980282303": "2a000000", + "682ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "06000000", + "682e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa": "16000000", + "6832b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b": "1d000000", + "6835947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d": "23000000", + "68381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd63": "18000000", + "683e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c": "1a000000", + "6841a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801": "0b000000", + "6844c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40": "20000000", + "684ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972": "0c000000", + "6850215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256": "1f000000", + "68507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2": "22000000", + "6852de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "09000000", + "68576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde6": "26000000", + "6858c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7": "1c000000", + "685a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91": "19000000", + "685a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "01000000", + "685a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "04000000", + "685fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575": "11000000", + "6860f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1": "2b000000", + "6861c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "02000000", + "6862dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8": "15000000", + "6863a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f": "0e000000", + "6863b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac": "14000000", + "68680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a": "13000000", + "686a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "03000000", + "6873e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf": "25000000", + "687407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f": "27000000", + "6878f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea2": "24000000", + "68792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d": "1b000000", + "68796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "0a000000", + "687992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec": "1e000000", + "68ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "00000000", + "7507244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d": "020000000056000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5000b000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "7544c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40": "020000000036000000fd204e0014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df0036000000fd204e0014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe", + "7560f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1": "020000000040000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65041500000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc040009000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "7562dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8": "010000000003000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "75792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d": "020000000005000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000007000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000" + }, + "after": { + "52": "07244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d33000000000000003100000000000000c0958cf4140000001027000000000000", + "4800000000": "ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5", + "4800000001": "5a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3", + "4800000002": "61c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed", + "4800000003": "6a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49", + "4800000004": "5a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135", + "4800000005": "023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86", + "4800000006": "2ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81", + "4800000007": "218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80", + "4800000008": "0d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637", + "4800000009": "52de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c", + "480000000a": "796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e", + "480000000b": "41a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801", + "480000000c": "4ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972", + "480000000d": "19621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69", + "480000000e": "63a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f", + "480000000f": "20cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31", + "4800000010": "195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e", + "4800000011": "5fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575", + "4800000012": "25ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f", + "4800000013": "680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a", + "4800000014": "63b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac", + "4800000015": "62dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8", + "4800000016": "2e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa", + "4800000017": "23403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e", + "4800000018": "381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd63", + "4800000019": "5a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91", + "480000001a": "3e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c", + "480000001b": "792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d", + "480000001c": "58c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7", + "480000001d": "32b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b", + "480000001e": "7992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec", + "480000001f": "50215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256", + "4800000020": "44c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40", + "4800000021": "0ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee", + "4800000022": "507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2", + "4800000023": "35947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d", + "4800000024": "78f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea2", + "4800000025": "73e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf", + "4800000026": "576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde6", + "4800000027": "7407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f", + "4800000028": "2a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe48672", + "4800000029": "0ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd", + "480000002a": "2c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a980282303", + "480000002b": "60f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1", + "480000002c": "07244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d", + "62023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "000000002e4d385e000000005a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d7821350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e1b13c02e1a1a62973bd11419b8ae101cc1d5673ac4367d2d5887ced610a7ffbbcdd88e0f14469f736ff4dbb17f3ae4632964d093efa6f9362d48581cc4f5300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000500000003124d696e657220666f7220646174612067656e080400000000000000080000000000000000", + "6207244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d": "0300000096a8385e0000000060f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da16c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060e9c7bb6f48fa01972ccb737b8a3b7ad6a86a693c3a63e9c9b34a2331f1c7d0bc594e1078e3e121e0379a60f09acb2a102d52d0070372a4621743f724770e9600000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2b00000001ba9d3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002c00000003124d696e657220666f7220646174612067656e082b00000000000000080000000000000000000000000217a1d4c313e25990c1d5955fd8f1f2dbaceb9b89f11daacad4066ce631c3026a00000000ffffffff528c8499ebe12e2ef754d1752dc8e8b6fea7efecc2ddb43a77e04301f7f4dbd400000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5007032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000401020304468a35770000000000147a98b6621e4ccc528560645f6a7682633a24fad80000000000000241775c6ee7167f573270b29450a141cfdca3fec0dbb880621360b2cc51b82cb83c6068cfe6d030267cff7099ebc785b97efff854ef11c42671f476e5238893fe320121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd530241279501bc9ab9b5732ac2211228e7943abaed43f0a5a4f383c8a8d2a69f77d4d774076bd85c1711fa55187ff248637a6bda1ff9544d1f3a291e815c3aa9cd5870012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "620ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd": "000000008ea1385e000000002a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe486726c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2a81c8b6fa6c23724a6f75c264b33274ce0bf81b6c7c96e941f6ecf0214466030f18d522ec485998e9c7e6d8dd56450c50e9e8718256aef1c2ebf7b1ddb4a0100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff280000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002900000003124d696e657220666f7220646174612067656e082800000000000000080000000000000000", + "620d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "010000003654385e00000000218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561ba8e15b6446ae574bd94831f78c34e3d36f891b499ea318c92d6c158cbc7062c56c8f928267cc7889f18ccd50a413daaba98fba47a74da65e46746a1b914400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff070000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000800000003124d696e657220666f7220646174612067656e080700000000000000080000000000000000", + "620ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee": "00000000ce8e385e0000000044c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e0179700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008fb43c4c4bd13d9e5cc08ca76c3407a94111097d4855759fb45abc0633c5d668f0e5935ac5f4058b276772434da60262d4b96fec397563d622819e4a02b40e0c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff200000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002100000003124d696e657220666f7220646174612067656e082000000000000000080000000000000000", + "62195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e": "00000000f666385e0000000020cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee6a7f23a0f57830311a30c3c8445b673587e120ea99558ea8c32faf66236d4fdec7715d616580a8008487f6df0133e0a101981f6b22fa007e45025a04c9628900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001000000003124d696e657220666f7220646174612067656e080f00000000000000080000000000000000", + "6219621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69": "03000000ee5f385e000000004ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a8acc94a096d21eb0dc87bdfb0cfcd4191d63c157af264c9aa5a3bc61154762f71621f91895fd7ce1c76bcd6ca7942803e1df39636b8f5df0541b601538903700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000d00000003124d696e657220666f7220646174612067656e080c00000000000000080000000000000000", + "6220cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31": "000000009e64385e0000000063a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000faf9826f22ab9e5fd8850a72e6d526c99774645b2da88d668acfc516ccf6a7bd1cfa41793cd6f68255eda0b063e35c68980a389ffd731d877bedc14b3d9a2e200000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000f00000003124d696e657220666f7220646174612067656e080e00000000000000080000000000000000", + "62218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "01000000de51385e000000002ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb8100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0fa2412035ea50aad5dfa552200a584f3c8b5f5ed0395a3ba9f61ffcdfaf03c10d7a31250c9abd77aa5f7ea994b191de4980200322f7017a60851612adc5d8d00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000700000003124d696e657220666f7220646174612067656e080600000000000000080000000000000000", + "6223403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e": "020000005e77385e000000002e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6a4069d1f79ea506489fa3ce6f2ea392a88ce03f5eca8da136d37f8173adbd5266d25967ca47f116f14b53c6a19befaec491fad22a84b37d38532b71b2f033e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff160000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001700000003124d696e657220666f7220646174612067656e081600000000000000080000000000000000", + "6225ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f": "00000000a66b385e000000005fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be185750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099440cecc52f2703578666b0adcfab3ca236ebb4c29e8d53648bf7fabf897fbbd8c894203ead6810c16ef6136d1cafc12a51e369dbd515727c16f6f6d745e74f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff110000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001200000003124d696e657220666f7220646174612067656e081100000000000000080000000000000000", + "622a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe48672": "00000000369f385e000000007407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a427d9fd0a8fc3c3425254b9fecc84d46e816b84490e5e5365ff71c648907da92c3e15b47fd4a34691dad929e94a579cc1708d5d6f0c4f25467fc9624dc15cd100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002800000003124d696e657220666f7220646174612067656e082700000000000000080000000000000000", + "622c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a980282303": "00000000e6a3385e000000000ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000253216431886015813a2c3265167c0b470bc033872d45199fe1b929fc226889e030f1ef09799fd5be8064fb4d730aab74a6b9aad1096d416e71b041917c1604500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff290000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002a00000003124d696e657220666f7220646174612067656e082900000000000000080000000000000000", + "622ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "00000000864f385e00000000023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009166e55997f82e200b1075b7a1cc9a12e40827d7ccac1b643c2dd690e09761b4a9cd7eeb8b6a7d937080f36c2a5804906b4b5bc875ed04435be54f5aad1a188a00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000600000003124d696e657220666f7220646174612067656e080500000000000000080000000000000000", + "622e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa": "010000000675385e0000000062dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c69c31b4a90751aa313b77c18784c64eb23ddc8e15463285873ea9df84f36ad5bd4b5b87eddf93c41b4098d936e3d237a9bae3d74a37161b02e2d8add70b2ec600000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff150000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001600000003124d696e657220666f7220646174612067656e081500000000000000080000000000000000", + "6232b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b": "010000006e85385e0000000058c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c65b77f0a82a02d8d54b6ec3e0b531681a3f4928de15d8818a8acda026c524257698df8ca2f1c793f77a0b4b53d2d2518cf2fef78bb76b1e3704ec8826fd81e800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001d00000003124d696e657220666f7220646174612067656e081c00000000000000080000000000000000", + "6235947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d": "000000007e93385e00000000507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e0179700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cecf592e30040c20d6efface2d5ff9323fa5cd8517471310ebbd4d2a40a7de5d75c1edfeedb93d1a88ee1bac8af79c4dfe6b89b2894afe202c2f243870e71ef00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff220000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002300000003124d696e657220666f7220646174612067656e082200000000000000080000000000000000", + "62381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd63": "01000000b679385e0000000023403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015dba10ca4e6573021176899859a904458c33fe013243535cd54991f4cff6c16b03a7a4872432985c6bb44420ca04fa251c5cdd3b32524d3b5c50f461c75f6500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff170000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001800000003124d696e657220666f7220646174612067656e081700000000000000080000000000000000", + "623e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c": "00000000667e385e000000005a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6d037463fcb5dcd1f39f9477d3e9a8bb1297bf2ebc0ba8e3a9c31fc89f6adc9d88940d30b8754686427f6425da074bbefafadfeacb8c1144acc2d4e2a89aefc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff190000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001a00000003124d696e657220666f7220646174612067656e081900000000000000080000000000000000", + "6241a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801": "010000003e5b385e00000000796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f666f45583cba91c8647a6f503f904e111bd60fa68c61461132ee2091f63582f7dce7d52e000f8bce8458275be81055f5e71b725863a60330a11185f5dd31f6b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000b00000003124d696e657220666f7220646174612067656e080a00000000000000080000000000000000", + "6244c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40": "00000000768c385e0000000050215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e01797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047533e120e5cbc1583a855125b7375090f97727bb378c41764939538223793daf5f8988619721c3c34adf81114ac7f1e6c976f689ff3c773c428c91bf720169f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f00000001d8a23577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002000000003124d696e657220666f7220646174612067656e081f000000000000000800000000000000000000000002190359c5e09ddc532432118e50528e8a09f4fc74a753874294d24319e399d95c00000000ffffffffc85aac696ab665bbddf16f7d746726b6daecac257f07bdff750514601c1863a900000000ffffffff0310270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65041500000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc0410270000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57504032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65041500000020c632c00673f5b777cb5275ed44da6747c0ba74bd8bf1e3170a041700b6638567483f0000000000000014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec700000000000002417b1d6a3ce8efc4190824af5dcbd3f4a3cb3dc6891c4e8a60383b5dae689803d8187791b052eb15db82616d6883ae4b58f3985acce24df72839ef1c6b50153d620121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd5302416c58f31f513f232b000a3840e0f812d6868f5d9bde8b1735f6504db040d388506d462b665d318a9d62f1fdbd9bce2d5196089e538c8713a972e3f085d79ce6ae0121031dfb31228b957bfa7e5bb71c8bd51eb5c17802b2ddd915767fdcb36c5a4fa83d", + "624ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972": "03000000965d385e0000000041a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009952c7481f1ebd82b54ffa2e29f268f5a1f646f0756ab5a9422deec9983a00a893c8556d944a646b8640201df462dc4acbdb3bea36cbe2f48933d0bc5fa680cb00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000c00000003124d696e657220666f7220646174612067656e080b00000000000000080000000000000000", + "6250215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256": "000000001e8a385e000000007992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e01797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019a0bdab3dff23fd435494b4944a0daeddd082e6ff7f05b4eccae067ac7fd1e45284a1dc05f01642f98bcb183abb13f1859d691307a69092bdc588ad0bd6df5e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001f00000003124d696e657220666f7220646174612067656e081e00000000000000080000000000000000", + "62507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2": "000000002691385e000000000ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4ef0d083671d3f7ba76de62f92e9a958c8fff5820726915369e95f6f8ddba8bee7496b1fb12fdc3dd4dd506f859f193e534d17cda07fa73b1668b1ed9f0b6fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff210000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002200000003124d696e657220666f7220646174612067656e082100000000000000080000000000000000", + "6252de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "000000008e56385e000000000d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e63700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000390e4b36e542e82c0e923ca4d1932f3f5347a16efb8902c61a4e60673cb3d8205b949d1715598766a84a6832a000ecc8c8d0af5d21e17d34334fb72d72a6f5fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff080000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000900000003124d696e657220666f7220646174612067656e080800000000000000080000000000000000", + "62576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde6": "04000000869a385e0000000073e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005557ec8eba2a8e148d090ee12ece24d76143ba3e7f74ed99e780db64b8bc3ba98bef5892e3207f2dd220d7bea608b574821caf899cbfe27ab46215987280b9e700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff250000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002600000003124d696e657220666f7220646174612067656e082500000000000000080000000000000000", + "6258c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7": "010000001683385e00000000792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000932ee74fa91d151c09263a2689da2248ee78297979f4986287787075bf22c419c607707b6dc2c599174227943c14f311f6861cea884ec78699f4558235e1c80f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001c00000003124d696e657220666f7220646174612067656e081b00000000000000080000000000000000", + "625a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91": "000000000e7c385e00000000381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd6300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a749890b54e0a02f69f70896c0028c0986d84faf11bbb313412130c0478a9efa78d1802b3dba2622f345986c97f8a5abb47e1b888763c1d95d83441ab924593900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff180000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001900000003124d696e657220666f7220646174612067656e081800000000000000080000000000000000", + "625a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "00000000ce43385e00000000ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083e6df7f577fa1444dca368383c7ac8b7c4cdaef03acfcaaa832c6a3a9b5b8e3b359fe257aaba2e2d74dfb3dcfdde5c270463fd1a56bf960955627e68a499a5100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff000000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000100000003124d696e657220666f7220646174612067656e080000000000000000080000000000000000", + "625a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "00000000d64a385e000000006a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd4900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043c0463823b12f4d83446c43b376c1342ededcc1da92d52246030f769a7e46ffb4d5d8c03171ba22ec589da0044aaa801b09bbedfa53b5e62b96a4fd90d993e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff030000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000400000003124d696e657220666f7220646174612067656e080300000000000000080000000000000000", + "625fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575": "020000004e69385e00000000195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009694eb45717668a57ec854436adb1900356b744741c79b65fad7645cdb377b471d83d7454586a56ab022cc65d158727c381fb135c869aa2e4cba66fc5f57afcc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff100000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001100000003124d696e657220666f7220646174612067656e081000000000000000080000000000000000", + "6260f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1": "020000003ea6385e000000002c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a9802823036c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fbad0df4d7a368e397126693ba2b9e57c8a1fdf4e2d92d0414c80789c199249e924154315a8fd52682500ae3aa1ed67737fef60a3e0671ae5f0f63e36cfa61e000000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2a00000001fa9e3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002b00000003124d696e657220666f7220646174612067656e082a0000000000000008000000000000000000000000020f2ff53543d3d9bbb9098544b3acf6f5c17b63e06047725d5ec45d7cdc44dfe100000000fffffffff213a9f7e04dda5bf839d3cf2d6ae6bf45860e3c435d47b1d75a5dc2be96126700000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50689357700000000001460976bb97754c7ab48bac4c894d27a4126bc23d10000000000000241353a23ba46e229685aafb85f6a76c8e93633d08637973a0714801c681667e0ab7e9bc1572c0ee7e841f15a0f69e0d96bb36fc0b0090057d84fa63c34a35d169e0121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd530241a9c56e8283769a96834d8cbef135abfeb2fc96f3dd3173bb466522bef55af2d2160f60a483855b527f3ce45f33b1c47b973219a5d929601b06d1729d3d655f40012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "6261c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "020000002646385e000000005a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5b3f00155bfcbcf9a5474b24b2502cfc04fb044618e080d0de01f169287019228f94d91b51f4ae612804fecadfb0f036b60bc52a92d83168153a5622ceeb38e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff010000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000200000003124d696e657220666f7220646174612067656e080100000000000000080000000000000000", + "6262dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8": "00000000ae72385e0000000063b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de43806f8990af0f90386c2e0fbae4dfd5d9a2e36c7b8d8aacd61e345e39d80dbffde28f2fd4c034ffd6dfa47c26f8cea86fceeb6500c5a0fd8582552f5f65bd00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1400000001629b3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001500000003124d696e657220666f7220646174612067656e0814000000000000000800000000000000000000000001f09e1b55be758049061eb1c5667d55f83ea2a5f85770521169545c0a8e70234d00000000ffffffff02000000000000000000142f6d31b494bdfa42034719ca28894496cdb8935902032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504000000000a6e7970767a766c7868619e8c35770000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000241ba9d3c7c0671b34249000b8f0d1d14e0bc84ff2e8a86c010c7c03beadcbf623b01b1fae5f06884bc8d675707901f748c77fa20e933773b3b74e3e41ab4c4c512012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "6263a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f": "020000004662385e0000000019621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f3b952fe99137d91bad3e8eff8c4e8ffdc1402988a71fc415dec1695561378ca7e17046f1ed6fc462c6d2363bb8ff188d11bb7f7f43dcc06443471dae2e956c400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000e00000003124d696e657220666f7220646174612067656e080d00000000000000080000000000000000", + "6263b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac": "000000005670385e00000000680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d33f379231913136bf301b5ed38cf5f2d3dae390bcc6c58e7ff3305f48eea8d7e8f9eb6e41344d13c30c63e3018266d5685e39b4adcdd805d356c70904df6cef00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff130000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001400000003124d696e657220666f7220646174612067656e081300000000000000080000000000000000", + "62680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a": "00000000fe6d385e0000000025ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000095e6537bbc29a59e564f19c258ec19a7c821b6ff001ec4ad47604861f150b89763f90b1e4f1fe82e89119cf8e2966cacb2562a1dc236168463b577b55d4d82ae00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff120000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001300000003124d696e657220666f7220646174612067656e081200000000000000080000000000000000", + "626a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "000000007e48385e0000000061c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac280bf1231e5aeaee10ce20f93393769452cf00abc404ac83ebcda710cc3dcb171727d58f15b3e6666de2efbe6c5a57de0ed3f64ac6a1cbd5d9800a2d2e974300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000300000003124d696e657220666f7220646174612067656e080200000000000000080000000000000000", + "6273e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf": "000000002e98385e0000000078f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea26c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b4a56e342513350812ec1166791f76c96e3b2391060474db6f11b892d62c5aefa8607ed5d859a8a6864b1dc4c3e453049589a42eb8f1dfdb6af537481ae6e1e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff240000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002500000003124d696e657220666f7220646174612067656e082400000000000000080000000000000000", + "627407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f": "04000000de9c385e00000000576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde66c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d992736510e384198ceea78cd9b574a8c447fd90a01c275b3c1e3c52155be651c03e4040df295c4883f4d599b9849389c6f3d270c8c9f408867f0ff03131ba900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff260000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002700000003124d696e657220666f7220646174612067656e082600000000000000080000000000000000", + "6278f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea2": "00000000d695385e0000000035947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d6c5608090af6152838357c3947671b7824f65d91bd17270b61db33e2b33e317d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063073cd9466141c84aeea40a3f9ec4d88279b675d7aa5cf0371808fbbe8e79fabf3cd9760a08ae1bb5072d65b1bec43eea01fd0898832bc6a5494f4cd3193fcb00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff230000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002400000003124d696e657220666f7220646174612067656e082300000000000000080000000000000000", + "62792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d": "00000000be80385e000000003e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e0179700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e02e9612d9dd3ef50fc22e905109cfc76dd0d845e7ad48ef3c654fdcee8f7694ce8c02e6ee76cf15c2c61d3d1a331d9d3d053488c7b78ec8d3069b40df0444e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000300000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1a0000000158a53577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001b00000003124d696e657220666f7220646174612067656e081a000000000000000800000000000000000000000001400819f704335fca6ce65693f9666beb0b0798062c47a5a14d9701e5e850d29500000000ffffffff02204e0000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df343d3577000000000014222740511246fe2b10957049ae10e0bbe7fa996c00000000000002417f01a99d973309f23fd407c2d58cedb0cf6256ade320c127db034b45c7d07eb70f7250aa73421a97b2d3f30b3963adea382dffecd7305c7906188abd68f8b353012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f200000000019b487bc7de42e644488cc6baa13a3b2c48a5f446596028010f26f840d62e8f9d00000000ffffffff02204e0000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe343d35770000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc0000000000000241b5206787bd07379af91e4d45a9cf4f19b8b1d930fefeaaa64e39ab428c11274a610c427e06312b0ae288107de92a3e31c4b0b6cb7bf744ad38065051fe58e162012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "62796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "00000000e658385e0000000052de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0c13adfe3ae131ba76a105a819e3a11bfd4aefc66c537aa54567f766ee3bb0c57d1d97ddbd3641748c818adf8967b100a5a3b480a029ccbb077ed50e7455ae100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff090000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000a00000003124d696e657220666f7220646174612067656e080900000000000000080000000000000000", + "627992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec": "00000000c687385e0000000032b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b306f13a181bbb2df738afb55834dc42ec43691d8130e539f2b8cc64022e017970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa8d227f12e1ffc326c92e376be83ae054b5e47fce08d82f37cf871cab641229254915b59b8273cd840b1687a1aa807bba79c415632f404685a239b2b0bb166500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001e00000003124d696e657220666f7220646174612067656e081d00000000000000080000000000000000", + "62ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "000000007841385e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a2c60b9439206938f8d7823782abdb8b211a57431e9c9b6a6365d8d428933518e4c9756fef2ad10375f360e0560fcc7587eb5223ddf8cd7c7e06e60a1140b1500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff01d04c5777000000000014f0237ae2e8f860f7d79124fc513f012e5aaa8d23000000000000042050b8937fc5def08f9f3cbda7e5f08c706edb80aba5880c000000000000000000202d5de58609d4970fb548f85ad07a87db40e054e34cc81c951ca995a58f674db72010d748eda1b9c67b94d3244e0211677618a9b4b329e896ad90431f9f48034bad20e2c0299a1e466773516655f09a64b1e16b2579530de6c4a59ce5654dea45180f", + "68023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "05000000", + "6807244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d": "2c000000", + "680ce28f0a95fc1a0e378b64272b3d6e1e18880fe84d2890fda7ba14e193dcfdfd": "29000000", + "680d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "08000000", + "680ec0c0f468a2f02c68b8ac6d1921ac203c53424ee57d99db4d70cebcc5f621ee": "21000000", + "68195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e": "10000000", + "6819621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69": "0d000000", + "6820cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31": "0f000000", + "68218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "07000000", + "6823403cdfdc350e93bb31781722718d384a505388dfde0d632a44c8f24b5e9b0e": "17000000", + "6825ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f": "12000000", + "682a753bab9a6af0d32f72ed128bf10b55d074a685c0362076925deb953fe48672": "28000000", + "682c1ea451d0c7810e6c2c7e165af15d4620de547be5c275ef512204a980282303": "2a000000", + "682ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "06000000", + "682e3f351c0ed5a14c93a6466777acd22bead318efb378f76fcdc20bf0790619aa": "16000000", + "6832b6d118c119dcf6687bf6c4f2c851ca7d3c9492d2dba482961ce9a798b3ec9b": "1d000000", + "6835947119c75f6b8e7253ddf2d138f991f43ae731689e1f2b2ce5db7f6856bc3d": "23000000", + "68381d59bea933cc0f7cb41c0643f1ccaba64bf73ed8a06c16d617049bc442fd63": "18000000", + "683e228e21aea186ae1fa1eac4299740aad80e5928ecf0148f7641476c1c8f0b4c": "1a000000", + "6841a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801": "0b000000", + "6844c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40": "20000000", + "684ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972": "0c000000", + "6850215fcddf2d7553b4bf0143a4bb753828359c51d0ed4fab90c03842f5139256": "1f000000", + "68507935f58a062f1ce115ee1054d547915cfb2acd13eb199315b45150c7a5cdf2": "22000000", + "6852de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "09000000", + "68576db8b45d9c70fc2da459aa450c53012a9cd952de8c84f27b1eb580e1dbfde6": "26000000", + "6858c8e924b693e1c6441d5966d86fd714decfd37e71282704c90c0ffccae8f9c7": "1c000000", + "685a3a623c77e2bf754b57aabf7c339cbddfe25ed4ebe66e46a853dc2c6eca4f91": "19000000", + "685a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "01000000", + "685a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "04000000", + "685fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575": "11000000", + "6860f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1": "2b000000", + "6861c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "02000000", + "6862dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8": "15000000", + "6863a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f": "0e000000", + "6863b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac": "14000000", + "68680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a": "13000000", + "686a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "03000000", + "6873e484a663e04d2a9f2093f4aa8f49aea7ba8514aea387b3cc21ef7d0bc53fdf": "25000000", + "687407ff629b7a562f30064117cae7c09e92fa1f03009c88a63d226a786745e86f": "27000000", + "6878f86f67f7c9de6202854e9d460a13dc576a82fc1b29b0914c55c109567aeea2": "24000000", + "68792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d": "1b000000", + "68796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "0a000000", + "687992cb896698a6fab3e6b40cb8d08ab64e105c753191881fe818125ea2a602ec": "1e000000", + "68ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "00000000", + "7507244c4d9ec62f4c28871cf35b2242a80df713532c24ad6fc6a054848da47f7d": "020000000056000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5000b000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "7544c77257e1d92034905d3d2f306473d92d84ce195bf8ca9f8ef5d48e2049ea40": "020000000036000000fd204e0014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df0036000000fd204e0014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504150000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe", + "7560f0fda098148e47bdf2c6d0fe0de24f86248ae8ff4a61fb0522070c4cf76da1": "020000000040000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65041500000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc040009000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "7562dd991f17e895b6ad184a2ef22884af3597f7cf20a86359fb045d6493ddc7d8": "010000000003000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "75792227630829648a22c948c29f21e9c43950058964354af87a966257451c657d": "020000000005000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000007000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000" + } +} diff --git a/test/data/migrations/chain-2-blockstore-gen.js b/test/data/migrations/chain-2-blockstore-gen.js new file mode 100644 index 000000000..4571cc596 --- /dev/null +++ b/test/data/migrations/chain-2-blockstore-gen.js @@ -0,0 +1,183 @@ +'use strict'; + +/** + * Migration from v2.x to v3.0.0 + */ + +const Logger = require('blgr'); +const Network = require('../../../lib/protocol/network'); +const Mempool = require('../../../lib/mempool/mempool'); +const Miner = require('../../../lib/mining/miner'); +const Chain = require('../../../lib/blockchain/chain'); +const MemWallet = require('../../util/memwallet'); +const HD = require('../../../lib/hd'); +// const rules = require('../../../lib/covenants/rules'); +const mutils = require('../../util/migrations'); + +const NETWORK = Network.get('regtest'); +let blockstore = null; + +try { + blockstore = require('../../../lib/blockstore'); +} catch (e) { + ; +} + +const wallet1priv = 'rprvKE8qsHtkmUxUSPQdn2sFKFUcKyUQz9pKQhxjEWecnXg9hgJMsmJXcw' + + 'J77SqmHT1R6mcuNqVPzgT2EoGStsXaUN92VJKhQWUB6uZdL8gAZvez'; + +async function dumpMigration(prune) { + NETWORK.block.pruneAfterHeight = 10; + NETWORK.block.keepBlocks = 40; + + let txID = 0; + + const commonOptions = { + memory: true, + network: NETWORK, + logger: Logger.global + }; + + let blocks = null; + + if (blockstore) { + blocks = blockstore.create(commonOptions); + + await blocks.open(); + } + + const chain = new Chain({ + ...commonOptions, + entryCache: 5000, + blocks, + prune + }); + + const mempool = new Mempool({ + ...commonOptions, + chain + }); + + const miner = new Miner({ + ...commonOptions, + mempool, + chain + }); + + const master = HD.HDPrivateKey.fromBase58(wallet1priv, NETWORK); + const wallet = new MemWallet({ + network: NETWORK, + master + }); + + const address = wallet.getAddress(); + miner.addAddress(address); + + mempool.on('tx', (tx) => { + miner.cpu.notifyEntry(); + wallet.addTX(tx); + }); + + chain.on('connect', async (entry, block, view) => { + try { + await mempool._addBlock(entry, block.txs, view); + wallet.addBlock(entry, block.txs); + } catch (e) { + ; + } + }); + + chain.on('disconnect', async (entry, block) => { + try { + await mempool._removeBlock(entry, block.txs); + } catch (e) { + ; + } + }); + + await chain.open(); + await mempool.open(); + await miner.open(); + + miner.createBlock = async (tip, address) => { + return mutils.createBlock({ + txno: txID++, + chain, + miner, + tip, + address + }); + }; + + const mineBlock = async () => { + const block = await miner.mineBlock(chain.tip, address); + await chain.add(block); + }; + + for (let i = 0; i < 40; i++) + await mineBlock(); + + // full auction from start to finish. + // const name = rules.grindName(10, chain.tip.height + 1, NETWORK); + const name = 'nypvzvlxha'; + const openTX = await wallet.createOpen(name); + await mempool.addTX(openTX.toTX()); + + for (let i = 0; i < NETWORK.names.treeInterval + 1; i++) + await mineBlock(); + + const bidTX1 = await wallet.createBid(name, 10000, 20000); + await mempool.addTX(bidTX1.toTX()); + const bidTX2 = await wallet.createBid(name, 10000, 20000); + await mempool.addTX(bidTX2.toTX()); + + for (let i = 0; i < NETWORK.names.biddingPeriod; i++) + await mineBlock(); + + const reveal = await wallet.createReveal(name); + await mempool.addTX(reveal.toTX()); + + for (let i = 0; i < NETWORK.names.revealPeriod + 1; i++) + await mineBlock(); + + const register = await wallet.createRegister(name, Buffer.from([1,2,3])); + await mempool.addTX(register.toTX()); + await mineBlock(); + + const update = await wallet.createUpdate(name, Buffer.from([1,2,3,4])); + await mempool.addTX(update.toTX()); + await mineBlock(); + + const data = await getMigrationDump(chain); + + await miner.close(); + await mempool.close(); + await chain.close(); + + if (blocks) + await blocks.close(); + + return data; +} + +(async () => { + const full = await dumpMigration(false); + const pruned = await dumpMigration(true); + + console.log(JSON.stringify({ + full, + pruned + }, null, 2)); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); + +async function getMigrationDump(chain) { + const prefixes = [ + 'b', + 'u' + ]; + + return mutils.dumpChainDB(chain.db, prefixes.map(mutils.prefix2hex)); +} diff --git a/test/data/migrations/chain-2-blockstore.json b/test/data/migrations/chain-2-blockstore.json new file mode 100644 index 000000000..473d3230f --- /dev/null +++ b/test/data/migrations/chain-2-blockstore.json @@ -0,0 +1,139 @@ +{ + "description": "Migration from chaindb to blockstore. (v2.x -> 3.0.0)", + "network": { + "block": { + "pruneAfterHeight": 10, + "keepBlocks": 40 + } + }, + "full": { + "6201f4834003e1ae6467e46d99f8b0ea77cf5c0f2307b68a84e2524b4d1f140937": "00000000869a385e0000000069d3d31f6229c9897d3062af52f286ad9ae35bb9074101a46a123076664b211b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005557ec8eba2a8e148d090ee12ece24d76143ba3e7f74ed99e780db64b8bc3ba98bef5892e3207f2dd220d7bea608b574821caf899cbfe27ab46215987280b9e700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff250000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002600000003124d696e657220666f7220646174612067656e082500000000000000080000000000000000", + "62023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "000000002e4d385e000000005a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d7821350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e1b13c02e1a1a62973bd11419b8ae101cc1d5673ac4367d2d5887ced610a7ffbbcdd88e0f14469f736ff4dbb17f3ae4632964d093efa6f9362d48581cc4f5300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000500000003124d696e657220666f7220646174612067656e080400000000000000080000000000000000", + "6203fd277eb7864b864c6d4d1df862e37a2413fa9a9f1e4a8ca0d6d8a08f2e73da": "01000000369f385e000000005e12dc9b89512079e06821556df69f4655e41384aff52f59dc9862100e29ac5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a427d9fd0a8fc3c3425254b9fecc84d46e816b84490e5e5365ff71c648907da92c3e15b47fd4a34691dad929e94a579cc1708d5d6f0c4f25467fc9624dc15cd100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002800000003124d696e657220666f7220646174612067656e082700000000000000080000000000000000", + "6204639906391a0e10f21f7872fc0ad77ca42ef66befadc6e0252cde03da097673": "000000002691385e0000000032fcc0051a115a09787f755dc69fb92adfdfe39be5b8e9c30307cc1564574b0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4ef0d083671d3f7ba76de62f92e9a958c8fff5820726915369e95f6f8ddba8bee7496b1fb12fdc3dd4dd506f859f193e534d17cda07fa73b1668b1ed9f0b6fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff210000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002200000003124d696e657220666f7220646174612067656e082100000000000000080000000000000000", + "620a8ec0f7e346f2fea1ddfab1ed6d236b9641a3ad6dadc8fe6d85b30907830bf8": "01000000b679385e000000000bb2de90ca103cee4595465718dffc4373a46de295e581182bf9ec5bc0daed8900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015dba10ca4e6573021176899859a904458c33fe013243535cd54991f4cff6c16b03a7a4872432985c6bb44420ca04fa251c5cdd3b32524d3b5c50f461c75f6500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff170000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001800000003124d696e657220666f7220646174612067656e081700000000000000080000000000000000", + "620bb2de90ca103cee4595465718dffc4373a46de295e581182bf9ec5bc0daed89": "010000005e77385e0000000019986e8eb410f4f577293ca7946e56b687497e3bc344b814ecffc59def180b2c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6a4069d1f79ea506489fa3ce6f2ea392a88ce03f5eca8da136d37f8173adbd5266d25967ca47f116f14b53c6a19befaec491fad22a84b37d38532b71b2f033e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff160000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001700000003124d696e657220666f7220646174612067656e081600000000000000080000000000000000", + "620d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "010000003654385e00000000218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561ba8e15b6446ae574bd94831f78c34e3d36f891b499ea318c92d6c158cbc7062c56c8f928267cc7889f18ccd50a413daaba98fba47a74da65e46746a1b914400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff070000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000800000003124d696e657220666f7220646174612067656e080700000000000000080000000000000000", + "6213a3e4ac9804e9d23581e39f177b9e14ec60a07230e8fe3bfa3434ac2d80e526": "000000001e8a385e0000000074aeedf1bd09cde9bfdffcf3906c928a9629d28bc54d55ed6f590e001248d2ad0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019a0bdab3dff23fd435494b4944a0daeddd082e6ff7f05b4eccae067ac7fd1e45284a1dc05f01642f98bcb183abb13f1859d691307a69092bdc588ad0bd6df5e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001f00000003124d696e657220666f7220646174612067656e081e00000000000000080000000000000000", + "62151b3be5f32e0324ba6677a85b6f6bf64240b109bc3ea2972902c19218ad0ebf": "00000000e6a3385e00000000735b9e424057caccb7ceb21bd56a45b42809c3e580af13cfe2fe06e113b23f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000253216431886015813a2c3265167c0b470bc033872d45199fe1b929fc226889e030f1ef09799fd5be8064fb4d730aab74a6b9aad1096d416e71b041917c1604500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff290000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002a00000003124d696e657220666f7220646174612067656e082900000000000000080000000000000000", + "62195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e": "00000000f666385e0000000020cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee6a7f23a0f57830311a30c3c8445b673587e120ea99558ea8c32faf66236d4fdec7715d616580a8008487f6df0133e0a101981f6b22fa007e45025a04c9628900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001000000003124d696e657220666f7220646174612067656e080f00000000000000080000000000000000", + "6219621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69": "03000000ee5f385e000000004ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a8acc94a096d21eb0dc87bdfb0cfcd4191d63c157af264c9aa5a3bc61154762f71621f91895fd7ce1c76bcd6ca7942803e1df39636b8f5df0541b601538903700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000d00000003124d696e657220666f7220646174612067656e080c00000000000000080000000000000000", + "6219986e8eb410f4f577293ca7946e56b687497e3bc344b814ecffc59def180b2c": "000000000675385e00000000736125af8ebd0bf374b34f1ba05a184e17c7c3cb912f26272aaa57b36a50b1d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c69c31b4a90751aa313b77c18784c64eb23ddc8e15463285873ea9df84f36ad5bd4b5b87eddf93c41b4098d936e3d237a9bae3d74a37161b02e2d8add70b2ec600000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff150000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001600000003124d696e657220666f7220646174612067656e081500000000000000080000000000000000", + "621d1cd648871f2dd0d128322b0e647b9a12d7c149bc3e8e8be4bbbfb238619955": "010000000e7c385e000000000a8ec0f7e346f2fea1ddfab1ed6d236b9641a3ad6dadc8fe6d85b30907830bf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a749890b54e0a02f69f70896c0028c0986d84faf11bbb313412130c0478a9efa78d1802b3dba2622f345986c97f8a5abb47e1b888763c1d95d83441ab924593900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff180000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001900000003124d696e657220666f7220646174612067656e081800000000000000080000000000000000", + "6220cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31": "000000009e64385e0000000063a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000faf9826f22ab9e5fd8850a72e6d526c99774645b2da88d668acfc516ccf6a7bd1cfa41793cd6f68255eda0b063e35c68980a389ffd731d877bedc14b3d9a2e200000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000f00000003124d696e657220666f7220646174612067656e080e00000000000000080000000000000000", + "62218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "01000000de51385e000000002ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb8100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0fa2412035ea50aad5dfa552200a584f3c8b5f5ed0395a3ba9f61ffcdfaf03c10d7a31250c9abd77aa5f7ea994b191de4980200322f7017a60851612adc5d8d00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000700000003124d696e657220666f7220646174612067656e080600000000000000080000000000000000", + "622410e9397d0e94ecb04f1dc26f295884f71621c0a38273b455fc4408beefabdd": "01000000d695385e000000003623d3e95c253b5c0013f5ca59be7b27d8627752fa6ab5db6ef9a1c575c7a88b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063073cd9466141c84aeea40a3f9ec4d88279b675d7aa5cf0371808fbbe8e79fabf3cd9760a08ae1bb5072d65b1bec43eea01fd0898832bc6a5494f4cd3193fcb00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff230000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002400000003124d696e657220666f7220646174612067656e082300000000000000080000000000000000", + "6225ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f": "00000000a66b385e000000005fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be185750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099440cecc52f2703578666b0adcfab3ca236ebb4c29e8d53648bf7fabf897fbbd8c894203ead6810c16ef6136d1cafc12a51e369dbd515727c16f6f6d745e74f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff110000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001200000003124d696e657220666f7220646174612067656e081100000000000000080000000000000000", + "6225ecece67696be82533b236e082ffc9be5664107c957e18a74815baddc165259": "03000000aebd385e000000004180159d8865229e71727ccbaae7462d0dca3ce29a382ee24c58f726fa4c5587c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae465954edd9fecec9e75c9d6764d5fb92f60ff2651795ce5dcb516c13473c752a1728a34d1c816fba5ec0b64263731be69835e447f8d0b41d8da10e7cadab5b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff340000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003500000003124d696e657220666f7220646174612067656e083400000000000000080000000000000000", + "622ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "00000000864f385e00000000023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009166e55997f82e200b1075b7a1cc9a12e40827d7ccac1b643c2dd690e09761b4a9cd7eeb8b6a7d937080f36c2a5804906b4b5bc875ed04435be54f5aad1a188a00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000600000003124d696e657220666f7220646174612067656e080500000000000000080000000000000000", + "623133af956ccf659c54198db2e47fe5ba38988e73010355dd4b300734a7c41d1c": "090000001ed5385e0000000043e04cca2bbd8e92941ae0e2d50bcb0a171dc06f6ac7772a8a4cb4f1ad8dbdafb19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097bcf7ef8a04b6a2b846efb9cc9c098969c6745f82868965f543fa7feaefb016fb02ebe559fbbe8a01af8943175e85c3afec96c886f77be17732c8b0024a85d800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3e00000001fa9e3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003f00000003124d696e657220666f7220646174612067656e083e00000000000000080000000000000000000000000296974fa8c2e0cec0642a9421d351a333ecefc145cd2057bc397580b77a06556a00000000fffffffff213a9f7e04dda5bf839d3cf2d6ae6bf45860e3c435d47b1d75a5dc2be96126700000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50689357700000000001460976bb97754c7ab48bac4c894d27a4126bc23d10000000000000241e8221b7fde5b99a6d02cadecc4032992dfd2df9375f777b6e1bf57c30af0480d0ddb80f87ace6fb7ab249a72f05c69d43f6472e731b6e6f8afa06b5dddab32cd0121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd5302415f97f70ec3ac091041b08bc8c165cda1f00e93ae3ab3c6a01b71c64ebd2f4ebd09f9f9b5d894736749d4538df0c6e957a9ec7e7d4b025560a47d6c28eb41e617012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "6232fcc0051a115a09787f755dc69fb92adfdfe39be5b8e9c30307cc1564574b05": "00000000ce8e385e000000006cca43f36eb13b2a6af73e9185e083a84426cbe8d16094f62fb158adcb542970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008fb43c4c4bd13d9e5cc08ca76c3407a94111097d4855759fb45abc0633c5d668f0e5935ac5f4058b276772434da60262d4b96fec397563d622819e4a02b40e0c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff200000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002100000003124d696e657220666f7220646174612067656e082000000000000000080000000000000000", + "62356f2ee752c03a6df51b2c881c15fa88257a6664707b578cc008324125035d13": "00000000be80385e000000005313cfc86e21c665f4bef6d96c8806924d771c62b0c83faaa6c38fd5ec9d5ee3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bddbe396291d8209b5f3959cf2315fc92bf897c34bb7296845d997362112ff630e91cc8cad1cac9bcd4418dd0d1c0e6cf55d27d757eba4751b215f2d82803f700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001b00000003124d696e657220666f7220646174612067656e081a00000000000000080000000000000000", + "623623d3e95c253b5c0013f5ca59be7b27d8627752fa6ab5db6ef9a1c575c7a88b": "000000007e93385e0000000004639906391a0e10f21f7872fc0ad77ca42ef66befadc6e0252cde03da097673000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cecf592e30040c20d6efface2d5ff9323fa5cd8517471310ebbd4d2a40a7de5d75c1edfeedb93d1a88ee1bac8af79c4dfe6b89b2894afe202c2f243870e71ef00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff220000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002300000003124d696e657220666f7220646174612067656e082200000000000000080000000000000000", + "623b22bafe7f2b6605f14d99eba22919544b3df99277491c129a7cabdcdc33c8c3": "0000000076d7385e000000003133af956ccf659c54198db2e47fe5ba38988e73010355dd4b300734a7c41d1cb19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b634214c8a12a9a05a2e97f88143972d6527d9dd082cf47d5e27cdae3bb3b729140667917a48059e6667d4903294259eea1ebc09fc8795f7defd66b137b56f200000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3f00000001ba9d3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300004000000003124d696e657220666f7220646174612067656e083f000000000000000800000000000000000000000002bb3352304fb5d34a5e4dc208d4610580c1b4cad3be98ee6aa4eaf2d5663b090500000000ffffffff528c8499ebe12e2ef754d1752dc8e8b6fea7efecc2ddb43a77e04301f7f4dbd400000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5007032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000401020304468a35770000000000147a98b6621e4ccc528560645f6a7682633a24fad800000000000002413f55dd7aada2d2c918a20c3fb754d4b5ffd22c6d80bceeb1ec7b7a21e6743a527e0314e74604c29479711ed3c4cabaa71072226e4d7efd987e3a8c80ff89cb260121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd53024165fa162d65bd4643f38b604e2bbce6d8d80938ae749d69521486e2ca353abff050e9dad05e9b9f4b0a4aa1016d17b0afdb5ece1838a5ecba8fd460b41778e519012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "623ddebe9d71ff2519c0935f0318e9290b77703119120360affce4583f22b3ce55": "01000000becb385e0000000074b29e4d16a90a69e50029d222509684e1d65b215aa4d989b5c44292e56d5571b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069edb4580cc2cb175bff2ae52921c7e5117972fcdc806e8ebda6e80248bac5225651a26386f8bbe76d0394b8a6ea490e79447a113b9780760780cba8ebc7ee8700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003b00000003124d696e657220666f7220646174612067656e083a00000000000000080000000000000000", + "623f6882c784bf0c97e7907e20094f867464a51c3b4ccbec48dbf5c0e280b73598": "0000000006c0385e0000000025ecece67696be82533b236e082ffc9be5664107c957e18a74815baddc165259c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca09300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c6a09e6db28f1a6801abdc65cd3210477341080119463a2dc72abd2425c3ad0a74118dcb889285036088e6ef5834c98ffd4a77c95e7e8bbc73dab3aa7319cde00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff350000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003600000003124d696e657220666f7220646174612067656e083500000000000000080000000000000000", + "6241764d19cb289d8d17877a438d07e892b68058d18b3aeb696c13de53deee153a": "0100000096a8385e0000000077cb23bcf8547925555a5ee305af5c7b1d7c5d9f0004f5311b12cdf5a99c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ae7b5dd119e910636116f15bc9c60f2d158a9f3f48214dcb145fd261c4d9954b88decc7fef157c72147744fba3fcdc688a560c696faa9fbd18db5ef5611c98800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002c00000003124d696e657220666f7220646174612067656e082b00000000000000080000000000000000", + "624180159d8865229e71727ccbaae7462d0dca3ce29a382ee24c58f726fa4c5587": "0300000056bb385e00000000645231c0267327a3aa0ddd27ec506d16490e9ee75cddb7cab8f41a09912f8701c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b9b639363cb0dc5b9ebbacec4bcbe8c8443fb5b6320c89c72625fa960bb866326de5c637466fb378d484a5144d076f837897405a9d4478e356eb0563c6cce69a00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3300000001d8a23577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003400000003124d696e657220666f7220646174612067656e08330000000000000008000000000000000000000000027361d878d19200ccdfb0ce7d0a10947ad9c2feb68072d1cd935c4e30b82a799f00000000ffffffff7eaf52ce39eef2744479061c4dd1020dc00d12f771054642edf46ae84d52242e00000000ffffffff0310270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65042900000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc0410270000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57504032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65042900000020c632c00673f5b777cb5275ed44da6747c0ba74bd8bf1e3170a041700b6638567483f0000000000000014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec700000000000002414cd845ed70bdba7499b67b2f854e0ce67e9e9c46fb2529c7d784eab1837fc86b3ce289a7eaf52af83bec1cd88cf3b9bd95f198c352d0530539e31cef941682bf0121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd5302416159fd1599ef37f02c9705a5ae85eee1b4f3cbbbd2a8fc3a9361cb81a2a8c261705709aeca36fb8661b7ceebd1b84d1e44022da78ff2db757ddfd391fab3ebb10121031dfb31228b957bfa7e5bb71c8bd51eb5c17802b2ddd915767fdcb36c5a4fa83d", + "6241a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801": "010000003e5b385e00000000796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f666f45583cba91c8647a6f503f904e111bd60fa68c61461132ee2091f63582f7dce7d52e000f8bce8458275be81055f5e71b725863a60330a11185f5dd31f6b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000b00000003124d696e657220666f7220646174612067656e080a00000000000000080000000000000000", + "6243e04cca2bbd8e92941ae0e2d50bcb0a171dc06f6ac7772a8a4cb4f1ad8dbdaf": "04000000c6d2385e00000000676e176ded401273ba3a89dc7a03bdc6d79ec86ea06b4e8b5fc2d7b2cc773644b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a508973f8f76f85c71a817920579521cf52e9410a8178934f5e2a80a514c2011ebf2f8030d66213326a70ed96bcc5788f422c8d688880aa640edb902f8c581e400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003e00000003124d696e657220666f7220646174612067656e083d00000000000000080000000000000000", + "624670eb9a36ddab3606440b78aeb61c2047466f23fad806f1b457ff9fa21d1247": "020000009eaf385e000000006d24d44368cbce5de1d73e1b25364511432be27ca7c6aef54a5c79705171d38ac0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000814e3ef3bf108bb0cb95448c8265088e6fbac0a59b7bf6986b5e6f3dcc5e1aa35a0e7dbad0879b61c94857c85831484ffa7276b367c886b3475bd4ec56dbb25b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000300000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2e0000000158a53577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002f00000003124d696e657220666f7220646174612067656e082e000000000000000800000000000000000000000001400819f704335fca6ce65693f9666beb0b0798062c47a5a14d9701e5e850d29500000000ffffffff02204e0000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df343d3577000000000014222740511246fe2b10957049ae10e0bbe7fa996c0000000000000241fb44b19381d8686a64b86048c4952f57c1e1f156fefeee14501449a6404b5c226bc23d0583b625f4d1a2fa3f0e99ee80a1815a74bb3ed0dbe19b10b06538d043012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f200000000019b487bc7de42e644488cc6baa13a3b2c48a5f446596028010f26f840d62e8f9d00000000ffffffff02204e0000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe343d35770000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc00000000000002415bd6657fb202db27c35a80bd795d8dfb30236a8b5410c872befea396b929f87c1d1bc14c9582dc7e623dd32d614d306987d58c14d555b1a48e6f4c59e4e4995a012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "624ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972": "03000000965d385e0000000041a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009952c7481f1ebd82b54ffa2e29f268f5a1f646f0756ab5a9422deec9983a00a893c8556d944a646b8640201df462dc4acbdb3bea36cbe2f48933d0bc5fa680cb00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000c00000003124d696e657220666f7220646174612067656e080b00000000000000080000000000000000", + "624dd4b93fee39400ba4e05e8b2ea5afdb2ce547cb53bad25cc5835b4d92f7d2c8": "010000004eb4385e000000004f54e598c206348d4ea687e9f5c604560b2a37cc9de9af25baa432c73a14a45fc0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000457aaa74c57dc72665408a08a44160e742b4c41b8b354f0ca0122c3a444abc05ba110d1a51f143f98e5f7e7679d944c12c9ff59c0ff9549c3edb244422460b0400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff300000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003100000003124d696e657220666f7220646174612067656e083000000000000000080000000000000000", + "624f54e598c206348d4ea687e9f5c604560b2a37cc9de9af25baa432c73a14a45f": "00000000f6b1385e000000004670eb9a36ddab3606440b78aeb61c2047466f23fad806f1b457ff9fa21d1247c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca093000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048e6329ae25fd0ca05ab8bf7287ef824eec292b0b5ea528fa604ac00d92f7f63dd30acaa1a3eb181f0295de15b6091060e411816559f49abef918bd4fa20f61600000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003000000003124d696e657220666f7220646174612067656e082f00000000000000080000000000000000", + "6252542bb7d5411695c2cfe7617afbe7475a72b268de11883ff8d9c9bfcb336ae7": "020000000ec7385e000000006531739e2aab343af2a3ce3fd834daab6e79fbae29bd05088b2128518a5b0560b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160284d66a009dc474c09b86a4ff408939c96aa58ead11b16eb71c76c7a4e242b4fff920f83dc8f0a84e7aef6b254deec5f866d950f17a3639806d66965719a100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff380000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003900000003124d696e657220666f7220646174612067656e083800000000000000080000000000000000", + "6252de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "000000008e56385e000000000d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e63700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000390e4b36e542e82c0e923ca4d1932f3f5347a16efb8902c61a4e60673cb3d8205b949d1715598766a84a6832a000ecc8c8d0af5d21e17d34334fb72d72a6f5fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff080000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000900000003124d696e657220666f7220646174612067656e080800000000000000080000000000000000", + "625313cfc86e21c665f4bef6d96c8806924d771c62b0c83faaa6c38fd5ec9d5ee3": "00000000667e385e000000001d1cd648871f2dd0d128322b0e647b9a12d7c149bc3e8e8be4bbbfb23861995500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6d037463fcb5dcd1f39f9477d3e9a8bb1297bf2ebc0ba8e3a9c31fc89f6adc9d88940d30b8754686427f6425da074bbefafadfeacb8c1144acc2d4e2a89aefc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff190000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001a00000003124d696e657220666f7220646174612067656e081900000000000000080000000000000000", + "6258c5a89699f3782408030d211325e20046cfa5113457265b55f06175bc3f8992": "000000001683385e00000000356f2ee752c03a6df51b2c881c15fa88257a6664707b578cc008324125035d1300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000932ee74fa91d151c09263a2689da2248ee78297979f4986287787075bf22c419c607707b6dc2c599174227943c14f311f6861cea884ec78699f4558235e1c80f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001c00000003124d696e657220666f7220646174612067656e081b00000000000000080000000000000000", + "625a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "00000000ce43385e00000000ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083e6df7f577fa1444dca368383c7ac8b7c4cdaef03acfcaaa832c6a3a9b5b8e3b359fe257aaba2e2d74dfb3dcfdde5c270463fd1a56bf960955627e68a499a5100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff000000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000100000003124d696e657220666f7220646174612067656e080000000000000000080000000000000000", + "625a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "00000000d64a385e000000006a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd4900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043c0463823b12f4d83446c43b376c1342ededcc1da92d52246030f769a7e46ffb4d5d8c03171ba22ec589da0044aaa801b09bbedfa53b5e62b96a4fd90d993e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff030000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000400000003124d696e657220666f7220646174612067656e080300000000000000080000000000000000", + "625be82989b3d3a6455926d6a3655cfe16af16e08982b63bcfaa131c4783191e70": "000000005ec2385e000000003f6882c784bf0c97e7907e20094f867464a51c3b4ccbec48dbf5c0e280b73598c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000588d25bb061531a2ae0dd514fc68f5db708168c82a5794a18dd18bc65f1f80bf1b7c01bc8b5c63bf25a73d07cc776b3fba13202cfb93696ccc4c29b97869b58200000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff360000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003700000003124d696e657220666f7220646174612067656e083600000000000000080000000000000000", + "625c9f0b068ad51164b01f78e45bb86dd738e13a8d56552889639c01c481efcd4f": "000000006e85385e0000000058c5a89699f3782408030d211325e20046cfa5113457265b55f06175bc3f899200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c65b77f0a82a02d8d54b6ec3e0b531681a3f4928de15d8818a8acda026c524257698df8ca2f1c793f77a0b4b53d2d2518cf2fef78bb76b1e3704ec8826fd81e800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001d00000003124d696e657220666f7220646174612067656e081c00000000000000080000000000000000", + "625e12dc9b89512079e06821556df69f4655e41384aff52f59dc9862100e29ac50": "00000000de9c385e0000000001f4834003e1ae6467e46d99f8b0ea77cf5c0f2307b68a84e2524b4d1f140937000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d992736510e384198ceea78cd9b574a8c447fd90a01c275b3c1e3c52155be651c03e4040df295c4883f4d599b9849389c6f3d270c8c9f408867f0ff03131ba900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff260000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002700000003124d696e657220666f7220646174612067656e082600000000000000080000000000000000", + "625fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575": "020000004e69385e00000000195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009694eb45717668a57ec854436adb1900356b744741c79b65fad7645cdb377b471d83d7454586a56ab022cc65d158727c381fb135c869aa2e4cba66fc5f57afcc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff100000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001100000003124d696e657220666f7220646174612067656e081000000000000000080000000000000000", + "6260adae5d095140f61294db960fbbe9f951699adf0f6dc7603ce17e62bd8a28e6": "0000000016ce385e000000003ddebe9d71ff2519c0935f0318e9290b77703119120360affce4583f22b3ce55b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee08bb0a5343d0216861698c9908b2422c508172f8db3549d7c67cac16dddac479396a0dd81258718901c35474ca35e5ee1649cda75bfd783c757e26055a264c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003c00000003124d696e657220666f7220646174612067656e083b00000000000000080000000000000000", + "6261c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "020000002646385e000000005a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5b3f00155bfcbcf9a5474b24b2502cfc04fb044618e080d0de01f169287019228f94d91b51f4ae612804fecadfb0f036b60bc52a92d83168153a5622ceeb38e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff010000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000200000003124d696e657220666f7220646174612067656e080100000000000000080000000000000000", + "6263a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f": "020000004662385e0000000019621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f3b952fe99137d91bad3e8eff8c4e8ffdc1402988a71fc415dec1695561378ca7e17046f1ed6fc462c6d2363bb8ff188d11bb7f7f43dcc06443471dae2e956c400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000e00000003124d696e657220666f7220646174612067656e080d00000000000000080000000000000000", + "6263b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac": "000000005670385e00000000680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d33f379231913136bf301b5ed38cf5f2d3dae390bcc6c58e7ff3305f48eea8d7e8f9eb6e41344d13c30c63e3018266d5685e39b4adcdd805d356c70904df6cef00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff130000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001400000003124d696e657220666f7220646174612067656e081300000000000000080000000000000000", + "62645231c0267327a3aa0ddd27ec506d16490e9ee75cddb7cab8f41a09912f8701": "01000000feb8385e00000000769997955a1dde6ea267844c666d4b55b5e07e6087b77de3dcc22d03aa9193a0c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca093000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018fee165d5f7f38080dff98a724a4ca7e17c2f53a7a955e1536547a29017efc49af695dd6a0e58ce76dc671f2183b36ab425fc895998d71c6e980b279c786c6b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff320000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003300000003124d696e657220666f7220646174612067656e083200000000000000080000000000000000", + "626531739e2aab343af2a3ce3fd834daab6e79fbae29bd05088b2128518a5b0560": "01000000b6c4385e000000005be82989b3d3a6455926d6a3655cfe16af16e08982b63bcfaa131c4783191e70b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000653997d80b33bd0033e78b02e02f35eb5b3a361051bd84b81408c53445745805c87081223549004f4fee9cc21324ba49f2a3b511b1e6bf19731996973279471c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff370000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003800000003124d696e657220666f7220646174612067656e083700000000000000080000000000000000", + "62676e176ded401273ba3a89dc7a03bdc6d79ec86ea06b4e8b5fc2d7b2cc773644": "000000006ed0385e0000000060adae5d095140f61294db960fbbe9f951699adf0f6dc7603ce17e62bd8a28e6b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006379b6dbf1ca95b66768e7e80a111bb6889f63d80e5e127a45805ea4dbd63bc9330a65a190909e8a8a97985686aa71d28dfe5ecf8da585d7d966431934bb7c3500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003d00000003124d696e657220666f7220646174612067656e083c00000000000000080000000000000000", + "62680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a": "00000000fe6d385e0000000025ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000095e6537bbc29a59e564f19c258ec19a7c821b6ff001ec4ad47604861f150b89763f90b1e4f1fe82e89119cf8e2966cacb2562a1dc236168463b577b55d4d82ae00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff120000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001300000003124d696e657220666f7220646174612067656e081200000000000000080000000000000000", + "6269d3d31f6229c9897d3062af52f286ad9ae35bb9074101a46a123076664b211b": "000000002e98385e000000002410e9397d0e94ecb04f1dc26f295884f71621c0a38273b455fc4408beefabdd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b4a56e342513350812ec1166791f76c96e3b2391060474db6f11b892d62c5aefa8607ed5d859a8a6864b1dc4c3e453049589a42eb8f1dfdb6af537481ae6e1e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff240000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002500000003124d696e657220666f7220646174612067656e082400000000000000080000000000000000", + "626a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "000000007e48385e0000000061c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac280bf1231e5aeaee10ce20f93393769452cf00abc404ac83ebcda710cc3dcb171727d58f15b3e6666de2efbe6c5a57de0ed3f64ac6a1cbd5d9800a2d2e974300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000300000003124d696e657220666f7220646174612067656e080200000000000000080000000000000000", + "626cca43f36eb13b2a6af73e9185e083a84426cbe8d16094f62fb158adcb542970": "02000000768c385e0000000013a3e4ac9804e9d23581e39f177b9e14ec60a07230e8fe3bfa3434ac2d80e526000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b4954135e3bec9fdd8b130d394dc26562da62f533700308aa045757f779f64cb4d8b2b501a32f75115be80f974b76eb6d7ed9a9eccfc136eb5942dbe9e65d2800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002000000003124d696e657220666f7220646174612067656e081f00000000000000080000000000000000", + "626d24d44368cbce5de1d73e1b25364511432be27ca7c6aef54a5c79705171d38a": "0000000046ad385e0000000077b13357c3c68f1c1fb0f4967715ceb9d4dcb6fe686379c3a77fd1404de37aa4c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca09300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6a32cc6d71901696955b0eb6e50fdc5cee79817545b9035a18a9fae4c9d77d4d80f04db7015294e9aaab1f090bee88549631359abe550fc1bdb3d7850d4a8e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002e00000003124d696e657220666f7220646174612067656e082d00000000000000080000000000000000", + "62735b9e424057caccb7ceb21bd56a45b42809c3e580af13cfe2fe06e113b23f6f": "000000008ea1385e0000000003fd277eb7864b864c6d4d1df862e37a2413fa9a9f1e4a8ca0d6d8a08f2e73da000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf8c0f64a5e3f2dd9e388ab2d54fa5b528548b766ad721057cb808127ce76d895c98e5f3282b21f0267ea9da585654e061825525757e57619bcf057900c28300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2800000001629b3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002900000003124d696e657220666f7220646174612067656e0828000000000000000800000000000000000000000001f09e1b55be758049061eb1c5667d55f83ea2a5f85770521169545c0a8e70234d00000000ffffffff02000000000000000000142f6d31b494bdfa42034719ca28894496cdb8935902032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504000000000a6e7970767a766c7868619e8c35770000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000241ba9d3c7c0671b34249000b8f0d1d14e0bc84ff2e8a86c010c7c03beadcbf623b01b1fae5f06884bc8d675707901f748c77fa20e933773b3b74e3e41ab4c4c512012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "62736125af8ebd0bf374b34f1ba05a184e17c7c3cb912f26272aaa57b36a50b1d0": "00000000ae72385e0000000063b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6b1536a06374f4c7d7f744b35ebbf2bbb800d6886b548f9fb18a1d91a967979106fb520813c578883084f7b6077fed978f7245d162c415c06c6008a75ea5b1c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff140000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001500000003124d696e657220666f7220646174612067656e081400000000000000080000000000000000", + "6274aeedf1bd09cde9bfdffcf3906c928a9629d28bc54d55ed6f590e001248d2ad": "00000000c687385e000000005c9f0b068ad51164b01f78e45bb86dd738e13a8d56552889639c01c481efcd4f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa8d227f12e1ffc326c92e376be83ae054b5e47fce08d82f37cf871cab641229254915b59b8273cd840b1687a1aa807bba79c415632f404685a239b2b0bb166500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001e00000003124d696e657220666f7220646174612067656e081d00000000000000080000000000000000", + "6274b29e4d16a90a69e50029d222509684e1d65b215aa4d989b5c44292e56d5571": "0200000066c9385e0000000052542bb7d5411695c2cfe7617afbe7475a72b268de11883ff8d9c9bfcb336ae7b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b82f4e0bfd6e465d4dd8dfb28b3d699475078cf15bd7906b9e70da9bb7e359802dedd6dc4fc79da84db23eafda55f7ce77fb7075addc0a1f25b2fb0c052b2cdc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff390000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003a00000003124d696e657220666f7220646174612067656e083900000000000000080000000000000000", + "62769997955a1dde6ea267844c666d4b55b5e07e6087b77de3dcc22d03aa9193a0": "00000000a6b6385e000000004dd4b93fee39400ba4e05e8b2ea5afdb2ce547cb53bad25cc5835b4d92f7d2c8c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca09300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006bb54e939e44c64861185dcbe35332cab369e59953053e9878ed6e88e492e99009423890b236998eb6f1083e7097cc9119c47717e286da2aee05f7b57f34c42f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff310000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003200000003124d696e657220666f7220646174612067656e083100000000000000080000000000000000", + "6277b13357c3c68f1c1fb0f4967715ceb9d4dcb6fe686379c3a77fd1404de37aa4": "00000000eeaa385e0000000041764d19cb289d8d17877a438d07e892b68058d18b3aeb696c13de53deee153a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7cee0aaba7635dc82c496778cb4042a806a01eeedff3452d47b884ab214beaad1a5cf1c2a9dbc96abc8c39ec15a07fda8b677d91e28842c8a37ae985986040300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002d00000003124d696e657220666f7220646174612067656e082c00000000000000080000000000000000", + "6277cb23bcf8547925555a5ee305af5c7b1d7c5d9f0004f5311b12cdf5a99c6574": "010000003ea6385e00000000151b3be5f32e0324ba6677a85b6f6bf64240b109bc3ea2972902c19218ad0ebf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6110cbb59b73bc95f31484b27ce9f42a19f662ea61c0ede9db476d6043f3865dfdd9e33d38e51d4542f39e07fd8fd27c28a46a74363f1f37bb467a2086863c400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002b00000003124d696e657220666f7220646174612067656e082a00000000000000080000000000000000", + "62796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "00000000e658385e0000000052de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0c13adfe3ae131ba76a105a819e3a11bfd4aefc66c537aa54567f766ee3bb0c57d1d97ddbd3641748c818adf8967b100a5a3b480a029ccbb077ed50e7455ae100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff090000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000a00000003124d696e657220666f7220646174612067656e080900000000000000080000000000000000", + "62ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "000000007841385e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a2c60b9439206938f8d7823782abdb8b211a57431e9c9b6a6365d8d428933518e4c9756fef2ad10375f360e0560fcc7587eb5223ddf8cd7c7e06e60a1140b1500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff01d04c5777000000000014f0237ae2e8f860f7d79124fc513f012e5aaa8d23000000000000042050b8937fc5def08f9f3cbda7e5f08c706edb80aba5880c000000000000000000202d5de58609d4970fb548f85ad07a87db40e054e34cc81c951ca995a58f674db72010d748eda1b9c67b94d3244e0211677618a9b4b329e896ad90431f9f48034bad20e2c0299a1e466773516655f09a64b1e16b2579530de6c4a59ce5654dea45180f", + "753133af956ccf659c54198db2e47fe5ba38988e73010355dd4b300734a7c41d1c": "020000000068000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65042900000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc040009000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "753b22bafe7f2b6605f14d99eba22919544b3df99277491c129a7cabdcdc33c8c3": "02000000007e000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5000b000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "754180159d8865229e71727ccbaae7462d0dca3ce29a382ee24c58f726fa4c5587": "02000000005e000000fd204e0014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df005e000000fd204e0014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe", + "754670eb9a36ddab3606440b78aeb61c2047466f23fad806f1b457ff9fa21d1247": "020000000005000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000007000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "75735b9e424057caccb7ceb21bd56a45b42809c3e580af13cfe2fe06e113b23f6f": "010000000003000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000" + }, + "pruned": { + "6201f4834003e1ae6467e46d99f8b0ea77cf5c0f2307b68a84e2524b4d1f140937": "00000000869a385e0000000069d3d31f6229c9897d3062af52f286ad9ae35bb9074101a46a123076664b211b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005557ec8eba2a8e148d090ee12ece24d76143ba3e7f74ed99e780db64b8bc3ba98bef5892e3207f2dd220d7bea608b574821caf899cbfe27ab46215987280b9e700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff250000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002600000003124d696e657220666f7220646174612067656e082500000000000000080000000000000000", + "62023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "000000002e4d385e000000005a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d7821350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e1b13c02e1a1a62973bd11419b8ae101cc1d5673ac4367d2d5887ced610a7ffbbcdd88e0f14469f736ff4dbb17f3ae4632964d093efa6f9362d48581cc4f5300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000500000003124d696e657220666f7220646174612067656e080400000000000000080000000000000000", + "6203fd277eb7864b864c6d4d1df862e37a2413fa9a9f1e4a8ca0d6d8a08f2e73da": "01000000369f385e000000005e12dc9b89512079e06821556df69f4655e41384aff52f59dc9862100e29ac5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a427d9fd0a8fc3c3425254b9fecc84d46e816b84490e5e5365ff71c648907da92c3e15b47fd4a34691dad929e94a579cc1708d5d6f0c4f25467fc9624dc15cd100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002800000003124d696e657220666f7220646174612067656e082700000000000000080000000000000000", + "6204639906391a0e10f21f7872fc0ad77ca42ef66befadc6e0252cde03da097673": "000000002691385e0000000032fcc0051a115a09787f755dc69fb92adfdfe39be5b8e9c30307cc1564574b0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4ef0d083671d3f7ba76de62f92e9a958c8fff5820726915369e95f6f8ddba8bee7496b1fb12fdc3dd4dd506f859f193e534d17cda07fa73b1668b1ed9f0b6fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff210000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002200000003124d696e657220666f7220646174612067656e082100000000000000080000000000000000", + "620d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "010000003654385e00000000218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561ba8e15b6446ae574bd94831f78c34e3d36f891b499ea318c92d6c158cbc7062c56c8f928267cc7889f18ccd50a413daaba98fba47a74da65e46746a1b914400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff070000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000800000003124d696e657220666f7220646174612067656e080700000000000000080000000000000000", + "6213a3e4ac9804e9d23581e39f177b9e14ec60a07230e8fe3bfa3434ac2d80e526": "000000001e8a385e0000000074aeedf1bd09cde9bfdffcf3906c928a9629d28bc54d55ed6f590e001248d2ad0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019a0bdab3dff23fd435494b4944a0daeddd082e6ff7f05b4eccae067ac7fd1e45284a1dc05f01642f98bcb183abb13f1859d691307a69092bdc588ad0bd6df5e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001f00000003124d696e657220666f7220646174612067656e081e00000000000000080000000000000000", + "62151b3be5f32e0324ba6677a85b6f6bf64240b109bc3ea2972902c19218ad0ebf": "00000000e6a3385e00000000735b9e424057caccb7ceb21bd56a45b42809c3e580af13cfe2fe06e113b23f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000253216431886015813a2c3265167c0b470bc033872d45199fe1b929fc226889e030f1ef09799fd5be8064fb4d730aab74a6b9aad1096d416e71b041917c1604500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff290000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002a00000003124d696e657220666f7220646174612067656e082900000000000000080000000000000000", + "621d1cd648871f2dd0d128322b0e647b9a12d7c149bc3e8e8be4bbbfb238619955": "010000000e7c385e000000000a8ec0f7e346f2fea1ddfab1ed6d236b9641a3ad6dadc8fe6d85b30907830bf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a749890b54e0a02f69f70896c0028c0986d84faf11bbb313412130c0478a9efa78d1802b3dba2622f345986c97f8a5abb47e1b888763c1d95d83441ab924593900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff180000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001900000003124d696e657220666f7220646174612067656e081800000000000000080000000000000000", + "62218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "01000000de51385e000000002ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb8100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0fa2412035ea50aad5dfa552200a584f3c8b5f5ed0395a3ba9f61ffcdfaf03c10d7a31250c9abd77aa5f7ea994b191de4980200322f7017a60851612adc5d8d00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000700000003124d696e657220666f7220646174612067656e080600000000000000080000000000000000", + "622410e9397d0e94ecb04f1dc26f295884f71621c0a38273b455fc4408beefabdd": "01000000d695385e000000003623d3e95c253b5c0013f5ca59be7b27d8627752fa6ab5db6ef9a1c575c7a88b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063073cd9466141c84aeea40a3f9ec4d88279b675d7aa5cf0371808fbbe8e79fabf3cd9760a08ae1bb5072d65b1bec43eea01fd0898832bc6a5494f4cd3193fcb00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff230000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002400000003124d696e657220666f7220646174612067656e082300000000000000080000000000000000", + "6225ecece67696be82533b236e082ffc9be5664107c957e18a74815baddc165259": "03000000aebd385e000000004180159d8865229e71727ccbaae7462d0dca3ce29a382ee24c58f726fa4c5587c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae465954edd9fecec9e75c9d6764d5fb92f60ff2651795ce5dcb516c13473c752a1728a34d1c816fba5ec0b64263731be69835e447f8d0b41d8da10e7cadab5b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff340000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003500000003124d696e657220666f7220646174612067656e083400000000000000080000000000000000", + "622ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "00000000864f385e00000000023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009166e55997f82e200b1075b7a1cc9a12e40827d7ccac1b643c2dd690e09761b4a9cd7eeb8b6a7d937080f36c2a5804906b4b5bc875ed04435be54f5aad1a188a00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000600000003124d696e657220666f7220646174612067656e080500000000000000080000000000000000", + "623133af956ccf659c54198db2e47fe5ba38988e73010355dd4b300734a7c41d1c": "090000001ed5385e0000000043e04cca2bbd8e92941ae0e2d50bcb0a171dc06f6ac7772a8a4cb4f1ad8dbdafb19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097bcf7ef8a04b6a2b846efb9cc9c098969c6745f82868965f543fa7feaefb016fb02ebe559fbbe8a01af8943175e85c3afec96c886f77be17732c8b0024a85d800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3e00000001fa9e3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003f00000003124d696e657220666f7220646174612067656e083e00000000000000080000000000000000000000000296974fa8c2e0cec0642a9421d351a333ecefc145cd2057bc397580b77a06556a00000000fffffffff213a9f7e04dda5bf839d3cf2d6ae6bf45860e3c435d47b1d75a5dc2be96126700000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50689357700000000001460976bb97754c7ab48bac4c894d27a4126bc23d10000000000000241e8221b7fde5b99a6d02cadecc4032992dfd2df9375f777b6e1bf57c30af0480d0ddb80f87ace6fb7ab249a72f05c69d43f6472e731b6e6f8afa06b5dddab32cd0121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd5302415f97f70ec3ac091041b08bc8c165cda1f00e93ae3ab3c6a01b71c64ebd2f4ebd09f9f9b5d894736749d4538df0c6e957a9ec7e7d4b025560a47d6c28eb41e617012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "6232fcc0051a115a09787f755dc69fb92adfdfe39be5b8e9c30307cc1564574b05": "00000000ce8e385e000000006cca43f36eb13b2a6af73e9185e083a84426cbe8d16094f62fb158adcb542970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008fb43c4c4bd13d9e5cc08ca76c3407a94111097d4855759fb45abc0633c5d668f0e5935ac5f4058b276772434da60262d4b96fec397563d622819e4a02b40e0c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff200000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002100000003124d696e657220666f7220646174612067656e082000000000000000080000000000000000", + "62356f2ee752c03a6df51b2c881c15fa88257a6664707b578cc008324125035d13": "00000000be80385e000000005313cfc86e21c665f4bef6d96c8806924d771c62b0c83faaa6c38fd5ec9d5ee3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bddbe396291d8209b5f3959cf2315fc92bf897c34bb7296845d997362112ff630e91cc8cad1cac9bcd4418dd0d1c0e6cf55d27d757eba4751b215f2d82803f700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001b00000003124d696e657220666f7220646174612067656e081a00000000000000080000000000000000", + "623623d3e95c253b5c0013f5ca59be7b27d8627752fa6ab5db6ef9a1c575c7a88b": "000000007e93385e0000000004639906391a0e10f21f7872fc0ad77ca42ef66befadc6e0252cde03da097673000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cecf592e30040c20d6efface2d5ff9323fa5cd8517471310ebbd4d2a40a7de5d75c1edfeedb93d1a88ee1bac8af79c4dfe6b89b2894afe202c2f243870e71ef00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff220000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002300000003124d696e657220666f7220646174612067656e082200000000000000080000000000000000", + "623b22bafe7f2b6605f14d99eba22919544b3df99277491c129a7cabdcdc33c8c3": "0000000076d7385e000000003133af956ccf659c54198db2e47fe5ba38988e73010355dd4b300734a7c41d1cb19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b634214c8a12a9a05a2e97f88143972d6527d9dd082cf47d5e27cdae3bb3b729140667917a48059e6667d4903294259eea1ebc09fc8795f7defd66b137b56f200000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3f00000001ba9d3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300004000000003124d696e657220666f7220646174612067656e083f000000000000000800000000000000000000000002bb3352304fb5d34a5e4dc208d4610580c1b4cad3be98ee6aa4eaf2d5663b090500000000ffffffff528c8499ebe12e2ef754d1752dc8e8b6fea7efecc2ddb43a77e04301f7f4dbd400000000ffffffff0210270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5007032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000401020304468a35770000000000147a98b6621e4ccc528560645f6a7682633a24fad800000000000002413f55dd7aada2d2c918a20c3fb754d4b5ffd22c6d80bceeb1ec7b7a21e6743a527e0314e74604c29479711ed3c4cabaa71072226e4d7efd987e3a8c80ff89cb260121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd53024165fa162d65bd4643f38b604e2bbce6d8d80938ae749d69521486e2ca353abff050e9dad05e9b9f4b0a4aa1016d17b0afdb5ece1838a5ecba8fd460b41778e519012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "623ddebe9d71ff2519c0935f0318e9290b77703119120360affce4583f22b3ce55": "01000000becb385e0000000074b29e4d16a90a69e50029d222509684e1d65b215aa4d989b5c44292e56d5571b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069edb4580cc2cb175bff2ae52921c7e5117972fcdc806e8ebda6e80248bac5225651a26386f8bbe76d0394b8a6ea490e79447a113b9780760780cba8ebc7ee8700000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003b00000003124d696e657220666f7220646174612067656e083a00000000000000080000000000000000", + "623f6882c784bf0c97e7907e20094f867464a51c3b4ccbec48dbf5c0e280b73598": "0000000006c0385e0000000025ecece67696be82533b236e082ffc9be5664107c957e18a74815baddc165259c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca09300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c6a09e6db28f1a6801abdc65cd3210477341080119463a2dc72abd2425c3ad0a74118dcb889285036088e6ef5834c98ffd4a77c95e7e8bbc73dab3aa7319cde00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff350000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003600000003124d696e657220666f7220646174612067656e083500000000000000080000000000000000", + "6241764d19cb289d8d17877a438d07e892b68058d18b3aeb696c13de53deee153a": "0100000096a8385e0000000077cb23bcf8547925555a5ee305af5c7b1d7c5d9f0004f5311b12cdf5a99c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ae7b5dd119e910636116f15bc9c60f2d158a9f3f48214dcb145fd261c4d9954b88decc7fef157c72147744fba3fcdc688a560c696faa9fbd18db5ef5611c98800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002c00000003124d696e657220666f7220646174612067656e082b00000000000000080000000000000000", + "624180159d8865229e71727ccbaae7462d0dca3ce29a382ee24c58f726fa4c5587": "0300000056bb385e00000000645231c0267327a3aa0ddd27ec506d16490e9ee75cddb7cab8f41a09912f8701c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b9b639363cb0dc5b9ebbacec4bcbe8c8443fb5b6320c89c72625fa960bb866326de5c637466fb378d484a5144d076f837897405a9d4478e356eb0563c6cce69a00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3300000001d8a23577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003400000003124d696e657220666f7220646174612067656e08330000000000000008000000000000000000000000027361d878d19200ccdfb0ce7d0a10947ad9c2feb68072d1cd935c4e30b82a799f00000000ffffffff7eaf52ce39eef2744479061c4dd1020dc00d12f771054642edf46ae84d52242e00000000ffffffff0310270000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65042900000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc0410270000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57504032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65042900000020c632c00673f5b777cb5275ed44da6747c0ba74bd8bf1e3170a041700b6638567483f0000000000000014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec700000000000002414cd845ed70bdba7499b67b2f854e0ce67e9e9c46fb2529c7d784eab1837fc86b3ce289a7eaf52af83bec1cd88cf3b9bd95f198c352d0530539e31cef941682bf0121021e2d78b577a9e7f92cc200cb4131e0c867c54327a6d6a831602f7488eed7bd5302416159fd1599ef37f02c9705a5ae85eee1b4f3cbbbd2a8fc3a9361cb81a2a8c261705709aeca36fb8661b7ceebd1b84d1e44022da78ff2db757ddfd391fab3ebb10121031dfb31228b957bfa7e5bb71c8bd51eb5c17802b2ddd915767fdcb36c5a4fa83d", + "6243e04cca2bbd8e92941ae0e2d50bcb0a171dc06f6ac7772a8a4cb4f1ad8dbdaf": "04000000c6d2385e00000000676e176ded401273ba3a89dc7a03bdc6d79ec86ea06b4e8b5fc2d7b2cc773644b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a508973f8f76f85c71a817920579521cf52e9410a8178934f5e2a80a514c2011ebf2f8030d66213326a70ed96bcc5788f422c8d688880aa640edb902f8c581e400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003e00000003124d696e657220666f7220646174612067656e083d00000000000000080000000000000000", + "624670eb9a36ddab3606440b78aeb61c2047466f23fad806f1b457ff9fa21d1247": "020000009eaf385e000000006d24d44368cbce5de1d73e1b25364511432be27ca7c6aef54a5c79705171d38ac0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000814e3ef3bf108bb0cb95448c8265088e6fbac0a59b7bf6986b5e6f3dcc5e1aa35a0e7dbad0879b61c94857c85831484ffa7276b367c886b3475bd4ec56dbb25b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000300000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2e0000000158a53577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002f00000003124d696e657220666f7220646174612067656e082e000000000000000800000000000000000000000001400819f704335fca6ce65693f9666beb0b0798062c47a5a14d9701e5e850d29500000000ffffffff02204e0000000000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df343d3577000000000014222740511246fe2b10957049ae10e0bbe7fa996c0000000000000241fb44b19381d8686a64b86048c4952f57c1e1f156fefeee14501449a6404b5c226bc23d0583b625f4d1a2fa3f0e99ee80a1815a74bb3ed0dbe19b10b06538d043012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f200000000019b487bc7de42e644488cc6baa13a3b2c48a5f446596028010f26f840d62e8f9d00000000ffffffff02204e0000000000000014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe343d35770000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc00000000000002415bd6657fb202db27c35a80bd795d8dfb30236a8b5410c872befea396b929f87c1d1bc14c9582dc7e623dd32d614d306987d58c14d555b1a48e6f4c59e4e4995a012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "624dd4b93fee39400ba4e05e8b2ea5afdb2ce547cb53bad25cc5835b4d92f7d2c8": "010000004eb4385e000000004f54e598c206348d4ea687e9f5c604560b2a37cc9de9af25baa432c73a14a45fc0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000457aaa74c57dc72665408a08a44160e742b4c41b8b354f0ca0122c3a444abc05ba110d1a51f143f98e5f7e7679d944c12c9ff59c0ff9549c3edb244422460b0400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff300000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003100000003124d696e657220666f7220646174612067656e083000000000000000080000000000000000", + "624f54e598c206348d4ea687e9f5c604560b2a37cc9de9af25baa432c73a14a45f": "00000000f6b1385e000000004670eb9a36ddab3606440b78aeb61c2047466f23fad806f1b457ff9fa21d1247c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca093000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048e6329ae25fd0ca05ab8bf7287ef824eec292b0b5ea528fa604ac00d92f7f63dd30acaa1a3eb181f0295de15b6091060e411816559f49abef918bd4fa20f61600000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003000000003124d696e657220666f7220646174612067656e082f00000000000000080000000000000000", + "6252542bb7d5411695c2cfe7617afbe7475a72b268de11883ff8d9c9bfcb336ae7": "020000000ec7385e000000006531739e2aab343af2a3ce3fd834daab6e79fbae29bd05088b2128518a5b0560b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160284d66a009dc474c09b86a4ff408939c96aa58ead11b16eb71c76c7a4e242b4fff920f83dc8f0a84e7aef6b254deec5f866d950f17a3639806d66965719a100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff380000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003900000003124d696e657220666f7220646174612067656e083800000000000000080000000000000000", + "6252de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "000000008e56385e000000000d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e63700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000390e4b36e542e82c0e923ca4d1932f3f5347a16efb8902c61a4e60673cb3d8205b949d1715598766a84a6832a000ecc8c8d0af5d21e17d34334fb72d72a6f5fc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff080000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000900000003124d696e657220666f7220646174612067656e080800000000000000080000000000000000", + "625313cfc86e21c665f4bef6d96c8806924d771c62b0c83faaa6c38fd5ec9d5ee3": "00000000667e385e000000001d1cd648871f2dd0d128322b0e647b9a12d7c149bc3e8e8be4bbbfb23861995500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6d037463fcb5dcd1f39f9477d3e9a8bb1297bf2ebc0ba8e3a9c31fc89f6adc9d88940d30b8754686427f6425da074bbefafadfeacb8c1144acc2d4e2a89aefc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff190000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001a00000003124d696e657220666f7220646174612067656e081900000000000000080000000000000000", + "6258c5a89699f3782408030d211325e20046cfa5113457265b55f06175bc3f8992": "000000001683385e00000000356f2ee752c03a6df51b2c881c15fa88257a6664707b578cc008324125035d1300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000932ee74fa91d151c09263a2689da2248ee78297979f4986287787075bf22c419c607707b6dc2c599174227943c14f311f6861cea884ec78699f4558235e1c80f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001c00000003124d696e657220666f7220646174612067656e081b00000000000000080000000000000000", + "625a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "00000000ce43385e00000000ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083e6df7f577fa1444dca368383c7ac8b7c4cdaef03acfcaaa832c6a3a9b5b8e3b359fe257aaba2e2d74dfb3dcfdde5c270463fd1a56bf960955627e68a499a5100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff000000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000100000003124d696e657220666f7220646174612067656e080000000000000000080000000000000000", + "625a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "00000000d64a385e000000006a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd4900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043c0463823b12f4d83446c43b376c1342ededcc1da92d52246030f769a7e46ffb4d5d8c03171ba22ec589da0044aaa801b09bbedfa53b5e62b96a4fd90d993e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff030000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000400000003124d696e657220666f7220646174612067656e080300000000000000080000000000000000", + "625be82989b3d3a6455926d6a3655cfe16af16e08982b63bcfaa131c4783191e70": "000000005ec2385e000000003f6882c784bf0c97e7907e20094f867464a51c3b4ccbec48dbf5c0e280b73598c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca0930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000588d25bb061531a2ae0dd514fc68f5db708168c82a5794a18dd18bc65f1f80bf1b7c01bc8b5c63bf25a73d07cc776b3fba13202cfb93696ccc4c29b97869b58200000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff360000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003700000003124d696e657220666f7220646174612067656e083600000000000000080000000000000000", + "625c9f0b068ad51164b01f78e45bb86dd738e13a8d56552889639c01c481efcd4f": "000000006e85385e0000000058c5a89699f3782408030d211325e20046cfa5113457265b55f06175bc3f899200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c65b77f0a82a02d8d54b6ec3e0b531681a3f4928de15d8818a8acda026c524257698df8ca2f1c793f77a0b4b53d2d2518cf2fef78bb76b1e3704ec8826fd81e800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001d00000003124d696e657220666f7220646174612067656e081c00000000000000080000000000000000", + "625e12dc9b89512079e06821556df69f4655e41384aff52f59dc9862100e29ac50": "00000000de9c385e0000000001f4834003e1ae6467e46d99f8b0ea77cf5c0f2307b68a84e2524b4d1f140937000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d992736510e384198ceea78cd9b574a8c447fd90a01c275b3c1e3c52155be651c03e4040df295c4883f4d599b9849389c6f3d270c8c9f408867f0ff03131ba900000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff260000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002700000003124d696e657220666f7220646174612067656e082600000000000000080000000000000000", + "6260adae5d095140f61294db960fbbe9f951699adf0f6dc7603ce17e62bd8a28e6": "0000000016ce385e000000003ddebe9d71ff2519c0935f0318e9290b77703119120360affce4583f22b3ce55b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee08bb0a5343d0216861698c9908b2422c508172f8db3549d7c67cac16dddac479396a0dd81258718901c35474ca35e5ee1649cda75bfd783c757e26055a264c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3b0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003c00000003124d696e657220666f7220646174612067656e083b00000000000000080000000000000000", + "6261c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "020000002646385e000000005a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5b3f00155bfcbcf9a5474b24b2502cfc04fb044618e080d0de01f169287019228f94d91b51f4ae612804fecadfb0f036b60bc52a92d83168153a5622ceeb38e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff010000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000200000003124d696e657220666f7220646174612067656e080100000000000000080000000000000000", + "62645231c0267327a3aa0ddd27ec506d16490e9ee75cddb7cab8f41a09912f8701": "01000000feb8385e00000000769997955a1dde6ea267844c666d4b55b5e07e6087b77de3dcc22d03aa9193a0c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca093000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018fee165d5f7f38080dff98a724a4ca7e17c2f53a7a955e1536547a29017efc49af695dd6a0e58ce76dc671f2183b36ab425fc895998d71c6e980b279c786c6b00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff320000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003300000003124d696e657220666f7220646174612067656e083200000000000000080000000000000000", + "626531739e2aab343af2a3ce3fd834daab6e79fbae29bd05088b2128518a5b0560": "01000000b6c4385e000000005be82989b3d3a6455926d6a3655cfe16af16e08982b63bcfaa131c4783191e70b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000653997d80b33bd0033e78b02e02f35eb5b3a361051bd84b81408c53445745805c87081223549004f4fee9cc21324ba49f2a3b511b1e6bf19731996973279471c00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff370000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003800000003124d696e657220666f7220646174612067656e083700000000000000080000000000000000", + "62676e176ded401273ba3a89dc7a03bdc6d79ec86ea06b4e8b5fc2d7b2cc773644": "000000006ed0385e0000000060adae5d095140f61294db960fbbe9f951699adf0f6dc7603ce17e62bd8a28e6b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006379b6dbf1ca95b66768e7e80a111bb6889f63d80e5e127a45805ea4dbd63bc9330a65a190909e8a8a97985686aa71d28dfe5ecf8da585d7d966431934bb7c3500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003d00000003124d696e657220666f7220646174612067656e083c00000000000000080000000000000000", + "6269d3d31f6229c9897d3062af52f286ad9ae35bb9074101a46a123076664b211b": "000000002e98385e000000002410e9397d0e94ecb04f1dc26f295884f71621c0a38273b455fc4408beefabdd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b4a56e342513350812ec1166791f76c96e3b2391060474db6f11b892d62c5aefa8607ed5d859a8a6864b1dc4c3e453049589a42eb8f1dfdb6af537481ae6e1e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff240000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002500000003124d696e657220666f7220646174612067656e082400000000000000080000000000000000", + "626a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "000000007e48385e0000000061c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac280bf1231e5aeaee10ce20f93393769452cf00abc404ac83ebcda710cc3dcb171727d58f15b3e6666de2efbe6c5a57de0ed3f64ac6a1cbd5d9800a2d2e974300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000300000003124d696e657220666f7220646174612067656e080200000000000000080000000000000000", + "626cca43f36eb13b2a6af73e9185e083a84426cbe8d16094f62fb158adcb542970": "02000000768c385e0000000013a3e4ac9804e9d23581e39f177b9e14ec60a07230e8fe3bfa3434ac2d80e526000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b4954135e3bec9fdd8b130d394dc26562da62f533700308aa045757f779f64cb4d8b2b501a32f75115be80f974b76eb6d7ed9a9eccfc136eb5942dbe9e65d2800000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002000000003124d696e657220666f7220646174612067656e081f00000000000000080000000000000000", + "626d24d44368cbce5de1d73e1b25364511432be27ca7c6aef54a5c79705171d38a": "0000000046ad385e0000000077b13357c3c68f1c1fb0f4967715ceb9d4dcb6fe686379c3a77fd1404de37aa4c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca09300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6a32cc6d71901696955b0eb6e50fdc5cee79817545b9035a18a9fae4c9d77d4d80f04db7015294e9aaab1f090bee88549631359abe550fc1bdb3d7850d4a8e00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002e00000003124d696e657220666f7220646174612067656e082d00000000000000080000000000000000", + "62735b9e424057caccb7ceb21bd56a45b42809c3e580af13cfe2fe06e113b23f6f": "000000008ea1385e0000000003fd277eb7864b864c6d4d1df862e37a2413fa9a9f1e4a8ca0d6d8a08f2e73da000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf8c0f64a5e3f2dd9e388ab2d54fa5b528548b766ad721057cb808127ce76d895c98e5f3282b21f0267ea9da585654e061825525757e57619bcf057900c28300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2800000001629b3577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002900000003124d696e657220666f7220646174612067656e0828000000000000000800000000000000000000000001f09e1b55be758049061eb1c5667d55f83ea2a5f85770521169545c0a8e70234d00000000ffffffff02000000000000000000142f6d31b494bdfa42034719ca28894496cdb8935902032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504000000000a6e7970767a766c7868619e8c35770000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000241ba9d3c7c0671b34249000b8f0d1d14e0bc84ff2e8a86c010c7c03beadcbf623b01b1fae5f06884bc8d675707901f748c77fa20e933773b3b74e3e41ab4c4c512012102681296e4f1c052e411977562f6e160d3fca1c6a2ae83e4d0ff3708a65bb5f7f2", + "6274aeedf1bd09cde9bfdffcf3906c928a9629d28bc54d55ed6f590e001248d2ad": "00000000c687385e000000005c9f0b068ad51164b01f78e45bb86dd738e13a8d56552889639c01c481efcd4f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa8d227f12e1ffc326c92e376be83ae054b5e47fce08d82f37cf871cab641229254915b59b8273cd840b1687a1aa807bba79c415632f404685a239b2b0bb166500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1d0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300001e00000003124d696e657220666f7220646174612067656e081d00000000000000080000000000000000", + "6274b29e4d16a90a69e50029d222509684e1d65b215aa4d989b5c44292e56d5571": "0200000066c9385e0000000052542bb7d5411695c2cfe7617afbe7475a72b268de11883ff8d9c9bfcb336ae7b19ece0d3ac02122874ffe0b9274a96ef20f1b0adf1f710a5cdc050ca5c2abb40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b82f4e0bfd6e465d4dd8dfb28b3d699475078cf15bd7906b9e70da9bb7e359802dedd6dc4fc79da84db23eafda55f7ce77fb7075addc0a1f25b2fb0c052b2cdc00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff390000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003a00000003124d696e657220666f7220646174612067656e083900000000000000080000000000000000", + "62769997955a1dde6ea267844c666d4b55b5e07e6087b77de3dcc22d03aa9193a0": "00000000a6b6385e000000004dd4b93fee39400ba4e05e8b2ea5afdb2ce547cb53bad25cc5835b4d92f7d2c8c0e4ca6efc200c7b77fdc7c67a5a47c250f14d0942ffe5b73cf9a592b3fca09300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006bb54e939e44c64861185dcbe35332cab369e59953053e9878ed6e88e492e99009423890b236998eb6f1083e7097cc9119c47717e286da2aee05f7b57f34c42f00000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff310000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300003200000003124d696e657220666f7220646174612067656e083100000000000000080000000000000000", + "6277b13357c3c68f1c1fb0f4967715ceb9d4dcb6fe686379c3a77fd1404de37aa4": "00000000eeaa385e0000000041764d19cb289d8d17877a438d07e892b68058d18b3aeb696c13de53deee153a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7cee0aaba7635dc82c496778cb4042a806a01eeedff3452d47b884ab214beaad1a5cf1c2a9dbc96abc8c39ec15a07fda8b677d91e28842c8a37ae985986040300000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2c0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002d00000003124d696e657220666f7220646174612067656e082c00000000000000080000000000000000", + "6277cb23bcf8547925555a5ee305af5c7b1d7c5d9f0004f5311b12cdf5a99c6574": "010000003ea6385e00000000151b3be5f32e0324ba6677a85b6f6bf64240b109bc3ea2972902c19218ad0ebf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6110cbb59b73bc95f31484b27ce9f42a19f662ea61c0ede9db476d6043f3865dfdd9e33d38e51d4542f39e07fd8fd27c28a46a74363f1f37bb467a2086863c400000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2a0000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300002b00000003124d696e657220666f7220646174612067656e082a00000000000000080000000000000000", + "62796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "00000000e658385e0000000052de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0c13adfe3ae131ba76a105a819e3a11bfd4aefc66c537aa54567f766ee3bb0c57d1d97ddbd3641748c818adf8967b100a5a3b480a029ccbb077ed50e7455ae100000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffff090000000100943577000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000a00000003124d696e657220666f7220646174612067656e080900000000000000080000000000000000", + "62ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "000000007841385e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a2c60b9439206938f8d7823782abdb8b211a57431e9c9b6a6365d8d428933518e4c9756fef2ad10375f360e0560fcc7587eb5223ddf8cd7c7e06e60a1140b1500000000ffff7f2000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff01d04c5777000000000014f0237ae2e8f860f7d79124fc513f012e5aaa8d23000000000000042050b8937fc5def08f9f3cbda7e5f08c706edb80aba5880c000000000000000000202d5de58609d4970fb548f85ad07a87db40e054e34cc81c951ca995a58f674db72010d748eda1b9c67b94d3244e0211677618a9b4b329e896ad90431f9f48034bad20e2c0299a1e466773516655f09a64b1e16b2579530de6c4a59ce5654dea45180f", + "753133af956ccf659c54198db2e47fe5ba38988e73010355dd4b300734a7c41d1c": "020000000068000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5004032088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce65042900000020a45d60f2b830394a706d2ffa1d27f22d46ba2ce93ef0c97973c1647d954ddc040009000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "753b22bafe7f2b6605f14d99eba22919544b3df99277491c129a7cabdcdc33c8c3": "02000000007e000000fd10270014b27ecd0a361b0fe9acafecdc47841e021a4ebf5006042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000301020320ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5000b000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "754180159d8865229e71727ccbaae7462d0dca3ce29a382ee24c58f726fa4c5587": "02000000005e000000fd204e0014b27ecd0a361b0fe9acafecdc47841e021a4ebf5003042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c78686120989b0e9f812857e4b4656d176fbe48924ce1e1c3f12c2083c6c3e7c7daf792df005e000000fd204e0014cc2953e1188c162d1689206ce79fabdd54c0a57503042088c5d7c6b74ab0478282d225de1c5913774a690774e6b634906c6340c94cce6504290000000a6e7970767a766c7868612065b8da249d07dae85945e1f21ca58042e709dbd061d242e45c4568079cac22fe", + "754670eb9a36ddab3606440b78aeb61c2047466f23fad806f1b457ff9fa21d1247": "020000000005000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa300000007000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000", + "75735b9e424057caccb7ceb21bd56a45b42809c3e580af13cfe2fe06e113b23f6f": "010000000003000000fe009435770014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa30000" + } +} diff --git a/test/data/migrations/chain-3-treestate-gen.js b/test/data/migrations/chain-3-treestate-gen.js new file mode 100644 index 000000000..9ada8433e --- /dev/null +++ b/test/data/migrations/chain-3-treestate-gen.js @@ -0,0 +1,207 @@ +'use strict'; + +/** + * Migration from v3 to v4 + */ + +const Logger = require('blgr'); +const Network = require('../../../lib/protocol/network'); +const Mempool = require('../../../lib/mempool/mempool'); +const Miner = require('../../../lib/mining/miner'); +const Chain = require('../../../lib/blockchain/chain'); +const MemWallet = require('../../util/memwallet'); +const HD = require('../../../lib/hd'); +// const rules = require('../../../lib/covenants/rules'); +const mutils = require('../../util/migrations'); + +const NETWORK = Network.get('regtest'); +let blockstore = null; + +try { + blockstore = require('../../../lib/blockstore'); +} catch (e) { + ; +} + +const wallet1priv = 'rprvKE8qsHtkmUxUSPQdn2sFKFUcKyUQz9pKQhxjEWecnXg9hgJMsmJXcw' + + 'J77SqmHT1R6mcuNqVPzgT2EoGStsXaUN92VJKhQWUB6uZdL8gAZvez'; + +let txID = 0; + +async function dumpMigration() { + const commonOptions = { + memory: true, + network: NETWORK, + logger: Logger.global + }; + + let blocks = null; + + if (blockstore) { + blocks = blockstore.create(commonOptions); + + await blocks.open(); + } + + const chain = new Chain({ + ...commonOptions, + entryCache: 5000, + blocks + }); + + const mempool = new Mempool({ + ...commonOptions, + chain + }); + + const miner = new Miner({ + ...commonOptions, + mempool, + chain + }); + + const master = HD.HDPrivateKey.fromBase58(wallet1priv, NETWORK); + const wallet = new MemWallet({ + network: NETWORK, + master + }); + + const address = wallet.getAddress(); + miner.addAddress(address); + + mempool.on('tx', (tx) => { + miner.cpu.notifyEntry(); + wallet.addTX(tx); + }); + + chain.on('connect', async (entry, block, view) => { + try { + await mempool._addBlock(entry, block.txs, view); + wallet.addBlock(entry, block.txs); + } catch (e) { + ; + } + }); + + chain.on('disconnect', async (entry, block) => { + try { + await mempool._removeBlock(entry, block.txs); + } catch (e) { + ; + } + }); + + await chain.open(); + await mempool.open(); + await miner.open(); + + miner.createBlock = async (tip, address) => { + return mutils.createBlock({ + txno: txID++, + chain, + miner, + tip, + address + }); + }; + + const mineBlock = async () => { + const block = await miner.mineBlock(chain.tip, address); + await chain.add(block); + }; + + // 10 blocks + for (let i = 0; i < 20; i++) { + await mineBlock(); + } + + // full auction from start to finish. + // const names = []; + // for (let i = 0; i < 10; i++) { + // names.push(rules.grindName(10, chain.tip.height + 1, NETWORK)); + // } + + const names = [ + 'hakhblkjfd', + 'bnfmvwgrzs', + 'zvfhqmuwog', + 'ovosxkjjmu', + 'qbcbwcvggr', + 'flvtbrbzun', + 'ovxogcczhi', + 'spmkswpciv', + 'wkhdfzwfmr', + 'wvstfqfuyq' + ]; + + for (const name of names) { + const openTX = await wallet.createOpen(name); + await mempool.addTX(openTX.toTX()); + } + + for (let i = 0; i < NETWORK.names.treeInterval + 1; i++) + await mineBlock(); + + for (const name of names) { + const bidTX1 = await wallet.createBid(name, 10000, 20000); + await mempool.addTX(bidTX1.toTX()); + const bidTX2 = await wallet.createBid(name, 10000, 20000); + await mempool.addTX(bidTX2.toTX()); + } + + for (let i = 0; i < NETWORK.names.biddingPeriod; i++) + await mineBlock(); + + for (const name of names) { + const reveal = await wallet.createReveal(name); + await mempool.addTX(reveal.toTX()); + } + + for (let i = 0; i < NETWORK.names.revealPeriod + 1; i++) + await mineBlock(); + + for (const name of names) { + const register = await wallet.createRegister(name, Buffer.from([1,2,3])); + await mempool.addTX(register.toTX()); + } + await mineBlock(); + + for (const name of names) { + const update = await wallet.createUpdate(name, Buffer.from([1,2,3,4])); + await mempool.addTX(update.toTX()); + } + await mineBlock(); + + const data = await getMigrationDump(chain); + + await miner.close(); + await mempool.close(); + await chain.close(); + + if (blocks) + await blocks.close(); + + return data; +} + +(async () => { + const full = await dumpMigration(); + + console.log(JSON.stringify({ + full + }, null, 2)); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); + +async function getMigrationDump(chain) { + const prefixes = [ + 'R', + 'h', + 's', + 'V' + ]; + + return mutils.dumpChainDB(chain.db, prefixes.map(mutils.prefix2hex)); +} diff --git a/test/data/migrations/chain-3-treestate.json b/test/data/migrations/chain-3-treestate.json new file mode 100644 index 000000000..df330557e --- /dev/null +++ b/test/data/migrations/chain-3-treestate.json @@ -0,0 +1,103 @@ +{ + "description": "Migration for tree state. (v3.x -> v4.0.0)", + "before": { + "52": "020c6dc2adf8984b7bf6ba3f7e7746ba45e616137a3b3c07b02a65695b8e587569000000000000004b0000000000000030368bf414000000a086010000000000", + "56": "636861696e02000000", + "73": "1c77828f60e2a6d83252efe603dcfa2991c7ec969f0f42ea3910b0db167c6281", + "68017855f147ed9905dabf680bc1de476c093daa13941d8b5ad57357de0a48b252": "1d000000", + "68020c6dc2adf8984b7bf6ba3f7e7746ba45e616137a3b3c07b02a65695b8e5875": "2c000000", + "68023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "05000000", + "6803017e78e79440234d3482e1f50a3973486b6d5391a0eba31bbcefd7972b6728": "1c000000", + "6806247d6dd4951c9a24dfa62808954f2a11ee8b23fe2a9aa0c4af98c7743103a2": "15000000", + "6806fccb3f5c64f5e885f7850aefb984eeb0f5087afbffd5071c7084bb0667a2b3": "2a000000", + "680d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "08000000", + "680f9b118ea311b01bb76ca07ccacc3566201d125e037b9502f546c142afbe631f": "1e000000", + "681448c8e0590f0d8040898e5d6af518680a0ddf279d053e900e9e7639d3d36e16": "26000000", + "68195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e": "10000000", + "6819621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69": "0d000000", + "681d32b130b81557dfad89ea0f3ffd4606d079e8ff1a7285419c3bca20147b1826": "19000000", + "681d4ae30ef787a7aeb926b2c19ac4c04ecca8bf352ef93af8ee8d9da3f69010c2": "17000000", + "681d7428daa5c999eeb3031c6da2344c3c62092963615f53b6baf3389b1f3bf70c": "1a000000", + "681ffa6d3e2039d5fc9d6b0018cd04c89bd6d261ee09d05a3639752170a5a1e508": "16000000", + "6820cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31": "0f000000", + "6821867e9f95fc91090e2f9523e715c88bcee22d92ebef8d0e842f2bf755d44ea6": "18000000", + "68218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "07000000", + "6821fd2ccb78408757d59eb1d84c7386511ff755b9515cf5890800a1ffcf72970c": "1f000000", + "6825ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f": "12000000", + "682ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "06000000", + "6841a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801": "0b000000", + "68430cbafda65ad334dd82ab6abdd61bd2f45bca7203aff99953a92c355ac118f4": "23000000", + "6848b783f086643d3542b8e2b188c56dd9b9659f8711f87e295308b6b45f66da63": "25000000", + "684ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972": "0c000000", + "684b950bc72d0599ba29e1d73f697d5c5e0c18c4dd2da59db25de5e5f5970c427d": "20000000", + "684ce8b9a57f6078397811102f1292946ebc31c4895f33f6192adcec2b38d6dbef": "28000000", + "684d05f932e0460934d3023e5269caaf9dfa99f024d976280e8e2025b16bf3b6b4": "22000000", + "685024e4f08095e52e7ffa377e466344e26e010cfe92d3173e1c2a87b94a9526ec": "27000000", + "68523487a92b1292d7161c642107f09ae4355f5244c347a7fd26ab2c068cf06700": "21000000", + "6852de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "09000000", + "68583527270d3c3f97a3c3ca9b566604afea4c6228bcebe17c3285a02d4f2a8f6c": "24000000", + "685a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "01000000", + "685a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "04000000", + "685ae465b2b22ef53dd660a9f7d166b065c535f597ae6ad059f9b68a4381d53fb4": "29000000", + "685fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575": "11000000", + "6861c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "02000000", + "6863a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f": "0e000000", + "6863b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac": "14000000", + "68680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a": "13000000", + "686a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "03000000", + "686bf81834ca9ed10cea6965cb1aed77aae4da4cf2d9fd95f8e6e426589ce33d2a": "1b000000", + "68796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "0a000000", + "687aefcc95412ab860e12ecd6b25488328685d9c8c165e927b24c3d0f3d1b99961": "2b000000", + "68ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "00000000" + }, + "after": { + "52": "020c6dc2adf8984b7bf6ba3f7e7746ba45e616137a3b3c07b02a65695b8e587569000000000000004b0000000000000030368bf414000000a086010000000000", + "56": "636861696e03000000", + "73": "1c77828f60e2a6d83252efe603dcfa2991c7ec969f0f42ea3910b0db167c628128000000000000000000000000000000000000000000000000000000000000000000000000000000", + "68017855f147ed9905dabf680bc1de476c093daa13941d8b5ad57357de0a48b252": "1d000000", + "68020c6dc2adf8984b7bf6ba3f7e7746ba45e616137a3b3c07b02a65695b8e5875": "2c000000", + "68023f611e564cd864139d8911f96a4d2b2676d782b57fbc3e49b6ae88e5b74f86": "05000000", + "6803017e78e79440234d3482e1f50a3973486b6d5391a0eba31bbcefd7972b6728": "1c000000", + "6806247d6dd4951c9a24dfa62808954f2a11ee8b23fe2a9aa0c4af98c7743103a2": "15000000", + "6806fccb3f5c64f5e885f7850aefb984eeb0f5087afbffd5071c7084bb0667a2b3": "2a000000", + "680d5bf49f21f6f1e5505331985fa155c29e2f9b13763fee7a8a1316c37035e637": "08000000", + "680f9b118ea311b01bb76ca07ccacc3566201d125e037b9502f546c142afbe631f": "1e000000", + "681448c8e0590f0d8040898e5d6af518680a0ddf279d053e900e9e7639d3d36e16": "26000000", + "68195950197389e97252ec48b5c7cc8e03b24cfe5ae3aea874b184b51df68cac2e": "10000000", + "6819621f4cb1d7781a0a7aa7389df218cdf339a53a52700e3319f9b173fb2c0f69": "0d000000", + "681d32b130b81557dfad89ea0f3ffd4606d079e8ff1a7285419c3bca20147b1826": "19000000", + "681d4ae30ef787a7aeb926b2c19ac4c04ecca8bf352ef93af8ee8d9da3f69010c2": "17000000", + "681d7428daa5c999eeb3031c6da2344c3c62092963615f53b6baf3389b1f3bf70c": "1a000000", + "681ffa6d3e2039d5fc9d6b0018cd04c89bd6d261ee09d05a3639752170a5a1e508": "16000000", + "6820cac350b9461cfe68a2dc8ec73c8b1654f9267b4476d473e33dd0d2c94f3f31": "0f000000", + "6821867e9f95fc91090e2f9523e715c88bcee22d92ebef8d0e842f2bf755d44ea6": "18000000", + "68218bd044eb749f2979ee8a525c25605ced06bfc6aed615888946b1ca22e98b80": "07000000", + "6821fd2ccb78408757d59eb1d84c7386511ff755b9515cf5890800a1ffcf72970c": "1f000000", + "6825ddc9baf064bfa5ac890f05c7dc7ac973459202ed1353d45589c05c8594165f": "12000000", + "682ccad4f238f5f0296f138c2ba98ba74f6219d15dea2fa0e2a8be94d1c900eb81": "06000000", + "6841a86791f3112f18d886d893e15bf7629adaa59fade58d3d98c9cbe78798a801": "0b000000", + "68430cbafda65ad334dd82ab6abdd61bd2f45bca7203aff99953a92c355ac118f4": "23000000", + "6848b783f086643d3542b8e2b188c56dd9b9659f8711f87e295308b6b45f66da63": "25000000", + "684ac1a9239ef18a09572c2b1e2ac681e38052bb918b976f189df685a1714e9972": "0c000000", + "684b950bc72d0599ba29e1d73f697d5c5e0c18c4dd2da59db25de5e5f5970c427d": "20000000", + "684ce8b9a57f6078397811102f1292946ebc31c4895f33f6192adcec2b38d6dbef": "28000000", + "684d05f932e0460934d3023e5269caaf9dfa99f024d976280e8e2025b16bf3b6b4": "22000000", + "685024e4f08095e52e7ffa377e466344e26e010cfe92d3173e1c2a87b94a9526ec": "27000000", + "68523487a92b1292d7161c642107f09ae4355f5244c347a7fd26ab2c068cf06700": "21000000", + "6852de5b96511c125f8f8ddc25f5f55221aa007fa3c363195158ad840af5d1882c": "09000000", + "68583527270d3c3f97a3c3ca9b566604afea4c6228bcebe17c3285a02d4f2a8f6c": "24000000", + "685a5c8af161645d726552e891ebdac4ed23eece5bb309aca798079a2b631091a3": "01000000", + "685a8bd214a5d785d73a0b347e58d7dda7da6469dbf2eb1477bc2f83cd3d782135": "04000000", + "685ae465b2b22ef53dd660a9f7d166b065c535f597ae6ad059f9b68a4381d53fb4": "29000000", + "685fd00d965676a8d3fba3f812a506d2860898d4a30d88dde7e2876cf95be18575": "11000000", + "6861c48314290fc7c81a9dfd98ce03d2d943f70d8ceafa8b02e20b7a9bad5558ed": "02000000", + "6863a865e5ead9cba63b0bde0dede246edb05b05abbb888ac4faa35bcebd640f6f": "0e000000", + "6863b14a4f67a3faccdeed987c6f16af5d1960205dd302b77eb1eeba51eed45fac": "14000000", + "68680d90fc380c5f1ee9c5a613f519a8a1d2d9dc241c69e45fe80259ec3ed7f86a": "13000000", + "686a26766301d7354cde3c996bd14ae43d8d2a14e24ba9727a6ceb995b71becd49": "03000000", + "686bf81834ca9ed10cea6965cb1aed77aae4da4cf2d9fd95f8e6e426589ce33d2a": "1b000000", + "68796897402c6a47e489cc9ecd05bc8352882f271d9ff9fb5692d18992516d1a2e": "0a000000", + "687aefcc95412ab860e12ecd6b25488328685d9c8c165e927b24c3d0f3d1b99961": "2b000000", + "68ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5": "00000000" + } +} diff --git a/test/data/migrations/wallet-0-migrate-migrations-gen.js b/test/data/migrations/wallet-0-migrate-migrations-gen.js new file mode 100644 index 000000000..3c69ec9b8 --- /dev/null +++ b/test/data/migrations/wallet-0-migrate-migrations-gen.js @@ -0,0 +1,38 @@ +'use strict'; + +/** + * walletdb version 0 to 1 migration. + * Final migration data needs modifications for + * the correct next migration value. + */ + +const Network = require('../../../lib/protocol/network'); +const WalletDB = require('../../../lib/wallet/walletdb'); +const mutils = require('../../util/migrations'); + +const NETWORK = Network.get('regtest'); + +(async () => { + const wdb = new WalletDB({ + network: NETWORK, + memory: true + }); + + await wdb.open(); + console.log(JSON.stringify({ + data: await getMigrationDump(wdb) + }, null, 2)); + + await wdb.close(); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); + +async function getMigrationDump(wdb) { + const prefixes = [ + 'M' + ]; + + return mutils.dumpDB(wdb, prefixes.map(mutils.prefix2hex)); +} diff --git a/test/data/migrations/wallet-0-migrate-migrations.json b/test/data/migrations/wallet-0-migrate-migrations.json new file mode 100644 index 000000000..37020a660 --- /dev/null +++ b/test/data/migrations/wallet-0-migrate-migrations.json @@ -0,0 +1,21 @@ +{ + "description": "Migrate migrations. Affects wdb layout M.", + "cases": [ + { + "description": "Migration after migration flag was set.", + "before": { + "4d00000000": "00" + }, + "after": { + "4d": "000000000200" + } + }, + { + "description": "Migration before flag was set.", + "before": {}, + "after": { + "4d": "000000000200" + } + } + ] +} diff --git a/test/data/migrations/wallet-1-change-gen.js b/test/data/migrations/wallet-1-change-gen.js new file mode 100644 index 000000000..2d71d63cc --- /dev/null +++ b/test/data/migrations/wallet-1-change-gen.js @@ -0,0 +1,201 @@ +'use strict'; + +/** + * This migration is checked against v2.1.3 - which is buggy version. + * And then checked with v2.2.0 - which has fix and migration, + * but run w/o migratoin. + * + * This generates two wallets with 2 accounts each. + * Each account with 1 tx each that receives 1 change output that goes to + * the LOOKAHEAD - 1 address. Lookahead for this test is set to 10. + */ + +const bdb = require('bdb'); +const Network = require('../../../lib/protocol/network'); +const WalletDB = require('../../../lib/wallet/walletdb'); +const wutils = require('../../util/wallet'); +const mutils = require('../../util/migrations'); + +const NETWORK = Network.get('regtest'); + +const wallet1priv = 'rprvKE8qsHtkmUxUSPQdn2sFKFUcKyUQz9pKQhxjEWecnXg9hgJMsmJXcw' + + 'J77SqmHT1R6mcuNqVPzgT2EoGStsXaUN92VJKhQWUB6uZdL8gAZvez'; +const wallet2priv = 'rprvKE8qsHtkmUxUSR4jE7Lti9XV77hv7xxacAShw5MvxY6RfsAYVeB1WL' + + 'WtjiebDmqTruVJxmMeQUMkk61e83WDZbZidDnNPhHyQpeEwxjuSZuG'; +const rpub = 'rpubKBAPj83PkcGYSz3GR9xfzXBfTFTPtERb2x7fKvbikH9utGMvr6HDd4sTxt5zo' + + 'arw4bzzgH1VDzBUoX9fotzmPrrngFyLMz3ozAi1ozAbJjSY'; + +const layout = { + wdb: { + // W[wid] -> wallet id + W: bdb.key('W', ['uint32']), + + // w[wid] -> wallet + w: bdb.key('w', ['uint32']), + + // l[id] -> wid + l: bdb.key('l', ['ascii']), + + // n[wid][index] -> account name + n: bdb.key('n', ['uint32', 'uint32']), + + // a[wid][index] -> account + a: bdb.key('a', ['uint32', 'uint32']), + + // p[addr-hash] -> address->wid map + p: bdb.key('p', ['hash']), + + // P[wid][addr-hash] -> path data + P: bdb.key('P', ['uint32', 'hash']), + + // r[wid][index][addr-hash] -> dummy (addr by account) + r: bdb.key('r', ['uint32', 'uint32', 'hash']) + } +}; + +const LOOKAHEAD = 10; + +(async () => { + const wdb = new WalletDB({ + network: NETWORK, + memory: true + }); + + await wdb.open(); + + const wallet1 = await wdb.create({ + id: 'wallet1', + master: wallet1priv, + lookahead: LOOKAHEAD + }); + + await wallet1.createAccount({ + name: 'alt1', + lookahead: LOOKAHEAD + }); + + const wallet2 = await wdb.create({ + id: 'wallet2', + master: wallet2priv, + lookahead: LOOKAHEAD, + m: 1, + n: 2 + }); + + await wallet2.addSharedKey(0, rpub); + + await mineBlocks(wdb, 100); + + for (let i = 0; i < LOOKAHEAD; i++) { + // Derive receives to get deterministic outputs. + await wallet1.createReceive(0); + await wallet1.createReceive(1); + await wallet2.createReceive(0); + + await wallet1.createChange(0); + await wallet1.createChange(1); + await wallet2.createChange(0); + } + + // const accounts = [ + // await wallet1.getAccount(0), + // await wallet1.getAccount(1), + // await wallet2.getAccount(0) + // ]; + + // const lookaheadHashes = []; + + // for (const acc of accounts) { + // const addr = await acc.deriveChange(LOOKAHEAD * 2); + // lookaheadHashes.push(addr.getHash()); + // } + + // let hasKey = false; + // for (const hash of lookaheadHashes) { + // const map = await wdb.getPathMap(hash); + // if (map) { + // hasKey = true; + // break; + // } + // } + + const {beforeOnly, filtered} = await getMigrationDump(wdb); + const data = { + beforeOnly: beforeOnly, + data: filtered + }; + console.log(JSON.stringify(data, null, 2)); +})().catch((err) => { + console.error(err.stack); + process.exit(1); +}); + +async function mineBlocks(wdb, n) { + for (let i = 0; i < n; i++) { + const entry = wutils.nextEntry(wdb); + await wdb.addBlock(entry, []); + } +}; + +async function getMigrationDump(wdb) { + const HASH = Buffer.alloc(32); + const beforeOnlyPrefixes = []; + + const prefixes = [ + layout.wdb.p.encode(HASH).slice(0, 1).toString('hex'), + layout.wdb.n.encode(0, 0).slice(0, 1).toString('hex'), + layout.wdb.l.encode('wid').slice(0, 1).toString('hex') + ]; + + // SKIP Primary wallet. + for (let i = 1; i < 3; i++) { + prefixes.push(layout.wdb.a.encode(i, 0).slice(0, 5).toString('hex')); + prefixes.push(layout.wdb.P.encode(i, HASH).slice(0, 5).toString('hex')); + prefixes.push(layout.wdb.r.encode(i, 0, HASH).slice(0, 5).toString('hex')); + prefixes.push(layout.wdb.W.encode(i).toString('hex')); + prefixes.push(layout.wdb.w.encode(i).toString('hex')); + } + + beforeOnlyPrefixes.push(layout.wdb.a.encode(0, 0).slice(0, 5).toString('hex')); + beforeOnlyPrefixes.push(layout.wdb.P.encode(0, HASH).slice(0, 5).toString('hex')); + beforeOnlyPrefixes.push(layout.wdb.r.encode(0, 0, HASH).slice(0, 5).toString('hex')); + beforeOnlyPrefixes.push(layout.wdb.W.encode(0).toString('hex')); + beforeOnlyPrefixes.push(layout.wdb.w.encode(0).toString('hex')); + + const dump = await mutils.dumpDB(wdb, prefixes); + const dumpBeforeOnly = await mutils.dumpDB(wdb, beforeOnlyPrefixes); + const beforeOnly = dumpBeforeOnly; + + const filtered = {}; + for (const [key, value] of Object.entries(dump)) { + // This is maprecord for primary wallet. We don't want primary wallet. + // 4 byte (size of wid set) + 4 byte * (size of wid set) (Small Endian) + // 1 size + 0 wid. + if (key.startsWith('70') && value === '0100000000000000') { + beforeOnly[key] = value; + continue; + } + + // Maprecord for performance is not deserialized and instead data is appended + // to it. This can cause duplicate indexes in the dump. So manually fix that + // for the migration. + if (key.startsWith('70')) { + if (value === '020000000100000001000000') { + filtered[key] = '0100000001000000'; + continue; + } + + if (value === '020000000200000002000000') { + filtered[key] = '0100000002000000'; + continue; + } + } + + filtered[key] = value; + } + + return { + filtered, + beforeOnly + }; +} diff --git a/test/data/migrations/wallet-1-change.json b/test/data/migrations/wallet-1-change.json new file mode 100644 index 000000000..d10f608e6 --- /dev/null +++ b/test/data/migrations/wallet-1-change.json @@ -0,0 +1,771 @@ +{ + "beforeOnly": { + "500000000014078c75fc899a9522eaac0ab5484f814159cd030f": "0000000000000100000005000000", + "500000000014081ce653ab7640bfb31d716def39cfd5a7ff0a52": "0000000000000000000003000000", + "50000000001415a25350291187b15580d3d0a18ceec5b7969305": "0000000000000100000002000000", + "50000000001419eef9d871d658cae10c4ade4a7064dc8b538b30": "0000000000000000000007000000", + "5000000000142c3426996014db7fc7c7ce953ed1c2f5a0a0b49b": "0000000000000000000000000000", + "500000000014349627c832e62b010297fc2a0698dd006dbfcfd0": "0000000000000000000005000000", + "5000000000144646243902084a1d1864f6fa0d9f045bb92334fa": "000000000000000000000a000000", + "50000000001446eec5df1d34e07b46611be0cd8788f3c8065356": "0000000000000000000008000000", + "50000000001448a163c1613bfcaf6bb0331ccb5498aa0defd912": "0000000000000100000003000000", + "500000000014496b2467e53a9b2a170ad0f0b08dafc3f72b06c6": "0000000000000000000001000000", + "5000000000145eba33b50da8299d4857baeae79b29b73810703d": "0000000000000000000009000000", + "5000000000145ffb080135704e063368d148e5e5e3e16cc34f86": "0000000000000100000007000000", + "500000000014619468975e93235e749e4ed895a15d736f805ac6": "0000000000000000000006000000", + "500000000014631c8719e73957475f66f632dbac94c526ac9248": "0000000000000100000009000000", + "500000000014b1e56edd12c9ea3eca5e7128292e89954c8e993a": "000000000000010000000a000000", + "500000000014b284bab5ed7eaad2d389807824f5a97195521ff3": "0000000000000000000004000000", + "500000000014b8a2a210a56954ff397f8cc0770442453797a491": "0000000000000000000002000000", + "500000000014c3b56321bb142984a1f7b408c699e177079a1fab": "0000000000000100000001000000", + "500000000014d8cf398baea8873a7ade2dc9d0b7a9bce24d9676": "0000000000000100000008000000", + "500000000014e2cb7a2c96bbb70cade230fc2ba66f67b5e78a03": "0000000000000100000004000000", + "500000000014ebbb6926a7bdb679dea385cf697e0923f6ac994d": "0000000000000100000000000000", + "500000000014ff292b7001afa90137d231e9e72e040c42f91c77": "0000000000000100000006000000", + "5700000000": "077072696d617279", + "610000000000000000": "0100010101000000010000000a03c454535d8000000006d65d23d4e9353be6994b792cc84668b9ec0d4825285866ae89eb076fa26b3f036e88bca49e73a56c90d5a3557daa8432fe052b8439c407b7e685239332f60e4200", + "72000000000000000014078c75fc899a9522eaac0ab5484f814159cd030f": "00", + "72000000000000000014081ce653ab7640bfb31d716def39cfd5a7ff0a52": "00", + "7200000000000000001415a25350291187b15580d3d0a18ceec5b7969305": "00", + "7200000000000000001419eef9d871d658cae10c4ade4a7064dc8b538b30": "00", + "720000000000000000142c3426996014db7fc7c7ce953ed1c2f5a0a0b49b": "00", + "72000000000000000014349627c832e62b010297fc2a0698dd006dbfcfd0": "00", + "720000000000000000144646243902084a1d1864f6fa0d9f045bb92334fa": "00", + "7200000000000000001446eec5df1d34e07b46611be0cd8788f3c8065356": "00", + "7200000000000000001448a163c1613bfcaf6bb0331ccb5498aa0defd912": "00", + "72000000000000000014496b2467e53a9b2a170ad0f0b08dafc3f72b06c6": "00", + "720000000000000000145eba33b50da8299d4857baeae79b29b73810703d": "00", + "720000000000000000145ffb080135704e063368d148e5e5e3e16cc34f86": "00", + "72000000000000000014619468975e93235e749e4ed895a15d736f805ac6": "00", + "72000000000000000014631c8719e73957475f66f632dbac94c526ac9248": "00", + "72000000000000000014b1e56edd12c9ea3eca5e7128292e89954c8e993a": "00", + "72000000000000000014b284bab5ed7eaad2d389807824f5a97195521ff3": "00", + "72000000000000000014b8a2a210a56954ff397f8cc0770442453797a491": "00", + "72000000000000000014c3b56321bb142984a1f7b408c699e177079a1fab": "00", + "72000000000000000014d8cf398baea8873a7ade2dc9d0b7a9bce24d9676": "00", + "72000000000000000014e2cb7a2c96bbb70cade230fc2ba66f67b5e78a03": "00", + "72000000000000000014ebbb6926a7bdb679dea385cf697e0923f6ac994d": "00", + "72000000000000000014ff292b7001afa90137d231e9e72e040c42f91c77": "00", + "7700000000": "0001000000204583bb94ad56a44b20aa58be59138bf8d20efac758980a53ccfd13851baaff0000000000507c507ac06795b8ee90ef90f66b104abd7b756072e9168daa2d792bfc0665c87a5ee90f534e70d19f40e2b1986a67be26ff18486ffbe49d0b605bff9cbe082f010001025d46d5438bf34da17e9604cb2bfd90593109951a4480a0c27ae40bd44e8c2788", + "7014078c75fc899a9522eaac0ab5484f814159cd030f": "0100000000000000", + "7014081ce653ab7640bfb31d716def39cfd5a7ff0a52": "0100000000000000", + "701415a25350291187b15580d3d0a18ceec5b7969305": "0100000000000000", + "701419eef9d871d658cae10c4ade4a7064dc8b538b30": "0100000000000000", + "70142c3426996014db7fc7c7ce953ed1c2f5a0a0b49b": "0100000000000000", + "7014349627c832e62b010297fc2a0698dd006dbfcfd0": "0100000000000000", + "70144646243902084a1d1864f6fa0d9f045bb92334fa": "0100000000000000", + "701446eec5df1d34e07b46611be0cd8788f3c8065356": "0100000000000000", + "701448a163c1613bfcaf6bb0331ccb5498aa0defd912": "0100000000000000", + "7014496b2467e53a9b2a170ad0f0b08dafc3f72b06c6": "0100000000000000", + "70145eba33b50da8299d4857baeae79b29b73810703d": "0100000000000000", + "70145ffb080135704e063368d148e5e5e3e16cc34f86": "0100000000000000", + "7014619468975e93235e749e4ed895a15d736f805ac6": "0100000000000000", + "7014631c8719e73957475f66f632dbac94c526ac9248": "0100000000000000", + "7014b1e56edd12c9ea3eca5e7128292e89954c8e993a": "0100000000000000", + "7014b284bab5ed7eaad2d389807824f5a97195521ff3": "0100000000000000", + "7014b8a2a210a56954ff397f8cc0770442453797a491": "0100000000000000", + "7014c3b56321bb142984a1f7b408c699e177079a1fab": "0100000000000000", + "7014d8cf398baea8873a7ade2dc9d0b7a9bce24d9676": "0100000000000000", + "7014e2cb7a2c96bbb70cade230fc2ba66f67b5e78a03": "0100000000000000", + "7014ebbb6926a7bdb679dea385cf697e0923f6ac994d": "0100000000000000", + "7014ff292b7001afa90137d231e9e72e040c42f91c77": "0100000000000000" + }, + "before": { + "5000000001140441cbe166229b9e2adb6aa90751052523cb298d": "0000000000000000000008000000", + "500000000114045216fd9b9f0aeff2be3c18327c3083fc4ffd4a": "0100000000000000000012000000", + "50000000011404d0a093756a7da12d94f6f5cc7bbd6b1a25505a": "010000000000010000000a000000", + "5000000001140c20cf0e2a2e0ea22b4214a16e76c93aa59510d2": "0100000000000100000008000000", + "500000000114198a5f555dc8f78bcc35bf23baefc03c954e7053": "0000000000000000000011000000", + "5000000001141b3c16c74f65720cd520148e0557a3e057550ae2": "0000000000000100000008000000", + "5000000001141c28474c2fbd6281a6d536218c8ca7b4e79691c0": "0100000000000000000010000000", + "5000000001141fcdaa5bd5efa8811106f6390e5ecc327083426b": "0000000000000100000001000000", + "500000000114222740511246fe2b10957049ae10e0bbe7fa996c": "0000000000000100000002000000", + "5000000001142667d9e317cf8f0aac2543fd27f38e0ab9da3ecb": "0100000000000000000005000000", + "500000000114292e9aae355f8cd57ec7e5aedb49cf19a1c30a73": "0000000000000100000007000000", + "5000000001142a52b414589aca0f9e6b79be9563e3e0b8e053aa": "000000000000000000000e000000", + "5000000001142cd7a7b49b1013603d53b6a234493ee120847175": "0100000000000000000002000000", + "5000000001142dcae735994e7802bc103da010633db1a32b18dc": "0000000000000000000010000000", + "5000000001142f6d31b494bdfa42034719ca28894496cdb89359": "0000000000000000000003000000", + "500000000114309a0821ae883849b41aeb44c24b075af338b8c0": "0100000000000000000004000000", + "50000000011431fcd6dd84b2bd8d450bca497b035e9e44e43418": "0100000000000100000009000000", + "500000000114355379bd23a29cfc1457908f7fe1e30d22efd007": "0100000000000100000007000000", + "50000000011441136683ea0485bbef46e9d9f6a2ff766128efad": "000000000000000000000f000000", + "500000000114433b4a5bdd977ac6ab958e07834a08fbd2e7f961": "0000000000000000000013000000", + "500000000114452b000bd69cd9c4a99322d1c41e972cdcd662fe": "010000000000000000000b000000", + "5000000001144931bd2996fb46b2f17a5f4bae7d0b2a04ee65bc": "0100000000000000000014000000", + "5000000001144c43158dc848068c85406c14957b0cab073f343b": "0000000000000000000006000000", + "5000000001144eca2ab57c2a64d356a40fab260140a48fb39ca4": "0100000000000000000011000000", + "5000000001144f9644e04b6bbc4f53ae49e444d005524cc1422f": "010000000000000000000c000000", + "5000000001145258eb6f21709d00f66fa39deab6e8d5391eae5f": "0100000000000100000001000000", + "5000000001145428d3cebe215a9f78bf3ca6d3ebc055b828f3d9": "0100000000000000000007000000", + "5000000001145c4c9a889c8102fce3f2d40149842898270f96d0": "0100000000000100000004000000", + "5000000001145cb324e109b920b19494549b3e540f57d43c4ef1": "010000000000000000000e000000", + "50000000011460976bb97754c7ab48bac4c894d27a4126bc23d1": "0000000000000100000005000000", + "5000000001146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54": "0100000000000000000001000000", + "500000000114646f81d8c9569f04c93d1559a606da8185cb49c1": "010000000000000000000a000000", + "500000000114657eb0831fd4fafd3e595163416d76861ea9acd9": "0100000000000000000000000000", + "50000000011466bdd1c0be1d2785d86469fd11dee568e293c0a0": "0100000000000100000006000000", + "5000000001146a94c2244bd9a4b5257a8c946b68af72f4118b35": "0000000000000000000002000000", + "5000000001146dddb81cee99d34e617876a2d4bd091fea7da844": "0100000000000000000006000000", + "5000000001147220144f51a799765a1cfebc74a6ff789e0cf098": "000000000000000000000a000000", + "5000000001147a98b6621e4ccc528560645f6a7682633a24fad8": "0000000000000100000006000000", + "5000000001147db1e3611d2409939fb385976712f032de7629ef": "0100000000000000000009000000", + "500000000114873c0013e79e492b0e9e6ecf631b4a2dfe5f1ed0": "0000000000000100000009000000", + "500000000114881668afa843bcf71d7717815f31a9c6c8f96d7f": "0100000000000100000005000000", + "5000000001148d375a0132f2077de9c4a96c66e15c24c5501851": "0000000000000000000004000000", + "5000000001149259252916ee5b5573f1f911ed49c99592b1db44": "000000000000000000000d000000", + "50000000011499b6d711eb6ba45313ed8ff37d9a8ec5df572750": "000000000000000000000b000000", + "5000000001149ae567da742bcd8894e5f0c789aa1a5a017396cc": "0000000000000100000003000000", + "5000000001149d03a12b2facd319be3b174ac49e78effae0f9fc": "000000000000000000000c000000", + "5000000001149df7d0326747749b602967421b892f1efe19fd73": "0000000000000000000014000000", + "500000000114a2affbcd20614676abcac636b5db306e96441121": "0100000000000000000013000000", + "500000000114a62c71694f8bf7583bfe73bd1b2235b81ee12558": "0000000000000100000000000000", + "500000000114a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec7": "0000000000000100000004000000", + "500000000114b27ecd0a361b0fe9acafecdc47841e021a4ebf50": "0000000000000000000005000000", + "500000000114b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3": "0000000000000000000001000000", + "500000000114bac3a07219bdb2d75cdebcf7e42a28c58bb41d79": "0100000000000100000003000000", + "500000000114c53ec93147b4fa7e1e1bf56f0edbdd319895e253": "0000000000000000000012000000", + "500000000114c674ba19506d9e2c178ab0eb4acd47b701192705": "0100000000000100000002000000", + "500000000114c9dfb2e5898b3432f8808d5aea4c3936c6db3888": "000000000000010000000a000000", + "500000000114cc2953e1188c162d1689206ce79fabdd54c0a575": "0000000000000000000007000000", + "500000000114cfe3810f02a4af8dd98092bb6650841bac0ff2c4": "0100000000000000000008000000", + "500000000114cffd1219803f5f43f72a2a8e841a2fefb0347657": "0000000000000000000000000000", + "500000000114d20cf3c6172ea38a0ec42514435978a76a99b1eb": "010000000000000000000d000000", + "500000000114e154c1b406841cc45d1d63fbd0c36003b5e34cc9": "0000000000000000000009000000", + "500000000114f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c": "0100000000000100000000000000", + "500000000114f4f1b2cd73eebe34bef87c174e5d4903e86493ff": "0100000000000000000003000000", + "500000000114f7c2c1cf022472febc2deb36c91ae2e450c36b29": "010000000000000000000f000000", + "500000000220014ae391c03cd9362e262b394c85ce06cea7ebd07c13f97506a839a1a11d651d": "000000000000000000000f000000", + "5000000002200230be98454e5b569c471677d182bfa226e02f382161dfba4808c814768fa53e": "0000000000000000000009000000", + "50000000022005a8ddf9b9d2f9444136255ca8bd90421fb7670ee5f3df010f13b6ee2c6c5cf7": "000000000000000000000a000000", + "5000000002201388e7721a373ad570a648fcf2b9b67bc1db13290ec4ff72ecd9bc28458c41a4": "000000000000000000000d000000", + "5000000002201b82351fd6ef197d346bb3792dd068cd30886b76b5fc6ca6e42eb85dd80231d2": "0000000000000100000000000000", + "500000000220218bcaf58e810fbb368ae372412933f8179cd26ac62a69c111b8fbc0d47846c1": "0000000000000100000002000000", + "5000000002203700fcb0cf9da3afd6b58ab12c98ecd92ee1d70e667a8135c5b43f13e3a934fb": "0000000000000000000003000000", + "5000000002203862684532dbfd6e87a5f3b2176f039ada4ab17300acd90dc33e294dfa74f2f0": "0000000000000000000004000000", + "500000000220391883de87eff56ee0ea8afe56a271bc3eefbee6391504532fb71ef53975aaad": "0000000000000100000007000000", + "50000000022039cf4d6f59eb602d1874642a27b0eede313ef13dc840a98869df40a27fb9fc27": "000000000000000000000c000000", + "5000000002203a5a5f9793853a5e2b33ccf9cb7e8311e4ccf8b42400bc9f543c0450f83660a0": "0000000000000000000014000000", + "5000000002203b6112f1ece55eaba9f4533ef15b8a7e2a28a3e945789ae0a3d52a7eebd149fa": "0000000000000100000005000000", + "5000000002203f25468e114c28631ef9a0b2142302a5e11ab4cbb08a6fece18b758230ec94b9": "0000000000000000000011000000", + "50000000022042ea2ba78922e99b8eadec8b0a7e44fcdbb2ebac2195dec45112dc91119c86e5": "0000000000000000000007000000", + "500000000220483f05b0be57c59d87ff0d6a44a61dc28b69745d5366bb46cab1e19e6976ec62": "0000000000000000000006000000", + "5000000002205d77fb0b2b6239c51750e30087a54d1f0cd50d88a9890a540f27e0867d55901c": "000000000000000000000e000000", + "5000000002205d7c541fd6af1b22dcd959d3721f8bb236df5b2d0e2c2731f3bb213dbee79771": "000000000000010000000a000000", + "5000000002205f36a17f56382c90a5223800cb9af46ca2e3ae366412a865d9b85e85bd3be0b8": "000000000000000000000b000000", + "5000000002207e287ea1c99bceb191a5de13159e108c2969a1b7101517078017e824d0d68bb6": "0000000000000000000002000000", + "500000000220924a9a1ec8f7c21f4d1dfc1503a27a38687808ea2199dca262150b1b149dd81b": "0000000000000100000009000000", + "500000000220951b5f6437ea0a0feed1935aa6c53f9337c5d3e63649a7b8143eb7c8050d63d0": "0000000000000000000005000000", + "500000000220a06bac796162ff65142d7e1be882db42492e7bd2e5f6803ddba20b49f6b77a91": "0000000000000000000013000000", + "500000000220a1bb3afd4e182e7fedfb8be7f67c264cf21d9a3560c6ae01eceb80e3a9939a7d": "0000000000000000000000000000", + "500000000220b7055255263baba965dcc8cd553af0d6d8fa8642bca55bddff52780d8a81e7f6": "0000000000000000000010000000", + "500000000220b879ff665513666627d900db039d4741bdd304f24bfe7bfd8987e298caaf3135": "0000000000000100000006000000", + "500000000220bed8310af201ca58abc475429fb9b92b9dbaac71ab915fbdcb448b6c3a11296a": "0000000000000100000003000000", + "500000000220c2d6e806851b1ab0411bb27ded2fb44dbe147cb19bd5bbc8d313894875aa749f": "0000000000000100000004000000", + "500000000220cf4a622bc2e507d60ac31a5e04ce858023acef83009ffbfd55d2677a0e74d3f2": "0000000000000000000012000000", + "500000000220d4102bb5808580bb47a1a0d5e07e8cf35fdff44457dfa39f1ddc2f0de19caa73": "0000000000000000000001000000", + "500000000220e5b1375a597f61dca88393bcb6d061fd29f80ac005ba522bb6ff4930326d5184": "0000000000000100000008000000", + "500000000220eff0443f2a5114012303803760e595f4c0bc9f5d8b81e64d1b7ac3ea124b47d6": "0000000000000000000008000000", + "500000000220f9dcf5ddd1748e63056e1c2b2afeff3a57c5289c26a8fd7c6c282171ee1c9251": "0000000000000100000001000000", + "5700000001": "0777616c6c657431", + "5700000002": "0777616c6c657432", + "610000000100000000": "010001010b0000000b0000000a03875c90a480000000fdc9691244b5dc0636ce0eabd905556c333e208b2815e463a6e65db29833c15d024a4400453207366ed8567bf78fdda5dccc1a0f8b1398f9925b744cc563575a7400", + "610000000100000001": "010001010b0000000b0000000a03875c90a480000001fc7d26586b3e6e082df9d01051258b8809960d5434d1abfe088ab507a0cfbbf003f44993e0ed4638fb5d32a272cca586ffe85040755d95568e01ca5c4845e27c9e00", + "610000000200000000": "010101020b0000000b0000000a034a0c1e1180000000bda10baaccd9189fb3a56e2950ad198c45611cd3dbf0f154f699fa1c9ddc4b89022e05837732c62feb49896bc8b3cd8fd2f1b9b6bc415074ba31d858a810bbb29401032ac4ec93800000008921a0a732ac09e78b4f51464880ca9fc3e09a129564c0d8aef9fb1fd2ff175302f2b8aebdc25d17815420662ff8e2b9e24202a9b628f07ce2671cedac5f8001e1", + "6c077072696d617279": "00000000", + "6c0777616c6c657431": "01000000", + "6c0777616c6c657432": "02000000", + "6e0000000000000000": "0764656661756c74", + "6e0000000100000000": "0764656661756c74", + "6e0000000100000001": "04616c7431", + "6e0000000200000000": "0764656661756c74", + "70140441cbe166229b9e2adb6aa90751052523cb298d": "0100000001000000", + "7014045216fd9b9f0aeff2be3c18327c3083fc4ffd4a": "0100000001000000", + "701404d0a093756a7da12d94f6f5cc7bbd6b1a25505a": "0100000001000000", + "70140c20cf0e2a2e0ea22b4214a16e76c93aa59510d2": "0100000001000000", + "7014198a5f555dc8f78bcc35bf23baefc03c954e7053": "0100000001000000", + "70141b3c16c74f65720cd520148e0557a3e057550ae2": "0100000001000000", + "70141c28474c2fbd6281a6d536218c8ca7b4e79691c0": "0100000001000000", + "70141fcdaa5bd5efa8811106f6390e5ecc327083426b": "0100000001000000", + "7014222740511246fe2b10957049ae10e0bbe7fa996c": "0100000001000000", + "70142667d9e317cf8f0aac2543fd27f38e0ab9da3ecb": "0100000001000000", + "7014292e9aae355f8cd57ec7e5aedb49cf19a1c30a73": "0100000001000000", + "70142a52b414589aca0f9e6b79be9563e3e0b8e053aa": "0100000001000000", + "70142cd7a7b49b1013603d53b6a234493ee120847175": "0100000001000000", + "70142dcae735994e7802bc103da010633db1a32b18dc": "0100000001000000", + "70142f6d31b494bdfa42034719ca28894496cdb89359": "0100000001000000", + "7014309a0821ae883849b41aeb44c24b075af338b8c0": "0100000001000000", + "701431fcd6dd84b2bd8d450bca497b035e9e44e43418": "0100000001000000", + "7014355379bd23a29cfc1457908f7fe1e30d22efd007": "0100000001000000", + "701441136683ea0485bbef46e9d9f6a2ff766128efad": "0100000001000000", + "7014433b4a5bdd977ac6ab958e07834a08fbd2e7f961": "0100000001000000", + "7014452b000bd69cd9c4a99322d1c41e972cdcd662fe": "0100000001000000", + "70144931bd2996fb46b2f17a5f4bae7d0b2a04ee65bc": "0100000001000000", + "70144c43158dc848068c85406c14957b0cab073f343b": "0100000001000000", + "70144eca2ab57c2a64d356a40fab260140a48fb39ca4": "0100000001000000", + "70144f9644e04b6bbc4f53ae49e444d005524cc1422f": "0100000001000000", + "70145258eb6f21709d00f66fa39deab6e8d5391eae5f": "0100000001000000", + "70145428d3cebe215a9f78bf3ca6d3ebc055b828f3d9": "0100000001000000", + "70145c4c9a889c8102fce3f2d40149842898270f96d0": "0100000001000000", + "70145cb324e109b920b19494549b3e540f57d43c4ef1": "0100000001000000", + "701460976bb97754c7ab48bac4c894d27a4126bc23d1": "0100000001000000", + "70146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54": "0100000001000000", + "7014646f81d8c9569f04c93d1559a606da8185cb49c1": "0100000001000000", + "7014657eb0831fd4fafd3e595163416d76861ea9acd9": "0100000001000000", + "701466bdd1c0be1d2785d86469fd11dee568e293c0a0": "0100000001000000", + "70146a94c2244bd9a4b5257a8c946b68af72f4118b35": "0100000001000000", + "70146dddb81cee99d34e617876a2d4bd091fea7da844": "0100000001000000", + "70147220144f51a799765a1cfebc74a6ff789e0cf098": "0100000001000000", + "70147a98b6621e4ccc528560645f6a7682633a24fad8": "0100000001000000", + "70147db1e3611d2409939fb385976712f032de7629ef": "0100000001000000", + "7014873c0013e79e492b0e9e6ecf631b4a2dfe5f1ed0": "0100000001000000", + "7014881668afa843bcf71d7717815f31a9c6c8f96d7f": "0100000001000000", + "70148d375a0132f2077de9c4a96c66e15c24c5501851": "0100000001000000", + "70149259252916ee5b5573f1f911ed49c99592b1db44": "0100000001000000", + "701499b6d711eb6ba45313ed8ff37d9a8ec5df572750": "0100000001000000", + "70149ae567da742bcd8894e5f0c789aa1a5a017396cc": "0100000001000000", + "70149d03a12b2facd319be3b174ac49e78effae0f9fc": "0100000001000000", + "70149df7d0326747749b602967421b892f1efe19fd73": "0100000001000000", + "7014a2affbcd20614676abcac636b5db306e96441121": "0100000001000000", + "7014a62c71694f8bf7583bfe73bd1b2235b81ee12558": "0100000001000000", + "7014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec7": "0100000001000000", + "7014b27ecd0a361b0fe9acafecdc47841e021a4ebf50": "0100000001000000", + "7014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3": "0100000001000000", + "7014bac3a07219bdb2d75cdebcf7e42a28c58bb41d79": "0100000001000000", + "7014c53ec93147b4fa7e1e1bf56f0edbdd319895e253": "0100000001000000", + "7014c674ba19506d9e2c178ab0eb4acd47b701192705": "0100000001000000", + "7014c9dfb2e5898b3432f8808d5aea4c3936c6db3888": "0100000001000000", + "7014cc2953e1188c162d1689206ce79fabdd54c0a575": "0100000001000000", + "7014cfe3810f02a4af8dd98092bb6650841bac0ff2c4": "0100000001000000", + "7014cffd1219803f5f43f72a2a8e841a2fefb0347657": "0100000001000000", + "7014d20cf3c6172ea38a0ec42514435978a76a99b1eb": "0100000001000000", + "7014e154c1b406841cc45d1d63fbd0c36003b5e34cc9": "0100000001000000", + "7014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c": "0100000001000000", + "7014f4f1b2cd73eebe34bef87c174e5d4903e86493ff": "0100000001000000", + "7014f7c2c1cf022472febc2deb36c91ae2e450c36b29": "0100000001000000", + "7020014ae391c03cd9362e262b394c85ce06cea7ebd07c13f97506a839a1a11d651d": "0100000002000000", + "70200230be98454e5b569c471677d182bfa226e02f382161dfba4808c814768fa53e": "0100000002000000", + "702005a8ddf9b9d2f9444136255ca8bd90421fb7670ee5f3df010f13b6ee2c6c5cf7": "0100000002000000", + "70201388e7721a373ad570a648fcf2b9b67bc1db13290ec4ff72ecd9bc28458c41a4": "0100000002000000", + "70201b82351fd6ef197d346bb3792dd068cd30886b76b5fc6ca6e42eb85dd80231d2": "0100000002000000", + "7020218bcaf58e810fbb368ae372412933f8179cd26ac62a69c111b8fbc0d47846c1": "0100000002000000", + "70203700fcb0cf9da3afd6b58ab12c98ecd92ee1d70e667a8135c5b43f13e3a934fb": "0100000002000000", + "70203862684532dbfd6e87a5f3b2176f039ada4ab17300acd90dc33e294dfa74f2f0": "0100000002000000", + "7020391883de87eff56ee0ea8afe56a271bc3eefbee6391504532fb71ef53975aaad": "0100000002000000", + "702039cf4d6f59eb602d1874642a27b0eede313ef13dc840a98869df40a27fb9fc27": "0100000002000000", + "70203a5a5f9793853a5e2b33ccf9cb7e8311e4ccf8b42400bc9f543c0450f83660a0": "0100000002000000", + "70203b6112f1ece55eaba9f4533ef15b8a7e2a28a3e945789ae0a3d52a7eebd149fa": "0100000002000000", + "70203f25468e114c28631ef9a0b2142302a5e11ab4cbb08a6fece18b758230ec94b9": "0100000002000000", + "702042ea2ba78922e99b8eadec8b0a7e44fcdbb2ebac2195dec45112dc91119c86e5": "0100000002000000", + "7020483f05b0be57c59d87ff0d6a44a61dc28b69745d5366bb46cab1e19e6976ec62": "0100000002000000", + "70205d77fb0b2b6239c51750e30087a54d1f0cd50d88a9890a540f27e0867d55901c": "0100000002000000", + "70205d7c541fd6af1b22dcd959d3721f8bb236df5b2d0e2c2731f3bb213dbee79771": "0100000002000000", + "70205f36a17f56382c90a5223800cb9af46ca2e3ae366412a865d9b85e85bd3be0b8": "0100000002000000", + "70207e287ea1c99bceb191a5de13159e108c2969a1b7101517078017e824d0d68bb6": "0100000002000000", + "7020924a9a1ec8f7c21f4d1dfc1503a27a38687808ea2199dca262150b1b149dd81b": "0100000002000000", + "7020951b5f6437ea0a0feed1935aa6c53f9337c5d3e63649a7b8143eb7c8050d63d0": "0100000002000000", + "7020a06bac796162ff65142d7e1be882db42492e7bd2e5f6803ddba20b49f6b77a91": "0100000002000000", + "7020a1bb3afd4e182e7fedfb8be7f67c264cf21d9a3560c6ae01eceb80e3a9939a7d": "0100000002000000", + "7020b7055255263baba965dcc8cd553af0d6d8fa8642bca55bddff52780d8a81e7f6": "0100000002000000", + "7020b879ff665513666627d900db039d4741bdd304f24bfe7bfd8987e298caaf3135": "0100000002000000", + "7020bed8310af201ca58abc475429fb9b92b9dbaac71ab915fbdcb448b6c3a11296a": "0100000002000000", + "7020c2d6e806851b1ab0411bb27ded2fb44dbe147cb19bd5bbc8d313894875aa749f": "0100000002000000", + "7020cf4a622bc2e507d60ac31a5e04ce858023acef83009ffbfd55d2677a0e74d3f2": "0100000002000000", + "7020d4102bb5808580bb47a1a0d5e07e8cf35fdff44457dfa39f1ddc2f0de19caa73": "0100000002000000", + "7020e5b1375a597f61dca88393bcb6d061fd29f80ac005ba522bb6ff4930326d5184": "0100000002000000", + "7020eff0443f2a5114012303803760e595f4c0bc9f5d8b81e64d1b7ac3ea124b47d6": "0100000002000000", + "7020f9dcf5ddd1748e63056e1c2b2afeff3a57c5289c26a8fd7c6c282171ee1c9251": "0100000002000000", + "720000000100000000140441cbe166229b9e2adb6aa90751052523cb298d": "00", + "72000000010000000014198a5f555dc8f78bcc35bf23baefc03c954e7053": "00", + "720000000100000000141b3c16c74f65720cd520148e0557a3e057550ae2": "00", + "720000000100000000141fcdaa5bd5efa8811106f6390e5ecc327083426b": "00", + "72000000010000000014222740511246fe2b10957049ae10e0bbe7fa996c": "00", + "72000000010000000014292e9aae355f8cd57ec7e5aedb49cf19a1c30a73": "00", + "720000000100000000142a52b414589aca0f9e6b79be9563e3e0b8e053aa": "00", + "720000000100000000142dcae735994e7802bc103da010633db1a32b18dc": "00", + "720000000100000000142f6d31b494bdfa42034719ca28894496cdb89359": "00", + "7200000001000000001441136683ea0485bbef46e9d9f6a2ff766128efad": "00", + "72000000010000000014433b4a5bdd977ac6ab958e07834a08fbd2e7f961": "00", + "720000000100000000144c43158dc848068c85406c14957b0cab073f343b": "00", + "7200000001000000001460976bb97754c7ab48bac4c894d27a4126bc23d1": "00", + "720000000100000000146a94c2244bd9a4b5257a8c946b68af72f4118b35": "00", + "720000000100000000147220144f51a799765a1cfebc74a6ff789e0cf098": "00", + "720000000100000000147a98b6621e4ccc528560645f6a7682633a24fad8": "00", + "72000000010000000014873c0013e79e492b0e9e6ecf631b4a2dfe5f1ed0": "00", + "720000000100000000148d375a0132f2077de9c4a96c66e15c24c5501851": "00", + "720000000100000000149259252916ee5b5573f1f911ed49c99592b1db44": "00", + "7200000001000000001499b6d711eb6ba45313ed8ff37d9a8ec5df572750": "00", + "720000000100000000149ae567da742bcd8894e5f0c789aa1a5a017396cc": "00", + "720000000100000000149d03a12b2facd319be3b174ac49e78effae0f9fc": "00", + "720000000100000000149df7d0326747749b602967421b892f1efe19fd73": "00", + "72000000010000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558": "00", + "72000000010000000014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec7": "00", + "72000000010000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf50": "00", + "72000000010000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3": "00", + "72000000010000000014c53ec93147b4fa7e1e1bf56f0edbdd319895e253": "00", + "72000000010000000014c9dfb2e5898b3432f8808d5aea4c3936c6db3888": "00", + "72000000010000000014cc2953e1188c162d1689206ce79fabdd54c0a575": "00", + "72000000010000000014cffd1219803f5f43f72a2a8e841a2fefb0347657": "00", + "72000000010000000014e154c1b406841cc45d1d63fbd0c36003b5e34cc9": "00", + "72000000010000000114045216fd9b9f0aeff2be3c18327c3083fc4ffd4a": "00", + "7200000001000000011404d0a093756a7da12d94f6f5cc7bbd6b1a25505a": "00", + "720000000100000001140c20cf0e2a2e0ea22b4214a16e76c93aa59510d2": "00", + "720000000100000001141c28474c2fbd6281a6d536218c8ca7b4e79691c0": "00", + "720000000100000001142667d9e317cf8f0aac2543fd27f38e0ab9da3ecb": "00", + "720000000100000001142cd7a7b49b1013603d53b6a234493ee120847175": "00", + "72000000010000000114309a0821ae883849b41aeb44c24b075af338b8c0": "00", + "7200000001000000011431fcd6dd84b2bd8d450bca497b035e9e44e43418": "00", + "72000000010000000114355379bd23a29cfc1457908f7fe1e30d22efd007": "00", + "72000000010000000114452b000bd69cd9c4a99322d1c41e972cdcd662fe": "00", + "720000000100000001144931bd2996fb46b2f17a5f4bae7d0b2a04ee65bc": "00", + "720000000100000001144eca2ab57c2a64d356a40fab260140a48fb39ca4": "00", + "720000000100000001144f9644e04b6bbc4f53ae49e444d005524cc1422f": "00", + "720000000100000001145258eb6f21709d00f66fa39deab6e8d5391eae5f": "00", + "720000000100000001145428d3cebe215a9f78bf3ca6d3ebc055b828f3d9": "00", + "720000000100000001145c4c9a889c8102fce3f2d40149842898270f96d0": "00", + "720000000100000001145cb324e109b920b19494549b3e540f57d43c4ef1": "00", + "720000000100000001146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54": "00", + "72000000010000000114646f81d8c9569f04c93d1559a606da8185cb49c1": "00", + "72000000010000000114657eb0831fd4fafd3e595163416d76861ea9acd9": "00", + "7200000001000000011466bdd1c0be1d2785d86469fd11dee568e293c0a0": "00", + "720000000100000001146dddb81cee99d34e617876a2d4bd091fea7da844": "00", + "720000000100000001147db1e3611d2409939fb385976712f032de7629ef": "00", + "72000000010000000114881668afa843bcf71d7717815f31a9c6c8f96d7f": "00", + "72000000010000000114a2affbcd20614676abcac636b5db306e96441121": "00", + "72000000010000000114bac3a07219bdb2d75cdebcf7e42a28c58bb41d79": "00", + "72000000010000000114c674ba19506d9e2c178ab0eb4acd47b701192705": "00", + "72000000010000000114cfe3810f02a4af8dd98092bb6650841bac0ff2c4": "00", + "72000000010000000114d20cf3c6172ea38a0ec42514435978a76a99b1eb": "00", + "72000000010000000114f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c": "00", + "72000000010000000114f4f1b2cd73eebe34bef87c174e5d4903e86493ff": "00", + "72000000010000000114f7c2c1cf022472febc2deb36c91ae2e450c36b29": "00", + "72000000020000000020014ae391c03cd9362e262b394c85ce06cea7ebd07c13f97506a839a1a11d651d": "00", + "720000000200000000200230be98454e5b569c471677d182bfa226e02f382161dfba4808c814768fa53e": "00", + "7200000002000000002005a8ddf9b9d2f9444136255ca8bd90421fb7670ee5f3df010f13b6ee2c6c5cf7": "00", + "720000000200000000201388e7721a373ad570a648fcf2b9b67bc1db13290ec4ff72ecd9bc28458c41a4": "00", + "720000000200000000201b82351fd6ef197d346bb3792dd068cd30886b76b5fc6ca6e42eb85dd80231d2": "00", + "72000000020000000020218bcaf58e810fbb368ae372412933f8179cd26ac62a69c111b8fbc0d47846c1": "00", + "720000000200000000203700fcb0cf9da3afd6b58ab12c98ecd92ee1d70e667a8135c5b43f13e3a934fb": "00", + "720000000200000000203862684532dbfd6e87a5f3b2176f039ada4ab17300acd90dc33e294dfa74f2f0": "00", + "72000000020000000020391883de87eff56ee0ea8afe56a271bc3eefbee6391504532fb71ef53975aaad": "00", + "7200000002000000002039cf4d6f59eb602d1874642a27b0eede313ef13dc840a98869df40a27fb9fc27": "00", + "720000000200000000203a5a5f9793853a5e2b33ccf9cb7e8311e4ccf8b42400bc9f543c0450f83660a0": "00", + "720000000200000000203b6112f1ece55eaba9f4533ef15b8a7e2a28a3e945789ae0a3d52a7eebd149fa": "00", + "720000000200000000203f25468e114c28631ef9a0b2142302a5e11ab4cbb08a6fece18b758230ec94b9": "00", + "7200000002000000002042ea2ba78922e99b8eadec8b0a7e44fcdbb2ebac2195dec45112dc91119c86e5": "00", + "72000000020000000020483f05b0be57c59d87ff0d6a44a61dc28b69745d5366bb46cab1e19e6976ec62": "00", + "720000000200000000205d77fb0b2b6239c51750e30087a54d1f0cd50d88a9890a540f27e0867d55901c": "00", + "720000000200000000205d7c541fd6af1b22dcd959d3721f8bb236df5b2d0e2c2731f3bb213dbee79771": "00", + "720000000200000000205f36a17f56382c90a5223800cb9af46ca2e3ae366412a865d9b85e85bd3be0b8": "00", + "720000000200000000207e287ea1c99bceb191a5de13159e108c2969a1b7101517078017e824d0d68bb6": "00", + "72000000020000000020924a9a1ec8f7c21f4d1dfc1503a27a38687808ea2199dca262150b1b149dd81b": "00", + "72000000020000000020951b5f6437ea0a0feed1935aa6c53f9337c5d3e63649a7b8143eb7c8050d63d0": "00", + "72000000020000000020a06bac796162ff65142d7e1be882db42492e7bd2e5f6803ddba20b49f6b77a91": "00", + "72000000020000000020a1bb3afd4e182e7fedfb8be7f67c264cf21d9a3560c6ae01eceb80e3a9939a7d": "00", + "72000000020000000020b7055255263baba965dcc8cd553af0d6d8fa8642bca55bddff52780d8a81e7f6": "00", + "72000000020000000020b879ff665513666627d900db039d4741bdd304f24bfe7bfd8987e298caaf3135": "00", + "72000000020000000020bed8310af201ca58abc475429fb9b92b9dbaac71ab915fbdcb448b6c3a11296a": "00", + "72000000020000000020c2d6e806851b1ab0411bb27ded2fb44dbe147cb19bd5bbc8d313894875aa749f": "00", + "72000000020000000020cf4a622bc2e507d60ac31a5e04ce858023acef83009ffbfd55d2677a0e74d3f2": "00", + "72000000020000000020d4102bb5808580bb47a1a0d5e07e8cf35fdff44457dfa39f1ddc2f0de19caa73": "00", + "72000000020000000020e5b1375a597f61dca88393bcb6d061fd29f80ac005ba522bb6ff4930326d5184": "00", + "72000000020000000020eff0443f2a5114012303803760e595f4c0bc9f5d8b81e64d1b7ac3ea124b47d6": "00", + "72000000020000000020f9dcf5ddd1748e63056e1c2b2afeff3a57c5289c26a8fd7c6c282171ee1c9251": "00", + "7700000001": "0002000000a6538c20efa72e74ddbfcf36f434049452539ffeec9be7911892d09c51c2451800000000003ee42ccdfa128b8c6959af76f5eae78df6ec12b3e029e2134db7b3b931d81d50563337a7b5a121c129fbb0330bffc536225a4beb46ebac9b11008307dd0fcb2400", + "7700000002": "0001000000c9ad8772eb6f2277ec148815351af1c8e84f9635d198a3000cb5bf68f86751c30000000000e54d5f740a38311e7f3e5f181598d194ee5ba56d9216b31291803ced97a8b00752e92e488395005319ac6b4e03a51ffb22a596619221887116226dd0f99aa74500" + }, + "after": { + "500000000114027ae8b1f114c757d89b9534f6660e4b84f0c725": "000000000000010000000d000000", + "5000000001140441cbe166229b9e2adb6aa90751052523cb298d": "0000000000000000000008000000", + "500000000114045216fd9b9f0aeff2be3c18327c3083fc4ffd4a": "0100000000000000000012000000", + "50000000011404d0a093756a7da12d94f6f5cc7bbd6b1a25505a": "010000000000010000000a000000", + "5000000001140c20cf0e2a2e0ea22b4214a16e76c93aa59510d2": "0100000000000100000008000000", + "500000000114198a5f555dc8f78bcc35bf23baefc03c954e7053": "0000000000000000000011000000", + "5000000001141b3c16c74f65720cd520148e0557a3e057550ae2": "0000000000000100000008000000", + "5000000001141c28474c2fbd6281a6d536218c8ca7b4e79691c0": "0100000000000000000010000000", + "5000000001141fcdaa5bd5efa8811106f6390e5ecc327083426b": "0000000000000100000001000000", + "500000000114222740511246fe2b10957049ae10e0bbe7fa996c": "0000000000000100000002000000", + "5000000001142667d9e317cf8f0aac2543fd27f38e0ab9da3ecb": "0100000000000000000005000000", + "500000000114292e9aae355f8cd57ec7e5aedb49cf19a1c30a73": "0000000000000100000007000000", + "5000000001142a52b414589aca0f9e6b79be9563e3e0b8e053aa": "000000000000000000000e000000", + "5000000001142cd7a7b49b1013603d53b6a234493ee120847175": "0100000000000000000002000000", + "5000000001142d47d6a4d3b232b7cc566bc40cf119954691ceb7": "000000000000010000000c000000", + "5000000001142dcae735994e7802bc103da010633db1a32b18dc": "0000000000000000000010000000", + "5000000001142f6d31b494bdfa42034719ca28894496cdb89359": "0000000000000000000003000000", + "500000000114309a0821ae883849b41aeb44c24b075af338b8c0": "0100000000000000000004000000", + "50000000011431fcd6dd84b2bd8d450bca497b035e9e44e43418": "0100000000000100000009000000", + "500000000114355379bd23a29cfc1457908f7fe1e30d22efd007": "0100000000000100000007000000", + "500000000114359f98022fcbec853e5b34e61a3451249ef65c08": "0000000000000100000014000000", + "50000000011441136683ea0485bbef46e9d9f6a2ff766128efad": "000000000000000000000f000000", + "500000000114433b4a5bdd977ac6ab958e07834a08fbd2e7f961": "0000000000000000000013000000", + "50000000011443e2c1fe531890f0e687144aa1bc0cb2dd3d61c8": "0000000000000100000013000000", + "500000000114452b000bd69cd9c4a99322d1c41e972cdcd662fe": "010000000000000000000b000000", + "5000000001144931bd2996fb46b2f17a5f4bae7d0b2a04ee65bc": "0100000000000000000014000000", + "5000000001144b04764f3cb8d954da8943beeb152037879df6df": "0100000000000100000011000000", + "5000000001144c43158dc848068c85406c14957b0cab073f343b": "0000000000000000000006000000", + "5000000001144eca2ab57c2a64d356a40fab260140a48fb39ca4": "0100000000000000000011000000", + "5000000001144ee8b5003f41b2afefccc2a6da691bd2dc3307fc": "0000000000000100000011000000", + "5000000001144f9644e04b6bbc4f53ae49e444d005524cc1422f": "010000000000000000000c000000", + "5000000001145258eb6f21709d00f66fa39deab6e8d5391eae5f": "0100000000000100000001000000", + "50000000011452ecdbc8da2a6533e6ef00b982cfdfe68660ead2": "000000000000010000000b000000", + "5000000001145428d3cebe215a9f78bf3ca6d3ebc055b828f3d9": "0100000000000000000007000000", + "500000000114587b68ffd042dd172e9b1b2961489fb717bfb0d1": "010000000000010000000c000000", + "5000000001145c4c9a889c8102fce3f2d40149842898270f96d0": "0100000000000100000004000000", + "5000000001145cb324e109b920b19494549b3e540f57d43c4ef1": "010000000000000000000e000000", + "50000000011460976bb97754c7ab48bac4c894d27a4126bc23d1": "0000000000000100000005000000", + "5000000001146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54": "0100000000000000000001000000", + "500000000114646f81d8c9569f04c93d1559a606da8185cb49c1": "010000000000000000000a000000", + "500000000114657eb0831fd4fafd3e595163416d76861ea9acd9": "0100000000000000000000000000", + "50000000011466bdd1c0be1d2785d86469fd11dee568e293c0a0": "0100000000000100000006000000", + "5000000001146a94c2244bd9a4b5257a8c946b68af72f4118b35": "0000000000000000000002000000", + "5000000001146dddb81cee99d34e617876a2d4bd091fea7da844": "0100000000000000000006000000", + "5000000001146fd57650dc0dbaced6f904d060592f6d1b41c86a": "010000000000010000000f000000", + "5000000001147220144f51a799765a1cfebc74a6ff789e0cf098": "000000000000000000000a000000", + "5000000001147a98b6621e4ccc528560645f6a7682633a24fad8": "0000000000000100000006000000", + "5000000001147db1e3611d2409939fb385976712f032de7629ef": "0100000000000000000009000000", + "500000000114873c0013e79e492b0e9e6ecf631b4a2dfe5f1ed0": "0000000000000100000009000000", + "500000000114881668afa843bcf71d7717815f31a9c6c8f96d7f": "0100000000000100000005000000", + "5000000001148d375a0132f2077de9c4a96c66e15c24c5501851": "0000000000000000000004000000", + "5000000001148e89a3cde3b0ead4ce743b1842df6b0c4c1c568d": "0000000000000100000010000000", + "5000000001149259252916ee5b5573f1f911ed49c99592b1db44": "000000000000000000000d000000", + "50000000011495506ba4efe4f5e2deabcf223affaf9ca2e6a658": "000000000000010000000e000000", + "50000000011499b6d711eb6ba45313ed8ff37d9a8ec5df572750": "000000000000000000000b000000", + "5000000001149ae567da742bcd8894e5f0c789aa1a5a017396cc": "0000000000000100000003000000", + "5000000001149d03a12b2facd319be3b174ac49e78effae0f9fc": "000000000000000000000c000000", + "5000000001149df7d0326747749b602967421b892f1efe19fd73": "0000000000000000000014000000", + "500000000114a2affbcd20614676abcac636b5db306e96441121": "0100000000000000000013000000", + "500000000114a62c71694f8bf7583bfe73bd1b2235b81ee12558": "0000000000000100000000000000", + "500000000114a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec7": "0000000000000100000004000000", + "500000000114aa9bc87be810eb386dd30e85bd440dfa14934478": "0100000000000100000013000000", + "500000000114ab5ae51b13e09f892ffe60c1e4c0fe5f1f029530": "000000000000010000000f000000", + "500000000114abcbd76f17cec312e114e6c71cfdf0f49674af86": "010000000000010000000d000000", + "500000000114b27ecd0a361b0fe9acafecdc47841e021a4ebf50": "0000000000000000000005000000", + "500000000114b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3": "0000000000000000000001000000", + "500000000114bac3a07219bdb2d75cdebcf7e42a28c58bb41d79": "0100000000000100000003000000", + "500000000114c1ee72587261a8cf15628130c3d61ef1110ce6be": "0100000000000100000012000000", + "500000000114c53ec93147b4fa7e1e1bf56f0edbdd319895e253": "0000000000000000000012000000", + "500000000114c674ba19506d9e2c178ab0eb4acd47b701192705": "0100000000000100000002000000", + "500000000114c80172ac203e8f6d76fe8576d13bfb525bb3b22f": "0100000000000100000010000000", + "500000000114c9dfb2e5898b3432f8808d5aea4c3936c6db3888": "000000000000010000000a000000", + "500000000114cc2953e1188c162d1689206ce79fabdd54c0a575": "0000000000000000000007000000", + "500000000114cfe3810f02a4af8dd98092bb6650841bac0ff2c4": "0100000000000000000008000000", + "500000000114cffd1219803f5f43f72a2a8e841a2fefb0347657": "0000000000000000000000000000", + "500000000114d20cf3c6172ea38a0ec42514435978a76a99b1eb": "010000000000000000000d000000", + "500000000114e0d7066a75b4833d70c35f71d70835abdf8e1165": "010000000000010000000e000000", + "500000000114e154c1b406841cc45d1d63fbd0c36003b5e34cc9": "0000000000000000000009000000", + "500000000114ee65c67c7bfbd3420eb24c249c7fc13387964363": "0000000000000100000012000000", + "500000000114ef9932f62986df8853b821c8667687aa314b850c": "0100000000000100000014000000", + "500000000114f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c": "0100000000000100000000000000", + "500000000114f4f1b2cd73eebe34bef87c174e5d4903e86493ff": "0100000000000000000003000000", + "500000000114f7c2c1cf022472febc2deb36c91ae2e450c36b29": "010000000000000000000f000000", + "500000000114faeee1160b0eb2b222799ddeea46d051159d89e0": "010000000000010000000b000000", + "500000000220014ae391c03cd9362e262b394c85ce06cea7ebd07c13f97506a839a1a11d651d": "000000000000000000000f000000", + "5000000002200230be98454e5b569c471677d182bfa226e02f382161dfba4808c814768fa53e": "0000000000000000000009000000", + "50000000022002eeadc67945c86d1c1c8399ea870b265c4b23896fdd5b7bde704a989667a1e4": "0000000000000100000012000000", + "5000000002200502cd117d30301680061168c31a41cef99130be760127bdee802f8d06e197b4": "0000000000000100000011000000", + "50000000022005a8ddf9b9d2f9444136255ca8bd90421fb7670ee5f3df010f13b6ee2c6c5cf7": "000000000000000000000a000000", + "5000000002201388e7721a373ad570a648fcf2b9b67bc1db13290ec4ff72ecd9bc28458c41a4": "000000000000000000000d000000", + "5000000002201b82351fd6ef197d346bb3792dd068cd30886b76b5fc6ca6e42eb85dd80231d2": "0000000000000100000000000000", + "5000000002201f57d998c4155cafcc7d6f75fe2e5ad0df44b7a3d2f1077a111a2d28e61e3512": "0000000000000100000010000000", + "500000000220218bcaf58e810fbb368ae372412933f8179cd26ac62a69c111b8fbc0d47846c1": "0000000000000100000002000000", + "50000000022022eff97ee70726f5fa5ab959500e4bd36d8bf443888fda879a60ba1bb568e283": "0000000000000100000014000000", + "5000000002203700fcb0cf9da3afd6b58ab12c98ecd92ee1d70e667a8135c5b43f13e3a934fb": "0000000000000000000003000000", + "5000000002203862684532dbfd6e87a5f3b2176f039ada4ab17300acd90dc33e294dfa74f2f0": "0000000000000000000004000000", + "500000000220391883de87eff56ee0ea8afe56a271bc3eefbee6391504532fb71ef53975aaad": "0000000000000100000007000000", + "50000000022039cf4d6f59eb602d1874642a27b0eede313ef13dc840a98869df40a27fb9fc27": "000000000000000000000c000000", + "5000000002203a5a5f9793853a5e2b33ccf9cb7e8311e4ccf8b42400bc9f543c0450f83660a0": "0000000000000000000014000000", + "5000000002203b6112f1ece55eaba9f4533ef15b8a7e2a28a3e945789ae0a3d52a7eebd149fa": "0000000000000100000005000000", + "5000000002203f25468e114c28631ef9a0b2142302a5e11ab4cbb08a6fece18b758230ec94b9": "0000000000000000000011000000", + "50000000022042ea2ba78922e99b8eadec8b0a7e44fcdbb2ebac2195dec45112dc91119c86e5": "0000000000000000000007000000", + "500000000220483f05b0be57c59d87ff0d6a44a61dc28b69745d5366bb46cab1e19e6976ec62": "0000000000000000000006000000", + "5000000002205d77fb0b2b6239c51750e30087a54d1f0cd50d88a9890a540f27e0867d55901c": "000000000000000000000e000000", + "5000000002205d7c541fd6af1b22dcd959d3721f8bb236df5b2d0e2c2731f3bb213dbee79771": "000000000000010000000a000000", + "5000000002205f36a17f56382c90a5223800cb9af46ca2e3ae366412a865d9b85e85bd3be0b8": "000000000000000000000b000000", + "5000000002207e287ea1c99bceb191a5de13159e108c2969a1b7101517078017e824d0d68bb6": "0000000000000000000002000000", + "500000000220886386f38a48386a20bd0e2e88a84edf740c5a6462f084e3c0d93280887ff8a6": "000000000000010000000e000000", + "5000000002208a09c6a069af6edd04937782a60417265e6afc01ccb91d959c5d39b9d97c8a73": "000000000000010000000c000000", + "5000000002208dd2237958da52e8f262f5214ca6d7158bba5a47203b46022dc095512adba529": "0000000000000100000013000000", + "500000000220924a9a1ec8f7c21f4d1dfc1503a27a38687808ea2199dca262150b1b149dd81b": "0000000000000100000009000000", + "500000000220951b5f6437ea0a0feed1935aa6c53f9337c5d3e63649a7b8143eb7c8050d63d0": "0000000000000000000005000000", + "500000000220a06bac796162ff65142d7e1be882db42492e7bd2e5f6803ddba20b49f6b77a91": "0000000000000000000013000000", + "500000000220a1bb3afd4e182e7fedfb8be7f67c264cf21d9a3560c6ae01eceb80e3a9939a7d": "0000000000000000000000000000", + "500000000220a7abae2898ce16fa2e6646f544cae13edf6296c47d55c9c45ffe85ce07a908de": "000000000000010000000f000000", + "500000000220ad76fdf19aeff5211afae72d81d0821a912d7d77fdfc0ae37e7d6ba90d96be27": "000000000000010000000b000000", + "500000000220b7055255263baba965dcc8cd553af0d6d8fa8642bca55bddff52780d8a81e7f6": "0000000000000000000010000000", + "500000000220b879ff665513666627d900db039d4741bdd304f24bfe7bfd8987e298caaf3135": "0000000000000100000006000000", + "500000000220bed8310af201ca58abc475429fb9b92b9dbaac71ab915fbdcb448b6c3a11296a": "0000000000000100000003000000", + "500000000220c2d6e806851b1ab0411bb27ded2fb44dbe147cb19bd5bbc8d313894875aa749f": "0000000000000100000004000000", + "500000000220cf4a622bc2e507d60ac31a5e04ce858023acef83009ffbfd55d2677a0e74d3f2": "0000000000000000000012000000", + "500000000220d4102bb5808580bb47a1a0d5e07e8cf35fdff44457dfa39f1ddc2f0de19caa73": "0000000000000000000001000000", + "500000000220dee3cd95ac855961d3e15dc6def11d6b4385c0efe23655a27825323e4ef5b88a": "000000000000010000000d000000", + "500000000220e5b1375a597f61dca88393bcb6d061fd29f80ac005ba522bb6ff4930326d5184": "0000000000000100000008000000", + "500000000220eff0443f2a5114012303803760e595f4c0bc9f5d8b81e64d1b7ac3ea124b47d6": "0000000000000000000008000000", + "500000000220f9dcf5ddd1748e63056e1c2b2afeff3a57c5289c26a8fd7c6c282171ee1c9251": "0000000000000100000001000000", + "5700000001": "0777616c6c657431", + "5700000002": "0777616c6c657432", + "610000000100000000": "010001010b0000000b0000000a03875c90a480000000fdc9691244b5dc0636ce0eabd905556c333e208b2815e463a6e65db29833c15d024a4400453207366ed8567bf78fdda5dccc1a0f8b1398f9925b744cc563575a7400", + "610000000100000001": "010001010b0000000b0000000a03875c90a480000001fc7d26586b3e6e082df9d01051258b8809960d5434d1abfe088ab507a0cfbbf003f44993e0ed4638fb5d32a272cca586ffe85040755d95568e01ca5c4845e27c9e00", + "610000000200000000": "010101020b0000000b0000000a034a0c1e1180000000bda10baaccd9189fb3a56e2950ad198c45611cd3dbf0f154f699fa1c9ddc4b89022e05837732c62feb49896bc8b3cd8fd2f1b9b6bc415074ba31d858a810bbb29401032ac4ec93800000008921a0a732ac09e78b4f51464880ca9fc3e09a129564c0d8aef9fb1fd2ff175302f2b8aebdc25d17815420662ff8e2b9e24202a9b628f07ce2671cedac5f8001e1", + "6c077072696d617279": "00000000", + "6c0777616c6c657431": "01000000", + "6c0777616c6c657432": "02000000", + "6e0000000000000000": "0764656661756c74", + "6e0000000100000000": "0764656661756c74", + "6e0000000100000001": "04616c7431", + "6e0000000200000000": "0764656661756c74", + "7014027ae8b1f114c757d89b9534f6660e4b84f0c725": "0100000001000000", + "70140441cbe166229b9e2adb6aa90751052523cb298d": "0100000001000000", + "7014045216fd9b9f0aeff2be3c18327c3083fc4ffd4a": "0100000001000000", + "701404d0a093756a7da12d94f6f5cc7bbd6b1a25505a": "0100000001000000", + "70140c20cf0e2a2e0ea22b4214a16e76c93aa59510d2": "0100000001000000", + "7014198a5f555dc8f78bcc35bf23baefc03c954e7053": "0100000001000000", + "70141b3c16c74f65720cd520148e0557a3e057550ae2": "0100000001000000", + "70141c28474c2fbd6281a6d536218c8ca7b4e79691c0": "0100000001000000", + "70141fcdaa5bd5efa8811106f6390e5ecc327083426b": "0100000001000000", + "7014222740511246fe2b10957049ae10e0bbe7fa996c": "0100000001000000", + "70142667d9e317cf8f0aac2543fd27f38e0ab9da3ecb": "0100000001000000", + "7014292e9aae355f8cd57ec7e5aedb49cf19a1c30a73": "0100000001000000", + "70142a52b414589aca0f9e6b79be9563e3e0b8e053aa": "0100000001000000", + "70142cd7a7b49b1013603d53b6a234493ee120847175": "0100000001000000", + "70142d47d6a4d3b232b7cc566bc40cf119954691ceb7": "0100000001000000", + "70142dcae735994e7802bc103da010633db1a32b18dc": "0100000001000000", + "70142f6d31b494bdfa42034719ca28894496cdb89359": "0100000001000000", + "7014309a0821ae883849b41aeb44c24b075af338b8c0": "0100000001000000", + "701431fcd6dd84b2bd8d450bca497b035e9e44e43418": "0100000001000000", + "7014355379bd23a29cfc1457908f7fe1e30d22efd007": "0100000001000000", + "7014359f98022fcbec853e5b34e61a3451249ef65c08": "0100000001000000", + "701441136683ea0485bbef46e9d9f6a2ff766128efad": "0100000001000000", + "7014433b4a5bdd977ac6ab958e07834a08fbd2e7f961": "0100000001000000", + "701443e2c1fe531890f0e687144aa1bc0cb2dd3d61c8": "0100000001000000", + "7014452b000bd69cd9c4a99322d1c41e972cdcd662fe": "0100000001000000", + "70144931bd2996fb46b2f17a5f4bae7d0b2a04ee65bc": "0100000001000000", + "70144b04764f3cb8d954da8943beeb152037879df6df": "0100000001000000", + "70144c43158dc848068c85406c14957b0cab073f343b": "0100000001000000", + "70144eca2ab57c2a64d356a40fab260140a48fb39ca4": "0100000001000000", + "70144ee8b5003f41b2afefccc2a6da691bd2dc3307fc": "0100000001000000", + "70144f9644e04b6bbc4f53ae49e444d005524cc1422f": "0100000001000000", + "70145258eb6f21709d00f66fa39deab6e8d5391eae5f": "0100000001000000", + "701452ecdbc8da2a6533e6ef00b982cfdfe68660ead2": "0100000001000000", + "70145428d3cebe215a9f78bf3ca6d3ebc055b828f3d9": "0100000001000000", + "7014587b68ffd042dd172e9b1b2961489fb717bfb0d1": "0100000001000000", + "70145c4c9a889c8102fce3f2d40149842898270f96d0": "0100000001000000", + "70145cb324e109b920b19494549b3e540f57d43c4ef1": "0100000001000000", + "701460976bb97754c7ab48bac4c894d27a4126bc23d1": "0100000001000000", + "70146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54": "0100000001000000", + "7014646f81d8c9569f04c93d1559a606da8185cb49c1": "0100000001000000", + "7014657eb0831fd4fafd3e595163416d76861ea9acd9": "0100000001000000", + "701466bdd1c0be1d2785d86469fd11dee568e293c0a0": "0100000001000000", + "70146a94c2244bd9a4b5257a8c946b68af72f4118b35": "0100000001000000", + "70146dddb81cee99d34e617876a2d4bd091fea7da844": "0100000001000000", + "70146fd57650dc0dbaced6f904d060592f6d1b41c86a": "0100000001000000", + "70147220144f51a799765a1cfebc74a6ff789e0cf098": "0100000001000000", + "70147a98b6621e4ccc528560645f6a7682633a24fad8": "0100000001000000", + "70147db1e3611d2409939fb385976712f032de7629ef": "0100000001000000", + "7014873c0013e79e492b0e9e6ecf631b4a2dfe5f1ed0": "0100000001000000", + "7014881668afa843bcf71d7717815f31a9c6c8f96d7f": "0100000001000000", + "70148d375a0132f2077de9c4a96c66e15c24c5501851": "0100000001000000", + "70148e89a3cde3b0ead4ce743b1842df6b0c4c1c568d": "0100000001000000", + "70149259252916ee5b5573f1f911ed49c99592b1db44": "0100000001000000", + "701495506ba4efe4f5e2deabcf223affaf9ca2e6a658": "0100000001000000", + "701499b6d711eb6ba45313ed8ff37d9a8ec5df572750": "0100000001000000", + "70149ae567da742bcd8894e5f0c789aa1a5a017396cc": "0100000001000000", + "70149d03a12b2facd319be3b174ac49e78effae0f9fc": "0100000001000000", + "70149df7d0326747749b602967421b892f1efe19fd73": "0100000001000000", + "7014a2affbcd20614676abcac636b5db306e96441121": "0100000001000000", + "7014a62c71694f8bf7583bfe73bd1b2235b81ee12558": "0100000001000000", + "7014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec7": "0100000001000000", + "7014aa9bc87be810eb386dd30e85bd440dfa14934478": "0100000001000000", + "7014ab5ae51b13e09f892ffe60c1e4c0fe5f1f029530": "0100000001000000", + "7014abcbd76f17cec312e114e6c71cfdf0f49674af86": "0100000001000000", + "7014b27ecd0a361b0fe9acafecdc47841e021a4ebf50": "0100000001000000", + "7014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3": "0100000001000000", + "7014bac3a07219bdb2d75cdebcf7e42a28c58bb41d79": "0100000001000000", + "7014c1ee72587261a8cf15628130c3d61ef1110ce6be": "0100000001000000", + "7014c53ec93147b4fa7e1e1bf56f0edbdd319895e253": "0100000001000000", + "7014c674ba19506d9e2c178ab0eb4acd47b701192705": "0100000001000000", + "7014c80172ac203e8f6d76fe8576d13bfb525bb3b22f": "0100000001000000", + "7014c9dfb2e5898b3432f8808d5aea4c3936c6db3888": "0100000001000000", + "7014cc2953e1188c162d1689206ce79fabdd54c0a575": "0100000001000000", + "7014cfe3810f02a4af8dd98092bb6650841bac0ff2c4": "0100000001000000", + "7014cffd1219803f5f43f72a2a8e841a2fefb0347657": "0100000001000000", + "7014d20cf3c6172ea38a0ec42514435978a76a99b1eb": "0100000001000000", + "7014e0d7066a75b4833d70c35f71d70835abdf8e1165": "0100000001000000", + "7014e154c1b406841cc45d1d63fbd0c36003b5e34cc9": "0100000001000000", + "7014ee65c67c7bfbd3420eb24c249c7fc13387964363": "0100000001000000", + "7014ef9932f62986df8853b821c8667687aa314b850c": "0100000001000000", + "7014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c": "0100000001000000", + "7014f4f1b2cd73eebe34bef87c174e5d4903e86493ff": "0100000001000000", + "7014f7c2c1cf022472febc2deb36c91ae2e450c36b29": "0100000001000000", + "7014faeee1160b0eb2b222799ddeea46d051159d89e0": "0100000001000000", + "7020014ae391c03cd9362e262b394c85ce06cea7ebd07c13f97506a839a1a11d651d": "0100000002000000", + "70200230be98454e5b569c471677d182bfa226e02f382161dfba4808c814768fa53e": "0100000002000000", + "702002eeadc67945c86d1c1c8399ea870b265c4b23896fdd5b7bde704a989667a1e4": "0100000002000000", + "70200502cd117d30301680061168c31a41cef99130be760127bdee802f8d06e197b4": "0100000002000000", + "702005a8ddf9b9d2f9444136255ca8bd90421fb7670ee5f3df010f13b6ee2c6c5cf7": "0100000002000000", + "70201388e7721a373ad570a648fcf2b9b67bc1db13290ec4ff72ecd9bc28458c41a4": "0100000002000000", + "70201b82351fd6ef197d346bb3792dd068cd30886b76b5fc6ca6e42eb85dd80231d2": "0100000002000000", + "70201f57d998c4155cafcc7d6f75fe2e5ad0df44b7a3d2f1077a111a2d28e61e3512": "0100000002000000", + "7020218bcaf58e810fbb368ae372412933f8179cd26ac62a69c111b8fbc0d47846c1": "0100000002000000", + "702022eff97ee70726f5fa5ab959500e4bd36d8bf443888fda879a60ba1bb568e283": "0100000002000000", + "70203700fcb0cf9da3afd6b58ab12c98ecd92ee1d70e667a8135c5b43f13e3a934fb": "0100000002000000", + "70203862684532dbfd6e87a5f3b2176f039ada4ab17300acd90dc33e294dfa74f2f0": "0100000002000000", + "7020391883de87eff56ee0ea8afe56a271bc3eefbee6391504532fb71ef53975aaad": "0100000002000000", + "702039cf4d6f59eb602d1874642a27b0eede313ef13dc840a98869df40a27fb9fc27": "0100000002000000", + "70203a5a5f9793853a5e2b33ccf9cb7e8311e4ccf8b42400bc9f543c0450f83660a0": "0100000002000000", + "70203b6112f1ece55eaba9f4533ef15b8a7e2a28a3e945789ae0a3d52a7eebd149fa": "0100000002000000", + "70203f25468e114c28631ef9a0b2142302a5e11ab4cbb08a6fece18b758230ec94b9": "0100000002000000", + "702042ea2ba78922e99b8eadec8b0a7e44fcdbb2ebac2195dec45112dc91119c86e5": "0100000002000000", + "7020483f05b0be57c59d87ff0d6a44a61dc28b69745d5366bb46cab1e19e6976ec62": "0100000002000000", + "70205d77fb0b2b6239c51750e30087a54d1f0cd50d88a9890a540f27e0867d55901c": "0100000002000000", + "70205d7c541fd6af1b22dcd959d3721f8bb236df5b2d0e2c2731f3bb213dbee79771": "0100000002000000", + "70205f36a17f56382c90a5223800cb9af46ca2e3ae366412a865d9b85e85bd3be0b8": "0100000002000000", + "70207e287ea1c99bceb191a5de13159e108c2969a1b7101517078017e824d0d68bb6": "0100000002000000", + "7020886386f38a48386a20bd0e2e88a84edf740c5a6462f084e3c0d93280887ff8a6": "0100000002000000", + "70208a09c6a069af6edd04937782a60417265e6afc01ccb91d959c5d39b9d97c8a73": "0100000002000000", + "70208dd2237958da52e8f262f5214ca6d7158bba5a47203b46022dc095512adba529": "0100000002000000", + "7020924a9a1ec8f7c21f4d1dfc1503a27a38687808ea2199dca262150b1b149dd81b": "0100000002000000", + "7020951b5f6437ea0a0feed1935aa6c53f9337c5d3e63649a7b8143eb7c8050d63d0": "0100000002000000", + "7020a06bac796162ff65142d7e1be882db42492e7bd2e5f6803ddba20b49f6b77a91": "0100000002000000", + "7020a1bb3afd4e182e7fedfb8be7f67c264cf21d9a3560c6ae01eceb80e3a9939a7d": "0100000002000000", + "7020a7abae2898ce16fa2e6646f544cae13edf6296c47d55c9c45ffe85ce07a908de": "0100000002000000", + "7020ad76fdf19aeff5211afae72d81d0821a912d7d77fdfc0ae37e7d6ba90d96be27": "0100000002000000", + "7020b7055255263baba965dcc8cd553af0d6d8fa8642bca55bddff52780d8a81e7f6": "0100000002000000", + "7020b879ff665513666627d900db039d4741bdd304f24bfe7bfd8987e298caaf3135": "0100000002000000", + "7020bed8310af201ca58abc475429fb9b92b9dbaac71ab915fbdcb448b6c3a11296a": "0100000002000000", + "7020c2d6e806851b1ab0411bb27ded2fb44dbe147cb19bd5bbc8d313894875aa749f": "0100000002000000", + "7020cf4a622bc2e507d60ac31a5e04ce858023acef83009ffbfd55d2677a0e74d3f2": "0100000002000000", + "7020d4102bb5808580bb47a1a0d5e07e8cf35fdff44457dfa39f1ddc2f0de19caa73": "0100000002000000", + "7020dee3cd95ac855961d3e15dc6def11d6b4385c0efe23655a27825323e4ef5b88a": "0100000002000000", + "7020e5b1375a597f61dca88393bcb6d061fd29f80ac005ba522bb6ff4930326d5184": "0100000002000000", + "7020eff0443f2a5114012303803760e595f4c0bc9f5d8b81e64d1b7ac3ea124b47d6": "0100000002000000", + "7020f9dcf5ddd1748e63056e1c2b2afeff3a57c5289c26a8fd7c6c282171ee1c9251": "0100000002000000", + "72000000010000000014027ae8b1f114c757d89b9534f6660e4b84f0c725": "00", + "720000000100000000140441cbe166229b9e2adb6aa90751052523cb298d": "00", + "72000000010000000014198a5f555dc8f78bcc35bf23baefc03c954e7053": "00", + "720000000100000000141b3c16c74f65720cd520148e0557a3e057550ae2": "00", + "720000000100000000141fcdaa5bd5efa8811106f6390e5ecc327083426b": "00", + "72000000010000000014222740511246fe2b10957049ae10e0bbe7fa996c": "00", + "72000000010000000014292e9aae355f8cd57ec7e5aedb49cf19a1c30a73": "00", + "720000000100000000142a52b414589aca0f9e6b79be9563e3e0b8e053aa": "00", + "720000000100000000142d47d6a4d3b232b7cc566bc40cf119954691ceb7": "00", + "720000000100000000142dcae735994e7802bc103da010633db1a32b18dc": "00", + "720000000100000000142f6d31b494bdfa42034719ca28894496cdb89359": "00", + "72000000010000000014359f98022fcbec853e5b34e61a3451249ef65c08": "00", + "7200000001000000001441136683ea0485bbef46e9d9f6a2ff766128efad": "00", + "72000000010000000014433b4a5bdd977ac6ab958e07834a08fbd2e7f961": "00", + "7200000001000000001443e2c1fe531890f0e687144aa1bc0cb2dd3d61c8": "00", + "720000000100000000144c43158dc848068c85406c14957b0cab073f343b": "00", + "720000000100000000144ee8b5003f41b2afefccc2a6da691bd2dc3307fc": "00", + "7200000001000000001452ecdbc8da2a6533e6ef00b982cfdfe68660ead2": "00", + "7200000001000000001460976bb97754c7ab48bac4c894d27a4126bc23d1": "00", + "720000000100000000146a94c2244bd9a4b5257a8c946b68af72f4118b35": "00", + "720000000100000000147220144f51a799765a1cfebc74a6ff789e0cf098": "00", + "720000000100000000147a98b6621e4ccc528560645f6a7682633a24fad8": "00", + "72000000010000000014873c0013e79e492b0e9e6ecf631b4a2dfe5f1ed0": "00", + "720000000100000000148d375a0132f2077de9c4a96c66e15c24c5501851": "00", + "720000000100000000148e89a3cde3b0ead4ce743b1842df6b0c4c1c568d": "00", + "720000000100000000149259252916ee5b5573f1f911ed49c99592b1db44": "00", + "7200000001000000001495506ba4efe4f5e2deabcf223affaf9ca2e6a658": "00", + "7200000001000000001499b6d711eb6ba45313ed8ff37d9a8ec5df572750": "00", + "720000000100000000149ae567da742bcd8894e5f0c789aa1a5a017396cc": "00", + "720000000100000000149d03a12b2facd319be3b174ac49e78effae0f9fc": "00", + "720000000100000000149df7d0326747749b602967421b892f1efe19fd73": "00", + "72000000010000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558": "00", + "72000000010000000014a7cd0f0c61bcdb86967f1b11128b7d3ba7d78ec7": "00", + "72000000010000000014ab5ae51b13e09f892ffe60c1e4c0fe5f1f029530": "00", + "72000000010000000014b27ecd0a361b0fe9acafecdc47841e021a4ebf50": "00", + "72000000010000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3": "00", + "72000000010000000014c53ec93147b4fa7e1e1bf56f0edbdd319895e253": "00", + "72000000010000000014c9dfb2e5898b3432f8808d5aea4c3936c6db3888": "00", + "72000000010000000014cc2953e1188c162d1689206ce79fabdd54c0a575": "00", + "72000000010000000014cffd1219803f5f43f72a2a8e841a2fefb0347657": "00", + "72000000010000000014e154c1b406841cc45d1d63fbd0c36003b5e34cc9": "00", + "72000000010000000014ee65c67c7bfbd3420eb24c249c7fc13387964363": "00", + "72000000010000000114045216fd9b9f0aeff2be3c18327c3083fc4ffd4a": "00", + "7200000001000000011404d0a093756a7da12d94f6f5cc7bbd6b1a25505a": "00", + "720000000100000001140c20cf0e2a2e0ea22b4214a16e76c93aa59510d2": "00", + "720000000100000001141c28474c2fbd6281a6d536218c8ca7b4e79691c0": "00", + "720000000100000001142667d9e317cf8f0aac2543fd27f38e0ab9da3ecb": "00", + "720000000100000001142cd7a7b49b1013603d53b6a234493ee120847175": "00", + "72000000010000000114309a0821ae883849b41aeb44c24b075af338b8c0": "00", + "7200000001000000011431fcd6dd84b2bd8d450bca497b035e9e44e43418": "00", + "72000000010000000114355379bd23a29cfc1457908f7fe1e30d22efd007": "00", + "72000000010000000114452b000bd69cd9c4a99322d1c41e972cdcd662fe": "00", + "720000000100000001144931bd2996fb46b2f17a5f4bae7d0b2a04ee65bc": "00", + "720000000100000001144b04764f3cb8d954da8943beeb152037879df6df": "00", + "720000000100000001144eca2ab57c2a64d356a40fab260140a48fb39ca4": "00", + "720000000100000001144f9644e04b6bbc4f53ae49e444d005524cc1422f": "00", + "720000000100000001145258eb6f21709d00f66fa39deab6e8d5391eae5f": "00", + "720000000100000001145428d3cebe215a9f78bf3ca6d3ebc055b828f3d9": "00", + "72000000010000000114587b68ffd042dd172e9b1b2961489fb717bfb0d1": "00", + "720000000100000001145c4c9a889c8102fce3f2d40149842898270f96d0": "00", + "720000000100000001145cb324e109b920b19494549b3e540f57d43c4ef1": "00", + "720000000100000001146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54": "00", + "72000000010000000114646f81d8c9569f04c93d1559a606da8185cb49c1": "00", + "72000000010000000114657eb0831fd4fafd3e595163416d76861ea9acd9": "00", + "7200000001000000011466bdd1c0be1d2785d86469fd11dee568e293c0a0": "00", + "720000000100000001146dddb81cee99d34e617876a2d4bd091fea7da844": "00", + "720000000100000001146fd57650dc0dbaced6f904d060592f6d1b41c86a": "00", + "720000000100000001147db1e3611d2409939fb385976712f032de7629ef": "00", + "72000000010000000114881668afa843bcf71d7717815f31a9c6c8f96d7f": "00", + "72000000010000000114a2affbcd20614676abcac636b5db306e96441121": "00", + "72000000010000000114aa9bc87be810eb386dd30e85bd440dfa14934478": "00", + "72000000010000000114abcbd76f17cec312e114e6c71cfdf0f49674af86": "00", + "72000000010000000114bac3a07219bdb2d75cdebcf7e42a28c58bb41d79": "00", + "72000000010000000114c1ee72587261a8cf15628130c3d61ef1110ce6be": "00", + "72000000010000000114c674ba19506d9e2c178ab0eb4acd47b701192705": "00", + "72000000010000000114c80172ac203e8f6d76fe8576d13bfb525bb3b22f": "00", + "72000000010000000114cfe3810f02a4af8dd98092bb6650841bac0ff2c4": "00", + "72000000010000000114d20cf3c6172ea38a0ec42514435978a76a99b1eb": "00", + "72000000010000000114e0d7066a75b4833d70c35f71d70835abdf8e1165": "00", + "72000000010000000114ef9932f62986df8853b821c8667687aa314b850c": "00", + "72000000010000000114f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c": "00", + "72000000010000000114f4f1b2cd73eebe34bef87c174e5d4903e86493ff": "00", + "72000000010000000114f7c2c1cf022472febc2deb36c91ae2e450c36b29": "00", + "72000000010000000114faeee1160b0eb2b222799ddeea46d051159d89e0": "00", + "72000000020000000020014ae391c03cd9362e262b394c85ce06cea7ebd07c13f97506a839a1a11d651d": "00", + "720000000200000000200230be98454e5b569c471677d182bfa226e02f382161dfba4808c814768fa53e": "00", + "7200000002000000002002eeadc67945c86d1c1c8399ea870b265c4b23896fdd5b7bde704a989667a1e4": "00", + "720000000200000000200502cd117d30301680061168c31a41cef99130be760127bdee802f8d06e197b4": "00", + "7200000002000000002005a8ddf9b9d2f9444136255ca8bd90421fb7670ee5f3df010f13b6ee2c6c5cf7": "00", + "720000000200000000201388e7721a373ad570a648fcf2b9b67bc1db13290ec4ff72ecd9bc28458c41a4": "00", + "720000000200000000201b82351fd6ef197d346bb3792dd068cd30886b76b5fc6ca6e42eb85dd80231d2": "00", + "720000000200000000201f57d998c4155cafcc7d6f75fe2e5ad0df44b7a3d2f1077a111a2d28e61e3512": "00", + "72000000020000000020218bcaf58e810fbb368ae372412933f8179cd26ac62a69c111b8fbc0d47846c1": "00", + "7200000002000000002022eff97ee70726f5fa5ab959500e4bd36d8bf443888fda879a60ba1bb568e283": "00", + "720000000200000000203700fcb0cf9da3afd6b58ab12c98ecd92ee1d70e667a8135c5b43f13e3a934fb": "00", + "720000000200000000203862684532dbfd6e87a5f3b2176f039ada4ab17300acd90dc33e294dfa74f2f0": "00", + "72000000020000000020391883de87eff56ee0ea8afe56a271bc3eefbee6391504532fb71ef53975aaad": "00", + "7200000002000000002039cf4d6f59eb602d1874642a27b0eede313ef13dc840a98869df40a27fb9fc27": "00", + "720000000200000000203a5a5f9793853a5e2b33ccf9cb7e8311e4ccf8b42400bc9f543c0450f83660a0": "00", + "720000000200000000203b6112f1ece55eaba9f4533ef15b8a7e2a28a3e945789ae0a3d52a7eebd149fa": "00", + "720000000200000000203f25468e114c28631ef9a0b2142302a5e11ab4cbb08a6fece18b758230ec94b9": "00", + "7200000002000000002042ea2ba78922e99b8eadec8b0a7e44fcdbb2ebac2195dec45112dc91119c86e5": "00", + "72000000020000000020483f05b0be57c59d87ff0d6a44a61dc28b69745d5366bb46cab1e19e6976ec62": "00", + "720000000200000000205d77fb0b2b6239c51750e30087a54d1f0cd50d88a9890a540f27e0867d55901c": "00", + "720000000200000000205d7c541fd6af1b22dcd959d3721f8bb236df5b2d0e2c2731f3bb213dbee79771": "00", + "720000000200000000205f36a17f56382c90a5223800cb9af46ca2e3ae366412a865d9b85e85bd3be0b8": "00", + "720000000200000000207e287ea1c99bceb191a5de13159e108c2969a1b7101517078017e824d0d68bb6": "00", + "72000000020000000020886386f38a48386a20bd0e2e88a84edf740c5a6462f084e3c0d93280887ff8a6": "00", + "720000000200000000208a09c6a069af6edd04937782a60417265e6afc01ccb91d959c5d39b9d97c8a73": "00", + "720000000200000000208dd2237958da52e8f262f5214ca6d7158bba5a47203b46022dc095512adba529": "00", + "72000000020000000020924a9a1ec8f7c21f4d1dfc1503a27a38687808ea2199dca262150b1b149dd81b": "00", + "72000000020000000020951b5f6437ea0a0feed1935aa6c53f9337c5d3e63649a7b8143eb7c8050d63d0": "00", + "72000000020000000020a06bac796162ff65142d7e1be882db42492e7bd2e5f6803ddba20b49f6b77a91": "00", + "72000000020000000020a1bb3afd4e182e7fedfb8be7f67c264cf21d9a3560c6ae01eceb80e3a9939a7d": "00", + "72000000020000000020a7abae2898ce16fa2e6646f544cae13edf6296c47d55c9c45ffe85ce07a908de": "00", + "72000000020000000020ad76fdf19aeff5211afae72d81d0821a912d7d77fdfc0ae37e7d6ba90d96be27": "00", + "72000000020000000020b7055255263baba965dcc8cd553af0d6d8fa8642bca55bddff52780d8a81e7f6": "00", + "72000000020000000020b879ff665513666627d900db039d4741bdd304f24bfe7bfd8987e298caaf3135": "00", + "72000000020000000020bed8310af201ca58abc475429fb9b92b9dbaac71ab915fbdcb448b6c3a11296a": "00", + "72000000020000000020c2d6e806851b1ab0411bb27ded2fb44dbe147cb19bd5bbc8d313894875aa749f": "00", + "72000000020000000020cf4a622bc2e507d60ac31a5e04ce858023acef83009ffbfd55d2677a0e74d3f2": "00", + "72000000020000000020d4102bb5808580bb47a1a0d5e07e8cf35fdff44457dfa39f1ddc2f0de19caa73": "00", + "72000000020000000020dee3cd95ac855961d3e15dc6def11d6b4385c0efe23655a27825323e4ef5b88a": "00", + "72000000020000000020e5b1375a597f61dca88393bcb6d061fd29f80ac005ba522bb6ff4930326d5184": "00", + "72000000020000000020eff0443f2a5114012303803760e595f4c0bc9f5d8b81e64d1b7ac3ea124b47d6": "00", + "72000000020000000020f9dcf5ddd1748e63056e1c2b2afeff3a57c5289c26a8fd7c6c282171ee1c9251": "00", + "7700000001": "0002000000a6538c20efa72e74ddbfcf36f434049452539ffeec9be7911892d09c51c2451800000000003ee42ccdfa128b8c6959af76f5eae78df6ec12b3e029e2134db7b3b931d81d50563337a7b5a121c129fbb0330bffc536225a4beb46ebac9b11008307dd0fcb2400", + "7700000002": "0001000000c9ad8772eb6f2277ec148815351af1c8e84f9635d198a3000cb5bf68f86751c30000000000e54d5f740a38311e7f3e5f181598d194ee5ba56d9216b31291803ced97a8b00752e92e488395005319ac6b4e03a51ffb22a596619221887116226dd0f99aa74500" + } +} diff --git a/test/data/migrations/wallet-2-account-lookahead-gen.js b/test/data/migrations/wallet-2-account-lookahead-gen.js new file mode 100644 index 000000000..fe2d45f60 --- /dev/null +++ b/test/data/migrations/wallet-2-account-lookahead-gen.js @@ -0,0 +1,87 @@ +'use strict'; + +/** + * This migration is for v4 -> v5. + */ + +const bdb = require('bdb'); +const Network = require('../../../lib/protocol/network'); +const WalletDB = require('../../../lib/wallet/walletdb'); +const wutils = require('../../util/wallet'); +const mutils = require('../../util/migrations'); + +const layout = { + wdb: { + // W[wid] -> wallet id + W: bdb.key('W', ['uint32']), + + // a[wid][index] -> account + a: bdb.key('a', ['uint32', 'uint32']) + } +}; + +const NETWORK = Network.get('regtest'); +const LOOKAHEAD1 = 10; +const LOOKAHEAD2 = 200; + +const wallet1priv = 'rprvKE8qsHtkmUxUSPQdn2sFKFUcKyUQz9pKQhxjEWecnXg9hgJMsmJXcw' + + 'J77SqmHT1R6mcuNqVPzgT2EoGStsXaUN92VJKhQWUB6uZdL8gAZvez'; +const wallet2priv = 'rprvKE8qsHtkmUxUSR4jE7Lti9XV77hv7xxacAShw5MvxY6RfsAYVeB1WL' + + 'WtjiebDmqTruVJxmMeQUMkk61e83WDZbZidDnNPhHyQpeEwxjuSZuG'; +const rpub = 'rpubKBAPj83PkcGYSz3GR9xfzXBfTFTPtERb2x7fKvbikH9utGMvr6HDd4sTxt5zo' + + 'arw4bzzgH1VDzBUoX9fotzmPrrngFyLMz3ozAi1ozAbJjSY'; + +(async () => { + const wdb = new WalletDB({ + network: NETWORK, + memory: true + }); + + await wdb.open(); + + const wallet1 = await wdb.create({ + id: 'wallet1', + master: wallet1priv, + lookahead: LOOKAHEAD1 + }); + + await wallet1.createAccount({ + name: 'alt', + lookahead: LOOKAHEAD2 + }); + + const wallet2 = await wdb.create({ + id: 'wallet2', + master: wallet2priv, + lookahead: LOOKAHEAD2, + m: 1, + n: 2 + }); + + await wallet2.addSharedKey(0, rpub); + + for (let i = 0; i < 100; i++) { + const entry = wutils.nextEntry(wdb); + await wdb.addBlock(entry, []); + } + + console.log(JSON.stringify({ + data: await getMigrationDump(wdb) + }, null, 2)); + + await wdb.close(); +})().catch((e) => { + console.error(e); +}); + +async function getMigrationDump(wdb) { + const prefixes = []; + + // skip primary wallet. + for (let i = 1; i < 3; i++) { + prefixes.push(layout.wdb.W.encode(i).toString('hex')); + prefixes.push(layout.wdb.a.encode(i, 0).slice(0, 5).toString('hex')); + } + + return await mutils.dumpDB(wdb, prefixes); +} diff --git a/test/data/migrations/wallet-2-account-lookahead.json b/test/data/migrations/wallet-2-account-lookahead.json new file mode 100644 index 000000000..78510b199 --- /dev/null +++ b/test/data/migrations/wallet-2-account-lookahead.json @@ -0,0 +1,17 @@ +{ + "before": { + "5700000001": "0777616c6c657431", + "5700000002": "0777616c6c657432", + "610000000100000000": "0100010101000000010000000a03875c90a480000000fdc9691244b5dc0636ce0eabd905556c333e208b2815e463a6e65db29833c15d024a4400453207366ed8567bf78fdda5dccc1a0f8b1398f9925b744cc563575a7400", + "610000000100000001": "010001010100000001000000c803875c90a480000001fc7d26586b3e6e082df9d01051258b8809960d5434d1abfe088ab507a0cfbbf003f44993e0ed4638fb5d32a272cca586ffe85040755d95568e01ca5c4845e27c9e00", + "610000000200000000": "010101020100000001000000c8034a0c1e1180000000bda10baaccd9189fb3a56e2950ad198c45611cd3dbf0f154f699fa1c9ddc4b89022e05837732c62feb49896bc8b3cd8fd2f1b9b6bc415074ba31d858a810bbb29401032ac4ec93800000008921a0a732ac09e78b4f51464880ca9fc3e09a129564c0d8aef9fb1fd2ff175302f2b8aebdc25d17815420662ff8e2b9e24202a9b628f07ce2671cedac5f8001e1" + + }, + "after": { + "5700000001": "0777616c6c657431", + "5700000002": "0777616c6c657432", + "610000000100000000": "0100010101000000010000000a00000003875c90a480000000fdc9691244b5dc0636ce0eabd905556c333e208b2815e463a6e65db29833c15d024a4400453207366ed8567bf78fdda5dccc1a0f8b1398f9925b744cc563575a7400", + "610000000100000001": "010001010100000001000000c800000003875c90a480000001fc7d26586b3e6e082df9d01051258b8809960d5434d1abfe088ab507a0cfbbf003f44993e0ed4638fb5d32a272cca586ffe85040755d95568e01ca5c4845e27c9e00", + "610000000200000000": "010101020100000001000000c8000000034a0c1e1180000000bda10baaccd9189fb3a56e2950ad198c45611cd3dbf0f154f699fa1c9ddc4b89022e05837732c62feb49896bc8b3cd8fd2f1b9b6bc415074ba31d858a810bbb29401032ac4ec93800000008921a0a732ac09e78b4f51464880ca9fc3e09a129564c0d8aef9fb1fd2ff175302f2b8aebdc25d17815420662ff8e2b9e24202a9b628f07ce2671cedac5f8001e1" + } +} diff --git a/test/data/migrations/wallet-4-bid-reveal-gen.js b/test/data/migrations/wallet-4-bid-reveal-gen.js new file mode 100644 index 000000000..b42934dfe --- /dev/null +++ b/test/data/migrations/wallet-4-bid-reveal-gen.js @@ -0,0 +1,186 @@ +'use strict'; + +// Works for walletdb version 2 to 3 migration. +// HSD v6 -> v7 migration. + +const assert = require('bsert'); +const bdb = require('bdb'); +const Network = require('../../../lib/protocol/network'); +const WalletDB = require('../../../lib/wallet/walletdb'); +const MTX = require('../../../lib/primitives/mtx'); +const wutils = require('../../../test/util/wallet'); +const rules = require('../../../lib/covenants/rules'); + +const layout = { + wdb: { + V: bdb.key('V'), + // W[wid] -> wallet id + W: bdb.key('W', ['uint32']) + }, + txdb: { + prefix: bdb.key('t', ['uint32']), + // t[tx-hash] -> extended tx (Read only) + t: bdb.key('t', ['hash256']), + // i[name-hash][tx-hash][index] -> txdb.BlindBid + i: bdb.key('i', ['hash256', 'hash256', 'uint32']), + // B[name-hash][tx-hash][index] -> txdb.BidReveal + B: bdb.key('B', ['hash256', 'hash256', 'uint32']), + // E[name-hash][tx-hash][index] -> bid to reveal out. + E: bdb.key('E', ['hash256', 'hash256', 'uint32']) + } +}; + +const NETWORK = Network.get('regtest'); + +const wallet1priv = 'rprvKE8qsHtkmUxUSPQdn2sFKFUcKyUQz9pKQhxjEWecnXg9hgJMsmJXcw' + + 'J77SqmHT1R6mcuNqVPzgT2EoGStsXaUN92VJKhQWUB6uZdL8gAZvez'; +const wallet2priv = 'rprvKE8qsHtkmUxUSR4jE7Lti9XV77hv7xxacAShw5MvxY6RfsAYVeB1WL' + + 'WtjiebDmqTruVJxmMeQUMkk61e83WDZbZidDnNPhHyQpeEwxjuSZuG'; + +let txID = 0; +let timeCounter = 0; + +(async () => { + const wdb = new WalletDB({ + network: NETWORK, + memory: true, + nowFn: () => timeCounter++ + }); + + await wdb.open(); + + const wallet1 = await wdb.create({ + id: 'wallet1', + master: wallet1priv + }); + + await wallet1.createAccount('alt'); + + const wallet2 = await wdb.create({ + id: 'wallet2', + master: wallet2priv + }); + + // add 10 blocks to the wallet. + await mineBlocks(wdb, 100); + + // fund wallets + const mtx1 = new MTX(); + mtx1.addInput(wutils.deterministicInput(txID++)); + mtx1.addOutput(await wallet1.receiveAddress(0), 10e6); + + const mtx2 = new MTX(); + mtx2.addInput(wutils.deterministicInput(txID++)); + mtx2.addOutput(await wallet1.receiveAddress(1), 10e6); + + // fund second wallet. + const mtx3 = new MTX(); + mtx3.addInput(wutils.deterministicInput(txID++)); + mtx3.addOutput(await wallet2.receiveAddress(), 10e6); + + await wdb.addBlock(wutils.nextEntry(wdb), [ + mtx1.toTX(), + mtx2.toTX(), + mtx3.toTX() + ]); + + const name1 = 'testname1'; + const name2 = 'testname2'; + + const open1 = await wallet1.createOpen(name1, { + account: 0 + }); + await wdb.addTX(open1.toTX()); + const open2 = await wallet1.createOpen(name2, { + account: 0 + }); + + await wdb.addBlock(wutils.nextEntry(wdb), [ + open1.toTX(), + open2.toTX() + ]); + + await mineBlocks(wdb, NETWORK.names.treeInterval + 1); + + const ns = await wallet1.getNameState(rules.hashName(name1)); + const bid1 = await wallet1.createBid(name1, 2e6, 2e6, { + account: 0 + }); + + const bid2 = await wallet1.createBid(name2, 2e6, 2e6, { + account: 1 + }); + + // wallet2 does not know the state of the name. + const _getNameStatusBak = wdb.getNameStatus; + wdb.getNameStatus = (nameHash) => { + assert(Buffer.isBuffer(nameHash)); + assert(nameHash.equals(rules.hashName(name1))); + + return ns; + }; + + const bid3 = await wallet2.createBid(name1, 3e6, 3e6); + await wdb.addBlock(wutils.nextEntry(wdb), [ + bid1.toTX(), + bid2.toTX(), + bid3.toTX() + ]); + + wdb.getNameStatus = _getNameStatusBak; + await mineBlocks(wdb, NETWORK.names.biddingPeriod); + + const reveal1 = await wallet1.createReveal(name1, { + account: 0 + }); + + const reveal2 = await wallet1.createReveal(name2, { + account: 1 + }); + + const reveal3 = await wallet2.createReveal(name1); + + await wdb.addBlock(wutils.nextEntry(wdb), [ + reveal1.toTX(), + reveal2.toTX(), + reveal3.toTX() + ]); + + const dump = await getMigrationDump(wdb); + console.log(JSON.stringify({ + data: dump + }, null, 2)); + + await wdb.close(); +})().catch((e) => { + console.error(e.stack); + process.exit(1); +}); + +async function mineBlocks(wdb, n) { + for (let i = 0; i < n; i++) { + const entry = wutils.nextEntry(wdb); + await wdb.addBlock(entry, []); + } +}; + +async function getMigrationDump(wdb) { + const prefixes = []; + + for (let i = 1; i < 3; i++) { + const tprefix = layout.txdb.prefix.encode(i).toString('hex'); + const ti = tprefix + 'i'.charCodeAt(0).toString(16); + const tB = tprefix + 'B'.charCodeAt(0).toString(16); + const tE = tprefix + 'E'.charCodeAt(0).toString(16); + const tt = tprefix + 't'.charCodeAt(0).toString(16); + prefixes.push(ti, tB, tE, tt); + } + + for (let i = 0; i < 3; i++) { + prefixes.push(layout.wdb.W.encode(i).toString('hex')); + } + + const dump = await wutils.dumpWDB(wdb, prefixes); + + return dump; +}; diff --git a/test/data/migrations/wallet-4-bid-reveal.json b/test/data/migrations/wallet-4-bid-reveal.json new file mode 100644 index 000000000..181e6aad0 --- /dev/null +++ b/test/data/migrations/wallet-4-bid-reveal.json @@ -0,0 +1,86 @@ +{ + "description": "Migration for linking bid to reveal and vice versa. Affects txdb layout i, B and E (new). Uses txdb layout t to collect data.", + "before": { + "5700000000": "077072696d617279", + "5700000001": "0777616c6c657431", + "5700000002": "0777616c6c657432", + "740000000142074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382ccc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000": "09746573746e616d653280841e00000000007300000001", + "740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d00000000007300000000", + "740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e00000000007300000001", + "740000000169074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "09746573746e616d653280841e0000000000d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980b01", + "740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d400", + "740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "09746573746e616d653180841e0000000000fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b890501", + "7400000001741706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f4": "00000000029a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000ffffffff9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849101000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000020450566fda05dda024354f47c1f6d07769342a57c5dffe1d3a908a9043a83156bb8cd79000000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc000000000000020021037f44c97b1d5c69af830f7c14647c710e95300bd94dd92ad75fdd30ca7489287f020021024e3c15df14b521891f269af40258119698ebaed5f7299abf9583546e2438e73209000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "7400000001741b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a": "0000000001a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f30915501000000ffffffff02000000000000000000146a94c2244bd9a4b5257a8c946b68af72f4118b35020320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c040000000009746573746e616d6532207998000000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000200210256bc84ba99364f4a0146c1203b52104447d1085aee74df1242be003664e36cdf0400000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f", + "7400000001741ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a746": "0000000001a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc270200000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54030420074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000009746573746e616d653220d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980bbc007a00000000000014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c00000000000002002102df1a2c03a8d1b89b0b2f570ad99764f7dc4bcc710afa9745af65f2bc7789cfc806000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "7400000001742bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f3": "000000000111da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be900000000ffffffff0180969800000000000014cffd1219803f5f43f72a2a8e841a2fefb0347657000000000000000000000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f", + "7400000001749a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d8491": "00000000011b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a01000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89055ce37900000000000014222740511246fe2b10957049ae10e0bbe7fa996c000000000000020021023960f57b3d5844794ac0dd5576fb59b3a64d82d4d76285a36173d169412539e205000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "740000000174a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f309155": "00000000012bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f300000000ffffffff0200000000000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3020320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a040000000009746573746e616d6531d0879800000000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558000000000000020021020c30819abd60558d2f9d1fdfabb5d46df7254b5a9346c9d78dc9b5ace6b1089e0300000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f", + "740000000174a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc2702": "0000000001e12c22d4f162d9a012c9319233da5d3e923cc5e1029b8f90e47249c9ab256b3500000000ffffffff0180969800000000000014657eb0831fd4fafd3e595163416d76861ea9acd9000000000000000100000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f", + "740000000174cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd": "00000000021ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000ffffffff1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74601000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54040320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000020f70095f015fc11f5bae44f6136cbe1bb846103e79e065ea54f9e56f58e5322ab18eb79000000000000145258eb6f21709d00f66fa39deab6e8d5391eae5f00000000000002002103d1ab347d75498ccde90f26907c8aad98fcb5ecc922ff7de8f4be83c9b7a099b6020021021e772405b9b642a904d6e1f5739b56d1714327853f6cbb97777c70ef0c54c5450b000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d00000000007300000001", + "740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e00000000007300000000", + "740000000269aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d401", + "74000000027401f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf": "00000000023bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000ffffffff3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d901000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a0466000000204c679195282ea315863be7119a2f1f6a4b9486fe7f86e6e778435f4fe9ed4083d8a86a00000000000014bfe57d3d51c357a99aa9c84195fe8e000273399a00000000000002002103aa28c5dc394dd41bda11c296f1e0ef98edfa09e5730b6abdab5f1e1e700e2a61020021036b182787487be70c07012724b985e8fe7f49067f2a3e4c5535da0118a21d13fa0c000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "7400000002743bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d9": "0000000001abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a600000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d47cbe6a00000000000014b44aae19323f53e63e5cd184dba19a71616808150000000000000200210237b59c5644219778989265c96ecdb213578feca83fc908a975d8465359779e8807000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "740000000274abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a6": "00000000017b0aa1735e5ba58d3236316c671fe4f00ed366ee72417c9ed02a53a8019e85b800000000ffffffff01809698000000000000148ec33e2b7a9dc5fde7e6a166005d3b29265b313d000000000000000200000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f" + }, + "fullAfter": { + "5700000000": "077072696d617279", + "5700000001": "0777616c6c657431", + "5700000002": "0777616c6c657432", + "740000000142074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382ccc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000": "09746573746e616d653280841e000000000073000000011ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000", + "740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000003bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000", + "740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000019a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000", + "740000000145074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000", + "740000000145aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000", + "740000000145aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000", + "740000000169074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "09746573746e616d653280841e0000000000d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980b6d00000001", + "740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d46d00000000", + "740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "09746573746e616d653180841e0000000000fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89056d00000001", + "7400000001741706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f4": "00000000029a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000ffffffff9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849101000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000020450566fda05dda024354f47c1f6d07769342a57c5dffe1d3a908a9043a83156bb8cd79000000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc000000000000020021037f44c97b1d5c69af830f7c14647c710e95300bd94dd92ad75fdd30ca7489287f020021024e3c15df14b521891f269af40258119698ebaed5f7299abf9583546e2438e73209000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "7400000001741b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a": "0000000001a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f30915501000000ffffffff02000000000000000000146a94c2244bd9a4b5257a8c946b68af72f4118b35020320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c040000000009746573746e616d6532207998000000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000200210256bc84ba99364f4a0146c1203b52104447d1085aee74df1242be003664e36cdf0400000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f", + "7400000001741ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a746": "0000000001a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc270200000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54030420074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000009746573746e616d653220d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980bbc007a00000000000014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c00000000000002002102df1a2c03a8d1b89b0b2f570ad99764f7dc4bcc710afa9745af65f2bc7789cfc806000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "7400000001742bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f3": "000000000111da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be900000000ffffffff0180969800000000000014cffd1219803f5f43f72a2a8e841a2fefb0347657000000000000000000000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f", + "7400000001749a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d8491": "00000000011b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a01000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89055ce37900000000000014222740511246fe2b10957049ae10e0bbe7fa996c000000000000020021023960f57b3d5844794ac0dd5576fb59b3a64d82d4d76285a36173d169412539e205000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "740000000174a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f309155": "00000000012bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f300000000ffffffff0200000000000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3020320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a040000000009746573746e616d6531d0879800000000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558000000000000020021020c30819abd60558d2f9d1fdfabb5d46df7254b5a9346c9d78dc9b5ace6b1089e0300000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f", + "740000000174a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc2702": "0000000001e12c22d4f162d9a012c9319233da5d3e923cc5e1029b8f90e47249c9ab256b3500000000ffffffff0180969800000000000014657eb0831fd4fafd3e595163416d76861ea9acd9000000000000000100000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f", + "740000000174cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd": "00000000021ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000ffffffff1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74601000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54040320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000020f70095f015fc11f5bae44f6136cbe1bb846103e79e065ea54f9e56f58e5322ab18eb79000000000000145258eb6f21709d00f66fa39deab6e8d5391eae5f00000000000002002103d1ab347d75498ccde90f26907c8aad98fcb5ecc922ff7de8f4be83c9b7a099b6020021021e772405b9b642a904d6e1f5739b56d1714327853f6cbb97777c70ef0c54c5450b000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000013bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000", + "740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000009a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000", + "740000000245aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000", + "740000000245aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000", + "740000000269aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d46d00000001", + "74000000027401f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf": "00000000023bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000ffffffff3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d901000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a0466000000204c679195282ea315863be7119a2f1f6a4b9486fe7f86e6e778435f4fe9ed4083d8a86a00000000000014bfe57d3d51c357a99aa9c84195fe8e000273399a00000000000002002103aa28c5dc394dd41bda11c296f1e0ef98edfa09e5730b6abdab5f1e1e700e2a61020021036b182787487be70c07012724b985e8fe7f49067f2a3e4c5535da0118a21d13fa0c000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "7400000002743bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d9": "0000000001abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a600000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d47cbe6a00000000000014b44aae19323f53e63e5cd184dba19a71616808150000000000000200210237b59c5644219778989265c96ecdb213578feca83fc908a975d8465359779e8807000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "740000000274abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a6": "00000000017b0aa1735e5ba58d3236316c671fe4f00ed366ee72417c9ed02a53a8019e85b800000000ffffffff01809698000000000000148ec33e2b7a9dc5fde7e6a166005d3b29265b313d000000000000000200000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f" + }, + "after": { + "5700000000": "077072696d617279", + "5700000001": "0777616c6c657431", + "5700000002": "0777616c6c657432", + "740000000142074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382ccc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000": "09746573746e616d653280841e000000000073000000011ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000", + "740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000000000000000000000000000000000000000000000000000000000000000000000ffffffff", + "740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000019a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000", + "740000000145074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000", + "740000000145aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000", + "740000000169074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "09746573746e616d653280841e0000000000d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980b6d00000001", + "740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d4ffffffff00", + "740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "09746573746e616d653180841e0000000000fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89056d00000001", + "7400000001741706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f4": "00000000029a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000ffffffff9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849101000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000020450566fda05dda024354f47c1f6d07769342a57c5dffe1d3a908a9043a83156bb8cd79000000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc000000000000020021037f44c97b1d5c69af830f7c14647c710e95300bd94dd92ad75fdd30ca7489287f020021024e3c15df14b521891f269af40258119698ebaed5f7299abf9583546e2438e73209000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "7400000001741b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a": "0000000001a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f30915501000000ffffffff02000000000000000000146a94c2244bd9a4b5257a8c946b68af72f4118b35020320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c040000000009746573746e616d6532207998000000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000200210256bc84ba99364f4a0146c1203b52104447d1085aee74df1242be003664e36cdf0400000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f", + "7400000001741ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a746": "0000000001a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc270200000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54030420074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000009746573746e616d653220d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980bbc007a00000000000014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c00000000000002002102df1a2c03a8d1b89b0b2f570ad99764f7dc4bcc710afa9745af65f2bc7789cfc806000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "7400000001742bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f3": "000000000111da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be900000000ffffffff0180969800000000000014cffd1219803f5f43f72a2a8e841a2fefb0347657000000000000000000000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f", + "7400000001749a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d8491": "00000000011b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a01000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89055ce37900000000000014222740511246fe2b10957049ae10e0bbe7fa996c000000000000020021023960f57b3d5844794ac0dd5576fb59b3a64d82d4d76285a36173d169412539e205000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "740000000174a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f309155": "00000000012bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f300000000ffffffff0200000000000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3020320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a040000000009746573746e616d6531d0879800000000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558000000000000020021020c30819abd60558d2f9d1fdfabb5d46df7254b5a9346c9d78dc9b5ace6b1089e0300000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f", + "740000000174a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc2702": "0000000001e12c22d4f162d9a012c9319233da5d3e923cc5e1029b8f90e47249c9ab256b3500000000ffffffff0180969800000000000014657eb0831fd4fafd3e595163416d76861ea9acd9000000000000000100000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f", + "740000000174cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd": "00000000021ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000ffffffff1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74601000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54040320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000020f70095f015fc11f5bae44f6136cbe1bb846103e79e065ea54f9e56f58e5322ab18eb79000000000000145258eb6f21709d00f66fa39deab6e8d5391eae5f00000000000002002103d1ab347d75498ccde90f26907c8aad98fcb5ecc922ff7de8f4be83c9b7a099b6020021021e772405b9b642a904d6e1f5739b56d1714327853f6cbb97777c70ef0c54c5450b000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000013bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000", + "740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000000000000000000000000000000000000000000000000000000000000000000000ffffffff", + "740000000245aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000", + "740000000269aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d46d00000001", + "74000000027401f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf": "00000000023bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000ffffffff3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d901000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a0466000000204c679195282ea315863be7119a2f1f6a4b9486fe7f86e6e778435f4fe9ed4083d8a86a00000000000014bfe57d3d51c357a99aa9c84195fe8e000273399a00000000000002002103aa28c5dc394dd41bda11c296f1e0ef98edfa09e5730b6abdab5f1e1e700e2a61020021036b182787487be70c07012724b985e8fe7f49067f2a3e4c5535da0118a21d13fa0c000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f", + "7400000002743bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d9": "0000000001abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a600000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d47cbe6a00000000000014b44aae19323f53e63e5cd184dba19a71616808150000000000000200210237b59c5644219778989265c96ecdb213578feca83fc908a975d8465359779e8807000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f", + "740000000274abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a6": "00000000017b0aa1735e5ba58d3236316c671fe4f00ed366ee72417c9ed02a53a8019e85b800000000ffffffff01809698000000000000148ec33e2b7a9dc5fde7e6a166005d3b29265b313d000000000000000200000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f" + } +} diff --git a/test/data/script-tests.json b/test/data/script-tests.json index 6b7115bc1..89fab98e5 100644 --- a/test/data/script-tests.json +++ b/test/data/script-tests.json @@ -10735,6 +10735,18 @@ "flags": [], "result": "NEGATIVE_LOCKTIME" }, + { + "comments": "CHECKLOCKTIMEVERIFY automatically fails if stack top is negative", + "script": "OP_CHECKLOCKTIMEVERIFY", + "value": 0, + "witness": [ + "81" + ], + "locktime": 0, + "sequence": 4294967295, + "flags": [], + "result": "NEGATIVE_LOCKTIME" + }, { "comments": "CSV fails if stack top is not minimally encoded", "script": "OP_CHECKSEQUENCEVERIFY", diff --git a/test/dns-test.js b/test/dns-test.js index dfb762d67..37a24c83f 100644 --- a/test/dns-test.js +++ b/test/dns-test.js @@ -13,7 +13,8 @@ const recursiveResolver = new Resolver({timeout: 1000}); rootResolver.setServers([`127.0.0.1:${network.nsPort}`]); recursiveResolver.setServers([`127.0.0.1:${network.rsPort}`]); -describe('Server Configuration', function() { +// TODO: Enable once node.js supports ANY + SIG(0) queries (c-ares issue) +describe.skip('Server Configuration', function() { describe('Full Node', function() { let node; diff --git a/test/getwork-test.js b/test/getwork-test.js index aaf4a9d04..f80657fc5 100644 --- a/test/getwork-test.js +++ b/test/getwork-test.js @@ -22,8 +22,10 @@ const {wdb} = node.require('walletdb'); let wallet = null; +const TIMEOUT = 45000; + describe('Get Work', function() { - this.timeout(45000); + this.timeout(TIMEOUT); it('should open chain and miner', async () => { await node.open(); @@ -36,13 +38,15 @@ describe('Get Work', function() { }); it('should mine 10 blocks', async () => { - const connectEvents = forEvent(chain, 'connect', 10, 10000); + const DELAY = 500; + const waits = DELAY * 10; + const connectEvents = forEvent(chain, 'connect', 10, TIMEOUT - waits); for (let i = 0; i < 10; i++) { const block = await miner.mineBlock(); assert(block); await chain.add(block); // lower mtp. - await sleep(500); + await sleep(DELAY); } await connectEvents; diff --git a/test/http-test.js b/test/http-test.js deleted file mode 100644 index b5c6a978b..000000000 --- a/test/http-test.js +++ /dev/null @@ -1,409 +0,0 @@ -'use strict'; - -const assert = require('bsert'); -const consensus = require('../lib/protocol/consensus'); -const Address = require('../lib/primitives/address'); -const Outpoint = require('../lib/primitives/outpoint'); -const MTX = require('../lib/primitives/mtx'); -const Script = require('../lib/script/script'); -const FullNode = require('../lib/node/fullnode'); -const pkg = require('../lib/pkg'); -const Network = require('../lib/protocol/network'); -const network = Network.get('regtest'); -const {ZERO_HASH} = consensus; - -const node = new FullNode({ - network: 'regtest', - apiKey: 'foo', - walletAuth: true, - memory: true, - workers: true, - plugins: [require('../lib/wallet/plugin')] -}); - -const {NodeClient, WalletClient} = require('hs-client'); - -const nclient = new NodeClient({ - port: network.rpcPort, - apiKey: 'foo' -}); - -const wclient = new WalletClient({ - port: network.walletPort, - apiKey: 'foo' -}); - -let wallet = null; - -const {wdb} = node.require('walletdb'); - -let addr = null; -let hash = null; - -describe('HTTP', function() { - this.timeout(20000); - - it('should open node', async () => { - await node.open(); - await nclient.open(); - await wclient.open(); - }); - - it('should create wallet', async () => { - const info = await wclient.createWallet('test'); - assert.strictEqual(info.id, 'test'); - wallet = wclient.wallet('test', info.token); - await wallet.open(); - }); - - it('should get info', async () => { - const info = await nclient.getInfo(); - assert.strictEqual(info.network, node.network.type); - assert.strictEqual(info.version, pkg.version); - assert(info.pool); - assert.strictEqual(info.pool.agent, node.pool.options.agent); - assert(info.chain); - assert.strictEqual(info.chain.height, 0); - assert.strictEqual(info.chain.treeRoot, ZERO_HASH.toString('hex')); - // state comes from genesis block - assert.strictEqual(info.chain.state.tx, 1); - assert.strictEqual(info.chain.state.coin, 1); - assert.strictEqual(info.chain.state.burned, 0); - }); - - it('should get wallet info', async () => { - const info = await wallet.getInfo(); - assert.strictEqual(info.id, 'test'); - const acct = await wallet.getAccount('default'); - const str = acct.receiveAddress; - assert(typeof str === 'string'); - addr = Address.fromString(str, node.network); - }); - - it('should fill with funds', async () => { - const mtx = new MTX(); - mtx.addOutpoint(new Outpoint(consensus.ZERO_HASH, 0)); - mtx.addOutput(addr, 50460); - mtx.addOutput(addr, 50460); - mtx.addOutput(addr, 50460); - mtx.addOutput(addr, 50460); - - const tx = mtx.toTX(); - - let balance = null; - wallet.once('balance', (b) => { - balance = b; - }); - - let receive = null; - wallet.once('address', (r) => { - receive = r[0]; - }); - - let details = null; - wallet.once('tx', (d) => { - details = d; - }); - - await wdb.addTX(tx); - await new Promise(r => setTimeout(r, 300)); - - assert(receive); - assert.strictEqual(receive.name, 'default'); - assert.strictEqual(receive.branch, 0); - assert(balance); - assert.strictEqual(balance.confirmed, 0); - assert.strictEqual(balance.unconfirmed, 201840); - assert(details); - assert.strictEqual(details.hash, tx.txid()); - }); - - it('should get balance', async () => { - const balance = await wallet.getBalance(); - assert.strictEqual(balance.confirmed, 0); - assert.strictEqual(balance.unconfirmed, 201840); - }); - - it('should send a tx', async () => { - const options = { - rate: 10000, - outputs: [{ - value: 10000, - address: addr.toString(node.network) - }] - }; - - const tx = await wallet.send(options); - - assert(tx); - assert.strictEqual(tx.inputs.length, 1); - assert.strictEqual(tx.outputs.length, 2); - - let value = 0; - value += tx.outputs[0].value; - value += tx.outputs[1].value; - - assert.strictEqual(value, 49060); - - hash = tx.hash; - }); - - it('should get a tx', async () => { - const tx = await wallet.getTX(hash); - assert(tx); - assert.strictEqual(tx.hash, hash); - }); - - it('should generate new api key', async () => { - const old = wallet.token.toString('hex'); - const result = await wallet.retoken(null); - assert.strictEqual(result.token.length, 64); - assert.notStrictEqual(result.token, old); - }); - - it('should get balance', async () => { - const balance = await wallet.getBalance(); - assert.strictEqual(balance.unconfirmed, 200440); - }); - - it('should execute an rpc call', async () => { - const info = await nclient.execute('getblockchaininfo', []); - assert.strictEqual(info.blocks, 0); - }); - - it('should execute an rpc call with bool parameter', async () => { - const info = await nclient.execute('getrawmempool', [true]); - assert.deepStrictEqual(info, {}); - }); - - it('should create account', async () => { - const info = await wallet.createAccount('foo1'); - assert(info); - assert(info.initialized); - assert.strictEqual(info.name, 'foo1'); - assert.strictEqual(info.accountIndex, 1); - assert.strictEqual(info.m, 1); - assert.strictEqual(info.n, 1); - }); - - it('should create account', async () => { - const info = await wallet.createAccount('foo2', { - type: 'multisig', - m: 1, - n: 2 - }); - assert(info); - assert(!info.initialized); - assert.strictEqual(info.name, 'foo2'); - assert.strictEqual(info.accountIndex, 2); - assert.strictEqual(info.m, 1); - assert.strictEqual(info.n, 2); - }); - - it('should get a block template', async () => { - const json = await nclient.execute('getblocktemplate', []); - assert.deepStrictEqual(json, { - capabilities: ['proposal'], - mutable: ['time', 'transactions', 'prevblock'], - version: 0, - rules: [], - vbavailable: {}, - vbrequired: 0, - height: 1, - previousblockhash: network.genesis.hash.toString('hex'), - treeroot: network.genesis.treeRoot.toString('hex'), - reservedroot: consensus.ZERO_HASH.toString('hex'), - mask: json.mask, - target: - '7fffff0000000000000000000000000000000000000000000000000000000000', - bits: '207fffff', - noncerange: '' - + '000000000000000000000000000000000000000000000000' - + 'ffffffffffffffffffffffffffffffffffffffffffffffff', - curtime: json.curtime, - mintime: 1580745081, - maxtime: json.maxtime, - expires: json.expires, - sigoplimit: 80000, - sizelimit: 1000000, - weightlimit: 4000000, - longpollid: node.chain.tip.hash.toString('hex') + '00000000', - submitold: false, - coinbaseaux: { flags: '6d696e656420627920687364' }, - coinbasevalue: 2000000000, - claims: [], - airdrops: [], - transactions: [] - }); - }); - - it('should send a block template proposal', async () => { - const attempt = await node.miner.createBlock(); - const block = attempt.toBlock(); - const hex = block.toHex(); - const json = await nclient.execute('getblocktemplate', [{ - mode: 'proposal', - data: hex - }]); - assert.strictEqual(json, null); - }); - - it('should validate an address', async () => { - const json = await nclient.execute('validateaddress', [ - addr.toString(node.network) - ]); - assert.deepStrictEqual(json, { - isvalid: true, - isscript: false, - isspendable: true, - address: addr.toString(node.network), - witness_program: addr.hash.toString('hex'), - witness_version: addr.version - }); - }); - - it('should not validate invalid address', async () => { - const json = await nclient.execute('validateaddress', [ - addr.toString('main') - ]); - assert.deepStrictEqual(json, { - isvalid: false - }); - }); - - it('should validate a p2wsh address', async () => { - const pubkeys = []; - for (let i = 0; i < 2; i++) { - const result = await wallet.createAddress('default'); - pubkeys.push(Buffer.from(result.publicKey, 'hex')); - } - - const script = Script.fromMultisig(2, 2, pubkeys); - const address = Address.fromScript(script); - - const json = await nclient.execute('validateaddress', [ - address.toString(node.network) - ]); - - assert.deepStrictEqual(json, { - address: address.toString(node.network), - isscript: true, - isspendable: true, - isvalid: true, - witness_version: address.version, - witness_program: address.hash.toString('hex') - }); - }); - - it('should validate a null address', async () => { - const data = Buffer.from('foobar', 'ascii'); - const nullAddr = Address.fromNulldata(data); - - const json = await nclient.execute('validateaddress', [ - nullAddr.toString(node.network) - ]); - - assert.deepStrictEqual(json, { - address: nullAddr.toString(node.network), - isscript: false, - isspendable: false, - isvalid: true, - witness_version: nullAddr.version, - witness_program: nullAddr.hash.toString('hex') - }); - }); - - it('should get mempool rejection filter', async () => { - const filterInfo = await nclient.get('/mempool/invalid', { verbose: true }); - - assert.ok('items' in filterInfo); - assert.ok('filter' in filterInfo); - assert.ok('size' in filterInfo); - assert.ok('entries' in filterInfo); - assert.ok('n' in filterInfo); - assert.ok('limit' in filterInfo); - assert.ok('tweak' in filterInfo); - - assert.equal(filterInfo.entries, 0); - }); - - it('should add an entry to the mempool rejection filter', async () => { - const mtx = new MTX(); - mtx.addOutpoint(new Outpoint(consensus.ZERO_HASH, 0)); - - const raw = mtx.toHex(); - const txid = await nclient.execute('sendrawtransaction', [raw]); - - const json = await nclient.get(`/mempool/invalid/${txid}`); - assert.equal(json.invalid, true); - - const filterInfo = await nclient.get('/mempool/invalid'); - assert.equal(filterInfo.entries, 1); - }); - - it('should generate 10 blocks from RPC call', async () => { - const blocks = await nclient.execute('generatetoaddress', [10, addr.toString(network)]); - assert.strictEqual(blocks.length, 10); - }); - - // depends on the previous test to generate blocks - it('should fetch block header by height', async () => { - // fetch corresponding header and block - const height = 7; - const header = await nclient.get(`/header/${height}`); - assert.equal(header.height, height); - - const properties = [ - 'hash', 'version', 'prevBlock', - 'merkleRoot', 'time', 'bits', - 'nonce', 'height', 'chainwork' - ]; - - for (const property of properties) - assert(property in header); - - const block = await nclient.getBlock(height); - - assert.equal(block.hash, header.hash); - assert.equal(block.height, header.height); - assert.equal(block.version, header.version); - assert.equal(block.prevBlock, header.prevBlock); - assert.equal(block.merkleRoot, header.merkleRoot); - assert.equal(block.time, header.time); - assert.equal(block.bits, header.bits); - assert.equal(block.nonce, header.nonce); - }); - - it('should fetch null for block header that does not exist', async () => { - // many blocks in the future - const header = await nclient.get(`/header/${40000}`); - assert.equal(header, null); - }); - - it('should have valid header chain', async () => { - // starting at the genesis block - let prevBlock = '0000000000000000000000000000000000000000000000000000000000000000'; - for (let i = 0; i < 10; i++) { - const header = await nclient.get(`/header/${i}`); - - assert.equal(prevBlock, header.prevBlock); - prevBlock = header.hash; - } - }); - - it('should fetch block header by hash', async () => { - const info = await nclient.getInfo(); - - const headerByHash = await nclient.get(`/header/${info.chain.tip}`); - const headerByHeight = await nclient.get(`/header/${info.chain.height}`); - - assert.deepEqual(headerByHash, headerByHeight); - }); - - it('should cleanup', async () => { - await wallet.close(); - await wclient.close(); - await nclient.close(); - await node.close(); - }); -}); diff --git a/test/interactive-swap-test.js b/test/interactive-swap-test.js index 13aa28e18..705418186 100644 --- a/test/interactive-swap-test.js +++ b/test/interactive-swap-test.js @@ -10,7 +10,7 @@ const Script = require('../lib/script/script'); const rules = require('../lib/covenants/rules'); const {types} = rules; const {Resource} = require('../lib/dns/resource'); -const {WalletClient} = require('hs-client'); +const WalletClient = require('../lib/client/wallet'); const network = Network.get('regtest'); @@ -83,7 +83,7 @@ describe('Interactive name swap', function() { }); it('should win name with Alice\'s wallet', async () => { - await alice.sendOpen(name, false); + await alice.sendOpen(name); await mineBlocks(network.names.treeInterval + 1); await alice.sendBid(name, 100000, 200000); @@ -102,7 +102,7 @@ describe('Interactive name swap', function() { }); it('should not be able to send a TRANSFER before REGISTER', async () => { - assert.rejects(async () => { + await assert.rejects(async () => { await alice.sendTransfer(name, bobReceive); }, { message: `Name is not registered: ${name}.` @@ -163,14 +163,15 @@ describe('Interactive name swap', function() { const output0 = new Output(); output0.value = coin.value; output0.address = bobReceive; - output0.covenant.type = types.FINALIZE; - output0.covenant.pushHash(nameHash); - output0.covenant.pushU32(ns.height); - output0.covenant.push(Buffer.from(name, 'ascii')); - output0.covenant.pushU8(0); // flags, may be required if name was CLAIMed - output0.covenant.pushU32(ns.claimed); - output0.covenant.pushU32(ns.renewals); - output0.covenant.pushHash(await wdb.getRenewalBlock()); + output0.covenant.setFinalize( + nameHash, + ns.height, + Buffer.from(name, 'ascii'), + 0, // flags, may be required if name was CLAIMed + ns.claimed, + ns.renewals, + await wdb.getRenewalBlock() + ); const output1 = new Output(); output1.address = aliceReceive; diff --git a/test/mempool-invalidation-test.js b/test/mempool-invalidation-test.js new file mode 100644 index 000000000..120d781e1 --- /dev/null +++ b/test/mempool-invalidation-test.js @@ -0,0 +1,621 @@ +'use strict'; + +const assert = require('bsert'); +const {BufferMap} = require('buffer-map'); +const Network = require('../lib/protocol/network'); +const {ownership} = require('../lib/covenants/ownership'); +const rules = require('../lib/covenants/rules'); +const {states} = require('../lib/covenants/namestate'); +const {Resource} = require('../lib/dns/resource'); +const {forEvent} = require('./util/common'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); +const NodeContext = require('./util/node-context'); + +const network = Network.get('regtest'); +const { + treeInterval, + claimPeriod, + renewalWindow +} = network.names; + +const ACTUAL_CLAIM_PERIOD = claimPeriod; +const ACTUAL_RENEWAL_WINDOW = renewalWindow; + +describe('Mempool Invalidation', function() { + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + + before(() => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; + }); + + after(() => { + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + }); + + const NAMES = [ + // roots + 'nl', + + // top 100 + 'paypal', + + // custom + 'cloudflare', + + // other + 'steamdb' + ]; + + describe('Covenant invalidation (Integration)', function() { + this.timeout(3000); + let nodeCtx; + let node, wallet, wallet2; + + const getNameState = async (name) => { + const ns = await nodeCtx.chain.db.getNameStateByName(name); + + if (!ns) + return null; + + return ns.state(nodeCtx.chain.tip.height + 1, network); + }; + + const isExpired = async (name) => { + const ns = await nodeCtx.chain.db.getNameStateByName(name); + + if (!ns) + return true; + + return ns.isExpired(nodeCtx.chain.tip.height + 1, network); + }; + + before(async () => { + network.names.renewalWindow = 200; + + nodeCtx = new NodeContext({ + network: 'regtest', + memory: true, + wallet: true + }); + + await nodeCtx.open(); + + node = nodeCtx.node; + + const wdb = nodeCtx.wdb; + wallet = await wdb.get('primary'); + wallet2 = await wdb.create({ + id: 'secondary' + }); + + const addr = await wallet.receiveAddress('default'); + node.miner.addAddress(addr.toString()); + + for (let i = 0; i < treeInterval; i++) + await mineBlock(node); + + const fundTX = forEvent(nodeCtx.mempool, 'tx', 1, 2000); + const w2addr = (await wallet2.receiveAddress('default')).toString(); + + await wallet.send({ + outputs: [{ + address: w2addr, + value: 10e6 + }, { + address: w2addr, + value: 10e6 + }, { + address: w2addr, + value: 10e6 + }] + }); + + await fundTX; + await mineBlock(node); + }); + + after(async () => { + network.names.renewalWindow = ACTUAL_RENEWAL_WINDOW; + await nodeCtx.close(); + await nodeCtx.destroy(); + }); + + it('should invalidate opens', async () => { + // This is handled in remove Double Opens on addBlock. + assert.strictEqual(node.mempool.map.size, 0); + + const name = rules.grindName(10, 0, network); + + const txEvents = forEvent(node.mempool, 'tx', 1, 2000); + + const blkopen = await wallet.createOpen(name); + await wallet.sign(blkopen); + + const memopen = await wallet2.sendOpen(name); + await txEvents; + + assert(node.mempool.map.has(memopen.hash())); + assert.strictEqual(node.mempool.map.size, 1); + + assert.strictEqual(await getNameState(name), null); + + { + const tx = blkopen.commit(); + await mineBlock(node, { + empty: true, + txs: [tx] + }); + } + + assert.strictEqual(await getNameState(name), states.OPENING); + + assert.strictEqual(node.mempool.map.size, 0); + const pending = await wallet2.getPending(); + assert.strictEqual(pending.length, 0); + }); + + it('should invalidate bids', async () => { + assert.strictEqual(node.mempool.map.size, 0); + + const name = rules.grindName(10, 0, network); + const txEvent = forEvent(node.mempool, 'tx', 1, 2000); + await wallet.sendOpen(name); + await txEvent; + + for (let i = 0; i < treeInterval + 1; i++) + await mineBlock(node); + + assert.strictEqual(await getNameState(name), states.BIDDING); + + const txEvents = forEvent(node.mempool, 'tx', 2, 2000); + const bid1 = await wallet2.sendBid(name, 1e6, 1e6); + const bid2 = await wallet.sendBid(name, 1e6, 1e6); + await txEvents; + + assert.strictEqual(node.mempool.map.size, 2); + + // leave 2 blocks, 1 for bid inclusion another for ending bidding period. + for (let i = 0; i < network.names.biddingPeriod - 2; i++) + await mineBlock(node, { empty: true }); + + assert.strictEqual(node.mempool.map.size, 2); + assert.strictEqual(await getNameState(name), states.BIDDING); + + { + // this one finally ends the bidding period. + await mineBlock(node, { + empty: true, + txs: [[bid1]] + }); + } + + assert(node.mempool.map.has(bid2.hash())); + assert.strictEqual(node.mempool.map.size, 1); + + await mineBlock(node, { empty: true }); + assert.strictEqual(node.mempool.map.size, 0); + assert.strictEqual(await getNameState(name), states.REVEAL); + + await wallet.abandon(bid2.hash()); + }); + + it('should invalidate reveals', async () => { + let txEvents; + assert.strictEqual(node.mempool.map.size, 0); + + const name = rules.grindName(10, 0, network); + + await wallet.sendOpen(name); + + for (let i = 0; i < treeInterval + 1; i++) + await mineBlock(node); + + txEvents = forEvent(node.mempool, 'tx', 2, 2000); + await wallet.sendBid(name, 1e6, 1e6); + await wallet2.sendBid(name, 1e6, 1e6); + await txEvents; + + assert.strictEqual(node.mempool.map.size, 2); + + for (let i = 0; i < network.names.biddingPeriod; i++) + await mineBlock(node); + + assert.strictEqual(node.mempool.map.size, 0); + assert.strictEqual(await getNameState(name), states.REVEAL); + + txEvents = forEvent(node.mempool, 'tx', 2, 2000); + const reveal1 = await wallet.sendReveal(name); + const reveal2 = await wallet2.sendReveal(name); + await txEvents; + + assert.strictEqual(node.mempool.map.size, 2); + assert.strictEqual(await getNameState(name), states.REVEAL); + + for (let i = 0; i < network.names.revealPeriod - 1; i++) + await mineBlock(node, { empty: true }); + + // include only one in the last block. + await mineBlock(node, { + empty: true, + txs: [[reveal2]] + }); + + assert.strictEqual(await getNameState(name), states.CLOSED); + assert.strictEqual(node.mempool.map.size, 0); + await wallet.abandon(reveal1.hash()); + }); + + it('should invalidate reveals with expire', async () => { + let txEvents; + assert.strictEqual(node.mempool.map.size, 0); + + const name = rules.grindName(10, 0, network); + + await wallet.sendOpen(name); + + for (let i = 0; i < treeInterval + 1; i++) + await mineBlock(node); + + txEvents = forEvent(node.mempool, 'tx', 2, 2000); + await wallet.sendBid(name, 1e6, 1e6); + await wallet2.sendBid(name, 1e6, 1e6); + await txEvents; + + assert.strictEqual(node.mempool.map.size, 2); + + for (let i = 0; i < network.names.biddingPeriod; i++) + await mineBlock(node); + + assert.strictEqual(node.mempool.map.size, 0); + assert.strictEqual(await getNameState(name), states.REVEAL); + + txEvents = forEvent(node.mempool, 'tx', 2, 2000); + await wallet.sendReveal(name); + await wallet2.sendReveal(name); + await txEvents; + + assert.strictEqual(node.mempool.map.size, 2); + assert.strictEqual(await getNameState(name), states.REVEAL); + + for (let i = 0; i < network.names.revealPeriod; i++) + await mineBlock(node, { empty: true }); + + assert.strictEqual(await getNameState(name), states.CLOSED); + assert.strictEqual(node.mempool.map.size, 0); + }); + + it('should invalidate updates when name expires', async () => { + let txEvents; + + assert.strictEqual(node.mempool.map.size, 0); + const name = rules.grindName(10, 0, network); + + await wallet.sendOpen(name); + + for (let i = 0; i < treeInterval + 1; i++) + await mineBlock(node); + + txEvents = forEvent(node.mempool, 'tx', 2, 2000); + await wallet.sendBid(name, 1e6, 1e6); + await wallet2.sendBid(name, 1e6, 1e6); + await txEvents; + + assert.strictEqual(node.mempool.map.size, 2); + + for (let i = 0; i < network.names.biddingPeriod; i++) + await mineBlock(node); + + assert.strictEqual(node.mempool.map.size, 0); + assert.strictEqual(await getNameState(name), states.REVEAL); + + txEvents = forEvent(node.mempool, 'tx', 2, 2000); + await wallet.sendReveal(name); + await wallet2.sendReveal(name); + await txEvents; + + for (let i = 0; i < network.names.revealPeriod; i++) + await mineBlock(node); + + assert.strictEqual(await getNameState(name), states.CLOSED); + + await wallet.sendUpdate(name, Resource.fromJSON({ records: [] })); + + for (let i = 0; i < network.names.renewalWindow - 2; i++) + await mineBlock(node); + + txEvents = forEvent(node.mempool, 'tx', 1, 2000); + await wallet.sendRenewal(name); + await txEvents; + assert.strictEqual(node.mempool.map.size, 1); + + assert.strictEqual(await getNameState(name), states.CLOSED); + assert.strictEqual(await isExpired(name), false); + + await mineBlock(node, { empty: true }); + assert.strictEqual(node.mempool.map.size, 1); + assert.strictEqual(await isExpired(name), false); + await mineBlock(node, { empty: true }); + assert.strictEqual(await isExpired(name), true); + assert.strictEqual(node.mempool.map.size, 0); + }); + }); + + describe('Claim Invalidation (Integration)', function() { + this.timeout(50000); + + let nodeCtx; + let node, wallet; + + // copy names + const TEST_CLAIMS = NAMES.slice(); + + before(async () => { + nodeCtx = new NodeContext({ + network: network.type, + memory: true, + wallet: true + }); + + await nodeCtx.open(); + node = nodeCtx.node; + + // Ignore claim validation + ownership.ignore = true; + + const wdb = nodeCtx.wdb; + wallet = await wdb.get('primary'); + + const addr = await wallet.receiveAddress('default'); + node.miner.addAddress(addr.toString()); + + // first interval maturity + // second interval mine claim + network.names.claimPeriod = treeInterval * 3; + + // third interval last block should invalidate. + }); + + after(async () => { + network.names.claimPeriod = ACTUAL_CLAIM_PERIOD; + + await nodeCtx.close(); + await nodeCtx.destroy(); + }); + + it('should mine an interval', async () => { + for (let i = 0; i < treeInterval; i++) + await mineBlock(node); + }); + + it('should mine claims before claimPeriod timeout', async () => { + const name = TEST_CLAIMS.shift(); + + const claim = await wallet.makeFakeClaim(name); + let block; + + await node.mempool.insertClaim(claim); + assert.strictEqual(node.mempool.claims.size, 1); + + // retain claim in mempool. + [block] = await mineBlock(node, { ignoreClaims: true }); + assert.strictEqual(node.mempool.claims.size, 1); + assert.strictEqual(block.txs[0].outputs.length, 1); + + // Now we can mine it. + [block] = await mineBlock(node); + assert.strictEqual(node.mempool.claims.size, 0); + assert.strictEqual(block.txs[0].outputs.length, 2); + assert.strictEqual(block.txs[0].outputs[1].covenant.type, rules.types.CLAIM); + }); + + it('should invalidate claim after claimPeriod timeout', async () => { + const name = TEST_CLAIMS.shift(); + const claim = await wallet.makeFakeClaim(name); + + let block = null; + + // Mempool treats txs in it as if they were mined in the next block, + // so we need next block to still be valid. + while (node.chain.tip.height < network.names.claimPeriod - 2) + await mineBlock(node); + + await node.mempool.insertClaim(claim); + [block] = await mineBlock(node, { ignoreClaims: true }); + + // Should invalidate the claim, because next block can't have claims. + assert.strictEqual(node.mempool.claims.size, 0); + assert.strictEqual(block.txs[0].outputs.length, 1); + + // Should fail to insert claim, as they can't be mined. + let err; + try { + err = await node.mempool.insertClaim(claim); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.type, 'VerifyError'); + assert.strictEqual(err.reason, 'invalid-covenant'); + + [block] = await mineBlock(node); + assert.strictEqual(node.mempool.claims.size, 0); + assert.strictEqual(block.txs[0].outputs.length, 1); + }); + }); + + describe('Claim Invalidation on reorg (Integration)', function() { + this.timeout(50000); + + let nodeCtx; + let node, wallet; + + // copy names + const TEST_CLAIMS = NAMES.slice(); + + before(async () => { + nodeCtx = new NodeContext({ + network: network.type, + memory: true, + wallet: true + }); + + await nodeCtx.open(); + + // Ignore claim validation + ownership.ignore = true; + + node = nodeCtx.node; + const wdb = nodeCtx.wdb; + wallet = await wdb.get('primary'); + + const addr = await wallet.receiveAddress('default'); + node.miner.addAddress(addr.toString()); + + // first interval maturity + // second interval mine claim + network.names.claimPeriod = treeInterval * 3; + + // third interval last block should invalidate. + }); + + after(async () => { + network.names.claimPeriod = ACTUAL_CLAIM_PERIOD; + + await nodeCtx.close(); + await nodeCtx.destroy(); + }); + + it('should mine an interval', async () => { + for (let i = 0; i < treeInterval; i++) + await mineBlock(node); + }); + + it('should mine claims before claimPeriod timeout', async () => { + const name = TEST_CLAIMS.shift(); + + const claim = await wallet.makeFakeClaim(name); + let block; + + await node.mempool.insertClaim(claim); + assert.strictEqual(node.mempool.claims.size, 1); + + // retain claim in mempool. + [block] = await mineBlock(node, { ignoreClaims: true }); + assert.strictEqual(node.mempool.claims.size, 1); + assert.strictEqual(block.txs[0].outputs.length, 1); + + // Now we can mine it. + [block] = await mineBlock(node); + assert.strictEqual(node.mempool.claims.size, 0); + assert.strictEqual(block.txs[0].outputs.length, 2); + assert.strictEqual(block.txs[0].outputs[1].covenant.type, rules.types.CLAIM); + }); + + it('should invalidate claim after claimPeriod timeout', async () => { + const name = TEST_CLAIMS.shift(); + const claim = await wallet.makeFakeClaim(name); + + let block, entry; + + // Mempool treats txs in it as if they were mined in the next block, + // so we need next block to still be valid. + while (node.chain.tip.height < network.names.claimPeriod - 2) + await mineBlock(node); + + await node.mempool.insertClaim(claim); + // here we experience a reorg into the claim period. + const tip = node.chain.tip; + const prev = await node.chain.getPrevious(tip); + + [block, entry] = await mineBlock(node, { + ignoreClaims: true, + tip: prev, + blockWait: false + }); + + assert.strictEqual(node.mempool.claims.size, 1); + assert.strictEqual(block.txs[0].outputs.length, 1); + + // Now reorg. + [block, entry] = await mineBlock(node, { + ignoreClaims: true, + tip: entry + }); + + // Should invalidate the claim, because next block can't have claims. + assert.strictEqual(node.mempool.claims.size, 0); + assert.strictEqual(block.txs[0].outputs.length, 1); + + // Should fail to insert claim, as they can't be mined. + let err; + try { + err = await node.mempool.insertClaim(claim); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.type, 'VerifyError'); + assert.strictEqual(err.reason, 'invalid-covenant'); + + [block] = await mineBlock(node); + assert.strictEqual(node.mempool.claims.size, 0); + assert.strictEqual(block.txs[0].outputs.length, 1); + }); + }); +}); + +async function mineBlock(node, opts = {}) { + assert(node); + const chain = node.chain; + const miner = node.miner; + + const ignoreClaims = opts.ignoreClaims ?? false; + const tip = opts.tip || chain.tip; + const blockWait = opts.blockWait ?? true; + const empty = opts.empty ?? false; + const txs = opts.txs ?? []; + + let forBlock = null; + + if (blockWait) + forBlock = forEvent(node, 'block', 1, 2000); + + let backupClaims = null; + let backupTXs = null; + + if (ignoreClaims) { + backupClaims = node.mempool.claims; + node.mempool.claims = new BufferMap(); + } + + if (empty) { + backupTXs = node.mempool.map; + node.mempool.map = new BufferMap(); + } + + const job = await miner.cpu.createJob(tip); + + for (const [tx, view] of txs) + job.pushTX(tx, view); + + job.refresh(); + + if (ignoreClaims) + node.mempool.claims = backupClaims; + + if (empty) + node.mempool.map = backupTXs; + + const block = await job.mineAsync(); + const entry = await chain.add(block); + + if (blockWait) + await forBlock; + + return [block, entry]; +} diff --git a/test/mempool-reorg-test.js b/test/mempool-reorg-test.js index 330558ca9..16a870a83 100644 --- a/test/mempool-reorg-test.js +++ b/test/mempool-reorg-test.js @@ -8,6 +8,7 @@ const plugin = require('../lib/wallet/plugin'); const Coin = require('../lib/primitives/coin'); const Address = require('../lib/primitives/address'); const MTX = require('../lib/primitives/mtx'); +const {forEvent} = require('./util/common'); const network = Network.get('regtest'); const { @@ -25,8 +26,13 @@ describe('Mempool Covenant Reorg', function () { let wallet, name; before(async () => { + const wdb = node.require('walletdb').wdb; + const syncDone = forEvent(wdb, 'sync done'); await node.open(); + wallet = node.get('walletdb').wdb.primary; + + await syncDone; }); after(async () => { diff --git a/test/mempool-test.js b/test/mempool-test.js index 9a4725c12..7d3fa14c3 100644 --- a/test/mempool-test.js +++ b/test/mempool-test.js @@ -30,68 +30,25 @@ const ALL = Script.hashType.ALL; const common = require('../lib/blockchain/common'); const VERIFY_BODY = common.flags.VERIFY_BODY; const rules = require('../lib/covenants/rules'); -const {types} = rules; const NameState = require('../lib/covenants/namestate'); const {states} = NameState; -const ownership = require('../lib/covenants/ownership'); +const {ownership} = require('../lib/covenants/ownership'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); const ONE_HASH = Buffer.alloc(32, 0x00); ONE_HASH[0] = 0x01; const network = Network.get('regtest'); -const workers = new WorkerPool({ - enabled: true, - size: 2 -}); - -const blocks = new BlockStore({ - memory: true, - network -}); - -const chain = new Chain({ - memory: true, - network, - blocks, - workers -}); - -const mempool = new Mempool({ - chain, - memory: true, - workers -}); - -const wallet = new MemWallet({ network }); - -let cachedTX = null; - -function dummyInput(addr, hash, value = 70000) { - const coin = new Coin(); - coin.height = 0; - coin.value = 0; - coin.address = addr; - coin.hash = hash; - coin.index = 0; - - const fund = new MTX(); - fund.addCoin(coin); - fund.addOutput(addr, value); - - const [tx, view] = fund.commit(); - - const entry = MempoolEntry.fromTX(tx, view, 0); - mempool.trackEntry(entry, view); - - return Coin.fromTX(fund, 0, -1); -} +async function getMockBlock(chain, txs = [], cb = true) { + if (cb) { + const raddr = KeyRing.generate().getAddress(); + const mtx = new MTX(); + mtx.addInput(new Input()); + mtx.addOutput(raddr, 0); + mtx.locktime = chain.height + 1; -async function dummyBlock(txs, coinbase = false) { - if (coinbase) { - const cb = new MTX(); - cb.locktime = chain.height + 1; - txs = [cb, ...txs]; + txs = [mtx.toTX(), ...txs]; } const view = new CoinView(); @@ -102,20 +59,16 @@ async function dummyBlock(txs, coinbase = false) { const now = util.now(); const time = chain.tip.time <= now ? chain.tip.time + 1 : now; - const block = new Block({ - version: 1, - prevBlock: Buffer.from(chain.tip.hash, 'hex'), - merkleRoot: random.randomBytes(32), - witnessRoot: random.randomBytes(32), - treeRoot: random.randomBytes(32), - reservedRoot: random.randomBytes(32), - time: time, - bits: await chain.getTarget(time, chain.tip), - nonce: 0, - extraNonce: Buffer.alloc(consensus.NONCE_SIZE), - mask: random.randomBytes(32), - txs: txs - }); + const block = new Block(); + block.txs = txs; + block.prevBlock = chain.tip.hash; + block.time = time; + block.bits = await chain.getTarget(block.time, chain.tip); + + // Ensure mockblocks are unique (required for reorg testing) + block.merkleRoot = block.createMerkleRoot(); + block.witnessRoot = block.createWitnessRoot(); + block.treeRoot = chain.db.treeRoot(); return [block, view]; } @@ -123,435 +76,499 @@ async function dummyBlock(txs, coinbase = false) { describe('Mempool', function() { this.timeout(5000); - it('should open mempool', async () => { - await workers.open(); - await blocks.open(); - await chain.open(); - await mempool.open(); + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + + before(() => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; }); - it('should handle incoming orphans and TXs', async () => { - const key = KeyRing.generate(); - const addr = key.getAddress(); + after(() => { + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + }); - const t1 = new MTX(); - t1.addOutput(wallet.getAddress(), 50000); - t1.addOutput(wallet.getAddress(), 10000); + describe('Mempool TXs', function() { + let workers, blocks, chain, mempool, wallet; + let cachedTX; - const script = Script.fromPubkeyhash(key.getHash()); + const dummyInput = (addr, hash, value = 70000) => { + const coin = new Coin(); + coin.height = 0; + coin.value = 0; + coin.address = addr; + coin.hash = hash; + coin.index = 0; - t1.addCoin(dummyInput(addr, ONE_HASH)); + const fund = new MTX(); + fund.addCoin(coin); + fund.addOutput(addr, value); - const sig = t1.signature(0, script, 70000, key.privateKey, ALL); + const [tx, view] = fund.commit(); - t1.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); + const entry = MempoolEntry.fromTX(tx, view, 0); - // balance: 51000 - wallet.sign(t1); + mempool.trackEntry(entry, view); - const t2 = new MTX(); - t2.addTX(t1, 0); // 50000 - t2.addOutput(wallet.getAddress(), 20000); - t2.addOutput(wallet.getAddress(), 20000); + return Coin.fromTX(fund, 0, -1); + }; - // balance: 49000 - wallet.sign(t2); + before(async () => { + workers = new WorkerPool({ + enabled: true, + size: 2 + }); - const t3 = new MTX(); - t3.addTX(t1, 1); // 10000 - t3.addTX(t2, 0); // 20000 - t3.addOutput(wallet.getAddress(), 23000); + blocks = new BlockStore({ + memory: true, + network + }); - // balance: 47000 - wallet.sign(t3); + chain = new Chain({ + memory: true, + network, + blocks, + workers + }); - const t4 = new MTX(); - t4.addTX(t2, 1); // 24000 - t4.addTX(t3, 0); // 23000 - t4.addOutput(wallet.getAddress(), 11000); - t4.addOutput(wallet.getAddress(), 11000); + mempool = new Mempool({ + chain, + memory: true, + workers + }); - // balance: 22000 - wallet.sign(t4); + wallet = new MemWallet({ network }); - const f1 = new MTX(); - f1.addTX(t4, 1); // 11000 - f1.addOutput(new Address(), 9000); + await workers.open(); + await blocks.open(); + await chain.open(); + await mempool.open(); + }); - // balance: 11000 - wallet.sign(f1); + after(async () => { + await mempool.close(); + await chain.close(); + await blocks.close(); + await workers.close(); + }); - const fake = new MTX(); - fake.addTX(t1, 1); // 1000 (already redeemed) - fake.addOutput(wallet.getAddress(), 6000); // 6000 instead of 500 + it('should handle incoming orphans and TXs', async () => { + const key = KeyRing.generate(); + const addr = key.getAddress(); - // Script inputs but do not sign - wallet.template(fake); + const t1 = new MTX(); + t1.addOutput(wallet.getAddress(), 50000); + t1.addOutput(wallet.getAddress(), 10000); - // Fake signature - const input = fake.inputs[0]; - input.witness.setData(0, Buffer.alloc(65, 0x00)); - input.witness.compile(); - // balance: 11000 + const script = Script.fromPubkeyhash(key.getHash()); - { - await mempool.addTX(fake.toTX()); - await mempool.addTX(t4.toTX()); + t1.addCoin(dummyInput(addr, ONE_HASH)); - const balance = mempool.getBalance(); - assert.strictEqual(balance, 70000); - } + const sig = t1.signature(0, script, 70000, key.privateKey, ALL); - { - await mempool.addTX(t1.toTX()); + t1.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); - const balance = mempool.getBalance(); - assert.strictEqual(balance, 60000); - } + // balance: 51000 + wallet.sign(t1); - { - await mempool.addTX(t2.toTX()); + const t2 = new MTX(); + t2.addTX(t1, 0); // 50000 + t2.addOutput(wallet.getAddress(), 20000); + t2.addOutput(wallet.getAddress(), 20000); - const balance = mempool.getBalance(); - assert.strictEqual(balance, 50000); - } + // balance: 49000 + wallet.sign(t2); - { - await mempool.addTX(t3.toTX()); + const t3 = new MTX(); + t3.addTX(t1, 1); // 10000 + t3.addTX(t2, 0); // 20000 + t3.addOutput(wallet.getAddress(), 23000); - const balance = mempool.getBalance(); - assert.strictEqual(balance, 22000); - } + // balance: 47000 + wallet.sign(t3); - { - await mempool.addTX(f1.toTX()); + const t4 = new MTX(); + t4.addTX(t2, 1); // 24000 + t4.addTX(t3, 0); // 23000 + t4.addOutput(wallet.getAddress(), 11000); + t4.addOutput(wallet.getAddress(), 11000); - const balance = mempool.getBalance(); - assert.strictEqual(balance, 20000); - } + // balance: 22000 + wallet.sign(t4); - const txs = mempool.getHistory(); - assert(txs.some((tx) => { - return tx.hash().equals(f1.hash()); - })); - }); + const f1 = new MTX(); + f1.addTX(t4, 1); // 11000 + f1.addOutput(new Address(), 9000); - it('should get spent coins and reflect in coinview', async () => { - const wallet = new MemWallet({ network }); - const addr = wallet.getAddress(); - - const dummyCoin = dummyInput(addr, random.randomBytes(32)); - - const mtx1 = new MTX(); - mtx1.addOutput(wallet.getAddress(), 50000); - mtx1.addCoin(dummyCoin); - - wallet.sign(mtx1); - - const tx1 = mtx1.toTX(); - const coin1 = Coin.fromTX(tx1, 0, -1); - - const mtx2 = new MTX(); - mtx2.addOutput(wallet.getAddress(), 10000); - mtx2.addOutput(wallet.getAddress(), 30000); // 10k fee - mtx2.addCoin(coin1); - - wallet.sign(mtx2); - - const tx2 = mtx2.toTX(); - - await mempool.addTX(tx1); - - { - const view = await mempool.getCoinView(tx2); - const sview = await mempool.getSpentView(tx2); - assert(view.hasEntry(coin1)); - assert(sview.hasEntry(coin1)); - assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), true); - assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), false); - } - - await mempool.addTX(tx2); - - { - const view = await mempool.getCoinView(tx1); - const sview = await mempool.getSpentView(tx1); - assert(!view.hasEntry(dummyCoin)); - assert(sview.hasEntry(dummyCoin)); - assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), false); - assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), true); - } - - { - const view = await mempool.getCoinView(tx2); - const sview = await mempool.getSpentView(tx2); - assert(!view.hasEntry(coin1)); - assert(sview.hasEntry(coin1)); - assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), false); - assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), true); - } - }); + // balance: 11000 + wallet.sign(f1); - it('should handle locktime', async () => { - const key = KeyRing.generate(); - const addr = key.getAddress(); + const fake = new MTX(); + fake.addTX(t1, 1); // 1000 (already redeemed) + fake.addOutput(wallet.getAddress(), 6000); // 6000 instead of 500 - const tx = new MTX(); - tx.addOutput(wallet.getAddress(), 50000); - tx.addOutput(wallet.getAddress(), 10000); + // Script inputs but do not sign + wallet.template(fake); - const prev = Script.fromPubkeyhash(key.getHash()); - const prevHash = random.randomBytes(32); + // Fake signature + const input = fake.inputs[0]; + input.witness.setData(0, Buffer.alloc(65, 0x00)); + input.witness.compile(); + // balance: 11000 - tx.addCoin(dummyInput(addr, prevHash)); - tx.setLocktime(200); + { + await mempool.addTX(fake.toTX()); + await mempool.addTX(t4.toTX()); - chain.tip.height = 200; + const balance = mempool.getBalance(); + assert.strictEqual(balance, 70000); + } - const sig = tx.signature(0, prev, 70000, key.privateKey, ALL); - tx.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); + { + await mempool.addTX(t1.toTX()); - await mempool.addTX(tx.toTX()); - chain.tip.height = 0; - }); + const balance = mempool.getBalance(); + assert.strictEqual(balance, 60000); + } - it('should handle invalid locktime', async () => { - const key = KeyRing.generate(); - const addr = key.getAddress(); + { + await mempool.addTX(t2.toTX()); - const tx = new MTX(); - tx.addOutput(wallet.getAddress(), 50000); - tx.addOutput(wallet.getAddress(), 10000); + const balance = mempool.getBalance(); + assert.strictEqual(balance, 50000); + } - const prev = Script.fromPubkeyhash(key.getHash()); - const prevHash = random.randomBytes(32); + { + await mempool.addTX(t3.toTX()); - tx.addCoin(dummyInput(addr, prevHash)); - tx.setLocktime(200); - chain.tip.height = 200 - 1; + const balance = mempool.getBalance(); + assert.strictEqual(balance, 22000); + } - const sig = tx.signature(0, prev, 70000, key.privateKey, ALL); - tx.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); + { + await mempool.addTX(f1.toTX()); - let err; - try { - await mempool.addTX(tx.toTX()); - } catch (e) { - err = e; - } + const balance = mempool.getBalance(); + assert.strictEqual(balance, 20000); + } - assert(err); + const txs = mempool.getHistory(); + assert(txs.some((tx) => { + return tx.hash().equals(f1.hash()); + })); + }); - chain.tip.height = 0; - }); + it('should get spent coins and reflect in coinview', async () => { + const wallet = new MemWallet({ network }); + const addr = wallet.getAddress(); - it('should not cache a malleated wtx with mutated sig', async () => { - const key = KeyRing.generate(); - const addr = key.getAddress(); + const dummyCoin = dummyInput(addr, random.randomBytes(32)); - const tx = new MTX(); - tx.addOutput(wallet.getAddress(), 50000); - tx.addOutput(wallet.getAddress(), 10000); + const mtx1 = new MTX(); + mtx1.addOutput(wallet.getAddress(), 50000); + mtx1.addCoin(dummyCoin); - const prevHash = random.randomBytes(32); + wallet.sign(mtx1); - tx.addCoin(dummyInput(addr, prevHash)); + const tx1 = mtx1.toTX(); + const coin1 = Coin.fromTX(tx1, 0, -1); - const prevs = Script.fromPubkeyhash(key.getKeyHash()); + const mtx2 = new MTX(); + mtx2.addOutput(wallet.getAddress(), 10000); + mtx2.addOutput(wallet.getAddress(), 30000); // 10k fee + mtx2.addCoin(coin1); - const sig = tx.signature(0, prevs, 70000, key.privateKey, ALL); - sig[sig.length - 1] = 0; + wallet.sign(mtx2); - tx.inputs[0].witness = new Witness([sig, key.publicKey]); + const tx2 = mtx2.toTX(); - let err; - try { - await mempool.addTX(tx.toTX()); - } catch (e) { - err = e; - } + await mempool.addTX(tx1); - assert(err); - assert(!mempool.hasReject(tx.hash())); - }); + { + const view = await mempool.getCoinView(tx2); + const sview = await mempool.getSpentView(tx2); + assert(view.hasEntry(coin1)); + assert(sview.hasEntry(coin1)); + assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), true); + assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), false); + } - it('should not cache non-malleated tx without sig', async () => { - const key = KeyRing.generate(); - const addr = key.getAddress(); + await mempool.addTX(tx2); - const tx = new MTX(); - tx.addOutput(wallet.getAddress(), 50000); - tx.addOutput(wallet.getAddress(), 10000); + { + const view = await mempool.getCoinView(tx1); + const sview = await mempool.getSpentView(tx1); + assert(!view.hasEntry(dummyCoin)); + assert(sview.hasEntry(dummyCoin)); + assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), false); + assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), true); + } - const prevHash = random.randomBytes(32); + { + const view = await mempool.getCoinView(tx2); + const sview = await mempool.getSpentView(tx2); + assert(!view.hasEntry(coin1)); + assert(sview.hasEntry(coin1)); + assert.strictEqual(mempool.hasCoin(coin1.hash, coin1.index), false); + assert.strictEqual(mempool.isSpent(coin1.hash, coin1.index), true); + } + }); + + it('should handle locktime', async () => { + const key = KeyRing.generate(); + const addr = key.getAddress(); - tx.addCoin(dummyInput(addr, prevHash)); + const tx = new MTX(); + tx.addOutput(wallet.getAddress(), 50000); + tx.addOutput(wallet.getAddress(), 10000); + + const prev = Script.fromPubkeyhash(key.getHash()); + const prevHash = random.randomBytes(32); + + tx.addCoin(dummyInput(addr, prevHash)); + tx.setLocktime(200); + + chain.tip.height = 200; + + const sig = tx.signature(0, prev, 70000, key.privateKey, ALL); + tx.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); - let err; - try { await mempool.addTX(tx.toTX()); - } catch (e) { - err = e; - } + chain.tip.height = 0; + }); - assert(err); - assert(!mempool.hasReject(tx.hash())); + it('should handle invalid locktime', async () => { + const key = KeyRing.generate(); + const addr = key.getAddress(); - cachedTX = tx; - }); + const tx = new MTX(); + tx.addOutput(wallet.getAddress(), 50000); + tx.addOutput(wallet.getAddress(), 10000); - it('should clear reject cache', async () => { - const tx = new MTX(); - tx.addOutpoint(new Outpoint()); - tx.addOutput(wallet.getAddress(), 50000); + const prev = Script.fromPubkeyhash(key.getHash()); + const prevHash = random.randomBytes(32); - assert(!mempool.hasReject(cachedTX.hash())); + tx.addCoin(dummyInput(addr, prevHash)); + tx.setLocktime(200); + chain.tip.height = 200 - 1; - await mempool.addBlock({ height: 1 }, [tx.toTX()], new CoinView()); + const sig = tx.signature(0, prev, 70000, key.privateKey, ALL); + tx.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); - assert(!mempool.hasReject(cachedTX.hash())); - }); + let err; + try { + await mempool.addTX(tx.toTX()); + } catch (e) { + err = e; + } - it('should remove tx after being included in block', async () => { - const key = KeyRing.generate(); - const addr = key.getAddress(); + assert(err); - const t1 = new MTX(); - { - t1.addOutput(wallet.getAddress(), 50000); - t1.addOutput(wallet.getAddress(), 10000); + chain.tip.height = 0; + }); - const script = Script.fromPubkeyhash(key.getHash()); + it('should not cache a malleated wtx with mutated sig', async () => { + const key = KeyRing.generate(); + const addr = key.getAddress(); - t1.addCoin(dummyInput(addr, ONE_HASH)); + const tx = new MTX(); + tx.addOutput(wallet.getAddress(), 50000); + tx.addOutput(wallet.getAddress(), 10000); - const sig = t1.signature(0, script, 70000, key.privateKey, ALL); - t1.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); - await mempool.addTX(t1.toTX()); - } + const prevHash = random.randomBytes(32); + + tx.addCoin(dummyInput(addr, prevHash)); + + const prevs = Script.fromPubkeyhash(key.getKeyHash()); + + const sig = tx.signature(0, prevs, 70000, key.privateKey, ALL); + sig[sig.length - 1] = 0; + + tx.inputs[0].witness = new Witness([sig, key.publicKey]); + + let err; + try { + await mempool.addTX(tx.toTX()); + } catch (e) { + err = e; + } + + assert(err); + assert(!mempool.hasReject(tx.hash())); + }); - const t2 = new MTX(); - { + it('should not cache non-malleated tx without sig', async () => { const key = KeyRing.generate(); const addr = key.getAddress(); - t2.addOutput(wallet.getAddress(), 50000); - t2.addOutput(wallet.getAddress(), 10000); + const tx = new MTX(); + tx.addOutput(wallet.getAddress(), 50000); + tx.addOutput(wallet.getAddress(), 10000); - const script = Script.fromPubkeyhash(key.getHash()); + const prevHash = random.randomBytes(32); - t2.addCoin(dummyInput(addr, ONE_HASH)); + tx.addCoin(dummyInput(addr, prevHash)); - const sig = t2.signature(0, script, 70000, key.privateKey, ALL); - t2.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); - await mempool.addTX(t2.toTX()); - } + let err; + try { + await mempool.addTX(tx.toTX()); + } catch (e) { + err = e; + } - const [block, view] = await dummyBlock([t1], true); + assert(err); + assert(!mempool.hasReject(tx.hash())); - { - const entry = await mempool.getEntry(t1.hash()); - assert.equal(entry.txid(), t1.txid()); - } + cachedTX = tx; + }); - await mempool.addBlock(block, block.txs, view); + it('should clear reject cache', async () => { + const tx = new MTX(); + tx.addOutpoint(new Outpoint()); + tx.addOutput(wallet.getAddress(), 50000); - { - const entry = await mempool.getEntry(t1.hash()); - assert.equal(entry, undefined); - } + assert(!mempool.hasReject(cachedTX.hash())); - { - const tx = t2.toTX(); - const entry = await mempool.getEntry(tx.hash()); - assert.equal(entry.txid(), tx.txid()); - } - }); + await mempool.addBlock({ height: 1 }, [tx.toTX()], new CoinView()); - it('should reject absurd fee', async () => { - const wallet = new MemWallet({ network }); - const addr = wallet.getAddress(); - const funds = 10000e6; + assert(!mempool.hasReject(cachedTX.hash())); + }); - const mtx = new MTX(); - mtx.addCoin( - dummyInput( - addr, - random.randomBytes(32), - funds - ) - ); - mtx.addOutput(wallet.getAddress(), 0); // temp - wallet.sign(mtx); - - const vsize = mtx.getVirtualSize(); - const minFee = (vsize / 1000) * network.minRelay; - const absurdFee = minFee * policy.ABSURD_FEE_FACTOR; - - // Revise with exactly absurd fee - mtx.outputs[0].value = funds - absurdFee - 1; - mtx.inputs[0].witness.items.length = 0; - wallet.sign(mtx); - const tx1 = mtx.toTX(); - - await assert.rejects( - mempool.addTX(tx1), - {message: /absurdly-high-fee/} - ); - - // Revise again with just under absurd fee - mtx.outputs[0].value = funds - absurdFee; - mtx.inputs[0].witness.items.length = 0; - wallet.sign(mtx); - const tx2 = mtx.toTX(); - - await mempool.addTX(tx2); - }); + it('should remove tx after being included in block', async () => { + const key = KeyRing.generate(); + const addr = key.getAddress(); - it('should reject too-low fee', async () => { - const wallet = new MemWallet({ network }); - const addr = wallet.getAddress(); - const funds = 10000e6; + const t1 = new MTX(); + { + t1.addOutput(wallet.getAddress(), 50000); + t1.addOutput(wallet.getAddress(), 10000); - const mtx = new MTX(); - mtx.addCoin( - dummyInput( - addr, - random.randomBytes(32), - funds - ) - ); - mtx.addOutput(wallet.getAddress(), 0); // temp - wallet.sign(mtx); - - const vsize = mtx.getVirtualSize(); - const minFee = (vsize / 1000) * network.minRelay; - - // Revise with just under minFee - mtx.outputs[0].value = funds - minFee + 1; - mtx.inputs[0].witness.items.length = 0; - wallet.sign(mtx); - const tx1 = mtx.toTX(); - - await assert.rejects( - mempool.addTX(tx1), - {message: /insufficient priority/} - ); - - // Revise again with exactly minFee - mtx.outputs[0].value = funds - minFee; - mtx.inputs[0].witness.items.length = 0; - wallet.sign(mtx); - const tx2 = mtx.toTX(); - - await mempool.addTX(tx2); - }); + const script = Script.fromPubkeyhash(key.getHash()); + + t1.addCoin(dummyInput(addr, ONE_HASH)); + + const sig = t1.signature(0, script, 70000, key.privateKey, ALL); + t1.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); + await mempool.addTX(t1.toTX()); + } + + const t2 = new MTX(); + { + const key = KeyRing.generate(); + const addr = key.getAddress(); + + t2.addOutput(wallet.getAddress(), 50000); + t2.addOutput(wallet.getAddress(), 10000); + + const script = Script.fromPubkeyhash(key.getHash()); + + t2.addCoin(dummyInput(addr, ONE_HASH)); + + const sig = t2.signature(0, script, 70000, key.privateKey, ALL); + t2.inputs[0].witness = Witness.fromItems([sig, key.publicKey]); + await mempool.addTX(t2.toTX()); + } + + const [block, view] = await getMockBlock(chain, [t1], true); + + { + const entry = await mempool.getEntry(t1.hash()); + assert.equal(entry.txid(), t1.txid()); + } + + await mempool.addBlock(block, block.txs, view); + + { + const entry = await mempool.getEntry(t1.hash()); + assert.equal(entry, undefined); + } + + { + const tx = t2.toTX(); + const entry = await mempool.getEntry(tx.hash()); + assert.equal(entry.txid(), tx.txid()); + } + }); + + it('should reject absurd fee', async () => { + const wallet = new MemWallet({ network }); + const addr = wallet.getAddress(); + const funds = 10000e6; + + const mtx = new MTX(); + mtx.addCoin( + dummyInput( + addr, + random.randomBytes(32), + funds + ) + ); + mtx.addOutput(wallet.getAddress(), 0); // temp + wallet.sign(mtx); + + const vsize = mtx.getVirtualSize(); + const minFee = (vsize / 1000) * network.minRelay; + const absurdFee = minFee * policy.ABSURD_FEE_FACTOR; + + // Revise with exactly absurd fee + mtx.outputs[0].value = funds - absurdFee - 1; + mtx.inputs[0].witness.items.length = 0; + wallet.sign(mtx); + const tx1 = mtx.toTX(); + + await assert.rejects( + mempool.addTX(tx1), + {message: /absurdly-high-fee/} + ); + + // Revise again with just under absurd fee + mtx.outputs[0].value = funds - absurdFee; + mtx.inputs[0].witness.items.length = 0; + wallet.sign(mtx); + const tx2 = mtx.toTX(); + + await mempool.addTX(tx2); + }); + + it('should reject too-low fee', async () => { + const wallet = new MemWallet({ network }); + const addr = wallet.getAddress(); + const funds = 10000e6; + + const mtx = new MTX(); + mtx.addCoin( + dummyInput( + addr, + random.randomBytes(32), + funds + ) + ); + mtx.addOutput(wallet.getAddress(), 0); // temp + wallet.sign(mtx); + + const vsize = mtx.getVirtualSize(); + const minFee = (vsize / 1000) * network.minRelay; + + // Revise with just under minFee + mtx.outputs[0].value = funds - minFee + 1; + mtx.inputs[0].witness.items.length = 0; + wallet.sign(mtx); + const tx1 = mtx.toTX(); + + await assert.rejects( + mempool.addTX(tx1), + {message: /insufficient priority/} + ); + + // Revise again with exactly minFee + mtx.outputs[0].value = funds - minFee; + mtx.inputs[0].witness.items.length = 0; + wallet.sign(mtx); + const tx2 = mtx.toTX(); - it('should destroy mempool', async () => { - await mempool.close(); - await chain.close(); - await blocks.close(); - await workers.close(); + await mempool.addTX(tx2); + }); }); describe('Mempool disconnect and reorg handling', function () { @@ -583,7 +600,6 @@ describe('Mempool', function() { const COINBASE_MATURITY = mempool.network.coinbaseMaturity; const TREE_INTERVAL = mempool.network.names.treeInterval; - mempool.network.names.auctionStart = 0; before(async () => { await mempool.open(); @@ -615,43 +631,9 @@ describe('Mempool', function() { chaincoins.getNameStatus = async (nameHash) => { assert(Buffer.isBuffer(nameHash)); const height = chain.height + 1; - const state = await chain.getNextState(); - const hardened = state.hasHardening(); - return chain.db.getNameStatus(nameHash, height, hardened); + return chain.db.getNameStatus(nameHash, height); }; - async function getMockBlock(chain, txs = [], cb = true) { - if (cb) { - const raddr = KeyRing.generate().getAddress(); - const mtx = new MTX(); - mtx.addInput(new Input()); - mtx.addOutput(raddr, 0); - mtx.locktime = chain.height + 1; - - txs = [mtx.toTX(), ...txs]; - } - - const view = new CoinView(); - for (const tx of txs) { - view.addTX(tx, -1); - } - - const time = chain.tip.time + 1; - - const block = new Block(); - block.txs = txs; - block.prevBlock = chain.tip.hash; - block.time = time; - block.bits = await chain.getTarget(block.time, chain.tip); - - // Ensure mockblocks are unique (required for reorg testing) - block.merkleRoot = block.createMerkleRoot(); - block.witnessRoot = block.createWitnessRoot(); - block.treeRoot = chain.db.treeRoot(); - - return [block, view]; - } - it('should create coins in chain', async () => { const mtx = new MTX(); mtx.locktime = chain.height + 1; @@ -991,10 +973,7 @@ describe('Mempool', function() { const name = rules.grindName(10, 0, mempool.network); const rawName = Buffer.from(name, 'ascii'); const nameHash = rules.hashName(rawName); - open.outputs[0].covenant.type = types.OPEN; - open.outputs[0].covenant.pushHash(nameHash); - open.outputs[0].covenant.pushU32(0); - open.outputs[0].covenant.push(rawName); + open.outputs[0].covenant.setOpen(nameHash, rawName); chaincoins.sign(open); open = open.toTX(); @@ -1024,11 +1003,12 @@ describe('Mempool', function() { bid.addCoin(bidCoin); bid.addOutput(addr, 70000); - bid.outputs[0].covenant.type = types.BID; - bid.outputs[0].covenant.pushHash(nameHash); - bid.outputs[0].covenant.pushU32(ns.height); - bid.outputs[0].covenant.push(rawName); - bid.outputs[0].covenant.pushHash(Buffer.alloc(32, 0x01)); + bid.outputs[0].covenant.setBid( + nameHash, + ns.height, + rawName, + Buffer.alloc(32, 0x01) + ); chaincoins.sign(bid); bid = bid.toTX(); @@ -1119,6 +1099,8 @@ describe('Mempool', function() { }); it('should handle reorg: name claim - DNSSEC timestamp', async () => { + this.timeout(10000); + // Mempool is empty await mempool.reset(); assert.strictEqual(mempool.map.size, 0); @@ -1205,6 +1187,8 @@ describe('Mempool', function() { }); it('should handle reorg: name claim - block commitment', async () => { + this.timeout(10000); + // Mempool is empty await mempool.reset(); assert.strictEqual(mempool.map.size, 0); @@ -1332,6 +1316,11 @@ describe('Mempool', function() { size: 2 }); + const blocks = new BlockStore({ + memory: true, + network + }); + const chain = new Chain({ memory: true, blocks, @@ -1367,39 +1356,6 @@ describe('Mempool', function() { const chaincoins = new MemWallet({ network }); const wallet = new MemWallet({ network }); - async function getMockBlock(chain, txs = [], cb = true) { - if (cb) { - const raddr = KeyRing.generate().getAddress(); - const mtx = new MTX(); - mtx.addInput(new Input()); - mtx.addOutput(raddr, 0); - mtx.locktime = chain.height + 1; - - txs = [mtx.toTX(), ...txs]; - } - - const view = new CoinView(); - for (const tx of txs) { - view.addTX(tx, -1); - } - - const now = Math.floor(Date.now() / 1000); - const time = chain.tip.time <= now ? chain.tip.time + 1 : now; - - const block = new Block(); - block.txs = txs; - block.prevBlock = chain.tip.hash; - block.time = time; - block.bits = await chain.getTarget(block.time, chain.tip); - - // Ensure mockblocks are unique (required for reorg testing) - block.merkleRoot = block.createMerkleRoot(); - block.witnessRoot = block.createWitnessRoot(); - block.treeRoot = chain.db.treeRoot(); - - return [block, view]; - } - it('should create coins in chain', async () => { const mtx = new MTX(); mtx.locktime = chain.height + 1; @@ -1529,6 +1485,11 @@ describe('Mempool', function() { size: 2 }); + const blocks = new BlockStore({ + memory: true, + network + }); + const chain = new Chain({ memory: true, blocks, diff --git a/test/mtx-test.js b/test/mtx-test.js index a1a9c9857..70dea97bd 100644 --- a/test/mtx-test.js +++ b/test/mtx-test.js @@ -1,10 +1,13 @@ 'use strict'; const assert = require('bsert'); +const random = require('bcrypto/lib/random'); const CoinView = require('../lib/coins/coinview'); const WalletCoinView = require('../lib/wallet/walletcoinview'); +const Coin = require('../lib/primitives/coin'); const MTX = require('../lib/primitives/mtx'); const Path = require('../lib/wallet/path'); +const MemWallet = require('./util/memwallet'); const mtx1json = require('./data/mtx1.json'); const mtx2json = require('./data/mtx2.json'); @@ -134,4 +137,631 @@ describe('MTX', function() { assert.strictEqual(estimate, actual); }); }); + + describe('Fund', function() { + const wallet1 = new MemWallet(); + const wallet2 = new MemWallet(); + + const coins1 = [ + dummyCoin(wallet1.getAddress(), 1000000), + dummyCoin(wallet1.getAddress(), 1000000), + dummyCoin(wallet1.getAddress(), 1000000), + dummyCoin(wallet1.getAddress(), 1000000), + dummyCoin(wallet1.getAddress(), 1000000) + ]; + + const last1 = coins1[coins1.length - 1]; + const last2 = coins1[coins1.length - 2]; + + /** + * Test matrix + * fund w/o inputs, just coins + * fund with preferred inputs - no view && coins + * fund with preferred inputs - view && no coins + * fund with preferred inputs - view && coins + * fund with preferred inputs - no view && coins - error + * + * fund with existing inputs - no view && coins + * fund with existing inputs - view && no coins + * fund with existing inputs - view && coins + * fund with existing inputs - no view && no coins - error + * + * fund with both inputs - no view && coins(1e, 1p) + * fund with both inputs - view(1e, 1p) && no coins + * fund with both inputs - view(1e, 1p) && coins(1e, 1p) + * fund with both inputs (1e, 1p) - no view(1e) && no coins(1e) - error. + * fund with both inputs (1e, 1p) - no view(1p) && no coins(1p) - error. + * fund with both inputs (1e, 1p) - no view && no coins - error. + */ + + it('should fund mtx', async () => { + const mtx = new MTX(); + + mtx.addOutput(wallet2.getAddress(), 1500000); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange() + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + }); + + it('should add all preferred coins regardless of value', async () => { + const mtx = new MTX(); + + // 1 preferred is enough. + mtx.addOutput(wallet2.getAddress(), 1000000); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + hardFee: 0, + + // Use all coins as preferred, but one. + inputs: coins1.slice(0, -1).map(coin => ({ + hash: coin.hash, + index: coin.index + })) + }); + + // all of them got used. + assert.strictEqual(mtx.inputs.length, coins1.length - 1); + assert.strictEqual(mtx.outputs.length, 2); + assert.strictEqual(mtx.outputs[0].value, 1000000); + assert.strictEqual(mtx.outputs[1].value, 3000000); + }); + + it('should fund with preferred inputs - coins', async () => { + const mtx = new MTX(); + const coin = last1; + + mtx.addOutput(wallet2.getAddress(), 1500000); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin.hash, + index: coin.index + }] + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, coin.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, coin.index); + + assert(mtx.view.hasEntry({ + hash: coin.hash, + index: coin.index + })); + }); + + it('should fund with preferred inputs - view', async () => { + const mtx = new MTX(); + const coin = dummyCoin(wallet1.getAddress(), 1000000); + + mtx.addOutput(wallet2.getAddress(), 1500000); + mtx.view.addCoin(coin); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin.hash, + index: coin.index + }] + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, coin.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, coin.index); + + assert(mtx.view.hasEntry({ + hash: coin.hash, + index: coin.index + })); + }); + + it('should fund with preferred inputs - coins && view', async () => { + const mtx = new MTX(); + const viewCoin = dummyCoin(wallet1.getAddress(), 1000000); + const lastCoin = last1; + + mtx.addOutput(wallet2.getAddress(), 1500000); + mtx.view.addCoin(viewCoin); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: viewCoin.hash, + index: viewCoin.index + }, { + hash: last1.hash, + index: last1.index + }] + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, viewCoin.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, viewCoin.index); + assert.bufferEqual(mtx.inputs[1].prevout.hash, lastCoin.hash); + assert.strictEqual(mtx.inputs[1].prevout.index, lastCoin.index); + + assert(mtx.view.hasEntry({ + hash: viewCoin.hash, + index: viewCoin.index + })); + + assert(mtx.view.hasEntry({ + hash: lastCoin.hash, + index: lastCoin.index + })); + }); + + it('should not fund with preferred inputs and no coin info', async () => { + const mtx = new MTX(); + const coin = dummyCoin(wallet1.getAddress(), 1000000); + + mtx.addOutput(wallet2.getAddress(), 1500000); + + let err; + + try { + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin.hash, + index: coin.index + }] + }); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'Could not resolve preferred inputs.'); + }); + + it('should fund with existing inputs view - coins', async () => { + const mtx = new MTX(); + const coin = last1; + + mtx.addInput({ + prevout: { + hash: coin.hash, + index: coin.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 1500000); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange() + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, coin.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, coin.index); + + assert(mtx.view.hasEntry({ + hash: coin.hash, + index: coin.index + })); + }); + + it('should fund with existing inputs view - view', async () => { + const mtx = new MTX(); + const coin = dummyCoin(wallet1.getAddress(), 1000000); + + mtx.addInput({ + prevout: { + hash: coin.hash, + index: coin.index + } + }); + + mtx.view.addCoin(coin); + + mtx.addOutput(wallet2.getAddress(), 1500000); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange() + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, coin.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, coin.index); + + assert(mtx.view.hasEntry({ + hash: coin.hash, + index: coin.index + })); + }); + + it('should fund with existing inputs view - coins && view', async () => { + const mtx = new MTX(); + const viewCoin = dummyCoin(wallet1.getAddress(), 1000000); + const lastCoin = last1; + + mtx.addInput({ + prevout: { + hash: viewCoin.hash, + index: viewCoin.index + } + }); + + mtx.addInput({ + prevout: { + hash: last1.hash, + index: last1.index + } + }); + + mtx.view.addCoin(viewCoin); + + mtx.addOutput(wallet2.getAddress(), 1500000); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange() + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, viewCoin.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, viewCoin.index); + assert.bufferEqual(mtx.inputs[1].prevout.hash, lastCoin.hash); + assert.strictEqual(mtx.inputs[1].prevout.index, lastCoin.index); + + assert(mtx.view.hasEntry({ + hash: viewCoin.hash, + index: viewCoin.index + })); + + assert(mtx.view.hasEntry({ + hash: lastCoin.hash, + index: lastCoin.index + })); + }); + + it('should not fund with existing inputs and no coin info', async () => { + const mtx = new MTX(); + const coin = dummyCoin(wallet1.getAddress(), 1000000); + + mtx.addInput({ + prevout: { + hash: coin.hash, + index: coin.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 1500000); + + let err; + + try { + await mtx.fund(coins1, { + changeAddress: wallet1.getChange() + }); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'Could not resolve preferred inputs.'); + }); + + it('should fund with preferred & existing inputs - coins', async () => { + const mtx = new MTX(); + const coin1 = last1; + const coin2 = last2; + + mtx.addInput({ + prevout: { + hash: coin1.hash, + index: coin1.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 1500000); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin2.hash, + index: coin2.index + }] + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, coin1.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, coin1.index); + assert.bufferEqual(mtx.inputs[1].prevout.hash, coin2.hash); + assert.strictEqual(mtx.inputs[1].prevout.index, coin2.index); + + assert(mtx.view.hasEntry({ + hash: coin1.hash, + index: coin1.index + })); + + assert(mtx.view.hasEntry({ + hash: coin2.hash, + index: coin2.index + })); + }); + + it('should fund with preferred & existing inputs - view', async () => { + const mtx = new MTX(); + const coin1 = dummyCoin(wallet1.getAddress(), 1000000); + const coin2 = dummyCoin(wallet1.getAddress(), 1000000); + + mtx.addInput({ + prevout: { + hash: coin1.hash, + index: coin1.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 1500000); + mtx.view.addCoin(coin1); + mtx.view.addCoin(coin2); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin2.hash, + index: coin2.index + }] + }); + + assert.strictEqual(mtx.inputs.length, 2); + assert.strictEqual(mtx.outputs.length, 2); + + assert.bufferEqual(mtx.inputs[0].prevout.hash, coin1.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, coin1.index); + assert.bufferEqual(mtx.inputs[1].prevout.hash, coin2.hash); + assert.strictEqual(mtx.inputs[1].prevout.index, coin2.index); + + assert(mtx.view.hasEntry({ + hash: coin1.hash, + index: coin1.index + })); + + assert(mtx.view.hasEntry({ + hash: coin2.hash, + index: coin2.index + })); + }); + + it('should fund with preferred & existing inputs', async () => { + const mtx = new MTX(); + // existing + const coin1 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast1 = last1; + + // preferred + const coin2 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast2 = last2; + + mtx.addInput({ + prevout: { + hash: coin1.hash, + index: coin1.index + } + }); + mtx.addInput({ + prevout: { + hash: coinLast1.hash, + index: coinLast1.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 5000000); + mtx.view.addCoin(coin1); + mtx.view.addCoin(coin2); + + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin2.hash, + index: coin2.index + }, { + hash: coinLast2.hash, + index: coinLast2.index + }] + }); + + assert.strictEqual(mtx.inputs.length, 6); + assert.strictEqual(mtx.outputs.length, 2); + + // first comes existing + assert.bufferEqual(mtx.inputs[0].prevout.hash, coin1.hash); + assert.strictEqual(mtx.inputs[0].prevout.index, coin1.index); + assert.bufferEqual(mtx.inputs[1].prevout.hash, coinLast1.hash); + assert.strictEqual(mtx.inputs[1].prevout.index, coinLast1.index); + + // then comes preferred + assert.bufferEqual(mtx.inputs[2].prevout.hash, coin2.hash); + assert.strictEqual(mtx.inputs[2].prevout.index, coin2.index); + assert.bufferEqual(mtx.inputs[3].prevout.hash, coinLast2.hash); + assert.strictEqual(mtx.inputs[3].prevout.index, coinLast2.index); + + assert(mtx.view.hasEntry({ + hash: coin1.hash, + index: coin1.index + })); + + assert(mtx.view.hasEntry({ + hash: coin2.hash, + index: coin2.index + })); + + assert(mtx.view.hasEntry({ + hash: coinLast1.hash, + index: coinLast1.index + })); + + assert(mtx.view.hasEntry({ + hash: coinLast2.hash, + index: coinLast2.index + })); + }); + + it('should not fund with missing coin info (both)', async () => { + const mtx = new MTX(); + // existing + const coin1 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast1 = last1; + + // preferred + const coin2 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast2 = last2; + + mtx.addInput({ + prevout: { + hash: coin1.hash, + index: coin1.index + } + }); + mtx.addInput({ + prevout: { + hash: coinLast1.hash, + index: coinLast1.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 5000000); + + let err; + try { + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin2.hash, + index: coin2.index + }, { + hash: coinLast2.hash, + index: coinLast2.index + }] + }); + } catch (e) { + err = e; + } + + assert(err); + // inputs are resolved first, so it should throw there. + assert.strictEqual(err.message, 'Could not resolve preferred inputs.'); + }); + + it('should not fund with missing coin info(only existing)', async () => { + const mtx = new MTX(); + // existing + const coin1 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast1 = last1; + + // preferred + const coin2 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast2 = last2; + + mtx.addInput({ + prevout: { + hash: coin1.hash, + index: coin1.index + } + }); + mtx.addInput({ + prevout: { + hash: coinLast1.hash, + index: coinLast1.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 5000000); + mtx.view.addCoin(coin1); + + let err; + try { + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin2.hash, + index: coin2.index + }, { + hash: coinLast2.hash, + index: coinLast2.index + }] + }); + } catch (e) { + err = e; + } + + assert(err); + // preferred is missing. + assert.strictEqual(err.message, 'Could not resolve preferred inputs.'); + }); + + it('should not fund with missing coin info(only preferred)', async () => { + const mtx = new MTX(); + // existing + const coin1 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast1 = last1; + + // preferred + const coin2 = dummyCoin(wallet1.getAddress(), 1000000); + const coinLast2 = last2; + + mtx.addInput({ + prevout: { + hash: coin1.hash, + index: coin1.index + } + }); + mtx.addInput({ + prevout: { + hash: coinLast1.hash, + index: coinLast1.index + } + }); + + mtx.addOutput(wallet2.getAddress(), 5000000); + mtx.view.addCoin(coin2); + + let err; + try { + await mtx.fund(coins1, { + changeAddress: wallet1.getChange(), + inputs: [{ + hash: coin2.hash, + index: coin2.index + }, { + hash: coinLast2.hash, + index: coinLast2.index + }] + }); + } catch (e) { + err = e; + } + + assert(err); + // preferred is missing. + assert.strictEqual(err.message, 'Could not resolve preferred inputs.'); + }); + }); }); + +function dummyCoin(address, value) { + const hash = random.randomBytes(32); + const index = 0; + + return new Coin({address, value, hash, index}); +} diff --git a/test/namestate-test.js b/test/namestate-test.js new file mode 100644 index 000000000..e4b227a02 --- /dev/null +++ b/test/namestate-test.js @@ -0,0 +1,330 @@ +/* eslint-env mocha */ +/* eslint prefer-arrow-callback: "off" */ + +'use strict'; + +const assert = require('bsert'); +const NameState = require('../lib/covenants/namestate'); +const rules = require('../lib/covenants/rules'); +const Network = require('../lib/protocol/network'); + +const network = Network.get('regtest'); + +const { + treeInterval, + biddingPeriod, + revealPeriod, + renewalWindow, + claimPeriod, + lockupPeriod +} = network.names; + +describe('Namestate', function() { + describe('open auction name', function() { + const name = 'handshake'; + const nameHash = rules.hashName(name); + let height = 0; + + const ns = new NameState(); + ns.nameHash = nameHash; + ns.set(Buffer.from(name, 'ascii'), height); + + // After this height transfers and expirations return different stats + const auctionLifespan = treeInterval + 1 + biddingPeriod + revealPeriod; + + describe('single auction flow', function() { + it('should be OPENING', () => { + while (height < treeInterval + 1) { + const json = ns.getJSON(height, network); + + assert.strictEqual(json.state, 'OPENING'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'openPeriodStart', + 'openPeriodEnd', + 'blocksUntilBidding', + 'hoursUntilBidding' + ] + ); + height++; + } + }); + + it('should be BIDDING', () => { + while (height < treeInterval + 1 + biddingPeriod) { + const json = ns.getJSON(height, network); + + assert.strictEqual(json.state, 'BIDDING'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'bidPeriodStart', + 'bidPeriodEnd', + 'blocksUntilReveal', + 'hoursUntilReveal' + ] + ); + height++; + } + }); + + it('should be REVEALING', () => { + while (height < treeInterval + 1 + biddingPeriod + revealPeriod) { + const json = ns.getJSON(height, network); + + assert.strictEqual(json.state, 'REVEAL'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'revealPeriodStart', + 'revealPeriodEnd', + 'blocksUntilClose', + 'hoursUntilClose' + ] + ); + height++; + } + }); + + it('should be CLOSED without owner', () => { + const json = ns.getJSON(height, network); + + assert.strictEqual(json.state, 'CLOSED'); + assert(json.stats === null); + }); + }); + + describe('post-auction states', function() { + it('should be CLOSED until expiration with owner', () => { + // Start right after auction is over + let heightWithOwner = auctionLifespan; + + // Someone won the name + ns.owner.hash = Buffer.alloc(32, 0x01); + ns.owner.index = 0; + + while (heightWithOwner < renewalWindow) { + const json = ns.getJSON(heightWithOwner, network); + + assert.strictEqual(json.state, 'CLOSED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'renewalPeriodStart', + 'renewalPeriodEnd', + 'blocksUntilExpire', + 'daysUntilExpire' + ] + ); + heightWithOwner++; + } + + // Expired without renewal + while (heightWithOwner < renewalWindow + 10) { + const json = ns.getJSON(heightWithOwner, network); + + assert.strictEqual(json.state, 'CLOSED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'blocksSinceExpired' + ] + ); + heightWithOwner++; + } + }); + + it('should be CLOSED with transfer statistics', () => { + // Start right after auction is over + let heightWithTransfer = auctionLifespan; + + // Someone won the name + ns.owner.hash = Buffer.alloc(32, 0x01); + ns.owner.index = 0; + + // Winner confirmed a TRANSFER + ns.transfer = heightWithTransfer; + + while (heightWithTransfer < renewalWindow) { + const json = ns.getJSON(heightWithTransfer, network); + + assert.strictEqual(json.state, 'CLOSED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'renewalPeriodStart', + 'renewalPeriodEnd', + 'blocksUntilExpire', + 'daysUntilExpire', + 'transferLockupStart', + 'transferLockupEnd', + 'blocksUntilValidFinalize', + 'hoursUntilValidFinalize' + ] + ); + + heightWithTransfer++; + } + + // Expired before FINALIZE (which resets everything) + while (heightWithTransfer < renewalWindow + 10) { + const json = ns.getJSON(heightWithTransfer, network); + + assert.strictEqual(json.state, 'CLOSED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'blocksSinceExpired' + ] + ); + heightWithTransfer++; + } + }); + + it('should be REVOKED', () => { + // Start right after auction is over + let heightWithRevoke = auctionLifespan; + + // Someone won the name + ns.owner.hash = Buffer.alloc(32, 0x01); + ns.owner.index = 0; + + // Winner confirmed a TRANSFER + ns.transfer = heightWithRevoke; + + while (heightWithRevoke < height + 10) { + const json = ns.getJSON(heightWithRevoke, network); + + assert.strictEqual(json.state, 'CLOSED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'renewalPeriodStart', + 'renewalPeriodEnd', + 'blocksUntilExpire', + 'daysUntilExpire', + 'transferLockupStart', + 'transferLockupEnd', + 'blocksUntilValidFinalize', + 'hoursUntilValidFinalize' + ] + ); + heightWithRevoke++; + } + + // Winner REVOKEd before FINALIZE + ns.transfer = 0; + ns.revoked = heightWithRevoke; + const revokedHeight = heightWithRevoke; + + // Revoked stats remain until re-opened + while (heightWithRevoke < revokedHeight + renewalWindow) { + const json = ns.getJSON(heightWithRevoke, network); + + assert.strictEqual(json.state, 'REVOKED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'revokePeriodStart', + 'revokePeriodEnd', + 'blocksUntilReopen', + 'hoursUntilReopen' + ] + ); + heightWithRevoke++; + } + }); + }); + }); + + describe('reserved name', function() { + const name = 'handshake'; + const nameHash = rules.hashName(name); + let height = 1; // ns.claimed can not be 0 + + const ns = new NameState(); + ns.nameHash = nameHash; + ns.set(Buffer.from(name, 'ascii'), height); + // Someone claimed the name + ns.owner.hash = Buffer.alloc(32, 0x01); + ns.owner.index = 0; + ns.claimed = height; + + it('should be LOCKED', () => { + while (height - 1 < lockupPeriod) { + const json = ns.getJSON(height, network); + + assert.strictEqual(json.state, 'LOCKED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'lockupPeriodStart', + 'lockupPeriodEnd', + 'blocksUntilClosed', + 'hoursUntilClosed' + ] + ); + height++; + } + }); + + it('should be CLOSED', () => { + while (height < claimPeriod) { + const json = ns.getJSON(height, network); + + assert.strictEqual(json.state, 'CLOSED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'renewalPeriodStart', + 'renewalPeriodEnd', + 'blocksUntilExpire', + 'daysUntilExpire' + ] + ); + height++; + } + }); + + it('should be CLOSED and expired', () => { + // Expired without renewal + while (height < claimPeriod + 10) { + const json = ns.getJSON(height, network); + + assert.strictEqual(json.state, 'CLOSED'); + + const stats = Object.keys(json.stats); + assert.deepStrictEqual( + stats, + [ + 'blocksSinceExpired' + ] + ); + height++; + } + }); + }); +}); diff --git a/test/net-spv-test.js b/test/net-spv-test.js index 2bc36d4af..4a0d6cd84 100644 --- a/test/net-spv-test.js +++ b/test/net-spv-test.js @@ -116,7 +116,7 @@ describe('SPV', function() { }); it('should run auction and register name', async () => { - await wallet.sendOpen(name, false); + await wallet.sendOpen(name); await mineBlocks(treeInterval + 1); await wallet.sendBid(name, 10000, 10000); await mineBlocks(biddingPeriod); diff --git a/test/net-test.js b/test/net-test.js index af74a9104..e3c8a60fd 100644 --- a/test/net-test.js +++ b/test/net-test.js @@ -3,7 +3,7 @@ const assert = require('bsert'); const {resolve} = require('path'); const fs = require('fs'); -const {BloomFilter} = require('bfilter'); +const {BloomFilter} = require('@handshake-org/bfilter'); const {nonce} = require('../lib/net/common'); const consensus = require('../lib/protocol/consensus'); const Parser = require('../lib/net/parser'); diff --git a/test/node-http-test.js b/test/node-http-test.js index b7335a880..1fbcca6e8 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -2,9 +2,6 @@ const assert = require('bsert'); const bio = require('bufio'); -const {NodeClient} = require('hs-client'); -const Network = require('../lib/protocol/network'); -const FullNode = require('../lib/node/fullnode'); const Address = require('../lib/primitives/address'); const Mnemonic = require('../lib/hd/mnemonic'); const Witness = require('../lib/script/witness'); @@ -15,27 +12,311 @@ const Coin = require('../lib/primitives/coin'); const MTX = require('../lib/primitives/mtx'); const rules = require('../lib/covenants/rules'); const common = require('./util/common'); +const NodeContext = require('./util/node-context'); +const pkg = require('../lib/pkg'); const mnemonics = require('./data/mnemonic-english.json'); +const consensus = require('../lib/protocol/consensus'); +const Outpoint = require('../lib/primitives/outpoint'); +const {ZERO_HASH} = consensus; + // Commonly used test mnemonic const phrase = mnemonics[0][1]; describe('Node HTTP', function() { - describe('Networking info', function() { - it('should not have public address: regtest', async () => { - const network = Network.get('regtest'); + describe('Mempool', function() { + let nodeCtx, nclient; + + beforeEach(async () => { + nodeCtx = new NodeContext(); + + await nodeCtx.open(); + nclient = nodeCtx.nclient; + }); + + afterEach(async () => { + await nodeCtx.close(); + nodeCtx = null; + }); + + it('should get mempool rejection filter', async () => { + const filterInfo = await nclient.getMempoolRejectionFilter({ + verbose: true + }); + + assert.ok('items' in filterInfo); + assert.ok('filter' in filterInfo); + assert.ok('size' in filterInfo); + assert.ok('entries' in filterInfo); + assert.ok('n' in filterInfo); + assert.ok('limit' in filterInfo); + assert.ok('tweak' in filterInfo); + + assert.equal(filterInfo.entries, 0); + }); + + it('should add an entry to the mempool rejection filter', async () => { + const mtx = new MTX(); + mtx.addOutpoint(new Outpoint(consensus.ZERO_HASH, 0)); + + const raw = mtx.toHex(); + const txid = await nclient.execute('sendrawtransaction', [raw]); + + const json = await nclient.checkMempoolRejectionFilter(txid); + assert.equal(json.invalid, true); + + const filterInfo = await nclient.getMempoolRejectionFilter(); + assert.equal(filterInfo.entries, 1); + }); + }); + + describe('Blockheader', function() { + let nodeCtx, nclient; + + beforeEach(async () => { + nodeCtx = new NodeContext(); + await nodeCtx.open(); + nclient = nodeCtx.nclient; + }); + + afterEach(async () => { + await nodeCtx.close(); + nodeCtx = null; + }); + + it('should fetch block header by height', async () => { + await nclient.execute( + 'generatetoaddress', + [8, 'rs1q7q3h4chglps004u3yn79z0cp9ed24rfrhvrxnx'] + ); + + // fetch corresponding header and block + const height = 7; + const header = await nclient.getBlockHeader(height); + assert.equal(header.height, height); + + const properties = [ + 'hash', 'version', 'prevBlock', + 'merkleRoot', 'time', 'bits', + 'nonce', 'height', 'chainwork' + ]; + + for (const property of properties) + assert(property in header); + + const block = await nclient.getBlock(height); + + assert.equal(block.hash, header.hash); + assert.equal(block.height, header.height); + assert.equal(block.version, header.version); + assert.equal(block.prevBlock, header.prevBlock); + assert.equal(block.merkleRoot, header.merkleRoot); + assert.equal(block.time, header.time); + assert.equal(block.bits, header.bits); + assert.equal(block.nonce, header.nonce); + }); + + it('should fetch null for block header that does not exist', async () => { + // many blocks in the future + const header = await nclient.getBlockHeader(40000); + assert.equal(header, null); + }); + + it('should have valid header chain', async () => { + await nclient.execute( + 'generatetoaddress', + [10, 'rs1q7q3h4chglps004u3yn79z0cp9ed24rfrhvrxnx'] + ); + + // starting at the genesis block + let prevBlock = '0000000000000000000000000000000000000000000000000000000000000000'; + + for (let i = 0; i < 10; i++) { + const header = await nclient.getBlockHeader(i); + + assert.equal(prevBlock, header.prevBlock); + prevBlock = header.hash; + } + }); + + it('should fetch block header by hash', async () => { + const info = await nclient.getInfo(); + + const headerByHash = await nclient.getBlockHeader(info.chain.tip); + const headerByHeight = await nclient.getBlockHeader(info.chain.height); + + assert.deepEqual(headerByHash, headerByHeight); + }); + }); + + describe('Chain info', function() { + let nodeCtx; + + afterEach(async () => { + await nodeCtx.close(); + nodeCtx = null; + }); + + it('should get info', async () => { + nodeCtx = new NodeContext(); + await nodeCtx.open(); + + const {node, nclient, network} = nodeCtx; + + const info = await nclient.getInfo(); + assert.strictEqual(info.network, network.type); + assert.strictEqual(info.version, pkg.version); + assert(info.pool); + assert.strictEqual(info.pool.agent, node.pool.options.agent); + assert(info.chain); + assert.strictEqual(info.chain.height, 0); + assert.strictEqual(info.chain.treeRoot, ZERO_HASH.toString('hex')); + // state comes from genesis block + assert.strictEqual(info.chain.state.tx, 1); + assert.strictEqual(info.chain.state.coin, 1); + assert.strictEqual(info.chain.state.burned, 0); + }); + + it('should get full node chain info', async () => { + nodeCtx = new NodeContext({ + network: 'regtest' + }); + + await nodeCtx.open(); + const {chain} = await nodeCtx.nclient.getInfo(); + assert.strictEqual(chain.height, 0); + assert.strictEqual(chain.tip, nodeCtx.network.genesis.hash.toString('hex')); + assert.strictEqual(chain.treeRoot, Buffer.alloc(32, 0).toString('hex')); + assert.strictEqual(chain.progress, 0); + assert.strictEqual(chain.indexers.indexTX, false); + assert.strictEqual(chain.indexers.indexAddress, false); + assert.strictEqual(chain.options.spv, false); + assert.strictEqual(chain.options.prune, false); + assert.strictEqual(chain.treeCompaction.compacted, false); + assert.strictEqual(chain.treeCompaction.compactOnInit, false); + assert.strictEqual(chain.treeCompaction.compactInterval, null); + assert.strictEqual(chain.treeCompaction.nextCompaction, null); + assert.strictEqual(chain.treeCompaction.lastCompaction, null); + }); + + it('should get fullnode chain info with indexers', async () => { + nodeCtx = new NodeContext({ + network: 'regtest', + indexAddress: true, + indexTX: true + }); + + await nodeCtx.open(); + const {chain} = await nodeCtx.nclient.getInfo(); + assert.strictEqual(chain.indexers.indexTX, true); + assert.strictEqual(chain.indexers.indexAddress, true); + }); + + it('should get fullnode chain info with pruning', async () => { + nodeCtx = new NodeContext({ + network: 'regtest', + prune: true + }); + + await nodeCtx.open(); + + const {chain} = await nodeCtx.nclient.getInfo(); + assert.strictEqual(chain.options.prune, true); + }); + + it('should get fullnode chain info with compact', async () => { + nodeCtx = new NodeContext({ + network: 'regtest', + compactTreeOnInit: true, + compactTreeInitInterval: 20000 + }); + + await nodeCtx.open(); - const node = new FullNode({ - network: network.type + const {chain} = await nodeCtx.nclient.getInfo(); + assert.strictEqual(chain.treeCompaction.compacted, false); + assert.strictEqual(chain.treeCompaction.compactOnInit, true); + assert.strictEqual(chain.treeCompaction.compactInterval, 20000); + assert.strictEqual(chain.treeCompaction.lastCompaction, null); + // last compaction height + keepBlocks + compaction interval + // regtest: 0 + 10000 + 20000 + assert.strictEqual(chain.treeCompaction.nextCompaction, 30000); + }); + + it('should get spv node chain info', async () => { + nodeCtx = new NodeContext({ + network: 'regtest', + spv: true }); - const nclient = new NodeClient({ - port: network.rpcPort + await nodeCtx.open(); + + const {chain} = await nodeCtx.nclient.getInfo(); + assert.strictEqual(chain.options.spv, true); + }); + + it('should get next tree update height', async () => { + const someAddr = 'rs1q7q3h4chglps004u3yn79z0cp9ed24rfrhvrxnx'; + nodeCtx = new NodeContext({ + network: 'regtest' }); - await node.open(); - await node.connect(); + await nodeCtx.open(); + const interval = nodeCtx.network.names.treeInterval; + + const nclient = nodeCtx.nclient; + const node = nodeCtx.node; + + { + // 0th block will be 0. + const {chain} = await nclient.getInfo(); + assert.strictEqual(chain.treeRootHeight, 0); + } + + // blocks from 1 - 4 will be 1. + // last block commits the tree root. + for (let i = 0; i < interval - 1; i++) { + await node.rpc.generateToAddress([1, someAddr]); + const {chain} = await nclient.getInfo(); + assert.strictEqual(chain.treeRootHeight, 1); + } + + { + // block 5 is also 1 and it commits the new root. + await node.rpc.generateToAddress([1, someAddr]); + const {chain} = await nclient.getInfo(); + assert.strictEqual(chain.treeRootHeight, 1); + } + + for (let i = 0; i < interval; i++) { + await node.rpc.generateToAddress([1, someAddr]); + const {chain} = await nclient.getInfo(); + assert.strictEqual(chain.treeRootHeight, interval + 1); + } + + // This block will be part of the new tree batch. + await node.rpc.generateToAddress([1, someAddr]); + const {chain} = await nclient.getInfo(); + assert.strictEqual(chain.treeRootHeight, interval * 2 + 1); + }); + }); + + describe('Networking info', function() { + let nodeCtx = null; + + afterEach(async () => { + if (nodeCtx) + await nodeCtx.close(); + }); + + it('should not have public address: regtest', async () => { + nodeCtx = new NodeContext({ + network: 'regtest' + }); + + await nodeCtx.open(); + const {network, nclient} = nodeCtx; + const {pool} = await nclient.getInfo(); - await node.close(); assert.strictEqual(pool.host, '0.0.0.0'); assert.strictEqual(pool.port, network.port); @@ -50,21 +331,14 @@ describe('Node HTTP', function() { }); it('should not have public address: regtest, listen', async () => { - const network = Network.get('regtest'); - - const node = new FullNode({ - network: network.type, + nodeCtx = new NodeContext({ + network: 'regtest', listen: true }); - const nclient = new NodeClient({ - port: network.rpcPort - }); - - await node.open(); - await node.connect(); + await nodeCtx.open(); + const {network, nclient} = nodeCtx; const {pool} = await nclient.getInfo(); - await node.close(); assert.strictEqual(pool.host, '0.0.0.0'); assert.strictEqual(pool.port, network.port); @@ -79,20 +353,14 @@ describe('Node HTTP', function() { }); it('should not have public address: main', async () => { - const network = Network.get('main'); - - const node = new FullNode({ - network: network.type + nodeCtx = new NodeContext({ + network: 'main' }); - const nclient = new NodeClient({ - port: network.rpcPort - }); + await nodeCtx.open(); + const {network, nclient} = nodeCtx; - await node.open(); - await node.connect(); const {pool} = await nclient.getInfo(); - await node.close(); assert.strictEqual(pool.host, '0.0.0.0'); assert.strictEqual(pool.port, network.port); @@ -107,21 +375,15 @@ describe('Node HTTP', function() { }); it('should not have public address: main, listen', async () => { - const network = Network.get('main'); - - const node = new FullNode({ - network: network.type, + nodeCtx = new NodeContext({ + network: 'main', listen: true }); - const nclient = new NodeClient({ - port: network.rpcPort - }); + await nodeCtx.open(); + const {network, nclient} = nodeCtx; - await node.open(); - await node.connect(); const {pool} = await nclient.getInfo(); - await node.close(); assert.strictEqual(pool.host, '0.0.0.0'); assert.strictEqual(pool.port, network.port); @@ -136,27 +398,22 @@ describe('Node HTTP', function() { }); it('should have public address: main, listen, publicHost', async () => { - const network = Network.get('main'); const publicHost = '100.200.11.22'; const publicPort = 11111; const publicBrontidePort = 22222; - const node = new FullNode({ - network: network.type, + nodeCtx = new NodeContext({ + network: 'main', listen: true, publicHost, publicPort, publicBrontidePort }); - const nclient = new NodeClient({ - port: network.rpcPort - }); + await nodeCtx.open(); + const {network, nclient} = nodeCtx; - await node.open(); - await node.connect(); const {pool} = await nclient.getInfo(); - await node.close(); assert.strictEqual(pool.host, '0.0.0.0'); assert.strictEqual(pool.port, network.port); @@ -175,24 +432,18 @@ describe('Node HTTP', function() { this.timeout(15000); describe('tree commit', () => { - const network = Network.get('regtest'); const {types} = rules; - const node = new FullNode({ - network: 'regtest', + const nodeCtx = new NodeContext({ apiKey: 'foo', - walletAuth: true, - memory: true, indexTx: true, indexAddress: true, rejectAbsurdFees: false }); - const nclient = new NodeClient({ - port: network.rpcPort, - apiKey: 'foo' - }); + nodeCtx.init(); + const {network, nclient} = nodeCtx; const {treeInterval} = network.names; let privkey, pubkey; @@ -201,19 +452,17 @@ describe('Node HTTP', function() { // take into account race conditions async function mineBlocks(count, address) { - for (let i = 0; i < count; i++) { - const obj = { complete: false }; - node.once('block', () => { - obj.complete = true; - }); - await nclient.execute('generatetoaddress', [1, address]); - await common.forValue(obj, 'complete', true); - } + const blockEvents = common.forEvent( + nodeCtx.nclient.socket.events, + 'block connect', + count + ); + await nodeCtx.mineBlocks(count, address); + await blockEvents; } before(async () => { - await node.open(); - await nclient.open(); + await nodeCtx.open(); await nclient.call('watch chain'); const mnemonic = Mnemonic.fromPhrase(phrase); @@ -237,14 +486,13 @@ describe('Node HTTP', function() { socketData.push({root, entry, block}); }); - node.mempool.on('tx', (tx) => { + nodeCtx.mempool.on('tx', (tx) => { mempoolData[tx.txid()] = true; }); }); after(async () => { - await nclient.close(); - await node.close(); + await nodeCtx.close(); }); beforeEach(() => { @@ -281,7 +529,7 @@ describe('Node HTTP', function() { coins.sort((a, b) => a.height - b.height); const coin = Coin.fromJSON(coins[0]); - assert.ok(node.chain.height > coin.height + network.coinbaseMaturity); + assert.ok(nodeCtx.chain.height > coin.height + network.coinbaseMaturity); mtx.addCoin(coin); const addr = Address.fromPubkey(pubkey); @@ -294,7 +542,7 @@ describe('Node HTTP', function() { assert.ok(valid); const tx = mtx.toTX(); - await node.sendTX(tx); + await nodeCtx.node.sendTX(tx); await common.forValue(mempoolData, tx.txid(), true); @@ -307,7 +555,7 @@ describe('Node HTTP', function() { assert.equal(socketData.length, 1); const {root, block, entry} = socketData[0]; - assert.bufferEqual(node.chain.db.treeRoot(), root); + assert.bufferEqual(nodeCtx.chain.db.treeRoot(), root); const info = await nclient.getInfo(); assert.notEqual(pre.chain.tip, info.chain.tip); diff --git a/test/node-rescan-test.js b/test/node-rescan-test.js new file mode 100644 index 000000000..a3868872f --- /dev/null +++ b/test/node-rescan-test.js @@ -0,0 +1,998 @@ +'use strict'; + +const assert = require('bsert'); +const {BufferSet} = require('buffer-map'); +const {BloomFilter} = require('@handshake-org/bfilter'); +const TX = require('../lib/primitives/tx'); +const nodeCommon = require('../lib/blockchain/common'); +const {scanActions} = nodeCommon; +const NodeContext = require('./util/node-context'); +const {forEvent, sleep} = require('./util/common'); +const MemWallet = require('./util/memwallet'); +const rules = require('../lib/covenants/rules'); + +describe('Node Rescan Interactive API', function() { + const TIMEOUT = 10000; + + this.timeout(TIMEOUT); + + /** @type {NodeContext} */ + let nodeCtx; + let funderWallet; + + const RESCAN_DEPTH = 10; + const names = []; + const addresses = []; + // store txs by height. + const allTXs = {}; + const addressTXs = {}; + const txHashTXs = {}; + const nameHashTXs = {}; + // use smaller filters than the default + const addressFilter = BloomFilter.fromRate(10000, 0.001); + const txHashFilter = BloomFilter.fromRate(10000, 0.001); + const nameHashFilter = BloomFilter.fromRate(10000, 0.001); + + // test matrix + const tests = [{ + name: 'all', + filter: null, + txs: allTXs, + // +1 for the coinbase tx + txCountCheck: (height, txs) => txs.length === allTXs[height].length + 1 + }, { + name: 'txhash', + filter: txHashFilter, + txs: txHashTXs, + // This has LOW Chance of failing because of the BloomFilter nature. + txCountCheck: (height, txs) => txs.length === txHashTXs[height].length + }, { + name: 'address', + filter: addressFilter, + txs: addressTXs, + // We can't do exact check because filter get's updated. + // (TODO: Add non-updating filter test + // issue - https://github.com/handshake-org/hsd/issues/855) + txCountCheck: (height, txs) => txs.length >= addressTXs[height].length + }, { + name: 'namehash', + filter: nameHashFilter, + txs: nameHashTXs, + // We can't do exact check because filter get's updated. + // (TODO: Add non-updating filter test + // issue - https://github.com/handshake-org/hsd/issues/855) + txCountCheck: (height, txs) => txs.length >= nameHashTXs[height].length + }]; + + before(async () => { + nodeCtx = new NodeContext(); + + await nodeCtx.open(); + const {network} = nodeCtx; + funderWallet = new MemWallet({ network }); + + nodeCtx.on('connect', (entry, block) => { + funderWallet.addBlock(entry, block.txs); + }); + + nodeCtx.miner.addAddress(funderWallet.getReceive()); + + // Prepare addresses bloom filter. + const walletForAddrs = new MemWallet({ network }); + + for (let i = 0; i < RESCAN_DEPTH; i++) { + const addr = walletForAddrs.createReceive(); + const hash = addr.getHash(); + addressFilter.add(hash); + addresses.push(addr.getAddress().toString(network)); + } + + { + // generate 20 blocks. + const blockEvents = forEvent(nodeCtx, 'block', 20); + + for (let i = 0; i < 20; i++) { + const block = await nodeCtx.miner.mineBlock(); + await nodeCtx.chain.add(block); + } + + await blockEvents; + } + + // For 10 blocks create 3 different kind of transactions for each filter: + // 1. regular send to address. + // 2. regular send but only txhash + // 3. name open + { + const blockEvents = forEvent(nodeCtx, 'block', RESCAN_DEPTH); + + for (let i = 0; i < RESCAN_DEPTH; i++) { + const name = rules.grindName(20, nodeCtx.height, nodeCtx.network); + const nameHash = rules.hashName(name, nodeCtx.network); + nameHashFilter.add(nameHash); + names.push(name); + + const openTX = await funderWallet.sendOpen(name); + const sendTX = await funderWallet.send({ + outputs: [{ + address: addresses[i], + value: 1e4 + }] + }); + + const normalTX = await funderWallet.send({}); + const txHash = normalTX.hash(); + + allTXs[nodeCtx.height + 1] = [openTX, sendTX, normalTX]; + addressTXs[nodeCtx.height + 1] = [sendTX]; + txHashTXs[nodeCtx.height + 1] = [normalTX]; + nameHashTXs[nodeCtx.height + 1] = [openTX]; + + txHashFilter.add(txHash); + + const txEvents = forEvent(nodeCtx.mempool, 'tx', 3); + await nodeCtx.mempool.addTX(openTX.toTX()); + await nodeCtx.mempool.addTX(sendTX.toTX()); + await nodeCtx.mempool.addTX(normalTX.toTX()); + await txEvents; + + const block = await nodeCtx.miner.mineBlock(); + await nodeCtx.chain.add(block); + } + + await blockEvents; + }; + }); + + after(async () => { + await nodeCtx.close(); + await nodeCtx.destroy(); + }); + + for (const test of tests) { + it(`should rescan all blocks with ${test.name} filter`, async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + let count = 0; + + await node.scanInteractive(startHeight, test.filter, async (entry, txs) => { + assert.strictEqual(entry.height, startHeight + count); + count++; + + const testTXs = test.txs[entry.height]; + + assert(test.txCountCheck(entry.height, txs)); + const hashset = txsToTXHashes(txs); + + for (const tx of testTXs) + assert(hashset.has(tx.hash())); + + return { + type: scanActions.NEXT + }; + }); + }); + + it(`should rescan only 5 blocks and stop with ${test.name} filter`, async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + let count = 0; + + const iter = async (entry, txs) => { + assert.strictEqual(entry.height, startHeight + count); + + const testTXs = test.txs[entry.height]; + + assert(test.txCountCheck(entry.height, txs)); + const hashset = txsToTXHashes(txs); + + for (const tx of testTXs) + assert(hashset.has(tx.hash())); + + count++; + + if (count === 5) { + return { + type: scanActions.ABORT + }; + } + + return { + type: scanActions.NEXT + }; + }; + + let err; + try { + await node.scanInteractive(startHeight, test.filter, iter); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + }); + + it(`should rescan the same block 5 times with ${test.name} filter (REPEAT_SET)`, async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + let count = 0; + const iter = async (entry, txs) => { + // we are repeating same block. + assert.strictEqual(entry.height, startHeight); + assert(test.txCountCheck(entry.height, txs)); + + count++; + + if (count === 5) { + return { + type: scanActions.ABORT + }; + } + + return { + type: scanActions.REPEAT_SET, + filter: test.filter + }; + }; + + let err; + try { + await node.scanInteractive(startHeight, test.filter, iter); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + }); + + it(`should rescan the same block 5 times with ${test.name} filter (REPEAT)`, async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + let count = 0; + const iter = async (entry, txs) => { + // we are repeating same block. + assert.strictEqual(entry.height, startHeight); + assert(test.txCountCheck(entry.height, txs)); + + count++; + + if (count === 5) { + return { + type: scanActions.ABORT + }; + } + + return { + type: scanActions.REPEAT + }; + }; + + let err; + try { + await node.scanInteractive(startHeight, test.filter, iter); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + }); + } + + it('should rescan the same block with updated filters (REPEAT_SET)', async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + const filterAndTXs = tests.slice(); + let test = filterAndTXs.shift(); + + // initial run is the first filter test. + let count = 0; + const iter = async (entry, txs) => { + count++; + + // we are repeating same block. + assert.strictEqual(entry.height, startHeight); + + // we are testing against the current filter. + assert(test.txCountCheck(entry.height, txs)); + + if (filterAndTXs.length === 0) { + return { + type: scanActions.ABORT + }; + } + + // next test + test = filterAndTXs.shift(); + + return { + type: scanActions.REPEAT_SET, + filter: test.filter + }; + }; + + let err; + try { + await node.scanInteractive(startHeight, test.filter, iter); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, tests.length); + }); + + it('should rescan the same block with updated filters (REPEAT_ADD)', async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + const filter = BloomFilter.fromRate(10000, 0.001); + const testTXs = allTXs[startHeight].slice(); + let expected = 0; + + const iter = async (entry, txs) => { + // we are repeating same block. + assert.strictEqual(entry.height, startHeight); + // May fail sometimes (BloomFilter) + assert.strictEqual(txs.length, expected); + + if (testTXs.length === 0) { + return { + type: scanActions.ABORT + }; + } + + // next test + const tx = testTXs.shift(); + const chunks = [tx.hash()]; + expected++; + + return { + type: scanActions.REPEAT_ADD, + chunks: chunks + }; + }; + + let err; + try { + await node.scanInteractive(startHeight, filter, iter); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + }); + + it('should rescan in parallel', async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + const events = []; + const getIter = (counterObj) => { + return async (entry, txs) => { + assert.strictEqual(entry.height, startHeight + counterObj.count); + assert.strictEqual(txs.length, 4); + + events.push({ ...counterObj }); + counterObj.count++; + + return { + type: scanActions.NEXT + }; + }; + }; + + const counter1 = { id: 1, count: 0 }; + const counter2 = { id: 2, count: 0 }; + await Promise.all([ + node.scanInteractive(startHeight, null, getIter(counter1)), + node.scanInteractive(startHeight, null, getIter(counter2)) + ]); + + assert.strictEqual(counter1.count, RESCAN_DEPTH); + assert.strictEqual(counter2.count, RESCAN_DEPTH); + + // Chain gets locked per block by default, so we should see alternating events. + // Because they start in parallel, but id1 starts first they will be + // getting events in alternating older (first one gets lock, second waits, + // second gets lock, first waits, etc.) + for (let i = 0; i < RESCAN_DEPTH; i++) { + assert.strictEqual(events[i].id, 1); + assert.strictEqual(events[i + 1].id, 2); + i++; + } + }); + + it('should rescan in series', async () => { + const {node} = nodeCtx; + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + const events = []; + const getIter = (counterObj) => { + return async (entry, txs) => { + assert.strictEqual(entry.height, startHeight + counterObj.count); + assert.strictEqual(txs.length, 4); + + events.push({ ...counterObj }); + counterObj.count++; + + return { + type: scanActions.NEXT + }; + }; + }; + + const counter1 = { id: 1, count: 0 }; + const counter2 = { id: 2, count: 0 }; + await Promise.all([ + node.scanInteractive(startHeight, null, getIter(counter1), true), + node.scanInteractive(startHeight, null, getIter(counter2), true) + ]); + + assert.strictEqual(counter1.count, RESCAN_DEPTH); + assert.strictEqual(counter2.count, RESCAN_DEPTH); + + // We lock the whole chain for this test, so we should see events + // from one to other. + for (let i = 0; i < RESCAN_DEPTH; i++) { + assert.strictEqual(events[i].id, 1); + assert.strictEqual(events[i + RESCAN_DEPTH].id, 2); + } + }); + + describe('HTTP', function() { + let client = null; + + beforeEach(async () => { + client = nodeCtx.nodeClient(); + + await client.open(); + }); + + afterEach(async () => { + if (client.opened) + await client.close(); + }); + + for (const test of tests) { + it(`should rescan all blocks with ${test.name} filter`, async () => { + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + let count = 0; + + client.hook('block rescan interactive', (rawEntry, rawTXs) => { + const [entry, txs] = parseBlock(rawEntry, rawTXs); + assert.strictEqual(entry.height, startHeight + count); + count++; + + const testTXs = test.txs[entry.height]; + + assert(test.txCountCheck(entry.height, txs)); + const hashset = txsToTXHashes(txs); + + for (const tx of testTXs) + assert(hashset.has(tx.hash())); + + return { + type: scanActions.NEXT + }; + }); + + let filter = null; + + if (test.filter) + filter = test.filter.encode(); + + await client.rescanInteractive(startHeight, filter); + assert.strictEqual(count, RESCAN_DEPTH); + + count = 0; + if (test.filter) + await client.setFilter(test.filter.encode()); + + await client.rescanInteractive(startHeight); + }); + + it(`should rescan only 5 blocks and stop with ${test.name} filter`, async () => { + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + let count = 0; + + client.hook('block rescan interactive', (rawEntry, rawTXs) => { + const [entry, txs] = parseBlock(rawEntry, rawTXs); + assert.strictEqual(entry.height, startHeight + count); + + const testTXs = test.txs[entry.height]; + + assert(test.txCountCheck(entry.height, txs)); + const hashset = txsToTXHashes(txs); + + for (const tx of testTXs) + assert(hashset.has(tx.hash())); + + count++; + + if (count === 5) { + return { + type: scanActions.ABORT + }; + } + + return { + type: scanActions.NEXT + }; + }); + + let aborted = false; + + client.hook('block rescan interactive abort', (message) => { + assert.strictEqual(message, 'scan request aborted.'); + aborted = true; + }); + + let filter = null; + + if (test.filter) + filter = test.filter.encode(); + + let err; + try { + await client.rescanInteractive(startHeight, filter); + } catch (e) { + err = e; + } + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + assert.strictEqual(aborted, true); + + // rescan using socket.filter + count = 0; + aborted = false; + + if (test.filter) + await client.setFilter(test.filter.encode()); + + err = null; + try { + await client.rescanInteractive(startHeight, null); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + assert.strictEqual(aborted, true); + }); + + it(`should rescan the same block 5 times with ${test.name} filter (REPEAT_SET)`, async () => { + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + let count = 0; + client.hook('block rescan interactive', (rawEntry, rawTXs) => { + const [entry, txs] = parseBlock(rawEntry, rawTXs); + + // we are repeating same block. + assert.strictEqual(entry.height, startHeight); + assert(test.txCountCheck(entry.height, txs)); + + count++; + + if (count === 5) { + return { + type: scanActions.ABORT + }; + } + + return { + type: scanActions.REPEAT_SET, + filter: test.filter ? test.filter.encode() : null + }; + }); + + let aborted = false; + + client.hook('block rescan interactive abort', (message) => { + assert.strictEqual(message, 'scan request aborted.'); + aborted = true; + }); + + let filter = null; + + if (test.filter) + filter = test.filter.encode(); + + let err; + try { + await client.rescanInteractive(startHeight, filter); + } catch (e) { + err = e; + } + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + assert.strictEqual(aborted, true); + + count = 0; + aborted = false; + + if (test.filter) + await client.setFilter(test.filter.encode()); + + err = null; + try { + await client.rescanInteractive(startHeight); + } catch (e) { + err = e; + } + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + assert.strictEqual(aborted, true); + }); + + it(`should rescan the same block 5 times with ${test.name} filter (REPEAT)`, async () => { + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + let count = 0; + client.hook('block rescan interactive', (rawEntry, rawTXs) => { + const [entry, txs] = parseBlock(rawEntry, rawTXs); + + // we are repeating same block. + assert.strictEqual(entry.height, startHeight); + assert(test.txCountCheck(entry.height, txs)); + + count++; + + if (count === 5) { + return { + type: scanActions.ABORT + }; + } + + return { + type: scanActions.REPEAT, + filter: test.filter ? test.filter.encode() : null + }; + }); + + let aborted = false; + + client.hook('block rescan interactive abort', (message) => { + assert.strictEqual(message, 'scan request aborted.'); + aborted = true; + }); + + let filter = null; + + if (test.filter) + filter = test.filter.encode(); + + let err; + try { + await client.rescanInteractive(startHeight, filter); + } catch (e) { + err = e; + } + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + assert.strictEqual(aborted, true); + + count = 0; + aborted = false; + + if (test.filter) + await client.setFilter(test.filter.encode()); + + err = null; + try { + await client.rescanInteractive(startHeight); + } catch (e) { + err = e; + } + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, 5); + assert.strictEqual(aborted, true); + }); + } + + it('should rescan the same block with update filters (REPEAT_SET)', async () => { + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + const filterAndTXs = tests.slice(); + let test = filterAndTXs.shift(); + + let count = 0; + + client.hook('block rescan interactive', (rawEntry, rawTXs) => { + count++; + + const [entry, txs] = parseBlock(rawEntry, rawTXs); + + assert.strictEqual(entry.height, startHeight); + assert(test.txCountCheck(entry.height, txs)); + + if (filterAndTXs.length === 0) { + return { + type: scanActions.ABORT + }; + } + + test = filterAndTXs.shift(); + + return { + type: scanActions.REPEAT_SET, + filter: test.filter.encode() + }; + }); + + let aborted = false; + client.hook('block rescan interactive abort', (message) => { + assert.strictEqual(message, 'scan request aborted.'); + aborted = true; + }); + + let filter = null; + + if (test.filter) + filter = test.filter.encode(); + + let err; + try { + await client.rescanInteractive(startHeight, filter); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(count, tests.length); + assert.strictEqual(aborted, true); + }); + + it('should rescan the same block with updated filters (REPEAT_ADD)', async () => { + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + + let testTXs = allTXs[startHeight].slice(); + let filter = BloomFilter.fromRate(10000, 0.001); + let expected = 0; + + client.hook('block rescan interactive', (rawEntry, rawTXs) => { + const [entry, txs] = parseBlock(rawEntry, rawTXs); + + // we are repeating same block. + assert.strictEqual(entry.height, startHeight); + // May fail sometimes (BloomFilter) + assert.strictEqual(txs.length, expected); + + if (testTXs.length === 0) { + return { + type: scanActions.ABORT + }; + } + + // next test + const tx = testTXs.shift(); + const chunks = [tx.hash()]; + expected++; + + return { + type: scanActions.REPEAT_ADD, + chunks: chunks + }; + }); + + let aborted = false; + client.hook('block rescan interactive abort', (message) => { + assert.strictEqual(message, 'scan request aborted.'); + aborted = true; + }); + + let err; + try { + await client.rescanInteractive(startHeight, filter.encode()); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(aborted, true); + + // Now try using client.filter + err = null; + aborted = false; + filter = BloomFilter.fromRate(10000, 0.001); + testTXs = allTXs[startHeight].slice(); + expected = 0; + + await client.setFilter(filter.encode()); + try { + await client.rescanInteractive(startHeight); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'scan request aborted.'); + assert.strictEqual(aborted, true); + }); + + it('should rescan in parallel', async () => { + const client2 = nodeCtx.nodeClient(); + await client2.open(); + + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + const events = []; + const counter1 = { id: 1, count: 0 }; + const counter2 = { id: 2, count: 0 }; + + const getIter = (counterObj) => { + return async (rawEntry, rawTXs) => { + const [entry, txs] = parseBlock(rawEntry, rawTXs); + assert.strictEqual(entry.height, startHeight + counterObj.count); + assert.strictEqual(txs.length, 4); + + events.push({ ...counterObj }); + counterObj.count++; + + return { + type: scanActions.NEXT + }; + }; + }; + + client.hook('block rescan interactive', getIter(counter1)); + client2.hook('block rescan interactive', getIter(counter2)); + + await Promise.all([ + client.rescanInteractive(startHeight), + client2.rescanInteractive(startHeight) + ]); + + assert.strictEqual(counter1.count, RESCAN_DEPTH); + assert.strictEqual(counter2.count, RESCAN_DEPTH); + + // Chain gets locked per block, so we should see alternating events. + // Because they start in parallel, but id1 starts first they will be + // getting events in alternating older (first one gets lock, second waits, + // second gets lock, first waits, etc.) + for (let i = 0; i < RESCAN_DEPTH; i++) { + assert.strictEqual(events[i].id, 1); + assert.strictEqual(events[i + 1].id, 2); + i++; + } + }); + + it('should rescan in series', async () => { + const client2 = nodeCtx.nodeClient(); + await client2.open(); + + const startHeight = nodeCtx.height - RESCAN_DEPTH + 1; + const events = []; + const counter1 = { id: 1, count: 0 }; + const counter2 = { id: 2, count: 0 }; + + const getIter = (counterObj) => { + return async (rawEntry, rawTXs) => { + const [entry, txs] = parseBlock(rawEntry, rawTXs); + assert.strictEqual(entry.height, startHeight + counterObj.count); + assert.strictEqual(txs.length, 4); + + events.push({ ...counterObj }); + counterObj.count++; + + return { + type: scanActions.NEXT + }; + }; + }; + + client.hook('block rescan interactive', getIter(counter1)); + client2.hook('block rescan interactive', getIter(counter2)); + + await Promise.all([ + client.rescanInteractive(startHeight, null, true), + client2.rescanInteractive(startHeight, null, true) + ]); + + assert.strictEqual(counter1.count, RESCAN_DEPTH); + assert.strictEqual(counter2.count, RESCAN_DEPTH); + + // We lock the whole chain for this test, so we should see events + // from one to other. + for (let i = 0; i < RESCAN_DEPTH; i++) { + assert.strictEqual(events[i].id, 1); + assert.strictEqual(events[i + RESCAN_DEPTH].id, 2); + } + }); + + // Make sure the client closing does not cause the chain locker to get + // indefinitely locked. (https://github.com/bcoin-org/bsock/pull/11) + it('should stop rescan when client closes', async () => { + const client2 = nodeCtx.nodeClient(); + + const addr = funderWallet.getAddress().toString(nodeCtx.network); + + // Client does not need rescan hooks, because we make + // sure that the rescan hooks are never actually called. + // Client closes before they are called. + // We simulate this by acquiring chain lock before we + // call rescan and then closing the client. + const unlock = await nodeCtx.chain.locker.lock(); + const rescan = client.rescanInteractive(0); + let err = null; + rescan.catch(e => err = e); + + // make sure call reaches the server. + await sleep(50); + await client.close(); + try { + await rescan; + } catch (e) { + err = e; + } + + assert(err); + assert(err.message, 'Job timed out.'); + unlock(); + + // Make sure lock was unlocked. + // w/o bsock update this will fail with timeout. + await client2.execute('generatetoaddress', [1, addr]); + }); + }); +}); + +function txsToTXHashes(txs) { + return new BufferSet(txs.map(tx => tx.hash())); +} + +function parseEntry(data) { + // 32 hash + // 4 height + // 4 nonce + // 8 time + // 32 prev + // 32 tree + // 24 extranonce + // 32 reserved + // 32 witness + // 32 merkle + // 4 version + // 4 bits + // 32 mask + // 32 chainwork + // 304 TOTAL + + assert(Buffer.isBuffer(data)); + // Just enough to read the three data below + assert(data.length >= 44); + + return { + hash: data.slice(0, 32), + height: data.readUInt32LE(32), + time: data.readUInt32LE(40) + }; +} + +function parseBlock(entry, txs) { + const block = parseEntry(entry); + const out = []; + + for (const tx of txs) + out.push(TX.decode(tx)); + + return [block, out]; +} diff --git a/test/node-rpc-test.js b/test/node-rpc-test.js index 1bc8c7937..9701d4398 100644 --- a/test/node-rpc-test.js +++ b/test/node-rpc-test.js @@ -1,15 +1,14 @@ 'use strict'; const assert = require('bsert'); -const FullNode = require('../lib/node/fullnode'); -const SPVNode = require('../lib/node/spvnode'); const Network = require('../lib/protocol/network'); const consensus = require('../lib/protocol/consensus'); const MemWallet = require('./util/memwallet'); const TX = require('../lib/primitives/tx'); -const {NodeClient} = require('hs-client'); +const NodeContext = require('./util/node-context'); +const Address = require('../lib/primitives/address'); +const Script = require('../lib/script/script'); -const TIMEOUT = 15000; const API_KEY = 'foo'; const NETWORK = 'regtest'; @@ -22,7 +21,6 @@ const ports = { const nodeOptions = { network: NETWORK, apiKey: API_KEY, - walletAuth: true, memory: true, workers: true, workersSize: 2, @@ -30,12 +28,6 @@ const nodeOptions = { httpPort: ports.node }; -const clientOptions = { - port: ports.node, - apiKey: API_KEY, - timeout: TIMEOUT -}; - const errs = { MISC_ERROR: -1 }; @@ -43,18 +35,64 @@ const errs = { describe('RPC', function() { this.timeout(15000); + describe('getblockchaininfo', function() { + const nodeCtx = new NodeContext(nodeOptions); + nodeCtx.init(); + const nclient = nodeCtx.nclient; + + before(async () => { + await nodeCtx.open(); + }); + + after(async () => { + await nodeCtx.close(); + }); + + it('should get blockchain info', async () => { + const info = await nclient.execute('getblockchaininfo', []); + assert.strictEqual(info.chain, NETWORK); + assert.strictEqual(info.blocks, 0); + assert.strictEqual(info.headers, 0); + assert.strictEqual(info.pruned, false); + }); + }); + + describe('getrawmempool', function() { + const nodeCtx = new NodeContext(nodeOptions); + nodeCtx.init(); + const nclient = nodeCtx.nclient; + + before(async () => { + await nodeCtx.open(); + }); + + after(async () => { + await nodeCtx.close(); + }); + + it('should get raw mempool', async () => { + const hashes = await nclient.execute('getrawmempool', [true]); + assert.deepEqual(hashes, {}); + }); + }); + describe('getblock', function () { - const node = new FullNode(nodeOptions); - const nclient = new NodeClient(clientOptions); + let nodeCtx, nclient, node; before(async () => { - await node.open(); - await nclient.open(); + nodeCtx = new NodeContext({ + ...nodeOptions, + name: 'node-rpc-test' + }); + + await nodeCtx.open(); + nclient = nodeCtx.nclient; + node = nodeCtx.node; }); after(async () => { - await nclient.close(); - await node.close(); + await nodeCtx.close(); + nodeCtx = null; }); it('should rpc getblock', async () => { @@ -83,7 +121,7 @@ describe('RPC', function() { const address = 'rs1qjjpnmnrzfvxgqlqf5j48j50jmq9pyqjz0a7ytz'; // Mine two blocks. - await nclient.execute('generatetoaddress', [2, address]); + await nodeCtx.mineBlocks(2, address); const {chain} = await nclient.getInfo(); const info = await nclient.execute('getblock', [chain.tip]); @@ -138,100 +176,6 @@ describe('RPC', function() { const info = await nclient.execute('getblock', [hash]); assert.deepEqual(info.confirmations, -1); }); - - it('should validateresource (valid)', async () => { - const records = [ - [{type: 'NS', ns: 'ns1.handshake.org.'}], - [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: '00'.repeat(32)}], - [{type: 'TXT', txt: ['i like turtles', 'then who phone']}], - [{type: 'GLUE4', ns: 'ns1.nam.ek.', address: '192.168.0.1'}], - [{type: 'GLUE6', ns: 'ns2.nam.ek.', address: '::'}], - [{type: 'SYNTH4', address: '192.168.0.1'}], - [{type: 'SYNTH6', address: '::'}] - ]; - - for (const record of records) { - const data = {records: record}; - const info = await nclient.execute('validateresource', [data]); - assert.deepEqual(info, data); - } - }); - - it('should validateresource (invalid)', async () => { - const records = [ - [ - // No trailing dot - [{type: 'NS', ns: 'ns1.handshake.org'}], - 'Invalid NS record. ns must be a valid name.' - ], - [ - [{type: 'DS', keyTag: 0xffffff}], - 'Invalid DS record. KeyTag must be a uint16.' - ], - [ - [{type: 'DS', keyTag: 0xffff, algorithm: 0xffff}], - 'Invalid DS record. Algorithm must be a uint8.' - ], - [ - [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xffff}], - 'Invalid DS record. DigestType must be a uint8.' - ], - [ - [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: Buffer.alloc(0)}], - 'Invalid DS record. Digest must be a String.' - ], - [ - [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: '00'.repeat(256)}], - 'Invalid DS record. Digest is too large.' - ], - [ - [{type: 'TXT', txt: 'foobar'}], - 'Invalid TXT record. txt must be an Array.' - ], - [ - [{type: 'TXT', txt: [{}]}], - 'Invalid TXT record. Entries in txt Array must be type String.' - ], - [ - [{type: 'TXT', txt: ['0'.repeat(256)]}], - 'Invalid TXT record. Entries in txt Array must be <= 255 in length.' - ], - [ - [{type: 'GLUE4', ns: 'ns1.nam.ek', address: '192.168.0.1'}], - 'Invalid GLUE4 record. ns must be a valid name.' - ], - [ - [{type: 'GLUE4', ns: 'ns1.nam.ek.', address: '::'}], - 'Invalid GLUE4 record. Address must be a valid IPv4 address.' - ], - [ - [{type: 'GLUE6', ns: 'ns1.nam.ek', address: '::'}], - 'Invalid GLUE6 record. ns must be a valid name.' - ], - [ - [{type: 'GLUE6', ns: 'ns1.nam.ek.', address: '0.0.0.0'}], - 'Invalid GLUE6 record. Address must be a valid IPv6 address.' - ], - [ - [{type: 'SYNTH4', address: '::'}], - 'Invalid SYNTH4 record. Address must be a valid IPv4 address.' - ], - [ - [{type: 'SYNTH6', address: '127.0.0.1'}], - 'Invalid SYNTH6 record. Address must be a valid IPv6 address.' - ] - ]; - - for (const [record, reason] of records) { - try { - const data = {records: record}; - await nclient.execute('validateresource', [data]); - assert.fail(); - } catch (e) { - assert.equal(e.message, reason); - } - } - }); }); describe('pruneblockchain', function() { @@ -243,7 +187,7 @@ describe('RPC', function() { const TEST_PRUNED_BLOCKS = 10; const TEST_PRUNE_AFTER_HEIGHT = 10; - let nclient, node; + let nodeCtx; before(() => { network.block.pruneAfterHeight = TEST_PRUNE_AFTER_HEIGHT; @@ -256,91 +200,77 @@ describe('RPC', function() { }); afterEach(async () => { - if (nclient && nclient.opened) - await nclient.close(); - - if (node && node.opened) - await node.close(); + if (nodeCtx) + await nodeCtx.close(); }); it('should fail with wrong arguments', async () => { - node = new FullNode(nodeOptions); - nclient = new NodeClient(clientOptions); - - await node.open(); + nodeCtx = new NodeContext(nodeOptions); + await nodeCtx.open(); await assert.rejects(async () => { - await nclient.execute('pruneblockchain', [1]); + await nodeCtx.nclient.execute('pruneblockchain', [1]); }, { code: errs.MISC_ERROR, type: 'RPCError', message: 'pruneblockchain' }); - - await node.close(); }); it('should not work for spvnode', async () => { - node = new SPVNode(nodeOptions); - nclient = new NodeClient(clientOptions); + nodeCtx = new NodeContext({ + ...nodeOptions, + spv: true + }); - await node.open(); + await nodeCtx.open(); await assert.rejects(async () => { - await nclient.execute('pruneblockchain'); + await nodeCtx.nclient.execute('pruneblockchain'); }, { type: 'RPCError', message: 'Cannot prune chain in SPV mode.', code: errs.MISC_ERROR }); - - await node.close(); }); it('should fail for pruned node', async () => { - node = new FullNode({ + nodeCtx = new NodeContext({ ...nodeOptions, prune: true }); - - await node.open(); + await nodeCtx.open(); await assert.rejects(async () => { - await nclient.execute('pruneblockchain'); + await nodeCtx.nclient.execute('pruneblockchain'); }, { type: 'RPCError', code: errs.MISC_ERROR, message: 'Chain is already pruned.' }); - - await node.close(); }); it('should fail for short chain', async () => { - node = new FullNode(nodeOptions); - - await node.open(); + nodeCtx = new NodeContext(nodeOptions); + await nodeCtx.open(); await assert.rejects(async () => { - await nclient.execute('pruneblockchain'); + await nodeCtx.nclient.execute('pruneblockchain'); }, { type: 'RPCError', code: errs.MISC_ERROR, message: 'Chain is too short for pruning.' }); - - await node.close(); }); it('should prune chain', async () => { // default - prune: false - node = new FullNode(nodeOptions); - nclient = new NodeClient(clientOptions); - - await node.open(); + nodeCtx = new NodeContext(nodeOptions); + await nodeCtx.open(); + const {miner, nclient} = nodeCtx; const addr = 'rs1q4rvs9pp9496qawp2zyqpz3s90fjfk362q92vq8'; - node.miner.addAddress(addr); + miner.addAddress(addr); let genBlocks = TEST_PRUNE_AFTER_HEIGHT; genBlocks += TEST_PRUNED_BLOCKS; @@ -386,14 +316,19 @@ describe('RPC', function() { const block = await nclient.execute('getblock', [blocks[i]]); assert(block, `block ${i} was pruned.`); } - - await node.close(); }); }); describe('mining', function() { - const node = new FullNode(nodeOptions); - const nclient = new NodeClient(clientOptions); + const nodeCtx = new NodeContext(nodeOptions); + nodeCtx.init(); + const { + miner, + chain, + mempool, + nodeRPC, + nclient + } = nodeCtx; const wallet = new MemWallet({ network: NETWORK @@ -402,32 +337,80 @@ describe('RPC', function() { let mtx1, mtx2; before(async () => { - await node.open(); - await nclient.open(); + await nodeCtx.open(); }); after(async () => { - await nclient.close(); - await node.close(); + await nodeCtx.close(); + }); + + it('should get a block template', async () => { + const {network, chain} = nodeCtx; + const json = await nclient.execute('getblocktemplate', []); + assert.deepStrictEqual(json, { + capabilities: ['proposal'], + mutable: ['time', 'transactions', 'prevblock'], + version: 0, + rules: [], + vbavailable: {}, + vbrequired: 0, + height: 1, + previousblockhash: network.genesis.hash.toString('hex'), + treeroot: network.genesis.treeRoot.toString('hex'), + reservedroot: consensus.ZERO_HASH.toString('hex'), + mask: json.mask, + target: + '7fffff0000000000000000000000000000000000000000000000000000000000', + bits: '207fffff', + noncerange: '' + + '000000000000000000000000000000000000000000000000' + + 'ffffffffffffffffffffffffffffffffffffffffffffffff', + curtime: json.curtime, + mintime: 1580745081, + maxtime: json.maxtime, + expires: json.expires, + sigoplimit: 80000, + sizelimit: 1000000, + weightlimit: 4000000, + longpollid: chain.tip.hash.toString('hex') + '00000000', + submitold: false, + coinbaseaux: { flags: '6d696e656420627920687364' }, + coinbasevalue: 2000000000, + claims: [], + airdrops: [], + transactions: [] + }); + }); + + it('should send a block template proposal', async () => { + const {node} = nodeCtx; + const attempt = await node.miner.createBlock(); + const block = attempt.toBlock(); + const hex = block.toHex(); + const json = await nclient.execute('getblocktemplate', [{ + mode: 'proposal', + data: hex + }]); + assert.strictEqual(json, null); }); it('should submit a block', async () => { - const block = await node.miner.mineBlock(); + const block = await miner.mineBlock(); const hex = block.toHex(); const result = await nclient.execute('submitblock', [hex]); assert.strictEqual(result, null); - assert.bufferEqual(node.chain.tip.hash, block.hash()); + assert.bufferEqual(chain.tip.hash, block.hash()); }); it('should add transactions to mempool', async () => { // Fund MemWallet - node.miner.addresses.length = 0; - node.miner.addAddress(wallet.getReceive()); + miner.addresses.length = 0; + miner.addAddress(wallet.getReceive()); for (let i = 0; i < 10; i++) { - const block = await node.miner.mineBlock(); - const entry = await node.chain.add(block); + const block = await miner.mineBlock(); + const entry = await chain.add(block); wallet.addBlock(entry, block.txs); } @@ -439,7 +422,7 @@ describe('RPC', function() { address: wallet.getReceive() }] }); - await node.mempool.addTX(mtx1.toTX()); + await mempool.addTX(mtx1.toTX()); // Low fee mtx2 = await wallet.send({ @@ -449,13 +432,13 @@ describe('RPC', function() { address: wallet.getReceive() }] }); - await node.mempool.addTX(mtx2.toTX()); + await mempool.addTX(mtx2.toTX()); - assert.strictEqual(node.mempool.map.size, 2); + assert.strictEqual(mempool.map.size, 2); }); it('should get a block template', async () => { - node.rpc.refreshBlock(); + nodeRPC.refreshBlock(); const result = await nclient.execute( 'getblocktemplate', @@ -491,7 +474,7 @@ describe('RPC', function() { let fees = 0; let weight = 0; - node.rpc.refreshBlock(); + nodeRPC.refreshBlock(); const result = await nclient.execute( 'getblocktemplate', @@ -513,15 +496,25 @@ describe('RPC', function() { }); it('should mine a block', async () => { - const block = await node.miner.mineBlock(); + const block = await miner.mineBlock(); assert(block); - await node.chain.add(block); + await chain.add(block); }); }); describe('transactions', function() { - const node = new FullNode({...nodeOptions, indexTx: true}); - const nclient = new NodeClient(clientOptions); + const nodeCtx = new NodeContext({ + ...nodeOptions, + indexTX: true + }); + nodeCtx.init(); + + const { + miner, + chain, + mempool, + nclient + } = nodeCtx; const wallet = new MemWallet({ network: NETWORK @@ -530,22 +523,20 @@ describe('RPC', function() { let tx1; before(async () => { - await node.open(); - await nclient.open(); + await nodeCtx.open(); }); after(async () => { - await nclient.close(); - await node.close(); + await nodeCtx.close(); }); it('should confirm a transaction in a block', async () => { // Fund MemWallet - node.miner.addresses.length = 0; - node.miner.addAddress(wallet.getReceive()); + miner.addresses.length = 0; + miner.addAddress(wallet.getReceive()); for (let i = 0; i < 10; i++) { - const block = await node.miner.mineBlock(); - const entry = await node.chain.add(block); + const block = await miner.mineBlock(); + const entry = await chain.add(block); wallet.addBlock(entry, block.txs); } @@ -557,14 +548,15 @@ describe('RPC', function() { }] }); tx1 = mtx1.toTX(); - await node.mempool.addTX(tx1); - assert.strictEqual(node.mempool.map.size, 1); + await mempool.addTX(tx1); - const block = await node.miner.mineBlock(); + assert.strictEqual(mempool.map.size, 1); + + const block = await miner.mineBlock(); assert(block); assert.strictEqual(block.txs.length, 2); - await node.chain.add(block); + await chain.add(block); }); it('should get raw transaction', async () => { @@ -591,24 +583,23 @@ describe('RPC', function() { for (const [i, vout] of result.vout.entries()) { const output = tx.output(i); assert.equal(vout.address.version, output.address.version); - assert.equal(vout.address.string, output.address.toString(node.network)); + assert.equal(vout.address.string, output.address.toString(nodeCtx.network)); assert.equal(vout.address.hash, output.address.hash.toString('hex')); } }); }); describe('networking', function() { - const node = new FullNode({...nodeOptions, bip37: true}); - const nclient = new NodeClient(clientOptions); + const nodeCtx = new NodeContext({ ...nodeOptions, bip37: true }); + nodeCtx.init(); + const nclient = nodeCtx.nclient; before(async () => { - await node.open(); - await nclient.open(); + await nodeCtx.open(); }); after(async () => { - await nclient.close(); - await node.close(); + await nodeCtx.close(); }); it('should get service names for rpc getnetworkinfo', async () => { @@ -618,18 +609,17 @@ describe('RPC', function() { }); }); - describe('utility', function() { - const node = new FullNode({...nodeOptions}); - const nclient = new NodeClient(clientOptions); + describe('DNS Utility', function() { + const nodeCtx = new NodeContext(nodeOptions); + nodeCtx.init(); + const nclient = nodeCtx.nclient; before(async () => { - await node.open(); - await nclient.open(); + await nodeCtx.open(); }); after(async () => { - await nclient.close(); - await node.close(); + await nodeCtx.close(); }); it('should decode resource', async () => { @@ -675,5 +665,192 @@ describe('RPC', function() { } ); }); + + it('should validateresource (valid)', async () => { + const records = [ + [{type: 'NS', ns: 'ns1.handshake.org.'}], + [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: '00'.repeat(32)}], + [{type: 'TXT', txt: ['i like turtles', 'then who phone']}], + [{type: 'GLUE4', ns: 'ns1.nam.ek.', address: '192.168.0.1'}], + [{type: 'GLUE6', ns: 'ns2.nam.ek.', address: '::'}], + [{type: 'SYNTH4', address: '192.168.0.1'}], + [{type: 'SYNTH6', address: '::'}] + ]; + + for (const record of records) { + const data = {records: record}; + const info = await nclient.execute('validateresource', [data]); + assert.deepEqual(info, data); + } + }); + + it('should validateresource (invalid)', async () => { + const records = [ + [ + // No trailing dot + [{type: 'NS', ns: 'ns1.handshake.org'}], + 'Invalid NS record. ns must be a valid name.' + ], + [ + [{type: 'DS', keyTag: 0xffffff}], + 'Invalid DS record. KeyTag must be a uint16.' + ], + [ + [{type: 'DS', keyTag: 0xffff, algorithm: 0xffff}], + 'Invalid DS record. Algorithm must be a uint8.' + ], + [ + [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xffff}], + 'Invalid DS record. DigestType must be a uint8.' + ], + [ + [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: Buffer.alloc(0)}], + 'Invalid DS record. Digest must be a String.' + ], + [ + [{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: '00'.repeat(256)}], + 'Invalid DS record. Digest is too large.' + ], + [ + [{type: 'TXT', txt: 'foobar'}], + 'Invalid TXT record. txt must be an Array.' + ], + [ + [{type: 'TXT', txt: [{}]}], + 'Invalid TXT record. Entries in txt Array must be type String.' + ], + [ + [{type: 'TXT', txt: ['0'.repeat(256)]}], + 'Invalid TXT record. Entries in txt Array must be <= 255 in length.' + ], + [ + [{type: 'GLUE4', ns: 'ns1.nam.ek', address: '192.168.0.1'}], + 'Invalid GLUE4 record. ns must be a valid name.' + ], + [ + [{type: 'GLUE4', ns: 'ns1.nam.ek.', address: '::'}], + 'Invalid GLUE4 record. Address must be a valid IPv4 address.' + ], + [ + [{type: 'GLUE6', ns: 'ns1.nam.ek', address: '::'}], + 'Invalid GLUE6 record. ns must be a valid name.' + ], + [ + [{type: 'GLUE6', ns: 'ns1.nam.ek.', address: '0.0.0.0'}], + 'Invalid GLUE6 record. Address must be a valid IPv6 address.' + ], + [ + [{type: 'SYNTH4', address: '::'}], + 'Invalid SYNTH4 record. Address must be a valid IPv4 address.' + ], + [ + [{type: 'SYNTH6', address: '127.0.0.1'}], + 'Invalid SYNTH6 record. Address must be a valid IPv6 address.' + ] + ]; + + for (const [record, reason] of records) { + try { + const data = {records: record}; + await nclient.execute('validateresource', [data]); + assert.fail(); + } catch (e) { + assert.equal(e.message, reason); + } + } + }); + }); + + describe('Address Utility', function() { + const nodeCtx = new NodeContext({ + ...nodeOptions, + wallet: true + }); + + nodeCtx.init(); + + const { + node, + nclient, + wdb + } = nodeCtx; + + let wallet, addr; + + before(async () => { + await nodeCtx.open(); + wallet = await wdb.create({ id: 'test'}); + }); + + after(async () => { + await nodeCtx.close(); + }); + + it('should validate an address', async () => { + addr = await wallet.receiveAddress('default'); + const json = await nclient.execute('validateaddress', [ + addr.toString(nodeCtx.network) + ]); + + assert.deepStrictEqual(json, { + isvalid: true, + isscript: false, + isspendable: true, + address: addr.toString(node.network), + witness_program: addr.hash.toString('hex'), + witness_version: addr.version + }); + }); + + it('should not validate invalid address', async () => { + const json = await nclient.execute('validateaddress', [ + addr.toString('main') + ]); + assert.deepStrictEqual(json, { + isvalid: false + }); + }); + + it('should validate a p2wsh address', async () => { + const pubkeys = []; + for (let i = 0; i < 2; i++) { + const result = await wallet.receiveKey('default'); + pubkeys.push(Buffer.from(result.publicKey, 'hex')); + } + + const script = Script.fromMultisig(2, 2, pubkeys); + const address = Address.fromScript(script); + + const json = await nclient.execute('validateaddress', [ + address.toString(node.network) + ]); + + assert.deepStrictEqual(json, { + address: address.toString(node.network), + isscript: true, + isspendable: true, + isvalid: true, + witness_version: address.version, + witness_program: address.hash.toString('hex') + }); + }); + + it('should validate a null address', async () => { + const data = Buffer.from('foobar', 'ascii'); + const nullAddr = Address.fromNulldata(data); + + const json = await nclient.execute('validateaddress', [ + nullAddr.toString(node.network) + ]); + + assert.deepStrictEqual(json, { + address: nullAddr.toString(node.network), + isscript: false, + isspendable: false, + isvalid: true, + witness_version: nullAddr.version, + witness_program: nullAddr.hash.toString('hex') + }); + }); }); }); diff --git a/test/node-spv-sync-test.js b/test/node-spv-sync-test.js index 62134b2ec..7d411cbbc 100644 --- a/test/node-spv-sync-test.js +++ b/test/node-spv-sync-test.js @@ -158,7 +158,7 @@ describe('SPV Node Sync', function() { }); it('should send a tx from chain 1 to SPV node', async () => { - const balanceEvent = forEvent(spvwallet, 'balance'); + const balanceEvent = forEvent(spvwallet, 'balance', 1, 9000); await wallet.send({ outputs: [{ value: 1012345678, diff --git a/test/ownership-test.js b/test/ownership-test.js index b93abddee..7fcd3fa77 100644 --- a/test/ownership-test.js +++ b/test/ownership-test.js @@ -1,7 +1,7 @@ 'use strict'; const assert = require('bsert'); -const ownership = require('../lib/covenants/ownership'); +const {ownership} = require('../lib/covenants/ownership'); const Address = require('../lib/primitives/address'); const Network = require('../lib/protocol/network'); const network = Network.get('regtest'); diff --git a/test/reserved-test.js b/test/reserved-test.js index 9097b9745..202b8c726 100644 --- a/test/reserved-test.js +++ b/test/reserved-test.js @@ -24,7 +24,10 @@ describe('Reserved', function() { 'hex'), target: 'twitter.com.', value: 630133143116, - root: false + root: false, + top100: true, + custom: false, + zero: false }); }); @@ -39,7 +42,10 @@ describe('Reserved', function() { 'hex'), target: 'craigslist.org.', value: 503513487, - root: false + root: false, + top100: false, + custom: false, + zero: false }); }); @@ -54,7 +60,10 @@ describe('Reserved', function() { 'hex'), target: 'google.', value: 660214983416, - root: true + root: true, + top100: true, + custom: false, + zero: false }); }); @@ -69,7 +78,10 @@ describe('Reserved', function() { 'hex'), target: 'eth.ens.domains.', value: 136503513487, - root: false + root: false, + top100: false, + custom: true, + zero: false }); }); @@ -90,7 +102,10 @@ describe('Reserved', function() { 'hex'), target: 'kp.', value: 0, - root: true + root: true, + top100: false, + custom: false, + zero: true }); }); @@ -133,7 +148,10 @@ describe('Reserved', function() { hash: Buffer.from(hash, 'hex'), target: name, value, - root + root, + top100, + custom, + zero }); total += value; diff --git a/test/script-test.js b/test/script-test.js index 65853eb1d..9f436b143 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -8,6 +8,7 @@ const Stack = require('../lib/script/stack'); const Opcode = require('../lib/script/opcode'); const TX = require('../lib/primitives/tx'); const consensus = require('../lib/protocol/consensus'); +const ScriptNum = require('../lib/script/scriptnum'); const scripts = require('./data/script-tests.json'); @@ -328,4 +329,380 @@ describe('Script', function() { assert.ifError(err); }); } + + describe('ScriptNum', function () { + // Src: https://github.com/bitcoin/bitcoin/blob/ff564c75e751db6cfaf2a5f1b8a3b471f510976f/test/functional/test_framework/script.py#L750-L769 + const sn2bytesVector = [ + [0, []], + [1, [0x01]], + [-1, [0x81]], + [0x7F, [0x7F]], + [-0x7F, [0xFF]], + [0x80, [0x80, 0x00]], + [-0x80, [0x80, 0x80]], + [0xFF, [0xFF, 0x00]], + [-0xFF, [0xFF, 0x80]], + [0x100, [0x00, 0x01]], + [-0x100, [0x00, 0x81]], + [0x7FFF, [0xFF, 0x7F]], + [-0x8000, [0x00, 0x80, 0x80]], + [-0x7FFFFF, [0xFF, 0xFF, 0xFF]], + [0x80000000, [0x00, 0x00, 0x00, 0x80, 0x00]], + [-0x80000000, [0x00, 0x00, 0x00, 0x80, 0x80]], + [0xFFFFFFFF, [0xFF, 0xFF, 0xFF, 0xFF, 0x00]], + [123456789, [0x15, 0xCD, 0x5B, 0x07]], + [-54321, [0x31, 0xD4, 0x80]] + ]; + + // Src: https://github.com/bitcoin/bitcoin/blob/ff564c75e751db6cfaf2a5f1b8a3b471f510976f/test/functional/test_framework/script.py#L771-L775 + const serializationVector = [ + 0, 1, -1, -2, 127, 128, -255, 256, (1 << 15) - 1, -(1 << 16), + (1 << 24) - 1, (1 << 31), 1500, -1500 + ]; + + it('should serialize script numbers correctly', () => { + for (const [num, bytes] of sn2bytesVector) { + const sn = ScriptNum.fromNumber(num); + const numBytes = sn.encode(); + const testBuffer = Buffer.from(bytes); + + assert.bufferEqual(numBytes, testBuffer); + } + }); + + it('should serialize/deserialize script numbers correctly', () => { + for (const num of serializationVector) { + const encoded = ScriptNum.fromNumber(num).encode(); + const final = ScriptNum.decode(encoded).toNumber(); + + assert.strictEqual(num, final); + } + }); + }); + + describe('Script - mathops', function () { + const execOK = (scriptStr) => { + const stack = new Stack(); + + let err; + try { + const script = Script.fromString(scriptStr); + script.execute(stack); + } catch (e) { + err = e; + } + + assert(!err, `${scriptStr} ${err}`); + assert(isSuccess(stack), `${scriptStr}`); + }; + + it('should OP_1ADD', () => { + const add1Vectors = [ + '1 OP_1ADD 2 equal', + '0 OP_1ADD 1 equal', + '-1 OP_1ADD 0 equal', + '-2147483647 OP_1ADD -2147483646 equal', + '2147483647 OP_1ADD 2147483648 equal' + ]; + + for (const script of add1Vectors) + execOK(script); + }); + + it('should OP_1SUB', () => { + const sub1Vectors = [ + '1 OP_1SUB 0 equal', + '0 OP_1SUB -1 equal', + '-1 OP_1SUB -2 equal', + '-2147483647 OP_1SUB -2147483648 equal', + '2147483647 OP_1SUB 2147483646 equal' + ]; + + for (const script of sub1Vectors) + execOK(script); + }); + + it('should OP_NEGATE', () => { + const negateVectors = [ + '0 OP_NEGATE 0 equal', + '1 OP_NEGATE -1 equal', + '-1 OP_NEGATE 1 equal', + '-2147483647 OP_NEGATE 2147483647 equal', + '2147483647 OP_NEGATE -2147483647 equal' + ]; + + for (const script of negateVectors) + execOK(script); + }); + + it('should OP_ABS', () => { + const absVectors = [ + '0 OP_ABS 0 equal', + '1 OP_ABS 1 equal', + '-1 OP_ABS 1 equal', + '-2147483647 OP_ABS 2147483647 equal', + '2147483647 OP_ABS 2147483647 equal' + ]; + + for (const script of absVectors) + execOK(script); + }); + + it('should OP_NOT', () => { + const notVectors = [ + '0 OP_NOT 1 equal', + '1 OP_NOT 0 equal', + '-1 OP_NOT 0 equal', + '-2147483647 OP_NOT 0 equal', + '2147483647 OP_NOT 0 equal' + ]; + + for (const script of notVectors) + execOK(script); + }); + + it('should OP_0NOTEQUAL', () => { + const notVectors = [ + '0 OP_0NOTEQUAL 0 equal', + '1 OP_0NOTEQUAL 1 equal', + '-1 OP_0NOTEQUAL 1 equal', + '-2147483647 OP_0NOTEQUAL 1 equal', + '2147483647 OP_0NOTEQUAL 1 equal' + ]; + + for (const script of notVectors) + execOK(script); + }); + + it('should OP_ADD', () => { + const addVectors = [ + '0 0 add 0 equal', + '0 1 add 1 equal', + '1 -1 add 0 equal', + '1 2 add 3 equal', + '-2147483647 2147483647 add 0 equal', + '-2147483647 -2147483647 add -4294967294 equal', + '2147483647 2147483647 add 4294967294 equal' + ]; + + for (const script of addVectors) + execOK(script); + }); + + it('should OP_SUB', () => { + const subVectors = [ + '0 0 sub 0 equal', + '0 1 sub -1 equal', + '1 -1 sub 2 equal', + '1 2 sub -1 equal', + '-2147483647 2147483647 sub -4294967294 equal', + '-2147483647 -2147483647 sub 0 equal', + '2147483647 2147483647 sub 0 equal', + '2147483647 -2147483647 sub 4294967294 equal' + ]; + + for (const script of subVectors) + execOK(script); + }); + + it('should OP_BOOLAND', () => { + const boolandVectors = [ + '0 0 booland 0 equal', + '0 1 booland 0 equal', + '1 -1 booland 1 equal', + '1 2 booland 1 equal', + '-2147483647 2147483647 booland 1 equal', + '-2147483647 -2147483647 booland 1 equal', + '2147483647 2147483647 booland 1 equal', + '2147483647 -2147483647 booland 1 equal', + '2147483647 0 booland 0 equal', + '0 2147483647 booland 0 equal' + ]; + + for (const script of boolandVectors) + execOK(script); + }); + + it('should OP_BOOLOR', () => { + const boolorVectors = [ + '0 0 boolor 0 equal', + '0 1 boolor 1 equal', + '1 -1 boolor 1 equal', + '1 2 boolor 1 equal', + '-2147483647 2147483647 boolor 1 equal', + '-2147483647 -2147483647 boolor 1 equal', + '2147483647 2147483647 boolor 1 equal', + '2147483647 -2147483647 boolor 1 equal', + '2147483647 0 boolor 1 equal', + '0 2147483647 boolor 1 equal' + ]; + + for (const script of boolorVectors) + execOK(script); + }); + + it('should OP_NUMEQUAL', () => { + const numequalVectors = [ + '0 0 numequal 1 equal', + '0 1 numequal 0 equal', + '1 -1 numequal 0 equal', + '1 2 numequal 0 equal', + '-2147483647 2147483647 numequal 0 equal', + '-2147483647 -2147483647 numequal 1 equal', + '2147483647 2147483647 numequal 1 equal', + '2147483647 -2147483647 numequal 0 equal', + '2147483647 0 numequal 0 equal', + '0 2147483647 numequal 0 equal' + ]; + + for (const script of numequalVectors) + execOK(script); + }); + + it('should OP_NUMNOTEQUAL', () => { + const numnotequalVectors = [ + '0 0 numnotequal 0 equal', + '0 1 numnotequal 1 equal', + '1 -1 numnotequal 1 equal', + '1 2 numnotequal 1 equal', + '-2147483647 2147483647 numnotequal 1 equal', + '-2147483647 -2147483647 numnotequal 0 equal', + '2147483647 2147483647 numnotequal 0 equal', + '2147483647 -2147483647 numnotequal 1 equal', + '2147483647 0 numnotequal 1 equal', + '0 2147483647 numnotequal 1 equal' + ]; + + for (const script of numnotequalVectors) + execOK(script); + }); + + it('should OP_LESSTHAN', () => { + const lessthanVectors = [ + '0 0 lessthan 0 equal', + '0 1 lessthan 1 equal', + '1 -1 lessthan 0 equal', + '1 2 lessthan 1 equal', + '-2147483647 2147483647 lessthan 1 equal', + '-2147483647 -2147483647 lessthan 0 equal', + '2147483647 2147483647 lessthan 0 equal', + '2147483647 -2147483647 lessthan 0 equal', + '2147483647 0 lessthan 0 equal', + '0 2147483647 lessthan 1 equal' + ]; + + for (const script of lessthanVectors) + execOK(script); + }); + + it('should OP_GREATERTHAN', () => { + const greaterthanVectors = [ + '0 0 greaterthan 0 equal', + '0 1 greaterthan 0 equal', + '1 -1 greaterthan 1 equal', + '1 2 greaterthan 0 equal', + '-2147483647 2147483647 greaterthan 0 equal', + '-2147483647 -2147483647 greaterthan 0 equal', + '2147483647 2147483647 greaterthan 0 equal', + '2147483647 -2147483647 greaterthan 1 equal', + '2147483647 0 greaterthan 1 equal', + '0 2147483647 greaterthan 0 equal' + ]; + + for (const script of greaterthanVectors) + execOK(script); + }); + + it('should OP_LESSTHANOREQUAL', () => { + const lessthanorequalVectors = [ + '0 0 lessthanorequal 1 equal', + '0 1 lessthanorequal 1 equal', + '1 -1 lessthanorequal 0 equal', + '1 2 lessthanorequal 1 equal', + '-2147483647 2147483647 lessthanorequal 1 equal', + '-2147483647 -2147483647 lessthanorequal 1 equal', + '2147483647 2147483647 lessthanorequal 1 equal', + '2147483647 -2147483647 lessthanorequal 0 equal', + '2147483647 0 lessthanorequal 0 equal', + '0 2147483647 lessthanorequal 1 equal' + ]; + + for (const script of lessthanorequalVectors) + execOK(script); + }); + + it('should OP_GREATERTHANOREQUAL', () => { + const greaterthanorequalVectors = [ + '0 0 greaterthanorequal 1 equal', + '0 1 greaterthanorequal 0 equal', + '1 -1 greaterthanorequal 1 equal', + '1 2 greaterthanorequal 0 equal', + '-2147483647 2147483647 greaterthanorequal 0 equal', + '-2147483647 -2147483647 greaterthanorequal 1 equal', + '2147483647 2147483647 greaterthanorequal 1 equal', + '2147483647 -2147483647 greaterthanorequal 1 equal', + '2147483647 0 greaterthanorequal 1 equal', + '0 2147483647 greaterthanorequal 0 equal' + ]; + + for (const script of greaterthanorequalVectors) + execOK(script); + }); + + it('should OP_MIN', () => { + const minVectors = [ + '0 0 min 0 equal', + '0 1 min 0 equal', + '1 -1 min -1 equal', + '1 2 min 1 equal', + '-2147483647 2147483647 min -2147483647 equal', + '-2147483647 -2147483647 min -2147483647 equal', + '2147483647 2147483647 min 2147483647 equal', + '2147483647 -2147483647 min -2147483647 equal', + '2147483647 0 min 0 equal', + '0 2147483647 min 0 equal' + ]; + + for (const script of minVectors) + execOK(script); + }); + + it('should OP_MAX', () => { + const maxVectors = [ + '0 0 max 0 equal', + '0 1 max 1 equal', + '1 -1 max 1 equal', + '1 2 max 2 equal', + '-2147483647 0 max 0 equal', + '-2147483647 2147483647 max 2147483647 equal', + '-2147483647 -2147483647 max -2147483647 equal', + '2147483647 2147483647 max 2147483647 equal', + '2147483647 -2147483647 max 2147483647 equal', + '2147483647 0 max 2147483647 equal', + '0 2147483647 max 2147483647 equal' + ]; + + for (const script of maxVectors) + execOK(script); + }); + + it('should OP_WITHIN', () => { + const withinVectors = [ + '0 -1 1 within 1 equal', + '0 0 1 within 1 equal', + '0 -2147483647 2147483647 within 1 equal', + '-2147483647 -2147483647 2147483647 within 1 equal', + '2147483646 -2147483647 2147483647 within 1 equal', + '2147483647 -2147483647 2147483647 within 0 equal', + '0 -2147483647 -2147483647 within 0 equal', + '0 2147483647 2147483647 within 0 equal', + '0 2147483647 -2147483647 within 0 equal', + '0 2147483647 0 within 0 equal', + '0 0 2147483647 within 1 equal' + ]; + + for (const script of withinVectors) + execOK(script); + }); + }); }); diff --git a/test/txstart-test.js b/test/txstart-test.js index e458de6c6..2d349e92b 100644 --- a/test/txstart-test.js +++ b/test/txstart-test.js @@ -13,7 +13,8 @@ const Block = require('../lib/primitives/block'); const Address = require('../lib/primitives/address'); const Script = require('../lib/script/script'); const common = require('../lib/blockchain/common'); -const ownership = require('../lib/covenants/ownership'); +const {ownership} = require('../lib/covenants/ownership'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); const VERIFY_NONE = common.flags.VERIFY_NONE; const node = new FullNode({ @@ -41,9 +42,7 @@ node.chain.on('connect', (entry, block) => { wallet.getNameStatus = async (nameHash) => { assert(Buffer.isBuffer(nameHash)); const height = node.chain.height + 1; - const state = await node.chain.getNextState(); - const hardened = state.hasHardening(); - return node.chain.db.getNameStatus(nameHash, height, hardened); + return node.chain.db.getNameStatus(nameHash, height); }; describe('Disable TXs', function() { @@ -51,7 +50,12 @@ describe('Disable TXs', function() { let utxo, lastTX; + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + before(async () => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; node.network.txStart = 5; await node.open(); @@ -61,8 +65,10 @@ describe('Disable TXs', function() { }); after(async () => { - await node.close(); node.network.txStart = RESET_TXSTART; + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + await node.close(); }); it('should reject tx from mempool before txStart', async () => { @@ -77,6 +83,8 @@ describe('Disable TXs', function() { }); it('should reject claim from mempool before txStart', async () => { + this.timeout(10000); + const claim = await wallet.fakeClaim('cloudflare'); try { @@ -184,6 +192,8 @@ describe('Disable TXs', function() { }); it('should allow claim in mempool one block before txStart', async () => { + this.timeout(10000); + const claim = await wallet.fakeClaim('cloudflare'); try { diff --git a/test/util/balance.js b/test/util/balance.js new file mode 100644 index 000000000..d53b612bf --- /dev/null +++ b/test/util/balance.js @@ -0,0 +1,144 @@ +'use strict'; + +const assert = require('bsert'); +const Wallet = require('../../lib/wallet/wallet'); +const WalletClient = require('../../lib/client/wallet'); + +/** + * @property {Number} tx + * @property {Number} coin + * @property {Number} confirmed + * @property {Number} unconfirmed + * @property {Number} ulocked - unconfirmed locked + * @property {Number} clocked - confirmed locked + */ + +class Balance { + constructor(options) { + options = options || {}; + + this.tx = options.tx || 0; + this.coin = options.coin || 0; + this.confirmed = options.confirmed || 0; + this.unconfirmed = options.unconfirmed || 0; + this.ulocked = options.ulocked || 0; + this.clocked = options.clocked || 0; + } + + clone() { + return new Balance(this); + } + + cloneWithDelta(obj) { + return this.clone().apply(obj); + } + + fromBalance(obj) { + this.tx = obj.tx; + this.coin = obj.coin; + this.confirmed = obj.confirmed; + this.unconfirmed = obj.unconfirmed; + this.ulocked = obj.lockedUnconfirmed; + this.clocked = obj.lockedConfirmed; + + return this; + } + + apply(balance) { + this.tx += balance.tx || 0; + this.coin += balance.coin || 0; + this.confirmed += balance.confirmed || 0; + this.unconfirmed += balance.unconfirmed || 0; + this.ulocked += balance.ulocked || 0; + this.clocked += balance.clocked || 0; + + return this; + } + + diff(balance) { + return new Balance({ + tx: this.tx - balance.tx, + coin: this.coin - balance.coin, + confirmed: this.confirmed - balance.confirmed, + unconfirmed: this.unconfirmed - balance.unconfirmed, + ulocked: this.ulocked - balance.ulocked, + clocked: this.clocked - balance.clocked + }); + } + + static fromBalance(wbalance) { + return new this().fromBalance(wbalance); + } +} + +/** + * @param {Wallet} wallet + * @param {String} accountName + * @returns {Promise} + */ + +async function getWalletBalance(wallet, accountName) { + assert(wallet instanceof Wallet); + const balance = await wallet.getBalance(accountName); + return Balance.fromBalance(balance.getJSON(true)); +} + +/** + * @param {WalletClient} wclient + * @param {String} id + * @param {String} accountName + * @returns {Promise} + */ + +async function getWClientBalance(wclient, id, accountName) { + assert(wclient instanceof WalletClient); + const balance = await wclient.getBalance(id, accountName); + return Balance.fromBalance(balance); +} + +/** + * @param {WalletClient.Wallet} balance + * @param {String} accountName + * @returns {Promise} + */ + +async function getWClientWalletBalance(wallet, accountName) { + assert(wallet instanceof WalletClient.Wallet); + const balance = await wallet.getBalance(accountName); + return Balance.fromBalance(balance); +} + +async function getBalance(wallet, accountName) { + if (wallet instanceof WalletClient.Wallet) + return getWClientWalletBalance(wallet, accountName); + + return getWalletBalance(wallet, accountName); +} + +/** + * @param {Wallet} wallet + * @param {String} accountName + * @param {Balance} expectedBalance + * @param {String} message + * @returns {Promise} + */ + +async function assertBalanceEquals(wallet, accountName, expectedBalance, message) { + const balance = await getBalance(wallet, accountName); + assert.deepStrictEqual(balance, expectedBalance, message); +} + +async function assertWClientBalanceEquals(wclient, id, accountName, expectedBalance, message) { + const balance = await getWClientBalance(wclient, id, accountName); + assert.deepStrictEqual(balance, expectedBalance, message); +} + +exports.Balance = Balance; + +exports.getBalance = getBalance; +exports.getWalletBalance = getWalletBalance; +exports.getWClientBalance = getWClientBalance; +exports.getWClientWalletBalance = getWClientWalletBalance; + +exports.assertBalanceEquals = assertBalanceEquals; +exports.assertWClientBalanceEquals = assertWClientBalanceEquals; diff --git a/test/util/chain.js b/test/util/chain.js index 6e338d36c..36c1bcdea 100644 --- a/test/util/chain.js +++ b/test/util/chain.js @@ -116,13 +116,32 @@ chainUtils.syncChain = async (fromChain, toChain, startHeight) => { return endHeight - startHeight; }; -chainUtils.chainTreeHas = async (chain, name) => { +chainUtils.mineBlock = async (chainObj, mtxs) => { + const tip = chainObj.chain.tip; + const job = await chainObj.miner.createJob(tip); + + if (mtxs) { + for (const mtx of mtxs) { + const [tx, view] = mtx.commit(); + + job.addTX(tx, view); + } + } + + job.refresh(); + + const block = await job.mineAsync(); + const entry = await chainObj.chain.add(block); + return { block, entry }; +}; + +chainUtils.chainTreeHasName = async (chain, name) => { assert(!chain.options.spv); const hash = rules.hashName(name); return await chain.db.tree.get(hash) != null; }; -chainUtils.chainTxnHas = async (chain, name) => { +chainUtils.chainTxnHasName = async (chain, name) => { assert(!chain.options.spv); const hash = rules.hashName(name); return await chain.db.txn.get(hash) != null; diff --git a/test/util/common.js b/test/util/common.js index c2a6e3f06..57b738558 100644 --- a/test/util/common.js +++ b/test/util/common.js @@ -103,13 +103,14 @@ common.rimraf = async function(p) { return await fs.rimraf(p); }; -common.forValue = async function forValue(obj, key, val, timeout = 5000) { +common.forValue = async function forValue(obj, key, val, timeout = 2000) { assert(typeof obj === 'object'); assert(typeof key === 'string'); const ms = 10; let interval = null; let count = 0; + const stack = getStack(); return new Promise((resolve, reject) => { interval = setInterval(() => { @@ -118,14 +119,16 @@ common.forValue = async function forValue(obj, key, val, timeout = 5000) { resolve(); } else if (count * ms >= timeout) { clearInterval(interval); - reject(new Error('Timeout waiting for value.')); + const error = new Error('Timeout waiting for value.'); + error.stack = error.stack + '\n' + stack; + reject(error); } count += 1; }, ms); }); }; -common.forEvent = async function forEvent(obj, name, count = 1, timeout = 5000) { +common.forEvent = async function forEvent(obj, name, count = 1, timeout = 2000) { assert(typeof obj === 'object'); assert(typeof name === 'string'); assert(typeof count === 'number'); @@ -134,6 +137,8 @@ common.forEvent = async function forEvent(obj, name, count = 1, timeout = 5000) let countdown = count; const events = []; + const stack = getStack(); + return new Promise((resolve, reject) => { let timeoutHandler, listener; @@ -159,9 +164,11 @@ common.forEvent = async function forEvent(obj, name, count = 1, timeout = 5000) timeoutHandler = setTimeout(() => { cleanup(); const msg = `Timeout waiting for event ${name} ` - + `(received ${count - countdown}/${count})`; + + `(received ${count - countdown}/${count})\n${stack}`; - reject(new Error(msg)); + const error = new Error(msg); + error.stack = error.stack + '\n' + stack; + reject(error); return; }, timeout); @@ -169,12 +176,14 @@ common.forEvent = async function forEvent(obj, name, count = 1, timeout = 5000) }); }; -common.forEventCondition = async function forEventCondition(obj, name, fn, timeout = 5000) { +common.forEventCondition = async function forEventCondition(obj, name, fn, timeout = 2000) { assert(typeof obj === 'object'); assert(typeof name === 'string'); assert(typeof fn === 'function'); assert(typeof timeout === 'number'); + const stack = getStack(); + return new Promise((resolve, reject) => { let timeoutHandler, listener; @@ -190,6 +199,7 @@ common.forEventCondition = async function forEventCondition(obj, name, fn, timeo res = await fn(...args); } catch (e) { cleanup(); + e.stack = e.stack + '\n' + stack; reject(e); return; } @@ -203,7 +213,9 @@ common.forEventCondition = async function forEventCondition(obj, name, fn, timeo timeoutHandler = setTimeout(() => { cleanup(); const msg = `Timeout waiting for event ${name} with condition`; - reject(new Error(msg)); + const error = new Error(msg); + error.stack = error.stack + '\n' + stack; + reject(error); return; }, timeout); @@ -357,3 +369,7 @@ class TXContext { return [tx, view]; } } + +function getStack() { + return new Error().stack.split('\n').slice(2).join('\n'); +} diff --git a/test/util/memwallet.js b/test/util/memwallet.js index 82c15d647..8604493fd 100644 --- a/test/util/memwallet.js +++ b/test/util/memwallet.js @@ -14,7 +14,7 @@ const rules = require('../../lib/covenants/rules'); const Network = require('../../lib/protocol/network'); const MTX = require('../../lib/primitives/mtx'); const HD = require('../../lib/hd/hd'); -const {BloomFilter} = require('bfilter'); +const {BloomFilter} = require('@handshake-org/bfilter'); const KeyRing = require('../../lib/primitives/keyring'); const Outpoint = require('../../lib/primitives/outpoint'); const CoinView = require('../../lib/coins/coinview'); @@ -25,11 +25,11 @@ const Claim = require('../../lib/primitives/claim'); const NameState = require('../../lib/covenants/namestate'); const NameUndo = require('../../lib/covenants/undo'); const reserved = require('../../lib/covenants/reserved'); -const ownership = require('../../lib/covenants/ownership'); +const Ownership = require('../../lib/covenants/ownership'); const policy = require('../../lib/protocol/policy'); const {Resource} = require('../../lib/dns/resource'); const Address = require('../../lib/primitives/address'); -const {OwnershipProof} = ownership; +const {OwnershipProof, ownership} = Ownership; const {states} = NameState; const {types} = rules; @@ -40,7 +40,6 @@ class MemWallet { this.network = Network.primary; this.master = null; this.key = null; - this.witness = false; this.account = 0; this.height = 0; this.receiveDepth = 1; @@ -86,11 +85,6 @@ class MemWallet { this.key = options.key; } - if (options.witness != null) { - assert(typeof options.witness === 'boolean'); - this.witness = options.witness; - } - if (options.account != null) { assert(typeof options.account === 'number'); this.account = options.account; @@ -1030,10 +1024,7 @@ class MemWallet { const output = new Output(); output.address = addr; output.value = 0; - output.covenant.type = types.OPEN; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(0); - output.covenant.push(rawName); + output.covenant.setOpen(nameHash, rawName); const mtx = new MTX(); mtx.outputs.push(output); @@ -1084,11 +1075,7 @@ class MemWallet { const output = new Output(); output.address = addr; output.value = lockup; - output.covenant.type = types.BID; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(start); - output.covenant.push(rawName); - output.covenant.pushHash(blind); + output.covenant.setBid(nameHash, start, rawName, blind); const mtx = new MTX(); mtx.outputs.push(output); @@ -1148,10 +1135,7 @@ class MemWallet { const output = new Output(); output.address = coin.address; output.value = value; - output.covenant.type = types.REVEAL; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushHash(nonce); + output.covenant.setReveal(nameHash, ns.height, nonce); mtx.addOutpoint(prevout); mtx.outputs.push(output); @@ -1208,9 +1192,7 @@ class MemWallet { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.REDEEM; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); + output.covenant.setRedeem(nameHash, ns.height); mtx.outputs.push(output); } @@ -1268,16 +1250,13 @@ class MemWallet { output.address = coin.address; output.value = ns.value; - output.covenant.type = types.REGISTER; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); + let record = EMPTY; if (resource) - output.covenant.push(resource); - else - output.covenant.push(EMPTY); + record = resource; - output.covenant.pushHash(this.getRenewalBlock()); + const blockHash = this.getRenewalBlock(); + output.covenant.setRegister(nameHash, ns.height, record, blockHash); const mtx = new MTX(); mtx.addOutpoint(ns.owner); @@ -1337,10 +1316,7 @@ class MemWallet { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.UPDATE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.push(resource); + output.covenant.setUpdate(nameHash, ns.height, resource); const mtx = new MTX(); mtx.addOutpoint(ns.owner); @@ -1392,10 +1368,7 @@ class MemWallet { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.RENEW; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushHash(this.getRenewalBlock()); + output.covenant.setRenew(nameHash, ns.height, this.getRenewalBlock()); const mtx = new MTX(); mtx.addOutpoint(ns.owner); @@ -1445,11 +1418,7 @@ class MemWallet { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.TRANSFER; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.pushU8(address.version); - output.covenant.push(address.hash); + output.covenant.setTransfer(nameHash, ns.height, address); const mtx = new MTX(); mtx.addOutpoint(ns.owner); @@ -1499,10 +1468,7 @@ class MemWallet { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.UPDATE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.push(EMPTY); + output.covenant.setUpdate(nameHash, ns.height, EMPTY); const mtx = new MTX(); mtx.addOutpoint(ns.owner); @@ -1559,14 +1525,15 @@ class MemWallet { const output = new Output(); output.address = address; output.value = coin.value; - output.covenant.type = types.FINALIZE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); - output.covenant.push(rawName); - output.covenant.pushU8(flags); - output.covenant.pushU32(ns.claimed); - output.covenant.pushU32(ns.renewals); - output.covenant.pushHash(this.getRenewalBlock()); + output.covenant.setFinalize( + nameHash, + ns.height, + rawName, + flags, + ns.claimed, + ns.renewals, + this.getRenewalBlock() + ); const mtx = new MTX(); mtx.addOutpoint(ns.owner); @@ -1615,9 +1582,7 @@ class MemWallet { const output = new Output(); output.address = coin.address; output.value = coin.value; - output.covenant.type = types.REVOKE; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(ns.height); + output.covenant.setRevoke(nameHash, ns.height); const mtx = new MTX(); mtx.addOutpoint(ns.owner); diff --git a/test/util/migrations.js b/test/util/migrations.js index d3f6a140a..ca1d627a9 100644 --- a/test/util/migrations.js +++ b/test/util/migrations.js @@ -9,11 +9,18 @@ const assert = require('bsert'); const Logger = require('blgr'); const Network = require('../../lib/protocol/network'); +const consensus = require('../../lib/protocol/consensus'); +const BlockTemplate = require('../../lib/mining/template'); const bdb = require('bdb'); -const { - Migrator -} = require('../../lib/migrations/migrator'); +let Migrator = class {}; + +try { + const migrator = require('../../lib/migrations/migrator'); + Migrator = migrator.Migrator; +} catch (e) { + ; +} const oldMockLayout = { V: bdb.key('V'), @@ -26,9 +33,9 @@ const mockLayout = { // data for testing a: bdb.key('a'), - b: bdb.key('a'), - c: bdb.key('a'), - d: bdb.key('a') + b: bdb.key('b'), + c: bdb.key('c'), + d: bdb.key('d') }; const DB_FLAG_ERROR = 'mock chain needs migration'; @@ -195,3 +202,148 @@ exports.migrationError = (migrations, ids, flagError) => { return error; }; + +exports.prefix2hex = function prefix2hex(prefix) { + return Buffer.from(prefix, 'ascii').toString('hex'); +}; + +exports.dumpDB = async (db, prefixes) => { + const data = await db.dump(); + const filtered = {}; + + for (const [key, value] of Object.entries(data)) { + for (const prefix of prefixes) { + if (key.startsWith(prefix)) { + filtered[key] = value; + break; + } + } + } + + return filtered; +}; + +exports.dumpChainDB = async (chaindb, prefixes) => { + return exports.dumpDB(chaindb.db, prefixes); +}; + +exports.checkEntries = async (ldb, data) => { + for (const [key, value] of Object.entries(data)) { + const bkey = Buffer.from(key, 'hex'); + const bvalue = Buffer.from(value, 'hex'); + + const stored = await ldb.get(bkey); + + assert(stored, + `Value for ${key} not found in db, expected: ${value}`); + assert.bufferEqual(stored, bvalue, + `Value for ${key}: ${stored.toString('hex')} does not match expected: ${value}`); + } +}; + +exports.fillEntries = async (ldb, data) => { + const batch = await ldb.batch(); + + for (const [key, value] of Object.entries(data)) { + const bkey = Buffer.from(key, 'hex'); + const bvalue = Buffer.from(value, 'hex'); + + batch.put(bkey, bvalue); + } + + await batch.write(); +}; + +exports.writeVersion = (b, key, name, version) => { + const value = Buffer.alloc(name.length + 4); + + value.write(name, 0, 'ascii'); + value.writeUInt32LE(version, name.length); + + b.put(key, value); +}; + +exports.getVersion = (data, name) => { + const error = 'version mismatch'; + + if (data.length !== name.length + 4) + throw new Error(error); + + if (data.toString('ascii', 0, name.length) !== name) + throw new Error(error); + + return data.readUInt32LE(name.length); +}; + +exports.checkVersion = async (ldb, versionDBKey, expectedVersion) => { + const data = await ldb.get(versionDBKey); + const version = exports.getVersion(data, 'wallet'); + + assert.strictEqual(version, expectedVersion); +}; + +// Chain generation +const REGTEST_TIME = 1580745078; +const getBlockTime = height => REGTEST_TIME + (height * 10 * 60); + +/** + * Create deterministic block. + * @param {Object} options + * @param {Chain} options.chain + * @param {Miner} options.miner + * @param {ChainEntry} options.tip + * @param {Address} options.address + * @param {Number} options.txno + * @returns {BlockTemplate} + */ + +exports.createBlock = async (options) => { + const { + chain, + miner, + tip, + address, + txno + } = options; + const version = await chain.computeBlockVersion(tip); + const mtp = await chain.getMedianTime(tip); + const time = getBlockTime(tip.height + 1); + + const state = await chain.getDeployments(time, tip); + const target = await chain.getTarget(time, tip); + const root = chain.db.treeRoot(); + + const attempt = new BlockTemplate({ + prevBlock: tip.hash, + treeRoot: root, + reservedRoot: consensus.ZERO_HASH, + height: tip.height + 1, + version: version, + time: time, + bits: target, + mtp: mtp, + flags: state.flags, + address: address, + coinbaseFlags: Buffer.from('Miner for data gen', 'ascii'), + interval: miner.network.halvingInterval, + weight: miner.options.reservedWeight, + sigops: miner.options.reservedSigops + }); + + miner.assemble(attempt); + + const _createCB = attempt.createCoinbase.bind(attempt); + attempt.createCoinbase = function createCoinbase() { + const cb = _createCB(); + const wit = Buffer.alloc(8); + const id = txno; + // make txs deterministic + wit.writeUInt32LE(id, 0, true); + cb.inputs[0].sequence = id; + cb.inputs[0].witness.setData(1, wit); + cb.refresh(); + return cb; + }; + + return attempt; +}; diff --git a/test/util/node-context.js b/test/util/node-context.js index 76a1d97a5..aa2efd3eb 100644 --- a/test/util/node-context.js +++ b/test/util/node-context.js @@ -1,122 +1,381 @@ 'use strict'; -const assert = require('assert'); -const FullNode = require('../../lib/node/fullnode'); +const assert = require('bsert'); +const common = require('./common'); +const fs = require('bfile'); const Network = require('../../lib/protocol/network'); +const SPVNode = require('../../lib/node/spvnode'); +const FullNode = require('../../lib/node/fullnode'); +const WalletNode = require('../../lib/wallet/node'); +const plugin = require('../../lib/wallet/plugin'); +const {NodeClient, WalletClient} = require('../../lib/client'); const Logger = require('blgr'); class NodeContext { - constructor(network, size) { - this.network = Network.get(network); - this.size = size || 4; - this.nodes = []; + /** @type {FullNode|SPVNode} */ + node; - this.init(); + /** @type {WalletClient} */ + wclient; + + /** @type {NodeClient} */ + nclient; + + constructor(options = {}) { + this.name = 'node-test'; + this.options = {}; + this.prefix = null; + this.opened = false; + this.logger = new Logger({ + console: true, + filename: null, + level: 'none' + }); + + this.initted = false; + this.node = null; + this.walletNode = null; + this.nclient = null; + this.wclient = null; + + this.clients = []; + + this.fromOptions(options); + } + + fromOptions(options) { + const fnodeOptions = { + ...options, + memory: true, + network: 'regtest', + listen: false, + wallet: false, + spv: false, + logger: this.logger, + + // wallet plugin options + walletHttpPort: null + }; + + if (options.name != null) { + assert(typeof options.name === 'string'); + this.name = options.name; + } + + if (options.network != null) + fnodeOptions.network = Network.get(options.network).type; + + if (options.name != null) + fnodeOptions.name = options.name; + + if (options.listen != null) { + assert(typeof options.listen === 'boolean'); + fnodeOptions.listen = options.listen; + } + + if (options.prefix != null) { + fnodeOptions.prefix = options.prefix; + fnodeOptions.memory = false; + this.prefix = fnodeOptions.prefix; + } + + if (options.memory != null) { + assert(typeof options.memory === 'boolean'); + assert(!(options.memory && options.prefix), + 'Can not set prefix with memory.'); + + fnodeOptions.memory = options.memory; + } + + if (!fnodeOptions.memory && !fnodeOptions.prefix) { + fnodeOptions.prefix = common.testdir(this.name); + this.prefix = fnodeOptions.prefix; + } + + if (options.wallet != null) + fnodeOptions.wallet = options.wallet; + + if (options.spv != null) { + assert(typeof options.spv === 'boolean'); + fnodeOptions.spv = options.spv; + } + + if (options.httpPort != null) { + assert(typeof options.httpPort === 'number'); + fnodeOptions.httpPort = options.httpPort; + } + + if (options.walletHttpPort != null) { + assert(typeof options.walletHttpPort === 'number'); + fnodeOptions.walletHttpPort = options.walletHttpPort; + } + + if (options.timeout != null) { + assert(typeof options.timeout === 'number'); + fnodeOptions.timeout = options.timeout; + } + + if (options.standalone != null) { + assert(typeof options.standalone === 'boolean'); + fnodeOptions.standalone = options.standalone; + } + + this.options = fnodeOptions; } init() { - for (let i = 0; i < this.size; i++) { - const port = this.network.port + i; - - let last = port - 1; - - if (last < this.network.port) - last = port; - - const node = new FullNode({ - network: this.network, - memory: true, - logger: new Logger({ - level: 'debug', - file: false, - console: false - }), - listen: true, - publicHost: '127.0.0.1', - publicPort: port, - httpPort: port + 100, - host: '127.0.0.1', - port: port, - seeds: [ - `127.0.0.1:${last}` - ] - }); + if (this.initted) + return; - node.on('error', (err) => { - node.logger.error(err); - }); + if (this.options.spv) + this.node = new SPVNode(this.options); + else + this.node = new FullNode(this.options); + + if (this.options.wallet && !this.options.standalone) { + this.node.use(plugin); + } else if (this.options.wallet && this.options.standalone) { + this.walletNode = new WalletNode({ + ...this.options, - this.nodes.push(node); + nodeHost: '127.0.0.1', + nodePort: this.options.httpPort, + nodeApiKey: this.options.apiKey, + + httpPort: this.options.walletHttpPort, + apiKey: this.options.apiKey + }); } + + // Initial wallets. + this.nclient = this.nodeClient(); + + if (this.options.wallet) + this.wclient = this.walletClient(); + + this.initted = true; } - open() { - const jobs = []; + get network() { + return this.node.network; + } - for (const node of this.nodes) - jobs.push(node.open()); + get miner() { + return this.node.miner; + } - return Promise.all(jobs); + get mempool() { + return this.node.mempool; } - close() { - const jobs = []; + get chain() { + return this.node.chain; + } - for (const node of this.nodes) - jobs.push(node.close()); + get nodeRPC() { + return this.node.rpc; + } - return Promise.all(jobs); + get height() { + return this.chain.tip.height; } - async connect() { - for (const node of this.nodes) { - await node.connect(); - await new Promise(r => setTimeout(r, 1000)); - } + get wdb() { + if (!this.options.wallet) + return null; + + if (this.walletNode) + return this.walletNode.wdb; + + return this.node.get('walletdb').wdb; + } + + /* + * Event Listeners wrappers + */ + + on(event, listener) { + this.node.on(event, listener); + } + + once(event, listener) { + this.node.once(event, listener); + } + + addListener(event, listener) { + this.node.addListener(event, listener); + } + + removeListener(event, listener) { + this.node.removeListener(event, listener); } - async disconnect() { - for (let i = this.nodes.length - 1; i >= 0; i--) { - const node = this.nodes[i]; - await node.disconnect(); - await new Promise(r => setTimeout(r, 1000)); + removeAllListeners(event) { + this.node.removeAllListeners(event); + } + + /* + * Life Cycle + */ + + async open() { + this.init(); + + if (this.opened) + return; + + if (this.prefix) + await fs.mkdirp(this.prefix); + + const open = common.forEvent(this.node, 'open'); + await this.node.ensure(); + await this.node.open(); + await this.node.connect(); + this.node.startSync(); + await open; + + if (this.walletNode) { + const walletOpen = common.forEvent(this.walletNode, 'open'); + await this.walletNode.open(); + await walletOpen; } + + if (this.wclient) + await this.wclient.open(); + + await this.nclient.open(); + + this.opened = true; } - startSync() { - for (const node of this.nodes) { - node.chain.synced = true; - node.chain.emit('full'); - node.startSync(); + async close() { + if (!this.opened) + return; + + const closeClients = []; + + for (const client of this.clients) { + if (client.opened) + closeClients.push(client.close()); + } + + await Promise.all(closeClients); + + if (this.walletNode) { + const walletClose = common.forEvent(this.walletNode, 'close'); + await this.walletNode.close(); + await walletClose; } + + const close = common.forEvent(this.node, 'close'); + await this.node.close(); + await close; + + this.node = null; + this.wclient = null; + this.nclient = null; + this.opened = false; + this.initted = false; } - stopSync() { - for (const node of this.nodes) - node.stopSync(); + async destroy() { + if (this.prefix) + await fs.rimraf(this.prefix); } - async generate(index, blocks) { - const node = this.nodes[index]; + /* + * Helpers + */ - assert(node); + enableLogging(level = 'debug') { + this.logger.setLevel(level); + } - for (let i = 0; i < blocks; i++) { - const block = await node.miner.mineBlock(); - await node.chain.add(block); - } + disableLogging() { + this.logger.setLevel('none'); + } + + /** + * Execute an RPC using the node client. + * @param {String} method - RPC method + * @param {Array} params - method parameters + * @returns {Promise} - Returns a two item array with the + * RPC's return value or null as the first item and an error or + * null as the second item. + */ + + async nrpc(method, params) { + return this.nclient.execute(method, params); } - height(index) { - const node = this.nodes[index]; + /** + * Execute an RPC using the wallet client. + * @param {String} method - RPC method + * @param {Array} params - method parameters + * @returns {Promise} - Returns a two item array with the RPC's return value + * or null as the first item and an error or null as the second item. + */ + + async wrpc(method, params) { + return this.wclient.execute(method, params); + }; - assert(node); + /** + * Create new client + * @param {Object} [options] + * @returns {NodeClient} + */ - return node.chain.height; + nodeClient(options = {}) { + const client = new NodeClient({ + timeout: this.options.timeout, + apiKey: this.options.apiKey, + port: this.options.httpPort || this.network.rpcPort, + ...options + }); + + this.clients.push(client); + + return client; } - async sync() { - return new Promise(r => setTimeout(r, 3000)); + /** + * Create new wallet client. + * @param {Object} [options] + * @returns {WalletClient} + */ + + walletClient(options = {}) { + const client = new WalletClient({ + timeout: this.options.timeout, + apiKey: this.options.apiKey, + port: this.options.walletHttpPort || this.network.walletPort, + ...options + }); + + this.clients.push(client); + + return client; + } + + /** + * Mine blocks and wait for connect. + * @param {Number} count + * @param {Address} address + * @param {ChainEntry} [tip=chain.tip] - Tip to mine on + * @returns {Promise} - Block hashes + */ + + async mineBlocks(count, address, tip) { + assert(this.open); + + if (!tip) + tip = this.chain.tip; + + for (let i = 0; i < count; i++) { + const block = await this.miner.mineBlock(tip, address); + tip = await this.chain.add(block); + } } } diff --git a/test/util/nodes-context.js b/test/util/nodes-context.js new file mode 100644 index 000000000..2989464df --- /dev/null +++ b/test/util/nodes-context.js @@ -0,0 +1,202 @@ +'use strict'; + +const assert = require('assert'); +const Network = require('../../lib/protocol/network'); +const NodeContext = require('./node-context'); + +class NodesContext { + constructor(network, size = 1) { + this.network = Network.get(network); + this.size = size; + this.nodeCtxs = []; + + assert(this.size > 0); + } + + init(options) { + for (let i = 0; i < this.size; i++) + this.addNode(options); + } + + addNode(options = {}) { + const index = this.nodeCtxs.length; + + let seedPort = getPort(this.network, index - 1); + + if (options.seedNodeIndex != null) + seedPort = getPort(this.network, options.seedNodeIndex); + + const port = this.network.port + index; + const brontidePort = this.network.brontidePort + index; + const httpPort = this.network.rpcPort + index + 100; + const walletHttpPort = this.network.walletPort + index + 200; + const nsPort = this.network.nsPort + index; + const rsPort = this.network.rsPort + index + 100; + + const seeds = []; + + if (options.seedNodeIndex != null || index > 0) + seeds.push(`127.0.0.1:${seedPort}`); + + const nodeCtx = new NodeContext({ + listen: true, + + ...options, + + // override + name: `node-${index}`, + network: this.network, + port: port, + brontidePort: brontidePort, + rsPort: rsPort, + nsPort: nsPort, + httpPort: httpPort, + walletHttpPort: walletHttpPort, + + seeds: seeds + }); + + this.nodeCtxs.push(nodeCtx); + return nodeCtx; + } + + /** + * Open all or specific nodes. + * @param {Number} [index=-1] default all + * @returns {Promise} + */ + + open(index = -1) { + if (index !== -1) + return this.context(index).open(); + + const jobs = []; + + for (const nodeCtx of this.nodeCtxs) + jobs.push(nodeCtx.open()); + + return Promise.all(jobs); + } + + /** + * Close all or specific nodes. + * @param {Number} [index=-1] default all + * @returns {Promise} + */ + + close(index = -1) { + if (index !== -1) + return this.context(index).close(); + + const jobs = []; + + for (const nodeCtx of this.nodeCtxs) + jobs.push(nodeCtx.close()); + + return Promise.all(jobs); + } + + /** + * Destroy specific or all nodes. Clean up directories on the disk. + * @param {Number} [index=-1] default all + * @returns {Promise} + */ + + destroy(index = -1) { + if (index !== -1) + return this.context(index).destroy(); + + const jobs = []; + + for (const nodeCtx of this.nodeCtxs) + jobs.push(nodeCtx.destroy()); + + return Promise.all(jobs); + } + + /** + * Connect all nodes. + * @returns {Promise} + */ + + async connect() { + for (const nodeCtx of this.nodeCtxs) { + await nodeCtx.node.connect(); + await nodeCtx.node.startSync(); + } + } + + /** + * Disconnect all nodes. + * @returns {Promise} + */ + + async disconnect() { + for (let i = this.nodeCtxs.length - 1; i >= 0; i--) { + const node = this.nodeCtxs[i].node; + await node.disconnect(); + } + } + + /** + * Start syncing. + */ + + startSync() { + for (const nodeCtx of this.nodeCtxs) { + nodeCtx.chain.synced = true; + nodeCtx.chain.emit('full'); + nodeCtx.node.startSync(); + } + } + + /** + * Stop syncing. + */ + + stopSync() { + for (const nodeCtx of this.nodeCtxs) + nodeCtx.stopSync(); + } + + /** + * Mine blocks. + * @param {Number} index + * @param {Number} blocks + * @param {String} address + * @param {ChainEntry} [tip=chain.tip] + * @returns {Promise} + */ + + async generate(index, blocks, address, tip) { + return this.context(index).mineBlocks(blocks, address, tip); + } + + /** + * Get NodeCtx for the node. + * @param {Number} index + * @returns {NodeContext} + */ + + context(index) { + const nodeCtx = this.nodeCtxs[index]; + assert(nodeCtx); + return nodeCtx; + } + + /** + * Get height for the node. + * @param {Number} index + * @returns {Number} + */ + + height(index) { + return this.context(index).height; + } +} + +function getPort(network, index) { + return Math.max(network.port + index, network.port); +} + +module.exports = NodesContext; diff --git a/test/util/stub.js b/test/util/stub.js new file mode 100644 index 000000000..75045ef09 --- /dev/null +++ b/test/util/stub.js @@ -0,0 +1,91 @@ +'use strict'; + +const assert = require('bsert'); +const path = require('path'); +const {StubResolver, wire} = require('bns'); +const fs = require('bfile'); +const {tmpdir} = require('os'); + +let CACHE = {}; + +/** + * Proxy requests if they are not cached. + */ + +class CachedStubResolver extends StubResolver { + constructor(options) { + super(options); + + this.enabled = true; + this.cacheOnDisk = process.env['HSD_TEST_DNS_FILE_CACHE'] === 'true'; + this.cacheDir = path.join(tmpdir(), 'hsd-test'); + this.cacheFile = path.join(this.cacheDir, 'dns-cache.json'); + + this.loadCacheSync(); + } + + loadCacheSync() { + if (!this.cacheOnDisk) + return; + + if (!fs.existsSync(this.cacheDir)) + fs.mkdirSync(this.cacheDir); + + if (fs.existsSync(this.cacheFile)) + CACHE = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8')); + } + + saveCacheSync() { + if (!this.cacheOnDisk) + return; + + const stringified = JSON.stringify(CACHE, null, 2); + fs.writeFileSync(this.cacheFile, stringified, 'utf8'); + } + + setCache(qs, res) { + if (!this.enabled) + return; + + assert(qs instanceof wire.Question); + assert(res instanceof wire.Message); + + CACHE[qs.toString()] = res.toString(); + this.saveCacheSync(); + } + + hasCache(qs) { + if (!this.enabled) + return false; + + assert(qs instanceof wire.Question); + + return Boolean(CACHE[qs.toString()]); + } + + getCache(qs) { + if (!this.enabled) + return null; + + assert(qs instanceof wire.Question); + + return wire.Message.fromString(CACHE[qs.toString()]); + } + + async resolve(qs) { + if (this.hasCache(qs)) + return this.getCache(qs); + + const resolved = await super.resolve(qs); + + if (!resolved) + return null; + + this.setCache(qs, resolved); + return resolved; + } +} + +exports.CachedStubResolver = CachedStubResolver; + +exports.STUB_SERVERS = ['1.1.1.1', '1.0.0.1']; diff --git a/test/util/wallet.js b/test/util/wallet.js new file mode 100644 index 000000000..7d2b9029e --- /dev/null +++ b/test/util/wallet.js @@ -0,0 +1,88 @@ +'use strict'; + +const assert = require('bsert'); +const blake2b = require('bcrypto/lib/blake2b'); +const random = require('bcrypto/lib/random'); +const ChainEntry = require('../../lib/blockchain/chainentry'); +const Input = require('../../lib/primitives/input'); +const Outpoint = require('../../lib/primitives/outpoint'); +const {ZERO_HASH} = require('../../lib/protocol/consensus'); + +const walletUtils = exports; + +walletUtils.fakeBlock = (height, prevSeed = 0, seed = prevSeed) => { + assert(height >= 0); + const prev = height === 0 ? ZERO_HASH : blake2b.digest(fromU32(((height - 1) ^ prevSeed) >>> 0)); + const hash = blake2b.digest(fromU32((height ^ seed) >>> 0)); + const root = blake2b.digest(fromU32((height | 0x80000000 ^ seed) >>> 0)); + + return { + hash: hash, + prevBlock: prev, + merkleRoot: root, + time: 500000000 + (height * (10 * 60)), + bits: 0, + nonce: 0, + height: height, + version: 0, + witnessRoot: Buffer.alloc(32), + treeRoot: Buffer.alloc(32), + reservedRoot: Buffer.alloc(32), + extraNonce: Buffer.alloc(24), + mask: Buffer.alloc(32) + }; +}; + +walletUtils.dummyInput = () => { + const hash = random.randomBytes(32); + return Input.fromOutpoint(new Outpoint(hash, 0)); +}; + +walletUtils.deterministicInput = (id) => { + const hash = blake2b.digest(fromU32(id)); + return Input.fromOutpoint(new Outpoint(hash, 0)); +}; + +walletUtils.nextBlock = (wdb, prevSeed = 0, seed = prevSeed) => { + return walletUtils.fakeBlock(wdb.state.height + 1, prevSeed, seed); +}; + +walletUtils.curBlock = (wdb, prevSeed = 0, seed = prevSeed) => { + return walletUtils.fakeBlock(wdb.state.height, prevSeed, seed); +}; + +walletUtils.fakeEntry = (height, prevSeed = 0, curSeed = prevSeed) => { + const cur = walletUtils.fakeBlock(height, prevSeed, curSeed); + return new ChainEntry(cur);; +}; + +walletUtils.nextEntry = (wdb, curSeed = 0, nextSeed = curSeed) => { + const next = walletUtils.nextBlock(wdb, curSeed, nextSeed); + return new ChainEntry(next); +}; + +walletUtils.curEntry = (wdb, prevSeed = 0, seed = prevSeed) => { + return walletUtils.fakeEntry(wdb.state.height, seed); +}; + +function fromU32(num) { + const data = Buffer.allocUnsafe(4); + data.writeUInt32LE(num, 0, true); + return data; +} + +walletUtils.dumpWDB = async (wdb, prefixes) => { + const data = await wdb.dump(); + const filtered = {}; + + for (const [key, value] of Object.entries(data)) { + for (const prefix of prefixes) { + if (key.startsWith(prefix)) { + filtered[key] = value; + break; + } + } + } + + return filtered; +}; diff --git a/test/wallet-accounts-auction-test.js b/test/wallet-accounts-auction-test.js index f370a4472..2244df580 100644 --- a/test/wallet-accounts-auction-test.js +++ b/test/wallet-accounts-auction-test.js @@ -6,7 +6,7 @@ const FullNode = require('../lib/node/fullnode'); const Address = require('../lib/primitives/address'); const rules = require('../lib/covenants/rules'); const Resource = require('../lib/dns/resource'); -const {WalletClient} = require('hs-client'); +const WalletClient = require('../lib/client/wallet'); const network = Network.get('regtest'); @@ -74,7 +74,7 @@ describe('Multiple accounts participating in same auction', function() { }); it('should open an auction and proceed to REVEAL phase', async () => { - await wallet.sendOpen(name, false, {account: 0}); + await wallet.sendOpen(name, {account: 0}); await mineBlocks(network.names.treeInterval + 2); let ns = await node.chain.db.getNameStateByName(name); assert(ns.isBidding(node.chain.height, network)); diff --git a/test/wallet-auction-test.js b/test/wallet-auction-test.js index 78d4b9eed..f9fc3b4be 100644 --- a/test/wallet-auction-test.js +++ b/test/wallet-auction-test.js @@ -12,11 +12,12 @@ const Network = require('../lib/protocol/network'); const rules = require('../lib/covenants/rules'); const Address = require('../lib/primitives/address'); const Output = require('../lib/primitives/output'); +const Coin = require('../lib/primitives/coin'); const Covenant = require('../lib/primitives/covenant'); const {Resource} = require('../lib/dns/resource'); +const {forEvent} = require('./util/common'); const network = Network.get('regtest'); -const NAME1 = rules.grindName(10, 2, network); const { treeInterval, biddingPeriod, @@ -56,7 +57,10 @@ const wdb = new WalletDB({ }); describe('Wallet Auction', function() { - let winner, openAuctionMTX, openAuctionMTX2; + let wallet, wallet2; + + const name1 = rules.grindName(10, 2, network); + const name2 = rules.grindName(10, 2, network); before(async () => { // Open @@ -64,23 +68,36 @@ describe('Wallet Auction', function() { await chain.open(); await miner.open(); await wdb.open(); + await wdb.connect(); // Set up wallet - winner = await wdb.create(); + wallet = await wdb.create(); + wallet2 = await wdb.create(); chain.on('connect', async (entry, block) => { await wdb.addBlock(entry, block.txs); }); + chain.on('disconnect', async (entry) => { + await wdb.removeBlock(entry); + }); + // Generate blocks to roll out name and fund wallet - let winnerAddr = await winner.createReceive(); - winnerAddr = winnerAddr.getAddress().toString(network); - for (let i = 0; i < 4; i++) { - const block = await cpu.mineBlock(null, winnerAddr); + let walletAddr = await wallet.createReceive(); + walletAddr = walletAddr.getAddress().toString(network); + for (let i = 0; i < 5; i++) { + const block = await cpu.mineBlock(null, walletAddr); + await chain.add(block); + } + + walletAddr = (await wallet2.createReceive()).getAddress().toString(network); + for (let i = 0; i < 5; i++) { + const block = await cpu.mineBlock(null, walletAddr); await chain.add(block); } }); after(async () => { + await wdb.disconnect(); await wdb.close(); await miner.close(); await chain.close(); @@ -88,45 +105,101 @@ describe('Wallet Auction', function() { }); describe('Duplicate OPENs', function() { + // Prepare several OPEN txs to mine them on the network. + // Because they don't have any height, we can reuse them whenever + // we want. + const OPENS1 = 4; + const openTXs = []; + + // block/mempool/confirm indexes + let openIndex = 0; + const insertIndexes = []; + it('should open auction', async () => { - openAuctionMTX = await winner.createOpen(NAME1, false); - await winner.sign(openAuctionMTX); - const tx = openAuctionMTX.toTX(); - await wdb.addTX(tx); + for (let i = 0; i < OPENS1; i++) { + const open = await wallet.createOpen(name1); + await wallet.sign(open); + + assert.strictEqual(open.inputs.length, 1); + // make sure we don't double spend. + wallet.lockCoin(open.inputs[0].prevout); + openTXs.push(open); + } + + // This one will not get confirmed, but will be forever erased. + insertIndexes[0] = openIndex; + const openMTX = openTXs[openIndex++]; + const tx = openMTX.toTX(); + const addResult = await wdb.addTX(tx); + assert.ok(addResult); + assert.strictEqual(addResult.wids.size, 1); + assert.ok(addResult.wids.has(wallet.wid)); + + const pending = await wallet.getPending(); + assert.strictEqual(pending.length, 1); + assert.bufferEqual(pending[0].hash, tx.hash()); }); it('should fail to create duplicate open', async () => { let err; try { - await winner.createOpen(NAME1, false); + await wallet.createOpen(name1); } catch (e) { err = e; } assert(err); - assert.strictEqual(err.message, `Already sent an open for: ${NAME1}.`); + assert.strictEqual(err.message, `Already sent an open for: ${name1}.`); + }); + + it('should not accept own duplicate open', async () => { + const pendingBefore = await wallet.getPending(); + assert.strictEqual(pendingBefore.length, 1); + assert.bufferEqual(pendingBefore[0].hash, openTXs[insertIndexes[0]].hash()); + + const openMTX = openTXs[openIndex]; + const result = await wdb.addTX(openMTX.toTX()); + assert.strictEqual(result, null); + + const pendingAfter = await wallet.getPending(); + assert.strictEqual(pendingAfter.length, 1); + assert.bufferEqual(pendingAfter[0].hash, openTXs[insertIndexes[0]].hash()); }); - it('should mine 1 block', async () => { + it('should mine 1 block with different OPEN tx', async () => { const job = await cpu.createJob(); - job.addTX(openAuctionMTX.toTX(), openAuctionMTX.view); + + const removeEvents = forEvent(wdb, 'remove tx'); + + insertIndexes[1] = openIndex; + const openMTX = openTXs[openIndex++]; + + const [tx, view] = openMTX.commit(); + job.addTX(tx, view); job.refresh(); const block = await job.mineAsync(); - assert(await chain.add(block)); + + const removedTXs = await removeEvents; + assert.strictEqual(removedTXs.length, 1); + const removedTX = removedTXs[0].values[1]; + assert.bufferEqual(removedTX.hash(), openTXs[0].hash()); + + const pending = await wallet.getPending(); + assert.strictEqual(pending.length, 0); }); it('should fail to re-open auction during OPEN phase', async () => { let err; try { - await winner.createOpen(NAME1, false); + await wallet.createOpen(name1); } catch (e) { err = e; } assert(err); - assert.strictEqual(err.message, `Name is already opening: ${NAME1}.`); + assert.strictEqual(err.message, `Name is already opening: ${name1}.`); }); it('should mine enough blocks to enter BIDDING phase', async () => { @@ -138,12 +211,12 @@ describe('Wallet Auction', function() { }); it('should fail to send bid to null address', async () => { - const mtx = await winner.makeBid(NAME1, 1000, 2000, 0); + const mtx = await wallet.makeBid(name1, 1000, 2000, 0); mtx.outputs[0].address = new Address(); - await winner.fill(mtx); - await winner.finalize(mtx); + await wallet.fill(mtx); + await wallet.finalize(mtx); - const fn = async () => await winner.sendMTX(mtx); + const fn = async () => await wallet.sendMTX(mtx); await assert.rejects(fn, {message: 'Cannot send to null address.'}); }); @@ -151,13 +224,13 @@ describe('Wallet Auction', function() { it('should fail to re-open auction during BIDDING phase', async () => { let err; try { - await winner.createOpen(NAME1, false); + await wallet.createOpen(name1); } catch (e) { err = e; } assert(err); - assert.strictEqual(err.message, `Name is not available: ${NAME1}.`); + assert.strictEqual(err.message, `Name is not available: ${name1}.`); }); it('should mine enough blocks to expire auction', async () => { @@ -169,33 +242,34 @@ describe('Wallet Auction', function() { }); it('should open auction (again)', async () => { - openAuctionMTX2 = await winner.createOpen(NAME1, false); - await winner.sign(openAuctionMTX2); - const tx = openAuctionMTX2.toTX(); - await wdb.addTX(tx); + // This one will be inserted and THEN confirmed. + insertIndexes[2] = openIndex; + const mtx = openTXs[openIndex++]; + await wdb.addTX(mtx.toTX()); }); it('should fail to create duplicate open (again)', async () => { let err; try { - await winner.createOpen(NAME1, false); + await wallet.createOpen(name1); } catch (e) { err = e; } assert(err); - assert.strictEqual(err.message, `Already sent an open for: ${NAME1}.`); + assert.strictEqual(err.message, `Already sent an open for: ${name1}.`); }); it('should confirm OPEN transaction', async () => { const job = await cpu.createJob(); - job.addTX(openAuctionMTX2.toTX(), openAuctionMTX2.view); + const [tx, view] = openTXs[insertIndexes[2]].commit(); + job.addTX(tx, view); job.refresh(); const block = await job.mineAsync(); assert(await chain.add(block)); - let ns = await chain.db.getNameStateByName(NAME1); + let ns = await chain.db.getNameStateByName(name1); let state = ns.state(chain.height, network); assert.strictEqual(state, states.OPENING); @@ -205,10 +279,361 @@ describe('Wallet Auction', function() { assert(await chain.add(block)); } - ns = await chain.db.getNameStateByName(NAME1); + ns = await chain.db.getNameStateByName(name1); state = ns.state(chain.height, network); assert.strictEqual(state, states.BIDDING); }); + + it('should create TX spending change of the OPEN', async () => { + // Last OPEN and spending change will be used for the test in + // the pending index test. + const lastOpenMTX = openTXs[insertIndexes[2]]; + const change = lastOpenMTX.outputs[1]; + assert.notStrictEqual(change.value, 0); + + // does not matter where this goes. + const spendMTX = wallet.makeTX([{ + value: 1e5, + address: change.address + }]); + + const coin = Coin.fromTX(lastOpenMTX.toTX(), 1, wdb.height); + await spendMTX.fund([coin], { + changeAddress: await wallet.changeAddress() + }); + + // We don't mine this transaction and reuse this to make sure + // double opens are properly removed. + await wallet.sign(spendMTX); + const added = await wdb.addTX(spendMTX.toTX()); + assert.ok(added); + assert.strictEqual(added.wids.size, 1); + }); + + it('should mine enough blocks to expire auction (again)', async () => { + for (let i = 0; i < biddingPeriod + revealPeriod; i++) { + const block = await cpu.mineBlock(); + assert(block); + assert(await chain.add(block)); + } + }); + + it('should insert OPEN into the block', async () => { + // This makes sure the confirmed/mined TX does not get removed + const job = await cpu.createJob(); + + insertIndexes[3] = openIndex; + const openMTX = openTXs[openIndex++]; + let countRemoves = 0; + + wdb.on('remove tx', () => { + countRemoves++; + }); + + const [tx, view] = openMTX.commit(); + job.addTX(tx, view); + job.refresh(); + + const block = await job.mineAsync(); + assert(await chain.add(block)); + + assert.strictEqual(countRemoves, 0); + }); + + it('should revert the two auctions and only leave one open', async () => { + const pendingBefore = await wallet.getPending(); + assert.strictEqual(pendingBefore.length, 1); + + await wdb.rollback(biddingPeriod + revealPeriod + 2); + const pendingAfter = await wallet.getPending(); + + // first OPEN and tx spending from first OPEN should get removed. + // This mimics the behaviour of the mempool where OPENs from the block + // will end up getting removed, if there's OPEN sitting there. + assert.strictEqual(pendingAfter.length, 1); + + const secondTX = await wallet.getTX(openTXs[insertIndexes[2]].hash()); + assert.strictEqual(secondTX, null); + }); + + it('should resync and recover', async () => { + for (let i = wdb.height; i <= chain.tip.height; i++) { + const entry = await chain.getEntryByHeight(i); + const block = await chain.getBlock(entry.hash); + await wdb.addBlock(entry, block.txs); + } + + const secondTX = await wallet.getTX(openTXs[insertIndexes[2]].hash()); + assert.notStrictEqual(secondTX, null); + }); + + it('should handle foreign double open after sending open', async () => { + const open = await wallet.createOpen(name2); + await wallet.sign(open); + + const open2 = await wallet2.createOpen(name2); + await wallet2.sign(open2); + + // try to open. + await wdb.addTX(open.toTX()); + const pending1 = await wallet.getPending(); + assert.strictEqual(pending1.length, 1); + assert.bufferEqual(pending1[0].hash, open.hash()); + + const job = await cpu.createJob(); + const [tx, view] = open2.commit(); + job.addTX(tx, view); + job.refresh(); + + const block = await job.mineAsync(); + assert(await chain.add(block)); + + const pending1after = await wallet.getPending(); + assert.strictEqual(pending1after.length, 0); + }); + }); + + // This affects linked ones mostly as they are guaranteed + // to reselect the same inputs: REVEAL, REDEEM, REGISTER, UPDATE, + // RENEW, TRANSFER, FINALIZE and REVOKE, + describe('Duplicate Pending Requests', function() { + const name1 = rules.grindName(10, 2, network); + const name2 = rules.grindName(10, 2, network); + const expectedError = name => `Credit is already pending for: ${name}.`; + + const mineBlock = async (txs = []) => { + const job = await cpu.createJob(); + + for (const tx of txs) + job.pushTX(tx); + + job.refresh(); + + const block = await job.mineAsync(); + assert(await chain.add(block)); + }; + + const mineBlocks = async (n) => { + for (let i = 0; i < n; i++) + await mineBlock(); + }; + + it('should get to the reveal', async () => { + const open1 = await wallet.sendOpen(name1); + const open2 = await wallet.sendOpen(name2); + + await mineBlock([open1, open2]); + await mineBlocks(treeInterval); + + const bid11 = await wallet.sendBid(name1, 1000, 2000); + const bid12 = await wallet.sendBid(name1, 1500, 2000); + const bid21 = await wallet.sendBid(name2, 1000, 2000); + const bid22 = await wallet.sendBid(name2, 1500, 2000); + + await mineBlock([bid11, bid12, bid21, bid22]); + await mineBlocks(biddingPeriod); + }); + + it('should REVEAL and fail duplicate reveal', async () => { + const reveal1 = await wallet.sendReveal(name1); + assert(reveal1); + const reveal2 = await wallet.sendReveal(name2); + assert(reveal2); + + let err; + try { + await wallet.sendReveal(name1); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, `No bids to reveal for name: ${name1}.`); + + await mineBlock([reveal1, reveal2]); + + err = null; + + try { + await wallet.sendReveal(name2); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, `No bids to reveal for name: ${name2}.`); + + await mineBlocks(revealPeriod); + }); + + it('should REDEEM and fail duplicate redeem', async () => { + const redeem1 = await wallet.sendRedeem(name1); + assert(redeem1); + const redeem2 = await wallet.sendRedeem(name2); + assert(redeem2); + + let err; + try { + await wallet.sendRedeem(name1); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, `No reveals to redeem for name: ${name1}.`); + + await mineBlock([redeem1, redeem2]); + + err = null; + + try { + await wallet.sendRedeem(name2); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, `No reveals to redeem for name: ${name2}.`); + }); + + it('should REGISTER and fail duplicate register', async () => { + const register1 = await wallet.sendUpdate(name1, Resource.fromString('name1.1')); + assert(register1); + const register2 = await wallet.sendUpdate(name2, Resource.fromString('name2.1')); + assert(register2); + + let err; + try { + await wallet.sendUpdate(name1, Resource.fromString('hello')); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, expectedError(name1)); + + await mineBlock([register1, register2]); + + // This becomes update. + const update = await wallet.sendUpdate(name2, Resource.fromString('name2.2')); + assert(update); + + await mineBlock([update]); + }); + + it('should UPDATE and fail duplicate UPDATE', async () => { + const update = await wallet.sendUpdate(name1, Resource.fromString('name1.2')); + assert(update); + + let err = null; + try { + await wallet.sendUpdate(name1, Resource.fromString('name1.3')); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, expectedError(name1)); + + await mineBlock([update]); + }); + + it('should RENEW and fail duplicate RENEW', async () => { + await mineBlocks(treeInterval + 1); + const renew = await wallet.sendRenewal(name1); + assert(renew); + const renew2 = await wallet.sendRenewal(name2); + assert(renew2); + + let err = null; + try { + await wallet.sendRenewal(name1); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, expectedError(name1)); + + await mineBlock([renew, renew2]); + + err = null; + try { + await wallet.sendRenewal(name2); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, `Can not renew yet: ${name2}.`); + + await mineBlocks(treeInterval + 1); + }); + + it('should TRANSFER and fail duplicate TRANSFER', async () => { + const recv = await wallet2.receiveAddress(); + const transfer = await wallet.sendTransfer(name1, recv); + assert(transfer); + + let err = null; + try { + await wallet.sendTransfer(name1, recv); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, expectedError(name1)); + + await mineBlock([transfer]); + + let err2 = null; + try { + await wallet.sendTransfer(name1, recv); + } catch (e) { + err2 = e; + } + + assert(err2); + assert.strictEqual(err2.message, `Name is already being transferred: ${name1}.`); + + await mineBlocks(transferLockup); + }); + + it('should FINALIZE and fail duplicate FINALIZE', async () => { + const finalize = await wallet.sendFinalize(name1); + assert(finalize); + + let err = null; + try { + await wallet.sendFinalize(name1); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, expectedError(name1)); + + await mineBlock([finalize]); + }); + + it('should REVOKE and fail duplicate REVOKE', async () => { + const revoke = await wallet.sendRevoke(name2); + assert(revoke); + + let err = null; + try { + await wallet.sendRevoke(name2); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, expectedError(name2)); + + await mineBlock([revoke]); + await mineBlocks(10); + }); }); describe('Batch TXs', function() { @@ -260,6 +685,7 @@ describe('Wallet Auction', function() { // Create wallet wallet = await wdb.create(); receive = await wallet.receiveAddress(); + mempool.length = 0; // Fund wallet await mineBlocks(20); @@ -828,15 +1254,28 @@ describe('Wallet Auction', function() { let startHeight; const oldRenewalWindow = network.names.renewalWindow; - before(() => { + let oldLookahead; + + before(async () => { network.names.renewalWindow = 160; for (let i = 0; i < 800; i++) names.push(`name_${i}`); + + // Increase lookahead + oldLookahead = (await wallet.getAccount('default')).lookahead; + await wallet.modifyAccount('default', { + lookahead: consensus.MAX_BLOCK_OPENS + 1 + }); }); - after(() => { + after(async () => { network.names.renewalWindow = oldRenewalWindow; + + // Reset lookahead + await wallet.modifyAccount('default', { + lookahead: oldLookahead + }); }); it('should not batch too many OPENs', async () => { @@ -846,7 +1285,7 @@ describe('Wallet Auction', function() { await assert.rejects( wallet.createBatch(batch), - {message: 'Too many OPENs.'} // Might exceed wallet lookahead also + {message: 'Too many OPENs.'} ); }); diff --git a/test/wallet-balance-test.js b/test/wallet-balance-test.js new file mode 100644 index 000000000..065f8c99c --- /dev/null +++ b/test/wallet-balance-test.js @@ -0,0 +1,2544 @@ +'use strict'; + +const assert = require('bsert'); +const Network = require('../lib/protocol/network'); +const Mnemonic = require('../lib/hd/mnemonic'); +const FullNode = require('../lib/node/fullnode'); +const WalletPlugin = require('../lib/wallet/plugin'); +const MTX = require('../lib/primitives/mtx'); +const Coin = require('../lib/primitives/coin'); +const Output = require('../lib/primitives/output'); +const {Resource} = require('../lib/dns/resource'); +const {types, grindName} = require('../lib/covenants/rules'); +const {forEventCondition} = require('./util/common'); +const {Balance, assertBalanceEquals} = require('./util/balance'); + +/** + * Wallet balance tracking tests. + * + * TODO: + * - Add CoinView support to chain <-> wallet and update input tests. + * - Add coin discovery on unconfirm + * - Add spent coin state recovery on unconfirm/confirm for pending txs. + * - Add spent coin state recovery on insert/insert(block) and confirm. + */ + +const network = Network.get('regtest'); +const mnemData = require('./data/mnemonic-english.json'); + +// make wallets addrs deterministic. +const phrases = mnemData.map(d => Mnemonic.fromPhrase(d[1])); + +const { + treeInterval, + biddingPeriod, + revealPeriod, + transferLockup +} = network.names; + +/** + * @enum {Number} + */ + +const DISCOVER_TYPES = { + NONE: 0, + BEFORE_CONFIRM: 1, + BEFORE_UNCONFIRM: 2, + BEFORE_ERASE: 3, + BEFORE_BLOCK_CONFIRM: 4, + BEFORE_BLOCK_UNCONFIRM: 5 +}; + +const { + NONE, + BEFORE_CONFIRM, + BEFORE_UNCONFIRM, + BEFORE_ERASE, + BEFORE_BLOCK_CONFIRM, + BEFORE_BLOCK_UNCONFIRM +} = DISCOVER_TYPES; + +const openingPeriod = treeInterval + 2; + +// default gen wallets. +const WALLET_N = 5; +const GRIND_NAME_LEN = 10; + +// Wallet consts +const DEFAULT_ACCOUNT = 'default'; +const ALT_ACCOUNT = 'alt'; + +// Balances +const INIT_BLOCKS = treeInterval; +const INIT_FUND = 10e6; +const NULL_BALANCE = new Balance({ + tx: 0, + coin: 0, + unconfirmed: 0, + confirmed: 0, + ulocked: 0, + clocked: 0 +}); + +const INIT_BALANCE = new Balance({ + tx: 1, + coin: 1, + unconfirmed: INIT_FUND, + confirmed: INIT_FUND, + ulocked: 0, + clocked: 0 +}); + +const HARD_FEE = 1e4; +const SEND_AMOUNT = 2e6; +const SEND_AMOUNT_2 = 3e6; + +// first is loser if it matters. +const BLIND_AMOUNT_1 = 1e6; +const BID_AMOUNT_1 = BLIND_AMOUNT_1 / 4; +const BLIND_ONLY_1 = BLIND_AMOUNT_1 - BID_AMOUNT_1; + +// second is winner. +const BLIND_AMOUNT_2 = 2e6; +const BID_AMOUNT_2 = BLIND_AMOUNT_2 / 4; +const BLIND_ONLY_2 = BLIND_AMOUNT_2 - BID_AMOUNT_2; + +// Loser balances for primary +const FINAL_PRICE_1 = 1e5; +const FINAL_PRICE_2 = 2e5; // less then 1e6/4 (2.5e5) + +// const BLIND_AMOUNT_3 = 3e6; +// const BID_AMOUNT_3 = BLIND_AMOUNT_3 / 4; +// const BLIND_ONLY_3 = BLIND_AMOUNT_3 - BID_AMOUNT_3; + +// Empty resource +const EMPTY_RS = Resource.fromJSON({ records: [] }); + +/* + * Wallet helpers + */ + +async function getAddrStr(wallet, acct = 0) { + return (await wallet.receiveAddress(acct)).toString(network); +} + +function getAheadAddr(account, ahead, master) { + const nextIndex = account.receiveDepth + account.lookahead + ahead; + const receiveKey = account.deriveReceive(nextIndex, master); + const nextAddr = receiveKey.getAddress(); + + return { nextAddr, receiveKey }; +} + +async function catchUpToAhead(wallet, accountName, ahead) { + for (let i = 0; i < ahead; i++) + await wallet.createReceive(accountName); +}; + +async function resign(wallet, mtx) { + for (const input of mtx.inputs) + input.witness.length = 0; + + await wallet.sign(mtx); +}; + +/* + * Balance helpers + */ + +/** + * @returns {Promise} + */ + +async function assertRecalcBalanceEquals(wallet, accountName, expected, message) { + await wallet.recalculateBalances(); + assertBalanceEquals(wallet, accountName, expected, message); +} + +/** + * @param {Balance} balance + * @param {Balance} delta + * @returns {Balance} + */ + +function applyDelta(balance, delta) { + return balance.cloneWithDelta(delta); +} + +describe('Wallet Balance', function() { + let node, chain, wdb, genWallets = WALLET_N;; + + // wallets + // alt wallets are clones of allWallets to aid us in lookahead test. + let primary, walletIndex, allWallets = [], cloneWallets = []; + + /* + * Contextual helpers + */ + + const prepare = () => { + node = new FullNode({ + network: network.type, + memory: true, + plugins: [WalletPlugin], + noDNS: true, + noNS: true + }); + + chain = node.chain; + wdb = node.require('walletdb').wdb; + }; + + const mineBlocks = async (blocks) => { + const tipHeight = chain.tip.height; + const forWalletBlock = forEventCondition(wdb, 'block connect', (entry) => { + return entry.height === tipHeight + 1; + }); + await node.rpc.generateToAddress([blocks, await getAddrStr(primary)]); + await forWalletBlock; + }; + + const setupWallets = async () => { + walletIndex = 0; + primary = await wdb.get('primary'); + + allWallets = []; + cloneWallets = []; + for (let i = 0; i < genWallets; i++) { + const name = 'wallet' + i; + const wallet = await wdb.create({ id: name, mnemonic: phrases[i] }); + const clone = await wdb.create({ id: name + '-alt', mnemonic: phrases[i] }); + allWallets.push(wallet); + cloneWallets.push(clone); + } + }; + + const fundWallets = async () => { + await mineBlocks(INIT_BLOCKS); + const addrs = []; + + for (let i = 0; i < genWallets; i++) + addrs.push(await getAddrStr(allWallets[i], DEFAULT_ACCOUNT)); + + await primary.send({ + outputs: addrs.map((addr) => { + return { + value: INIT_FUND, + address: addr + }; + }) + }); + await mineBlocks(1); + }; + + const getNextWallet = (index) => { + const i = index ? index : walletIndex++; + + if (!allWallets[i]) + throw new Error('There are not enough wallets, can not get at index: ' + i); + + return { + index: i, + wallet: allWallets[i], + wid: allWallets[i].id, + clone: cloneWallets[i], + cloneWID: cloneWallets[i].id, + opts: { + account: DEFAULT_ACCOUNT, + hardFee: HARD_FEE + } + }; + }; + + const forWTX = (id, hash) => { + return forEventCondition(wdb, 'tx', (wallet, tx) => { + return wallet.id === id && tx.hash().equals(hash); + }); + }; + + /* + * beforeall and afterall for each describe + */ + + const beforeAll = async () => { + prepare(); + + await node.open(); + await setupWallets(); + await fundWallets(); + }; + + const afterAll = async () => { + await node.close(); + node = null; + + // reduce time of the tests. + if (walletIndex !== genWallets) + console.log(`Leftover wallets, used: ${walletIndex} of ${genWallets}.`); + + genWallets = WALLET_N; + }; + + /* + * Balance testing steps. + */ + + /** + * @callback BalanceCheckFunction + * @param {Wallet} wallet + * @param {Wallet} clone + * @param {Number} ahead + * @param {Object} [opts] + */ + + /** + * @typedef {Object} TestBalances + * @property {Balance} TestBalances.initialBalance + * @property {Balance} TestBalances.sentBalance + * @property {Balance} TestBalances.confirmedBalance + * @property {Balance} TestBalances.unconfirmedBalance + * @property {Balance} TestBalances.eraseBalance + * @property {Balance} TestBalances.blockConfirmedBalance + * @property {Balance} TestBalances.blockUnconfirmedBalance + * @property {Balance} [TestBalances.blockFinalConfirmedBalance] + */ + + /** + * @typedef {Object} CheckFunctions + * @property {BalanceCheckFunction} CheckFunctions.initCheck + * @property {BalanceCheckFunction} CheckFunctions.sentCheck + * @property {BalanceCheckFunction} CheckFunctions.confirmedCheck + * @property {BalanceCheckFunction} CheckFunctions.unconfirmedCheck + * @property {BalanceCheckFunction} CheckFunctions.eraseCheck + * @property {BalanceCheckFunction} CheckFunctions.blockConfirmCheck + * @property {BalanceCheckFunction} CheckFunctions.blockUnconfirmCheck + * @property {BalanceCheckFunction} CheckFunctions.blockFinalConfirmCheck + */ + + /** + * @callback BalanceTestFunction + * @param {CheckFunctions} checks + * @param {BalanceCheckFunction} discoverFn + * @param {DISCOVER_TYPES} discoverAt + * @param {Object} opts + */ + + /** + * Supports missing address/discoveries at certain points. + * @param {BalanceCheckFunction} [setupFn] + * @param {BalanceCheckFunction} receiveFn + * @param {Number} ahead + * @returns {BalanceTestFunction} + */ + + const balanceTest = (setupFn, receiveFn, ahead) => { + return async (checks, discoverFn, discoverAt, opts = {}) => { + const {wallet, clone} = getNextWallet(); + + if (setupFn) + await setupFn(wallet, clone, ahead, opts); + + await checks.initCheck(wallet, ahead, opts); + + await receiveFn(wallet, clone, ahead, opts); + await checks.sentCheck(wallet, ahead, opts); + + if (discoverAt === BEFORE_CONFIRM) + await discoverFn(wallet, ahead, opts); + + await mineBlocks(1); + await checks.confirmedCheck(wallet, ahead, opts); + + // now unconfirm + if (discoverAt === BEFORE_UNCONFIRM) + await discoverFn(wallet, ahead, opts); + + await wdb.revert(chain.tip.height - 1); + await checks.unconfirmedCheck(wallet, ahead, opts); + + // now erase + if (discoverAt === BEFORE_ERASE) + await discoverFn(wallet, ahead, opts); + + await wallet.zap(-1, 0); + await checks.eraseCheck(wallet, ahead, opts); + + if (discoverAt === BEFORE_BLOCK_CONFIRM) + await discoverFn(wallet, ahead, opts); + + // Final look at full picture. + await wdb.rescan(chain.tip.height - 1); + await checks.blockConfirmCheck(wallet, ahead, opts); + + if (discoverAt === BEFORE_BLOCK_UNCONFIRM) + await discoverFn(wallet, ahead, opts); + + // Unconfirm + await wdb.revert(chain.tip.height - 1); + await checks.blockUnconfirmCheck(wallet, ahead, opts); + + // Clean up wallet. + await wdb.rescan(chain.tip.height - 1); + await checks.blockFinalConfirmCheck(wallet, ahead, opts); + }; + }; + + const BALANCE_CHECK_MAP = { + initCheck: ['initialBalance', 'Initial'], + sentCheck: ['sentBalance', 'Sent'], + confirmedCheck: ['confirmedBalance', 'Confirmed'], + unconfirmedCheck: ['unconfirmedBalance', 'Unconfirmed'], + eraseCheck: ['eraseBalance', 'Erase'], + blockConfirmCheck: ['blockConfirmedBalance', 'Block confirmed'], + blockUnconfirmCheck: ['blockUnconfirmedBalance', 'Block unconfirmed'], + blockFinalConfirmCheck: ['blockFinalConfirmedBalance', 'Block final confirmed'] + }; + + /** + * Check also wallet, default and alt account balances. + * @param {TestBalances} walletBalances + * @param {TestBalances} [defBalances] - default account + * @param {TestBalances} [altBalances] - alt account balances + * @returns {CheckFunctions} + */ + + const checkBalances = (walletBalances, defBalances, altBalances) => { + const checks = {}; + + if (defBalances == null) + defBalances = walletBalances; + + for (const [key, [balanceName, name]] of Object.entries(BALANCE_CHECK_MAP)) { + checks[key] = async (wallet) => { + await assertBalanceEquals( + wallet, + DEFAULT_ACCOUNT, + defBalances[balanceName], + `${name} balance is incorrect in the account ${DEFAULT_ACCOUNT}.` + ); + + await assertRecalcBalanceEquals( + wallet, + DEFAULT_ACCOUNT, + defBalances[balanceName], + `${name} balance is incorrect ` + + `after recalculation in the account ${DEFAULT_ACCOUNT}.` + ); + + if (altBalances != null) { + await assertBalanceEquals( + wallet, + ALT_ACCOUNT, + altBalances[balanceName], + `${name} balance is incorrect in the account ${ALT_ACCOUNT}.` + ); + + await assertRecalcBalanceEquals( + wallet, + ALT_ACCOUNT, + altBalances[balanceName], + `${name} balance is incorrect ` + + `after recalculation in the account ${ALT_ACCOUNT}.` + ); + } + + await assertBalanceEquals( + wallet, + -1, + walletBalances[balanceName], + `${name} balance is incorrect for the wallet.` + ); + + await assertRecalcBalanceEquals( + wallet, + -1, + walletBalances[balanceName], + `${name} balance is incorrect ` + + 'after recalculate for the wallet.' + ); + }; + } + + return checks; + }; + + const combineBalances = (undiscovered, discovered, discoverAt) => { + if (Array.isArray(undiscovered) && Array.isArray(discovered)) { + const combined = []; + + for (let i = 0; i < undiscovered.length; i++) + combined.push(combineBalances(undiscovered[i], discovered[i], discoverAt)); + + return combined; + } + + const balances = { ...undiscovered }; + + if (balances.blockFinalConfirmedBalance == null) + balances.blockFinalConfirmedBalance = balances.blockConfirmedBalance; + + switch (discoverAt) { + case BEFORE_CONFIRM: + balances.confirmedBalance = discovered.confirmedBalance; + case BEFORE_UNCONFIRM: + balances.unconfirmedBalance = discovered.unconfirmedBalance; + case BEFORE_ERASE: + case BEFORE_BLOCK_CONFIRM: + balances.blockConfirmedBalance = discovered.blockConfirmedBalance; + case BEFORE_BLOCK_UNCONFIRM: + balances.blockUnconfirmedBalance = discovered.blockUnconfirmedBalance; + balances.blockFinalConfirmedBalance = discovered.blockConfirmedBalance; + case NONE: + default: + } + + return balances; + }; + + const defDiscover = async (wallet, ahead) => { + await catchUpToAhead(wallet, DEFAULT_ACCOUNT, ahead); + }; + + const altDiscover = async (wallet, ahead) => { + await catchUpToAhead(wallet, ALT_ACCOUNT, ahead); + }; + + const genTests = (options) => { + const { + name, + undiscovered, + discovered, + tester, + discoverer + } = options; + + const genTestBody = (type) => { + // three balances including alt are different. + if (Array.isArray(undiscovered)) { + return async () => { + const balances = combineBalances(undiscovered, discovered, type); + await tester(checkBalances(balances[0], balances[1], balances[2]), discoverer, type); + }; + } + return async () => { + const balances = combineBalances(undiscovered, discovered, type); + await tester(checkBalances(balances), discoverer, type); + }; + }; + + it(`${name} (no discovery)`, genTestBody(NONE)); + it(`${name}, discover on confirm`, genTestBody(BEFORE_CONFIRM)); + it(`${name}, discover on unconfirm`, genTestBody(BEFORE_UNCONFIRM)); + it(`${name}, discover on erase`, genTestBody(BEFORE_ERASE)); + it(`${name}, discover on block confirm`, genTestBody(BEFORE_CONFIRM)); + it(`${name}, discover on block unconfirm`, genTestBody(BEFORE_ERASE)); + }; + + /** + * One to normal address. + * One in the future address + * These functions accumulate: + * fees: 1 + 1 + 1? + * coins: 2 + 1 + 1? + * tx: 1 + 1 + 1? + * + * Missing 1 register. + */ + + const INIT_REGISTERED_BALANCE = applyDelta(INIT_BALANCE, { + tx: 3, + coin: 3, + + // missing second FINAL_PRICE_2 register. + confirmed: -(HARD_FEE * 3) - FINAL_PRICE_2, + unconfirmed: -(HARD_FEE * 3) - FINAL_PRICE_2, + + clocked: FINAL_PRICE_1, + ulocked: FINAL_PRICE_1 + }); + + const setupTwoRegisteredNames = async (wallet, ahead, register = true) => { + const name1 = grindName(GRIND_NAME_LEN, chain.tip.height, network); + const name2 = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const {nextAddr} = getAheadAddr(account, ahead); + + await primary.sendBatch([ + ['OPEN', name1], + ['OPEN', name2] + ]); + await mineBlocks(openingPeriod); + + const txOpts = { hardFee: HARD_FEE }; + + // all three bids are there. + const bidMTX = await wallet.createBatch([ + ['BID', name1, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name2, BID_AMOUNT_2, BLIND_AMOUNT_2] + ], txOpts); + + assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); + assert.strictEqual(bidMTX.outputs[1].covenant.type, types.BID); + + bidMTX.outputs[1].address = nextAddr; + await resign(wallet, bidMTX); + + // make sure clone knows ahead addrs. + await defDiscover(wallet, ahead * 2); + + await node.mempool.addTX(bidMTX.toTX()); + + await mineBlocks(1); + + assert(FINAL_PRICE_1 <= BID_AMOUNT_1); + assert(FINAL_PRICE_2 <= BID_AMOUNT_2); + + // primary will lose + await primary.sendBid(name1, FINAL_PRICE_1, INIT_FUND); + await primary.sendBid(name2, FINAL_PRICE_2, INIT_FUND); + + await mineBlocks(biddingPeriod - 1); + + await primary.sendReveal(name1); + await primary.sendReveal(name2); + + await wallet.sendBatch([ + ['REVEAL', name1], + ['REVEAL', name2] + ], txOpts); + + await mineBlocks(revealPeriod); + + if (register !== false) { + await wallet.sendBatch([ + ['UPDATE', name1, EMPTY_RS], + ['UPDATE', name2, EMPTY_RS] + ], { + hardFee: HARD_FEE + }); + + await mineBlocks(1); + } + + return [name1, name2]; + }; + + describe('NONE -> NONE* (normal receive)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + const receive = async (wallet, clone, ahead) => { + const recvAddr = await wallet.receiveAddress(); + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const {nextAddr} = getAheadAddr(account, ahead); + + // Send one to the normal address + // Send another one to the gapped/missed adress + await primary.send({ + outputs: [{ + address: recvAddr, + value: SEND_AMOUNT + }, { + address: nextAddr, + value: SEND_AMOUNT_2 + }] + }); + }; + + // account.lookahead + AHEAD + const AHEAD = 10; + const testReceive = balanceTest(null, receive, AHEAD); + + // Balances if we did not discover + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = INIT_BALANCE; + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + coin: 1, + unconfirmed: SEND_AMOUNT + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: SEND_AMOUNT + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.sentBalance; + + // Balances if we discovered from the beginning + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: 2, + unconfirmed: SEND_AMOUNT + SEND_AMOUNT_2 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: SEND_AMOUNT + SEND_AMOUNT_2 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.sentBalance; + + genTests({ + name: 'should handle normal receive', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testReceive, + discoverer: defDiscover + }); + }); + + describe('NONE* -> NONE (spend our credits)', function() { + this.timeout(5000); + before(() => { + genWallets = 1; + return beforeAll(); + }); + + after(afterAll); + + let coins, nextAddr, receiveKey; + + const setupTXFromFuture = async (wallet, clone, ahead) => { + const recvAddr = await wallet.receiveAddress(); + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const aheadAddr = getAheadAddr(account, ahead, wallet.master); + nextAddr = aheadAddr.nextAddr; + receiveKey = aheadAddr.receiveKey; + + // Create transaction that creates two coins: + // 1. normal coin + // 2. one gapped/missed coin + const fundTX = await primary.send({ + sort: false, + outputs: [{ + address: recvAddr, + value: SEND_AMOUNT + }, { + address: nextAddr, + value: SEND_AMOUNT + HARD_FEE + }] + }); + + await mineBlocks(1); + + coins = [ + Coin.fromTX(fundTX, 0, chain.tip.height), + Coin.fromTX(fundTX, 1, chain.tip.height) + ]; + }; + + const receive = async (wallet) => { + const outAddr = await primary.receiveAddress(); + const changeAddr = await wallet.changeAddress(); + + // spend both coins in one tx. + const mtx = new MTX(); + + mtx.addOutput(new Output({ + address: outAddr, + value: SEND_AMOUNT * 2 + })); + + // HARD_FEE is paid by gapped/missed coin. + await mtx.fund(coins, { + hardFee: HARD_FEE, + changeAddress: changeAddr + }); + + await wallet.sign(mtx); + await mtx.signAsync(receiveKey); + + node.mempool.addTX(mtx.toTX()); + await forWTX(wallet.id, mtx.hash()); + }; + + const AHEAD = 10; + const test = balanceTest(setupTXFromFuture, receive, AHEAD); + + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = applyDelta(INIT_BALANCE, { + tx: 1, + coin: 1, + confirmed: SEND_AMOUNT, + unconfirmed: SEND_AMOUNT + }); + + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + coin: -1, + unconfirmed: -SEND_AMOUNT + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: -SEND_AMOUNT + }); + + UNDISCOVERED.unconfirmedBalance = applyDelta(UNDISCOVERED.confirmedBalance, { + confirmed: SEND_AMOUNT + }); + + UNDISCOVERED.eraseBalance = applyDelta(UNDISCOVERED.unconfirmedBalance, { + tx: -1, + coin: 1, + unconfirmed: SEND_AMOUNT + }); + + UNDISCOVERED.blockConfirmedBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + coin: -1, + confirmed: -SEND_AMOUNT, + unconfirmed: -SEND_AMOUNT + }); + + UNDISCOVERED.blockUnconfirmedBalance = applyDelta(UNDISCOVERED.blockConfirmedBalance, { + confirmed: SEND_AMOUNT + }); + + UNDISCOVERED.blockFinalConfirmedBalance = UNDISCOVERED.blockConfirmedBalance; + + it('should spend normal credit (no discovery)', async () => { + const balances = UNDISCOVERED; + + await test( + checkBalances(balances), + defDiscover, + DISCOVER_TYPES.NONE + ); + }); + + it.skip('should spend credit, discover before confirm', async () => { + // TODO: Implement with coinview update. + // This will be no different than normal credit spend if + // we don't receive CoinView from the chain. So skip this until we + // have that feature. + }); + + // We don't have any details about inputs, so it's not possible to recover them. + // it('should spend credit, discover before unconfirm', async () => {}); + // it('should spend credit, discover before erase', async () => {}); + + it.skip('should spend credit, discover before block confirm', async () => { + // This will be no different than normal credit spend if + // we don't receive CoinView from the chain. So skip this until we + // have that feature. + }); + + // We don't have any details about inputs, so it's not possible to recover them. + // it('should spend credit, discover on block unconfirm', async () => { }); + }); + + describe('NONE* -> NONE* (receive and spend in pending)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + const receive = async (wallet, clone, ahead) => { + const recvAddr = await wallet.receiveAddress(); + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const aheadAddr = getAheadAddr(account, ahead, wallet.master); + const nextAddr = aheadAddr.nextAddr; + const receiveKey = aheadAddr.receiveKey; + + // Create transaction that creates two coins: + // 1. normal coin + // 2. one gapped/missed coin + const fundTX = await primary.send({ + sort: false, + outputs: [{ + address: recvAddr, + value: SEND_AMOUNT + }, { + address: nextAddr, + value: SEND_AMOUNT + HARD_FEE + }] + }); + + const coins = [ + Coin.fromTX(fundTX, 0, chain.tip.height), + Coin.fromTX(fundTX, 1, chain.tip.height) + ]; + + const outAddr = await primary.receiveAddress(); + const changeAddr = await wallet.changeAddress(); + + // spend both coins in one tx. + const mtx = new MTX(); + + mtx.addOutput(new Output({ + address: outAddr, + value: SEND_AMOUNT * 2 + })); + + // HARD_FEE is paid by gapped/missed coin. + await mtx.fund(coins, { + hardFee: HARD_FEE, + changeAddress: changeAddr + }); + + await wallet.sign(mtx); + await mtx.signAsync(receiveKey); + + node.mempool.addTX(mtx.toTX()); + await forWTX(wallet.id, mtx.hash()); + }; + + const AHEAD = 10; + const test = balanceTest(null, receive, AHEAD); + + // Balances. + const UNDISCOVERED = {}; + + // For this test, the balances are same for all the test cases, + // but for different reasons. + UNDISCOVERED.initialBalance = INIT_BALANCE; + + // We receive 2 transactions (receiving one and spending one) + // But we spend discovered output right away. + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 2, + coin: 0, + unconfirmed: 0 + }); + + // Nothing changes for confirmed either. (Coins are spent in pending) + UNDISCOVERED.confirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.confirmedBalance; + + // We no longer have two txs. + UNDISCOVERED.eraseBalance = applyDelta(UNDISCOVERED.unconfirmedBalance, { tx: -2 }); + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.unconfirmedBalance; + + genTests({ + name: 'should spend credit', + undiscovered: UNDISCOVERED, + discovered: UNDISCOVERED, + tester: test, + discoverer: defDiscover + }); + }); + + describe('NONE -> OPEN', function() { + this.timeout(5000); + before(() => { + genWallets = 1; + return beforeAll(); + }); + + after(afterAll); + + const sendOpen = async (wallet) => { + const name = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + await wallet.sendOpen(name, { + hardFee: HARD_FEE + }); + }; + + const testOpen = balanceTest(null, sendOpen, 0); + + it('should handle open', async () => { + const balances = {}; + balances.initialBalance = INIT_BALANCE; + + // TODO: Should 0 value outs be counted towards coin and stored in coin set? + balances.sentBalance = applyDelta(balances.initialBalance, { + tx: 1, + coin: 1, + unconfirmed: -HARD_FEE + }); + + balances.confirmedBalance = applyDelta(balances.sentBalance, { + confirmed: -HARD_FEE + }); + + balances.unconfirmedBalance = applyDelta(balances.confirmedBalance, { + confirmed: HARD_FEE + }); + + // TODO: Should 0 value outs be counted towards coin and stored in coin set? + balances.eraseBalance = applyDelta(balances.unconfirmedBalance, { + tx: -1, + coin: -1, + unconfirmed: HARD_FEE + }); + + balances.blockConfirmedBalance = balances.confirmedBalance; + balances.blockUnconfirmedBalance = balances.unconfirmedBalance; + balances.blockFinalConfirmedBalance = balances.blockConfirmedBalance; + + await testOpen( + checkBalances(balances), + defDiscover, + DISCOVER_TYPES.NONE + ); + }); + }); + + /* + * Lock balances + */ + + describe('NONE -> BID* (normal receive)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name = null; + const setupBidName = async () => { + name = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + await primary.sendOpen(name, false); + await mineBlocks(openingPeriod); + }; + + const sendNormalBid = async (wallet, clone, ahead) => { + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const {nextAddr} = getAheadAddr(account, ahead); + const txOpts = { hardFee: HARD_FEE }; + + const bidMTX = await wallet.createBatch([ + ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + ], txOpts); + + assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); + assert.strictEqual(bidMTX.outputs[1].covenant.type, types.BID); + bidMTX.outputs[1].address = nextAddr; + + await resign(wallet, bidMTX); + node.mempool.addTX(bidMTX.toTX()); + await forWTX(wallet.id, bidMTX.hash()); + }; + + const AHEAD = 10; + const testBidReceive = balanceTest(setupBidName, sendNormalBid, AHEAD); + + // Balances if second BID was undiscovered. + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = INIT_BALANCE; + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + // We have additional coin because: output -> BID + Change + // Additional BID is undiscovered. + coin: 1, + // Bid we are not aware of is seen as spent. + unconfirmed: -HARD_FEE - BLIND_AMOUNT_2, + ulocked: BLIND_AMOUNT_1 + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: -HARD_FEE - BLIND_AMOUNT_2, + clocked: BLIND_AMOUNT_1 + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.unconfirmedBalance; + + // Balances if second BID was discovered right away. + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: 2, + // Bid we are not aware of is seen as spent. + unconfirmed: -HARD_FEE, + ulocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: -HARD_FEE, + clocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.unconfirmedBalance; + + genTests({ + name: 'should receive bid', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testBidReceive, + discoverer: defDiscover + }); + }); + + describe('NONE -> BID* (foreign bid)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name = null; + const setupBidName = async () => { + name = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + await primary.sendOpen(name, false); + await mineBlocks(openingPeriod); + }; + + const sendForeignBid = async (wallet, clone, ahead) => { + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const recvAddr = await wallet.receiveAddress(); + const {nextAddr} = getAheadAddr(account, ahead); + const txOpts = { hardFee: HARD_FEE }; + + const bidMTX = await primary.createBatch([ + ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + ], txOpts); + + assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); + assert.strictEqual(bidMTX.outputs[1].covenant.type, types.BID); + + bidMTX.outputs[0].address = recvAddr; + bidMTX.outputs[1].address = nextAddr; + + await resign(primary, bidMTX); + node.mempool.addTX(bidMTX.toTX()); + await forWTX(wallet.id, bidMTX.hash()); + }; + + const AHEAD = 10; + const testForeign = balanceTest(setupBidName, sendForeignBid, AHEAD); + + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = INIT_BALANCE; + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + // only BID + coin: 1, + // We did not own this money before + unconfirmed: BLIND_AMOUNT_1, + ulocked: BLIND_AMOUNT_1 + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: BLIND_AMOUNT_1, + clocked: BLIND_AMOUNT_1 + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.sentBalance; + + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: 2, + unconfirmed: BLIND_AMOUNT_1 + BLIND_AMOUNT_2, + ulocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: BLIND_AMOUNT_1 + BLIND_AMOUNT_2, + clocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.sentBalance; + + genTests({ + name: 'should receive foreign bid', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testForeign, + discoverer: defDiscover + }); + }); + + describe('NONE -> BID* (cross acct)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name; + const setupAcctAndBidName = async (wallet) => { + await wallet.createAccount({ + name: ALT_ACCOUNT + }); + + name = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + await primary.sendOpen(name, false); + await mineBlocks(openingPeriod); + }; + + const sendCrossAcct = async (wallet, clone, ahead) => { + const txOpts = { hardFee: HARD_FEE }; + const altAccount = await wallet.getAccount(ALT_ACCOUNT); + + // not actually next, we test normal recive. + const addr1 = getAheadAddr(altAccount, -altAccount.lookahead); + const addr2 = getAheadAddr(altAccount, ahead); + + const bidMTX = await wallet.createBatch([ + ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + ], txOpts); + + assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); + assert.strictEqual(bidMTX.outputs[1].covenant.type, types.BID); + + bidMTX.outputs[0].address = addr1.nextAddr; + // future tx. + bidMTX.outputs[1].address = addr2.nextAddr; + + await resign(wallet, bidMTX); + node.mempool.addTX(bidMTX.toTX()); + await forWTX(wallet.id, bidMTX.hash()); + }; + + const AHEAD = 10; + const testCrossAcctBalance = balanceTest(setupAcctAndBidName, sendCrossAcct, AHEAD); + + const UNDISCOVERED_WALLET = {}; + const UNDISCOVERED_DEFAULT = {}; + const UNDISCOVERED_ALT = {}; + + UNDISCOVERED_WALLET.initialBalance = INIT_BALANCE; + UNDISCOVERED_DEFAULT.initialBalance = INIT_BALANCE; + UNDISCOVERED_ALT.initialBalance = NULL_BALANCE; + + // sent from default to alt, default account does not lock + UNDISCOVERED_DEFAULT.sentBalance = applyDelta(UNDISCOVERED_DEFAULT.initialBalance, { + tx: 1, + // output -> change output + 2 BIDs to alt + coin: 0, + unconfirmed: -HARD_FEE - BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + // alt account balance locks unconfirmed and receives coin. + UNDISCOVERED_ALT.sentBalance = applyDelta(UNDISCOVERED_ALT.initialBalance, { + tx: 1, + // received BID + missed BID. + coin: 1, + unconfirmed: BLIND_AMOUNT_1, + ulocked: BLIND_AMOUNT_1 + }); + + // Wallet only spends FEE + UNDISCOVERED_WALLET.sentBalance = applyDelta(UNDISCOVERED_WALLET.initialBalance, { + tx: 1, + // Total coins is: output -> BID output + CHANGE + Undiscovered BID + coin: 1, + // for now another bid just out transaction. + unconfirmed: -HARD_FEE - BLIND_AMOUNT_2, + ulocked: BLIND_AMOUNT_1 + }); + + // NOW CONFIRM + UNDISCOVERED_DEFAULT.confirmedBalance = applyDelta(UNDISCOVERED_DEFAULT.sentBalance, { + confirmed: -HARD_FEE - BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + UNDISCOVERED_ALT.confirmedBalance = applyDelta(UNDISCOVERED_ALT.sentBalance, { + confirmed: BLIND_AMOUNT_1, + clocked: BLIND_AMOUNT_1 + }); + + UNDISCOVERED_WALLET.confirmedBalance = applyDelta(UNDISCOVERED_WALLET.sentBalance, { + confirmed: -HARD_FEE - BLIND_AMOUNT_2, + clocked: BLIND_AMOUNT_1 + }); + + // NOW Unconfirm again + UNDISCOVERED_DEFAULT.unconfirmedBalance = UNDISCOVERED_DEFAULT.sentBalance; + UNDISCOVERED_ALT.unconfirmedBalance = UNDISCOVERED_ALT.sentBalance; + UNDISCOVERED_WALLET.unconfirmedBalance = UNDISCOVERED_WALLET.sentBalance; + + // NOW Erase + UNDISCOVERED_WALLET.eraseBalance = UNDISCOVERED_WALLET.initialBalance; + UNDISCOVERED_DEFAULT.eraseBalance = UNDISCOVERED_DEFAULT.initialBalance; + UNDISCOVERED_ALT.eraseBalance = UNDISCOVERED_ALT.initialBalance; + + UNDISCOVERED_WALLET.blockConfirmedBalance = UNDISCOVERED_WALLET.confirmedBalance; + UNDISCOVERED_DEFAULT.blockConfirmedBalance = UNDISCOVERED_DEFAULT.confirmedBalance; + UNDISCOVERED_ALT.blockConfirmedBalance = UNDISCOVERED_ALT.confirmedBalance; + + UNDISCOVERED_WALLET.blockUnconfirmedBalance = UNDISCOVERED_WALLET.unconfirmedBalance; + UNDISCOVERED_DEFAULT.blockUnconfirmedBalance = UNDISCOVERED_DEFAULT.unconfirmedBalance; + UNDISCOVERED_ALT.blockUnconfirmedBalance = UNDISCOVERED_ALT.unconfirmedBalance; + + // Now DISCOVERED PART + const DISCOVERED_WALLET = {}; + const DISCOVERED_DEFAULT = {}; + const DISCOVERED_ALT = {}; + + DISCOVERED_WALLET.initialBalance = UNDISCOVERED_WALLET.initialBalance; + DISCOVERED_DEFAULT.initialBalance = UNDISCOVERED_DEFAULT.initialBalance; + DISCOVERED_ALT.initialBalance = UNDISCOVERED_ALT.initialBalance; + + // sent from default to alt, default account does not lock + DISCOVERED_DEFAULT.sentBalance = applyDelta(DISCOVERED_DEFAULT.initialBalance, { + tx: 1, + coin: 0, + unconfirmed: -HARD_FEE - BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + // alt account balance locks unconfirmed and receives coin. + DISCOVERED_ALT.sentBalance = applyDelta(DISCOVERED_ALT.initialBalance, { + tx: 1, + coin: 2, + unconfirmed: BLIND_AMOUNT_1 + BLIND_AMOUNT_2, + ulocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + // Wallet only spends FEE + DISCOVERED_WALLET.sentBalance = applyDelta(DISCOVERED_WALLET.initialBalance, { + tx: 1, + // Total coins is: output -> BID output + BID output + CHANGE + coin: 2, + unconfirmed: -HARD_FEE, + ulocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + // NOW CONFIRM + DISCOVERED_DEFAULT.confirmedBalance = applyDelta(DISCOVERED_DEFAULT.sentBalance, { + confirmed: -HARD_FEE - BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + DISCOVERED_ALT.confirmedBalance = applyDelta(DISCOVERED_ALT.sentBalance, { + confirmed: BLIND_AMOUNT_1 + BLIND_AMOUNT_2, + clocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + DISCOVERED_WALLET.confirmedBalance = applyDelta(DISCOVERED_WALLET.sentBalance, { + confirmed: -HARD_FEE, + clocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + // NOW Unconfirm again + DISCOVERED_DEFAULT.unconfirmedBalance = DISCOVERED_DEFAULT.sentBalance; + DISCOVERED_ALT.unconfirmedBalance = DISCOVERED_ALT.sentBalance; + DISCOVERED_WALLET.unconfirmedBalance = DISCOVERED_WALLET.sentBalance; + + // NOW Erase + DISCOVERED_WALLET.eraseBalance = DISCOVERED_WALLET.initialBalance; + DISCOVERED_DEFAULT.eraseBalance = DISCOVERED_DEFAULT.initialBalance; + DISCOVERED_ALT.eraseBalance = DISCOVERED_ALT.initialBalance; + + DISCOVERED_WALLET.blockConfirmedBalance = DISCOVERED_WALLET.confirmedBalance; + DISCOVERED_DEFAULT.blockConfirmedBalance = DISCOVERED_DEFAULT.confirmedBalance; + DISCOVERED_ALT.blockConfirmedBalance = DISCOVERED_ALT.confirmedBalance; + + DISCOVERED_WALLET.blockUnconfirmedBalance = DISCOVERED_WALLET.unconfirmedBalance; + DISCOVERED_DEFAULT.blockUnconfirmedBalance = DISCOVERED_DEFAULT.unconfirmedBalance; + DISCOVERED_ALT.blockUnconfirmedBalance = DISCOVERED_ALT.unconfirmedBalance; + + genTests({ + name: 'should send/receive bid cross acct', + undiscovered: [UNDISCOVERED_WALLET, UNDISCOVERED_DEFAULT, UNDISCOVERED_ALT], + discovered: [DISCOVERED_WALLET, DISCOVERED_DEFAULT, DISCOVERED_ALT], + tester: testCrossAcctBalance, + discoverer: altDiscover + }); + }); + + describe('BID* -> REVEAL*', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name = null; + + const setupBidName = async (wallet, clone, ahead) => { + name = grindName(GRIND_NAME_LEN, chain.tip.height, network); + const txOpts = { hardFee: HARD_FEE }; + + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const next = getAheadAddr(account, ahead, wallet.master); + const {nextAddr} = next; + + await primary.sendOpen(name, false); + await mineBlocks(openingPeriod); + + const bidMTX = await clone.createBatch([ + ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + ], txOpts); + + assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); + assert.strictEqual(bidMTX.outputs[1].covenant.type, types.BID); + bidMTX.outputs[1].address = nextAddr; + + await resign(clone, bidMTX); + + // Make sure we discover everything + await defDiscover(clone, ahead * 2); + + node.mempool.addTX(bidMTX.toTX()); + await forWTX(wallet.id, bidMTX.hash()); + await mineBlocks(biddingPeriod); + }; + + const sendReveal = async (wallet, clone, ahead) => { + await clone.sendReveal(name, { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testReveal = balanceTest(setupBidName, sendReveal, AHEAD); + + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = applyDelta(INIT_BALANCE, { + tx: 1, + // out = BID + Unknown BID + CHANGE + coin: 1, + + // one bid is unknown + confirmed: -HARD_FEE - BLIND_AMOUNT_2, + unconfirmed: -HARD_FEE - BLIND_AMOUNT_2, + + // one bid is unknown + clocked: BLIND_AMOUNT_1, + ulocked: BLIND_AMOUNT_1 + }); + + // Now we receive REVEAL - which frees BLIND and only locks bid amount. + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + // extra coin from Change + coin: 1, + // We recover BLIND_ONLY from the unknown BID via change. + unconfirmed: BLIND_ONLY_2 - HARD_FEE, + ulocked: -BLIND_ONLY_1 + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: BLIND_ONLY_2 - HARD_FEE, + clocked: -BLIND_ONLY_1 + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.unconfirmedBalance; + + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + + // Now we receive REVEAL - which frees BLIND and only locks bid amount. + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: 2, + unconfirmed: BLIND_AMOUNT_2 - HARD_FEE, + ulocked: BID_AMOUNT_2 - BLIND_ONLY_1 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: BLIND_AMOUNT_2 - HARD_FEE, + clocked: BID_AMOUNT_2 - BLIND_ONLY_1 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.unconfirmedBalance; + + genTests({ + name: 'should send/receive reveal', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testReveal, + discoverer: defDiscover + }); + }); + + describe('BID* -> REVEAL* (cross acct)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name; + + // This will create BID tx in the first account. + // two bids belong to the default account. + const setupRevealName = async (wallet) => { + name = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + await wallet.createAccount({ + name: ALT_ACCOUNT + }); + const txOpts = { hardFee: HARD_FEE }; + + await primary.sendOpen(name, false); + await mineBlocks(openingPeriod); + + await wallet.sendBatch([ + ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + ], txOpts); + await mineBlocks(biddingPeriod); + }; + + // Now we sent two REVEALs to second account (one seen, one missed) + const sendReveal = async (wallet, clone, ahead) => { + const altAccount = await wallet.getAccount(ALT_ACCOUNT); + const recv = getAheadAddr(altAccount, -altAccount.lookahead); + const next = getAheadAddr(altAccount, ahead); + + const revealMTX = await wallet.createReveal(name, { + hardFee: HARD_FEE + }); + assert.strictEqual(revealMTX.outputs[0].covenant.type, types.REVEAL); + assert.strictEqual(revealMTX.outputs[1].covenant.type, types.REVEAL); + revealMTX.outputs[0].address = recv.nextAddr; + revealMTX.outputs[1].address = next.nextAddr; + + await resign(wallet, revealMTX); + node.mempool.addTX(revealMTX.toTX()); + await forWTX(wallet.id, revealMTX.hash()); + }; + + const AHEAD = 10; + const testCrossActReveal = balanceTest(setupRevealName, sendReveal, AHEAD); + + /* + * Balances if we never discovered missing. + */ + + const UNDISCOVERED_WALLET = {}; + const UNDISCOVERED_DEFAULT = {}; + const UNDISCOVERED_ALT = {}; + + // we start with BID transaction + UNDISCOVERED_WALLET.initialBalance = applyDelta(INIT_BALANCE, { + tx: 1, + + // we have two bids at the start. + coin: 2, + + confirmed: -HARD_FEE, + unconfirmed: -HARD_FEE, + + clocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2, + ulocked: BLIND_AMOUNT_1 + BLIND_AMOUNT_2 + }); + + // same as wallet at this stage. + UNDISCOVERED_DEFAULT.initialBalance = UNDISCOVERED_WALLET.initialBalance; + // empty at the start. + UNDISCOVERED_ALT.initialBalance = NULL_BALANCE; + + // After REVEAL Transaction + UNDISCOVERED_WALLET.sentBalance = applyDelta(UNDISCOVERED_WALLET.initialBalance, { + tx: 1, + // extra coin from change. + // but one reveal becomes missed. + coin: 0, + + // We only lose reveal amount, diff is going into our change + unconfirmed: -BID_AMOUNT_2 - HARD_FEE, + // We also unlock missed bid->reveal, + // but totally unlock missed one. + ulocked: -BLIND_ONLY_1 - BLIND_AMOUNT_2 + }); + + // does not change + UNDISCOVERED_DEFAULT.sentBalance = applyDelta(UNDISCOVERED_DEFAULT.initialBalance, { + tx: 1, + // 2 BIDS -> 1 Change + out 2 reveals + coin: -1, + + unconfirmed: -BID_AMOUNT_1 - BID_AMOUNT_2 - HARD_FEE, + ulocked: -BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + UNDISCOVERED_ALT.sentBalance = applyDelta(UNDISCOVERED_ALT.initialBalance, { + tx: 1, + // we received 1 reveal (another is unknown) + coin: 1, + + unconfirmed: BID_AMOUNT_1, + ulocked: BID_AMOUNT_1 + }); + + // Now we confirm everything seen above. + UNDISCOVERED_WALLET.confirmedBalance = applyDelta(UNDISCOVERED_WALLET.sentBalance, { + confirmed: -BID_AMOUNT_2 - HARD_FEE, + clocked: -BLIND_ONLY_1 - BLIND_AMOUNT_2 + }); + + UNDISCOVERED_DEFAULT.confirmedBalance = applyDelta(UNDISCOVERED_DEFAULT.sentBalance, { + confirmed: -BID_AMOUNT_1 - BID_AMOUNT_2 - HARD_FEE, + clocked: -BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + UNDISCOVERED_ALT.confirmedBalance = applyDelta(UNDISCOVERED_ALT.sentBalance, { + confirmed: BID_AMOUNT_1, + clocked: BID_AMOUNT_1 + }); + + UNDISCOVERED_WALLET.unconfirmedBalance = UNDISCOVERED_WALLET.sentBalance; + UNDISCOVERED_DEFAULT.unconfirmedBalance = UNDISCOVERED_DEFAULT.sentBalance; + UNDISCOVERED_ALT.unconfirmedBalance = UNDISCOVERED_ALT.sentBalance; + + // Erase + UNDISCOVERED_WALLET.eraseBalance = UNDISCOVERED_WALLET.initialBalance; + UNDISCOVERED_DEFAULT.eraseBalance = UNDISCOVERED_DEFAULT.initialBalance; + UNDISCOVERED_ALT.eraseBalance = UNDISCOVERED_ALT.initialBalance; + + // Confirm in block + UNDISCOVERED_WALLET.blockConfirmedBalance = UNDISCOVERED_WALLET.confirmedBalance; + UNDISCOVERED_DEFAULT.blockConfirmedBalance = UNDISCOVERED_DEFAULT.confirmedBalance; + UNDISCOVERED_ALT.blockConfirmedBalance = UNDISCOVERED_ALT.confirmedBalance; + + // Unconfirm in block + UNDISCOVERED_WALLET.blockUnconfirmedBalance = UNDISCOVERED_WALLET.unconfirmedBalance; + UNDISCOVERED_DEFAULT.blockUnconfirmedBalance = UNDISCOVERED_DEFAULT.unconfirmedBalance; + UNDISCOVERED_ALT.blockUnconfirmedBalance = UNDISCOVERED_ALT.unconfirmedBalance; + + /* + * Balances if we had discovered it right away. + */ + + const DISCOVERED_WALLET = {}; + const DISCOVERED_DEFAULT = {}; + const DISCOVERED_ALT = {}; + + DISCOVERED_WALLET.initialBalance = UNDISCOVERED_WALLET.initialBalance;; + // same as wallet at this stage. + DISCOVERED_DEFAULT.initialBalance = UNDISCOVERED_DEFAULT.initialBalance; + // empty at the start. + DISCOVERED_ALT.initialBalance = UNDISCOVERED_ALT.initialBalance; + + // After REVEAL Transaction + DISCOVERED_WALLET.sentBalance = applyDelta(DISCOVERED_WALLET.initialBalance, { + tx: 1, + // extra change introduce by reveal tx. + coin: 1, + + unconfirmed: -HARD_FEE, + // unlock blinds, only BID are left locked. + ulocked: -BLIND_ONLY_1 - BLIND_ONLY_2 + }); + + // does not change + DISCOVERED_DEFAULT.sentBalance = applyDelta(DISCOVERED_DEFAULT.initialBalance, { + tx: 1, + // 2 BIDS -> 1 Change + out 2 reveals + coin: -1, + + unconfirmed: -BID_AMOUNT_1 - BID_AMOUNT_2 - HARD_FEE, + ulocked: -BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + DISCOVERED_ALT.sentBalance = applyDelta(DISCOVERED_ALT.initialBalance, { + tx: 1, + // we received 2 reveal + coin: 2, + + unconfirmed: BID_AMOUNT_1 + BID_AMOUNT_2, + ulocked: BID_AMOUNT_1 + BID_AMOUNT_2 + }); + + // Now we confirm everything seen above. + DISCOVERED_WALLET.confirmedBalance = applyDelta(DISCOVERED_WALLET.sentBalance, { + confirmed: -HARD_FEE, + clocked: -BLIND_ONLY_1 - BLIND_ONLY_2 + }); + + DISCOVERED_DEFAULT.confirmedBalance = applyDelta(DISCOVERED_DEFAULT.sentBalance, { + confirmed: -BID_AMOUNT_1 - BID_AMOUNT_2 - HARD_FEE, + clocked: -BLIND_AMOUNT_1 - BLIND_AMOUNT_2 + }); + + DISCOVERED_ALT.confirmedBalance = applyDelta(DISCOVERED_ALT.sentBalance, { + confirmed: BID_AMOUNT_1 + BID_AMOUNT_2, + clocked: BID_AMOUNT_1 + BID_AMOUNT_2 + }); + + DISCOVERED_WALLET.unconfirmedBalance = DISCOVERED_WALLET.sentBalance; + DISCOVERED_DEFAULT.unconfirmedBalance = DISCOVERED_DEFAULT.sentBalance; + DISCOVERED_ALT.unconfirmedBalance = DISCOVERED_ALT.sentBalance; + + // Erase + DISCOVERED_WALLET.eraseBalance = DISCOVERED_WALLET.initialBalance; + DISCOVERED_DEFAULT.eraseBalance = DISCOVERED_DEFAULT.initialBalance; + DISCOVERED_ALT.eraseBalance = DISCOVERED_ALT.initialBalance; + + // Confirm in block + DISCOVERED_WALLET.blockConfirmedBalance = DISCOVERED_WALLET.confirmedBalance; + DISCOVERED_DEFAULT.blockConfirmedBalance = DISCOVERED_DEFAULT.confirmedBalance; + DISCOVERED_ALT.blockConfirmedBalance = DISCOVERED_ALT.confirmedBalance; + + // Unconfirm in block + DISCOVERED_WALLET.blockUnconfirmedBalance = DISCOVERED_WALLET.unconfirmedBalance; + DISCOVERED_DEFAULT.blockUnconfirmedBalance = DISCOVERED_DEFAULT.unconfirmedBalance; + DISCOVERED_ALT.blockUnconfirmedBalance = DISCOVERED_ALT.unconfirmedBalance; + + genTests({ + name: 'should send/receive reveal', + undiscovered: [UNDISCOVERED_WALLET, UNDISCOVERED_DEFAULT, UNDISCOVERED_ALT], + discovered: [DISCOVERED_WALLET, DISCOVERED_DEFAULT, DISCOVERED_ALT], + tester: testCrossActReveal, + discoverer: altDiscover + }); + }); + + describe('BID -> REVEAL* (foreign reveal)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name; + const setupRevealName = async () => { + name = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + await primary.sendOpen(name, false); + await mineBlocks(openingPeriod); + await primary.sendBatch([ + ['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2] + ]); + await mineBlocks(biddingPeriod); + }; + + const sendReveal = async (wallet, clone, ahead) => { + const account = await wallet.getAccount(DEFAULT_ACCOUNT); + const {nextAddr} = getAheadAddr(account, ahead); + const recv = await wallet.receiveAddress(); + + const mtx = await primary.createReveal(name); + assert.strictEqual(mtx.outputs[0].covenant.type, types.REVEAL); + assert.strictEqual(mtx.outputs[1].covenant.type, types.REVEAL); + + mtx.outputs[0].address = recv; + mtx.outputs[1].address = nextAddr; + await resign(primary, mtx); + + node.mempool.addTX(mtx.toTX()); + await forWTX(wallet.id, mtx.hash()); + }; + + const AHEAD = 10; + const testForeignReveal = balanceTest(setupRevealName, sendReveal, AHEAD); + + // balances if missing reveal was not discovered. + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = INIT_BALANCE; + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + coin: 1, + + unconfirmed: BID_AMOUNT_1, + ulocked: BID_AMOUNT_1 + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: BID_AMOUNT_1, + clocked: BID_AMOUNT_1 + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.unconfirmedBalance; + + // Balances if everyting was discovered from the begining. + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: 2, + + unconfirmed: BID_AMOUNT_1 + BID_AMOUNT_2, + ulocked: BID_AMOUNT_1 + BID_AMOUNT_2 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: BID_AMOUNT_1 + BID_AMOUNT_2, + clocked: BID_AMOUNT_1 + BID_AMOUNT_2 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.sentBalance; + + genTests({ + name: 'should send/receive reveal', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testForeignReveal, + discoverer: defDiscover + }); + }); + + describe('REVEAL* -> REDEEM*', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + /* + * TODO: Move this tests to the auction tests. + * - 1 normal -> redeem (loser) + * - 1 missed -> redeem (loser) - bid chain is missing until reve + */ + + let name1, name2; + + const setupRevealNames = async (wallet, clone, ahead) => { + name1 = grindName(GRIND_NAME_LEN, chain.tip.height, network); + name2 = grindName(GRIND_NAME_LEN, chain.tip.height, network); + + const cloneAccount = await clone.getAccount(DEFAULT_ACCOUNT); + const addr1 = getAheadAddr(cloneAccount, ahead); + + await primary.sendBatch([ + ['OPEN', name1], + ['OPEN', name2] + ]); + await mineBlocks(openingPeriod); + + // primary will win + await primary.sendBid(name1, INIT_FUND, INIT_FUND); + await primary.sendBid(name2, INIT_FUND, INIT_FUND); + + const txOpts = { hardFee: HARD_FEE }; + + // all three bids are there. + const bidMTX = await clone.createBatch([ + ['BID', name1, BID_AMOUNT_1, BLIND_AMOUNT_1], + ['BID', name2, BID_AMOUNT_2, BLIND_AMOUNT_2] + ], txOpts); + + assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID); + assert.strictEqual(bidMTX.outputs[1].covenant.type, types.BID); + + bidMTX.outputs[1].address = addr1.nextAddr; + await resign(clone, bidMTX); + + // make sure clone knows ahead addrs. + await defDiscover(clone, ahead * 2); + + await node.mempool.addTX(bidMTX.toTX()); + await mineBlocks(biddingPeriod); + + await primary.sendReveal(name1); + await primary.sendReveal(name2); + + await clone.sendBatch([ + ['REVEAL', name1], + ['REVEAL', name2] + ], txOpts); + + await mineBlocks(revealPeriod + 1); + }; + + const sendRedeems = async (wallet, clone, ahead) => { + await clone.sendBatch([ + ['REDEEM', name1], + ['REDEEM', name2] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testRevealRedeems = balanceTest(setupRevealNames, sendRedeems, AHEAD); + + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = applyDelta(INIT_BALANCE, { + // 1 for bid, 1 for reveal. + tx: 2, + + // (1 coin -> 1 change = 0) + 1 change + 1 reveal + coin: 2, + + // Does not know about 1 reveal, so it is an out. + confirmed: -(HARD_FEE * 2) - BID_AMOUNT_2, + unconfirmed: -(HARD_FEE * 2) - BID_AMOUNT_2, + + // only aware of single reveal. + clocked: BID_AMOUNT_1, + ulocked: BID_AMOUNT_1 + }); + + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + // redeem tx + tx: 1, + + coin: 0, + + unconfirmed: -HARD_FEE, + ulocked: -BID_AMOUNT_1 + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: -HARD_FEE, + clocked: -BID_AMOUNT_1 + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.unconfirmedBalance; + + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: 1, + unconfirmed: -HARD_FEE + BID_AMOUNT_2, + ulocked: -BID_AMOUNT_1 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: -HARD_FEE + BID_AMOUNT_2, + clocked: -BID_AMOUNT_1 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.unconfirmedBalance; + + genTests({ + name: 'should send/receive reveal->redeem', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testRevealRedeems, + discoverer: defDiscover + }); + }); + + describe('REVEAL* -> REGISTER*', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + /* + * TODO: Move this tests to the auction tests. + * - 1 normal -> register (WINNER) + * - 1 missed -> register (WINNER) - bid chain is missing until reve + */ + + let name1, name2; + + const setupRevealNames = async (wallet, clone, ahead) => { + const names = await setupTwoRegisteredNames(clone, ahead, false); + + name1 = names[0]; + name2 = names[1]; + }; + + const sendRedeems = async (wallet, clone, ahead) => { + await clone.sendBatch([ + ['UPDATE', name1, EMPTY_RS], + ['UPDATE', name2, EMPTY_RS] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testRevealRedeems = balanceTest(setupRevealNames, sendRedeems, AHEAD); + + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = applyDelta(INIT_BALANCE, { + // 1 for bid, 1 for reveal. + tx: 2, + + // (1 coin -> 1 change = 0) + 1 change + 1 reveal + coin: 2, + + // Does not know about 1 reveal, so it is an out. + confirmed: -(HARD_FEE * 2) - BID_AMOUNT_2, + unconfirmed: -(HARD_FEE * 2) - BID_AMOUNT_2, + + // only aware of single reveal. + clocked: BID_AMOUNT_1, + ulocked: BID_AMOUNT_1 + }); + + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + // register tx + tx: 1, + // additional change for REGISTER tx. + coin: 1, + + // BID_AMOUNT_2 was returned via change + // only finalPrice2 is not accounted for. + unconfirmed: -HARD_FEE + BID_AMOUNT_2 - FINAL_PRICE_2, + ulocked: -BID_AMOUNT_1 + FINAL_PRICE_1 + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: -HARD_FEE + BID_AMOUNT_2 - FINAL_PRICE_2, + clocked: -BID_AMOUNT_1 + FINAL_PRICE_1 + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.unconfirmedBalance; + + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: 2, + unconfirmed: -HARD_FEE + BID_AMOUNT_2, + ulocked: -BID_AMOUNT_1 + FINAL_PRICE_1 + FINAL_PRICE_2 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: -HARD_FEE + BID_AMOUNT_2, + clocked: -BID_AMOUNT_1 + FINAL_PRICE_1 + FINAL_PRICE_2 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.unconfirmedBalance; + + genTests({ + name: 'should send/receive reveal->register', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testRevealRedeems, + discoverer: defDiscover + }); + }); + + /* + * All updates types have the same accounting outcomes + */ + + const UPDATE_UNDISCOVERED = {}; + UPDATE_UNDISCOVERED.initialBalance = INIT_REGISTERED_BALANCE; + UPDATE_UNDISCOVERED.sentBalance = applyDelta(UPDATE_UNDISCOVERED.initialBalance, { + tx: 1, + unconfirmed: -HARD_FEE + }); + + UPDATE_UNDISCOVERED.confirmedBalance = applyDelta(UPDATE_UNDISCOVERED.sentBalance, { + confirmed: -HARD_FEE + }); + + UPDATE_UNDISCOVERED.unconfirmedBalance = UPDATE_UNDISCOVERED.sentBalance; + UPDATE_UNDISCOVERED.eraseBalance = UPDATE_UNDISCOVERED.initialBalance; + UPDATE_UNDISCOVERED.blockConfirmedBalance = UPDATE_UNDISCOVERED.confirmedBalance; + UPDATE_UNDISCOVERED.blockUnconfirmedBalance = UPDATE_UNDISCOVERED.unconfirmedBalance; + + const UPDATE_DISCOVERED = {}; + UPDATE_DISCOVERED.initialBalance = UPDATE_UNDISCOVERED.initialBalance; + + UPDATE_DISCOVERED.sentBalance = applyDelta(UPDATE_DISCOVERED.initialBalance, { + tx: 1, + // discovers the unknown update + coin: 1, + + unconfirmed: -HARD_FEE + FINAL_PRICE_2, + ulocked: FINAL_PRICE_2 + }); + + UPDATE_DISCOVERED.confirmedBalance = applyDelta(UPDATE_DISCOVERED.sentBalance, { + confirmed: -HARD_FEE + FINAL_PRICE_2, + clocked: FINAL_PRICE_2 + }); + + UPDATE_DISCOVERED.unconfirmedBalance = UPDATE_DISCOVERED.sentBalance; + UPDATE_DISCOVERED.eraseBalance = UPDATE_DISCOVERED.initialBalance; + UPDATE_DISCOVERED.blockConfirmedBalance = UPDATE_DISCOVERED.confirmedBalance; + UPDATE_DISCOVERED.blockUnconfirmedBalance = UPDATE_DISCOVERED.unconfirmedBalance; + + describe('REGISTER* -> UPDATE*', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name1, name2; + + const setupRegisteredNames = async (wallet, clone, ahead) => { + const names = await setupTwoRegisteredNames(clone, ahead); + + name1 = names[0]; + name2 = names[1]; + }; + + const sendUpdates = async (wallet, clone) => { + await clone.sendBatch([ + ['UPDATE', name1, EMPTY_RS], + ['UPDATE', name2, EMPTY_RS] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testSendUpdate = balanceTest(setupRegisteredNames, sendUpdates, AHEAD); + + genTests({ + name: 'should send/receive register->update', + undiscovered: UPDATE_UNDISCOVERED, + discovered: UPDATE_DISCOVERED, + tester: testSendUpdate, + discoverer: defDiscover + }); + }); + + // NOTE: Revokes are permanently burned coins, should we discount them from + // balance and UTXO set? (moved to burned balance) + describe('REGISTER/UPDATE* -> REVOKE*', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name1, name2; + + const setupRegisteredNames = async (wallet, clone, ahead) => { + const names = await setupTwoRegisteredNames(clone, ahead); + + name1 = names[0]; + name2 = names[1]; + }; + + const sendRevokes = async (wallet, clone) => { + await clone.sendBatch([ + ['REVOKE', name1], + ['REVOKE', name2] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testSendRevokes = balanceTest(setupRegisteredNames, sendRevokes, AHEAD); + + genTests({ + name: 'should send/receive register->revoke', + undiscovered: UPDATE_UNDISCOVERED, + discovered: UPDATE_DISCOVERED, + tester: testSendRevokes, + discoverer: defDiscover + }); + }); + + describe('REGISTER/UPDATE* -> RENEW*', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name1, name2; + + const setupRegisteredNames = async (wallet, clone, ahead) => { + const names = await setupTwoRegisteredNames(clone, ahead); + + name1 = names[0]; + name2 = names[1]; + }; + + const sendRenews = async (wallet, clone) => { + await mineBlocks(treeInterval); + await clone.sendBatch([ + ['RENEW', name1], + ['RENEW', name2] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testSendRenews = balanceTest(setupRegisteredNames, sendRenews, AHEAD); + + genTests({ + name: 'should send/receive register->renew', + undiscovered: UPDATE_UNDISCOVERED, + discovered: UPDATE_DISCOVERED, + tester: testSendRenews, + discoverer: defDiscover + }); + }); + + describe('REGISTER/UPDATE* -> TRANSFER*', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name1, name2; + + const setupRegisteredNames = async (wallet, clone, ahead) => { + const names = await setupTwoRegisteredNames(clone, ahead); + + name1 = names[0]; + name2 = names[1]; + }; + + const sendTransfers = async (wallet, clone) => { + await clone.sendBatch([ + ['TRANSFER', name1, await primary.receiveAddress()], + ['TRANSFER', name2, await primary.receiveAddress()] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testSendTransfer = balanceTest(setupRegisteredNames, sendTransfers, AHEAD); + + genTests({ + name: 'should send/receive register->renew', + undiscovered: UPDATE_UNDISCOVERED, + discovered: UPDATE_DISCOVERED, + tester: testSendTransfer, + discoverer: defDiscover + }); + }); + + describe('TRANSFER* -> FINALIZE', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name1, name2; + + const setupTransferNames = async (wallet, clone, ahead) => { + const names = await setupTwoRegisteredNames(clone, ahead); + + name1 = names[0]; + name2 = names[1]; + + await clone.sendBatch([ + ['TRANSFER', name1, await primary.receiveAddress()], + ['TRANSFER', name2, await primary.receiveAddress()] + ], { + hardFee: HARD_FEE + }); + + await mineBlocks(transferLockup); + }; + + const sendFinalizes = async (wallet, clone) => { + await clone.sendBatch([ + ['FINALIZE', name1], + ['FINALIZE', name2] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testSendFinalizes = balanceTest(setupTransferNames, sendFinalizes, AHEAD); + + const UNDISCOVERED = {}; + UNDISCOVERED.initialBalance = applyDelta(INIT_REGISTERED_BALANCE, { + // we sent TRANSFER + tx: 1, + confirmed: -HARD_FEE, + unconfirmed: -HARD_FEE + }); + + UNDISCOVERED.sentBalance = applyDelta(UNDISCOVERED.initialBalance, { + tx: 1, + coin: -1, + unconfirmed: -FINAL_PRICE_1 - HARD_FEE, + ulocked: -FINAL_PRICE_1 + }); + + UNDISCOVERED.confirmedBalance = applyDelta(UNDISCOVERED.sentBalance, { + confirmed: -FINAL_PRICE_1 - HARD_FEE, + clocked: -FINAL_PRICE_1 + }); + + UNDISCOVERED.unconfirmedBalance = UNDISCOVERED.sentBalance; + UNDISCOVERED.eraseBalance = UNDISCOVERED.initialBalance; + UNDISCOVERED.blockConfirmedBalance = UNDISCOVERED.confirmedBalance; + UNDISCOVERED.blockUnconfirmedBalance = UNDISCOVERED.unconfirmedBalance; + + const DISCOVERED = {}; + DISCOVERED.initialBalance = UNDISCOVERED.initialBalance; + DISCOVERED.sentBalance = applyDelta(DISCOVERED.initialBalance, { + tx: 1, + coin: -1, + + // Because we only discover when it's outgoing, it wont affect our balance. + unconfirmed: -FINAL_PRICE_1 - HARD_FEE, + ulocked: -FINAL_PRICE_1 + }); + + DISCOVERED.confirmedBalance = applyDelta(DISCOVERED.sentBalance, { + confirmed: -FINAL_PRICE_1 - HARD_FEE, + clocked: -FINAL_PRICE_1 + }); + + DISCOVERED.unconfirmedBalance = DISCOVERED.sentBalance; + DISCOVERED.eraseBalance = DISCOVERED.initialBalance; + DISCOVERED.blockConfirmedBalance = DISCOVERED.confirmedBalance; + DISCOVERED.blockUnconfirmedBalance = DISCOVERED.unconfirmedBalance; + + genTests({ + name: 'should send finalize', + undiscovered: UNDISCOVERED, + discovered: DISCOVERED, + tester: testSendFinalizes, + discoverer: defDiscover + }); + }); + + describe('TRANSFER* -> FINALIZE* (cross acct)', function() { + this.timeout(5000); + before(() => { + genWallets = 6; + return beforeAll(); + }); + + after(afterAll); + + let name1, name2; + + const setupTransferNames = async (wallet, clone, ahead) => { + await wallet.createAccount({ + name: ALT_ACCOUNT + }); + + const altAccount = await wallet.getAccount(ALT_ACCOUNT); + const recv = await wallet.receiveAddress(ALT_ACCOUNT); + const {nextAddr} = getAheadAddr(altAccount, ahead); + + const names = await setupTwoRegisteredNames(clone, ahead); + + name1 = names[0]; + name2 = names[1]; + + await clone.sendBatch([ + ['TRANSFER', name1, recv], + ['TRANSFER', name2, nextAddr] + ], { + hardFee: HARD_FEE + }); + + await mineBlocks(transferLockup); + }; + + const sendFinalizes = async (wallet, clone) => { + await clone.sendBatch([ + ['FINALIZE', name1], + ['FINALIZE', name2] + ], { + hardFee: HARD_FEE + }); + }; + + const AHEAD = 10; + const testSendFinalizes = balanceTest(setupTransferNames, sendFinalizes, AHEAD); + + const UNDISCOVERED_WALLET = {}; + const UNDISCOVERED_DEFAULT = {}; + const UNDISCOVERED_ALT = {}; + + UNDISCOVERED_WALLET.initialBalance = applyDelta(INIT_REGISTERED_BALANCE, { + // we sent TRANSFER + tx: 1, + confirmed: -HARD_FEE, + unconfirmed: -HARD_FEE + }); + + UNDISCOVERED_DEFAULT.initialBalance = UNDISCOVERED_WALLET.initialBalance; + UNDISCOVERED_ALT.initialBalance = NULL_BALANCE; + + UNDISCOVERED_WALLET.sentBalance = applyDelta(UNDISCOVERED_WALLET.initialBalance, { + tx: 1, + // default sent to alt. + coin: 0, + + unconfirmed: -HARD_FEE + }); + + UNDISCOVERED_DEFAULT.sentBalance = applyDelta(UNDISCOVERED_DEFAULT.initialBalance, { + tx: 1, + coin: -1, + unconfirmed: -FINAL_PRICE_1 - HARD_FEE, + ulocked: -FINAL_PRICE_1 + }); + + UNDISCOVERED_ALT.sentBalance = applyDelta(UNDISCOVERED_ALT.initialBalance, { + tx: 1, + coin: 1, + + unconfirmed: FINAL_PRICE_1, + ulocked: FINAL_PRICE_1 + }); + + UNDISCOVERED_WALLET.confirmedBalance = applyDelta(UNDISCOVERED_WALLET.sentBalance, { + confirmed: -HARD_FEE + }); + + UNDISCOVERED_DEFAULT.confirmedBalance = applyDelta(UNDISCOVERED_DEFAULT.sentBalance, { + confirmed: -FINAL_PRICE_1 - HARD_FEE, + clocked: -FINAL_PRICE_1 + }); + + UNDISCOVERED_ALT.confirmedBalance = applyDelta(UNDISCOVERED_ALT.sentBalance, { + confirmed: FINAL_PRICE_1, + clocked: FINAL_PRICE_1 + }); + + UNDISCOVERED_WALLET.unconfirmedBalance = UNDISCOVERED_WALLET.sentBalance; + UNDISCOVERED_WALLET.eraseBalance = UNDISCOVERED_WALLET.initialBalance; + UNDISCOVERED_WALLET.blockConfirmedBalance = UNDISCOVERED_WALLET.confirmedBalance; + UNDISCOVERED_WALLET.blockUnconfirmedBalance = UNDISCOVERED_WALLET.unconfirmedBalance; + + UNDISCOVERED_DEFAULT.unconfirmedBalance = UNDISCOVERED_DEFAULT.sentBalance; + UNDISCOVERED_DEFAULT.eraseBalance = UNDISCOVERED_DEFAULT.initialBalance; + UNDISCOVERED_DEFAULT.blockConfirmedBalance = UNDISCOVERED_DEFAULT.confirmedBalance; + UNDISCOVERED_DEFAULT.blockUnconfirmedBalance = UNDISCOVERED_DEFAULT.unconfirmedBalance; + + UNDISCOVERED_ALT.unconfirmedBalance = UNDISCOVERED_ALT.sentBalance; + UNDISCOVERED_ALT.eraseBalance = UNDISCOVERED_ALT.initialBalance; + UNDISCOVERED_ALT.blockConfirmedBalance = UNDISCOVERED_ALT.confirmedBalance; + UNDISCOVERED_ALT.blockUnconfirmedBalance = UNDISCOVERED_ALT.unconfirmedBalance; + + const DISCOVERED_WALLET = {}; + const DISCOVERED_DEFAULT = {}; + const DISCOVERED_ALT = {}; + + DISCOVERED_WALLET.initialBalance = UNDISCOVERED_WALLET.initialBalance; + DISCOVERED_DEFAULT.initialBalance = UNDISCOVERED_DEFAULT.initialBalance; + DISCOVERED_ALT.initialBalance = UNDISCOVERED_ALT.initialBalance; + + DISCOVERED_WALLET.sentBalance = applyDelta(DISCOVERED_WALLET.initialBalance, { + tx: 1, + // we discover receiving finalize + coin: 1, + + unconfirmed: -HARD_FEE + FINAL_PRICE_2, + ulocked: FINAL_PRICE_2 + }); + + DISCOVERED_DEFAULT.sentBalance = applyDelta(DISCOVERED_DEFAULT.initialBalance, { + tx: 1, + coin: -1, + + unconfirmed: -FINAL_PRICE_1 - HARD_FEE, + ulocked: -FINAL_PRICE_1 + }); + + DISCOVERED_ALT.sentBalance = applyDelta(DISCOVERED_ALT.initialBalance, { + tx: 1, + coin: 2, + + unconfirmed: FINAL_PRICE_1 + FINAL_PRICE_2, + ulocked: FINAL_PRICE_1 + FINAL_PRICE_2 + }); + + DISCOVERED_WALLET.confirmedBalance = applyDelta(DISCOVERED_WALLET.sentBalance, { + confirmed: -HARD_FEE + FINAL_PRICE_2, + clocked: FINAL_PRICE_2 + }); + + DISCOVERED_DEFAULT.confirmedBalance = applyDelta(DISCOVERED_DEFAULT.sentBalance, { + confirmed: -FINAL_PRICE_1 - HARD_FEE, + clocked: -FINAL_PRICE_1 + }); + + DISCOVERED_ALT.confirmedBalance = applyDelta(DISCOVERED_ALT.sentBalance, { + confirmed: FINAL_PRICE_1 + FINAL_PRICE_2, + clocked: FINAL_PRICE_1 + FINAL_PRICE_2 + }); + + DISCOVERED_WALLET.unconfirmedBalance = DISCOVERED_WALLET.sentBalance; + DISCOVERED_WALLET.eraseBalance = DISCOVERED_WALLET.initialBalance; + DISCOVERED_WALLET.blockConfirmedBalance = DISCOVERED_WALLET.confirmedBalance; + DISCOVERED_WALLET.blockUnconfirmedBalance = DISCOVERED_WALLET.unconfirmedBalance; + + DISCOVERED_DEFAULT.unconfirmedBalance = DISCOVERED_DEFAULT.sentBalance; + DISCOVERED_DEFAULT.eraseBalance = DISCOVERED_DEFAULT.initialBalance; + DISCOVERED_DEFAULT.blockConfirmedBalance = DISCOVERED_DEFAULT.confirmedBalance; + DISCOVERED_DEFAULT.blockUnconfirmedBalance = DISCOVERED_DEFAULT.unconfirmedBalance; + + DISCOVERED_ALT.unconfirmedBalance = DISCOVERED_ALT.sentBalance; + DISCOVERED_ALT.eraseBalance = DISCOVERED_ALT.initialBalance; + DISCOVERED_ALT.blockConfirmedBalance = DISCOVERED_ALT.confirmedBalance; + DISCOVERED_ALT.blockUnconfirmedBalance = DISCOVERED_ALT.unconfirmedBalance; + + genTests({ + name: 'should send finalize (cross acct)', + undiscovered: [UNDISCOVERED_WALLET, UNDISCOVERED_DEFAULT, UNDISCOVERED_ALT], + discovered: [DISCOVERED_WALLET, DISCOVERED_DEFAULT, DISCOVERED_ALT], + tester: testSendFinalizes, + discoverer: altDiscover + }); + }); +}); diff --git a/test/wallet-chainstate-test.js b/test/wallet-chainstate-test.js new file mode 100644 index 000000000..3adfe8f51 --- /dev/null +++ b/test/wallet-chainstate-test.js @@ -0,0 +1,567 @@ +'use strict'; + +const assert = require('bsert'); +const consensus = require('../lib/protocol/consensus'); +const Network = require('../lib/protocol/network'); +const MTX = require('../lib/primitives/mtx'); +const WorkerPool = require('../lib/workers/workerpool'); +const WalletDB = require('../lib/wallet/walletdb'); +const wutils = require('./util/wallet'); +const { + dummyInput, + nextEntry +} = wutils; + +const enabled = true; +const size = 2; +const network = Network.get('main'); + +describe('WalletDB ChainState', function() { + /** @type {WorkerPool} */ + let workers = null; + /** @type {WalletDB} */ + let wdb = null; + + const progressWithTX = async (wdb) => { + const addr = await wdb.primary.receiveAddress(); + const mtx = new MTX(); + mtx.addInput(dummyInput()); + mtx.addOutput(addr, 10000); + + const block = nextEntry(wdb); + const txs = [mtx.toTX()]; + await wdb.addBlock(block, txs); + + return {block, txs}; + }; + + const progressWithNoTX = async (wdb) => { + const block = nextEntry(wdb); + const txs = []; + + await wdb.addBlock(block, txs); + return {block, txs}; + }; + + beforeEach(async () => { + workers = new WorkerPool({ enabled, size }); + wdb = new WalletDB({ workers, network }); + await workers.open(); + await wdb.open(); + }); + + afterEach(async () => { + await wdb.close(); + await workers.close(); + }); + + it('should have initial state', () => { + assert.strictEqual(wdb.state.startHeight, 0); + assert.bufferEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, 0); + assert.strictEqual(wdb.state.height, 0); + assert.strictEqual(wdb.state.marked, false); + }); + + it('should progress height but not startHeight w/o txs', async () => { + const blocks = 10; + + for (let i = 0; i < blocks; i++) { + await progressWithNoTX(wdb); + assert.strictEqual(wdb.state.startHeight, 0); + assert.bufferEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, i + 1); + assert.strictEqual(wdb.state.height, i + 1); + assert.strictEqual(wdb.state.marked, false); + } + + assert.strictEqual(wdb.state.startHeight, 0); + assert.bufferEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, blocks); + assert.strictEqual(wdb.state.height, blocks); + assert.strictEqual(wdb.state.marked, false); + }); + + it('should change startHeight when receiveing txs', async () => { + const beforeBlocks = 10; + const blocks = 10; + + for (let i = 0; i < beforeBlocks; i++) { + await progressWithNoTX(wdb); + assert.strictEqual(wdb.state.startHeight, 0); + assert.bufferEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, i + 1); + assert.strictEqual(wdb.state.height, i + 1); + assert.strictEqual(wdb.state.marked, false); + } + + let firstBlock = null; + for (let i = 0; i < blocks; i++) { + const {block} = await progressWithTX(wdb); + + if (!firstBlock) + firstBlock = block; + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, beforeBlocks + i + 1); + assert.strictEqual(wdb.state.height, beforeBlocks + i + 1); + assert.strictEqual(wdb.state.marked, true); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, beforeBlocks + blocks); + assert.strictEqual(wdb.state.height, beforeBlocks + blocks); + assert.strictEqual(wdb.state.marked, true); + }); + + it('should not change startHeight once marked w/o txs', async () => { + const noTXBlocks1 = 5; + const txBlocks1 = 5; + const noTXBlocks2 = 5; + const txBlocks2 = 5; + + let height = 0; + let firstBlock = null; + + for (let i = 0; i < noTXBlocks1; i++) { + await progressWithNoTX(wdb); + height++; + + assert.strictEqual(wdb.state.startHeight, 0); + assert.bufferEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, height); + assert.strictEqual(wdb.state.height, height); + assert.strictEqual(wdb.state.marked, false); + } + + for (let i = 0; i < txBlocks1; i++) { + const {block} = await progressWithTX(wdb); + height++; + + if (!firstBlock) + firstBlock = block; + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, height); + assert.strictEqual(wdb.state.height, height); + assert.strictEqual(wdb.state.marked, true); + } + + for (let i = 0; i < noTXBlocks2; i++) { + await progressWithNoTX(wdb); + height++; + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, height); + assert.strictEqual(wdb.state.height, height); + assert.strictEqual(wdb.state.marked, true); + } + + for (let i = 0; i < txBlocks2; i++) { + await progressWithTX(wdb); + height++; + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, height); + assert.strictEqual(wdb.state.height, height); + assert.strictEqual(wdb.state.marked, true); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBlocks1 + noTXBlocks1 + txBlocks1 + txBlocks2); + assert.strictEqual(wdb.state.height, noTXBlocks1 + noTXBlocks1 + txBlocks1 + txBlocks2); + assert.strictEqual(wdb.state.marked, true); + }); + + it('should not change startHeight once marked on reorg (future reorgs)', async () => { + const noTXBuffer = 10; + const blocksPerAction = 5; + let firstBlock = null; + + for (let i = 0; i < noTXBuffer; i++) + await progressWithNoTX(wdb); + + for (let i = 0; i < blocksPerAction; i++) { + const {block} = await progressWithTX(wdb); + + if (!firstBlock) + firstBlock = block; + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.marked, true); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.marked, true); + + const removeBlocks = []; + // first 5 blocks with no txs. before reorg. + for (let i = 0; i < blocksPerAction; i++) { + const {block} = await progressWithNoTX(wdb); + removeBlocks.push(block); + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.marked, true); + } + + // Disconnect all the stuff. + for (let i = 0; i < blocksPerAction; i++) { + await wdb.removeBlock(removeBlocks.pop()); + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction * 2 - i - 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction * 2 - i - 1); + assert.strictEqual(wdb.state.marked, true); + } + + assert.strictEqual(removeBlocks.length, 0); + + // Reconnect with txs. + for (let i = 0; i < blocksPerAction; i++) { + const {block} = await progressWithTX(wdb); + removeBlocks.push(block); + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.marked, true); + } + + // Disconnect all the stuff again. + for (let i = 0; i < blocksPerAction; i++) { + await wdb.removeBlock(removeBlocks.pop()); + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction * 2 - i - 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction * 2 - i - 1); + assert.strictEqual(wdb.state.marked, true); + } + }); + + it('should should not change start height if reorg recovers txs at same height', async () => { + const noTXBuffer = 10; + const blocksPerAction = 5; + let firstBlock = null; + const removeBlocks = []; + + for (let i = 0; i < noTXBuffer; i++) + await progressWithNoTX(wdb); + + for (let i = 0; i < blocksPerAction; i++) { + const blockAndTXs = await progressWithNoTX(wdb); + removeBlocks.push(blockAndTXs); + } + + assert.strictEqual(wdb.state.startHeight, 0); + assert.bufferEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.marked, false); + + for (let i = 0; i < blocksPerAction; i++) { + const blockAndTXs = await progressWithTX(wdb); + removeBlocks.push(blockAndTXs); + + if (!firstBlock) + firstBlock = blockAndTXs.block; + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.marked, true); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.marked, true); + + const connectList = removeBlocks.slice(); + + for (let i = 0; i < blocksPerAction - 1; i++) { + const {block} = removeBlocks.pop(); + await wdb.removeBlock(block); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.bufferEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction + 1); + assert.strictEqual(wdb.state.marked, true); + + // Remove last block after which chain state becomes unmarked. + { + const {block} = removeBlocks.pop(); + await wdb.removeBlock(block); + const tip = await wdb.getTip(); + + // this block is no longer ours, so it gets unmarked + assert.strictEqual(wdb.state.startHeight, tip.height); + assert.bufferEqual(wdb.state.startHash, tip.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.marked, false); + } + + for (let i = 0; i < blocksPerAction; i++) { + const {block} = removeBlocks.pop(); + await wdb.removeBlock(block); + const tip = await wdb.getTip(); + + assert.strictEqual(wdb.state.startHeight, tip.height); + assert.bufferEqual(wdb.state.startHash, tip.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction - i - 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction - i - 1); + assert.strictEqual(wdb.state.marked, false); + } + + const tip = await wdb.getTip(); + assert.strictEqual(wdb.state.startHeight, tip.height); + assert.bufferEqual(wdb.state.startHash, tip.hash); + assert.strictEqual(wdb.height, noTXBuffer); + assert.strictEqual(wdb.state.height, noTXBuffer); + assert.strictEqual(wdb.state.marked, false); + + // Re add all the blocks. + let marked = false; + firstBlock = null; + + // Marked check only runs when there are transactions, + // so startHeight and startHash will be left behind until + // we find first block with txs. + const checkEntry = { + hash: tip.hash, + height: tip.height + }; + + for (const [i, {block, txs}] of connectList.entries()) { + await wdb.addBlock(block, txs); + + if (!firstBlock && txs.length > 0) { + firstBlock = block; + marked = true; + } + + // First block marks and changes startHash, startHeight + if (firstBlock) { + checkEntry.hash = firstBlock.hash; + checkEntry.height = firstBlock.height; + } + + assert.strictEqual(wdb.state.startHeight, checkEntry.height); + assert.bufferEqual(wdb.state.startHash, checkEntry.hash); + assert.strictEqual(wdb.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.marked, marked); + } + }); + + it('should change mark and startHeight on reorg to earlier', async () => { + const noTXBuffer = 10; + const blocksPerAction = 5; + let firstBlock = null; + const removeBlocks = []; + + for (let i = 0; i < noTXBuffer; i++) + await progressWithNoTX(wdb); + + for (let i = 0; i < blocksPerAction; i++) + removeBlocks.push(await progressWithNoTX(wdb)); + + for (let i = 0; i < blocksPerAction; i++) { + const blockAndTXs = await progressWithTX(wdb); + if (!firstBlock) + firstBlock = blockAndTXs.block; + removeBlocks.push(blockAndTXs); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.strictEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.marked, true); + + // revert all + for (const {block} of removeBlocks.reverse()) + await wdb.removeBlock(block); + + const tip = await wdb.getTip(); + assert.strictEqual(wdb.state.startHeight, tip.height); + assert.strictEqual(wdb.state.startHash, tip.hash); + assert.strictEqual(wdb.height, noTXBuffer); + assert.strictEqual(wdb.state.height, noTXBuffer); + assert.strictEqual(wdb.state.marked, false); + + // create new chain but all with txs. + firstBlock = null; + + for (let i = 0; i < blocksPerAction; i++) { + const blockAndTXs = await progressWithTX(wdb); + + if (!firstBlock) + firstBlock = blockAndTXs.block; + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.strictEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.marked, true); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.strictEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.marked, true); + }); + + it('should change mark and startHeight on reorg but later', async () => { + const noTXBuffer = 10; + const blocksPerAction = 5; + let firstBlock = null; + const removeBlocks = []; + + for (let i = 0; i < noTXBuffer; i++) + await progressWithNoTX(wdb); + + for (let i = 0; i < blocksPerAction * 2; i++) { + const blockAndTXs = await progressWithTX(wdb); + if (!firstBlock) + firstBlock = blockAndTXs.block; + removeBlocks.push(blockAndTXs); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.strictEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.marked, true); + + // revert all + for (const {block} of removeBlocks.reverse()) + await wdb.removeBlock(block); + + const tip = await wdb.getTip(); + assert.strictEqual(wdb.state.startHeight, tip.height); + assert.strictEqual(wdb.state.startHash, tip.hash); + assert.strictEqual(wdb.height, noTXBuffer); + assert.strictEqual(wdb.state.height, noTXBuffer); + assert.strictEqual(wdb.state.marked, false); + + for (let i = 0; i < blocksPerAction; i++) { + await progressWithNoTX(wdb); + + assert.strictEqual(wdb.state.startHeight, tip.height); + assert.strictEqual(wdb.state.startHash, tip.hash); + assert.strictEqual(wdb.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.marked, false); + } + + assert.strictEqual(wdb.state.startHeight, tip.height); + assert.strictEqual(wdb.state.startHash, tip.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction); + assert.strictEqual(wdb.state.marked, false); + + firstBlock = null; + + for (let i = 0; i < blocksPerAction; i++) { + const blockAndTXs = await progressWithTX(wdb); + if (!firstBlock) + firstBlock = blockAndTXs.block; + removeBlocks.push(blockAndTXs); + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.strictEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction + i + 1); + assert.strictEqual(wdb.state.marked, true); + } + + assert.strictEqual(wdb.state.startHeight, firstBlock.height); + assert.strictEqual(wdb.state.startHash, firstBlock.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction * 2); + assert.strictEqual(wdb.state.marked, true); + }); + + it('should recover to the proper mark/startHeight after corruption', async () => { + // If we receive a block that has TXs (meaning wdb should care) but it + // DB/Node closes/crashes and restarted node does not have txs in the blocks. + // startHeight and mark will be set incorrectly. + const noTXBuffer = 10; + const blocksPerAction = 5; + + for (let i = 0; i < noTXBuffer; i++) + await progressWithNoTX(wdb); + + assert.strictEqual(wdb.state.startHeight, 0); + assert.strictEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, noTXBuffer); + assert.strictEqual(wdb.state.height, noTXBuffer); + assert.strictEqual(wdb.state.marked, false); + + // This will be the corruption case. + const bakAdd = wdb.primary.add; + wdb.primary.add = () => { + throw new Error('Corruption'); + }; + + let err; + try { + await progressWithTX(wdb); + } catch (e) { + err = e; + } + + assert(err); + assert.strictEqual(err.message, 'Corruption'); + + assert.strictEqual(wdb.state.startHeight, 0); + assert.strictEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, noTXBuffer); + assert.strictEqual(wdb.state.height, noTXBuffer); + assert.strictEqual(wdb.state.marked, false); + + wdb.primary.add = bakAdd; + + // no tx blocks... + for (let i = 0; i < blocksPerAction; i++) { + await progressWithNoTX(wdb); + + assert.strictEqual(wdb.state.startHeight, 0); + assert.strictEqual(wdb.state.startHash, consensus.ZERO_HASH); + assert.strictEqual(wdb.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + i + 1); + assert.strictEqual(wdb.state.marked, false); + } + + const {block} = await progressWithTX(wdb); + assert.strictEqual(wdb.state.startHeight, block.height); + assert.strictEqual(wdb.state.startHash, block.hash); + assert.strictEqual(wdb.height, noTXBuffer + blocksPerAction + 1); + assert.strictEqual(wdb.state.height, noTXBuffer + blocksPerAction + 1); + assert.strictEqual(wdb.state.marked, true); + }); +}); diff --git a/test/wallet-coinselection-test.js b/test/wallet-coinselection-test.js index f4cf9e940..ab8d13c41 100644 --- a/test/wallet-coinselection-test.js +++ b/test/wallet-coinselection-test.js @@ -7,11 +7,30 @@ const { WalletDB, policy } = require('..'); +const {BlockMeta} = require('../lib/wallet/records'); // Use main instead of regtest because (deprecated) // CoinSelector.MAX_FEE was network agnostic const network = Network.get('main'); +function dummyBlock(tipHeight) { + const height = tipHeight + 1; + const hash = Buffer.alloc(32); + hash.writeUInt16BE(height); + + const prevHash = Buffer.alloc(32); + prevHash.writeUInt16BE(tipHeight); + + const dummyBlock = { + hash, + height, + time: Date.now(), + prevBlock: prevHash + }; + + return dummyBlock; +} + async function fundWallet(wallet, amounts) { assert(Array.isArray(amounts)); @@ -21,15 +40,8 @@ async function fundWallet(wallet, amounts) { mtx.addOutput(addr, amt); } - const height = wallet.wdb.height + 1; - const hash = Buffer.alloc(32); - hash.writeUInt16BE(height); - const dummyBlock = { - hash, - height, - time: Date.now() - }; - await wallet.wdb.addBlock(dummyBlock, [mtx.toTX()]); + const dummy = dummyBlock(wallet.wdb.height); + await wallet.wdb.addBlock(dummy, [mtx.toTX()]); } describe('Wallet Coin Selection', function () { @@ -41,6 +53,10 @@ describe('Wallet Coin Selection', function () { await wdb.open(); wdb.height = network.txStart + 1; wdb.state.height = wdb.height; + + const dummy = dummyBlock(network.txStart + 1); + const record = BlockMeta.fromEntry(dummy); + await wdb.setTip(record); wallet = wdb.primary; }); diff --git a/test/wallet-deepclean-test.js b/test/wallet-deepclean-test.js index e2508d26a..1675e4551 100644 --- a/test/wallet-deepclean-test.js +++ b/test/wallet-deepclean-test.js @@ -75,7 +75,7 @@ describe('Wallet Deep Clean', function() { const name = i < 5 ? `alice${i}` : `bob${i}`; const array = i < 5 ? aliceBlinds : bobBlinds; - await w.sendOpen(name, false, {account: 0}); + await w.sendOpen(name, {account: 0}); await mineBlocks(network.names.treeInterval + 2); // Send two bids so there is a winner/loser and name gets a value diff --git a/test/wallet-events-test.js b/test/wallet-events-test.js index 536d8485d..3e404c78f 100644 --- a/test/wallet-events-test.js +++ b/test/wallet-events-test.js @@ -7,11 +7,13 @@ const Wallet = require('../lib/wallet/wallet'); const WalletKey = require('../lib/wallet/walletkey'); const ChainEntry = require('../lib/blockchain/chainentry'); const MemWallet = require('./util/memwallet'); +const {forEvent} = require('./util/common'); describe('WalletDB Events', function () { const node = new FullNode({ memory: true, network: 'regtest', + noDNS: true, plugins: [require('../lib/wallet/plugin')] }); @@ -35,15 +37,31 @@ describe('WalletDB Events', function () { await node.close(); }); - it('should emit `tx` events', async () => { - const waiter = new Promise((resolve) => { - wdb.once('tx', (w, tx) => resolve([w, tx])); - }); + it('should emit `open`/`close` and `connect`/`disconnect` events', async () => { + const closeEvent = forEvent(wdb, 'close'); + const disconnectEvent = forEvent(wdb, 'disconnect'); + await node.close(); + await disconnectEvent; + await closeEvent; + const openEvent = forEvent(wdb, 'open'); + const connectEvent = forEvent(wdb, 'connect'); + const syncDone = forEvent(wdb, 'sync done'); + await node.open(); + await openEvent; + await connectEvent; + await syncDone; + + wallet = await wdb.get('primary'); + }); + + it('should emit `tx` events', async () => { + const waiter = forEvent(wdb, 'tx'); const walletReceive = await wallet.receiveAddress(); await mineBlocks(1, walletReceive); - const [w, tx] = await waiter; + const events = await waiter; + const [w, tx] = events[0].values; assert(w); assert(w instanceof Wallet); @@ -55,14 +73,13 @@ describe('WalletDB Events', function () { }); it('should emit `address` events', async () => { - const waiter = new Promise((resolve) => { - wdb.once('address', (w, walletKey) => resolve([w, walletKey])); - }); + const waiter = forEvent(wdb, 'address'); const walletReceive = await wallet.receiveAddress(); await mineBlocks(1, walletReceive); - const [w, walletKey] = await waiter; + const events = await waiter; + const [w, walletKey] = events[0].values; assert(w); assert(w instanceof Wallet); @@ -75,10 +92,7 @@ describe('WalletDB Events', function () { describe('should emit `block connect` events', () => { it('with a block that includes a wallet tx', async () => { - const waiter = new Promise((resolve) => { - wdb.once('block connect', (entry, txs) => resolve([entry, txs])); - }); - + const waiter = forEvent(wdb, 'block connect'); const walletReceive = await wallet.receiveAddress(); await wallet.send({ @@ -89,7 +103,8 @@ describe('WalletDB Events', function () { await mineBlocks(1); - const [entry, txs] = await waiter; + const events = await waiter; + const [entry, txs] = events[0].values; assert(entry); assert(entry instanceof ChainEntry); @@ -127,15 +142,14 @@ describe('WalletDB Events', function () { otherTx = otherMtx.toTX(); } - const waiter = new Promise((resolve) => { - wdb.once('block connect', (entry, txs) => resolve([entry, txs])); - }); + const waiter = forEvent(wdb, 'block connect'); await node.sendTX(otherTx); await mineBlocks(1); - const [entry, txs] = await waiter; + const events = await waiter; + const [entry, txs] = events[0].values; assert(entry); assert(entry instanceof ChainEntry); @@ -158,14 +172,13 @@ describe('WalletDB Events', function () { await mineBlocks(1); // Disconnect it - const waiter = new Promise((resolve) => { - wdb.once('block disconnect', entry => resolve(entry)); - }); + const waiter = forEvent(wdb, 'block disconnect'); const entryToDisconnect = node.chain.tip; await node.chain.disconnect(entryToDisconnect); - const entry = await waiter; + const events = await waiter; + const entry = events[0].values[0]; assert(entry); assert(entry instanceof ChainEntry); @@ -173,18 +186,16 @@ describe('WalletDB Events', function () { }); it('with a block that does not include a wallet tx', async () => { - const waiter = new Promise((resolve) => { - wdb.once('block disconnect', entry => resolve(entry)); - }); - - const entryToDisconnect = node.chain.tip; - await node.chain.disconnect(entryToDisconnect); + const waiter = forEvent(wdb, 'block disconnect'); + const entryToDisconnect = node.chain.tip; + await node.chain.disconnect(entryToDisconnect); - const entry = await waiter; + const events = await waiter; + const entry = events[0].values[0]; - assert(entry); - assert(entry instanceof ChainEntry); - assert(entry.hash = entryToDisconnect.hash); + assert(entry); + assert(entry instanceof ChainEntry); + assert(entry.hash = entryToDisconnect.hash); }); }); }); diff --git a/test/wallet-http-test.js b/test/wallet-http-test.js index a633214ef..c231c6d49 100644 --- a/test/wallet-http-test.js +++ b/test/wallet-http-test.js @@ -1,8 +1,6 @@ 'use strict'; -const {NodeClient, WalletClient} = require('hs-client'); const Network = require('../lib/protocol/network'); -const FullNode = require('../lib/node/fullnode'); const MTX = require('../lib/primitives/mtx'); const {isSignatureEncoding, isKeyEncoding} = require('../lib/script/common'); const {Resource} = require('../lib/dns/resource'); @@ -15,35 +13,11 @@ const {types} = rules; const secp256k1 = require('bcrypto/lib/secp256k1'); const network = Network.get('regtest'); const assert = require('bsert'); +const {BufferSet} = require('buffer-map'); const common = require('./util/common'); - -const node = new FullNode({ - network: 'regtest', - apiKey: 'foo', - walletAuth: true, - memory: true, - workers: true, - plugins: [require('../lib/wallet/plugin')] -}); - -const nclient = new NodeClient({ - port: network.rpcPort, - apiKey: 'foo' -}); - -const wclient = new WalletClient({ - port: network.walletPort, - apiKey: 'foo' -}); - -const {wdb} = node.require('walletdb'); -const wallet = wclient.wallet('primary'); -const wallet2 = wclient.wallet('secondary'); - -let name, cbAddress; -const accountTwo = 'foobar'; -const ownedNames = []; -const allNames = []; +const Outpoint = require('../lib/primitives/outpoint'); +const consensus = require('../lib/protocol/consensus'); +const NodeContext = require('./util/node-context'); const { treeInterval, @@ -55,1494 +29,2300 @@ const { describe('Wallet HTTP', function() { this.timeout(20000); - before(async () => { - await node.open(); - await nclient.open(); - await wclient.open(); + /** @type {NodeContext} */ + let nodeCtx; + let wclient, nclient; + + // primary wallet client. + let wallet, cbAddress; + + const beforeAll = async () => { + nodeCtx = new NodeContext({ + apiKey: 'foo', + network: 'regtest', + walletAuth: true, + wallet: true + }); + + await nodeCtx.open(); + + wclient = nodeCtx.wclient; + nclient = nodeCtx.nclient; - await wclient.createWallet('secondary'); + wallet = nodeCtx.wclient.wallet('primary'); cbAddress = (await wallet.createAddress('default')).address; - await wallet.createAccount(accountTwo); + }; + + const afterAll = async () => { + await nodeCtx.close(); + }; + + describe('Create wallet', function() { + before(beforeAll); + after(afterAll); + + it('should create wallet', async () => { + const info = await wclient.createWallet('test'); + assert.strictEqual(info.id, 'test'); + const wallet = wclient.wallet('test', info.token); + await wallet.open(); + }); + + it('should create wallet with spanish mnemonic', async () => { + await wclient.createWallet( + 'cartera1', + {language: 'spanish'} + ); + const master = await wclient.getMaster('cartera1'); + const phrase = master.mnemonic.phrase; + for (const word of phrase.split(' ')) { + const language = Mnemonic.getLanguage(word); + assert.strictEqual(language, 'spanish'); + // Comprobar la cordura: + assert.notStrictEqual(language, 'english'); + } + + // Verificar + await wclient.createWallet( + 'cartera2', + {mnemonic: phrase} + ); + assert.deepStrictEqual( + await wclient.getAccount('cartera1', 'default'), + await wclient.getAccount('cartera2', 'default') + ); + }); }); - after(async () => { - await nclient.close(); - await wclient.close(); - await node.close(); + describe('Lookahead', function() { + before(beforeAll); + after(afterAll); + + it('should create wallet with default account 1000 lookahead', async () => { + const wname = 'lookahead'; + await wclient.createWallet(wname, { + lookahead: 1000 + }); + + const defAccount = await wclient.getAccount(wname, 'default'); + assert.strictEqual(defAccount.lookahead, 1000); + + const newAccount = await wclient.createAccount(wname, 'newaccount', { + lookahead: 1001 + }); + assert.strictEqual(newAccount.lookahead, 1001); + const getNewAccount = await wclient.getAccount(wname, 'newaccount', { + lookahead: 1001 + }); + + assert.strictEqual(getNewAccount.lookahead, 1001); + }); + + it('should modify account lookahead to 1000', async () => { + const wname = 'lookahead2'; + await wclient.createWallet(wname); + + const defAccount = await wclient.getAccount(wname, 'default'); + assert.strictEqual(defAccount.lookahead, 200); + + const modified = await wclient.modifyAccount(wname, 'default', { + lookahead: 1000 + }); + assert.strictEqual(modified.lookahead, 1000); + }); }); - beforeEach(async () => { - name = await nclient.execute('grindname', [5]); + describe('Wallet info', function() { + let wallet; + + before(async () => { + await beforeAll(); + + await wclient.createWallet('test'); + wallet = wclient.wallet('test'); + }); + after(afterAll); + + it('should get wallet info', async () => { + const info = await wallet.getInfo(); + assert.strictEqual(info.id, 'test'); + const acct = await wallet.getAccount('default'); + const str = acct.receiveAddress; + assert(typeof str === 'string'); + }); }); - afterEach(async () => { - await node.mempool.reset(); + describe('Key/Address', function() { + before(beforeAll); + after(afterAll); + + it('should get key by address from watch-only', async () => { + const phrase = 'abandon abandon abandon abandon abandon abandon ' + + 'abandon abandon abandon abandon abandon about'; + const master = HD.HDPrivateKey.fromPhrase(phrase); + const xprv = master.deriveAccount(44, 5355, 5); + const xpub = xprv.toPublic(); + const pubkey = xpub.derive(0).derive(0); + const addr = Address.fromPubkey(pubkey.publicKey); + const wallet = wclient.wallet('watchonly'); + await wclient.createWallet('watchonly', { + watchOnly: true, + accountKey: xpub.xpubkey('regtest') + }); + const key = await wallet.getKey(addr.toString('regtest')); + assert.equal(xpub.childIndex ^ HD.common.HARDENED, key.account); + assert.equal(0, key.branch); + assert.equal(0, key.index); + }); }); - it('should create wallet with spanish mnemonic', async () => { - await wclient.createWallet( - 'cartera1', - {language: 'spanish'} - ); - const master = await wclient.getMaster('cartera1'); - const phrase = master.mnemonic.phrase; - for (const word of phrase.split(' ')) { - const language = Mnemonic.getLanguage(word); - assert.strictEqual(language, 'spanish'); - // Comprobar la cordura: - assert.notStrictEqual(language, 'english'); - } - - // Verificar - await wclient.createWallet( - 'cartera2', - {mnemonic: phrase} - ); - assert.deepStrictEqual( - await wclient.getAccount('cartera1', 'default'), - await wclient.getAccount('cartera2', 'default') - ); + describe('Mine/Fund', function() { + before(beforeAll); + after(afterAll); + + it('should mine to the primary/default wallet', async () => { + const height = 20; + + await nodeCtx.mineBlocks(height, cbAddress); + + const info = await nclient.getInfo(); + assert.equal(info.chain.height, height); + + const accountInfo = await wallet.getAccount('default'); + // each coinbase output was indexed + assert.equal(accountInfo.balance.coin, height); + + const coins = await wallet.getCoins(); + // the wallet has no previous history besides + // what it has mined + assert.ok(coins.every(coin => coin.coinbase === true)); + }); }); - it('should create wallet with default account 1000 lookahead', async () => { - const wname = 'lookahead'; - await wclient.createWallet(wname, { - lookahead: 1000 + describe('Events', function() { + before(beforeAll); + after(afterAll); + + it('balance address and tx events', async () => { + await wclient.createWallet('test'); + const testWallet = wclient.wallet('test'); + await testWallet.open(); + const {address} = await testWallet.createAddress('default'); + + const mtx = new MTX(); + mtx.addOutpoint(new Outpoint(consensus.ZERO_HASH, 0)); + mtx.addOutput(address, 50460); + mtx.addOutput(address, 50460); + mtx.addOutput(address, 50460); + mtx.addOutput(address, 50460); + + const tx = mtx.toTX(); + + let balance = null; + testWallet.once('balance', (b) => { + balance = b; + }); + + let receive = null; + testWallet.once('address', (r) => { + receive = r[0]; + }); + + let details = null; + testWallet.once('tx', (d) => { + details = d; + }); + + await nodeCtx.wdb.addTX(tx); + await new Promise(r => setTimeout(r, 300)); + + assert(receive); + assert.strictEqual(receive.name, 'default'); + assert.strictEqual(receive.branch, 0); + assert(balance); + assert.strictEqual(balance.confirmed, 0); + assert.strictEqual(balance.unconfirmed, 201840); + assert(details); + assert.strictEqual(details.hash, tx.txid()); }); + }); - const defAccount = await wclient.getAccount(wname, 'default'); - assert.strictEqual(defAccount.lookahead, 1000); + describe('Create/Send transaction', function() { + let wallet2; - const newAccount = await wclient.createAccount(wname, 'newaccount', { - lookahead: 1001 + before(async () => { + await beforeAll(); + await nodeCtx.mineBlocks(20, cbAddress); + await wclient.createWallet('secondary'); + wallet2 = wclient.wallet('secondary'); }); - assert.strictEqual(newAccount.lookahead, 1001); - const getNewAccount = await wclient.getAccount(wname, 'newaccount', { - lookahead: 1001 + + after(afterAll); + + it('should create a transaction', async () => { + const tx = await wallet.createTX({ + outputs: [{ address: cbAddress, value: 1e4 }] + }); + + assert.ok(tx); + assert.equal(tx.outputs.length, 1 + 1); // send + change + assert.equal(tx.locktime, 0); }); - assert.strictEqual(getNewAccount.lookahead, 1001); - }); + it('should create self-send transaction with HD paths', async () => { + const tx = await wallet.createTX({ + paths: true, + outputs: [{ address: cbAddress, value: 1e4 }] + }); - it('should get key by address from watch-only', async () => { - const phrase = 'abandon abandon abandon abandon abandon abandon ' - + 'abandon abandon abandon abandon abandon about'; - const master = HD.HDPrivateKey.fromPhrase(phrase); - const xprv = master.deriveAccount(44, 5355, 5); - const xpub = xprv.toPublic(); - const pubkey = xpub.derive(0).derive(0); - const addr = Address.fromPubkey(pubkey.publicKey); - const wallet = wclient.wallet('watchonly'); - await wclient.createWallet('watchonly', { - watchOnly: true, - accountKey: xpub.xpubkey('regtest') - }); - const key = await wallet.getKey(addr.toString('regtest')); - assert.equal(xpub.childIndex ^ HD.common.HARDENED, key.account); - assert.equal(0, key.branch); - assert.equal(0, key.index); - }); + assert.ok(tx); + assert.ok(tx.inputs); - it('should mine to the primary/default wallet', async () => { - const height = 20; + for (let i = 0; i < tx.inputs.length; i++) { + const path = tx.inputs[i].path; - await mineBlocks(height, cbAddress); + assert.ok(typeof path.name === 'string'); + assert.ok(typeof path.account === 'number'); + assert.ok(typeof path.change === 'boolean'); + assert.ok(typeof path.derivation === 'string'); + } - const info = await nclient.getInfo(); - assert.equal(info.chain.height, height); + // cbAddress is a self-send + // so all output paths including change should be known + for (let i = 0; i < tx.outputs.length; i++) { + const path = tx.outputs[i].path; - const accountInfo = await wallet.getAccount('default'); - // each coinbase output was indexed - assert.equal(accountInfo.balance.coin, height); + assert.ok(typeof path.name === 'string'); + assert.ok(typeof path.account === 'number'); + assert.ok(typeof path.change === 'boolean'); + assert.ok(typeof path.derivation === 'string'); + } + }); + + it('should create a transaction with HD paths', async () => { + const tx = await wallet.createTX({ + paths: true, + outputs: [{ + address: 'rs1qlf5se77y0xlg5940slyf00djvveskcsvj9sdrd', + value: 1e4 + }] + }); + + assert.ok(tx); + assert.ok(tx.inputs); + + for (let i = 0; i < tx.inputs.length; i++) { + const path = tx.inputs[i].path; + + assert.ok(typeof path.name === 'string'); + assert.ok(typeof path.account === 'number'); + assert.ok(typeof path.change === 'boolean'); + assert.ok(typeof path.derivation === 'string'); + } + { + const path = tx.outputs[1].path; // change + assert.ok(typeof path.name === 'string'); + assert.ok(typeof path.account === 'number'); + assert.ok(typeof path.change === 'boolean'); + assert.ok(typeof path.derivation === 'string'); + } + { + const path = tx.outputs[0].path; // receiver + assert(!path); + } + }); - const coins = await wallet.getCoins(); - // the wallet has no previous history besides - // what it has mined - assert.ok(coins.every(coin => coin.coinbase === true)); + it('should create a transaction with a locktime', async () => { + const locktime = 8e6; + + const tx = await wallet.createTX({ + locktime: locktime, + outputs: [{ address: cbAddress, value: 1e4 }] + }); + + assert.equal(tx.locktime, locktime); + }); + + it('should create a transaction that is not bip 69 sorted', async () => { + // create a list of outputs that descend in value + // bip 69 sorts in ascending order based on the value + const outputs = []; + for (let i = 0; i < 5; i++) { + const addr = await wallet.createAddress('default'); + outputs.push({ address: addr.address, value: (5 - i) * 1e5 }); + } + + const tx = await wallet.createTX({ + outputs: outputs, + sort: false + }); + + // assert outputs in the same order that they were sent from the client + for (const [i, output] of outputs.entries()) { + assert.equal(tx.outputs[i].value, output.value); + assert.equal(tx.outputs[i].address.toString(network), output.address); + } + + const mtx = MTX.fromJSON(tx); + mtx.sortMembers(); + + // the order changes after sorting + assert.ok(tx.outputs[0].value !== mtx.outputs[0].value); + }); + + it('should create a transaction that is bip 69 sorted', async () => { + const outputs = []; + for (let i = 0; i < 5; i++) { + const addr = await wallet.createAddress('default'); + outputs.push({ address: addr.address, value: (5 - i) * 1e5 }); + } + + const tx = await wallet.createTX({ + outputs: outputs + }); + + const mtx = MTX.fromJSON(tx); + mtx.sortMembers(); + + // assert the ordering of the outputs is the + // same after sorting the response client side + for (const [i, output] of tx.outputs.entries()) { + assert.equal(output.value, mtx.outputs[i].value); + assert.equal(output.address, mtx.outputs[i].address.toString(network)); + } + }); + + it('should mine to the secondary/default wallet', async () => { + const height = 5; + + const {address} = await wallet2.createAddress('default'); + await nodeCtx.mineBlocks(height, address); + + const accountInfo = await wallet2.getAccount('default'); + assert.equal(accountInfo.balance.coin, height); + }); }); - it('should create a transaction', async () => { - const tx = await wallet.createTX({ - outputs: [{ address: cbAddress, value: 1e4 }] + describe('Get balance', function() { + before(async () => { + await beforeAll(); + await nodeCtx.mineBlocks(20, cbAddress); }); - assert.ok(tx); - assert.equal(tx.outputs.length, 1 + 1); // send + change - assert.equal(tx.locktime, 0); + after(afterAll); + + it('should get balance', async () => { + const balance = await wallet.getBalance(); + assert.equal(balance.tx, 20); + assert.equal(balance.coin, 20); + }); }); - it('should create a transaction with HD paths', async () => { - const tx = await wallet.createTX({ - paths: true, - outputs: [{ address: cbAddress, value: 1e4 }] + describe('Get TX', function() { + let hash; + + before(async () => { + await beforeAll(); + + await nodeCtx.mineBlocks(10, cbAddress); + const {address} = await wallet.createAddress('default'); + const tx = await wallet.send({outputs: [{address, value: 1e4}]}); + + hash = tx.hash; }); - assert.ok(tx); - assert.ok(tx.inputs); + after(afterAll); - for (let i = 0; i < tx.inputs.length; i++) { - const path = tx.inputs[i].path; + it('should fail to get TX that does not exist', async () => { + const hash = consensus.ZERO_HASH; + const tx = await wallet.getTX(hash.toString('hex')); + assert.strictEqual(tx, null); + }); - assert.ok(typeof path.name === 'string'); - assert.ok(typeof path.account === 'number'); - assert.ok(typeof path.change === 'boolean'); - assert.ok(typeof path.derivation === 'string'); - } + it('should get TX', async () => { + const tx = await wallet.getTX(hash.toString('hex')); + assert(tx); + assert.strictEqual(tx.hash, hash); + }); }); - it('should create a transaction with a locktime', async () => { - const locktime = 8e6; + describe('Create account (Integration)', function() { + before(beforeAll); + after(afterAll); + + it('should create an account', async () => { + const info = await wallet.createAccount('foo'); + assert(info); + assert(info.initialized); + assert.strictEqual(info.name, 'foo'); + assert.strictEqual(info.accountIndex, 1); + assert.strictEqual(info.m, 1); + assert.strictEqual(info.n, 1); + }); + + it('should create account', async () => { + const info = await wallet.createAccount('foo1'); + assert(info); + assert(info.initialized); + assert.strictEqual(info.name, 'foo1'); + assert.strictEqual(info.accountIndex, 2); + assert.strictEqual(info.m, 1); + assert.strictEqual(info.n, 1); + }); + + it('should create account', async () => { + const info = await wallet.createAccount('foo2', { + type: 'multisig', + m: 1, + n: 2 + }); + assert(info); + assert(!info.initialized); + assert.strictEqual(info.name, 'foo2'); + assert.strictEqual(info.accountIndex, 3); + assert.strictEqual(info.m, 1); + assert.strictEqual(info.n, 2); + }); + }); + + describe('Wallet auction (Integration)', function() { + const accountTwo = 'foobar'; + + let name, wallet2; + + const ownedNames = []; + const allNames = []; + + before(async () => { + await beforeAll(); - const tx = await wallet.createTX({ - locktime: locktime, - outputs: [{ address: cbAddress, value: 1e4 }] + await nodeCtx.mineBlocks(20, cbAddress); + await wallet.createAccount(accountTwo); + + await wclient.createWallet('secondary'); + wallet2 = wclient.wallet('secondary'); + const saddr = (await wallet2.createAddress('default')).address; + await nodeCtx.mineBlocks(5, saddr); }); - assert.equal(tx.locktime, locktime); - }); + after(afterAll); - it('should create a transaction that is not bip 69 sorted', async () => { - // create a list of outputs that descend in value - // bip 69 sorts in ascending order based on the value - const outputs = []; - for (let i = 0; i < 5; i++) { - const addr = await wallet.createAddress('default'); - outputs.push({ address: addr.address, value: (5 - i) * 1e5 }); - } + beforeEach(async () => { + name = await nclient.execute('grindname', [5]); + }); - const tx = await wallet.createTX({ - outputs: outputs, - sort: false + afterEach(async () => { + await nodeCtx.mempool.reset(); }); - // assert outputs in the same order that they were sent from the client - for (const [i, output] of outputs.entries()) { - assert.equal(tx.outputs[i].value, output.value); - assert.equal(tx.outputs[i].address.toString(network), output.address); - } + it('should have no name state indexed initially', async () => { + const names = await wallet.getNames(); + assert.strictEqual(names.length, 0); + }); - const mtx = MTX.fromJSON(tx); - mtx.sortMembers(); + it('should allow covenants with create tx', async () => { + const {address} = await wallet.createChange('default'); - // the order changes after sorting - assert.ok(tx.outputs[0].value !== mtx.outputs[0].value); - }); + const output = openOutput(name, address); - it('should create a transaction that is bip 69 sorted', async () => { - const outputs = []; - for (let i = 0; i < 5; i++) { - const addr = await wallet.createAddress('default'); - outputs.push({ address: addr.address, value: (5 - i) * 1e5 }); - } + const tx = await wallet.createTX({outputs: [output]}); + assert.equal(tx.outputs[0].covenant.type, types.OPEN); + }); + + it('should allow covenants with send tx', async () => { + const {address} = await wallet.createChange('default'); + + const output = openOutput(name, address); - const tx = await wallet.createTX({ - outputs: outputs + const tx = await wallet.send({outputs: [output]});; + assert.equal(tx.outputs[0].covenant.type, types.OPEN); }); - const mtx = MTX.fromJSON(tx); - mtx.sortMembers(); + it('should create an open and broadcast the tx', async () => { + let emitted = 0; + const handler = () => emitted++; + nodeCtx.mempool.on('tx', handler); - // assert the ordering of the outputs is the - // same after sorting the response client side - for (const [i, output] of tx.outputs.entries()) { - assert.equal(output.value, mtx.outputs[i].value); - assert.equal(output.address, mtx.outputs[i].address.toString(network)); - } - }); + const mempoolTXEvent = common.forEvent(nodeCtx.mempool, 'tx'); + const json = await wallet.createOpen({ + name: name + }); + await mempoolTXEvent; - it('should mine to the secondary/default wallet', async () => { - const height = 5; + const mempool = await nodeCtx.nclient.getMempool(); - const {address} = await wallet2.createAddress('default'); - await mineBlocks(height, address); + assert.ok(mempool.includes(json.hash)); - const accountInfo = await wallet2.getAccount('default'); - assert.equal(accountInfo.balance.coin, height); - }); + const opens = json.outputs.filter(output => output.covenant.type === types.OPEN); + assert.equal(opens.length, 1); - it('should have no name state indexed initially', async () => { - const names = await wallet.getNames(); + assert.equal(emitted, 1); - assert.strictEqual(names.length, 0); - }); + // reset for next test + nodeCtx.mempool.removeListener('tx', handler); + }); - it('should allow covenants with create tx', async () => { - const {address} = await wallet.createChange('default'); + it('should create an open and not broadcast the transaction', async () => { + let entered = false; + const handler = () => entered = true; + nodeCtx.mempool.on('tx', handler); + + const json = await wallet.createOpen({ + name: name, + broadcast: false + }); - const output = openOutput(name, address); + await sleep(200); - const tx = await wallet.createTX({outputs: [output]}); - assert.equal(tx.outputs[0].covenant.type, types.OPEN); - }); + // tx is not in the mempool + assert.equal(entered, false); + const mempool = await nclient.getMempool(); + assert.ok(!mempool.includes(json.hash)); - it('should allow covenants with send tx', async () => { - const {address} = await wallet.createChange('default'); + const mtx = MTX.fromJSON(json); + assert.ok(mtx.hasWitness()); - const output = openOutput(name, address); + // the signature and pubkey are templated correctly + const sig = mtx.inputs[0].witness.get(0); + assert.ok(isSignatureEncoding(sig)); + const pubkey = mtx.inputs[0].witness.get(1); + assert.ok(isKeyEncoding(pubkey)); + assert.ok(secp256k1.publicKeyVerify(pubkey)); - const tx = await wallet.send({outputs: [output]});; - assert.equal(tx.outputs[0].covenant.type, types.OPEN); - }); + // transaction is valid + assert.ok(mtx.verify()); - it('should create an open and broadcast the tx', async () => { - let emitted = 0; - const handler = () => emitted++; - node.mempool.on('tx', handler); + const opens = mtx.outputs.filter(output => output.covenant.type === types.OPEN); + assert.equal(opens.length, 1); - const json = await wallet.createOpen({ - name: name + // reset for next test + nodeCtx.mempool.removeListener('tx', handler); }); - // wait for tx event on mempool - await common.forEvent(node.mempool, 'tx'); + it('should create an open and not sign the transaction', async () => { + let entered = false; + const handler = () => entered = true; + nodeCtx.mempool.on('tx', handler); - const mempool = await nclient.getMempool(); + const json = await wallet.createOpen({ + name: name, + broadcast: false, + sign: false + }); - assert.ok(mempool.includes(json.hash)); + await sleep(200); - const opens = json.outputs.filter(output => output.covenant.type === types.OPEN); - assert.equal(opens.length, 1); + // tx is not in the mempool + assert.equal(entered, false); + const mempool = await nclient.getMempool(); + assert.ok(!mempool.includes(json.hash)); - assert.equal(emitted, 1); + // the signature is templated as an + // empty buffer + const mtx = MTX.fromJSON(json); + const sig = mtx.inputs[0].witness.get(0); + assert.bufferEqual(Buffer.from(''), sig); + assert.ok(!isSignatureEncoding(sig)); - // reset for next test - node.mempool.removeListener('tx', handler); - }); + // the pubkey is properly templated + const pubkey = mtx.inputs[0].witness.get(1); + assert.ok(isKeyEncoding(pubkey)); + assert.ok(secp256k1.publicKeyVerify(pubkey)); - it('should create an open and not broadcast the transaction', async () => { - let entered = false; - const handler = () => entered = true; - node.mempool.on('tx', handler); + // transaction not valid + assert.equal(mtx.verify(), false); - const json = await wallet.createOpen({ - name: name, - broadcast: false + // reset for next test + nodeCtx.mempool.removeListener('tx', handler); }); - await sleep(500); + it('should throw error with incompatible broadcast and sign options', async () => { + const fn = async () => await (wallet.createOpen({ + name: name, + broadcast: true, + sign: false + })); - // tx is not in the mempool - assert.equal(entered, false); - const mempool = await nclient.getMempool(); - assert.ok(!mempool.includes(json.hash)); + await assert.rejects(fn, {message: 'Must sign when broadcasting.'}); + }); - const mtx = MTX.fromJSON(json); - assert.ok(mtx.hasWitness()); + it('should fail to create open for account with no monies', async () => { + const info = await wallet.getAccount(accountTwo); + assert.equal(info.balance.tx, 0); + assert.equal(info.balance.coin, 0); - // the signature and pubkey are templated correctly - const sig = mtx.inputs[0].witness.get(0); - assert.ok(isSignatureEncoding(sig)); - const pubkey = mtx.inputs[0].witness.get(1); - assert.ok(isKeyEncoding(pubkey)); - assert.ok(secp256k1.publicKeyVerify(pubkey)); + const fn = async () => (await wallet.createOpen({ + name: name, + account: accountTwo + })); - // transaction is valid - assert.ok(mtx.verify()); + await assert.rejects(fn, {message: /Not enough funds./}); + }); - const opens = mtx.outputs.filter(output => output.covenant.type === types.OPEN); - assert.equal(opens.length, 1); + it('should mine to the account with no monies', async () => { + const height = 5; - // reset for next test - node.mempool.removeListener('tx', handler); - }); + const {receiveAddress} = await wallet.getAccount(accountTwo); - it('should create an open and not sign the transaction', async () => { - let entered = false; - const handler = () => entered = true; - node.mempool.on('tx', handler); + await nodeCtx.mineBlocks(height, receiveAddress); - const json = await wallet.createOpen({ - name: name, - broadcast: false, - sign: false + const info = await wallet.getAccount(accountTwo); + assert.equal(info.balance.tx, height); + assert.equal(info.balance.coin, height); }); - await sleep(500); + it('should create open for specific account', async () => { + const json = await wallet.createOpen({ + name: name, + account: accountTwo + }); - // tx is not in the mempool - assert.equal(entered, false); - const mempool = await nclient.getMempool(); - assert.ok(!mempool.includes(json.hash)); + const info = await wallet.getAccount(accountTwo); - // the signature is templated as an - // empty buffer - const mtx = MTX.fromJSON(json); - const sig = mtx.inputs[0].witness.get(0); - assert.bufferEqual(Buffer.from(''), sig); - assert.ok(!isSignatureEncoding(sig)); + // assert that each of the inputs belongs to the account + for (const {address} of json.inputs) { + const keyInfo = await wallet.getKey(address); + assert.equal(keyInfo.name, info.name); + } + }); - // the pubkey is properly templated - const pubkey = mtx.inputs[0].witness.get(1); - assert.ok(isKeyEncoding(pubkey)); - assert.ok(secp256k1.publicKeyVerify(pubkey)); + it('should open an auction', async () => { + await wallet.createOpen({ + name: name + }); - // transaction not valid - assert.equal(mtx.verify(), false); + // save chain height for later comparison + const info = await nclient.getInfo(); - // reset for next test - node.mempool.removeListener('tx', handler); - }); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - it('should throw error with incompatible broadcast and sign options', async () => { - const fn = async () => await (wallet.createOpen({ - name: name, - broadcast: true, - sign: false - })); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - await assert.rejects(fn, {message: 'Must sign when broadcasting.'}); - }); + const json = await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); + + const bids = json.outputs.filter(output => output.covenant.type === types.BID); + assert.equal(bids.length, 1); - it('should fail to create open for account with no monies', async () => { - const info = await wallet.getAccount(accountTwo); - assert.equal(info.balance.tx, 0); - assert.equal(info.balance.coin, 0); + const [bid] = bids; + assert.equal(bid.covenant.items.length, 4); - const fn = async () => (await wallet.createOpen({ - name: name, - account: accountTwo - })); + const [nameHash, start, rawName, blind] = bid.covenant.items; + assert.equal(nameHash, rules.hashName(name).toString('hex')); - await assert.rejects(fn, {message: /Not enough funds./}); - }); + // initially opened in the first block mined, so chain.height + 1 + const hex = Buffer.from(start, 'hex').reverse().toString('hex'); + assert.equal(parseInt(hex, 16), info.chain.height + 1); - it('should mine to the account with no monies', async () => { - const height = 5; + assert.equal(rawName, Buffer.from(name, 'ascii').toString('hex')); - const {receiveAddress} = await wallet.getAccount(accountTwo); + // blind is type string, so 32 * 2 + assert.equal(blind.length, 32 * 2); + }); - await mineBlocks(height, receiveAddress); + it('should be able to get nonce', async () => { + const bid = 100; - const info = await wallet.getAccount(accountTwo); - assert.equal(info.balance.tx, height); - assert.equal(info.balance.coin, height); - }); + const response = await wallet.getNonce(name, { + address: cbAddress, + bid: bid + }); - it('should create open for specific account', async () => { - const json = await wallet.createOpen({ - name: name, - account: accountTwo + const address = Address.fromString(cbAddress, network.type); + const nameHash = rules.hashName(name); + + const primary = nodeCtx.wdb.primary; + const nonces = await primary.generateNonces(nameHash, address, bid); + const blinds = nonces.map(nonce => rules.blind(bid, nonce)); + + assert.deepStrictEqual(response, { + address: address.toString(network.type), + blinds: blinds.map(blind => blind.toString('hex')), + nonces: nonces.map(nonce => nonce.toString('hex')), + bid: bid, + name: name, + nameHash: nameHash.toString('hex') + }); }); - const info = await wallet.getAccount(accountTwo); + it('should be able to get nonce for bid=0', async () => { + const bid = 0; - // assert that each of the inputs belongs to the account - for (const {address} of json.inputs) { - const keyInfo = await wallet.getKey(address); - assert.equal(keyInfo.name, info.name); - } - }); + const response = await wallet.getNonce(name, { + address: cbAddress, + bid: bid + }); + + const address = Address.fromString(cbAddress, network.type); + const nameHash = rules.hashName(name); + + const primary = nodeCtx.wdb.primary; + const nonces = await primary.generateNonces(nameHash, address, bid); + const blinds = nonces.map(nonce => rules.blind(bid, nonce)); - it('should open an auction', async () => { - await wallet.createOpen({ - name: name + assert.deepStrictEqual(response, { + address: address.toString(network.type), + blinds: blinds.map(blind => blind.toString('hex')), + nonces: nonces.map(nonce => nonce.toString('hex')), + bid: bid, + name: name, + nameHash: nameHash.toString('hex') + }); }); - // save chain height for later comparison - const info = await nclient.getInfo(); + it('should get name info', async () => { + const names = await wallet.getNames(); - await mineBlocks(treeInterval + 1, cbAddress); + assert.strictEqual(allNames.length, names.length); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + assert(names.length > 0); + const [ns] = names; - const json = await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 + const nameInfo = await wallet.getName(ns.name); + + assert.deepEqual(ns, nameInfo); }); - const bids = json.outputs.filter(output => output.covenant.type === types.BID); - assert.equal(bids.length, 1); + it('should fail to open a bid without a bid value', async () => { + const fn = async () => (await wallet.createBid({ + name: name + })); - const [bid] = bids; - assert.equal(bid.covenant.items.length, 4); + await assert.rejects(fn, {message: 'Bid is required.'}); + }); - const [nameHash, start, rawName, blind] = bid.covenant.items; - assert.equal(nameHash, rules.hashName(name).toString('hex')); + it('should fail to open a bid without a lockup value', async () => { + const fn = async () => (await wallet.createBid({ + name: name, + bid: 1000 + })); - // initially opened in the first block mined, so chain.height + 1 - const hex = Buffer.from(start, 'hex').reverse().toString('hex'); - assert.equal(parseInt(hex, 16), info.chain.height + 1); + await assert.rejects(fn, {message: 'Lockup is required.'}); + }); - assert.equal(rawName, Buffer.from(name, 'ascii').toString('hex')); + it('should send bid with 0 value and non-dust lockup', async () => { + await wallet.createOpen({ + name: name + }); - // blind is type string, so 32 * 2 - assert.equal(blind.length, 32 * 2); - }); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - it('should be able to get nonce', async () => { - const bid = 100; + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - const response = await wallet.getNonce(name, { - address: cbAddress, - bid: bid + await wallet.createBid({ + name: name, + bid: 0, + lockup: 1000 + }); }); - const address = Address.fromString(cbAddress, network.type); - const nameHash = rules.hashName(name); + it('should fail to send bid with 0 value and 0 lockup', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - const primary = node.plugins.walletdb.wdb.primary; - const nonces = await primary.generateNonces(nameHash, address, bid); - const blinds = nonces.map(nonce => rules.blind(bid, nonce)); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - assert.deepStrictEqual(response, { - address: address.toString(network.type), - blinds: blinds.map(blind => blind.toString('hex')), - nonces: nonces.map(nonce => nonce.toString('hex')), - bid: bid, - name: name, - nameHash: nameHash.toString('hex') + const fn = async () => await wallet.createBid({ + name: name, + bid: 0, + lockup: 0 + }); + + await assert.rejects(fn, {message: 'Output is dust.'}); }); - }); - it('should be able to get nonce for bid=0', async () => { - const bid = 0; + it('should get all bids (single player)', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); + + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); + + const tx1 = await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); - const response = await wallet.getNonce(name, { - address: cbAddress, - bid: bid + const tx2 = await wallet.createBid({ + name: name, + bid: 2000, + lockup: 3000 + }); + + const tx3 = await wallet.createBid({ + name: name, + bid: 4000, + lockup: 5000 + }); + + await nodeCtx.mineBlocks(1, cbAddress); + + // this method gets all bids for all names + const bids = await wallet.getBids(); + + // this depends on this it block creating + // the first bids of this test suite + assert.equal(bids.length, 3); + assert.ok(bids.every(bid => bid.name === name)); + assert.ok(bids.every(bid => bid.height === nodeCtx.height)); + + // tx1 + assert.ok(bids.find(bid => + (bid.value === 1000 + && bid.lockup === 2000 + && bid.prevout.hash === tx1.hash) + )); + + // tx2 + assert.ok(bids.find(bid => + (bid.value === 2000 + && bid.lockup === 3000 + && bid.prevout.hash === tx2.hash) + )); + + // tx3 + assert.ok(bids.find(bid => + (bid.value === 4000 + && bid.lockup === 5000 + && bid.prevout.hash === tx3.hash) + )); }); - const address = Address.fromString(cbAddress, network.type); - const nameHash = rules.hashName(name); + it('should get all bids (two players)', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); + + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); + + const tx1 = await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); + + const tx2 = await wallet2.createBid({ + name: name, + bid: 2000, + lockup: 3000 + }); + + await nodeCtx.mineBlocks(1, cbAddress); + + { + await sleep(100); + // fetch all bids for the name + const bids = await wallet.getBidsByName(name); + assert.equal(bids.length, 2); + + assert.ok(bids.every(bid => bid.height === nodeCtx.height)); + + // there is no value property on bids + // from other wallets + assert.ok(bids.find(bid => + (bid.lockup === 2000 + && bid.prevout.hash === tx1.hash) + )); + + assert.ok(bids.find(bid => + (bid.lockup === 3000 + && bid.prevout.hash === tx2.hash) + )); + } + + { + // fetch only own bids for the name + const bids = await wallet.getBidsByName(name, {own: true}); + assert.equal(bids.length, 1); + const [bid] = bids; + assert.equal(bid.prevout.hash, tx1.hash); + assert.strictEqual(bid.height, nodeCtx.height); + } + }); + + it('should create a reveal', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - const primary = node.plugins.walletdb.wdb.primary; - const nonces = await primary.generateNonces(nameHash, address, bid); - const blinds = nonces.map(nonce => rules.blind(bid, nonce)); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); + + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); + + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); + + const {info} = await nclient.execute('getnameinfo', [name]); + assert.equal(info.name, name); + assert.equal(info.state, 'REVEAL'); + + const json = await wallet.createReveal({ + name: name + }); + + const reveals = json.outputs.filter(output => output.covenant.type === types.REVEAL); + assert.equal(reveals.length, 1); + }); + + it('should create all reveals', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); + + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); + + for (let i = 0; i < 3; i++) { + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); + } + + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); + + const {info} = await nclient.execute('getnameinfo', [name]); + assert.equal(info.name, name); + assert.equal(info.state, 'REVEAL'); + + const json = await wallet.createReveal(); + + const reveals = json.outputs.filter(output => output.covenant.type === types.REVEAL); + assert.equal(reveals.length, 3); + + ownedNames.push(name); + + await nodeCtx.mineBlocks(1, cbAddress); + + const allReveals = await wallet.getReveals(); + assert.strictEqual(allReveals.length, 3); + assert.ok(allReveals.every(reveal => reveal.name === name)); + assert.ok(allReveals.every(reveal => reveal.height === nodeCtx.height)); + + const revealsByName = await wallet.getRevealsByName(name); + assert.strictEqual(revealsByName.length, 3); + assert.ok(revealsByName.every(reveal => reveal.name === name)); + assert.ok(revealsByName.every(reveal => reveal.height === nodeCtx.height)); + }); + + it('should get all reveals (single player)', async () => { + await wallet.createOpen({ + name: name + }); + + const name2 = await nclient.execute('grindname', [5]); + + await wallet.createOpen({ + name: name2 + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); + + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); + allNames.push(name2); + + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); + + await wallet.createBid({ + name: name2, + bid: 2000, + lockup: 3000 + }); + + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); + + await wallet.createReveal({ + name: name + }); + + await wallet.createReveal({ + name: name2 + }); + + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); + + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); + ownedNames.push(name2); + + { + const reveals = await wallet.getReveals(); + assert.equal(reveals.length, 5); + } + + { + // a single reveal per name + const reveals = await wallet.getRevealsByName(name); + const [reveal] = reveals; + assert.strictEqual(reveal.height + revealPeriod, nodeCtx.height); + assert.equal(reveals.length, 1); + } + }); + + // this test creates namestate to use duing the + // next test, hold on to the name being used. + const state = { + name: '', + bids: [], + reveals: [] + }; + + it('should get own reveals (two players)', async () => { + state.name = name; + + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); + + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); + + const b1 = await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); + + const b2 = await wallet2.createBid({ + name: name, + bid: 2000, + lockup: 3000 + }); + + state.bids.push(b1); + state.bids.push(b2); + + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); + + const r1 = await wallet.createReveal({ + name: name + }); + + const r2 = await wallet2.createReveal({ + name: name + }); - assert.deepStrictEqual(response, { - address: address.toString(network.type), - blinds: blinds.map(blind => blind.toString('hex')), - nonces: nonces.map(nonce => nonce.toString('hex')), - bid: bid, - name: name, - nameHash: nameHash.toString('hex') - }); - }); + state.reveals.push(r1); + state.reveals.push(r2); - it('should get name info', async () => { - const names = await wallet.getNames(); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - assert.strictEqual(allNames.length, names.length); + // wallet did not win this auction so name is not pushed to ownedNames[] - assert(names.length > 0); - const [ns] = names; + { + const reveals = await wallet.getRevealsByName(name, {own: true}); + assert.strictEqual(reveals.length, 1); + const [reveal] = reveals; + assert.strictEqual(reveal.bidPrevout.hash, state.bids[0].hash); + assert.strictEqual(reveal.bidPrevout.index, 0); + assert.strictEqual(reveal.own, true); + assert.strictEqual(reveal.prevout.hash, r1.hash); + } - const nameInfo = await wallet.getName(ns.name); + { + const reveals = await wallet.getRevealsByName(name); + assert.strictEqual(reveals.length, 2); - assert.deepEqual(ns, nameInfo); - }); + const r1 = reveals.find(reveal => + reveal.prevout.hash === state.reveals[0].hash); + const r2 = reveals.find(reveal => + reveal.prevout.hash === state.reveals[1].hash); - it('should fail to open a bid without a bid value', async () => { - const fn = async () => (await wallet.createBid({ - name: name - })); + assert.ok(r1); + assert.ok(r2); - await assert.rejects(fn, {message: 'Bid is required.'}); - }); + assert.strictEqual(r1.bidPrevout.hash, state.bids[0].hash); + assert.strictEqual(r1.bidPrevout.index, 0); - it('should fail to open a bid without a lockup value', async () => { - const fn = async () => (await wallet.createBid({ - name: name, - bid: 1000 - })); + assert.strictEqual(r2.bidPrevout.hash, state.bids[1].hash); + assert.strictEqual(r2.bidPrevout.index, 0); + } - await assert.rejects(fn, {message: 'Lockup is required.'}); - }); + const dump = await nodeCtx.wdb.dump(); + const dumpSlice = {}; - it('should send bid with 0 value and non-dust lockup', async () => { - await wallet.createOpen({ - name: name - }); + Object.keys(dump).filter((key) => { + const wid1 = '7400000001'; + // txdblayout.t + if (key.startsWith(wid1 + '74')) + dumpSlice[key] = dump[key]; - await mineBlocks(treeInterval + 1, cbAddress); + // txdblayout.i + if (key.startsWith(wid1 + '69')) + dumpSlice[key] = dump[key]; - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + // txdblayout.B + if (key.startsWith(wid1 + '42')) + dumpSlice[key] = dump[key]; + }); - await wallet.createBid({ - name: name, - bid: 0, - lockup: 1000 + console.log(dumpSlice); }); - }); - it('should fail to send bid with 0 value and 0 lockup', async () => { - await wallet.createOpen({ - name: name - }); + it('should get auction info', async () => { + const ns = await wallet.getName(state.name); - await mineBlocks(treeInterval + 1, cbAddress); + const auction = await wallet.getAuctionByName(ns.name); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + // auction info returns a list of bids + // and a list of reveals for the name + assert.ok(Array.isArray(auction.bids)); + assert.ok(Array.isArray(auction.reveals)); - const fn = async () => await wallet.createBid({ - name: name, - bid: 0, - lockup: 0 - }); + // 2 bids and 2 reveals in the previous test + assert.equal(auction.bids.length, 2); + assert.equal(auction.reveals.length, 2); - await assert.rejects(fn, {message: 'Output is dust.'}); - }); + // ordering can be nondeterministic + function matchTxId(namestates, target) { + assert.ok(namestates.find(ns => ns.prevout.hash === target)); + } - it('should get all bids (single player)', async () => { - await wallet.createOpen({ - name: name + matchTxId(auction.bids, state.bids[0].hash); + matchTxId(auction.bids, state.bids[1].hash); + matchTxId(auction.reveals, state.reveals[0].hash); + matchTxId(auction.reveals, state.reveals[1].hash); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should create a bid and a reveal (reveal in advance)', async () => { + const balanceBeforeTest = await wallet.getBalance(); + const lockConfirmedBeforeTest = balanceBeforeTest.lockedConfirmed; + const lockUnconfirmedBeforeTest = balanceBeforeTest.lockedUnconfirmed; - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await wallet.createOpen({ name: name }); - const tx1 = await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + await nodeCtx.mineBlocks(treeInterval + 2, cbAddress); - const tx2 = await wallet.createBid({ - name: name, - bid: 2000, - lockup: 3000 - }); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - const tx3 = await wallet.createBid({ - name: name, - bid: 4000, - lockup: 5000 - }); + const balanceBeforeBid = await wallet.getBalance(); + assert.equal(balanceBeforeBid.lockedConfirmed - lockConfirmedBeforeTest, 0); + assert.equal( + balanceBeforeBid.lockedUnconfirmed - lockUnconfirmedBeforeTest, + 0 + ); + + const bidValue = 1000000; + const lockupValue = 5000000; - await mineBlocks(1, cbAddress); + const auctionTXs = await wallet.client.post( + `/wallet/${wallet.id}/auction`, + { + name: name, + bid: 1000000, + lockup: 5000000, + broadcastBid: true + } + ); - // this method gets all bids for all names - const bids = await wallet.getBids(); + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); - // this depends on this it block creating - // the first bids of this test suite - assert.equal(bids.length, 3); - assert.ok(bids.every(bid => bid.name === name)); + let walletAuction = await wallet.getAuctionByName(name); + const bidFromWallet = walletAuction.bids.find( + b => b.prevout.hash === auctionTXs.bid.hash + ); + assert(bidFromWallet); - // tx1 - assert.ok(bids.find(bid => - (bid.value === 1000 - && bid.lockup === 2000 - && bid.prevout.hash === tx1.hash) - )); + const { info } = await nclient.execute('getnameinfo', [name]); + assert.equal(info.name, name); + assert.equal(info.state, 'REVEAL'); - // tx2 - assert.ok(bids.find(bid => - (bid.value === 2000 - && bid.lockup === 3000 - && bid.prevout.hash === tx2.hash) - )); + const b5 = await wallet.getBalance(); + assert.equal(b5.lockedConfirmed - lockConfirmedBeforeTest, lockupValue); + assert.equal(b5.lockedUnconfirmed - lockUnconfirmedBeforeTest, lockupValue); - // tx3 - assert.ok(bids.find(bid => - (bid.value === 4000 - && bid.lockup === 5000 - && bid.prevout.hash === tx3.hash) - )); - }); + await nclient.broadcast(auctionTXs.reveal.hex); + await nodeCtx.mineBlocks(1, cbAddress); - it('should get all bids (two players)', async () => { - await wallet.createOpen({ - name: name - }); + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); - await mineBlocks(treeInterval + 1, cbAddress); + walletAuction = await wallet.getAuctionByName(name); + const revealFromWallet = walletAuction.reveals.find( + b => b.prevout.hash === auctionTXs.reveal.hash + ); + assert(revealFromWallet); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + const b6 = await wallet.getBalance(); + assert.equal(b6.lockedConfirmed - lockConfirmedBeforeTest, bidValue); + assert.equal(b6.lockedUnconfirmed - lockUnconfirmedBeforeTest, bidValue); - const tx1 = await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - const tx2 = await wallet2.createBid({ - name: name, - bid: 2000, - lockup: 3000 + const ns = await nclient.execute('getnameinfo', [name]); + const coin = await wallet.getCoin(ns.info.owner.hash, ns.info.owner.index); + assert.ok(coin); }); - await mineBlocks(1, cbAddress); + it('should create a redeem', async () => { + await wallet.createOpen({ + name: name + }); - { - await sleep(100); - // fetch all bids for the name - const bids = await wallet.getBidsByName(name); - assert.equal(bids.length, 2); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - // there is no value property on bids - // from other wallets - assert.ok(bids.find(bid => - (bid.lockup === 2000 - && bid.prevout.hash === tx1.hash) - )); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - assert.ok(bids.find(bid => - (bid.lockup === 3000 - && bid.prevout.hash === tx2.hash) - )); - } + // wallet2 wins the auction, wallet can submit redeem + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); - { - // fetch only own bids for the name - const bids = await wallet.getBidsByName(name, {own: true}); - assert.equal(bids.length, 1); - const [bid] = bids; - assert.equal(bid.prevout.hash, tx1.hash); - } - }); + await wallet2.createBid({ + name: name, + bid: 2000, + lockup: 3000 + }); - it('should create a reveal', async () => { - await wallet.createOpen({ - name: name - }); + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); - await mineBlocks(treeInterval + 1, cbAddress); + await wallet.createReveal({ + name: name + }); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await wallet2.createReveal({ + name: name + }); - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - await mineBlocks(biddingPeriod + 1, cbAddress); + // wallet did not win this auction so name is not pushed to ownedNames[] - const {info} = await nclient.execute('getnameinfo', [name]); - assert.equal(info.name, name); - assert.equal(info.state, 'REVEAL'); + // wallet2 is the winner, therefore cannot redeem + const fn = async () => (await wallet2.createRedeem({ + name: name + })); - const json = await wallet.createReveal({ - name: name - }); + await assert.rejects( + fn, + {message: `No reveals to redeem for name: ${name}.`} + ); - const reveals = json.outputs.filter(output => output.covenant.type === types.REVEAL); - assert.equal(reveals.length, 1); - }); + const json = await wallet.createRedeem({ + name: name + }); - it('should create all reveals', async () => { - await wallet.createOpen({ - name: name + const redeem = json.outputs.filter(({covenant}) => covenant.type === types.REDEEM); + assert.ok(redeem.length > 0); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should create an update', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - for (let i = 0; i < 3; i++) { await wallet.createBid({ name: name, bid: 1000, lockup: 2000 }); - } - await mineBlocks(biddingPeriod + 1, cbAddress); + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); - const {info} = await nclient.execute('getnameinfo', [name]); - assert.equal(info.name, name); - assert.equal(info.state, 'REVEAL'); - - const json = await wallet.createReveal(); + await wallet.createReveal({ + name: name + }); - const reveals = json.outputs.filter(output => output.covenant.type === types.REVEAL); - assert.equal(reveals.length, 3); - }); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - it('should get all reveals (single player)', async () => { - await wallet.createOpen({ - name: name - }); + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); - const name2 = await nclient.execute('grindname', [5]); + { + const json = await wallet.createUpdate({ + name: name, + data: { + records: [ + { + type: 'TXT', + txt: ['foobar'] + } + ] + } + }); - await wallet.createOpen({ - name: name2 - }); + // register directly after reveal + const registers = json.outputs.filter(({covenant}) => covenant.type === types.REGISTER); + assert.equal(registers.length, 1); + } - await mineBlocks(treeInterval + 1, cbAddress); + // mine a block + await nodeCtx.mineBlocks(1, cbAddress); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); - allNames.push(name2); + { + const json = await wallet.createUpdate({ + name: name, + data: { + records: [ + { + type: 'TXT', + txt: ['barfoo'] + } + ] + } + }); - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 + // update after register or update + const updates = json.outputs.filter(({covenant}) => covenant.type === types.UPDATE); + assert.equal(updates.length, 1); + } }); - await wallet.createBid({ - name: name2, - bid: 2000, - lockup: 3000 - }); + it('should get name resource', async () => { + const names = await wallet.getNames(); + // filter out names that have data + // this test depends on the previous test + const [ns] = names.filter(n => n.data.length > 0); + assert(ns); - await mineBlocks(biddingPeriod + 1, cbAddress); + const state = Resource.decode(Buffer.from(ns.data, 'hex')); - await wallet.createReveal({ - name: name - }); + const resource = await wallet.getResource(ns.name); + assert(resource); + const res = Resource.fromJSON(resource); - await wallet.createReveal({ - name: name2 + assert.deepEqual(state, res); }); - await mineBlocks(revealPeriod + 1, cbAddress); + it('should fail to get name resource for non existent name', async () => { + const name = await nclient.execute('grindname', [10]); - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); - ownedNames.push(name2); + const resource = await wallet.getResource(name); + assert.equal(resource, null); + }); - { - const reveals = await wallet.getReveals(); - assert.equal(reveals.length, 2); - } + it('should create a renewal', async () => { + await wallet.createOpen({ + name: name + }); - { - // a single reveal per name - const reveals = await wallet.getRevealsByName(name); - assert.equal(reveals.length, 1); - } - }); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - // this test creates namestate to use duing the - // next test, hold on to the name being used. - const state = { - name: '', - bids: [], - reveals: [] - }; + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - it('should get own reveals (two players)', async () => { - state.name = name; + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); - await wallet.createOpen({ - name: name - }); + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); - await mineBlocks(treeInterval + 1, cbAddress); + await wallet.createReveal({ + name: name + }); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - const b1 = await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); - const b2 = await wallet2.createBid({ - name: name, - bid: 2000, - lockup: 3000 - }); + await wallet.createUpdate({ + name: name, + data: { + records: [ + { + type: 'TXT', + txt: ['foobar'] + } + ] + } + }); - state.bids.push(b1); - state.bids.push(b2); + // mine up to the earliest point in which a renewal + // can be submitted, a treeInterval into the future + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - await mineBlocks(biddingPeriod + 1, cbAddress); + const json = await wallet.createRenewal({ + name + }); - const r1 = await wallet.createReveal({ - name: name + const updates = json.outputs.filter(({covenant}) => covenant.type === types.RENEW); + assert.equal(updates.length, 1); }); - const r2 = await wallet2.createReveal({ - name: name - }); + it('should create a transfer', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - state.reveals.push(r1); - state.reveals.push(r2); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - await mineBlocks(revealPeriod + 1, cbAddress); + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); - // wallet did not win this auction so name is not pushed to ownedNames[] + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); - { - const reveals = await wallet.getRevealsByName(name, {own: true}); - assert.equal(reveals.length, 1); - const [reveal] = reveals; - assert.equal(reveal.own, true); - assert.equal(reveal.prevout.hash, r1.hash); - } + await wallet.createReveal({ + name: name + }); - { - const reveals = await wallet.getRevealsByName(name); - assert.equal(reveals.length, 2); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - assert.ok(reveals.find(reveal => - reveal.prevout.hash === r1.hash - )); + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); - assert.ok(reveals.find(reveal => - reveal.prevout.hash === r2.hash - )); - } - }); + await wallet.createUpdate({ + name: name, + data: { + records: [ + { + type: 'TXT', + txt: ['foobar'] + } + ] + } + }); - it('should get auction info', async () => { - const ns = await wallet.getName(state.name); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - const auction = await wallet.getAuctionByName(ns.name); + const {receiveAddress} = await wallet.getAccount(accountTwo); - // auction info returns a list of bids - // and a list of reveals for the name - assert.ok(Array.isArray(auction.bids)); - assert.ok(Array.isArray(auction.reveals)); + const json = await wallet.createTransfer({ + name, + address: receiveAddress + }); - // 2 bids and 2 reveals in the previous test - assert.equal(auction.bids.length, 2); - assert.equal(auction.reveals.length, 2); + const xfer = json.outputs.filter(({covenant}) => covenant.type === types.TRANSFER); + assert.equal(xfer.length, 1); + }); - // ordering can be nondeterministic - function matchTxId(namestates, target) { - assert.ok(namestates.find(ns => ns.prevout.hash === target)); - } + it('should create a finalize', async () => { + await wallet.createOpen({ + name: name + }); - matchTxId(auction.bids, state.bids[0].hash); - matchTxId(auction.bids, state.bids[1].hash); - matchTxId(auction.reveals, state.reveals[0].hash); - matchTxId(auction.reveals, state.reveals[1].hash); - }); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - it('should create a bid and a reveal (reveal in advance)', async () => { - const balanceBeforeTest = await wallet.getBalance(); - const lockConfirmedBeforeTest = balanceBeforeTest.lockedConfirmed; - const lockUnconfirmedBeforeTest = balanceBeforeTest.lockedUnconfirmed; + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - await wallet.createOpen({ name: name }); + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); - await mineBlocks(treeInterval + 2, cbAddress); + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await wallet.createReveal({ + name: name + }); - const balanceBeforeBid = await wallet.getBalance(); - assert.equal(balanceBeforeBid.lockedConfirmed - lockConfirmedBeforeTest, 0); - assert.equal( - balanceBeforeBid.lockedUnconfirmed - lockUnconfirmedBeforeTest, - 0 - ); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - const bidValue = 1000000; - const lockupValue = 5000000; + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); - const auctionTxs = await wallet.client.post( - `/wallet/${wallet.id}/auction`, - { + await wallet.createUpdate({ name: name, - bid: 1000000, - lockup: 5000000, - broadcastBid: true - } - ); - - await mineBlocks(biddingPeriod + 1, cbAddress); + data: { + records: [ + { + type: 'TXT', + txt: ['foobar'] + } + ] + } + }); - let walletAuction = await wallet.getAuctionByName(name); - const bidFromWallet = walletAuction.bids.find( - b => b.prevout.hash === auctionTxs.bid.hash - ); - assert(bidFromWallet); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - const { info } = await nclient.execute('getnameinfo', [name]); - assert.equal(info.name, name); - assert.equal(info.state, 'REVEAL'); + const {receiveAddress} = await wallet2.getAccount('default'); - const b5 = await wallet.getBalance(); - assert.equal(b5.lockedConfirmed - lockConfirmedBeforeTest, lockupValue); - assert.equal(b5.lockedUnconfirmed - lockUnconfirmedBeforeTest, lockupValue); + await wallet.createTransfer({ + name, + address: receiveAddress + }); - await nclient.broadcast(auctionTxs.reveal.hex); - await mineBlocks(1, cbAddress); + await nodeCtx.mineBlocks(transferLockup + 1, cbAddress); - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); + const json = await wallet.createFinalize({ + name + }); - walletAuction = await wallet.getAuctionByName(name); - const revealFromWallet = walletAuction.reveals.find( - b => b.prevout.hash === auctionTxs.reveal.hash - ); - assert(revealFromWallet); + const final = json.outputs.filter(({covenant}) => covenant.type === types.FINALIZE); + assert.equal(final.length, 1); - const b6 = await wallet.getBalance(); - assert.equal(b6.lockedConfirmed - lockConfirmedBeforeTest, bidValue); - assert.equal(b6.lockedUnconfirmed - lockUnconfirmedBeforeTest, bidValue); + await nodeCtx.mineBlocks(1, cbAddress); - await mineBlocks(revealPeriod + 1, cbAddress); + // Confirmed FINALIZE means this wallet is not the owner anymore! + ownedNames.splice(ownedNames.indexOf(name), 1); - const ns = await nclient.execute('getnameinfo', [name]); - const coin = await wallet.getCoin(ns.info.owner.hash, ns.info.owner.index); - assert.ok(coin); - }); + const ns = await nclient.execute('getnameinfo', [name]); + const coin = await nclient.getCoin(ns.info.owner.hash, ns.info.owner.index); - it('should create a redeem', async () => { - await wallet.createOpen({ - name: name + assert.equal(coin.address, receiveAddress); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should create a cancel', async () => { + await wallet.createOpen({ + name: name + }); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - // wallet2 wins the auction, wallet can submit redeem - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - await wallet2.createBid({ - name: name, - bid: 2000, - lockup: 3000 - }); + await wallet.createBid({ + name: name, + bid: 1000, + lockup: 2000 + }); - await mineBlocks(biddingPeriod + 1, cbAddress); + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); - await wallet.createReveal({ - name: name - }); + await wallet.createReveal({ + name: name + }); - await wallet2.createReveal({ - name: name - }); + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - await mineBlocks(revealPeriod + 1, cbAddress); + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); - // wallet did not win this auction so name is not pushed to ownedNames[] + await wallet.createUpdate({ + name: name, + data: { + records: [ + { + type: 'TXT', + txt: ['foobar'] + } + ] + } + }); - // wallet2 is the winner, therefore cannot redeem - const fn = async () => (await wallet2.createRedeem({ - name: name - })); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - await assert.rejects( - fn, - {message: `No reveals to redeem for name: ${name}.`} - ); + const {receiveAddress} = await wallet.getAccount(accountTwo); - const json = await wallet.createRedeem({ - name: name - }); + await wallet.createTransfer({ + name, + address: receiveAddress + }); - const redeem = json.outputs.filter(({covenant}) => covenant.type === types.REDEEM); - assert.ok(redeem.length > 0); - }); + await nodeCtx.mineBlocks(transferLockup + 1, cbAddress); - it('should create an update', async () => { - await wallet.createOpen({ - name: name - }); + const json = await wallet.createCancel({name}); - await mineBlocks(treeInterval + 1, cbAddress); + const cancel = json.outputs.filter(({covenant}) => covenant.type === types.UPDATE); + assert.equal(cancel.length, 1); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await nodeCtx.mineBlocks(1, cbAddress); - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + const ns = await nclient.execute('getnameinfo', [name]); + assert.equal(ns.info.name, name); - await mineBlocks(biddingPeriod + 1, cbAddress); + const coin = await wallet.getCoin(ns.info.owner.hash, ns.info.owner.index); + assert.ok(coin); - await wallet.createReveal({ - name: name + const keyInfo = await wallet.getKey(coin.address); + assert.ok(keyInfo); }); - await mineBlocks(revealPeriod + 1, cbAddress); + it('should create a revoke', async () => { + await wallet.createOpen({ + name: name + }); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); + // Confirmed OPEN adds name to wallet's namemap + allNames.push(name); - { - const json = await wallet.createUpdate({ + await wallet.createBid({ name: name, - data: { - records: [ - { - type: 'TXT', - txt: ['foobar'] - } - ] - } + bid: 1000, + lockup: 2000 }); - // register directly after reveal - const registers = json.outputs.filter(({covenant}) => covenant.type === types.REGISTER); - assert.equal(registers.length, 1); - } + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); + + await wallet.createReveal({ + name: name + }); + + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); - // mine a block - await mineBlocks(1, cbAddress); + // Confirmed REVEAL with highest bid makes wallet the owner + ownedNames.push(name); - { - const json = await wallet.createUpdate({ + await wallet.createUpdate({ name: name, data: { records: [ { type: 'TXT', - txt: ['barfoo'] + txt: ['foobar'] } ] } }); - // update after register or update - const updates = json.outputs.filter(({covenant}) => covenant.type === types.UPDATE); - assert.equal(updates.length, 1); - } - }); + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); - it('should get name resource', async () => { - const names = await wallet.getNames(); - // filter out names that have data - // this test depends on the previous test - const [ns] = names.filter(n => n.data.length > 0); - assert(ns); + const json = await wallet.createRevoke({name}); - const state = Resource.decode(Buffer.from(ns.data, 'hex')); + const final = json.outputs.filter(({covenant}) => covenant.type === types.REVOKE); + assert.equal(final.length, 1); - const resource = await wallet.getResource(ns.name); - assert(resource); - const res = Resource.fromJSON(resource); + await nodeCtx.mineBlocks(1, cbAddress); - assert.deepEqual(state, res); - }); + // Confirmed REVOKE means no one owns this name anymore + ownedNames.splice(ownedNames.indexOf(name), 1); - it('should fail to get name resource for non existent name', async () => { - const name = await nclient.execute('grindname', [10]); + const ns = await nclient.execute('getnameinfo', [name]); + assert.equal(ns.info.name, name); + assert.equal(ns.info.state, 'REVOKED'); + }); - const resource = await wallet.getResource(name); - assert.equal(resource, null); - }); + it('should require passphrase for auction TXs', async () => { + const passphrase = 'BitDNS!5353'; + await wclient.createWallet('lockedWallet', {passphrase}); + const lockedWallet = await wclient.wallet('lockedWallet'); - it('should create a renewal', async () => { - await wallet.createOpen({ - name: name + // Fast-forward through the default 60-second unlock timeout + async function lock() { + const wallet = await nodeCtx.wdb.get('lockedWallet'); + return wallet.lock(); + } + await lock(); + + // Wallet is created and encrypted + const info = await lockedWallet.getInfo(); + assert(info); + assert(info.master.encrypted); + + // Fund + const addr = await lockedWallet.createAddress('default'); + await nodeCtx.mineBlocks(10, addr.address); + await common.forValue(nodeCtx.wdb, 'height', nodeCtx.chain.height); + const bal = await lockedWallet.getBalance(); + assert(bal.confirmed > 0); + + // Open + await assert.rejects( + lockedWallet.createOpen({name}), + {message: 'No passphrase.'} + ); + + await lockedWallet.createOpen({name, passphrase}); + await lock(); + + await nodeCtx.mineBlocks(treeInterval + 1, cbAddress); + + // Bid + await assert.rejects( + lockedWallet.createBid({name, lockup: 1, bid: 1}), + {message: 'No passphrase.'} + ); + + // Send multiple bids, wallet remains unlocked for 60 seconds (all 3 bids) + await lockedWallet.createBid( + {name, lockup: 1000000, bid: 1000000, passphrase} + ); + await lockedWallet.createBid({name, lockup: 2000000, bid: 2000000}); + await lockedWallet.createBid({name, lockup: 3000000, bid: 3000000}); + await lock(); + + await nodeCtx.mineBlocks(biddingPeriod + 1, cbAddress); + + // Reveal + await assert.rejects( + lockedWallet.createReveal({name}), + {message: 'No passphrase.'} + ); + const revealAll = await lockedWallet.createReveal({name, passphrase}); + await lock(); + + // All 3 bids are revealed + const reveals = revealAll.outputs.filter( + output => output.covenant.type === types.REVEAL + ); + assert.equal(reveals.length, 3); + + await nodeCtx.mineBlocks(revealPeriod + 1, cbAddress); + + // Redeem all by not passing specific name + await assert.rejects( + lockedWallet.createRedeem(), + {message: 'No passphrase.'} + ); + const redeemAll = await lockedWallet.createRedeem({passphrase}); + await lock(); + + // Only 2 reveals are redeemed (because the third one is the winner) + const redeems = redeemAll.outputs.filter( + output => output.covenant.type === types.REDEEM + ); + assert.equal(redeems.length, 2); + + // Register + await assert.rejects( + lockedWallet.createUpdate({name, data: {records: []}}), + {message: 'No passphrase.'} + ); + const register = await lockedWallet.createUpdate( + {name, data: {records: []}, passphrase} + ); + await lock(); + + // Only 1 register, only 1 winner! + const registers = register.outputs.filter( + output => output.covenant.type === types.REGISTER + ); + assert.equal(registers.length, 1); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should get all wallet names', async () => { + const names = await wallet.getNames(); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + assert.equal(allNames.length, names.length); - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 + for (const {name} of names) { + assert(allNames.includes(name)); + } }); - await mineBlocks(biddingPeriod + 1, cbAddress); + it('should only get wallet-owned names', async () => { + const names = await wallet.getNames({ own: true }); + + assert.equal(names.length, ownedNames.length); - await wallet.createReveal({ - name: name + for (const {name} of names) { + assert(ownedNames.includes(name)); + } }); + }); - await mineBlocks(revealPeriod + 1, cbAddress); + describe('HTTP tx races (Integration)', function() { + const WNAME1 = 'racetest-1'; + const WNAME2 = 'racetest-2'; + const FUND_VALUE = 1e6; + const HARD_FEE = 1e4; + const NAMES = []; + const PASSPHRASE1 = 'racetest-passphrase-1'; + const PASSPHRASE2 = 'racetest-passphrase-2'; - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); + let rcwallet1, rcwallet2, wclient; + let w1addr; - await wallet.createUpdate({ - name: name, - data: { - records: [ - { - type: 'TXT', - txt: ['foobar'] - } - ] + const checkDoubleSpends = (txs) => { + const spentCoins = new BufferSet(); + + for (const tx of txs) { + for (const input of tx.inputs) { + const key = input.prevout.toKey(); + + if (spentCoins.has(key)) + throw new Error(`Input ${input.prevout.format()} is already spent.`); + + spentCoins.add(key); + } + } + }; + + const wMineBlocks = async (n = 1) => { + const forConnect = common.forEvent(nodeCtx.wdb, 'block connect', n); + await nodeCtx.mineBlocks(n, w1addr); + await forConnect; + }; + + const fundNcoins = async (recvWallet, n, value = FUND_VALUE) => { + assert(typeof n === 'number'); + for (let i = 0; i < n; i++) { + const addr = (await recvWallet.createAddress('default')).address; + + await wallet.send({ + hardFee: HARD_FEE, + outputs: [{ + address: addr, + value: value + }] + }); } - }); - // mine up to the earliest point in which a renewal - // can be submitted, a treeInterval into the future - await mineBlocks(treeInterval + 1, cbAddress); + await wMineBlocks(1); + }; - const json = await wallet.createRenewal({ - name - }); + before(async () => { + await beforeAll(); - const updates = json.outputs.filter(({covenant}) => covenant.type === types.RENEW); - assert.equal(updates.length, 1); - }); + wclient = nodeCtx.wclient; + rcwallet1 = wclient.wallet(WNAME1); + rcwallet2 = wclient.wallet(WNAME2); - it('should create a transfer', async () => { - await wallet.createOpen({ - name: name - }); + w1addr = (await wallet.createAddress('default')).address; + const winfo1 = await wclient.createWallet(WNAME1, { + passphrase: PASSPHRASE1 + }); - await mineBlocks(treeInterval + 1, cbAddress); + const winfo2 = await wclient.createWallet(WNAME2, { + passphrase: PASSPHRASE2 + }); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + assert(winfo1); + assert(winfo2); - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 + // Fund primary wallet. + await wMineBlocks(5); }); - await mineBlocks(biddingPeriod + 1, cbAddress); + after(afterAll); - await wallet.createReveal({ - name: name + beforeEach(async () => { + await rcwallet1.lock(); + await rcwallet2.lock(); }); - await mineBlocks(revealPeriod + 1, cbAddress); + it('should fund 3 new transactions', async () => { + const promises = []; - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); + await fundNcoins(rcwallet1, 3); - await wallet.createUpdate({ - name: name, - data: { - records: [ - { - type: 'TXT', - txt: ['foobar'] - } - ] - } - }); + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); - await mineBlocks(treeInterval + 1, cbAddress); + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.send({ + passphrase: PASSPHRASE1, + subtractFee: true, + hardFee: HARD_FEE, + outputs: [{ + address: w1addr, + value: FUND_VALUE + }] + })); + } - const {receiveAddress} = await wallet.getAccount(accountTwo); + const results = await Promise.all(promises); + const txs = results.map(details => MTX.fromHex(details.tx)); + checkDoubleSpends(txs); - const json = await wallet.createTransfer({ - name, - address: receiveAddress - }); + await forMemTX; + await wMineBlocks(1); - const xfer = json.outputs.filter(({covenant}) => covenant.type === types.TRANSFER); - assert.equal(xfer.length, 1); - }); + const balance = await rcwallet1.getBalance(); - it('should create a finalize', async () => { - await wallet.createOpen({ - name: name + assert.strictEqual(balance.confirmed, 0); + assert.strictEqual(balance.unconfirmed, 0); + assert.strictEqual(balance.coin, 0); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should open 3 name auctions', async () => { + await fundNcoins(rcwallet1, 3); - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); - - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + for (let i = 0; i < 3; i++) + NAMES.push(rules.grindName(10, nodeCtx.chain.tip.height, network)); - await mineBlocks(biddingPeriod + 1, cbAddress); + const promises = []; - await wallet.createReveal({ - name: name - }); + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 4); - await mineBlocks(revealPeriod + 1, cbAddress); + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.createOpen({ + name: NAMES[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE + })); + } - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); + const results = await Promise.all(promises); + const txs = results.map(result => MTX.fromHex(result.hex)); + checkDoubleSpends(txs); + + // spend all money for now. + // Passphrase not necessary as the wallet is unlocked. + await rcwallet1.send({ + subtractFee: true, + outputs: [{ + value: (FUND_VALUE - HARD_FEE) * 3, + address: w1addr + }] + }); - await wallet.createUpdate({ - name: name, - data: { - records: [ - { - type: 'TXT', - txt: ['foobar'] - } - ] + await forMemTX; + await wMineBlocks(1); + + const balance = await rcwallet1.getBalance(); + // 3 opens (0 value) + assert.strictEqual(balance.coin, 3); + assert.strictEqual(balance.confirmed, 0); + }); + + it('should bid 3 times', async () => { + const promises = []; + + // 2 blocks. + await fundNcoins(rcwallet1, 3); + await fundNcoins(rcwallet2, 6); + + // this is 2 blocks ahead, but does not matter for this test. + await wMineBlocks(network.names.treeInterval + 1); + + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3 + 3 * 2); + + for (let i = 0; i < 3; i++) { + // make sure we use ALL coins, no NONE left. + // winner. + promises.push(rcwallet1.createBid({ + name: NAMES[i], + bid: HARD_FEE, + lockup: HARD_FEE, + passphrase: PASSPHRASE1, + hardFee: FUND_VALUE - HARD_FEE + })); + + // We want redeemer to not have enough funds + // to redeem the money back and has to use + // extra funds for it. + // + // ALSO We want to have enough redeems to + // do redeemAll and redeem. + for (let j = 0; j < 2; j++) { + promises.push(rcwallet2.createBid({ + name: NAMES[i], + bid: HARD_FEE - 1, + lockup: HARD_FEE - 1, + passphrase: PASSPHRASE2, + // lose all funds in fees. + hardFee: FUND_VALUE - HARD_FEE + })); + } } - }); - await mineBlocks(treeInterval + 1, cbAddress); + const results = await Promise.all(promises); + const txs = results.map(result => MTX.fromHex(result.hex)); + checkDoubleSpends(txs); - const {receiveAddress} = await wallet2.getAccount('default'); + await forMemTX; - await wallet.createTransfer({ - name, - address: receiveAddress - }); + await wMineBlocks(1); + const balance1 = await rcwallet1.getBalance(); + const balance2 = await rcwallet2.getBalance(); - await mineBlocks(transferLockup + 1, cbAddress); + // 3 opens and 3 bids (nothing extra) + assert.strictEqual(balance1.coin, 6); + assert.strictEqual(balance1.confirmed, HARD_FEE * 3); - const json = await wallet.createFinalize({ - name + // 6 bids (nothing extra) + assert.strictEqual(balance2.coin, 6); + assert.strictEqual(balance2.confirmed, (HARD_FEE - 1) * 6); }); - const final = json.outputs.filter(({covenant}) => covenant.type === types.FINALIZE); - assert.equal(final.length, 1); + it('should reveal 3 times and reveal all', async () => { + // Now we don't have fees to reveal. Fund these fees. + await fundNcoins(rcwallet1, 3, HARD_FEE); + await fundNcoins(rcwallet2, 1, HARD_FEE); - await mineBlocks(1, cbAddress); + const promises = []; - // Confirmed FINALIZE means this wallet is not the owner anymore! - ownedNames.splice(ownedNames.indexOf(name), 1); + await wMineBlocks(network.names.biddingPeriod); - const ns = await nclient.execute('getnameinfo', [name]); - const coin = await nclient.getCoin(ns.info.owner.hash, ns.info.owner.index); + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 4); - assert.equal(coin.address, receiveAddress); - }); + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.createReveal({ + name: NAMES[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE + })); + } - it('should create a cancel', async () => { - await wallet.createOpen({ - name: name - }); + // do reveal all + promises.push(rcwallet2.createReveal({ + passphrase: PASSPHRASE2, + hardFee: HARD_FEE + })); - await mineBlocks(treeInterval + 1, cbAddress); + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await wMineBlocks(1); - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + const balance1 = await rcwallet1.getBalance(); + + // 3 opens and 3 reveals + assert.strictEqual(balance1.coin, 6); + assert.strictEqual(balance1.confirmed, HARD_FEE * 3); - await mineBlocks(biddingPeriod + 1, cbAddress); + const balance2 = await rcwallet2.getBalance(); - await wallet.createReveal({ - name: name + // 6 reveals + assert.strictEqual(balance2.coin, 6); + assert.strictEqual(balance2.confirmed, (HARD_FEE - 1) * 6); + await wMineBlocks(network.names.revealPeriod); }); - await mineBlocks(revealPeriod + 1, cbAddress); + it('should register 3 times', async () => { + const promises = []; - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); + // We don't have funds to fund anything. + // Add 3 coins to pay for the fees and cause + // double spend. + await fundNcoins(rcwallet1, 3, HARD_FEE); - await wallet.createUpdate({ - name: name, - data: { - records: [ - { - type: 'TXT', - txt: ['foobar'] + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); + + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.createUpdate({ + name: NAMES[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE, + data: { + records: [ + { + type: 'TXT', + txt: ['foobar'] + } + ] } - ] + })); } + + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; + + await wMineBlocks(1); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should redeem 3 times and redeem all', async () => { + const promises = []; - const {receiveAddress} = await wallet.getAccount(accountTwo); + await fundNcoins(rcwallet2, 3, HARD_FEE); - await wallet.createTransfer({ - name, - address: receiveAddress - }); + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); - await mineBlocks(transferLockup + 1, cbAddress); + for (let i = 0; i < 2; i++) { + promises.push(rcwallet2.createRedeem({ + name: NAMES[i], + passphrase: PASSPHRASE2, + hardFee: HARD_FEE + })); + } - const json = await wallet.createCancel({name}); + promises.push(rcwallet2.createRedeem({ + hardFee: HARD_FEE + })); - const cancel = json.outputs.filter(({covenant}) => covenant.type === types.UPDATE); - assert.equal(cancel.length, 1); + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; + }); - await mineBlocks(1, cbAddress); + it('should renew 3 names', async () => { + const promises = []; - const ns = await nclient.execute('getnameinfo', [name]); - assert.equal(ns.info.name, name); + await wMineBlocks(network.names.treeInterval); + await fundNcoins(rcwallet1, 3, HARD_FEE); - const coin = await wallet.getCoin(ns.info.owner.hash, ns.info.owner.index); - assert.ok(coin); + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); - const keyInfo = await wallet.getKey(coin.address); - assert.ok(keyInfo); - }); + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.createRenewal({ + name: NAMES[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE + })); + } - it('should create a revoke', async () => { - await wallet.createOpen({ - name: name + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; + + await wMineBlocks(1); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should transfer 3 names', async () => { + const promises = []; - // Confirmed OPEN adds name to wallet's namemap - allNames.push(name); + await fundNcoins(rcwallet1, 3, HARD_FEE); - await wallet.createBid({ - name: name, - bid: 1000, - lockup: 2000 - }); + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); - await mineBlocks(biddingPeriod + 1, cbAddress); + const addrs = [ + (await rcwallet2.createAddress('default')).address, + (await rcwallet2.createAddress('default')).address, + (await rcwallet2.createAddress('default')).address + ]; + + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.createTransfer({ + name: NAMES[i], + address: addrs[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE + })); + } - await wallet.createReveal({ - name: name + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; + await wMineBlocks(1); }); - await mineBlocks(revealPeriod + 1, cbAddress); + it('should cancel 3 names', async () => { + const promises = []; - // Confirmed REVEAL with highest bid makes wallet the owner - ownedNames.push(name); + await fundNcoins(rcwallet1, 3, HARD_FEE); - await wallet.createUpdate({ - name: name, - data: { - records: [ - { - type: 'TXT', - txt: ['foobar'] - } - ] + const forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); + + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.createCancel({ + name: NAMES[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE + })); } + + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; + await wMineBlocks(1); }); - await mineBlocks(treeInterval + 1, cbAddress); + it('should finalize 3 names', async () => { + await fundNcoins(rcwallet1, 6, HARD_FEE); + + let forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); - const json = await wallet.createRevoke({name}); + const addrs = [ + (await rcwallet2.createAddress('default')).address, + (await rcwallet2.createAddress('default')).address, + (await rcwallet2.createAddress('default')).address + ]; - const final = json.outputs.filter(({covenant}) => covenant.type === types.REVOKE); - assert.equal(final.length, 1); + for (let i = 0; i < 3; i++) { + await rcwallet1.createTransfer({ + name: NAMES[i], + address: addrs[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE + }); + } - await mineBlocks(1, cbAddress); + await forMemTX; + await wMineBlocks(network.names.transferLockup); - // Confirmed REVOKE means no one owns this name anymore - ownedNames.splice(ownedNames.indexOf(name), 1); + // Now we finalize all. + const promises = []; - const ns = await nclient.execute('getnameinfo', [name]); - assert.equal(ns.info.name, name); - assert.equal(ns.info.state, 'REVOKED'); - }); + forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); - it('should require passphrase for auction TXs', async () => { - const passphrase = 'BitDNS!5353'; - await wclient.createWallet('lockedWallet', {passphrase}); - const lockedWallet = await wclient.wallet('lockedWallet'); - - // Fast-forward through the default 60-second unlock timeout - async function lock() { - const wallet = await wdb.get('lockedWallet'); - return wallet.lock(); - } - await lock(); - - // Wallet is created and encrypted - const info = await lockedWallet.getInfo(); - assert(info); - assert(info.master.encrypted); - - // Fund - const addr = await lockedWallet.createAddress('default'); - await mineBlocks(10, addr.address); - await common.forValue(wdb, 'height', node.chain.height); - const bal = await lockedWallet.getBalance(); - assert(bal.confirmed > 0); - - // Open - await assert.rejects( - lockedWallet.createOpen({name}), - {message: 'No passphrase.'} - ); - - await lockedWallet.createOpen({name, passphrase}); - await lock(); - - await mineBlocks(treeInterval + 1, cbAddress); - - // Bid - await assert.rejects( - lockedWallet.createBid({name, lockup: 1, bid: 1}), - {message: 'No passphrase.'} - ); - - // Send multiple bids, wallet remains unlocked for 60 seconds (all 3 bids) - await lockedWallet.createBid( - {name, lockup: 1000000, bid: 1000000, passphrase} - ); - await lockedWallet.createBid({name, lockup: 2000000, bid: 2000000}); - await lockedWallet.createBid({name, lockup: 3000000, bid: 3000000}); - await lock(); - - await mineBlocks(biddingPeriod + 1, cbAddress); - - // Reveal - await assert.rejects( - lockedWallet.createReveal({name}), - {message: 'No passphrase.'} - ); - const revealAll = await lockedWallet.createReveal({name, passphrase}); - await lock(); - - // All 3 bids are revealed - const reveals = revealAll.outputs.filter( - output => output.covenant.type === types.REVEAL - ); - assert.equal(reveals.length, 3); - - await mineBlocks(revealPeriod + 1, cbAddress); - - // Redeem all by not passing specific name - await assert.rejects( - lockedWallet.createRedeem(), - {message: 'No passphrase.'} - ); - const redeemAll = await lockedWallet.createRedeem({passphrase}); - await lock(); - - // Only 2 reveals are redeemed (because the third one is the winner) - const redeems = redeemAll.outputs.filter( - output => output.covenant.type === types.REDEEM - ); - assert.equal(redeems.length, 2); - - // Register - await assert.rejects( - lockedWallet.createUpdate({name, data: {records: []}}), - {message: 'No passphrase.'} - ); - const register = await lockedWallet.createUpdate( - {name, data: {records: []}, passphrase} - ); - await lock(); - - // Only 1 register, only 1 winner! - const registers = register.outputs.filter( - output => output.covenant.type === types.REGISTER - ); - assert.equal(registers.length, 1); - }); + for (let i = 0; i < 3; i++) { + promises.push(rcwallet1.createFinalize({ + name: NAMES[i], + passphrase: PASSPHRASE1, + hardFee: HARD_FEE + })); + } - it('should get all wallet names', async () => { - const names = await wallet.getNames(); + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; - assert.equal(allNames.length, names.length); + await wMineBlocks(1); + }); - for (const {name} of names) { - assert(allNames.includes(name)); - } - }); + it('should revoke 3 names', async () => { + // send them back + await fundNcoins(rcwallet2, 6, HARD_FEE); + + let forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); + + const addrs = [ + (await rcwallet1.createAddress('default')).address, + (await rcwallet1.createAddress('default')).address, + (await rcwallet1.createAddress('default')).address + ]; - it('should only get wallet-owned names', async () => { - // TODO: convert to using hs-client method - // when wallet.getNames() allows `options` - const names = await wallet.client.get(`/wallet/${wallet.id}/name`, {own: true}); + for (let i = 0; i < 3; i++) { + await rcwallet2.createTransfer({ + name: NAMES[i], + address: addrs[i], + passphrase: PASSPHRASE2, + hardFee: HARD_FEE + }); + } + + await forMemTX; + await wMineBlocks(network.names.transferLockup); - assert.equal(names.length, ownedNames.length); + forMemTX = common.forEvent(nodeCtx.mempool, 'tx', 3); + const promises = []; + + for (let i = 0; i < 3; i++) { + promises.push(rcwallet2.createRevoke({ + name: NAMES[i], + passphrase: PASSPHRASE2, + hardFee: HARD_FEE + })); + } - for (const {name} of names) { - assert(ownedNames.includes(name)); - } + const results = await Promise.all(promises); + const txs = results.map(r => MTX.fromHex(r.hex)); + checkDoubleSpends(txs); + await forMemTX; + }); }); }); @@ -1550,18 +2330,6 @@ async function sleep(time) { return new Promise(resolve => setTimeout(resolve, time)); } -// take into account race conditions -async function mineBlocks(count, address) { - for (let i = 0; i < count; i++) { - const obj = { complete: false }; - node.once('block', () => { - obj.complete = true; - }); - await nclient.execute('generatetoaddress', [1, address]); - await common.forValue(obj, 'complete', true); - } -} - // create an OPEN output function openOutput(name, address) { const nameHash = rules.hashName(name); @@ -1570,10 +2338,7 @@ function openOutput(name, address) { const output = new Output(); output.address = Address.fromString(address); output.value = 0; - output.covenant.type = types.OPEN; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(0); - output.covenant.push(rawName); + output.covenant.setOpen(nameHash, rawName); return output; } diff --git a/test/wallet-importname-test.js b/test/wallet-importname-test.js index f6eb17e24..e2d4bfe13 100644 --- a/test/wallet-importname-test.js +++ b/test/wallet-importname-test.js @@ -5,7 +5,7 @@ const Network = require('../lib/protocol/network'); const FullNode = require('../lib/node/fullnode'); const Address = require('../lib/primitives/address'); const rules = require('../lib/covenants/rules'); -const {WalletClient} = require('hs-client'); +const WalletClient = require('../lib/client/wallet'); const {forValue} = require('./util/common'); const network = Network.get('regtest'); @@ -86,8 +86,8 @@ describe('Wallet Import Name', function() { const ns2 = await bob.getNameStateByName(name); assert(ns2 === null); - await alice.sendOpen(name, false); - await alice.sendOpen(wrongName, false); + await alice.sendOpen(name); + await alice.sendOpen(wrongName); await mineBlocks(network.names.treeInterval); await wdb.rescan(0); @@ -239,9 +239,9 @@ describe('Wallet Import Name', function() { let startHeight; it('should open and bid from Alice\'s wallet', async () => { - await alice.sendOpen(name1, false); - await alice.sendOpen(name2, false); - await alice.sendOpen(name3, false); + await alice.sendOpen(name1); + await alice.sendOpen(name2); + await alice.sendOpen(name3); startHeight = node.chain.tip.height; await mineBlocks(network.names.treeInterval + 1); diff --git a/test/wallet-importnonce-test.js b/test/wallet-importnonce-test.js new file mode 100644 index 000000000..85c6d2b0c --- /dev/null +++ b/test/wallet-importnonce-test.js @@ -0,0 +1,129 @@ +'use strict'; + +const assert = require('bsert'); +const FullNode = require('../lib/node/fullnode'); +const Network = require('../lib/protocol/network'); +const Address = require('../lib/primitives/address'); +const rules = require('../lib/covenants/rules'); + +/** @typedef {import('../lib/wallet/wallet')} Wallet */ + +const network = Network.get('regtest'); + +const node = new FullNode({ + memory: true, + network: network.type, + plugins: [require('../lib/wallet/plugin')] +}); + +const { wdb } = node.require('walletdb'); + +async function mineBlocks(n, addr) { + addr = addr ? addr : new Address().toString(network); + for (let i = 0; i < n; i++) { + const block = await node.miner.mineBlock(null, addr); + await node.chain.add(block); + } +} + +describe('Wallet Import Nonce', function () { + /** @type {Wallet} */ + let walletA; + + /** @type {Wallet} */ + let walletB; + + const NAME = rules.grindName(10, 1, network); + const NAMEHASH = rules.hashName(NAME); + const BIDS = [ + { value: 1e6, lockup: 2e6, addr: undefined }, // sendbid + { value: 2e6, lockup: 4e6, addr: undefined }, // -|sendbatch + { value: 4e6, lockup: 8e6, addr: undefined } // -|sendbatch + ]; + + before(async () => { + await node.ensure(); + await node.open(); + + // Both wallets have the same seed + walletA = await wdb.create(); + walletB = await wdb.create({ mnemonic: walletA.master.mnemonic }); + assert.bufferEqual(walletA.master.writeKey(), walletB.master.writeKey()); + }); + + after(async () => { + await node.close(); + }); + + it('should fund wallet', async () => { + await mineBlocks(2, await walletA.receiveAddress()); + }); + + it('should open an auction and advance to bidding period', async () => { + await walletA.sendOpen(NAME); + await mineBlocks(network.names.treeInterval + 1); + }); + + it('should bid with sendbid', async () => { + const bid = BIDS[0]; + + const bidTx = await walletA.sendBid(NAME, bid.value, bid.lockup); + + // Save address for importnonce later + bid.addr = bidTx.outputs[0].address; + }); + + it('should bid with sendbatch', async () => { + const batch = [ + ['BID', NAME, BIDS[1].value, BIDS[1].lockup], + ['BID', NAME, BIDS[2].value, BIDS[2].lockup] + ]; + + const bidTx = await walletA.sendBatch(batch); + + // Save address for importnonce later + for (const output of bidTx.outputs) { + if (!output.covenant.isBid()) + continue; + + const index = BIDS.findIndex(bid => bid.lockup === output.value); + BIDS[index].addr = output.address; + } + }); + + it('should verify bids were placed', async () => { + await mineBlocks(1); + const bidsA = await walletA.getBidsByName(NAME); + assert.strictEqual(bidsA.length, BIDS.length); + }); + + it('should not be known by other wallet', async () => { + const bidsB = await walletB.getBidsByName(NAME); + assert.strictEqual(bidsB.length, BIDS.length); + + for (const bid of bidsB) + assert.strictEqual(bid.value, -1); + }); + + it('should be imported by other wallet', async () => { + for (const bid of BIDS) + await walletB.generateBlinds(NAMEHASH, bid.addr, bid.value); + + const bidsB = await walletB.getBidsByName(NAME); + assert.strictEqual(bidsB.length, BIDS.length); + + // Ensure bids have correct true bid values + for (const bid of bidsB) { + const index = BIDS.findIndex(x => x.lockup === bid.lockup); + assert.strictEqual(BIDS[index].value, bid.value); + } + }); + + it('should reaveal all bids from other wallet', async () => { + await mineBlocks(network.names.biddingPeriod); + + const revealTx = await walletB.sendRevealAll(); + const revealOutputs = revealTx.outputs.filter(out => out.covenant.isReveal()); + assert.strictEqual(revealOutputs.length, BIDS.length); + }); +}); diff --git a/test/wallet-migration-test.js b/test/wallet-migration-test.js index e4f9f9b1a..89b347f70 100644 --- a/test/wallet-migration-test.js +++ b/test/wallet-migration-test.js @@ -2,9 +2,14 @@ const assert = require('bsert'); const fs = require('bfile'); +const random = require('bcrypto/lib/random'); const Network = require('../lib/protocol/network'); +const rules = require('../lib/covenants/rules'); +const Coin = require('../lib/primitives/coin'); const WalletDB = require('../lib/wallet/walletdb'); const layouts = require('../lib/wallet/layout'); +const TXDB = require('../lib/wallet/txdb'); +const {Credit} = TXDB; const WalletMigrator = require('../lib/wallet/migrations'); const {MigrateMigrations} = require('../lib/wallet/migrations'); const MigrationState = require('../lib/migrations/state'); @@ -13,7 +18,14 @@ const { types, oldLayout } = require('../lib/migrations/migrator'); -const {migrationError} = require('./util/migrations'); +const { + migrationError, + writeVersion, + getVersion, + checkVersion, + checkEntries, + fillEntries +} = require('./util/migrations'); const {rimraf, testdir} = require('./util/common'); const NETWORK = 'regtest'; @@ -85,6 +97,7 @@ describe('Wallet Migrations', function() { ldb = walletDB.db; WalletMigrator.migrations = mockMigrations; + await walletDB.open(); }); @@ -165,9 +178,9 @@ describe('Wallet Migrations', function() { it('should upgrade and run new migration with flag', async () => { const b = ldb.batch(); - b.del(layout.M.encode()); + b.del(layouts.wdb.M.encode()); b.put(oldLayout.M.encode(0), null); - writeVersion(b, 'wallet', 0); + writeVersion(b, layouts.wdb.V.encode(), 'wallet', 0); await b.write(); await walletDB.close(); @@ -175,8 +188,8 @@ describe('Wallet Migrations', function() { walletDB.version = 1; await walletDB.open(); - const versionData = await ldb.get(layout.V.encode()); - const version = getVersion(versionData, 'wallet'); + const versionData = await ldb.get(layouts.wdb.V.encode()); + const version = await getVersion(versionData, 'wallet'); assert.strictEqual(version, walletDB.version); const rawState = await ldb.get(layout.M.encode()); @@ -222,269 +235,173 @@ describe('Wallet Migrations', function() { }); }); - describe('Migrations #0 & #1', function() { - const location = testdir('migrate-wallet-0-1'); - const migrationsBAK = WalletMigrator.migrations; - const testMigrations = { - 0: WalletMigrator.MigrateMigrations, - 1: WalletMigrator.MigrateChangeAddress - }; + describe('Migrations #0 & #1 (data)', function() { + const location = testdir('migrate-wallet-0-1-int'); + const migrationBAK = WalletMigrator.migrations; + const data = require('./data/migrations/wallet-0-migrate-migrations.json'); + const Migration = WalletMigrator.MigrateMigrations; + const layout = Migration.layout(); const walletOptions = { prefix: location, memory: false, - network: network + network }; - let walletDB, ldb; + let wdb, ldb; beforeEach(async () => { + WalletMigrator.migrations = {}; await fs.mkdirp(location); - walletDB = new WalletDB(walletOptions); - walletDB.version = 1; - ldb = walletDB.db; - - WalletMigrator.migrations = testMigrations; + wdb = new WalletDB(walletOptions); + ldb = wdb.db; }); afterEach(async () => { - if (ldb.opened) - await ldb.close(); + WalletMigrator.migrations = migrationBAK; await rimraf(location); }); - after(() => { - WalletMigrator.migrations = migrationsBAK; - }); + for (let i = 0; i < data.cases.length; i++) { + it(`should migrate ${data.cases[i].description}`, async () => { + const before = data.cases[i].before; + const after = data.cases[i].after; + await ldb.open(); + const b = ldb.batch(); - it('should initialize fresh walletdb migration state', async () => { - await walletDB.open(); + for (const [key, value] of Object.entries(before)) { + const bkey = Buffer.from(key, 'hex'); + const bvalue = Buffer.from(value, 'hex'); - const rawState = await ldb.get(layout.M.encode()); - const state = MigrationState.decode(rawState); + b.put(bkey, bvalue); + } - assert.strictEqual(state.lastMigration, 1); - assert.strictEqual(state.skipped.length, 0); - assert.strictEqual(state.inProgress, false); + writeVersion(b, layouts.wdb.V.encode(), 'wallet', 0); - await walletDB.close(); - }); + await b.write(); + await ldb.close(); - it('should not migrate pre-old migration state w/o flag', async () => { - await walletDB.open(); - const b = ldb.batch(); - b.del(layout.M.encode()); - await b.write(); - await walletDB.close(); + WalletMigrator.migrations = { + 0: Migration, + 1: WalletMigrator.MigrateChangeAddress + }; - const expectedError = migrationError(WalletMigrator.migrations, [0, 1], - wdbFlagError(1)); + wdb.options.walletMigrate = 1; + wdb.version = 1; - await assert.rejects(async () => { - await walletDB.open(); - }, { - message: expectedError + await wdb.open(); + await checkVersion(ldb, layouts.wdb.V.encode(), 1); + await checkEntries(ldb, after); + const oldM = await ldb.get(layout.oldLayout.wdb.M.encode(0)); + assert.strictEqual(oldM, null); + await wdb.close(); }); + } + }); - const rawState = await ldb.get(layout.M.encode()); - const state = MigrationState.decode(rawState); - - assert.strictEqual(state.nextMigration, 0); - assert.strictEqual(state.lastMigration, -1); - assert.strictEqual(state.skipped.length, 0); - assert.strictEqual(state.inProgress, false); - await ldb.close(); - }); - - it('should migrate pre-old migration state with flag', async () => { - await walletDB.open(); - const b = ldb.batch(); - b.del(layout.M.encode()); - writeVersion(b, 'wallet', 0); - await b.write(); - await walletDB.close(); - - walletDB.options.walletMigrate = 1; - await walletDB.open(); - - const versionData = await ldb.get(layout.V.encode()); - const version = getVersion(versionData, 'wallet'); - assert.strictEqual(version, 1); - - const rawState = await ldb.get(layout.M.encode()); - const state = MigrationState.decode(rawState); - - assert.strictEqual(state.nextMigration, 2); - assert.strictEqual(state.lastMigration, 1); - assert.strictEqual(state.skipped.length, 0); - assert.strictEqual(state.inProgress, false); - await walletDB.close(); - }); - - it('should not migrate from last old migration state w/o flag', async () => { - await walletDB.open(); - - const b = ldb.batch(); - b.del(layout.M.encode()); - b.put(oldLayout.M.encode(0), null); - await b.write(); - await walletDB.close(); + describe('Migrate change address (data)', function() { + const location = testdir('wallet-change-data'); + const migrationsBAK = WalletMigrator.migrations; + const data = require('./data/migrations/wallet-1-change.json'); + const Migration = WalletMigrator.MigrateChangeAddress; - const expectedError = migrationError(WalletMigrator.migrations, [0], - wdbFlagError(1)); + const walletOptions = { + prefix: location, + memory: false, + network + }; - await assert.rejects(async () => { - await walletDB.open(); - }, { - message: expectedError - }); + let wdb, ldb; + before(async () => { + WalletMigrator.migrations = {}; + await fs.mkdirp(location); - const rawState = await ldb.get(layout.M.encode()); - const state = MigrationState.decode(rawState); - - assert.strictEqual(state.nextMigration, 0); - assert.strictEqual(state.lastMigration, -1); - assert.strictEqual(state.skipped.length, 0); - assert.strictEqual(state.inProgress, false); + wdb = new WalletDB(walletOptions); + ldb = wdb.db; + await ldb.open(); + await fillEntries(ldb, data.beforeOnly); + await fillEntries(ldb, data.before); await ldb.close(); }); - it('should not migrate from last old migration state with flag', async () => { - await walletDB.open(); - - const b = ldb.batch(); - b.del(layout.M.encode()); - b.put(oldLayout.M.encode(0), null); - await b.write(); - await walletDB.close(); - - walletDB.options.walletMigrate = 1; - await walletDB.open(); - const rawState = await ldb.get(layout.M.encode()); - const state = MigrationState.decode(rawState); + after(async () => { + WalletMigrator.migrations = migrationsBAK; + await rimraf(location); + }); - assert.strictEqual(state.nextMigration, 2); - assert.strictEqual(state.lastMigration, 1); - assert.strictEqual(state.skipped.length, 0); - assert.strictEqual(state.inProgress, false); - await walletDB.close(); + it('should have before entries', async () => { + wdb.version = 0; + try { + // We don't care that new wallet can't decode old data. + // It will still run migrations. + await wdb.open(); + } catch (e) { + ; + } + await checkVersion(ldb, layouts.wdb.V.encode(), 0); + await checkEntries(ldb, data.before); + await wdb.close(); }); - it('should not upgrade and run new migration w/o flag', async () => { + it('should enable wallet migration', () => { WalletMigrator.migrations = { - 0: MigrateMigrations, - 1: class extends AbstractMigration { - async check() { - return types.MIGRATE; - } - }, - 2: class extends AbstractMigration { - async check() { - return types.MIGRATE; - } - } + 0: Migration }; + }); - await walletDB.open(); - - const b = ldb.batch(); - b.del(layout.M.encode()); - b.put(oldLayout.M.encode(0), null); - await b.write(); - await walletDB.close(); - - const expectedError = migrationError(WalletMigrator.migrations, [0, 2], - wdbFlagError(2)); + it('should fail without migrate flag', async () => { + const expectedError = migrationError(WalletMigrator.migrations, [0], + wdbFlagError(0)); await assert.rejects(async () => { - await walletDB.open(); + await wdb.open(); }, { message: expectedError }); - const rawState = await ldb.get(layout.M.encode()); - const state = MigrationState.decode(rawState); - - assert.strictEqual(state.nextMigration, 0); - assert.strictEqual(state.lastMigration, -1); - assert.strictEqual(state.skipped.length, 0); - assert.strictEqual(state.inProgress, false); await ldb.close(); }); - it('should upgrade and run new migration with flag', async () => { - let migrated1 = false; - let migrated2 = false; - WalletMigrator.migrations = { - 0: MigrateMigrations, - 1: class extends AbstractMigration { - async check() { - return types.MIGRATE; - } - - async migrate() { - migrated1 = true; - } - }, - 2: class extends AbstractMigration { - async check() { - return types.MIGRATE; - } - - async migrate() { - migrated2 = true; - } - } - }; - - await walletDB.open(); - - const b = ldb.batch(); - b.del(layout.M.encode()); - b.put(oldLayout.M.encode(0), null); - writeVersion(b, 'wallet', 0); - await b.write(); - await walletDB.close(); + it('should migrate', async () => { + wdb.options.walletMigrate = 0; - walletDB.options.walletMigrate = 2; - await walletDB.open(); - - assert.strictEqual(migrated1, false); - assert.strictEqual(migrated2, true); - - const versionData = await ldb.get(layout.V.encode()); - const version = getVersion(versionData, 'wallet'); - assert.strictEqual(version, 1); - - const rawState = await ldb.get(layout.M.encode()); - const state = MigrationState.decode(rawState); - - assert.strictEqual(state.nextMigration, 3); - assert.strictEqual(state.lastMigration, 2); - assert.strictEqual(state.skipped.length, 0); - assert.strictEqual(state.inProgress, false); - await walletDB.close(); + try { + // We don't care that new wallet can't decode old data. + // It will still run migrations. + await wdb.open(); + } catch (e) { + ; + } + await checkVersion(ldb, layouts.wdb.V.encode(), 0); + await checkEntries(ldb, data.after); + await wdb.close(); }); }); - describe('Migrate change address (integration)', function() { - const location = testdir('wallet-change'); + describe('Migrate account lookahead (data)', function() { + const location = testdir('wallet-lookahead-data'); const migrationsBAK = WalletMigrator.migrations; + const data = require('./data/migrations/wallet-2-account-lookahead.json'); + const Migration = WalletMigrator.MigrateAccountLookahead; const walletOptions = { prefix: location, memory: false, - network: network + network }; - const ADD_CHANGE_DEPTH = 10; - - let walletDB, ldb; - const missingAddrs = []; + let wdb, ldb; before(async () => { WalletMigrator.migrations = {}; await fs.mkdirp(location); + + wdb = new WalletDB(walletOptions); + ldb = wdb.db; + + await ldb.open(); + await fillEntries(ldb, data.before); + await ldb.close(); }); after(async () => { @@ -492,59 +409,32 @@ describe('Wallet Migrations', function() { await rimraf(location); }); - beforeEach(async () => { - walletDB = new WalletDB(walletOptions); - ldb = walletDB.db; - }); - - afterEach(async () => { - if (ldb.opened) - await ldb.close(); - }); - - it('should set incorrect walletdb state', async () => { - await walletDB.open(); - - const wallet = walletDB.primary; - const account = await wallet.getAccount(0); - - for (let i = 0; i < ADD_CHANGE_DEPTH; i++) { - const {changeDepth, lookahead} = account; - const changeKey = account.deriveChange(changeDepth + lookahead); - missingAddrs.push(changeKey.getAddress()); - account.changeDepth += 1; - } - - const b = ldb.batch(); - walletDB.saveAccount(b, account); - await b.write(); - await walletDB.close(); - }); - - it('should have missing addresses', async () => { - await walletDB.open(); - const wallet = walletDB.primary; - - for (const addr of missingAddrs) { - const hasAddr = await wallet.hasAddress(addr); - assert.strictEqual(hasAddr, false); + it('should have before entries', async () => { + wdb.version = 1; + try { + // We don't care that new wallet can't decode old data. + // It will still run migrations. + await wdb.open(); + } catch (e) { + ; } - - await walletDB.close(); + await checkVersion(ldb, layouts.wdb.V.encode(), 1); + await checkEntries(ldb, data.before); + await wdb.close(); }); - it('should enable wallet change migration', () => { + it('should enable wallet migration', () => { WalletMigrator.migrations = { - 0: WalletMigrator.MigrateChangeAddress + 0: Migration }; }); it('should fail without migrate flag', async () => { const expectedError = migrationError(WalletMigrator.migrations, [0], - wdbFlagError(0)); + wdbFlagError(0)); await assert.rejects(async () => { - await walletDB.open(); + await wdb.open(); }, { message: expectedError }); @@ -552,30 +442,24 @@ describe('Wallet Migrations', function() { await ldb.close(); }); - it('should migrate with migrate flag', async () => { - walletDB.options.walletMigrate = 0; + it('should migrate', async () => { + wdb.options.walletMigrate = 0; - let rescan = false; - walletDB.scan = () => { - rescan = true; - }; - - await walletDB.open(); - const wallet = walletDB.primary; - - for (const addr of missingAddrs) { - const hasAddr = await wallet.hasAddress(addr); - assert.strictEqual(hasAddr, true); + try { + // We don't care that new wallet can't decode old data. + // It will still run migrations. + await wdb.open(); + } catch (e) { + ; } - - assert.strictEqual(rescan, true); - - await walletDB.close(); + await checkVersion(ldb, layouts.wdb.V.encode(), 2); + await checkEntries(ldb, data.after); + await wdb.close(); }); }); - describe('Mirate account lookahead (integration)', function () { - const location = testdir('wallet-change'); + describe('Migrate account lookahead (integration)', function () { + const location = testdir('wallet-lookahead'); const migrationsBAK = WalletMigrator.migrations; const TEST_LOOKAHEAD = 150; @@ -688,6 +572,7 @@ describe('Wallet Migrations', function() { }; walletDB.options.walletMigrate = 0; + walletDB.version = 2; await walletDB.open(); const wallet = walletDB.primary; @@ -697,25 +582,338 @@ describe('Wallet Migrations', function() { await walletDB.close(); }); }); -}); -function writeVersion(b, name, version) { - const value = Buffer.alloc(name.length + 4); + describe('Migrate txdb balances (integration)', function() { + const location = testdir('walet-txdb-refresh'); + const migrationsBAK = WalletMigrator.migrations; - value.write(name, 0, 'ascii'); - value.writeUInt32LE(version, name.length); + const walletOptions = { + prefix: location, + memory: false, + network + }; - b.put(layout.V.encode(), value); -} + const balanceEquals = (balance, expected) => { + assert.strictEqual(balance.tx, expected.tx); + assert.strictEqual(balance.coin, expected.coin); + assert.strictEqual(balance.unconfirmed, expected.unconfirmed); + assert.strictEqual(balance.confirmed, expected.confirmed); + assert.strictEqual(balance.ulocked, expected.ulocked); + assert.strictEqual(balance.clocked, expected.clocked); + }; -function getVersion(data, name) { - const error = 'version mismatch'; + let walletDB, ldb; + before(async () => { + WalletMigrator.migrations = {}; + await fs.mkdirp(location); + }); - if (data.length !== name.length + 4) - throw new Error(error); + after(async () => { + WalletMigrator.migrations = migrationsBAK; + await rimraf(location); + }); - if (data.toString('ascii', 0, name.length) !== name) - throw new Error(error); + beforeEach(async () => { + walletDB = new WalletDB(walletOptions); + ldb = walletDB.db; + }); - return data.readUInt32LE(name.length); -} + afterEach(async () => { + if (ldb.opened) + await ldb.close(); + }); + + it('should write some coins w/o updating balance', async () => { + // generate credits for the first 10 addresses stored on initialization. + await walletDB.open(); + + const wallet = walletDB.primary; + + await wallet.createAccount({ + name: 'alt' + }); + + const randomCoin = (options) => { + const coin = new Coin({ + version: 1, + coinbase: false, + hash: random.randomBytes(32), + index: 0, + ...options + }); + + if (options.covenantType != null) + coin.covenant.type = options.covenantType; + + return coin; + }; + + const coins = []; + const spentCoins = []; + + const addCoin = (addr, spent, confirmed, bid) => { + const list = spent ? spentCoins : coins; + + const coin = randomCoin({ + value: 1e6, + address: addr.getAddress(), + height: confirmed ? 1 : -1 + }); + + if (bid) + coin.covenant.type = rules.types.BID; + + list.push(coin); + }; + + for (let i = 0; i < 5; i++) { + const addr0 = await wallet.createReceive(0); + const addr1 = await wallet.createReceive(1); + + // 5 NONE coins to default account, of each type: + // confirmed spent, + // unconfirmed spent, + // unconfirmed unspent, + // confirmed unspent + + // confirmed += 1e6 * 5; + // unconfirmed += 1e6 * 5; + // coin += 5; + addCoin(addr0, false, true); + + // confirmed += 1e6 * 5; + // unconfirmed += 0; + // coin += 0; + addCoin(addr0, true, true); + + // confirmed += 0; + // unconfirmed += 0; + // coin += 0; + addCoin(addr0, true, false); + + // confirmed += 0; + // unconfirmed += 1e6 * 5; + // coin += 5; + addCoin(addr0, false, false); + + // 5 BID coins to alt account, of each type: + // confirmed spent, + // unconfirmed spent, + // unconfirmed unspent, + // confirmed unspent + + // confirmed += 1e6 * 5; + // unconfirmed += 1e6 * 5; + // coin += 5; + // locked += 1e6 * 5; + // unlocked += 1e6 * 5; + addCoin(addr1, false, true, true); + + // confirmed += 1e6 * 5; + // unconfirmed += 0; + // coin += 0; + // locked += 1e6 * 5; + // unlocked += 0; + addCoin(addr1, true, true, true); + + // confirmed += 0; + // unconfirmed += 0; + // coin += 0; + // locked += 0; + // unlocked += 0; + addCoin(addr1, true, false, true); + + // confirmed += 0; + // unconfirmed += 1e6 * 5; + // coin += 5; + // locked += 0; + // unlocked += 1e6 * 5; + addCoin(addr1, false, false, true); + } + + const batch = wallet.txdb.bucket.batch(); + for (const coin of coins) { + const path = await wallet.txdb.getPath(coin); + const credit = new Credit(coin); + await wallet.txdb.saveCredit(batch, credit, path); + } + + for (const coin of spentCoins) { + const path = await wallet.txdb.getPath(coin); + const credit = new Credit(coin, true); + await wallet.txdb.saveCredit(batch, credit, path); + } + + await batch.write(); + + await walletDB.close(); + }); + + it('should have incorrect balance before migration', async () => { + await walletDB.open(); + + const wallet = walletDB.primary; + const balance = await wallet.getBalance(-1); + const defBalance = await wallet.getBalance(0); + const altBalance = await wallet.getBalance(1); + + const empty = { + tx: 0, + coin: 0, + unconfirmed: 0, + confirmed: 0, + ulocked: 0, + clocked: 0 + }; + + balanceEquals(balance, empty); + balanceEquals(defBalance, empty); + balanceEquals(altBalance, empty); + + await walletDB.close(); + }); + + it('should enable txdb migration', () => { + WalletMigrator.migrations = { + 0: WalletMigrator.MigrateTXDBBalances + }; + }); + + it('should migrate', async () => { + walletDB.options.walletMigrate = 0; + + await walletDB.open(); + + const wallet = walletDB.primary; + const balance = await wallet.getBalance(-1); + const defBalance = await wallet.getBalance(0); + const altBalance = await wallet.getBalance(1); + + const expectedDefault = { + tx: 0, + coin: 10, + + confirmed: 10e6, + unconfirmed: 10e6, + + ulocked: 0, + clocked: 0 + }; + + const expectedAlt = { + tx: 0, + coin: 10, + + confirmed: 10e6, + unconfirmed: 10e6, + + ulocked: 10e6, + clocked: 10e6 + }; + + const expecteBalance = { + tx: expectedDefault.tx + expectedAlt.tx, + coin: expectedDefault.coin + expectedAlt.coin, + + confirmed: expectedDefault.confirmed + expectedAlt.confirmed, + unconfirmed: expectedDefault.unconfirmed + expectedAlt.unconfirmed, + + ulocked: expectedDefault.ulocked + expectedAlt.ulocked, + clocked: expectedDefault.clocked + expectedAlt.clocked + }; + + balanceEquals(defBalance, expectedDefault); + balanceEquals(altBalance, expectedAlt); + balanceEquals(balance, expecteBalance); + + await walletDB.close(); + }); + }); + + describe('Bid Reveal Migration (integration)', function() { + const location = testdir('wallet-bid-reveal'); + const migrationsBAK = WalletMigrator.migrations; + const data = require('./data/migrations/wallet-4-bid-reveal.json'); + const Migration = WalletMigrator.MigrateBidRevealEntries; + const layout = Migration.layout(); + + const walletOptions = { + prefix: location, + memory: false, + network + }; + + let walletDB, ldb; + before(async () => { + WalletMigrator.migrations = {}; + await fs.mkdirp(location); + + walletDB = new WalletDB(walletOptions); + ldb = walletDB.db; + + await ldb.open(); + + const b = ldb.batch(); + for (const [key, value] of Object.entries(data.before)) { + const bkey = Buffer.from(key, 'hex'); + const bvalue = Buffer.from(value, 'hex'); + + b.put(bkey, bvalue); + } + await b.write(); + + await ldb.close(); + }); + + after(async () => { + WalletMigrator.migrations = migrationsBAK; + await rimraf(location); + }); + + beforeEach(async () => { + walletDB = new WalletDB(walletOptions); + ldb = walletDB.db; + }); + + afterEach(async () => { + if (ldb.opened) + await ldb.close(); + }); + + it('should have before entries', async () => { + walletDB.version = 2; + await walletDB.open(); + await checkVersion(ldb, layout.wdb.V.encode(), 2); + await checkEntries(ldb, data.before); + await walletDB.close(); + }); + + it('should enable wallet migration', () => { + WalletMigrator.migrations = { + 0: Migration + }; + }); + + it('should fail without migrate flag', async () => { + const expectedError = migrationError(WalletMigrator.migrations, [0], + wdbFlagError(0)); + + await assert.rejects(async () => { + await walletDB.open(); + }, { + message: expectedError + }); + + await ldb.close(); + }); + + it('should migrate', async () => { + walletDB.options.walletMigrate = 0; + + await walletDB.open(); + // check we have migrated entries. + await checkEntries(ldb, data.after); + await walletDB.close(); + }); + }); +}); diff --git a/test/wallet-namestate-rescan-test.js b/test/wallet-namestate-rescan-test.js new file mode 100644 index 000000000..fbb0a32a3 --- /dev/null +++ b/test/wallet-namestate-rescan-test.js @@ -0,0 +1,650 @@ +'use strict'; + +const assert = require('bsert'); +const FullNode = require('../lib/node/fullnode'); +const MemWallet = require('./util/memwallet'); +const Network = require('../lib/protocol/network'); +const Address = require('../lib/primitives/address'); +const rules = require('../lib/covenants/rules'); +const {Resource} = require('../lib/dns/resource'); +const {forValue} = require('./util/common'); + +const network = Network.get('regtest'); + +const { + treeInterval, + biddingPeriod, + revealPeriod, + transferLockup +} = network.names; + +const GNAME_SIZE = 10; + +describe('Wallet rescan with namestate transitions', function() { + let node, wdb; + let alice, aliceAddr; + let bob, bobAddr; + + async function mineBlocks(n, addr) { + addr = addr ? addr : new Address().toString('regtest'); + const blocks = []; + for (let i = 0; i < n; i++) { + const block = await node.miner.mineBlock(null, addr); + await node.chain.add(block); + blocks.push(block); + } + + return blocks; + } + + async function sendTXs() { + const aliceTX = await alice.send({ + outputs: [{ + address: aliceAddr, + value: 20000 + }] + }); + alice.addTX(aliceTX.toTX()); + await node.mempool.addTX(aliceTX.toTX()); + await bob.send({ + outputs: [{ + address: bobAddr, + value: 20000 + }] + }); + } + + const beforeAll = async () => { + node = new FullNode({ + network: network.type, + memory: true, + plugins: [require('../lib/wallet/plugin')] + }); + + node.on('error', (err) => { + assert(false, err); + }); + + wdb = node.require('walletdb').wdb; + + alice = new MemWallet({ network }); + aliceAddr = alice.getAddress(); + + // Connect MemWallet to chain as minimally as possible + node.chain.on('connect', (entry, block) => { + alice.addBlock(entry, block.txs); + }); + + alice.getNameStatus = async (nameHash) => { + assert(Buffer.isBuffer(nameHash)); + const height = node.chain.height + 1; + return node.chain.db.getNameStatus(nameHash, height); + }; + + await node.open(); + bob = await wdb.create(); + bobAddr = await bob.receiveAddress(); + }; + + const afterAll = async () => { + await node.close(); + }; + + describe('Only sends OPEN', function() { + const NAME = rules.grindName(GNAME_SIZE, 4, network); + let aliceFinalizeHash; + + before(beforeAll); + after(afterAll); + + it('should fund wallets', async () => { + const blocks = 10; + await mineBlocks(blocks, aliceAddr); + await mineBlocks(blocks, bobAddr); + + const bobBal = await bob.getBalance(); + assert.strictEqual(bobBal.confirmed, blocks * 2000 * 1e6); + assert.strictEqual(alice.balance, blocks * 2000 * 1e6); + }); + + it('should run auction', async () => { + // Poor Bob, all he does is send an OPEN but his wallet will + // watch all the other activity including TRANSFERS for this name + await bob.sendOpen(NAME); + // Scatter unrelated TXs throughout the test. + // This will ensure that txdb.removeBlock() removes TXs + // in the reverse order from when they were added + await sendTXs(); + const openBlocks = await mineBlocks(1); + // Coinbase plus open + assert.strictEqual(openBlocks[0].txs.length, 4); + + // Advance to bidding phase + await mineBlocks(treeInterval); + await forValue(alice, 'height', node.chain.height); + + // Alice sends only bid + await sendTXs(); + const aliceBid = await alice.createBid(NAME, 20000, 20000); + await node.mempool.addTX(aliceBid.toTX()); + const bidBlocks = await mineBlocks(1); + assert.strictEqual(bidBlocks[0].txs.length, 4); + + // Advance to reveal phase + await mineBlocks(biddingPeriod); + await sendTXs(); + const aliceReveal = await alice.createReveal(NAME); + await node.mempool.addTX(aliceReveal.toTX()); + const revealBlocks = await mineBlocks(1); + assert.strictEqual(revealBlocks[0].txs.length, 4); + + // Close auction + await mineBlocks(revealPeriod); + + // Alice registers + await sendTXs(); + const aliceRegister = await alice.createRegister( + NAME, + Resource.fromJSON({records:[]}) + ); + await node.mempool.addTX(aliceRegister.toTX()); + + const registerBlocks = await mineBlocks(1); + assert.strictEqual(registerBlocks[0].txs.length, 4); + }); + + it('should get namestate', async () => { + const ns = await bob.getNameStateByName(NAME); + // Bob has the namestate + assert(ns); + + // Bob is not the name owner + const {hash, index} = ns.owner; + const coin = await bob.getCoin(hash, index); + assert.strictEqual(coin, null); + + // Name is not in mid-TRANSFER + assert.strictEqual(ns.transfer, 0); + }); + + it('should process TRANSFER', async () => { + // Alice transfers the name to her own address + await sendTXs(); + const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); + await node.mempool.addTX(aliceTransfer.toTX()); + const transferBlocks = await mineBlocks(1); + assert.strictEqual(transferBlocks[0].txs.length, 4); + + // Bob detects the TRANSFER even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.transfer, node.chain.height); + + // Bob's wallet has not indexed the TRANSFER + const bobTransfer = await bob.getTX(aliceTransfer.hash()); + assert.strictEqual(bobTransfer, null); + }); + + it('should fully rescan', async () => { + // Complete chain rescan + await wdb.rescan(0); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.transfer, node.chain.height); + }); + + it('should process FINALIZE', async () => { + await mineBlocks(transferLockup); + + // Alice finalizes the name + await sendTXs(); + const aliceFinalize = await alice.createFinalize(NAME); + await node.mempool.addTX(aliceFinalize.toTX()); + const finalizeBlocks = await mineBlocks(1); + assert.strictEqual(finalizeBlocks[0].txs.length, 4); + + aliceFinalizeHash = aliceFinalize.hash(); + + // Bob detects the FINALIZE even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); + + // Bob's wallet has not indexed the FINALIZE + const bobFinalize = await bob.getTX(aliceFinalize.hash()); + assert.strictEqual(bobFinalize, null); + }); + + it('should fully rescan', async () => { + // Complete chain rescan + await wdb.rescan(0); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); + }); + + it('should process TRANSFER (again)', async () => { + // Alice transfers the name to her own address + await sendTXs(); + const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); + await node.mempool.addTX(aliceTransfer.toTX()); + const transferBlocks = await mineBlocks(1); + assert.strictEqual(transferBlocks[0].txs.length, 4); + + // Bob detects the TRANSFER even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.transfer, node.chain.height); + + // Bob's wallet has not indexed the TRANSFER + const bobTransfer = await bob.getTX(aliceTransfer.hash()); + assert.strictEqual(bobTransfer, null); + }); + + it('should process REVOKE', async () => { + // Alice revokes the name + await sendTXs(); + const aliceRevoke = await alice.createRevoke(NAME); + await node.mempool.addTX(aliceRevoke.toTX()); + const revokeBlocks = await mineBlocks(1); + assert.strictEqual(revokeBlocks[0].txs.length, 4); + + // Bob detects the REVOKE even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.revoked, node.chain.height); + + // Bob's wallet has not indexed the REVOKE + const bobTransfer = await bob.getTX(aliceRevoke.hash()); + assert.strictEqual(bobTransfer, null); + }); + + it('should fully rescan', async () => { + // Complete chain rescan + await wdb.rescan(0); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.revoked, node.chain.height); + }); + }); + + describe('Bids, loses, shallow rescan', function() { + const NAME = rules.grindName(GNAME_SIZE, 4, network); + let aliceFinalizeHash; + let bidBlockHash; + + before(beforeAll); + after(afterAll); + + it('should fund wallets', async () => { + const blocks = 10; + await mineBlocks(blocks, aliceAddr); + await mineBlocks(blocks, bobAddr); + + const bobBal = await bob.getBalance(); + assert.strictEqual(bobBal.confirmed, blocks * 2000 * 1e6); + assert.strictEqual(alice.balance, blocks * 2000 * 1e6); + }); + + it('should run auction', async () => { + // Alice opens + await sendTXs(); + const aliceOpen = await alice.createOpen(NAME); + await node.mempool.addTX(aliceOpen.toTX()); + const openBlocks = await mineBlocks(1); + // Coinbase plus open + assert.strictEqual(openBlocks[0].txs.length, 4); + + // Advance to bidding phase + await mineBlocks(treeInterval); + await forValue(alice, 'height', node.chain.height); + + // Poor Bob, all he does is send one (losing) bid but his wallet will + // watch all the other activity including TRANSFERS for this name + await bob.sendBid(NAME, 10000, 10000); + + // Alice sends winning bid + await sendTXs(); + const aliceBid = await alice.createBid(NAME, 20000, 20000); + await node.mempool.addTX(aliceBid.toTX()); + const bidBlocks = await mineBlocks(1); + assert.strictEqual(bidBlocks[0].txs.length, 5); + + bidBlockHash = bidBlocks[0].hash(); + + // Advance to reveal phase + await mineBlocks(biddingPeriod); + await bob.sendReveal(NAME); + await sendTXs(); + const aliceReveal = await alice.createReveal(NAME); + await node.mempool.addTX(aliceReveal.toTX()); + const revealBlocks = await mineBlocks(1); + assert.strictEqual(revealBlocks[0].txs.length, 5); + + // Close auction + await mineBlocks(revealPeriod); + + // Alice registers + await sendTXs(); + const aliceRegister = await alice.createRegister( + NAME, + Resource.fromJSON({records:[]}) + ); + await node.mempool.addTX(aliceRegister.toTX()); + + const registerBlocks = await mineBlocks(1); + assert.strictEqual(registerBlocks[0].txs.length, 4); + }); + + it('should get namestate', async () => { + const ns = await bob.getNameStateByName(NAME); + // Bob has the namestate + assert(ns); + + // Bob is not the name owner + const {hash, index} = ns.owner; + const coin = await bob.getCoin(hash, index); + assert.strictEqual(coin, null); + + // Name is not in mid-TRANSFER + assert.strictEqual(ns.transfer, 0); + }); + + it('should process TRANSFER', async () => { + // Alice transfers the name to her own address + await sendTXs(); + const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); + await node.mempool.addTX(aliceTransfer.toTX()); + const transferBlocks = await mineBlocks(1); + assert.strictEqual(transferBlocks[0].txs.length, 4); + + // Bob detects the TRANSFER even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.transfer, node.chain.height); + + // Bob's wallet has indexed the TRANSFER + const bobTransfer = await bob.getTX(aliceTransfer.hash()); + assert.strictEqual(bobTransfer, null); + }); + + it('should fully rescan', async () => { + await wdb.rescan(0); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.transfer, node.chain.height); + }); + + it('should rescan since, but not including, the BIDs', async () => { + const bidBlock = await node.chain.getEntry(bidBlockHash); + await wdb.rescan(bidBlock.height); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.transfer, node.chain.height); + }); + + it('should process FINALIZE', async () => { + await mineBlocks(transferLockup); + + // Alice finalizes the name + await sendTXs(); + const aliceFinalize = await alice.createFinalize(NAME); + await node.mempool.addTX(aliceFinalize.toTX()); + const finalizeBlocks = await mineBlocks(1); + assert.strictEqual(finalizeBlocks[0].txs.length, 4); + + aliceFinalizeHash = aliceFinalize.hash(); + + // Bob detects the FINALIZE even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); + + // Bob's wallet has not indexed the FINALIZE + const bobFinalize = await bob.getTX(aliceFinalize.hash()); + assert.strictEqual(bobFinalize, null); + }); + + it('should fully rescan', async () => { + await wdb.rescan(0); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); + }); + + it('should rescan since, but not including, the BIDs', async () => { + const bidBlock = await node.chain.getEntry(bidBlockHash); + await wdb.rescan(bidBlock.height); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); + }); + + it('should process TRANSFER (again)', async () => { + // Alice transfers the name to her own address + await sendTXs(); + const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); + await node.mempool.addTX(aliceTransfer.toTX()); + const transferBlocks = await mineBlocks(1); + assert.strictEqual(transferBlocks[0].txs.length, 4); + + // Bob detects the TRANSFER even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.transfer, node.chain.height); + + // Bob's wallet has not indexed the TRANSFER + const bobTransfer = await bob.getTX(aliceTransfer.hash()); + assert.strictEqual(bobTransfer, null); + }); + + it('should process REVOKE', async () => { + // Alice revokes the name + await sendTXs(); + const aliceRevoke = await alice.createRevoke(NAME); + await node.mempool.addTX(aliceRevoke.toTX()); + const revokeBlocks = await mineBlocks(1); + assert.strictEqual(revokeBlocks[0].txs.length, 4); + + // Bob detects the REVOKE even though it doesn't involve him at all + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.revoked, node.chain.height); + + // Bob's wallet has not indexed the REVOKE + const bobTransfer = await bob.getTX(aliceRevoke.hash()); + assert.strictEqual(bobTransfer, null); + }); + + it('should fully rescan', async () => { + await wdb.rescan(0); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.revoked, node.chain.height); + }); + + it('should rescan since, but not including, the BIDs', async () => { + const bidBlock = await node.chain.getEntry(bidBlockHash); + await wdb.rescan(bidBlock.height); + await forValue(wdb, 'height', node.chain.height); + + // No change + const ns = await bob.getNameStateByName(NAME); + assert(ns); + assert.strictEqual(ns.revoked, node.chain.height); + }); + }); + + describe('Restore from seed', function() { + let wallet1, wallet2, wallet3; + let addr1; + let heightBeforeReveal; + + const name = rules.grindName(4, 4, network); + + before(beforeAll); + after(afterAll); + + it('should create and fund wallet 1', async () => { + wallet1 = await wdb.create(); + addr1 = (await wallet1.receiveAddress()).toString(network); + await node.rpc.generateToAddress([10, addr1]); + }); + + it('should open and bid from wallet 1', async () => { + await wallet1.sendOpen(name); + await node.rpc.generateToAddress([treeInterval + 1, addr1]); + await wallet1.sendBid(name, 1e6, 1e6); + await node.rpc.generateToAddress([1, addr1]); + }); + + it('should restore wallet 1 from seed into wallet 2', async () => { + const {mnemonic} = wallet1.master; + wallet2 = await wdb.create({mnemonic}); + + assert.strictEqual( + wallet1.master.key.xprivkey, + wallet2.master.key.xprivkey + ); + + // Sanity check + const bal1 = await wallet1.getBalance(); + assert(bal1.unconfirmed > 0); + assert(bal1.tx > 0); + assert((await wallet1.getBids()).length); + + // Wallet 2 has no history + const bal2 = await wallet2.getBalance(); + assert.strictEqual(bal2.unconfirmed, 0); + assert.strictEqual(bal2.tx, 0); + assert(!(await wallet2.getBids()).length); + }); + + it('should rescan wallet 2', async () => { + await wdb.rescan(0); + const bal2 = await wallet2.getBalance(); + assert(bal2.unconfirmed > 0); + assert(bal2.tx > 0); + assert((await wallet2.getBids()).length); + }); + + it('should bid from wallet 2', async () => { + await wallet2.sendBid(name, 2e6, 2e6); + await node.rpc.generateToAddress([1, addr1]); + }); + + it('should not have all blinds in either wallet', async () => { + const bids1 = await wallet1.getBids(); + assert.strictEqual(bids1.length, 2); + for (const bid of bids1) { + if (bid.lockup === 1e6) + assert(bid.value === 1e6); + else + assert(bid.value === -1); // unknown + } + + const bids2 = await wallet2.getBids(); + assert.strictEqual(bids2.length, 2); + for (const bid of bids2) { + if (bid.lockup === 2e6) + assert(bid.value === 2e6); + else + assert(bid.value === -1); // unknown + } + }); + + it('should reveal from each wallet', async () => { + await node.rpc.generateToAddress([biddingPeriod, addr1]); + + heightBeforeReveal = node.chain.height; + + // Wallet 1 only knows blind for one of the bids + const tx1 = await wallet1.sendReveal(name); + assert.strictEqual(tx1.outputs.length, 2); + assert.strictEqual(tx1.outputs[0].value, 1e6); + assert.strictEqual(tx1.outputs[0].covenant.type, rules.types.REVEAL); + assert.strictEqual(tx1.outputs[1].covenant.type, rules.types.NONE); + + // Confirm + await node.rpc.generateToAddress([1, addr1]); + + // Wallet 1 knows there's another bid but can't reveal it. + assert.strictEqual( + (await wallet1.getBids()).length, + 2 + ); + await assert.rejects( + wallet1.sendReveal(name), + {message: `No bids to reveal for name: ${name}.`} + ); + + // Wallet 2 can reveal the second bid + const tx2 = await wallet2.sendReveal(name); + assert.strictEqual(tx2.outputs.length, 2); + assert.strictEqual(tx2.outputs[0].value, 2e6); + assert.strictEqual(tx2.outputs[0].covenant.type, rules.types.REVEAL); + + // Confirm + await node.rpc.generateToAddress([1, addr1]); + }); + + it('should have all reveals in both wallets', async () => { + const reveals1 = await wallet1.getReveals(); + const reveals2 = await wallet2.getReveals(); + + assert.strictEqual(reveals1.length, 2); + assert.strictEqual(reveals2.length, 2); + + for (const reveal of reveals1.concat(reveals2)) { + assert(reveal.own); + assert(reveal.value); + } + }); + + it('should restore wallet 1 from seed into wallet 3', async () => { + const {mnemonic} = wallet1.master; + wallet3 = await wdb.create({mnemonic}); + }); + + it('should just rescan reveal phase', async () => { + await wdb.rescan(heightBeforeReveal); + + let bal1 = await wallet1.getBalance(); + let bal3 = await wallet3.getBalance(); + + assert.notDeepStrictEqual(bal1, bal3); + + // Complete rescan cleans everything up + await wdb.rescan(0); + + bal1 = await wallet1.getBalance(); + bal3 = await wallet3.getBalance(); + + assert.deepStrictEqual(bal1, bal3); + }); + }); +}); diff --git a/test/wallet-records-test.js b/test/wallet-records-test.js index 2d59c9ff9..744282551 100644 --- a/test/wallet-records-test.js +++ b/test/wallet-records-test.js @@ -56,15 +56,18 @@ function getRandomTXRecordData(genTX = false, genBlock = false) { if (genTX) tx = getRandomTX(); + const mtime = random.randomInt(); + const data = { height: block ? block.height : -1, time: block ? block.time : 0, block: block ? block.hash : null, tx: tx, - hash: tx ? tx.hash() : null + hash: tx ? tx.hash() : null, + mtime: mtime }; - return {data, tx, block}; + return {data, tx, block, mtime}; } /* @@ -114,6 +117,7 @@ function compareTXRecord(actual, expected) { assert(typeof actual.mtime === 'number'); assert(actual.mtime > 0); + assert.strictEqual(actual.mtime, expected.mtime); assert.strictEqual(actual.index, -1); } @@ -267,16 +271,28 @@ describe('Wallet Records', function() { }); }); + it('should initialize w/ time', () => { + const {mtime} = getRandomTXRecordData(false, false, true); + const wtx = new TXRecord(mtime); + + compareTXRecord(wtx, { + index: -1, + mtime: mtime, + ...emptyTX, + ...emptyBlock + }); + }); + it('should initialize w/ tx', () => { - const {data, tx} = getRandomTXRecordData(true); - const wtx = new TXRecord(tx); + const {data, tx, mtime} = getRandomTXRecordData(true); + const wtx = new TXRecord(mtime, tx); compareTXRecord(wtx, data); }); it('should initialize w/ tx and block', () => { - const {data, block, tx} = getRandomTXRecordData(true, true); - const wtx = new TXRecord(tx, block); + const {data, block, tx, mtime} = getRandomTXRecordData(true, true); + const wtx = new TXRecord(mtime, tx, block); compareTXRecord(wtx, data); }); @@ -294,9 +310,9 @@ describe('Wallet Records', function() { }); it('should encode/decode w/ tx', () => { - const {data, tx} = getRandomTXRecordData(true); + const {data, tx, mtime} = getRandomTXRecordData(true); - const wtx = new TXRecord(tx); + const wtx = new TXRecord(mtime, tx); const encoded = wtx.encode(); const decoded = TXRecord.decode(encoded); @@ -305,9 +321,9 @@ describe('Wallet Records', function() { }); it('should encode/decode w/ tx and block', () => { - const {data, tx, block} = getRandomTXRecordData(true, true); + const {data, tx, block, mtime} = getRandomTXRecordData(true, true); - const wtx = new TXRecord(tx, block); + const wtx = new TXRecord(mtime, tx, block); const encoded = wtx.encode(); const decoded = TXRecord.decode(encoded); @@ -316,20 +332,20 @@ describe('Wallet Records', function() { }); it('should initialize from TX', () => { - const {data, tx} = getRandomTXRecordData(true); - const wtx = TXRecord.fromTX(tx); + const {data, tx, mtime} = getRandomTXRecordData(true); + const wtx = TXRecord.fromTX(tx, null, mtime); compareTXRecord(wtx, data); }); it('should initialize from TX and Block', () => { - const {data, tx, block} = getRandomTXRecordData(true, true); - const wtx = TXRecord.fromTX(tx, block); + const {data, tx, block, mtime} = getRandomTXRecordData(true, true); + const wtx = TXRecord.fromTX(tx, block, mtime); compareTXRecord(wtx, data); }); it('should set and unset block', () => { - const {data, tx, block} = getRandomTXRecordData(true, true); - const wtx = TXRecord.fromTX(tx); + const {data, tx, block, mtime} = getRandomTXRecordData(true, true); + const wtx = TXRecord.fromTX(tx, null, mtime); assert.strictEqual(wtx.getBlock(), null); assert.strictEqual(wtx.getDepth(random.randomInt()), 0); diff --git a/test/wallet-rescan-test.js b/test/wallet-rescan-test.js index 4b57a9f16..0048126e7 100644 --- a/test/wallet-rescan-test.js +++ b/test/wallet-rescan-test.js @@ -1,728 +1,893 @@ 'use strict'; const assert = require('bsert'); -const FullNode = require('../lib/node/fullnode'); -const MemWallet = require('./util/memwallet'); const Network = require('../lib/protocol/network'); const Address = require('../lib/primitives/address'); -const rules = require('../lib/covenants/rules'); -const {Resource} = require('../lib/dns/resource'); -const {forValue} = require('./util/common'); - -const network = Network.get('regtest'); - -const { - treeInterval, - biddingPeriod, - revealPeriod, - transferLockup -} = network.names; - -const GNAME_SIZE = 10; - -describe('Wallet rescan with namestate transitions', function() { - describe('Only sends OPEN', function() { - // Bob runs a full node with wallet plugin - const node = new FullNode({ - network: network.type, - memory: true, - plugins: [require('../lib/wallet/plugin')] - }); - node.on('error', (err) => { - assert(false, err); - }); +const HDPublicKey = require('../lib/hd/public'); +const NodesContext = require('./util/nodes-context'); +const {forEvent, forEventCondition} = require('./util/common'); +const {Balance, getWClientBalance, getBalance} = require('./util/balance'); + +// Definitions: +// Gapped txs/addresses - addresses with lookahead + 1 gap when deriving. +// +// Setup: +// - Standalone Node (no wallet) responsible for progressing network. +// - Wallet Node (with wallet) responsible for rescanning. +// - Wallet SPV Node (with wallet) responsible for rescanning. +// - Wallet Standalone Node responsible for rescanning. +// - Wallet SPV Standalone Node responsible for rescanning. +// +// Test cases: +// - TX deeper depth -> TX shallower depth for derivation (Second tx is discovered first) +// - TX with outputs -> deeper, deep, shallow - derivation depths. +// (Outputs are discovered from shallower to deeper) +// - Replicate both transactions in the same block on rescan. +// - Replicate both transactions when receiving tip. +// +// If per block derivation lookahead is higher than wallet lookahed +// recovery is impossible. This tests situation where in block +// derivation depth is lower than wallet lookahead. + +const combinations = [ + { SPV: false, STANDALONE: false, name: 'Full/Plugin' }, + { SPV: false, STANDALONE: true, name: 'Full/Standalone' }, + { SPV: true, STANDALONE: false, name: 'SPV/Plugin' } + // Not supported. + // { SPV: true, STANDALONE: true, name: 'SPV/Standalone' } +]; + +const noSPVcombinations = combinations.filter(c => !c.SPV); +const regtest = Network.get('regtest'); + +describe('Wallet rescan/addBlock', function() { + for (const {SPV, STANDALONE, name} of noSPVcombinations) { + describe(`rescan/addBlock gapped addresses (${name} Integration)`, function() { + this.timeout(5000); + const TEST_LOOKAHEAD = 20; + + const MAIN = 0; + const TEST_ADDBLOCK = 1; + const TEST_RESCAN = 2; + + const WALLET_NAME = 'test'; + const ACCOUNT = 'default'; + + const regtest = Network.get('regtest'); + + /** @type {NodesContext} */ + let nodes; + let minerWallet, minerAddress; + let main, addBlock, rescan; - const {wdb} = node.require('walletdb'); - let bob, bobAddr; + before(async () => { + // Initial node is the one that progresses the network. + nodes = new NodesContext(regtest, 1); + // MAIN_WALLET = 0 + nodes.init({ + wallet: true, + standalone: true, + memory: true, + noDNS: true + }); - // Alice is some other wallet on the network - const alice = new MemWallet({ network }); - const aliceAddr = alice.getAddress(); + // Add the testing node. + // TEST_ADDBLOCK = 1 + nodes.addNode({ + spv: SPV, + wallet: true, + memory: true, + standalone: STANDALONE, + noDNS: true + }); - // Connect MemWallet to chain as minimally as possible - node.chain.on('connect', (entry, block) => { - alice.addBlock(entry, block.txs); - }); - alice.getNameStatus = async (nameHash) => { - assert(Buffer.isBuffer(nameHash)); - const height = node.chain.height + 1; - const state = await node.chain.getNextState(); - const hardened = state.hasHardening(); - return node.chain.db.getNameStatus(nameHash, height, hardened); - }; - - const NAME = rules.grindName(GNAME_SIZE, 4, network); - - // Hash of the FINALIZE transaction - let aliceFinalizeHash; - - async function mineBlocks(n, addr) { - addr = addr ? addr : new Address().toString('regtest'); - const blocks = []; - for (let i = 0; i < n; i++) { - const block = await node.miner.mineBlock(null, addr); - await node.chain.add(block); - blocks.push(block); - } + // Add the rescan test node. + // TEST_RESCAN = 2 + nodes.addNode({ + spv: SPV, + wallet: true, + memory: true, + standalone: STANDALONE, + noDNS: true + }); - return blocks; - } + await nodes.open(); - async function sendTXs() { - const aliceTX = await alice.send({ - outputs: [{ - address: aliceAddr, - value: 20000 - }] + const mainWClient = nodes.context(MAIN).wclient; + minerWallet = nodes.context(MAIN).wclient.wallet('primary'); + minerAddress = (await minerWallet.createAddress('default')).address; + + const mainWallet = await mainWClient.createWallet(WALLET_NAME, { + lookahead: TEST_LOOKAHEAD }); - alice.addTX(aliceTX.toTX()); - await node.mempool.addTX(aliceTX.toTX()); - await bob.send({ - outputs: [{ - address: bobAddr, - value: 20000 - }] + assert(mainWallet); + + const master = await mainWClient.getMaster(WALLET_NAME); + + const addBlockWClient = nodes.context(TEST_ADDBLOCK).wclient; + const addBlockWalletResult = await addBlockWClient.createWallet(WALLET_NAME, { + lookahead: TEST_LOOKAHEAD, + mnemonic: master.mnemonic.phrase }); - } + assert(addBlockWalletResult); - before(async () => { - await node.open(); - bob = await wdb.create(); - bobAddr = await bob.receiveAddress(); + const rescanWClient = nodes.context(TEST_RESCAN).wclient; + const rescanWalletResult = await rescanWClient.createWallet(WALLET_NAME, { + lookahead: TEST_LOOKAHEAD, + mnemonic: master.mnemonic.phrase + }); + assert(rescanWalletResult); + + main = {}; + main.client = mainWClient.wallet(WALLET_NAME); + await main.client.open(); + main.wdb = nodes.context(MAIN).wdb; + + addBlock = {}; + addBlock.client = addBlockWClient.wallet(WALLET_NAME); + await addBlock.client.open(); + addBlock.wdb = nodes.context(TEST_ADDBLOCK).wdb; + + rescan = {}; + rescan.client = rescanWClient.wallet(WALLET_NAME); + await rescan.client.open(); + rescan.wdb = nodes.context(TEST_RESCAN).wdb; + + await nodes.generate(MAIN, 10, minerAddress); }); after(async () => { - await node.close(); + await nodes.close(); + await nodes.destroy(); + }); + + // Prepare for the rescan and addBlock tests. + it('should send gapped txs on each block', async () => { + const expectedRescanBalance = await getBalance(main.client, ACCOUNT); + const height = nodes.height(MAIN); + const blocks = 5; + + // 1 address per block, all of them gapped. + // Start after first gap, make sure rescan has no clue. + const all = await generateGappedAddresses(main.client, blocks + 1, regtest); + await deriveAddresses(main.client, all[all.length - 1].depth); + const addresses = all.slice(1); + // give addBlock first address. + await deriveAddresses(addBlock.client, addresses[0].depth - TEST_LOOKAHEAD); + + const condFn = entry => entry.height === blocks + height; + const mainWalletBlocks = forEventCondition(main.wdb, 'block connect', condFn); + const addBlockWalletBlocks = forEventCondition(addBlock.wdb, 'block connect', condFn); + const rescanWalletBlocks = forEventCondition(rescan.wdb, 'block connect', condFn); + + for (let i = 0; i < blocks; i++) { + await minerWallet.send({ + outputs: [{ + address: addresses[i].address.toString(regtest), + value: 1e6 + }] + }); + + await nodes.generate(MAIN, 1, minerAddress); + } + + await Promise.all([ + mainWalletBlocks, + addBlockWalletBlocks, + rescanWalletBlocks + ]); + + const rescanBalance = await getBalance(rescan.client, ACCOUNT); + assert.deepStrictEqual(rescanBalance, expectedRescanBalance); + // before the rescan test. + await deriveAddresses(rescan.client, addresses[0].depth - TEST_LOOKAHEAD); }); - it('should fund wallets', async () => { - const blocks = 10; - await mineBlocks(blocks, aliceAddr); - await mineBlocks(blocks, bobAddr); + it('should receive gapped txs on each block (addBlock)', async () => { + const expectedBalance = await getBalance(main.client, ACCOUNT); + const addBlockBalance = await getBalance(addBlock.client, ACCOUNT); + assert.deepStrictEqual(addBlockBalance, expectedBalance); - const bobBal = await bob.getBalance(); - assert.strictEqual(bobBal.confirmed, blocks * 2000 * 1e6); - assert.strictEqual(alice.balance, blocks * 2000 * 1e6); + const mainInfo = await main.client.getAccount(ACCOUNT); + const addBlockInfo = await addBlock.client.getAccount(ACCOUNT); + assert.deepStrictEqual(addBlockInfo, mainInfo); }); - it('should run auction', async () => { - // Poor Bob, all he does is send an OPEN but his wallet will - // watch all the other activity including TRANSFERS for this name - await bob.sendOpen(NAME, true); - // Scatter unrelated TXs throughout the test. - // This will ensure that txdb.removeBlock() removes TXs - // in the reverse order from when they were added - await sendTXs(); - const openBlocks = await mineBlocks(1); - // Coinbase plus open - assert.strictEqual(openBlocks[0].txs.length, 4); - - // Advance to bidding phase - await mineBlocks(treeInterval); - await forValue(alice, 'height', node.chain.height); - - // Alice sends only bid - await sendTXs(); - const aliceBid = await alice.createBid(NAME, 20000, 20000); - await node.mempool.addTX(aliceBid.toTX()); - const bidBlocks = await mineBlocks(1); - assert.strictEqual(bidBlocks[0].txs.length, 4); - - // Advance to reveal phase - await mineBlocks(biddingPeriod); - await sendTXs(); - const aliceReveal = await alice.createReveal(NAME); - await node.mempool.addTX(aliceReveal.toTX()); - const revealBlocks = await mineBlocks(1); - assert.strictEqual(revealBlocks[0].txs.length, 4); - - // Close auction - await mineBlocks(revealPeriod); - - // Alice registers - await sendTXs(); - const aliceRegister = await alice.createRegister( - NAME, - Resource.fromJSON({records:[]}) - ); - await node.mempool.addTX(aliceRegister.toTX()); - - const registerBlocks = await mineBlocks(1); - assert.strictEqual(registerBlocks[0].txs.length, 4); + it('should receive gapped txs on each block (rescan)', async () => { + const expectedBalance = await getBalance(main.client, ACCOUNT); + const expectedInfo = await main.client.getAccount(ACCOUNT); + + // give rescan first address. + await rescan.wdb.rescan(0); + + const rescanBalance = await getBalance(rescan.client, ACCOUNT); + assert.deepStrictEqual(rescanBalance, expectedBalance); + + const rescanInfo = await rescan.client.getAccount(ACCOUNT); + assert.deepStrictEqual(rescanInfo, expectedInfo); }); - it('should get namestate', async () => { - const ns = await bob.getNameStateByName(NAME); - // Bob has the namestate - assert(ns); + it('should send gapped txs in the same block', async () => { + const expectedRescanBalance = await getBalance(rescan.client, ACCOUNT); + const txCount = 5; + + const all = await generateGappedAddresses(main.client, txCount + 1, regtest); + await deriveAddresses(main.client, all[all.length - 1].depth); + const addresses = all.slice(1); + + // give addBlock first address. + await deriveAddresses(addBlock.client, addresses[0].depth - TEST_LOOKAHEAD); + + const mainWalletBlocks = forEvent(main.wdb, 'block connect'); + const addBlockWalletBlocks = forEvent(addBlock.wdb, 'block connect'); + const rescanWalletBlocks = forEvent(rescan.wdb, 'block connect'); - // Bob is not the name owner - const {hash, index} = ns.owner; - const coin = await bob.getCoin(hash, index); - assert.strictEqual(coin, null); + for (const {address} of addresses) { + await minerWallet.send({ + outputs: [{ + address: address.toString(regtest), + value: 1e6 + }] + }); + } + + await nodes.generate(MAIN, 1, minerAddress); + + await Promise.all([ + mainWalletBlocks, + addBlockWalletBlocks, + rescanWalletBlocks + ]); - // Name is not in mid-TRANSFER - assert.strictEqual(ns.transfer, 0); + const rescanBalance = await getBalance(rescan.client, ACCOUNT); + assert.deepStrictEqual(rescanBalance, expectedRescanBalance); + + await deriveAddresses(rescan.client, addresses[0].depth - TEST_LOOKAHEAD); }); - it('should process TRANSFER', async () => { - // Alice transfers the name to her own address - await sendTXs(); - const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); - await node.mempool.addTX(aliceTransfer.toTX()); - const transferBlocks = await mineBlocks(1); - assert.strictEqual(transferBlocks[0].txs.length, 4); - - // Bob detects the TRANSFER even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.transfer, node.chain.height); - - // Bob's wallet has not indexed the TRANSFER - const bobTransfer = await bob.getTX(aliceTransfer.hash()); - assert.strictEqual(bobTransfer, null); + it.skip('should receive gapped txs in the same block (addBlock)', async () => { + const expectedBalance = await getBalance(main.client, ACCOUNT); + const addBlockBalance = await getBalance(addBlock.client, ACCOUNT); + assert.deepStrictEqual(addBlockBalance, expectedBalance); + + const mainInfo = await main.client.getAccount(ACCOUNT); + const addBlockInfo = await addBlock.client.getAccount(ACCOUNT); + assert.deepStrictEqual(addBlockInfo, mainInfo); }); - it('should fully rescan', async () => { - // Complete chain rescan - await wdb.rescan(0); - await forValue(wdb, 'height', node.chain.height); + it('should receive gapped txs in the same block (rescan)', async () => { + const expectedBalance = await getBalance(main.client, ACCOUNT); + const expectedInfo = await main.client.getAccount(ACCOUNT); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.transfer, node.chain.height); + await rescan.wdb.rescan(0); + + const rescanBalance = await getBalance(rescan.client, ACCOUNT); + assert.deepStrictEqual(rescanBalance, expectedBalance); + + const rescanInfo = await rescan.client.getAccount(ACCOUNT); + assert.deepStrictEqual(rescanInfo, expectedInfo); }); - it('should process FINALIZE', async () => { - await mineBlocks(transferLockup); + it('should send gapped outputs in the same tx', async () => { + const expectedRescanBalance = await getBalance(rescan.client, ACCOUNT); + const outCount = 5; - // Alice finalizes the name - await sendTXs(); - const aliceFinalize = await alice.createFinalize(NAME); - await node.mempool.addTX(aliceFinalize.toTX()); - const finalizeBlocks = await mineBlocks(1); - assert.strictEqual(finalizeBlocks[0].txs.length, 4); + const all = await generateGappedAddresses(main.client, outCount + 1, regtest); + await deriveAddresses(main.client, all[all.length - 1].depth); + const addresses = all.slice(1); - aliceFinalizeHash = aliceFinalize.hash(); + // give addBlock first address. + await deriveAddresses(addBlock.client, addresses[0].depth - TEST_LOOKAHEAD); - // Bob detects the FINALIZE even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); + const mainWalletBlocks = forEvent(main.wdb, 'block connect'); + const addBlockWalletBlocks = forEvent(addBlock.wdb, 'block connect'); + const rescanWalletBlocks = forEvent(rescan.wdb, 'block connect'); - // Bob's wallet has not indexed the FINALIZE - const bobFinalize = await bob.getTX(aliceFinalize.hash()); - assert.strictEqual(bobFinalize, null); - }); + const outputs = addresses.map(({address}) => ({ + address: address.toString(regtest), + value: 1e6 + })); - it('should fully rescan', async () => { - // Complete chain rescan - await wdb.rescan(0); - await forValue(wdb, 'height', node.chain.height); + await minerWallet.send({outputs}); + await nodes.generate(MAIN, 1, minerAddress); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); - }); + await Promise.all([ + mainWalletBlocks, + addBlockWalletBlocks, + rescanWalletBlocks + ]); + + const rescanBalance = await getBalance(rescan.client, ACCOUNT); + assert.deepStrictEqual(rescanBalance, expectedRescanBalance); - it('should process TRANSFER (again)', async () => { - // Alice transfers the name to her own address - await sendTXs(); - const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); - await node.mempool.addTX(aliceTransfer.toTX()); - const transferBlocks = await mineBlocks(1); - assert.strictEqual(transferBlocks[0].txs.length, 4); - - // Bob detects the TRANSFER even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.transfer, node.chain.height); - - // Bob's wallet has not indexed the TRANSFER - const bobTransfer = await bob.getTX(aliceTransfer.hash()); - assert.strictEqual(bobTransfer, null); + await deriveAddresses(rescan.client, addresses[0].depth - TEST_LOOKAHEAD); }); - it('should process REVOKE', async () => { - // Alice revokes the name - await sendTXs(); - const aliceRevoke = await alice.createRevoke(NAME); - await node.mempool.addTX(aliceRevoke.toTX()); - const revokeBlocks = await mineBlocks(1); - assert.strictEqual(revokeBlocks[0].txs.length, 4); - - // Bob detects the REVOKE even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.revoked, node.chain.height); - - // Bob's wallet has not indexed the REVOKE - const bobTransfer = await bob.getTX(aliceRevoke.hash()); - assert.strictEqual(bobTransfer, null); + it.skip('should receive gapped outputs in the same tx (addBlock)', async () => { + const expectedBalance = await getBalance(main.client, ACCOUNT); + const addBlockBalance = await getBalance(addBlock.client, ACCOUNT); + assert.deepStrictEqual(addBlockBalance, expectedBalance); + + const mainInfo = await main.client.getAccount(ACCOUNT); + const addBlockInfo = await addBlock.client.getAccount(ACCOUNT); + assert.deepStrictEqual(addBlockInfo, mainInfo); }); - it('should fully rescan', async () => { - // Complete chain rescan - await wdb.rescan(0); - await forValue(wdb, 'height', node.chain.height); + it('should receive gapped outputs in the same tx (rescan)', async () => { + const expectedBalance = await getBalance(main.client, ACCOUNT); + const expectedInfo = await main.client.getAccount(ACCOUNT); + + await rescan.wdb.rescan(0); + + const rescanBalance = await getBalance(rescan.client, ACCOUNT); + assert.deepStrictEqual(rescanBalance, expectedBalance); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.revoked, node.chain.height); + const rescanInfo = await rescan.client.getAccount(ACCOUNT); + assert.deepStrictEqual(rescanInfo, expectedInfo); }); }); + } + + for (const {SPV, STANDALONE, name} of combinations) { + describe(`Initial sync/rescan (${name} Integration)`, function() { + // Test wallet plugin/standalone is disabled and re-enabled after some time: + // 1. Normal received blocks. + // 2. Reorged after wallet was closed. + // NOTE: Node is not closed, only wallet. + + const MINER = 0; + const WALLET = 1; + const WALLET_NO_WALLET = 2; + + /** @type {NodesContext} */ + let nodes; + let wnodeCtx, noWnodeCtx; + let minerWallet, minerAddress; + let testWallet, testAddress; + + before(async () => { + nodes = new NodesContext(regtest, 1); + + // MINER = 0 + nodes.init({ + wallet: true, + noDNS: true, + bip37: true + }); - describe('Bids, loses, shallow rescan', function() { - // Bob runs a full node with wallet plugin - const node = new FullNode({ - network: network.type, - memory: true, - plugins: [require('../lib/wallet/plugin')] + // WALLET = 1 + wnodeCtx = nodes.addNode({ + noDNS: true, + wallet: true, + + standalone: STANDALONE, + spv: SPV, + + // We need to store on disk in order to test + // recovery on restart + memory: false + }); + + // WALLET_NO_WALLET = 2 + // Wallet node that uses same chain above one + // just does not start wallet. + noWnodeCtx = nodes.addNode({ + noDNS: true, + wallet: false, + prefix: wnodeCtx.prefix, + memory: false, + spv: SPV + }); + + // only open two at a time. + await nodes.open(MINER); + await nodes.open(WALLET); + + minerWallet = nodes.context(MINER).wclient.wallet('primary'); + minerAddress = (await minerWallet.createAddress('default')).address; + + testWallet = wnodeCtx.wclient.wallet('primary'); + testAddress = (await testWallet.createAddress('default')).address; + + await nodes.close(WALLET); }); - node.on('error', (err) => { - assert(false, err); + + after(async () => { + await nodes.close(); + await nodes.destroy(); }); - const {wdb} = node.require('walletdb'); - let bob, bobAddr; + afterEach(async () => { + await nodes.close(WALLET); + await nodes.close(WALLET_NO_WALLET); + }); - // Alice is some other wallet on the network - const alice = new MemWallet({ network }); - const aliceAddr = alice.getAddress(); + it('should fund and spend to wallet', async () => { + await wnodeCtx.open(); - // Connect MemWallet to chain as minimally as possible - node.chain.on('connect', (entry, block) => { - alice.addBlock(entry, block.txs); - }); - alice.getNameStatus = async (nameHash) => { - assert(Buffer.isBuffer(nameHash)); - const height = node.chain.height + 1; - const state = await node.chain.getNextState(); - const hardened = state.hasHardening(); - return node.chain.db.getNameStatus(nameHash, height, hardened); - }; - - const NAME = rules.grindName(GNAME_SIZE, 4, network); - - // Block that confirmed the bids - let bidBlockHash; - // Hash of the FINALIZE transaction - let aliceFinalizeHash; - - async function mineBlocks(n, addr) { - addr = addr ? addr : new Address().toString('regtest'); - const blocks = []; - for (let i = 0; i < n; i++) { - const block = await node.miner.mineBlock(null, addr); - await node.chain.add(block); - blocks.push(block); - } + const txEvent = forEvent(wnodeCtx.wdb, 'tx'); - return blocks; - } + // fund wallet. + await nodes.generate(MINER, 9, minerAddress); - async function sendTXs() { - const aliceTX = await alice.send({ + // Send TX to the test wallet. + await minerWallet.send({ outputs: [{ - address: aliceAddr, - value: 20000 + address: testAddress, + value: 1e6 }] }); - alice.addTX(aliceTX.toTX()); - await node.mempool.addTX(aliceTX.toTX()); - await bob.send({ + + await nodes.generate(MINER, 1, minerAddress); + await txEvent; + + const balance = await getWClientBalance(wnodeCtx.wclient, 'primary', 'default'); + assert.deepStrictEqual(balance, new Balance({ + coin: 1, + tx: 1, + confirmed: 1e6, + unconfirmed: 1e6 + })); + }); + + it('should rescan/resync after wallet was off', async () => { + // replace wallet node with new one w/o wallet. + await noWnodeCtx.open(); + + await nodes.generate(MINER, 10, minerAddress); + + // Mine in the last block that we will be reorging. + await minerWallet.send({ outputs: [{ - address: bobAddr, - value: 20000 + address: testAddress, + value: 2e6 }] }); - } - before(async () => { - await node.open(); - bob = await wdb.create(); - bobAddr = await bob.receiveAddress(); - }); + const waitHeight = nodes.height(MINER) + 1; + const nodeSync = forEventCondition(noWnodeCtx.node, 'connect', (entry) => { + return entry.height === waitHeight; + }); - after(async () => { - await node.close(); - }); + await nodes.generate(MINER, 1, minerAddress); + await nodeSync; + + // Disable wallet + await noWnodeCtx.close(); + + wnodeCtx.init(); + + const eventsToWait = []; + // For spv we don't wait for sync done, as it will do the full rescan + // and reset the SPVNode as well. It does not depend on the accumulated + // blocks. + if (SPV) { + // This will happen right away, as scan will just call reset + eventsToWait.push(forEvent(wnodeCtx.wdb, 'sync done')); + // This is what matters for the rescan. + eventsToWait.push(forEventCondition(wnodeCtx.wdb, 'block connect', (entry) => { + return entry.height === nodes.height(MINER); + })); + // Make sure node gets resets. + eventsToWait.push(forEvent(wnodeCtx.node, 'reset')); + } else { + eventsToWait.push(forEvent(wnodeCtx.wdb, 'sync done')); + } - it('should fund wallets', async () => { - const blocks = 10; - await mineBlocks(blocks, aliceAddr); - await mineBlocks(blocks, bobAddr); + await wnodeCtx.open(); + await Promise.all(eventsToWait); + assert.strictEqual(wnodeCtx.wdb.height, nodes.height(MINER)); - const bobBal = await bob.getBalance(); - assert.strictEqual(bobBal.confirmed, blocks * 2000 * 1e6); - assert.strictEqual(alice.balance, blocks * 2000 * 1e6); - }); + const balance = await getWClientBalance(wnodeCtx.wclient, 'primary', 'default'); + assert.deepStrictEqual(balance, new Balance({ + coin: 2, + tx: 2, + confirmed: 1e6 + 2e6, + unconfirmed: 1e6 + 2e6 + })); - it('should run auction', async () => { - // Alice opens - await sendTXs(); - const aliceOpen = await alice.createOpen(NAME); - await node.mempool.addTX(aliceOpen.toTX()); - const openBlocks = await mineBlocks(1); - // Coinbase plus open - assert.strictEqual(openBlocks[0].txs.length, 4); - - // Advance to bidding phase - await mineBlocks(treeInterval); - await forValue(alice, 'height', node.chain.height); - - // Poor Bob, all he does is send one (losing) bid but his wallet will - // watch all the other activity including TRANSFERS for this name - await bob.sendBid(NAME, 10000, 10000); - - // Alice sends winning bid - await sendTXs(); - const aliceBid = await alice.createBid(NAME, 20000, 20000); - await node.mempool.addTX(aliceBid.toTX()); - const bidBlocks = await mineBlocks(1); - assert.strictEqual(bidBlocks[0].txs.length, 5); - - bidBlockHash = bidBlocks[0].hash(); - - // Advance to reveal phase - await mineBlocks(biddingPeriod); - await bob.sendReveal(NAME); - await sendTXs(); - const aliceReveal = await alice.createReveal(NAME); - await node.mempool.addTX(aliceReveal.toTX()); - const revealBlocks = await mineBlocks(1); - assert.strictEqual(revealBlocks[0].txs.length, 5); - - // Close auction - await mineBlocks(revealPeriod); - - // Alice registers - await sendTXs(); - const aliceRegister = await alice.createRegister( - NAME, - Resource.fromJSON({records:[]}) - ); - await node.mempool.addTX(aliceRegister.toTX()); - - const registerBlocks = await mineBlocks(1); - assert.strictEqual(registerBlocks[0].txs.length, 4); + await wnodeCtx.close(); }); - it('should get namestate', async () => { - const ns = await bob.getNameStateByName(NAME); - // Bob has the namestate - assert(ns); + it('should rescan/resync after wallet was off and node reorged', async () => { + const minerCtx = nodes.context(MINER); - // Bob is not the name owner - const {hash, index} = ns.owner; - const coin = await bob.getCoin(hash, index); - assert.strictEqual(coin, null); + await noWnodeCtx.open(); - // Name is not in mid-TRANSFER - assert.strictEqual(ns.transfer, 0); - }); + // Reorg the network + const tip = minerCtx.chain.tip; + const block = await minerCtx.chain.getBlock(tip.hash); - it('should process TRANSFER', async () => { - // Alice transfers the name to her own address - await sendTXs(); - const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); - await node.mempool.addTX(aliceTransfer.toTX()); - const transferBlocks = await mineBlocks(1); - assert.strictEqual(transferBlocks[0].txs.length, 4); - - // Bob detects the TRANSFER even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.transfer, node.chain.height); - - // Bob's wallet has indexed the TRANSFER - const bobTransfer = await bob.getTX(aliceTransfer.hash()); - assert.strictEqual(bobTransfer, null); - }); + // Last block contained our tx from previous test. (integration) + assert.strictEqual(block.txs.length, 2); - it('should fully rescan', async () => { - await wdb.rescan(0); - await forValue(wdb, 'height', node.chain.height); + const reorgEvent = forEvent(minerCtx.node, 'reorganize'); + const forkTip = await minerCtx.chain.getPrevious(tip); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.transfer, node.chain.height); - }); + // REORG + await nodes.generate(MINER, 2, minerAddress, forkTip); + // Reset mempool/Get rid of tx after reorg. + await nodes.context(MINER).mempool.reset(); + await nodes.generate(MINER, 2, minerAddress); + await reorgEvent; - it('should rescan since, but not including, the BIDs', async () => { - const bidBlock = await node.chain.getEntry(bidBlockHash); - await wdb.rescan(bidBlock.height); - await forValue(wdb, 'height', node.chain.height); + // Send another tx, with different output. + await minerWallet.send({ + outputs: [{ + address: testAddress, + value: 3e6 + }] + }); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.transfer, node.chain.height); - }); + const waitHeight = nodes.height(MINER) + 1; + const nodeSync = forEventCondition(noWnodeCtx.node, 'connect', (entry) => { + return entry.height === waitHeight; + }); - it('should process FINALIZE', async () => { - await mineBlocks(transferLockup); + await nodes.generate(MINER, 1, minerAddress); + await nodeSync; - // Alice finalizes the name - await sendTXs(); - const aliceFinalize = await alice.createFinalize(NAME); - await node.mempool.addTX(aliceFinalize.toTX()); - const finalizeBlocks = await mineBlocks(1); - assert.strictEqual(finalizeBlocks[0].txs.length, 4); + await noWnodeCtx.close(); - aliceFinalizeHash = aliceFinalize.hash(); + wnodeCtx.init(); - // Bob detects the FINALIZE even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); + // initial sync + const eventsToWait = []; - // Bob's wallet has not indexed the FINALIZE - const bobFinalize = await bob.getTX(aliceFinalize.hash()); - assert.strictEqual(bobFinalize, null); - }); + if (SPV) { + // This will happen right away, as scan will just call reset + eventsToWait.push(forEvent(wnodeCtx.wdb, 'sync done')); - it('should fully rescan', async () => { - await wdb.rescan(0); - await forValue(wdb, 'height', node.chain.height); + // This is what matters for the rescan. + eventsToWait.push(forEventCondition(wnodeCtx.wdb, 'block connect', (entry) => { + return entry.height === nodes.height(MINER); + })); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); - }); + // Make sure node gets resets. + eventsToWait.push(forEvent(wnodeCtx.node, 'reset')); + eventsToWait.push(forEvent(wnodeCtx.wdb, 'unconfirmed')); + } else { + eventsToWait.push(forEvent(wnodeCtx.wdb, 'sync done')); + eventsToWait.push(forEvent(wnodeCtx.wdb, 'unconfirmed')); + } + await wnodeCtx.open(); + await Promise.all(eventsToWait); - it('should rescan since, but not including, the BIDs', async () => { - const bidBlock = await node.chain.getEntry(bidBlockHash); - await wdb.rescan(bidBlock.height); - await forValue(wdb, 'height', node.chain.height); + assert.strictEqual(wnodeCtx.height, nodes.height(MINER)); + assert.strictEqual(wnodeCtx.wdb.state.height, wnodeCtx.height); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.bufferEqual(ns.owner.hash, aliceFinalizeHash); - }); + const balance = await getWClientBalance(wnodeCtx.wclient, 'primary', 'default'); - it('should process TRANSFER (again)', async () => { - // Alice transfers the name to her own address - await sendTXs(); - const aliceTransfer = await alice.createTransfer(NAME, aliceAddr); - await node.mempool.addTX(aliceTransfer.toTX()); - const transferBlocks = await mineBlocks(1); - assert.strictEqual(transferBlocks[0].txs.length, 4); - - // Bob detects the TRANSFER even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.transfer, node.chain.height); - - // Bob's wallet has not indexed the TRANSFER - const bobTransfer = await bob.getTX(aliceTransfer.hash()); - assert.strictEqual(bobTransfer, null); - }); + // previous transaction should get unconfirmed. + assert.deepStrictEqual(balance, new Balance({ + coin: 3, + tx: 3, + confirmed: 1e6 + 3e6, + unconfirmed: 1e6 + 2e6 + 3e6 + })); - it('should process REVOKE', async () => { - // Alice revokes the name - await sendTXs(); - const aliceRevoke = await alice.createRevoke(NAME); - await node.mempool.addTX(aliceRevoke.toTX()); - const revokeBlocks = await mineBlocks(1); - assert.strictEqual(revokeBlocks[0].txs.length, 4); - - // Bob detects the REVOKE even though it doesn't involve him at all - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.revoked, node.chain.height); - - // Bob's wallet has not indexed the REVOKE - const bobTransfer = await bob.getTX(aliceRevoke.hash()); - assert.strictEqual(bobTransfer, null); + await wnodeCtx.close(); }); - it('should fully rescan', async () => { - await wdb.rescan(0); - await forValue(wdb, 'height', node.chain.height); + it('should rescan/resync after wallet was off and received gapped txs in the same block', async () => { + if (SPV) + this.skip(); + + const txCount = 5; + await wnodeCtx.open(); + const startingBalance = await getBalance(testWallet, 'default'); + const all = await generateGappedAddresses(testWallet, txCount, regtest); + await wnodeCtx.close(); + + await noWnodeCtx.open(); + + for (const {address} of all) { + await minerWallet.send({ + outputs: [{ + address: address.toString(regtest), + value: 1e6 + }] + }); + } + + const waitHeight = nodes.height(MINER) + 1; + const nodeSync = forEventCondition(noWnodeCtx.node, 'connect', (entry) => { + return entry.height === waitHeight; + }); + + await nodes.generate(MINER, 1, minerAddress); + + await nodeSync; + await noWnodeCtx.close(); + + wnodeCtx.init(); + + const syncDone = forEvent(wnodeCtx.wdb, 'sync done'); + await wnodeCtx.open(); + await syncDone; + assert.strictEqual(wnodeCtx.wdb.height, nodes.height(MINER)); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.revoked, node.chain.height); + const balance = await getBalance(testWallet, 'default'); + const diff = balance.diff(startingBalance); + assert.deepStrictEqual(diff, new Balance({ + tx: txCount, + coin: txCount, + confirmed: 1e6 * txCount, + unconfirmed: 1e6 * txCount + })); + + await wnodeCtx.close(); }); - it('should rescan since, but not including, the BIDs', async () => { - const bidBlock = await node.chain.getEntry(bidBlockHash); - await wdb.rescan(bidBlock.height); - await forValue(wdb, 'height', node.chain.height); + it('should rescan/resync after wallet was off and received gapped coins in the same tx', async () => { + if (SPV) + this.skip(); + + const outCount = 5; + await wnodeCtx.open(); + const startingBalance = await getBalance(testWallet, 'default'); + const all = await generateGappedAddresses(testWallet, outCount, regtest); + await wnodeCtx.close(); + + await noWnodeCtx.open(); + + const outputs = all.map(({address}) => ({ + address: address.toString(regtest), + value: 1e6 + })); + + await minerWallet.send({outputs}); + + const waitHeight = nodes.height(MINER) + 1; + const nodeSync = forEventCondition(noWnodeCtx.node, 'connect', (entry) => { + return entry.height === waitHeight; + }); + + await nodes.generate(MINER, 1, minerAddress); + + await nodeSync; + await noWnodeCtx.close(); + + wnodeCtx.init(); - // No change - const ns = await bob.getNameStateByName(NAME); - assert(ns); - assert.strictEqual(ns.revoked, node.chain.height); + const syncDone = forEvent(wnodeCtx.wdb, 'sync done'); + await wnodeCtx.open(); + await syncDone; + assert.strictEqual(wnodeCtx.wdb.height, nodes.height(MINER)); + + const balance = await getBalance(testWallet, 'default'); + const diff = balance.diff(startingBalance); + assert.deepStrictEqual(diff, new Balance({ + tx: 1, + coin: outCount, + confirmed: 1e6 * outCount, + unconfirmed: 1e6 * outCount + })); + + await wnodeCtx.close(); }); }); + } - describe('Restore from seed', function() { - const node = new FullNode({ - network: network.type, - memory: true, - plugins: [require('../lib/wallet/plugin')] - }); + for (const {STANDALONE, name} of noSPVcombinations) { + describe(`Deadlock (${name} Integration)`, function() { + this.timeout(10000); + const nodes = new NodesContext(regtest, 1); + let minerCtx; + let nodeCtx, address, node, wdb; + + before(async () => { + nodes.init({ + memory: false, + wallet: false + }); - const {wdb} = node.require('walletdb'); - let wallet1, wallet2, wallet3; - let addr1; - let heightBeforeReveal; + nodes.addNode({ + memory: false, + wallet: true, + standalone: STANDALONE + }); - const name = rules.grindName(4, 4, network); + await nodes.open(); - before(async () => { - await node.open(); + minerCtx = nodes.context(0); + nodeCtx = nodes.context(1); + node = nodeCtx.node; + wdb = nodeCtx.wdb; + + address = await wdb.primary.receiveAddress(); }); after(async () => { - await node.close(); + await nodes.close(); }); - it('should create and fund wallet 1', async () => { - wallet1 = await wdb.create(); - addr1 = (await wallet1.receiveAddress()).toString(network); - await node.rpc.generateToAddress([10, addr1]); - }); + it('should generate 20 blocks', async () => { + const BLOCKS = 20; + const chainBlocks = forEventCondition(node.chain, 'connect', (entry) => { + return entry.height === BLOCKS; + }, 5000); - it('should open and bid from wallet 1', async () => { - await wallet1.sendOpen(name, false); - await node.rpc.generateToAddress([treeInterval + 1, addr1]); - await wallet1.sendBid(name, 1e6, 1e6); - await node.rpc.generateToAddress([1, addr1]); - }); + const wdbBlocks = forEventCondition(wdb, 'block connect', (entry) => { + return entry.height === BLOCKS; + }, 5000); - it('should restore wallet 1 from seed into wallet 2', async () => { - const {mnemonic} = wallet1.master; - wallet2 = await wdb.create({mnemonic}); - - assert.strictEqual( - wallet1.master.key.xprivkey, - wallet2.master.key.xprivkey - ); - - // Sanity check - const bal1 = await wallet1.getBalance(); - assert(bal1.unconfirmed > 0); - assert(bal1.tx > 0); - assert((await wallet1.getBids()).length); - - // Wallet 2 has no history - const bal2 = await wallet2.getBalance(); - assert.strictEqual(bal2.unconfirmed, 0); - assert.strictEqual(bal2.tx, 0); - assert(!(await wallet2.getBids()).length); + await minerCtx.mineBlocks(BLOCKS, address); + await Promise.all([ + chainBlocks, + wdbBlocks + ]); }); - it('should rescan wallet 2', async () => { - await wdb.rescan(0); - const bal2 = await wallet2.getBalance(); - assert(bal2.unconfirmed > 0); - assert(bal2.tx > 0); - assert((await wallet2.getBids()).length); - }); + it('should rescan when receiving a block', async () => { + const preTip = await wdb.getTip(); + const blocks = forEventCondition(node.chain, 'connect', (entry) => { + return entry.height === preTip.height + 5; + }); + const wdbBlocks = forEventCondition(wdb, 'block connect', (entry) => { + return entry.height === preTip.height + 5; + }); - it('should bid from wallet 2', async () => { - await wallet2.sendBid(name, 2e6, 2e6); - await node.rpc.generateToAddress([1, addr1]); - }); + await Promise.all([ + minerCtx.mineBlocks(5, address), + wdb.rescan(0) + ]); - it('should not have all blinds in either wallet', async () => { - const bids1 = await wallet1.getBids(); - assert.strictEqual(bids1.length, 2); - for (const bid of bids1) { - if (bid.lockup === 1e6) - assert(bid.value === 1e6); - else - assert(bid.value === -1); // unknown - } + await blocks; + await wdbBlocks; - const bids2 = await wallet2.getBids(); - assert.strictEqual(bids2.length, 2); - for (const bid of bids2) { - if (bid.lockup === 2e6) - assert(bid.value === 2e6); - else - assert(bid.value === -1); // unknown - } + const wdbTip = await wdb.getTip(); + assert.strictEqual(wdbTip.height, preTip.height + 5); }); - it('should reveal from each wallet', async () => { - await node.rpc.generateToAddress([biddingPeriod, addr1]); - - heightBeforeReveal = node.chain.height; - - // Wallet 1 only knows blind for one of the bids - const tx1 = await wallet1.sendReveal(name); - assert.strictEqual(tx1.outputs.length, 2); - assert.strictEqual(tx1.outputs[0].value, 1e6); - assert.strictEqual(tx1.outputs[0].covenant.type, rules.types.REVEAL); - assert.strictEqual(tx1.outputs[1].covenant.type, rules.types.NONE); - - // Confirm - await node.rpc.generateToAddress([1, addr1]); - - // Wallet 1 knows there's another bid but can't reveal it. - assert.strictEqual( - (await wallet1.getBids()).length, - 2 - ); - await assert.rejects( - wallet1.sendReveal(name), - {message: `No bids to reveal for name: ${name}.`} - ); - - // Wallet 2 can reveal the second bid - const tx2 = await wallet2.sendReveal(name); - assert.strictEqual(tx2.outputs.length, 2); - assert.strictEqual(tx2.outputs[0].value, 2e6); - assert.strictEqual(tx2.outputs[0].covenant.type, rules.types.REVEAL); - - // Confirm - await node.rpc.generateToAddress([1, addr1]); - }); + it('should rescan when receiving blocks', async () => { + const preTip = await wdb.getTip(); + const minerHeight = minerCtx.height; + const BLOCKS = 50; + + const blocks = forEventCondition(node.chain, 'connect', (entry) => { + return entry.height === minerHeight + BLOCKS; + }); - it('should have all reveals in both wallets', async () => { - const reveals1 = await wallet1.getReveals(); - const reveals2 = await wallet2.getReveals(); + const wdbBlocks = forEventCondition(wdb, 'block connect', (entry) => { + return entry.height === minerHeight + BLOCKS; + }); - assert.strictEqual(reveals1.length, 2); - assert.strictEqual(reveals2.length, 2); + const promises = [ + minerCtx.mineBlocks(BLOCKS, address) + ]; - for (const reveal of reveals1.concat(reveals2)) { - assert(reveal.own); - assert(reveal.value); - } + await forEvent(node.chain, 'connect'); + promises.push(wdb.rescan(0)); + await Promise.all(promises); + + await blocks; + await wdbBlocks; + + const tip = await wdb.getTip(); + + assert.strictEqual(tip.height, preTip.height + BLOCKS); }); - it('should restore wallet 1 from seed into wallet 3', async () => { - const {mnemonic} = wallet1.master; - wallet3 = await wdb.create({mnemonic}); + it('should rescan when chain is reorging', async () => { + const minerHeight = minerCtx.height; + const BLOCKS = 50; + const reorgHeight = minerHeight - 10; + const newHeight = minerHeight + 40; + + const blocks = forEventCondition(node.chain, 'connect', (entry) => { + return entry.height === newHeight; + }, 10000); + + const walletBlocks = forEventCondition(wdb, 'block connect', (entry) => { + return entry.height === newHeight; + }, 10000); + + const reorgEntry = await minerCtx.chain.getEntry(reorgHeight); + + const promises = [ + minerCtx.mineBlocks(BLOCKS, address, reorgEntry) + ]; + + // We start rescan only after first disconnect is detected to ensure + // wallet guard is set. + await forEvent(node.chain, 'disconnect'); + promises.push(wdb.rescan(0)); + await Promise.all(promises); + + await blocks; + await walletBlocks; + + const tip = await wdb.getTip(); + assert.strictEqual(tip.height, newHeight); }); - it('should just rescan reveal phase', async () => { - await wdb.rescan(heightBeforeReveal); + // Rescanning alternate chain. + it('should rescan when chain is reorging (alternate chain)', async () => { + const minerHeight = minerCtx.height; + const BLOCKS = 50; + const reorgHeight = minerHeight - 20; + + const reorgEntry = await minerCtx.chain.getEntry(reorgHeight); + const mineBlocks = minerCtx.mineBlocks(BLOCKS, address, reorgEntry); - let bal1 = await wallet1.getBalance(); - let bal3 = await wallet3.getBalance(); + // We start rescan only after first disconnect is detected to ensure + // wallet guard is set. + await forEvent(node.chain, 'disconnect'); - assert.notDeepStrictEqual(bal1, bal3); + // abort should also report reason as an error. + const errorEvents = forEvent(wdb, 'error', 1); + + let err; + try { + // Because we are rescanning within the rescan blocks, + // these blocks will end up in alternate chain, resulting + // in error. + await wdb.rescan(minerHeight - 5); + } catch (e) { + err = e; + } - // Complete rescan cleans everything up - await wdb.rescan(0); + assert(err); + assert.strictEqual(err.message, 'Cannot rescan an alternate chain.'); - bal1 = await wallet1.getBalance(); - bal3 = await wallet3.getBalance(); + const errors = await errorEvents; + assert.strictEqual(errors.length, 1); + const errEv = errors[0].values[0]; + assert(errEv); + assert.strictEqual(errEv.message, 'Cannot rescan an alternate chain.'); - assert.deepStrictEqual(bal1, bal3); + await mineBlocks; }); }); + } }); + +async function deriveAddresses(walletClient, depth) { + const accInfo = await walletClient.getAccount('default'); + let currentDepth = accInfo.receiveDepth; + + if (depth <= currentDepth) + return; + + while (currentDepth !== depth) { + const addr = await walletClient.createAddress('default'); + currentDepth = addr.index; + } +} + +async function getAddress(walletClient, depth = -1, network = regtest) { + const accInfo = await walletClient.getAccount('default'); + const {accountKey, lookahead} = accInfo; + + if (depth === -1) + depth = accInfo.receiveDepth; + + const XPUBKey = HDPublicKey.fromBase58(accountKey, network); + const key = XPUBKey.derive(0).derive(depth).publicKey; + const address = Address.fromPubkey(key); + + const gappedDepth = depth + lookahead + 1; + return {address, depth, gappedDepth}; +} + +async function generateGappedAddresses(walletClient, count, network = regtest) { + let depth = -1; + + const addresses = []; + + // generate gapped addresses. + for (let i = 0; i < count; i++) { + const addrInfo = await getAddress(walletClient, depth, network); + + addresses.push({ + address: addrInfo.address, + depth: addrInfo.depth, + gappedDepth: addrInfo.gappedDepth + }); + + depth = addrInfo.gappedDepth; + } + + return addresses; +} diff --git a/test/wallet-rpc-test.js b/test/wallet-rpc-test.js index 9ab9f7879..ea1c93439 100644 --- a/test/wallet-rpc-test.js +++ b/test/wallet-rpc-test.js @@ -1,7 +1,7 @@ 'use strict'; -const {NodeClient,WalletClient} = require('hs-client'); const assert = require('bsert'); +const {NodeClient, WalletClient} = require('../lib/client'); const FullNode = require('../lib/node/fullnode'); const Network = require('../lib/protocol/network'); const Mnemonic = require('../lib/hd/mnemonic'); @@ -1109,7 +1109,7 @@ describe('Wallet RPC Methods', function() { // Bob cannot. await wclient.execute('selectwallet', ['msBob']); - assert.rejects( + await assert.rejects( wclient.execute('createreveal', [name]), {message: `No bids to reveal for name: ${name}.`} ); diff --git a/test/wallet-test.js b/test/wallet-test.js index d6d90f48c..f9ebd56b2 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -1,20 +1,17 @@ 'use strict'; const assert = require('bsert'); -const {WalletClient} = require('hs-client'); +const WalletClient = require('../lib/client/wallet'); const consensus = require('../lib/protocol/consensus'); const Network = require('../lib/protocol/network'); const util = require('../lib/utils/util'); -const blake2b = require('bcrypto/lib/blake2b'); const random = require('bcrypto/lib/random'); const FullNode = require('../lib/node/fullnode'); const WalletDB = require('../lib/wallet/walletdb'); const WorkerPool = require('../lib/workers/workerpool'); const Address = require('../lib/primitives/address'); const MTX = require('../lib/primitives/mtx'); -const ChainEntry = require('../lib/blockchain/chainentry'); const {Resource} = require('../lib/dns/resource'); -const Block = require('../lib/primitives/block'); const Coin = require('../lib/primitives/coin'); const KeyRing = require('../lib/primitives/keyring'); const Input = require('../lib/primitives/input'); @@ -26,8 +23,17 @@ const HDPrivateKey = require('../lib/hd/private'); const Mnemonic = require('../lib/hd/mnemonic'); const Wallet = require('../lib/wallet/wallet'); const rules = require('../lib/covenants/rules'); -const {types, hashName} = rules; const {forValue} = require('./util/common'); +const wutils = require('./util/wallet'); +const {ownership} = require('../lib/covenants/ownership'); +const {CachedStubResolver, STUB_SERVERS} = require('./util/stub'); +const { + dummyInput, + curBlock, + nextBlock, + curEntry, + nextEntry +} = wutils; const KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt' + 'qUP9iWfcHgJofs25xbaUpCps9GDXj83NiWvQCAkWQhVj5J4CorfnpKX94AZ'; @@ -48,60 +54,22 @@ let doubleSpendWallet = null; let doubleSpendCoin = null; let watchWallet = null; -function fromU32(num) { - const data = Buffer.allocUnsafe(4); - data.writeUInt32LE(num, 0, true); - return data; -} - -function curBlock(wdb) { - return fakeBlock(wdb.state.height); -}; - -function nextBlock(wdb) { - return fakeBlock(wdb.state.height + 1); -} - -function fakeBlock(height) { - const prev = blake2b.digest(fromU32((height - 1) >>> 0)); - const hash = blake2b.digest(fromU32(height >>> 0)); - const root = blake2b.digest(fromU32((height | 0x80000000) >>> 0)); - - return { - hash: hash, - prevBlock: prev, - merkleRoot: root, - time: 500000000 + (height * (10 * 60)), - bits: 0, - nonce: 0, - height: height, - version: 0, - witnessRoot: Buffer.alloc(32), - treeRoot: Buffer.alloc(32), - reservedRoot: Buffer.alloc(32), - extraNonce: Buffer.alloc(24), - mask: Buffer.alloc(32) - }; -} - -function curEntry(wdb) { - return new ChainEntry(curBlock(wdb)); -} - -function nextEntry(wdb) { - const cur = curEntry(wdb); - const next = new Block(nextBlock(wdb)); - return ChainEntry.fromBlock(next, cur); -} - -function dummyInput() { - const hash = random.randomBytes(32); - return Input.fromOutpoint(new Outpoint(hash, 0)); -} - describe('Wallet', function() { this.timeout(5000); + const originalResolver = ownership.Resolver; + const originalServers = ownership.servers; + + before(() => { + ownership.Resolver = CachedStubResolver; + ownership.servers = STUB_SERVERS; + }); + + after(() => { + ownership.Resolver = originalResolver; + ownership.servers = originalServers; + }); + it('should open walletdb', async () => { network.coinbaseMaturity = 1; await wdb.open(); @@ -227,6 +195,10 @@ describe('Wallet', function() { await wdb.addTX(t4.toTX()); const balance = await alice.getBalance(); + // UCoins: + // t4:0 - 11k + // t4:1 - 11k + assert.strictEqual(balance.coin, 2); assert.strictEqual(balance.unconfirmed, 22000); } @@ -234,28 +206,56 @@ describe('Wallet', function() { await wdb.addTX(t1.toTX()); const balance = await alice.getBalance(); + // UCoins: + // t1:0 - 50k + // t1:1 - 1k + // t4:0 - 11k + // t4:1 - 11k + assert.strictEqual(balance.coin, 4); + // 22000 + 51000 = 73000 assert.strictEqual(balance.unconfirmed, 73000); } { await wdb.addTX(t2.toTX()); + // t2 spends 50k from t1, but adds 48k from t2 + // 2k less = 71000. BUT t2 output is consumed by t4 so: + // -24k. 71000 - 24000 = 47000 + // UCoins: + // t1:0 - 1k (t1:1 - 50k gone) + // t2:0 - 24k (t2:1 is already consumed by t4) + // t4:0 - 11k + // t4:1 - 11k const balance = await alice.getBalance(); - assert.strictEqual(balance.unconfirmed, 71000); + assert.strictEqual(balance.coin, 4); + assert.strictEqual(balance.unconfirmed, 47000); } { await wdb.addTX(t3.toTX()); + // UCoins consumed: + // t1:1 - 1k + // t2:0 - 24k + // t3:0 - 23k - already spent by t4 + // UCoins: + // t4:0 - 11k + // t4:1 - 11k const balance = await alice.getBalance(); - assert.strictEqual(balance.unconfirmed, 69000); + assert.strictEqual(balance.coin, 2); + assert.strictEqual(balance.unconfirmed, 22000); } { await wdb.addTX(f1.toTX()); + // Coins consumed: + // t4:1 - 11k + // Coins: + // t4:0 - 11k const balance = await alice.getBalance(); - assert.strictEqual(balance.unconfirmed, 58000); + assert.strictEqual(balance.unconfirmed, 11000); const txs = await alice.getHistory(); assert(txs.some((wtx) => { @@ -351,6 +351,51 @@ describe('Wallet', function() { } }); + it('should remove unconfirmed coinbase tx', async () => { + const wallet = await wdb.create(); + + const block = nextBlock(wdb); + const normalTX = new MTX(); + + normalTX.addInput(dummyInput()); + normalTX.addOutput(await wallet.receiveAddress(), 5000); + + assert(!normalTX.isCoinbase()); + + const cbTX = new MTX(); + + cbTX.addInput({ + prevout: new Outpoint() + }); + + cbTX.addOutput(await wallet.receiveAddress(), 5000); + assert(cbTX.isCoinbase()); + + const pendingBefore = await wallet.getPending(); + assert.strictEqual(pendingBefore.length, 0); + + await wdb.addBlock(block, [cbTX.toTX(), normalTX.toTX()]); + + const balanceBefore = await wallet.getBalance(); + + assert.strictEqual(balanceBefore.confirmed, balanceBefore.unconfirmed); + assert.strictEqual(balanceBefore.confirmed, 10000); + assert.strictEqual(balanceBefore.tx, 2); + assert.strictEqual(balanceBefore.coin, 2); + + await wdb.removeBlock(block, [cbTX.toTX(), normalTX.toTX()]); + const pending = await wallet.getPending(); + + assert.strictEqual(pending.length, 1); + + const balance = await wallet.getBalance(); + + assert.strictEqual(balance.confirmed, 0); + assert.strictEqual(balance.unconfirmed, 5000); + assert.strictEqual(balance.tx, 1); + assert.strictEqual(balance.coin, 1); + }); + it('should handle double-spend (not our input)', async () => { const wallet = await wdb.create(); @@ -361,16 +406,16 @@ describe('Wallet', function() { await wdb.addTX(t1.toTX()); assert.strictEqual((await wallet.getBalance()).unconfirmed, 50000); - let conflict = false; + let conflict = 0; wallet.on('conflict', () => { - conflict = true; + conflict += 1; }); const t2 = new MTX(); t2.addInput(input); t2.addOutput(new Address(), 5000); await wdb.addTX(t2.toTX()); - assert(conflict); + assert.strictEqual(conflict, 1); assert.strictEqual((await wallet.getBalance()).unconfirmed, 0); }); @@ -389,9 +434,9 @@ describe('Wallet', function() { await wdb.addTX(txa.toTX()); assert.strictEqual((await wallet.getBalance()).unconfirmed, 50000); - let conflict = false; + let conflict = 0; wallet.on('conflict', () => { - conflict = true; + conflict += 1; }); const txb = new MTX(); @@ -400,10 +445,96 @@ describe('Wallet', function() { txb.addOutput(address, 49000); await wdb.addTX(txb.toTX()); - assert(conflict); + assert.strictEqual(conflict, 1); assert.strictEqual((await wallet.getBalance()).unconfirmed, 49000); }); + it('should handle double-spend (with block)', async () => { + const wallet = await wdb.create(); + const address = await wallet.receiveAddress(); + + const hash = random.randomBytes(32); + const input0 = Input.fromOutpoint(new Outpoint(hash, 0)); + const input1 = Input.fromOutpoint(new Outpoint(hash, 1)); + + const txa = new MTX(); + txa.addInput(input0); + txa.addInput(input1); + txa.addOutput(address, 50000); + await wdb.addTX(txa.toTX()); + assert.strictEqual((await wallet.getBalance()).unconfirmed, 50000); + + let conflict = 0; + wallet.on('conflict', () => { + conflict += 1; + }); + + const txb = new MTX(); + txb.addInput(input0); + txb.addInput(input1); + txb.addOutput(address, 49000); + + await wdb.addBlock(nextBlock(wdb), [txb.toTX()]); + assert.strictEqual(conflict, 1); + assert.strictEqual((await wallet.getBalance()).unconfirmed, 49000); + assert.strictEqual((await wallet.getBalance()).confirmed, 49000); + }); + + it('should recover from interrupt when removing conflict', async () => { + const wallet = await wdb.create(); + const address = await wallet.receiveAddress(); + + const hash = random.randomBytes(32); + const input0 = Input.fromOutpoint(new Outpoint(hash, 0)); + const input1 = Input.fromOutpoint(new Outpoint(hash, 1)); + + const txa = new MTX(); + txa.addInput(input0); + txa.addInput(input1); + txa.addOutput(address, 50000); + + await wdb.addTX(txa.toTX()); + assert.strictEqual((await wallet.getBalance()).unconfirmed, 50000); + assert.strictEqual((await wallet.getBalance()).confirmed, 0); + + let conflict = 0; + wallet.on('conflict', () => { + conflict += 1; + }); + + const txb = new MTX(); + txb.addInput(input0); + txb.addInput(input1); + txb.addOutput(address, 49000); + + const removeConflict = wallet.txdb.removeConflict; + + wallet.txdb.removeConflict = async () => { + throw new Error('Unexpected interrupt.'); + }; + + const entry = nextBlock(wdb); + + await assert.rejects(async () => { + await wdb.addBlock(entry, [txb.toTX()]); + }, { + name: 'Error', + message: 'Unexpected interrupt.' + }); + + wallet.txdb.removeConflict = removeConflict; + + assert.strictEqual(conflict, 0); + assert.strictEqual((await wallet.getBalance()).unconfirmed, 50000); + assert.strictEqual((await wallet.getBalance()).confirmed, 0); + + await wdb.addBlock(entry, [txb.toTX()]); + + assert.strictEqual(conflict, 1); + assert.strictEqual((await wallet.getBalance()).unconfirmed, 49000); + assert.strictEqual((await wallet.getBalance()).confirmed, 49000); + }); + it('should handle more missed txs', async () => { const alice = await wdb.create(); const bob = await wdb.create(); @@ -449,34 +580,67 @@ describe('Wallet', function() { await alice.sign(f1); { + // Coins: + // t4:0 - 11k + // t4:1 - 11k await wdb.addTX(t4.toTX()); const balance = await alice.getBalance(); + assert.strictEqual(balance.coin, 2); assert.strictEqual(balance.unconfirmed, 22000); } { + // Coins: + // t1:0 - 50k + // t1:1 - 1k + // t4:0 - 11k + // t4:1 - 11k await wdb.addTX(t1.toTX()); const balance = await alice.getBalance(); + assert.strictEqual(balance.coin, 4); assert.strictEqual(balance.unconfirmed, 73000); } { + // Coins consumed: + // t1:0 - 50k + // Coins already spent: + // t2:1 - 24k + // Coins: + // t1:1 - 1k + // t2:0 - 24k + // t4:0 - 11k + // t4:1 - 11k await wdb.addTX(t2.toTX()); const balance = await alice.getBalance(); - assert.strictEqual(balance.unconfirmed, 71000); + assert.strictEqual(balance.coin, 4); + assert.strictEqual(balance.unconfirmed, 47000); } { + // Coins consumed: + // t1:1 - 1k + // t2:0 - 24k + // Coins already spent: + // t3:0 - 23k + // Coins: + // t4:0 - 11k + // t4:1 - 11k await wdb.addTX(t3.toTX()); const balance = await alice.getBalance(); - assert.strictEqual(balance.unconfirmed, 69000); + assert.strictEqual(balance.coin, 2); + assert.strictEqual(balance.unconfirmed, 22000); } { await wdb.addTX(f1.toTX()); + // Coins consumed (alice) + // t4:1 - 11k + // Coins: + // t4:0 - 11k const balance = await alice.getBalance(); - assert.strictEqual(balance.unconfirmed, 58000); + assert.strictEqual(balance.unconfirmed, 11000); const txs = await alice.getHistory(); assert(txs.some((wtx) => { @@ -1539,7 +1703,7 @@ describe('Wallet', function() { await wdb.addTXMap(b, mtx.hash(), wid); // Add one name to NameMap - await wdb.addNameMap(b, hashName('test123'), wid); + await wdb.addNameMap(b, rules.hashName('test123'), wid); await b.write(); @@ -1548,7 +1712,7 @@ describe('Wallet', function() { assert(wids.has(wid)); // Should have wid in NameMap - let map = await wdb.getNameMap(hashName('test123')); + let map = await wdb.getNameMap(rules.hashName('test123')); assert(map.wids.has(wid)); // Remove wallet @@ -1559,7 +1723,7 @@ describe('Wallet', function() { assert.strictEqual(wids, null); // Should not return wid from NameMap after wid is removed - map = await wdb.getNameMap(hashName('test123')); + map = await wdb.getNameMap(rules.hashName('test123')); assert.strictEqual(map, null); // Should not return wallet after it is removed @@ -1662,7 +1826,7 @@ describe('Wallet', function() { assert.strictEqual(pending.length, policy.MEMPOOL_MAX_ANCESTORS); // One more unconfirmed change spend would exceed the limit - assert.rejects(async () => { + await assert.rejects(async () => { await wallet.send({ outputs: [{ address: recAddr, @@ -1834,7 +1998,7 @@ describe('Wallet', function() { }); it('should throw error with missing outputs', async () => { - const wallet = new Wallet({}); + const wallet = new Wallet({ options: {} }); let err = null; @@ -1845,7 +2009,18 @@ describe('Wallet', function() { } assert(err); - assert.equal(err.message, 'At least one output required.'); + assert.equal(err.message, 'At least one output is required.'); + }); + + it('should pass nowFn to the txdb', async () => { + const nowFn = () => 1; + const wallet = new Wallet({ + options: { + nowFn + } + }); + + assert.strictEqual(wallet.txdb.nowFn(), nowFn()); }); it('should cleanup', async () => { @@ -2002,12 +2177,14 @@ describe('Wallet', function() { // Hack required to focus test on txdb mechanics. // We don't otherwise need WalletDB or Blockchain + // TODO: Remove this after #888 is merged. wdb.getRenewalBlock = () => { return network.genesis.hash; }; before(async () => { await wdb.open(); + await wdb.connect(); wallet = await wdb.create(); recip = await wdb.create(); // rollout all names @@ -2015,6 +2192,7 @@ describe('Wallet', function() { }); after(async () => { + await wdb.disconnect(); await wdb.close(); }); @@ -2048,7 +2226,7 @@ describe('Wallet', function() { }); it('should send and confirm OPEN', async () => { - const open = await wallet.sendOpen(name, false, {hardFee: fee}); + const open = await wallet.sendOpen(name, {hardFee: fee}); uTXCount++; // Check @@ -2157,13 +2335,14 @@ describe('Wallet', function() { const output = new Output(); output.value = secondHighest; - output.covenant.type = types.REVEAL; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(start); - output.covenant.push(Buffer.from(name, 'ascii')); - output.covenant.pushHash(Buffer.alloc(32)); + output.covenant.setReveal( + nameHash, + start, + Buffer.alloc(32) + ); const mtx = new MTX(); + mtx.addInput(dummyInput()); mtx.outputs.push(output); // Confirm external REVEAL @@ -2270,7 +2449,7 @@ describe('Wallet', function() { uTXCount++; // Check - const senderBal3 = await wallet.getBalance(); + const senderBal3 = await wallet.getBalance(); assert.strictEqual(senderBal3.tx, 7); // One less wallet coin because name UTXO belongs to recip now assert.strictEqual(senderBal3.coin, 3); @@ -2369,6 +2548,7 @@ describe('Wallet', function() { // Hack required to focus test on txdb mechanics. // We don't otherwise need WalletDB or Blockchain + // TODO: Remove this after #888 is merged. wdb.getRenewalBlock = () => { return network.genesis.hash; }; @@ -2414,7 +2594,7 @@ describe('Wallet', function() { }); it('should confirm new OPEN', async () => { - const open = await wallet.createOpen(name, false, {hardFee: fee}); + const open = await wallet.createOpen(name, { hardFee: fee }); // Check let bal = await wallet.getBalance(); @@ -2523,13 +2703,14 @@ describe('Wallet', function() { const output = new Output(); output.value = secondHighest; - output.covenant.type = types.REVEAL; - output.covenant.pushHash(nameHash); - output.covenant.pushU32(start); - output.covenant.push(Buffer.from(name, 'ascii')); - output.covenant.pushHash(Buffer.alloc(32)); + output.covenant.setReveal( + nameHash, + start, + Buffer.alloc(32) + ); const mtx = new MTX(); + mtx.addInput(dummyInput()); mtx.outputs.push(output); // Confirm external REVEAL @@ -2750,6 +2931,46 @@ describe('Wallet', function() { assert.equal(txCount, 1); assert.equal(confirmedCount, 1); }); + + it('should emit conflict event (multiple inputs)', async () => { + const wallet = await wdb.create({id: 'test2'}); + const address = await wallet.receiveAddress(); + + const wclient = new WalletClient({port: ports.wallet}); + await wclient.open(); + + const cwallet = wclient.wallet(wallet.id, wallet.token); + await cwallet.open(); + + try { + const hash = random.randomBytes(32); + const input0 = Input.fromOutpoint(new Outpoint(hash, 0)); + const input1 = Input.fromOutpoint(new Outpoint(hash, 1)); + + const txa = new MTX(); + txa.addInput(input0); + txa.addInput(input1); + txa.addOutput(address, 50000); + await wdb.addTX(txa.toTX()); + assert.strictEqual((await wallet.getBalance()).unconfirmed, 50000); + + let conflict = 0; + cwallet.on('conflict', () => { + conflict += 1; + }); + + const txb = new MTX(); + txb.addInput(input0); + txb.addInput(input1); + txb.addOutput(address, 49000); + await wdb.addTX(txb.toTX()); + + assert.strictEqual(conflict, 1); + assert.strictEqual((await wallet.getBalance()).unconfirmed, 49000); + } finally { + await wclient.close(); + } + }); }); describe('Wallet Name Claims', function() { @@ -2763,7 +2984,7 @@ describe('Wallet', function() { // Verifiable with reserved-browser.js and names.json const lockup = 6800000000000 + 503513487; // By setting the fee rate to zero when we create the claim, - // we can ensure determinstic wallet values even if the size of + // we can ensure deterministic wallet values even if the size of // claim changes due to external factors like Cloudflare updating // their real world DNS zone, which is retrieved by the wallet in this test. const fee = 0; @@ -2772,6 +2993,7 @@ describe('Wallet', function() { before(async () => { await wdb.open(); + await wdb.connect(); wallet = await wdb.create(); for (let i = 0; i < 3; i++) { @@ -2781,6 +3003,7 @@ describe('Wallet', function() { }); after(async () => { + await wdb.disconnect(); await wdb.close(); }); @@ -2790,6 +3013,8 @@ describe('Wallet', function() { }); it('should confirm cloudflare CLAIM', async () => { + this.timeout(10000); + // Use a fresh wallet. const pre = await wallet.getBalance(); assert.equal(pre.tx, 0); @@ -2959,6 +3184,7 @@ describe('Wallet', function() { // Hack required to focus test on txdb mechanics. // We don't otherwise need WalletDB or Blockchain + // TODO: Remove this after #888 is merged. wdb.getRenewalBlock = () => { return network.genesis.hash; }; @@ -3004,7 +3230,7 @@ describe('Wallet', function() { }); it('should send and confirm OPEN', async () => { - const open = await wallet.sendOpen(name, false, { hardFee: fee }); + const open = await wallet.sendOpen(name, { hardFee: fee }); uTXCount++; // Check @@ -3079,11 +3305,11 @@ describe('Wallet', function() { assert.strictEqual(bal.ulocked, lockup); assert.strictEqual(bal.clocked, lockup); - const auctionsTxs = await wallet.createAuctionTxs(name, value, lockup, { + const auctionsTXs = await wallet.createAuctionTXs(name, value, lockup, { hardFee: fee }); - const winningBidUnsent = auctionsTxs.bid; - unsentReveal = auctionsTxs.reveal; + const winningBidUnsent = auctionsTXs.bid; + unsentReveal = auctionsTXs.reveal; const winningBid = await wallet.sendMTX(winningBidUnsent, null); uTXCount++; @@ -3217,7 +3443,7 @@ describe('Wallet', function() { assert.strictEqual(bal.ulocked, value); assert.strictEqual(bal.clocked, value + secondHighest); - // Confirm REGISTER + // Confirm REDEEM const block = { height: wdb.height + 1, hash: Buffer.alloc(32), @@ -3405,4 +3631,135 @@ describe('Wallet', function() { assert.strictEqual(await bob.txdb.hasBlind(expectedBlinds.bob), true); }); }); + + describe('Bid and Reveal by Reveal and Bid', function () { + const network = Network.get('regtest'); + const wdb = new WalletDB({ network }); + + // Hack required to focus test on txdb mechanics. + // We don't otherwise need WalletDB or Blockchain + // TODO: Remove this after #888 is merged. + wdb.getRenewalBlock = () => { + return network.genesis.hash; + }; + + const mineBlocks = async (count) => { + for (let i = 0; i < count; i++) { + await wdb.addBlock(nextEntry(wdb), []); + } + }; + + const NAME = rules.grindName(10, 1, network); + const NAMEHASH = rules.hashString(NAME); + let wallet; + + const BASE_BID = 1e6; + const BASE_LOCKUP = 2e6; + const BID_COUNT = 5; + const bids = []; + let revealMTX; + + before(async () => { + await wdb.open(); + await wdb.connect(); + + wallet = await wdb.create(); + }); + + after(async () => { + await wdb.disconnect(); + await wdb.close(); + }); + + it('should fund wallet', async () => { + const addr = await wallet.receiveAddress(); + + const txs = []; + for (let i = 0; i < BID_COUNT; i++) { + const mtx = new MTX(); + mtx.addOutpoint(new Outpoint(Buffer.alloc(32), 0)); + mtx.addOutput(addr, 20e6); + txs.push(mtx.toTX()); + } + + await wdb.addBlock(nextEntry(wdb), txs); + }); + + it('should open names', async () => { + const open = await wallet.createOpen(NAME); + await wdb.addBlock(nextEntry(wdb), [open.toTX()]); + await mineBlocks(network.names.treeInterval + 1); + }); + + it('should create and get bid', async () => { + for (let i = 0; i < BID_COUNT; i++) { + const bid = await wallet.createBid(NAME, BASE_BID + i, BASE_LOCKUP + i); + await wdb.addTX(bid.toTX()); + bids.push(bid); + } + + const txs = bids.map(b => b.toTX()); + await wdb.addBlock(nextEntry(wdb), txs); + + for (const [index, bid] of bids.entries()) { + const bidOut = bid.outpoint(0); + const blindBid = await wallet.getBid(NAMEHASH, bidOut); + assert.ok(blindBid); + assert.bufferEqual(blindBid.nameHash, NAMEHASH); + assert.bufferEqual(blindBid.prevout.hash, bid.hash()); + assert.strictEqual(blindBid.prevout.index, 0); + assert.strictEqual(blindBid.lockup, BASE_LOCKUP + index); + assert.strictEqual(blindBid.height, wdb.state.height); + assert.strictEqual(blindBid.own, true); + } + + await mineBlocks(network.names.biddingPeriod); + }); + + it('should create and get reveal', async () => { + revealMTX = await wallet.createReveal(NAME); + await wdb.addBlock(nextEntry(wdb), [revealMTX.toTX()]); + + for (const [index, out] of revealMTX.outputs.entries()) { + if (!out.covenant.isReveal()) + continue; + + const revealOut = revealMTX.outpoint(index); + const bidReveal = await wallet.getReveal(NAMEHASH, revealOut); + + assert.ok(bidReveal); + assert.bufferEqual(bidReveal.prevout.hash, revealMTX.hash()); + assert.strictEqual(bidReveal.prevout.index, index); + assert.bufferEqual(bidReveal.nameHash, NAMEHASH); + assert.strictEqual(bidReveal.height, wdb.state.height); + assert.strictEqual(bidReveal.own, true); + } + }); + + it('should get reveal by bid', async () => { + for (const [index, bidTX] of bids.entries()) { + const bidOut = bidTX.outpoint(0); + const bidReveal = await wallet.getRevealByBid(NAMEHASH, bidOut); + assert.ok(bidReveal); + + assert.bufferEqual(bidReveal.bidPrevout.hash, bidOut.hash); + assert.strictEqual(bidReveal.bidPrevout.index, bidOut.index); + assert.strictEqual(bidReveal.value, BASE_BID + index); + } + }); + + it('should get bid by reveal', async () => { + for (const bidTX of bids) { + const bidOut = bidTX.outpoint(0); + const bidReveal = await wallet.getRevealByBid(NAMEHASH, bidOut); + assert.ok(bidReveal); + + const origBlindBid = await wallet.getBid(NAMEHASH, bidOut); + const blindBid = await wallet.getBidByReveal(NAMEHASH, + new Outpoint(bidReveal.prevout.hash, bidReveal.prevout.index)); + + assert.deepStrictEqual(blindBid, origBlindBid); + } + }); + }); }); diff --git a/test/wallet-unit-test.js b/test/wallet-unit-test.js index ce7749a73..1e6a55302 100644 --- a/test/wallet-unit-test.js +++ b/test/wallet-unit-test.js @@ -5,20 +5,24 @@ const blake2b = require('bcrypto/lib/blake2b'); const base58 = require('bcrypto/lib/encoding/base58'); const random = require('bcrypto/lib/random'); const bio = require('bufio'); -const { - HDPrivateKey, - Mnemonic, - WalletDB, - Network, - wallet: { Wallet } -} = require('../lib/hsd'); +const Network = require('../lib/protocol/network'); +const MTX = require('../lib/primitives/mtx'); +const HDPrivateKey = require('../lib/hd/private'); +const Mnemonic = require('../lib/hd/mnemonic'); +const WalletDB = require('../lib/wallet/walletdb'); +const Wallet = require('../lib/wallet/wallet'); const Account = require('../lib/wallet/account'); +const wutils = require('./util/wallet'); +const {nextEntry, fakeEntry} = require('./util/wallet'); +const MemWallet = require('./util/memwallet'); + +/** @typedef {import('../lib/primitives/tx')} TX */ const mnemonics = require('./data/mnemonic-english.json'); const network = Network.get('main'); describe('Wallet Unit Tests', () => { - describe('constructor', () => { + describe('constructor', function() { // abandon, abandon... about const phrase = mnemonics[0][1]; const passphrase = mnemonics[0][2]; @@ -346,4 +350,211 @@ describe('Wallet Unit Tests', () => { } }); }); + + describe('addBlock', function() { + const ALT_SEED = 0xdeadbeef; + + /** @type {WalletDB} */ + let wdb; + /** @type {Wallet} */ + let wallet; + /** @type {MemWallet} */ + let memwallet; + + beforeEach(async () => { + wdb = new WalletDB({ + network: network.type, + memory: true + }); + + await wdb.open(); + wallet = wdb.primary; + + memwallet = new MemWallet({ + network + }); + + for (let i = 0; i < 10; i++) { + const entry = nextEntry(wdb); + await wdb.addBlock(entry, []); + } + }); + + afterEach(async () => { + await wdb.close(); + wdb = null; + }); + + // Move forward + it('should progress with 10 block', async () => { + const tip = await wdb.getTip(); + + for (let i = 0; i < 10; i++) { + const entry = nextEntry(wdb); + const added = await wdb.addBlock(entry, []); + assert.ok(added); + assert.strictEqual(added.txs, 0); + assert.strictEqual(added.filterUpdated, false); + assert.equal(wdb.height, entry.height); + } + + assert.strictEqual(wdb.height, tip.height + 10); + }); + + it('should return number of transactions added (owned)', async () => { + const tip = await wdb.getTip(); + const wtx = await fakeWTX(wallet); + const entry = nextEntry(wdb); + const added = await wdb.addBlock(entry, [wtx]); + + assert.ok(added); + assert.strictEqual(added.txs, 1); + assert.strictEqual(added.filterUpdated, true); + assert.equal(wdb.height, tip.height + 1); + }); + + it('should return number of transactions added (none)', async () => { + const tip = await wdb.getTip(); + const entry = nextEntry(wdb); + const added = await wdb.addBlock(entry, []); + + assert.ok(added); + assert.strictEqual(added.txs, 0); + assert.strictEqual(added.filterUpdated, false); + assert.equal(wdb.height, tip.height + 1); + }); + + it('should fail to add block on unusual reorg', async () => { + const tip = await wdb.getTip(); + const entry = nextEntry(wdb, ALT_SEED, ALT_SEED); + + // TODO: Detect sync chain is correct. + const added = await wdb.addBlock(entry, []); + assert.strictEqual(added, null); + assert.strictEqual(wdb.height, tip.height); + }); + + // Same block + it('should re-add the same block', async () => { + const tip = await wdb.getTip(); + const entry = nextEntry(wdb); + const wtx1 = await fakeWTX(wallet); + const wtx2 = await fakeWTX(wallet); + + const added1 = await wdb.addBlock(entry, [wtx1]); + assert.ok(added1); + assert.strictEqual(added1.txs, 1); + assert.strictEqual(added1.filterUpdated, true); + assert.equal(wdb.height, tip.height + 1); + + // Same TX wont show up second time. + const added2 = await wdb.addBlock(entry, [wtx1]); + assert.ok(added2); + assert.strictEqual(added2.txs, 0); + assert.strictEqual(added2.filterUpdated, false); + assert.equal(wdb.height, tip.height + 1); + + const added3 = await wdb.addBlock(entry, [wtx1, wtx2]); + assert.ok(added3); + assert.strictEqual(added3.txs, 1); + // Both txs are using the same address. + assert.strictEqual(added3.filterUpdated, false); + assert.equal(wdb.height, tip.height + 1); + }); + + it('should ignore txs not owned by wallet', async () => { + const tip = await wdb.getTip(); + const addr = memwallet.getReceive().toString(network); + const tx = fakeTX(addr); + + const entry = nextEntry(wdb); + const added = await wdb.addBlock(entry, [tx]); + assert.ok(added); + assert.strictEqual(added.txs, 0); + assert.strictEqual(added.filterUpdated, false); + + assert.strictEqual(wdb.height, tip.height + 1); + }); + + // This should not happen, but there should be guards in place. + it('should resync if the block is the same', async () => { + const tip = await wdb.getTip(); + const entry = fakeEntry(tip.height, 0, ALT_SEED); + + // TODO: Detect sync chain is correct. + const added = await wdb.addBlock(entry, []); + assert.strictEqual(added, null); + }); + + // LOW BLOCKS + it('should ignore blocks before tip', async () => { + const tip = await wdb.getTip(); + const entry = fakeEntry(tip.height - 1); + const wtx = await fakeWTX(wallet); + + // ignore low blocks. + const added = await wdb.addBlock(entry, [wtx]); + assert.strictEqual(added, null); + assert.strictEqual(wdb.height, tip.height); + }); + + it('should sync chain blocks before tip on unusual low block reorg', async () => { + const tip = await wdb.getTip(); + const entry = fakeEntry(tip.height - 1, 0, ALT_SEED); + const wtx = await fakeWTX(wallet); + + // TODO: Detect sync chain is correct. + + // ignore low blocks. + const added = await wdb.addBlock(entry, [wtx]); + assert.strictEqual(added, null); + assert.strictEqual(wdb.height, tip.height); + }); + + // HIGH BLOCKS + it('should rescan for missed blocks', async () => { + const tip = await wdb.getTip(); + // next + 1 + const entry = fakeEntry(tip.height + 2); + + let rescan = false; + let rescanHash = null; + + wdb.client.rescanInteractive = async (hash) => { + rescan = true; + rescanHash = hash; + }; + + const added = await wdb.addBlock(entry, []); + assert.strictEqual(added, null); + + assert.strictEqual(rescan, true); + assert.bufferEqual(rescanHash, tip.hash); + }); + }); }); + +/** + * @param {String} addr + * @returns {TX} + */ + +function fakeTX(addr) { + const tx = new MTX(); + tx.addInput(wutils.dummyInput()); + tx.addOutput({ + address: addr, + value: 5460 + }); + return tx.toTX(); +} + +/** + * @param {Wallet} wallet + * @returns {Promise} + */ + +async function fakeWTX(wallet) { + const addr = await wallet.receiveAddress(); + return fakeTX(addr); +}