diff --git a/packages/attestation-service/config/.env.development b/packages/attestation-service/config/.env.development index d429ab4e8a7..a8b1540c59f 100644 --- a/packages/attestation-service/config/.env.development +++ b/packages/attestation-service/config/.env.development @@ -1,4 +1,4 @@ -DB_URL=sqlite://db/dev.db +DATABASE_URL=sqlite://db/dev.db CELO_PROVIDER=https://integration-forno.celo-testnet.org ACCOUNT_ADDRESS=0xE6e53b5fc2e18F51781f14a3ce5E7FD468247a15 ATTESTATION_KEY=x diff --git a/packages/attestation-service/config/database.json b/packages/attestation-service/config/database.json index f15c454248a..76e76db046a 100644 --- a/packages/attestation-service/config/database.json +++ b/packages/attestation-service/config/database.json @@ -1,11 +1,11 @@ { "development": { - "use_env_variable": "DB_URL" + "use_env_variable": "DATABASE_URL" }, "staging": { - "use_env_variable": "DB_URL" + "use_env_variable": "DATABASE_URL" }, "production": { - "use_env_variable": "DB_URL" + "use_env_variable": "DATABASE_URL" } } diff --git a/packages/attestation-service/migrations/20191015211858-create-attestation.js b/packages/attestation-service/migrations/20191015211858-create-attestation.js new file mode 100644 index 00000000000..aca5106d69c --- /dev/null +++ b/packages/attestation-service/migrations/20191015211858-create-attestation.js @@ -0,0 +1,52 @@ +'use strict' +module.exports = { + up: async (queryInterface, Sequelize) => { + const transaction = await queryInterface.sequelize.transaction() + + try { + await queryInterface.createTable('Attestations', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + account: { + allowNull: false, + type: Sequelize.STRING, + }, + phoneNumber: { + allowNull: false, + type: Sequelize.STRING, + }, + issuer: { + allowNull: false, + type: Sequelize.STRING, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }) + + await queryInterface.addIndex( + 'Attestations', + ['account', 'phoneNumber', 'issuer'], + { fields: ['account', 'phoneNumber', 'issuer'], unique: true }, + { transaction } + ) + + await transaction.commit() + } catch (error) { + await transaction.rollback() + throw error + } + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('Attestations') + }, +} diff --git a/packages/attestation-service/package.json b/packages/attestation-service/package.json index 2fe9a73d1cb..99c3564d902 100644 --- a/packages/attestation-service/package.json +++ b/packages/attestation-service/package.json @@ -17,24 +17,29 @@ "clean": "tsc -b . --clean", "clean:all": "yarn clean && rm -rf lib", "prepublishOnly": "yarn build:gen && yarn build", - "start": "TS_NODE_FILES=true ts-node src/index.ts", + "start-ts": "TS_NODE_FILES=true ts-node src/index.ts", + "start": "node lib/index.js", "db:create:dev": "mkdir -p db && touch db/dev.db", "db:migrate": "sequelize db:migrate", + "db:migrate:undo": "sequelize db:migrate:undo", "db:migrate:dev": "sequelize db:migrate", "dev": "CONFIG=config/.env.development nodemon", "lint": "tslint -c tslint.json --project ." }, "dependencies": { - "@celo/contractkit": "0.1.1", + "@celo/contractkit": "0.1.6", "@celo/utils": "^0.1.0", "bignumber.js": "^7.2.0", "body-parser": "1.19.0", "debug": "^4.1.1", "dotenv": "8.0.0", "eth-lib": "^0.2.8", + "io-ts": "2.0.1", + "fp-ts":"2.1.1", "nexmo": "2.4.2", "web3": "1.0.0-beta.37", "express": "4.17.1", + "mysql2": "2.0.0-alpha1", "pg": "7.12.1", "pg-hstore": "2.3.3", "sequelize": "5.13.1", diff --git a/packages/attestation-service/src/attestation.ts b/packages/attestation-service/src/attestation.ts index 9790fef2de5..37411ea33f7 100644 --- a/packages/attestation-service/src/attestation.ts +++ b/packages/attestation-service/src/attestation.ts @@ -1,38 +1,124 @@ +import { AttestationState } from '@celo/contractkit/lib/wrappers/Attestations' import { attestToIdentifier, SignatureUtils } from '@celo/utils' +import { privateKeyToAddress } from '@celo/utils/lib/address' import { retryAsyncWithBackOff } from '@celo/utils/lib/async' import express from 'express' +import * as t from 'io-ts' +import { existingAttestationRequest, kit, persistAttestationRequest } from './db' +import { Address, AddressType, E164Number, E164PhoneNumberType } from './request' import { sendSms } from './sms' -function signAttestation(phoneNumber: string, account: string) { + +export const AttestationRequestType = t.type({ + phoneNumber: E164PhoneNumberType, + account: AddressType, + issuer: AddressType, +}) + +export type AttestationRequest = t.TypeOf + +function getAttestationKey() { if (process.env.ATTESTATION_KEY === undefined) { console.error('Did not specify ATTESTATION_KEY') throw new Error('Did not specify ATTESTATION_KEY') } - const signature = attestToIdentifier(phoneNumber, account, process.env.ATTESTATION_KEY) + return process.env.ATTESTATION_KEY +} + +async function validateAttestationRequest(request: AttestationRequest) { + // check if it exists in the database + if ( + (await existingAttestationRequest(request.phoneNumber, request.account, request.issuer)) !== + null + ) { + throw new Error('Attestation already sent') + } + const key = getAttestationKey() + const address = privateKeyToAddress(key) + + // TODO: Check with the new Accounts.sol + if (address.toLowerCase() !== request.issuer.toLowerCase()) { + throw new Error(`Mismatching issuer, I am ${address}`) + } + + const attestations = await kit.contracts.getAttestations() + const state = await attestations.getAttestationState( + request.phoneNumber, + request.account, + request.issuer + ) + + if (state.attestationState !== AttestationState.Incomplete) { + throw new Error('No incomplete attestation found') + } + + // TODO: Check expiration + return +} + +async function validateAttestation( + attestationRequest: AttestationRequest, + attestationCode: string +) { + const attestations = await kit.contracts.getAttestations() + const isValid = await attestations.validateAttestationCode( + attestationRequest.phoneNumber, + attestationRequest.account, + attestationRequest.issuer, + attestationCode + ) + if (!isValid) { + throw new Error('Valid attestation could not be provided') + } + return +} + +function signAttestation(phoneNumber: E164Number, account: Address) { + const signature = attestToIdentifier(phoneNumber, account, getAttestationKey()) return SignatureUtils.serializeSignature(signature) } function toBase64(str: string) { - return Buffer.from(str, 'hex').toString('base64') + return Buffer.from(str.slice(2), 'hex').toString('base64') } function createAttestationTextMessage(attestationCode: string) { return `<#> ${toBase64(attestationCode)} ${process.env.APP_SIGNATURE}` } -export async function handleAttestationRequest(req: express.Request, res: express.Response) { - // TODO: Should parse request appropriately - - // TODO: Should validate request here - // const attestations = await kit.contracts.getAttestations() - - // Produce attestation - const attestationCode = signAttestation(req.body.phoneNumber, req.body.account) - const textMessage = createAttestationTextMessage(attestationCode) +export async function handleAttestationRequest( + _req: express.Request, + res: express.Response, + attestationRequest: AttestationRequest +) { + let attestationCode + try { + await validateAttestationRequest(attestationRequest) + attestationCode = signAttestation(attestationRequest.phoneNumber, attestationRequest.account) + await validateAttestation(attestationRequest, attestationCode) + } catch (error) { + console.error(error) + res.status(422).json({ success: false, error: error.toString() }) + return + } - // Send the SMS - await retryAsyncWithBackOff(sendSms, 10, [req.body.phoneNumber, textMessage], 1000) + try { + const textMessage = createAttestationTextMessage(attestationCode) + await persistAttestationRequest( + attestationRequest.phoneNumber, + attestationRequest.account, + attestationRequest.issuer + ) + await retryAsyncWithBackOff(sendSms, 10, [attestationRequest.phoneNumber, textMessage], 1000) + } catch (error) { + console.error(error) + res.status(500).json({ + success: false, + error: 'Something went wrong while attempting to send SMS, try again later', + }) + return + } res.json({ success: true }) } diff --git a/packages/attestation-service/src/db.ts b/packages/attestation-service/src/db.ts index 2a2266bd372..5a75590d85c 100644 --- a/packages/attestation-service/src/db.ts +++ b/packages/attestation-service/src/db.ts @@ -1,15 +1,15 @@ import { ContractKit, newKit } from '@celo/contractkit' import { Sequelize } from 'sequelize' import { fetchEnv } from './env' +import Attestation, { AttestationStatic } from './models/attestation' export let sequelize: Sequelize | undefined export function initializeDB() { if (sequelize === undefined) { - sequelize = new Sequelize(fetchEnv('DB_URL')) + sequelize = new Sequelize(fetchEnv('DATABASE_URL')) return sequelize.authenticate() as Promise } - return Promise.resolve() } @@ -20,3 +20,29 @@ export function initializeKit() { kit = newKit(fetchEnv('CELO_PROVIDER')) } } + +let AttestationTable: AttestationStatic + +async function getAttestationTable() { + if (AttestationTable) { + return AttestationTable + } + AttestationTable = await Attestation(sequelize!) + return AttestationTable +} + +export async function existingAttestationRequest( + phoneNumber: string, + account: string, + issuer: string +): Promise { + return (await getAttestationTable()).findOne({ where: { phoneNumber, account, issuer } }) +} + +export async function persistAttestationRequest( + phoneNumber: string, + account: string, + issuer: string +) { + return (await getAttestationTable()).create({ phoneNumber, account, issuer }) +} diff --git a/packages/attestation-service/src/index.ts b/packages/attestation-service/src/index.ts index 6f55ba66bf7..8b7d059f1f1 100644 --- a/packages/attestation-service/src/index.ts +++ b/packages/attestation-service/src/index.ts @@ -1,7 +1,8 @@ import * as dotenv from 'dotenv' import express from 'express' -import { handleAttestationRequest } from './attestation' +import { AttestationRequestType, handleAttestationRequest } from './attestation' import { initializeDB, initializeKit } from './db' +import { createValidatedHandler } from './request' import { initializeSmsProviders } from './sms' async function init() { @@ -18,7 +19,10 @@ async function init() { const port = process.env.PORT || 3000 app.listen(port, () => console.log(`Server running on ${port}!`)) - app.post('/attestations', handleAttestationRequest) + app.post( + '/attestations', + createValidatedHandler(AttestationRequestType, handleAttestationRequest) + ) } init().catch((err) => { diff --git a/packages/attestation-service/src/models/attestation.ts b/packages/attestation-service/src/models/attestation.ts new file mode 100644 index 00000000000..a2b79e765b7 --- /dev/null +++ b/packages/attestation-service/src/models/attestation.ts @@ -0,0 +1,18 @@ +import { BuildOptions, DataTypes, Model, Sequelize } from 'sequelize' + +interface AttestationModel extends Model { + readonly id: number + account: string + phoneNumber: string + issuer: string +} + +export type AttestationStatic = typeof Model & + (new (values?: object, options?: BuildOptions) => AttestationModel) + +export default (sequelize: Sequelize) => + sequelize.define('Attestations', { + account: DataTypes.STRING, + phoneNumber: DataTypes.STRING, + issuer: DataTypes.STRING, + }) as AttestationStatic diff --git a/packages/attestation-service/src/request.ts b/packages/attestation-service/src/request.ts new file mode 100644 index 00000000000..dfd5ae13910 --- /dev/null +++ b/packages/attestation-service/src/request.ts @@ -0,0 +1,83 @@ +import { isE164NumberStrict } from '@celo/utils/lib/phoneNumbers' +import { isValidAddress } from '@celo/utils/lib/signatureUtils' +import express from 'express' +import { either, isLeft } from 'fp-ts/lib/Either' +import * as t from 'io-ts' + +export function createValidatedHandler( + requestType: t.Type, + handler: (req: express.Request, res: express.Response, parsedRequest: T) => Promise +) { + return async (req: express.Request, res: express.Response) => { + const parsedRequest = requestType.decode(req.body) + if (isLeft(parsedRequest)) { + res.status(422).json({ + success: false, + error: 'Error parsing invalid request', + errors: serializeErrors(parsedRequest.left), + }) + } else { + try { + await handler(req, res, parsedRequest.right) + } catch (error) { + console.error(error) + res.status(500).json({ success: false, error: 'Something went wrong' }) + } + } + } +} + +export const E164PhoneNumberType = new t.Type( + 'E164Number', + t.string.is, + (input, context) => + either.chain( + t.string.validate(input, context), + (stringValue) => + isE164NumberStrict(stringValue) + ? t.success(stringValue) + : t.failure(stringValue, context, 'is not a valid e164 number') + ), + String +) + +export const AddressType = new t.Type( + 'Address', + t.string.is, + (input, context) => + either.chain( + t.string.validate(input, context), + (stringValue) => + isValidAddress(stringValue) + ? t.success(stringValue) + : t.failure(stringValue, context, 'is not a valid address') + ), + String +) + +export type Address = t.TypeOf +export type E164Number = t.TypeOf + +function serializeErrors(errors: t.Errors) { + let serializedErrors: any = {} + errors.map((error) => { + const expectedType = error.context[error.context.length - 1].type + const path = error.context.map(({ key }) => key).join('.') + const value = + error.message || + `Expected value at path ${path} to be of type ${expectedType.name}, but received ${ + error.value + }` + + // Create recursive payload in case of nested properties + let payload: any = value + for (let index = error.context.length - 1; index > 0; index--) { + const innerError = payload + payload = {} + payload[error.context[index].key] = innerError + } + + serializedErrors = { ...serializedErrors, ...payload } + }) + return serializedErrors +} diff --git a/packages/celotool/src/cmds/account/verify.ts b/packages/celotool/src/cmds/account/verify.ts index 63b16bc48d4..126a7742f02 100644 --- a/packages/celotool/src/cmds/account/verify.ts +++ b/packages/celotool/src/cmds/account/verify.ts @@ -147,7 +147,7 @@ async function verifyCode( matchingIssuer, code ) - if (isValidRequest === NULL_ADDRESS) { + if (!isValidRequest) { console.warn('Code was not valid') return } diff --git a/packages/contractkit/package.json b/packages/contractkit/package.json index ea2be897e57..becf434c741 100644 --- a/packages/contractkit/package.json +++ b/packages/contractkit/package.json @@ -31,7 +31,7 @@ "bignumber.js": "^7.2.0", "cross-fetch": "3.0.4", "debug": "^4.1.1", - "fp-ts": "2.0.5", + "fp-ts": "2.1.1", "eth-lib": "^0.2.8", "io-ts": "2.0.1", "web3": "1.0.0-beta.37", diff --git a/packages/contractkit/src/wrappers/Attestations.ts b/packages/contractkit/src/wrappers/Attestations.ts index dc78173ebb5..863a4892e0a 100644 --- a/packages/contractkit/src/wrappers/Attestations.ts +++ b/packages/contractkit/src/wrappers/Attestations.ts @@ -2,7 +2,7 @@ import { ECIES, PhoneNumberUtils, SignatureUtils } from '@celo/utils' import { zip3 } from '@celo/utils/lib/collections' import BigNumber from 'bignumber.js' import * as Web3Utils from 'web3-utils' -import { Address, CeloContract } from '../base' +import { Address, CeloContract, NULL_ADDRESS } from '../base' import { Attestations } from '../generated/types/Attestations' import { BaseWrapper, @@ -20,6 +20,10 @@ export interface AttestationStat { total: number } +export interface AttestationStateForIssuer { + attestationState: AttestationState +} + export interface AttestationsToken { address: Address fee: BigNumber @@ -59,6 +63,7 @@ function attestationMessageToSign(phoneHash: string, account: Address) { return messageHash } +const stringIdentity = (x: string) => x export class AttestationsWrapper extends BaseWrapper { /** * Returns the time an attestation can be completable before it is considered expired @@ -94,6 +99,21 @@ export class AttestationsWrapper extends BaseWrapper { (stat) => ({ completed: toNumber(stat[0]), total: toNumber(stat[1]) }) ) + /** + * Returns the attestation state of a phone number/account/issuer tuple + * @param phoneNumber Phone Number + * @param account Account + */ + getAttestationState: ( + phoneNumber: string, + account: Address, + issuer: Address + ) => Promise = proxyCall( + this.contract.methods.getAttestationState, + tupleParser(PhoneNumberUtils.getPhoneHash, stringIdentity, stringIdentity), + (state) => ({ attestationState: parseInt(state[0], 10) }) + ) + /** * Returns the set wallet address for the account * @param account Account @@ -356,6 +376,9 @@ export class AttestationsWrapper extends BaseWrapper { const phoneHash = PhoneNumberUtils.getPhoneHash(phoneNumber) const expectedSourceMessage = attestationMessageToSign(phoneHash, account) const { r, s, v } = parseSignature(expectedSourceMessage, code, issuer.toLowerCase()) - return this.contract.methods.validateAttestationCode(phoneHash, account, v, r, s).call() + const result = await this.contract.methods + .validateAttestationCode(phoneHash, account, v, r, s) + .call() + return result.toLowerCase() !== NULL_ADDRESS } } diff --git a/packages/mobile/src/tokens/saga.ts b/packages/mobile/src/tokens/saga.ts index e0613912947..3d45da08c7a 100644 --- a/packages/mobile/src/tokens/saga.ts +++ b/packages/mobile/src/tokens/saga.ts @@ -47,18 +47,14 @@ export async function convertToContractDecimals(value: BigNumber, token: CURRENC export async function getTokenContract(token: CURRENCY_ENUM) { Logger.debug(TAG + '@getTokenContract', `Fetching contract for ${token}`) await waitForWeb3Sync() - let tokenContract: any switch (token) { case CURRENCY_ENUM.GOLD: - tokenContract = await contractKit.contracts.getGoldToken() - break + return contractKit.contracts.getGoldToken() case CURRENCY_ENUM.DOLLAR: - tokenContract = await contractKit.contracts.getStableToken() - break + return contractKit.contracts.getStableToken() default: throw new Error(`Could not fetch contract for unknown token ${token}`) } - return tokenContract } interface TokenFetchFactory { diff --git a/packages/utils/src/async.ts b/packages/utils/src/async.ts index 7b26b1f8ae7..3397efa09a2 100644 --- a/packages/utils/src/async.ts +++ b/packages/utils/src/async.ts @@ -5,14 +5,14 @@ export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)) } -type InFunction = (...params: any) => Promise +type InFunction = (...params: T) => Promise // Retries an async function when it raises an exeption // if all the tries fail it raises the last thrown exeption -export const retryAsync = async ( - inFunction: InFunction, +export const retryAsync = async ( + inFunction: InFunction, tries: number, - params: any, + params: T, delay = 100 ) => { let saveError @@ -32,10 +32,10 @@ export const retryAsync = async ( // Retries an async function when it raises an exeption // if all the tries fail it raises the last thrown exeption -export const retryAsyncWithBackOff = async ( - inFunction: InFunction, +export const retryAsyncWithBackOff = async ( + inFunction: InFunction, tries: number, - params: any, + params: T, delay = 100, factor = 1.5 ) => { diff --git a/packages/utils/src/phoneNumbers.ts b/packages/utils/src/phoneNumbers.ts index fc12a5f0164..3aabaa31c94 100644 --- a/packages/utils/src/phoneNumbers.ts +++ b/packages/utils/src/phoneNumbers.ts @@ -110,6 +110,15 @@ export function isE164Number(phoneNumber: string) { return E164RegEx.test(phoneNumber) } +// Actually runs through the parsing instead of using a regex +export function isE164NumberStrict(phoneNumber: string) { + const parsedPhoneNumber = phoneUtil.parse(phoneNumber) + if (!phoneUtil.isValidNumber(parsedPhoneNumber)) { + return false + } + return phoneUtil.format(parsedPhoneNumber, PhoneNumberFormat.E164) === phoneNumber +} + export function parsePhoneNumber( phoneNumberRaw: string, defaultCountryCode: string diff --git a/packages/utils/src/signatureUtils.test.ts b/packages/utils/src/signatureUtils.test.ts index c85ac04dfb7..4850893dfef 100644 --- a/packages/utils/src/signatureUtils.test.ts +++ b/packages/utils/src/signatureUtils.test.ts @@ -9,6 +9,6 @@ describe('signatures', () => { const message = Web3Utils.soliditySha3({ type: 'string', value: 'identifier' }) const signature = signMessage(message, pKey, address) const serializedSig = serializeSignature(signature) - parseSignature(message, '0x' + serializedSig, address) + parseSignature(message, serializedSig, address) }) }) diff --git a/packages/utils/src/signatureUtils.ts b/packages/utils/src/signatureUtils.ts index ec90d10c4fb..fce158cff2c 100644 --- a/packages/utils/src/signatureUtils.ts +++ b/packages/utils/src/signatureUtils.ts @@ -28,7 +28,7 @@ export function serializeSignature(signature: Signature) { const serializedV = signature.v.toString(16) const serializedR = signature.r.slice(2) const serializedS = signature.s.slice(2) - return serializedV + serializedR + serializedS + return '0x' + serializedV + serializedR + serializedS } export function parseSignature(messageHash: string, signature: string, signer: string) { diff --git a/yarn.lock b/yarn.lock index 87e0e3e9862..20ee84cf0d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2177,18 +2177,6 @@ resolved "https://registry.yarnpkg.com/@celo/client/-/client-0.0.173.tgz#30d8266f8f4897f1db5c9bf4ad344801e620326f" integrity sha512-pAzo3Oy+V/BEkKb7ZZe0l71s6mOdPLLJZfLNo2Ihm2bx37nx8g4LNkcfj+0vVtRQPtiLrGiQ42U8pby3LwmC4Q== -"@celo/contractkit@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@celo/contractkit/-/contractkit-0.1.1.tgz#bd85812d883b58fbc8d9897a59e2f7311cc0e2eb" - integrity sha512-GQbLuLUEgKtwy2I8ZVi4P0HBVhh9MlT5S90vIrGznb9eZ2vwGahfOxHapVKcxw2I4tbVBNTTWWpcjzOtJZCY7Q== - dependencies: - "@celo/utils" "^0.0.6-beta5" - "@types/debug" "^4.1.5" - bignumber.js "^7.2.0" - debug "^4.1.1" - web3 "1.0.0-beta.37" - web3-utils "1.0.0-beta.37" - "@celo/contractkit@0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@celo/contractkit/-/contractkit-0.1.5.tgz#097052ac8f672f03480a1226f11f0c398c35c75e" @@ -2255,23 +2243,6 @@ lodash "^4.17.14" web3-utils "1.0.0-beta.37" -"@celo/utils@^0.0.6-beta5": - version "0.0.6-beta5" - resolved "https://registry.yarnpkg.com/@celo/utils/-/utils-0.0.6-beta5.tgz#a558cfc9dc68d9ec15888798c95cd6e5f1d3fe67" - integrity sha512-TGIDjr1aCcNRVv2Z3DZkjd71K/vNd6C18BPZw8VfKzSFyt0UcxkbN/X+loqvJSrwymtYgu/s6JdIQD3t14xiJA== - dependencies: - "@umpirsky/country-list" "git://github.com/umpirsky/country-list#05fda51" - bignumber.js "^7.2.0" - bn.js "4.11.8" - country-data "^0.0.31" - crypto-js "^3.1.9-1" - elliptic "^6.4.1" - ethereumjs-util "^5.2.0" - futoin-hkdf "^1.0.3" - google-libphonenumber "^3.2.1" - lodash "^4.17.14" - web3-utils "1.0.0-beta.37" - "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" @@ -11933,6 +11904,11 @@ denodeify@^1.2.1: resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= +denque@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" + integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== + depd@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" @@ -15171,10 +15147,10 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= -fp-ts@2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.0.5.tgz#9560d8a6a4f53cbda9f9b31ed8d1458e41939e07" - integrity sha512-opI5r+rVlpZE7Rhk0YtqsrmxGkbIw0dRNqGca8FEAMMnjomXotG+R9QkLQg20onx7R8qhepAn4CCOP8usma/Xw== +fp-ts@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.1.1.tgz#c910544499d7c959351bb4260ee7c44a544084c1" + integrity sha512-YcWhMdDCFCja0MmaDroTgNu+NWWrrnUEn92nvDgrtVy9Z71YFnhNVIghoHPt8gs82ijoMzFGeWKvArbyICiJgw== fraction.js@4.0.8: version "4.0.8" @@ -15735,7 +15711,7 @@ generate-function@^1.0.1: resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-1.1.0.tgz#54c21b080192b16d9877779c5bb81666e772365f" integrity sha1-VMIbCAGSsW2Yd3ecW7gWZudyNl8= -generate-function@^2.0.0: +generate-function@^2.0.0, generate-function@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== @@ -16553,11 +16529,6 @@ google-gax@^1.0.0, google-gax@^1.1.2: semver "^6.0.0" walkdir "^0.4.0" -google-libphonenumber@^3.2.1: - version "3.2.5" - resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.5.tgz#2ebe6437fd3dbbffd65f4339ad1ba93b3dc56836" - integrity sha512-Y0r7MFCI11UDLn0KaMPBEInhROyIOkWkQIyvWMFVF2I+h+sHE3vbl5a7FVe39td6u/w+nlKDdUMP9dMOZyv+2Q== - google-libphonenumber@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.4.tgz#91d3fe62ca531f154165e6580b1c55ff6bd53abf" @@ -17827,6 +17798,13 @@ iconv-lite@^0.4.17, iconv-lite@^0.4.4: dependencies: safer-buffer "^2.1.0" +iconv-lite@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550" + integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw== + dependencies: + safer-buffer ">= 2.1.2 < 3" + icss-replace-symbols@1.1.0, icss-replace-symbols@^1.0.2, icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -21208,6 +21186,14 @@ lru-cache@^4.1.1, lru-cache@^4.1.2: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^4.1.3, lru-cache@^4.1.5, lru-cache@~4.1.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^5.0.0, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -22466,6 +22452,21 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" +mysql2@2.0.0-alpha1: + version "2.0.0-alpha1" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.0.0-alpha1.tgz#cb76b874c4c24471334cfebb874ebed9a2a6afe5" + integrity sha512-E1S97AZ7uuDycWzjzmSsPlp6Lgxd83XKFGyi8AOurFBmjC70XmqvSNigJfnioOjkh4ghsEe1m8nobx/PANi/FQ== + dependencies: + cardinal "^2.1.1" + denque "^1.4.1" + generate-function "^2.3.1" + iconv-lite "^0.5.0" + long "^4.0.0" + lru-cache "^5.1.1" + named-placeholders "^1.1.2" + seq-queue "^0.0.5" + sqlstring "^2.3.1" + mz@^2.6.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" @@ -22475,6 +22476,13 @@ mz@^2.6.0: object-assign "^4.0.1" thenify-all "^1.0.0" +named-placeholders@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.2.tgz#ceb1fbff50b6b33492b5cf214ccf5e39cef3d0e8" + integrity sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA== + dependencies: + lru-cache "^4.1.3" + nan@2.13.2: version "2.13.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" @@ -28672,6 +28680,11 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" +seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4= + sequelize-cli@5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-5.5.0.tgz#b0570352f70eaa489a25dccf55cf316675d6ff06" @@ -29775,6 +29788,11 @@ sqlite3@4.0.9: node-pre-gyp "^0.11.0" request "^2.87.0" +sqlstring@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" + integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A= + sshpk@^1.7.0: version "1.14.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" @@ -33921,6 +33939,7 @@ websocket@^1.0.28: dependencies: debug "^2.2.0" es5-ext "^0.10.50" + gulp "^4.0.2" nan "^2.14.0" typedarray-to-buffer "^3.1.5" yaeti "^0.0.6"