Skip to content

Commit

Permalink
added wallet cooldown code
Browse files Browse the repository at this point in the history
  • Loading branch information
maikelmclauflin committed Apr 4, 2019
1 parent 44644c7 commit 2ba2651
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ env:
- "TESTING_COHORTS=test"
- "TOKEN_LIST=foobarfoobar"
- "UPHOLD_DONOR_CARD_ID=6654ecb0-6079-4f6c-ba58-791cc890a561"
- "WALLET_COOLDOWN_BYPASS_TOKEN=fb9dcd45-36f5-4c2d-b511-90fa1f0cadae"
- "WALLET_COOLDOWN_HRS=24"
- "VOTING_COHORTS=control,grant,test,ads,safetynet"
- secure: "JQ9WipBrcCPtXFwv/fmzhv8LPz/tx/jCPpeTK0KmSZr4Yqs5kMG0YGH3QeT1YDpiRakW9X5us9tsnsfHuslvZ8Z565lc0wIK7b7CUcEzVuBts6cfVQtOpqKKYjw6jyq5TraTYawYGUfeaCkP+35oM/1fs4vPy6MbH5Plzg6f9iMfB+BFysRT20JYKXcW0LfihHRKStaAJYKDD2mpdY4kStu7vYB66LgR6Solw1IUwgJzfKst/dM6PJspfmNkFaHCoBNMEYcUJmeJPqmLCnfIA+eRCrSMfHOChjzCV6JbmcD11FOydISEhdt67X+5TDArePYVssrj7NygVlrn8TOvk6InXuul6n+oIpS0vz5e5QdxrReLLwb+WJPK7xKWD56Ko2gfTwtEUdevLzQhKfPMs5wW5ujjlcjAqi5PmC87Q+bxCjet19EdJ4j09y2NSKRo1LMzu1WIM6o1mIatha0vJEHa05XAUsuD9lCGrt/aG4DLYh5Q5gJP/SYDoMl+wJku254KJN+WesxaSREhgdqtV13GFnMofI9r/vq4wKrlcbsMh3UD337NZlNVZzONINeJ4afN3TRK+LS8bHGsss/ZpLcnfT0uYMXzneQQhkKmKWkss8VOLEZ2ccLRG0LNWsOJXznBE/SqXaxSK4NkgXQDyfVUuAj7GPyIDX6hygsguyA="
- secure: "be8ASUNkGT1MpO/wfwVQKuo0Absf5xQAnjXRmsrM105n+vGXdq1c/fl13wow7d/PQtR27tNlJ5UmoeROUCBLlOKm+bWWZ7wFThsxOQtHk3yJoaVtIApo5cwEQcMi/92zWQKDwO7ZuK+xY9oLiYng2yayfuxBUEzWlNxS0cMsmq6Xnf1qG8USVCqvo8gQp/X8hliscFWRbCJ+lzggmlIsoTe6RElF9N3yXngvWRJY/GDjOe3FD2wJNDoE8wYwHcxKvfcdL0c8dWhHl5KC366ZSiBMJHrwv2aVjUgwQ2Azsdehxy1bsqyiH2bS/ifH8FUosjMA/g8qRB89EgaLCS5NndIVl1q0miuRv20kMaF6syTOH3HyDi/o2L9tg1Z/XSuNSVI2o+A8yJfKiY1AwjCR8aQ+uyKj9kOg7nTlBElHrOcAOaiC0Rg13TZ2230IbFanQFIqDL0B8xQx1EAxsArgWIrebqSd1QbVYKEHE5+2dWXExPX+7e217rFvEz5Ltxq1U9IdHHnmcTxUrylr800ub2PtOyZvRNbBFXq5tDpENLl/lV8yf0Qm2q+8jUvqXZIG0akIqC5b0cpHEtxJcAGvfqFx8vZUV1WW4TVbxApmndSGFw2bF6psx6SJUDrdFfl87u1Rb7wWtesDXAMrfIphlr0AcH2P7XDqxAjifuQgtdk="
Expand Down
2 changes: 2 additions & 0 deletions base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@ services:
- UPHOLD_DONOR_CARD_ID
- UPHOLD_ENVIRONMENT
- VOTING_COHORTS
- WALLET_COOLDOWN_BYPASS_TOKEN
- WALLET_COOLDOWN_HRS
- YOUTUBE_API_KEY
35 changes: 29 additions & 6 deletions ledger/controllers/grants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ const bson = require('bson')
const underscore = require('underscore')
const uuidV4 = require('uuid/v4')
const wreck = require('wreck')

