Merge pull request #5 from tornadocash/queue

Add redis Queue
This commit is contained in:
Roman Storm 2019-12-12 11:09:12 -08:00 committed by GitHub
commit 030fb4800f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 394 additions and 36 deletions

View File

@ -1,5 +1,6 @@
NET_ID=42
RPC_URL=https://kovan.infura.io
REDIS_URL=redis://127.0.0.1:6379
# without 0x prefix
PRIVATE_KEY=

View File

@ -2,9 +2,9 @@ require('dotenv').config()
module.exports = {
netId: Number(process.env.NET_ID) || 42,
redisUrl: process.env.REDIS_URL,
rpcUrl: process.env.RPC_URL || 'https://kovan.infura.io/v3/a3f4d001c1fc4a359ea70dd27fd9cb51',
privateKey: process.env.PRIVATE_KEY,
nonce: 0,
mixers: {
netId1: {
dai: {

View File

@ -9,9 +9,10 @@ services:
NET_ID: 42
RPC_URL: https://kovan.infura.io
# without 0x prefix
PRIVATE_KEY:
PRIVATE_KEY:
# 2.5 means 2.5%
RELAYER_FEE: 2.5
REDIS_URL: redis://redis:6379
nginx:
image: jwilder/nginx-proxy
restart: always
@ -29,4 +30,7 @@ services:
volumes_from:
- nginx
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
redis:
image: redis
restart: always

View File

@ -11,9 +11,11 @@
"author": "tornado.cash",
"license": "MIT",
"dependencies": {
"bull": "^3.12.1",
"coingecko-api": "^1.0.6",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"ioredis": "^4.14.1",
"node-fetch": "^2.6.0",
"web3": "^1.2.2",
"web3-utils": "^1.2.2"

View File

@ -3,8 +3,7 @@ const fetch = require('node-fetch')
const { toWei } = require('web3-utils')
const { gasOracleUrls, defaultGasPrice } = require('../config')
const { getMainnetTokens } = require('./utils')
const config = require ('../config')
const { redisClient } = require('./redis')
class Fetcher {
constructor(web3) {
@ -70,8 +69,9 @@ class Fetcher {
}
async fetchNonce() {
try {
config.nonce = await this.web3.eth.getTransactionCount(this.web3.eth.defaultAccount)
console.log(`Current nonce: ${config.nonce}`)
const nonce = await this.web3.eth.getTransactionCount(this.web3.eth.defaultAccount)
await redisClient.set('nonce', nonce)
console.log(`Current nonce: ${nonce}`)
} catch(e) {
console.error('fetchNonce failed', e.message)
setTimeout(this.fetchNonce, 3000)

View File

@ -4,7 +4,7 @@ const relayController = require('./relayController')
const { fetcher, web3 } = require('./instances')
const { getMixers } = require('./utils')
const mixers = getMixers()
const { redisClient } = require('./redis')
const app = express()
app.use(express.json())
@ -28,9 +28,10 @@ app.get('/', function (req, res) {
res.send('This is <a href=https://tornado.cash>tornado.cash</a> Relayer service. Check the <a href=/status>/status</a> for settings')
})
app.get('/status', function (req, res) {
app.get('/status', async function (req, res) {
let nonce = await redisClient.get('nonce')
const { ethPrices, gasPrices } = fetcher
res.json({ relayerAddress: web3.eth.defaultAccount, mixers, gasPrices, netId, ethPrices, relayerServiceFee })
res.json({ relayerAddress: web3.eth.defaultAccount, mixers, gasPrices, netId, ethPrices, relayerServiceFee, nonce })
})
app.post('/relay', relayController)

19
src/redis.js Normal file
View File

@ -0,0 +1,19 @@
const { redisUrl } = require('../config')
const Redis = require('ioredis')
const redisClient = new Redis(redisUrl)
const subscriber = new Redis(redisUrl)
const redisOpts = {
createClient: function (type) {
switch (type) {
case 'client':
return redisClient
case 'subscriber':
return subscriber
default:
return new Redis(redisUrl)
}
}
}
module.exports = { redisOpts, redisClient }

View File

@ -1,15 +1,27 @@
const Queue = require('bull')
const { numberToHex, toWei, toHex, toBN, toChecksumAddress } = require('web3-utils')
const mixerABI = require('../abis/mixerABI.json')
const {
isValidProof, isValidArgs, isKnownContract, isEnoughFee
} = require('./utils')
const config = require('../config')
const { redisClient, redisOpts } = require('./redis')
const { web3, fetcher } = require('./instances')
const withdrawQueue = new Queue('withdraw', redisOpts)
async function relay (req, resp) {
const reponseCbs = {}
let respLambda = (job, { msg, status }) => {
const resp = reponseCbs[job.id]
resp.status(status).json(msg)
delete reponseCbs[job.id]
}
withdrawQueue.on('completed', respLambda)
async function relayController(req, resp) {
let requestJob
const { proof, args, contract } = req.body
const gasPrices = fetcher.gasPrices
let { valid , reason } = isValidProof(proof)
if (!valid) {
console.log('Proof is invalid:', reason)
@ -37,7 +49,7 @@ async function relay (req, resp) {
toBN(args[4]),
toBN(args[5])
]
console.log('fee, refund', fee.toString(), refund.toString())
console.log('fee, refund', fee.toString(), refund.toString(), recipient)
if (currency === 'eth' && !refund.isZero()) {
return resp.status(400).json({ error: 'Cannot send refund for eth currency.' })
}
@ -47,15 +59,39 @@ async function relay (req, resp) {
return resp.status(400).json({ error: 'Relayer address is invalid' })
}
await redisClient.set('foo', 'bar')
requestJob = await withdrawQueue.add({
contract, nullifierHash, root, proof, args, currency, amount, fee: fee.toString()
}, { removeOnComplete: true })
reponseCbs[requestJob.id] = resp
}
withdrawQueue.process(async function(job, done){
console.log(Date.now(), ' withdraw started', job.id)
const gasPrices = fetcher.gasPrices
const { contract, nullifierHash, root, proof, args, refund, currency, amount, fee } = job.data
// cb()
// job.data contains the custom data passed when the job was created
// job.id contains id of this job.
try {
const mixer = new web3.eth.Contract(mixerABI, req.body.contract)
const mixer = new web3.eth.Contract(mixerABI, contract)
const isSpent = await mixer.methods.isSpent(nullifierHash).call()
if (isSpent) {
return resp.status(400).json({ error: 'The note has been spent.' })
done(null, {
status: 400,
msg: {
error: 'The note has been spent.'
}
})
}
const isKnownRoot = await mixer.methods.isKnownRoot(root).call()
if (!isKnownRoot) {
return resp.status(400).json({ error: 'The merkle root is too old or invalid.' })
done(null, {
status: 400,
msg: {
error: 'The merkle root is too old or invalid.'
}
})
}
let gas = await mixer.methods.withdraw(proof, ...args).estimateGas({
@ -65,13 +101,18 @@ async function relay (req, resp) {
gas += 50000
const ethPrices = fetcher.ethPrices
const { isEnough, reason } = isEnoughFee({ gas, gasPrices, currency, amount, refund, ethPrices, fee })
const { isEnough, reason } = isEnoughFee({ gas, gasPrices, currency, amount, refund, ethPrices, fee: toBN(fee) })
if (!isEnough) {
console.log(`Wrong fee: ${reason}`)
return resp.status(400).json({ error: reason })
done(null, {
status: 400,
msg: { error: reason }
})
}
const data = mixer.methods.withdraw(proof, ...args).encodeABI()
let nonce = Number(await redisClient.get('nonce'))
console.log('nonce', nonce)
const tx = {
from: web3.eth.defaultAccount,
value: numberToHex(refund),
@ -80,24 +121,50 @@ async function relay (req, resp) {
to: mixer._address,
netId: config.netId,
data,
nonce: config.nonce
nonce
}
config.nonce++
let signedTx = await web3.eth.accounts.signTransaction(tx, config.privateKey)
let result = web3.eth.sendSignedTransaction(signedTx.rawTransaction)
result.once('transactionHash', function(txHash){
resp.json({ txHash })
console.log(`A new successfully sent tx ${txHash} for the ${recipient}`)
}).on('error', function(e){
config.nonce--
console.error('on transactionHash error', e.message)
return resp.status(400).json({ error: 'Proof is malformed.' })
})
nonce += 1
await redisClient.set('nonce', nonce)
sendTx(tx, done)
} catch (e) {
console.error(e, 'estimate gas failed')
return resp.status(400).json({ error: 'Proof is malformed or spent.' })
done(null, {
status: 400,
msg: { error: 'Internal Relayer Error. Please use a different relayer service' }
})
}
}
})
module.exports = relay
async function sendTx(tx, done, retryAttempt = 1) {
let signedTx = await web3.eth.accounts.signTransaction(tx, config.privateKey)
let result = web3.eth.sendSignedTransaction(signedTx.rawTransaction)
result.once('transactionHash', function(txHash){
done(null, {
status: 200,
msg: { txHash }
})
console.log(`A new successfully sent tx ${txHash}`)
}).on('error', async function(e){
console.log('error', e.message)
if(e.message === 'Returned error: Transaction gas price supplied is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.'
|| e.message === 'Returned error: Transaction nonce is too low. Try incrementing the nonce.') {
console.log('nonce too low, retrying')
if(retryAttempt <= 10) {
retryAttempt++
const newNonce = tx.nonce + 1
tx.nonce = newNonce
await redisClient.set('nonce', newNonce)
sendTx(tx, done, retryAttempt)
return
}
}
console.error('on transactionHash error', e.message)
done(null, {
status: 400,
msg: { error: 'Internal Relayer Error. Please use a different relayer service' }
})
})
}
module.exports = relayController

268
yarn.lock
View File

@ -382,6 +382,22 @@ buffer@^5.0.5, buffer@^5.2.1:
base64-js "^1.0.2"
ieee754 "^1.1.4"
bull@^3.12.1:
version "3.12.1"
resolved "https://registry.yarnpkg.com/bull/-/bull-3.12.1.tgz#ced62d0afca81c9264b44f1b6f39243df5d2e73f"
integrity sha512-X3bSP7gTqPXLYVSyUtQuTOqZuU0GwVbV304Et84Z8bxYP60R1VD3FUOLsESVRA9LIUEOWVH3hE8MFqlszmO0Gw==
dependencies:
cron-parser "^2.13.0"
debuglog "^1.0.0"
get-port "^5.0.0"
ioredis "^4.14.1"
lodash "^4.17.15"
p-timeout "^3.1.0"
promise.prototype.finally "^3.1.1"
semver "^6.3.0"
util.promisify "^1.0.0"
uuid "^3.3.3"
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
@ -456,6 +472,11 @@ clone-response@^1.0.2:
dependencies:
mimic-response "^1.0.0"
cluster-key-slot@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d"
integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==
coingecko-api@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/coingecko-api/-/coingecko-api-1.0.6.tgz#ecc42eb96fb1cc721e319c3d06244a642394ab34"
@ -563,6 +584,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
cron-parser@^2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.13.0.tgz#6f930bb6f2931790d2a9eec83b3ec276e27a6725"
integrity sha512-UWeIpnRb0eyoWPVk+pD3TDpNx3KCFQeezO224oJIkktBrcW6RoAPOx5zIKprZGfk6vcYSmA8yQXItejSaDBhbQ==
dependencies:
is-nan "^1.2.1"
moment-timezone "^0.5.25"
cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@ -613,13 +642,18 @@ debug@2.6.9, debug@^2.2.0:
dependencies:
ms "2.0.0"
debug@^4.0.1:
debug@^4.0.1, debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
debuglog@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@ -695,11 +729,23 @@ defer-to-connect@^1.0.1:
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.0.tgz#b41bd7efa8508cef13f8456975f7a278c72833fd"
integrity sha512-WE2sZoctWm/v4smfCAdjYbrfS55JiMRdlY9ZubFhsYbteCK9+BvAx4YV7nPjYM6ZnX5BcoVKwfmyx9sIFTgQMQ==
define-properties@^1.1.1, define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
object-keys "^1.0.12"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
denque@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
@ -816,6 +862,47 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
es-abstract@^1.17.0-next.0:
version "1.17.0-next.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0-next.0.tgz#1c75d39bc20bb93ac9248915c439f2204ef510e1"
integrity sha512-7t0Ywrgy60w0u1bL1Zc9f6yj0re25yaEA6o+bQOxv6AmzKGWmRkHCXA9i+FtfnxL50mMxV1P3oLC+4vHLwfLPA==
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
is-callable "^1.1.4"
is-regex "^1.0.4"
object-inspect "^1.7.0"
object-keys "^1.1.1"
string.prototype.trimleft "^2.1.0"
string.prototype.trimright "^2.1.0"
es-abstract@^1.5.1:
version "1.16.3"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.3.tgz#52490d978f96ff9f89ec15b5cf244304a5bca161"
integrity sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
is-callable "^1.1.4"
is-regex "^1.0.4"
object-inspect "^1.7.0"
object-keys "^1.1.1"
string.prototype.trimleft "^2.1.0"
string.prototype.trimright "^2.1.0"
es-to-primitive@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
dependencies:
is-callable "^1.1.4"
is-date-object "^1.0.1"
is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.50:
version "0.10.52"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.52.tgz#bb21777e919a04263736ded120a9d665f10ea63f"
@ -1266,11 +1353,23 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
get-port@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.0.0.tgz#aa22b6b86fd926dd7884de3e23332c9f70c031a6"
integrity sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==
dependencies:
type-fest "^0.3.0"
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
@ -1407,6 +1506,11 @@ has-symbol-support-x@^1.4.1:
resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455"
integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==
has-symbols@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
has-to-string-tag-x@^1.2.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
@ -1414,6 +1518,13 @@ has-to-string-tag-x@^1.2.0:
dependencies:
has-symbol-support-x "^1.4.1"
has@^1.0.1, has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
hash-base@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
@ -1562,11 +1673,36 @@ inquirer@^7.0.0:
strip-ansi "^5.1.0"
through "^2.3.6"
ioredis@^4.14.1:
version "4.14.1"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.14.1.tgz#b73ded95fcf220f106d33125a92ef6213aa31318"
integrity sha512-94W+X//GHM+1GJvDk6JPc+8qlM7Dul+9K+lg3/aHixPN7ZGkW6qlvX0DG6At9hWtH2v3B32myfZqWoANUJYGJA==
dependencies:
cluster-key-slot "^1.1.0"
debug "^4.1.1"
denque "^1.1.0"
lodash.defaults "^4.2.0"
lodash.flatten "^4.4.0"
redis-commands "1.5.0"
redis-errors "^1.2.0"
redis-parser "^3.0.0"
standard-as-callback "^2.0.1"
ipaddr.js@1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
is-callable@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
is-date-object@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@ -1599,6 +1735,13 @@ is-hex-prefixed@1.0.0:
resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554"
integrity sha1-fY035q135dEnFIkTxXPggtd39VQ=
is-nan@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.2.1.tgz#9faf65b6fb6db24b7f5c0628475ea71f988401e2"
integrity sha1-n69ltvttskt/XAYoR16nH5iEAeI=
dependencies:
define-properties "^1.1.1"
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
@ -1619,6 +1762,13 @@ is-promise@^2.1.0:
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
is-regex@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
dependencies:
has "^1.0.1"
is-retry-allowed@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
@ -1629,6 +1779,13 @@ is-stream@^1.0.0, is-stream@^1.1.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
is-symbol@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
dependencies:
has-symbols "^1.0.1"
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@ -1765,6 +1922,16 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
lodash.flatten@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
lodash@^4.17.14, lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
@ -1909,6 +2076,18 @@ mock-fs@^4.1.0:
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.10.3.tgz#d0550663dd2b5d33a7c1b8713c6925aab07a04ae"
integrity sha512-bcukePBvuA3qovmq0Qtqu9+1APCIGkFHnsozrPIVromt5XFGGgkQSfaN0H6RI8gStHkO/hRgimvS3tooNes4pQ==
moment-timezone@^0.5.25:
version "0.5.27"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.27.tgz#73adec8139b6fe30452e78f210f27b1f346b8877"
integrity sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==
dependencies:
moment ">= 2.9.0"
"moment@>= 2.9.0":
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -1992,6 +2171,24 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-inspect@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.getownpropertydescriptors@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=
dependencies:
define-properties "^1.1.2"
es-abstract "^1.5.1"
oboe@2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6"
@ -2059,6 +2256,13 @@ p-timeout@^1.1.1:
dependencies:
p-finally "^1.0.0"
p-timeout@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==
dependencies:
p-finally "^1.0.0"
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@ -2176,6 +2380,15 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
promise.prototype.finally@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.2.tgz#b8af89160c9c673cefe3b4c4435b53cfd0287067"
integrity sha512-A2HuJWl2opDH0EafgdjwEw7HysI8ff/n4lW4QEVBCUXFk9QeGecBWv0Deph0UmLe3tTNYegz8MOjsVuE6SMoJA==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.0"
function-bind "^1.1.1"
proxy-addr@~2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
@ -2286,6 +2499,23 @@ readable-stream@^2.3.0, readable-stream@^2.3.5:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
redis-commands@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.5.0.tgz#80d2e20698fe688f227127ff9e5164a7dd17e785"
integrity sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==
redis-errors@^1.0.0, redis-errors@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad"
integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=
redis-parser@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4"
integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=
dependencies:
redis-errors "^1.0.0"
regexpp@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
@ -2559,6 +2789,11 @@ sshpk@^1.7.0:
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
standard-as-callback@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.0.1.tgz#ed8bb25648e15831759b6023bdb87e6b60b38126"
integrity sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
@ -2587,6 +2822,22 @@ string-width@^4.1.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string.prototype.trimleft@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
dependencies:
define-properties "^1.1.3"
function-bind "^1.1.1"
string.prototype.trimright@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
dependencies:
define-properties "^1.1.3"
function-bind "^1.1.1"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
@ -2757,6 +3008,11 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
type-fest@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==
type-fest@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
@ -2856,6 +3112,14 @@ util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
util.promisify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
dependencies:
define-properties "^1.1.2"
object.getownpropertydescriptors "^2.0.3"
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
@ -2871,7 +3135,7 @@ uuid@3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
uuid@^3.3.2:
uuid@^3.3.2, uuid@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==