Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Merge pull request #4656 from willy-b/add-payment-history-simulator-4…
Browse files Browse the repository at this point in the history
…199-rebase

Add tools for generating a simulated Payment History for testing (replaces #4655)
  • Loading branch information
diracdeltas authored Oct 10, 2016
2 parents f4418d6 + d771fdf commit 7959e5c
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 14 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"port": "8080"
},
"scripts": {
"add-simulated-payment-history": "node ./tools/addSimulatedPaymentHistory.js",
"build-installer": "node ./tools/buildInstaller.js",
"build-package": "node ./tools/buildPackage.js",
"check-security": "nsp check",
Expand Down
10 changes: 10 additions & 0 deletions tools/addSimulatedPaymentHistory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let runUtilApp = require('./utilAppRunner')

let cmd = 'addSimulatedLedgerTransactions'

// if user has specified number of simulated transactions to add
if (process.argv[2]) {
cmd += ' ' + process.argv[2]
}

runUtilApp(cmd, undefined, ['inherit', 'inherit', 'inherit'])
16 changes: 2 additions & 14 deletions tools/clean.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
const path = require('path')
const proc = require('child_process')
const rimraf = require('./lib/rimraf')

const rootDir = path.join(__dirname, '..')

function runUtilApp (cmd, file) {
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const utilAppDir = path.join(__dirname, 'lib', 'utilApp')
const options = {
env: process.env,
cwd: utilAppDir
}
cmd = cmd.split(' ')
const utilApp = proc.spawnSync('electron', [utilAppDir].concat(cmd), options)
if (utilApp.error) {
console.log('Could not run utilApp - run `npm install electron-prebuilt` and try again', utilApp.error)
}
}
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const runUtilApp = require('./utilAppRunner')

module.exports.nodeModules = () => {
console.warn('removing node_modules...')
Expand Down
30 changes: 30 additions & 0 deletions tools/lib/simulateLedgerTransactions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const TxHelpers = require('./transactionHelpers')

let currentTimestamp = (new Date()).getTime()

const getNthContributionPeriodBack = function (n) {
return currentTimestamp - (1000 * 3600 * 24 * 30) * n
}

function simulateLedgerTransactions (numTx) {
let numTransactions = numTx || 10

let transactions = (new Array(numTransactions))
.fill(null)
.map(function (nothing, idx) {
let tx = TxHelpers.generateTransaction()
tx.submissionStamp = getNthContributionPeriodBack(idx)
tx.submissionDate = new Date(tx.submissionStamp)

let validatorOutput = TxHelpers.validateTransaction(tx)
if (validatorOutput.error) {
console.error(validatorOutput.error)
}

return tx
})

return transactions
}

module.exports = simulateLedgerTransactions
199 changes: 199 additions & 0 deletions tools/lib/transactionHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
let Joi = require('joi')

const SATOSHIS_PER_BTC = Math.pow(10, 8)

const VALID_CURRENCY_CODE_REGEX = /^[A-Z]+$/
const VALID_HOSTNAME_REGEX = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/

const transactionSchema = Joi.object().keys({
viewingId: Joi.string().guid().required().description('a unique id for the transaction'),
surveyorId: Joi.string().length(32, 'base64').required().description('a unique id for the surveyor. 32 bytes, base64 encoded'),
contribution: Joi.object().keys({
fiat: Joi.object().keys({
amount: Joi.number().min(0).precision(2).required(),
currency: Joi.string().required()
}).required(),
rates: Joi.object().pattern(VALID_CURRENCY_CODE_REGEX, Joi.number().min(0).precision(2)),
satoshis: Joi.number().integer().min(0).required().description('contribution amount in satoshis (10E-8 BTC)'),
fee: Joi.number().integer().min(0).required().description('transaction fee in satoshis for the contribution (10E-8 BTC)')
}).required().description('object describing contribution in fiat and BTC, with exchange rate at time of contribution and network transaction fee'),
submissionStamp: Joi.date().timestamp().required(),
/** submissionId is 32 bytes, 64 chars hex-encoded **/
submissionId: Joi.string().hex().length(32, 'hex').required(),
submissionDate: Joi.date(),
count: Joi.number().integer().min(0),

/** credential:
* complicated JSON BLOB from anonize2, come back to this later
**/
credential: Joi.string(),

/** surveyorIds:
* an array of random 32-byte ids (base64-encoded).
* length should equal sibling value `count`
**/
surveyorIds: Joi.array().items(Joi.string().length(32, 'base64')), // ideally something like .length(Joi.ref('count'))

satoshis: Joi.ref('contribution.satoshis'),
votes: Joi.ref('count'),
ballots: Joi.object().pattern(VALID_HOSTNAME_REGEX, Joi.number().integer().min(0))
})

const validateTransaction = function (tx) {
return Joi.validate(tx, transactionSchema)
}

const generateTransaction = function () {
const count = Math.round(Math.random() * 100)

const viewingId = generateViewingId()
const surveyorId = generateSurveyorId()
const contribution = generateContribution()
const submissionStamp = generateSubmissionStamp()
const submissionDate = new Date(submissionStamp)
const submissionId = generateSubmissionId()
const credential = generateCredential() // what args needed?
const surveyorIds = generateSurveyorIds(count)
const satoshis = contribution.satoshis
const votes = count
const ballots = generateBallots(votes)

return {
viewingId,
surveyorId,
contribution,
submissionStamp,
submissionDate,
submissionId,
credential,
surveyorIds,
count,
satoshis,
votes,
ballots
}
}

/** code for generating transaction object components **/
const uuid = require('node-uuid')
const crypto = require('crypto')
const randomBytes = crypto.randomBytes

const generateViewingId = function () {
return uuid.v4().toLowerCase()
}

const generateSurveyorId = function () {
/**
Random 32 bytes, generated in node-anonize2-relic/anonize2/anon.cpp:createSurvey as a random Big:
---
Big vid;
rand_int(vid);
---
Expressed in base64 everywhere in JS-land
**/
return randomBytes(32).toString('base64')
}

const generateSurveyorIds = function (count) {
if (!count) {
return []
}

return (new Array(count)).fill(null).map(generateSurveyorId)
}

const generateContribution = function (satoshis, currency, rate, fee) {
let plusOrMinusTenPercentFactor = 1 + (2 * (Math.random() - 0.5) * 0.1)
let randomExchangeRateUSDPerBTC = 620 * plusOrMinusTenPercentFactor
let randomExchangeRateSatoshisPerUSD = (1 / randomExchangeRateUSDPerBTC) * SATOSHIS_PER_BTC
let randomContributionAmountUSD = [5, 10, 15][ Math.round(Math.random() * 2) ]

currency = currency || 'USD'
currency = currency.toUpperCase()
rate = rate || randomExchangeRateUSDPerBTC
satoshis = satoshis || Math.round(randomContributionAmountUSD * randomExchangeRateSatoshisPerUSD)
fee = fee || (0.0001 * SATOSHIS_PER_BTC)

let rates = {}
rates[currency] = parseFloat(rate.toFixed(2))

if (currency.toUpperCase() !== 'USD') {
rates.USD = randomExchangeRateUSDPerBTC
}

let contribution = {
fiat: {
amount: parseFloat((satoshis / SATOSHIS_PER_BTC * rate).toFixed(2)),
currency: currency
},
rates: rates,
satoshis: satoshis,
fee: fee
}

return contribution
}

const generateSubmissionStamp = function () {
return (new Date()).getTime()
}

const generateSubmissionId = function () {
return randomBytes(32).toString('hex')
}

// this one is a TODO, as it is a complicated JSON blob from anonize
const generateCredential = function () {
return 'PLACEHOLDER_CREDENTIAL_STRING'
}

const generateBallots = function (votes) {
let ballots = {}

let votesRemaining = votes

while (votesRemaining) {
let votesToCast = Math.min(Math.round(Math.random() * votesRemaining) + 1, votesRemaining)
let host = _generateRandomHost()
ballots[host] = votesToCast
votesRemaining -= votesToCast
}

return ballots
}

const ALPHABET = 'abcdefghijklmnopqrstuvwxyz'
const _chooseRandomLetter = function () {
return ALPHABET[Math.round(Math.random() * (ALPHABET.length - 1))]
}

const _generateRandomString = function (len) {
return (new Array(len)).fill(null).map(_chooseRandomLetter).join('')
}

const TLDS = ['com', 'net', 'org', 'io', 'info']

const _generateRandomHost = function (maxLength, minLength) {
maxLength = maxLength || 10
minLength = minLength || 4

let len = Math.max(Math.round(Math.random() * maxLength), minLength)

let tld = TLDS[Math.round(Math.random() * (TLDS.length - 1))]

let numParts = Math.round(Math.random()) + 1

let host = (new Array(numParts)).fill(null).map(function () {
let partLen = Math.max(Math.round(Math.random() * len), minLength)
return _generateRandomString(partLen)
}).join('.') + '.' + tld

return host
}

module.exports = {
transactionSchema,
validateTransaction,
generateTransaction
}
28 changes: 28 additions & 0 deletions tools/lib/utilApp/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,40 @@ const cleanUserData = (location) => {
}
}

const simulateLedgerTransactions = require('../simulateLedgerTransactions')
const fs = require('fs')

function addSimulatedLedgerTransactions (numTx) {
let userDataPath = app.getPath('userData')
let ledgerStatePath = path.join(userDataPath, 'ledger-state.json')

try {
let ledgerState = JSON.parse(fs.readFileSync(ledgerStatePath).toString())

let simulatedTransactions = simulateLedgerTransactions(numTx)

ledgerState.transactions = (ledgerState.transactions || []).concat(simulatedTransactions)

fs.writeFileSync(ledgerStatePath, JSON.stringify(ledgerState, null, 2))

console.log(`Updated Ledger data file at ${ledgerStatePath}`)
} catch (exc) {
console.error('ERROR in addSimulatedLedgerTransactions: could not find/open/parse Ledger data file.')
console.error(`Expected path to Ledger data file: ${ledgerStatePath}`)
console.error('Probable solution: Run Brave and enable Payments, then execute this script. Enabling/disabling Payments (or restarting Brave) should then show the generated transactions in Brave.')
}
}

app.on('ready', () => {
const cmd = process.argv[2]
switch (cmd) {
case 'cleanUserData':
cleanUserData(process.argv[3])
break
case 'addSimulatedLedgerTransactions':
addSimulatedLedgerTransactions(process.argv[3])
break
}

process.exit(0)
})
22 changes: 22 additions & 0 deletions tools/utilAppRunner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const path = require('path')
const proc = require('child_process')

function runUtilApp (cmd, file, stdioOptions) {
console.log('runUtilApp: ')

process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const utilAppDir = path.join(__dirname, 'lib', 'utilApp')
const options = {
env: process.env,
cwd: utilAppDir,
stdio: stdioOptions
}
cmd = cmd.split(' ')
const utilApp = proc.spawnSync('electron', [utilAppDir].concat(cmd), options)
if (utilApp.error) {
console.log('Could not run utilApp - run `npm install electron-prebuilt` and try again', utilApp.error)
}
return utilApp
}

module.exports = runUtilApp

0 comments on commit 7959e5c

Please sign in to comment.