const {
cooldownOffset
} = require('../lib/grants')
const utils = require('bat-utils')
const braveJoi = utils.extras.joi
const braveHapi = utils.extras.hapi
const braveUtils = utils.extras.utils
const whitelist = utils.hapi.auth.whitelist

const rateLimitEnabled = process.env.NODE_ENV === 'production'
const { NODE_ENV } = process.env
const isProduction = NODE_ENV === 'production'
const rateLimitEnabled = isProduction

const qalist = { addresses: process.env.IP_QA_WHITELIST && process.env.IP_QA_WHITELIST.split(',') }

Expand Down Expand Up @@ -251,9 +255,11 @@ const getGrant = (protocolVersion) => (runtime) => {
}
}
}

const lang = request.query.lang
const paymentId = request.query.paymentId
const {
lang,
paymentId,
bypassCooldown
} = request.query
const languages = l10nparser.parse(lang)
const query = {
active: true,
Expand All @@ -265,6 +271,7 @@ const getGrant = (protocolVersion) => (runtime) => {
const promotions = runtime.database.get('promotions', debug)
const wallets = runtime.database.get('wallets', debug)
let entries, promotionIds, wallet
let walletTooYoung = false

if (qaOnlyP(request)) return reply(boom.notFound())

Expand All @@ -276,6 +283,7 @@ const getGrant = (protocolVersion) => (runtime) => {
wallet.grants.forEach((grant) => { promotionIds.push(grant.promotionId) })
}
underscore.extend(query, { promotionId: { $nin: promotionIds } })
walletTooYoung = walletCooldown(runtime, wallet, bypassCooldown)
}

if (protocolVersion === 4 && !paymentId) {
Expand All @@ -299,7 +307,7 @@ const getGrant = (protocolVersion) => (runtime) => {
underscore.extend(query, { providerId: wallet.addresses.CARD_ID })
}
const counted = await grants.count(query)
if (counted !== 0) {
if (counted !== 0 && !walletTooYoung) {
filteredPromotions.push({ promotionId, type })
}
}
Expand Down Expand Up @@ -372,6 +380,7 @@ v4.read = {

validate: {
query: {
bypassCooldown: Joi.string().guid().optional().description('a token to bypass the wallet cooldown time'),
lang: Joi.string().regex(localeRegExp).optional().default('en').description('the l10n language'),
paymentId: Joi.string().guid().optional().description('identity of the wallet')
}
Expand Down Expand Up @@ -1163,3 +1172,17 @@ function uploadTypedGrants (protocolVersion, uploadSchema, contentSchema) {
reply({})
}
}

function walletCooldown (runtime, wallet, bypassCooldown) {
const { paymentId, _id } = wallet
const { WALLET_COOLDOWN_BYPASS_TOKEN } = process.env
if (bypassCooldown !== WALLET_COOLDOWN_BYPASS_TOKEN) {
const offset = cooldownOffset()
const createdTime = braveUtils.createdTimestamp(_id)
return createdTime > (new Date() - offset)
} else if (WALLET_COOLDOWN_BYPASS_TOKEN && isProduction) {
// report to sentry that a bypass is being used
runtime.captureException(new Error('wallet bypass used'), { paymentId })
}
return false
}
15 changes: 15 additions & 0 deletions ledger/lib/grants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const _ = require('underscore')
const { WALLET_COOLDOWN_HRS } = process.env
module.exports = {
defaultCooldownHrs,
cooldownOffset
}

function defaultCooldownHrs (hours) {
const hrs = _.isUndefined(hours) ? WALLET_COOLDOWN_HRS : hours
return hrs ? (+hrs || 0) : 24
}

function cooldownOffset (hours = defaultCooldownHrs()) {
return hours * 60 * 60 * 1000
}
31 changes: 29 additions & 2 deletions test/ledger/grants.integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ import {
timeout,
uint8tohex
} from 'bat-utils/lib/extras-utils'
import {
defaultCooldownHrs,
cooldownOffset
} from '../../ledger/lib/grants'

test.before(cleanDbs)
test.after(cleanDbs)
test.afterEach.always(cleanDbs)

const bypassCooldown = process.env.WALLET_COOLDOWN_BYPASS_TOKEN
const promotionId = 'c96c39c8-77dd-4b2d-a8df-2ecf824bc9e9'
// expired grant
const expired = {'grants': ['eyJhbGciOiJFZERTQSIsImtpZCI6IiJ9.eyJhbHRjdXJyZW5jeSI6IkJBVCIsImdyYW50SWQiOiI0Y2ZjMzFmYy1mYjE1LTRmMTUtOTc0Zi0zNzJiMmI0YzBkYjYiLCJwcm9iaSI6IjMwMDAwMDAwMDAwMDAwMDAwMDAwIiwicHJvbW90aW9uSWQiOiJjOTZjMzljOC03N2RkLTRiMmQtYThkZi0yZWNmODI0YmM5ZTkiLCJtYXR1cml0eVRpbWUiOjE1MjY5NDE0MDAsImV4cGlyeVRpbWUiOjE1MjUxNzYwMDB9.iZBTNb9zilKubYYwYuc9MIUHZq0iv-7DsmnNu0GakeiEjcNqgbgbg-Wc2dowlMmMyjRbXjDUIC8rK4FiIqH8CQ'], 'promotions': [{promotionId, 'priority': 0, 'active': true, 'minimumReconcileTimestamp': 1526941400000}]}
Expand Down Expand Up @@ -366,13 +371,17 @@ test('protocolVersion 4 does not send back ads when none are available', async (
await ledgerAgent.post(url).send(adGrants).expect(ok)

// get available grant
await ledgerAgent
.get('/v4/grants')
.query({ paymentId })
.expect(404)
const {
body: {
grants: promotions
}
} = await ledgerAgent
.get(`/v4/grants`)
.query({ paymentId })
.query({ paymentId, bypassCooldown })
.expect(ok)
t.is(promotions.length, 1, 'only one promotion should be sent back')

Expand Down Expand Up @@ -470,13 +479,17 @@ test('protocolVersion 4 can claim both ads and ugp grants', async (t) => {
await ledgerAgent.post(url).send(adGrants).expect(ok)

// get available grant
await ledgerAgent
.get('/v4/grants')
.query({ paymentId })
.expect(404)
const {
body: {
grants: promotions
}
} = await ledgerAgent
.get(`/v4/grants`)
.query({ paymentId })
.query({ paymentId, bypassCooldown })
.expect(ok)
t.is(promotions.length, 2, '2 promotions should be sent back')

Expand Down Expand Up @@ -600,6 +613,20 @@ test('protocolVersion 4 can claim both ads and ugp grants even on claim v2', asy
}
})

test('default cooldown hrs', async (t) => {
t.is(defaultCooldownHrs(), defaultCooldownHrs(process.env.WALLET_COOLDOWN_HRS), 'uses env var for default')
t.is(defaultCooldownHrs(12), 12, 'can be passed number')
t.is(defaultCooldownHrs('12'), 12, 'can be passed numeric string')
t.is(defaultCooldownHrs('0'), 0, 'can be passed falsey numeric string')
t.is(defaultCooldownHrs('a'), 0, 'defaults to 0 if passed non numeric values')
})

test('cooldown offset', async (t) => {
t.is(cooldownOffset(), cooldownOffset(defaultCooldownHrs()), 'calculates hours to offset in terms of milliseconds')
t.is(cooldownOffset(12), 12 * 60 * 60 * 1000, 'gives back in ms')
t.is(cooldownOffset({}), NaN, 'only takes numeric values')
})

async function resolveCaptcha (wallets, {
version = 4,
paymentId,
Expand Down

0 comments on commit 2ba2651

Please sign in to comment.