This repository has been archived by the owner on Dec 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 974
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4656 from willy-b/add-payment-history-simulator-4…
…199-rebase Add tools for generating a simulated Payment History for testing (replaces #4655)
- Loading branch information
Showing
7 changed files
with
292 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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']) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |