diff --git a/.travis.yml b/.travis.yml index 2eaf649..739ffef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ node_js: node install: npm install script: - - npm run clean + - npm test - npm run build cache: diff --git a/package.json b/package.json index ea9cba7..83a2857 100644 --- a/package.json +++ b/package.json @@ -18,11 +18,12 @@ "build:cjs": "cross-env BABEL_ENV=cjs babel ./src -d dist/node", "build:dist": "cross-env NODE_ENV=production webpack -p", "clean": "rimraf dist/bundle dist/node", - "test": "echo \"Error: no test specified AWWWW YEAHHH\" && exit 1", + "test": "npm run lint", "release": "./node_modules/release-it/bin/release.js --src.tagName='v%s' --github.release --npm.publish --non-interactive", "release-minor": "./node_modules/release-it/bin/release.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive", "release-major": "./node_modules/release-it/bin/release.js major --src.tagName='v%s' --github.release --npm.publish --non-interactive", - "postinstall": "npm run build" + "postinstall": "npm run build", + "precommit": "npm run lint" }, "devDependencies": { "babel-cli": "^6.22.2", @@ -41,6 +42,7 @@ "eslint": "^3.14.1", "eslint-config-ascribe": "^3.0.1", "eslint-plugin-import": "^2.2.0", + "husky": "^0.13.4", "release-it": "^2.7.3", "rimraf": "^2.5.4", "webpack": "^2.2.1" @@ -58,6 +60,7 @@ "js-sha3": "^0.5.7", "js-utility-belt": "^1.5.0", "json-stable-stringify": "^1.0.1", + "query-string": "^4.3.4", "sprintf-js": "^1.0.3", "tweetnacl": "^1.0.0" }, diff --git a/src/Ed25519Keypair.js b/src/Ed25519Keypair.js index eadfd80..f6b07d9 100644 --- a/src/Ed25519Keypair.js +++ b/src/Ed25519Keypair.js @@ -1,6 +1,5 @@ -import base58 from 'bs58'; -import nacl from 'tweetnacl'; -import sha3 from 'js-sha3'; +import base58 from 'bs58' +import nacl from 'tweetnacl' /** * @public @@ -11,8 +10,8 @@ import sha3 from 'js-sha3'; * @property {string} privateKey */ export default function Ed25519Keypair(seed) { - const keyPair = seed ? nacl.sign.keyPair.fromSeed(seed) : nacl.sign.keyPair(); - this.publicKey = base58.encode(keyPair.publicKey); + const keyPair = seed ? nacl.sign.keyPair.fromSeed(seed) : nacl.sign.keyPair() + this.publicKey = base58.encode(keyPair.publicKey) // tweetnacl's generated secret key is the secret key + public key (resulting in a 64-byte buffer) - this.privateKey = base58.encode(keyPair.secretKey.slice(0, 32)); + this.privateKey = base58.encode(keyPair.secretKey.slice(0, 32)) } diff --git a/src/baseRequest.js b/src/baseRequest.js index 117b6a1..e75e92a 100644 --- a/src/baseRequest.js +++ b/src/baseRequest.js @@ -1,13 +1,13 @@ -import { Promise } from 'es6-promise'; -import fetchPonyfill from 'fetch-ponyfill'; -import { vsprintf } from 'sprintf-js'; +import { Promise } from 'es6-promise' +import fetchPonyfill from 'fetch-ponyfill' +import { vsprintf } from 'sprintf-js' -import formatText from './format_text'; +import formatText from './format_text' -import stringifyAsQueryParam from './stringify_as_query_param'; +import stringifyAsQueryParam from './stringify_as_query_param' -const fetch = fetchPonyfill(Promise); +const fetch = fetchPonyfill(Promise) /** @@ -38,35 +38,35 @@ const fetch = fetchPonyfill(Promise); * otherwise rejects with the response */ export default function baseRequest(url, { jsonBody, query, urlTemplateSpec, ...fetchConfig } = {}) { - let expandedUrl = url; + let expandedUrl = url if (urlTemplateSpec != null) { if (Array.isArray(urlTemplateSpec) && urlTemplateSpec.length) { // Use vsprintf for the array call signature - expandedUrl = vsprintf(url, urlTemplateSpec); + expandedUrl = vsprintf(url, urlTemplateSpec) } else if (urlTemplateSpec && typeof urlTemplateSpec === 'object' && Object.keys(urlTemplateSpec).length) { - expandedUrl = formatText(url, urlTemplateSpec); + expandedUrl = formatText(url, urlTemplateSpec) } else if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console - console.warn('Supplied urlTemplateSpec was not an array or object. Ignoring...'); + console.warn('Supplied urlTemplateSpec was not an array or object. Ignoring...') } } if (query != null) { if (typeof query === 'string') { - expandedUrl += query; + expandedUrl += query } else if (query && typeof query === 'object') { - expandedUrl += stringifyAsQueryParam(query); + expandedUrl += stringifyAsQueryParam(query) } else if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console - console.warn('Supplied query was not a string or object. Ignoring...'); + console.warn('Supplied query was not a string or object. Ignoring...') } } if (jsonBody != null) { - fetchConfig.body = JSON.stringify(jsonBody); + fetchConfig.body = JSON.stringify(jsonBody) } return fetch.fetch(expandedUrl, fetchConfig) @@ -74,8 +74,8 @@ export default function baseRequest(url, { jsonBody, query, urlTemplateSpec, ... // If status is not a 2xx (based on Response.ok), assume it's an error // See https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch if (!(res && res.ok)) { - throw res; + throw res } - return res; - }); -} \ No newline at end of file + return res + }) +} diff --git a/src/connection/index.js b/src/connection/index.js index 553bf74..26a241d 100644 --- a/src/connection/index.js +++ b/src/connection/index.js @@ -1,27 +1,27 @@ -import request from '../request'; +import request from '../request' export default class Connection { constructor(path, headers) { - this.path = path; - this.headers = headers; + this.path = path + this.headers = headers } getApiUrls(endpoints) { // TODO: Use camel case return { - 'blocks': this.path + 'blocks', - 'blocks_detail': this.path + 'blocks/%(blockId)s', - 'outputs': this.path + 'outputs', - 'statuses': this.path + 'statuses', - 'transactions': this.path + 'transactions', - 'transactions_detail': this.path + 'transactions/%(txId)s', - 'search_assets': this.path + 'assets', - 'votes': this.path + 'votes' - }[endpoints]; + 'blocks': `${this.path}blocks`, + 'blocks_detail': `${this.path}blocks/%(blockId)s`, + 'outputs': `${this.path}outputs`, + 'statuses': `${this.path}statuses`, + 'transactions': `${this.path}transactions`, + 'transactions_detail': `${this.path}transactions/%(txId)s`, + 'search_assets': `${this.path}assets`, + 'votes': `${this.path}votes` + }[endpoints] } - _req(path, options={}) { + _req(path, options = {}) { // NOTE: `options.headers` could be undefined, but that's OK. options.headers = Object.assign({}, options.headers, this.headers) return request(path, options) @@ -33,22 +33,22 @@ export default class Connection { */ getBlock(blockId) { return this._req(this.getApiUrls('blocks_detail'), { - urlTemplateSpec: { - blockId - } - }); + urlTemplateSpec: { + blockId + } + }) } /** * @public * @param tx_id */ - getStatus(tx_id) { + getStatus(tx_id) { // eslint-disable-line camelcase return this._req(this.getApiUrls('statuses'), { - query: { - tx_id - } - }); + query: { + tx_id + } + }) } /** @@ -57,10 +57,10 @@ export default class Connection { */ getTransaction(txId) { return this._req(this.getApiUrls('transactions_detail'), { - urlTemplateSpec: { - txId - } - }); + urlTemplateSpec: { + txId + } + }) } /** @@ -70,11 +70,11 @@ export default class Connection { */ listBlocks({ tx_id, status }) { return this._req(this.getApiUrls('blocks'), { - query: { - tx_id, - status - } - }); + query: { + tx_id, + status + } + }) } /** @@ -83,7 +83,7 @@ export default class Connection { * @param unspent * @param onlyJsonResponse */ - listOutputs({ public_key, unspent }, onlyJsonResponse=true) { + listOutputs({ public_key, unspent }, onlyJsonResponse = true) { return this._req(this.getApiUrls('outputs'), { query: { public_key, @@ -110,38 +110,38 @@ export default class Connection { * @public * @param block_id */ - listVotes(block_id) { + listVotes(block_id) { // eslint-disable-line camelcase return this._req(this.getApiUrls('votes'), { - query: { - block_id - } - }); + query: { + block_id + } + }) } /** * @public - * @param tx_id + * @param txId * @return {Promise} */ - pollStatusAndFetchTransaction(tx_id) { + pollStatusAndFetchTransaction(txId) { return new Promise((resolve, reject) => { const timer = setInterval(() => { - this.getStatus(tx_id) + this.getStatus(txId) .then((res) => { - console.log('Fetched transaction status:', res); + console.log('Fetched transaction status:', res) // eslint-disable-line no-console if (res.status === 'valid') { - clearInterval(timer); - this.getTransaction(tx_id) - .then((res) => { - console.log('Fetched transaction:', res); - resolve(res); - }); + clearInterval(timer) + this.getTransaction(txId) + .then((res_) => { + console.log('Fetched transaction:', res_) // eslint-disable-line no-console + resolve(res_) + }) } }) .catch((err) => { - clearInterval(timer); - reject(err); - }); + clearInterval(timer) + reject(err) + }) }, 500) }) } diff --git a/src/format_text.js b/src/format_text.js index 5db6988..63bb5ed 100644 --- a/src/format_text.js +++ b/src/format_text.js @@ -1,13 +1,13 @@ -import { sprintf } from 'sprintf-js'; +import { sprintf } from 'sprintf-js' // Regexes taken from or inspired by sprintf-js const Regex = { - TEMPLATE_LITERAL: /\${([^\)]+?)}/g, + TEMPLATE_LITERAL: /\${([^)]+?)}/g, KEY: /^([a-z_][a-z_\d]*)/i, KEY_ACCESS: /^\.([a-z_][a-z_\d]*)/i, INDEX_ACCESS: /^\[(\d+)\]/ -}; +} /** * imported from https://github.com/bigchaindb/js-utility-belt/ @@ -37,14 +37,14 @@ const Regex = { * => 'Berlin is best known for its Currywurst' */ export default function formatText(s, ...argv) { - let expandedFormatStr = s; + let expandedFormatStr = s // Try to replace formats of the form '${...}' if named replacement fields are used if (s && argv.length === 1 && typeof argv[0] === 'object') { - const templateSpecObj = argv[0]; + const templateSpecObj = argv[0] expandedFormatStr = s.replace(Regex.TEMPLATE_LITERAL, (match, replacement) => { - let interpolationLeft = replacement; + let interpolationLeft = replacement /** * Interpolation algorithm inspired by sprintf-js. @@ -61,21 +61,21 @@ export default function formatText(s, ...argv) { * And that in the regexes defined, the first matching group always corresponds to the * property matched. */ - let value; - let curMatch = Regex.KEY.exec(interpolationLeft); + let value + let curMatch = Regex.KEY.exec(interpolationLeft) if (curMatch !== null) { - value = templateSpecObj[curMatch[1]]; + value = templateSpecObj[curMatch[1]] // Assigning in the conditionals here makes the code less bloated /* eslint-disable no-cond-assign */ while ((interpolationLeft = interpolationLeft.substring(curMatch[0].length)) && value != null) { if ((curMatch = Regex.KEY_ACCESS.exec(interpolationLeft))) { - value = value[curMatch[1]]; + value = value[curMatch[1]] } else if ((curMatch = Regex.INDEX_ACCESS.exec(interpolationLeft))) { - value = value[curMatch[1]]; + value = value[curMatch[1]] } else { - break; + break } } /* eslint-enable no-cond-assign */ @@ -86,12 +86,12 @@ export default function formatText(s, ...argv) { if (interpolationLeft.length) { throw new SyntaxError( `[formatText] failed to parse named argument key: ${replacement}` - ); + ) } - return value; - }); + return value + }) } - return sprintf(expandedFormatStr, ...argv); -} \ No newline at end of file + return sprintf(expandedFormatStr, ...argv) +} diff --git a/src/index.js b/src/index.js index a2326ec..96b539c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ -export Ed25519Keypair from './Ed25519Keypair'; +export Ed25519Keypair from './Ed25519Keypair' -export * as Transaction from './transaction'; -export Connection from './connection'; +export * as Transaction from './transaction' +export Connection from './connection' diff --git a/src/request.js b/src/request.js index 8473600..8d23176 100644 --- a/src/request.js +++ b/src/request.js @@ -1,43 +1,41 @@ -import baseRequest from './baseRequest'; -import sanitize from './sanitize'; +import baseRequest from './baseRequest' +import sanitize from './sanitize' const DEFAULT_REQUEST_CONFIG = { headers: { 'Accept': 'application/json' } -}; +} /** * Small wrapper around js-utility-belt's request that provides url resolving, * default settings, and response handling. */ -export default function request(url, config = {}, onlyJsonResponse=true) { +export default function request(url, config = {}, onlyJsonResponse = true) { // Load default fetch configuration and remove any falsy query parameters const requestConfig = Object.assign({}, DEFAULT_REQUEST_CONFIG, config, { query: config.query && sanitize(config.query) - }); - let apiUrl = url; + }) + const apiUrl = url if (requestConfig.jsonBody) { requestConfig.headers = Object.assign({}, requestConfig.headers, { 'Content-Type': 'application/json' - }); + }) } if (!url) { - return Promise.reject(new Error('Request was not given a url.')); + return Promise.reject(new Error('Request was not given a url.')) } return baseRequest(apiUrl, requestConfig) - .then((res) => { - return onlyJsonResponse ? res.json() : - { - json: res.json(), - url: res.url - }; + .then((res) => onlyJsonResponse ? res.json() : // eslint-disable-line no-confusing-arrow + { + json: res.json(), + url: res.url }) .catch((err) => { - console.error(err); - throw err; - }); + console.error(err) + throw err + }) } diff --git a/src/sanitize.js b/src/sanitize.js index 96ef64f..dd3dc40 100644 --- a/src/sanitize.js +++ b/src/sanitize.js @@ -1,5 +1,5 @@ -import coreIncludes from 'core-js/library/fn/array/includes'; -import coreObjectEntries from 'core-js/library/fn/object/entries'; +import coreIncludes from 'core-js/library/fn/array/includes' +import coreObjectEntries from 'core-js/library/fn/object/entries' /** @@ -10,13 +10,13 @@ import coreObjectEntries from 'core-js/library/fn/object/entries'; function filterFromObject(obj, filter, { isInclusion = true } = {}) { if (filter && Array.isArray(filter)) { return applyFilterOnObject(obj, isInclusion ? ((_, key) => coreIncludes(filter, key)) - : ((_, key) => !coreIncludes(filter, key))); + : ((_, key) => !coreIncludes(filter, key))) } else if (filter && typeof filter === 'function') { // Flip the filter fn's return if it's for inclusion return applyFilterOnObject(obj, isInclusion ? filter - : (...args) => !filter(...args)); + : (...args) => !filter(...args)) } else { - throw new Error('The given filter is not an array or function. Exclude aborted'); + throw new Error('The given filter is not an array or function. Exclude aborted') } } @@ -26,17 +26,17 @@ function filterFromObject(obj, filter, { isInclusion = true } = {}) { */ function applyFilterOnObject(obj, filterFn) { if (filterFn == null) { - return Object.assign({}, obj); + return Object.assign({}, obj) } - const filteredObj = {}; + const filteredObj = {} coreObjectEntries(obj).forEach(([key, val]) => { if (filterFn(val, key)) { - filteredObj[key] = val; + filteredObj[key] = val } - }); + }) - return filteredObj; + return filteredObj } /** @@ -48,7 +48,7 @@ function applyFilterOnObject(obj, filterFn) { * @return {object} The new object */ function selectFromObject(obj, filter) { - return filterFromObject(obj, filter); + return filterFromObject(obj, filter) } /** @@ -60,5 +60,5 @@ function selectFromObject(obj, filter) { * @return {object} Sanitized Javascript object */ export default function sanitize(obj) { - return selectFromObject(obj, (val) => !!val); + return selectFromObject(obj, (val) => !!val) } diff --git a/src/sha256Hash.js b/src/sha256Hash.js index 605aa00..b8199f1 100644 --- a/src/sha256Hash.js +++ b/src/sha256Hash.js @@ -1,8 +1,8 @@ -import sha3 from 'js-sha3'; +import sha3 from 'js-sha3' export default function sha256Hash(data) { return sha3.sha3_256 .create() .update(data) - .hex(); + .hex() } diff --git a/src/stringify_as_query_param.js b/src/stringify_as_query_param.js index 9d03644..abff250 100644 --- a/src/stringify_as_query_param.js +++ b/src/stringify_as_query_param.js @@ -1,6 +1,6 @@ -import coreObjectEntries from 'core-js/library/fn/object/entries'; -import decamelize from 'decamelize'; -import queryString from 'query-string'; +import coreObjectEntries from 'core-js/library/fn/object/entries' +import decamelize from 'decamelize' +import queryString from 'query-string' /** @@ -30,13 +30,13 @@ import queryString from 'query-string'; */ export default function stringifyAsQueryParam(obj, transform = decamelize) { if (!obj || typeof obj !== 'object' || !Object.keys(obj).length) { - return ''; + return '' } const transformedKeysObj = coreObjectEntries(obj).reduce((paramsObj, [key, value]) => { - paramsObj[transform(key)] = value; - return paramsObj; - }, {}); + paramsObj[transform(key)] = value + return paramsObj + }, {}) - return `?${queryString.stringify(transformedKeysObj)}`; -} \ No newline at end of file + return `?${queryString.stringify(transformedKeysObj)}` +} diff --git a/src/transaction/hashTransaction.js b/src/transaction/hashTransaction.js index 9b31cdf..13d1c51 100644 --- a/src/transaction/hashTransaction.js +++ b/src/transaction/hashTransaction.js @@ -1,10 +1,10 @@ -import serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString'; -import sha256Hash from '../sha256Hash'; +import serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString' +import sha256Hash from '../sha256Hash' export default function hashTransaction(transaction) { // Safely remove any tx id from the given transaction for hashing - const tx = { ...transaction }; - delete tx.id; + const tx = { ...transaction } + delete tx.id - return sha256Hash(serializeTransactionIntoCanonicalString(tx)); -} \ No newline at end of file + return sha256Hash(serializeTransactionIntoCanonicalString(tx)) +} diff --git a/src/transaction/index.js b/src/transaction/index.js index 939ae7e..01d4a1f 100644 --- a/src/transaction/index.js +++ b/src/transaction/index.js @@ -1,11 +1,11 @@ -export makeEd25519Condition from './makeEd25519Condition'; -export makeSha256Condition from './makeSha256Condition'; -export makeThresholdCondition from './makeThresholdCondition'; -export makeCreateTransaction from './makeCreateTransaction'; -export makeOutput from './makeOutput'; -export makeTransaction from './makeTransaction'; -export makeTransferTransaction from './makeTransferTransaction'; -export serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString'; -export signTransaction from './signTransaction'; -export ccJsonLoad from './utils/ccJsonLoad'; -export ccJsonify from './utils/ccJsonify'; +export makeEd25519Condition from './makeEd25519Condition' +export makeSha256Condition from './makeSha256Condition' +export makeThresholdCondition from './makeThresholdCondition' +export makeCreateTransaction from './makeCreateTransaction' +export makeOutput from './makeOutput' +export makeTransaction from './makeTransaction' +export makeTransferTransaction from './makeTransferTransaction' +export serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString' +export signTransaction from './signTransaction' +export ccJsonLoad from './utils/ccJsonLoad' +export ccJsonify from './utils/ccJsonify' diff --git a/src/transaction/makeCreateTransaction.js b/src/transaction/makeCreateTransaction.js index 597dcb9..8b259de 100644 --- a/src/transaction/makeCreateTransaction.js +++ b/src/transaction/makeCreateTransaction.js @@ -1,5 +1,5 @@ -import makeInputTemplate from './makeInputTemplate'; -import makeTransaction from './makeTransaction'; +import makeInputTemplate from './makeInputTemplate' +import makeTransaction from './makeTransaction' /** @@ -24,8 +24,8 @@ import makeTransaction from './makeTransaction'; export default function makeCreateTransaction(asset, metadata, outputs, ...issuers) { const assetDefinition = { 'data': asset || null, - }; - const inputs = issuers.map((issuer) => makeInputTemplate([issuer])); + } + const inputs = issuers.map((issuer) => makeInputTemplate([issuer])) - return makeTransaction('CREATE', assetDefinition, metadata, outputs, inputs); -} \ No newline at end of file + return makeTransaction('CREATE', assetDefinition, metadata, outputs, inputs) +} diff --git a/src/transaction/makeEd25519Condition.js b/src/transaction/makeEd25519Condition.js index 4c6b7bb..1bdd0e3 100644 --- a/src/transaction/makeEd25519Condition.js +++ b/src/transaction/makeEd25519Condition.js @@ -1,9 +1,9 @@ -import { Buffer } from 'buffer'; +import { Buffer } from 'buffer' -import base58 from 'bs58'; -import cc from 'five-bells-condition'; +import base58 from 'bs58' +import cc from 'five-bells-condition' -import ccJsonify from './utils/ccJsonify'; +import ccJsonify from './utils/ccJsonify' /** @@ -13,15 +13,15 @@ import ccJsonify from './utils/ccJsonify'; * @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type * @returns {object} Ed25519 Condition (that will need to wrapped in an Output) */ -export default function makeEd25519Condition(publicKey, json=true) { - const publicKeyBuffer = new Buffer(base58.decode(publicKey)); +export default function makeEd25519Condition(publicKey, json = true) { + const publicKeyBuffer = new Buffer(base58.decode(publicKey)) - const ed25519Fulfillment = new cc.Ed25519(); - ed25519Fulfillment.setPublicKey(publicKeyBuffer); + const ed25519Fulfillment = new cc.Ed25519() + ed25519Fulfillment.setPublicKey(publicKeyBuffer) if (json) { return ccJsonify(ed25519Fulfillment) } - return ed25519Fulfillment; + return ed25519Fulfillment } diff --git a/src/transaction/makeInputTemplate.js b/src/transaction/makeInputTemplate.js index 32079df..f080b06 100644 --- a/src/transaction/makeInputTemplate.js +++ b/src/transaction/makeInputTemplate.js @@ -3,5 +3,5 @@ export default function makeInputTemplate(publicKeys = [], fulfills = null, fulf fulfillment, fulfills, 'owners_before': publicKeys, - }; -} \ No newline at end of file + } +} diff --git a/src/transaction/makeOutput.js b/src/transaction/makeOutput.js index e95ffa2..bf1172d 100644 --- a/src/transaction/makeOutput.js +++ b/src/transaction/makeOutput.js @@ -8,9 +8,9 @@ */ export default function makeOutput(condition, amount = 1) { return { - amount: amount.toString(), + 'amount': amount.toString(), condition, 'public_keys': condition.details.hasOwnProperty('public_key') ? [condition.details.public_key] : [], - }; + } } diff --git a/src/transaction/makeSha256Condition.js b/src/transaction/makeSha256Condition.js index 72ef2ef..1ecded9 100644 --- a/src/transaction/makeSha256Condition.js +++ b/src/transaction/makeSha256Condition.js @@ -1,8 +1,8 @@ -import { Buffer } from 'buffer'; +import { Buffer } from 'buffer' -import cc from 'five-bells-condition'; +import cc from 'five-bells-condition' -import ccJsonify from './utils/ccJsonify'; +import ccJsonify from './utils/ccJsonify' /** @@ -12,12 +12,12 @@ import ccJsonify from './utils/ccJsonify'; * @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type * @returns {object} Preimage-Sha256 Condition (that will need to wrapped in an Output) */ -export default function makeSha256Condition(preimage, json=true) { - const sha256Fulfillment = new cc.PreimageSha256(); - sha256Fulfillment.preimage = new Buffer(preimage); +export default function makeSha256Condition(preimage, json = true) { + const sha256Fulfillment = new cc.PreimageSha256() + sha256Fulfillment.preimage = new Buffer(preimage) if (json) { return ccJsonify(sha256Fulfillment) } - return sha256Fulfillment; + return sha256Fulfillment } diff --git a/src/transaction/makeThresholdCondition.js b/src/transaction/makeThresholdCondition.js index 1dc4c9d..3a8ec11 100644 --- a/src/transaction/makeThresholdCondition.js +++ b/src/transaction/makeThresholdCondition.js @@ -1,6 +1,6 @@ -import cc from 'five-bells-condition'; +import cc from 'five-bells-condition' -import ccJsonify from './utils/ccJsonify'; +import ccJsonify from './utils/ccJsonify' /** @@ -11,14 +11,14 @@ import ccJsonify from './utils/ccJsonify'; * @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type * @returns {object} Sha256 Threshold Condition (that will need to wrapped in an Output) */ -export default function makeThresholdCondition(threshold, subconditions=[], json=true) { - const thresholdCondition = new cc.ThresholdSha256(); - thresholdCondition.threshold = threshold; +export default function makeThresholdCondition(threshold, subconditions = [], json = true) { + const thresholdCondition = new cc.ThresholdSha256() + thresholdCondition.threshold = threshold subconditions.forEach((subcondition) => { // TODO: add support for Condition and URIs - thresholdCondition.addSubfulfillment(subcondition); - }); + thresholdCondition.addSubfulfillment(subcondition) + }) if (json) { return ccJsonify(thresholdCondition) diff --git a/src/transaction/makeTransaction.js b/src/transaction/makeTransaction.js index d9ecf83..bcd118d 100644 --- a/src/transaction/makeTransaction.js +++ b/src/transaction/makeTransaction.js @@ -1,4 +1,4 @@ -import hashTransaction from './hashTransaction'; +import hashTransaction from './hashTransaction' function makeTransactionTemplate() { @@ -10,19 +10,19 @@ function makeTransactionTemplate() { 'metadata': null, 'asset': null, 'version': '0.9', - }; + } } export default function makeTransaction(operation, asset, metadata = null, outputs = [], inputs = []) { - const tx = makeTransactionTemplate(); - tx.operation = operation; - tx.asset = asset; - tx.metadata = metadata; - tx.inputs = inputs; - tx.outputs = outputs; + const tx = makeTransactionTemplate() + tx.operation = operation + tx.asset = asset + tx.metadata = metadata + tx.inputs = inputs + tx.outputs = outputs // Hashing must be done after, as the hash is of the Transaction (up to now) - tx.id = hashTransaction(tx); - return tx; + tx.id = hashTransaction(tx) + return tx } diff --git a/src/transaction/makeTransferTransaction.js b/src/transaction/makeTransferTransaction.js index c5813c0..d28dae8 100644 --- a/src/transaction/makeTransferTransaction.js +++ b/src/transaction/makeTransferTransaction.js @@ -1,5 +1,5 @@ -import makeInputTemplate from './makeInputTemplate'; -import makeTransaction from './makeTransaction'; +import makeInputTemplate from './makeInputTemplate' +import makeTransaction from './makeTransaction' /** @@ -22,21 +22,26 @@ import makeTransaction from './makeTransaction'; * @returns {object} Unsigned transaction -- make sure to call signTransaction() on it before * sending it off! */ -export default function makeTransferTransaction(unspentTransaction, metadata, outputs, ...fulfilledOutputs) { +export default function makeTransferTransaction( + unspentTransaction, + metadata, + outputs, + ...fulfilledOutputs + ) { const inputs = fulfilledOutputs.map((outputIndex) => { - const fulfilledOutput = unspentTransaction.outputs[outputIndex]; + const fulfilledOutput = unspentTransaction.outputs[outputIndex] const transactionLink = { 'output': outputIndex, 'txid': unspentTransaction.id, - }; + } - return makeInputTemplate(fulfilledOutput.public_keys, transactionLink); - }); + return makeInputTemplate(fulfilledOutput.public_keys, transactionLink) + }) const assetLink = { 'id': unspentTransaction.operation === 'CREATE' ? unspentTransaction.id : unspentTransaction.asset.id - }; + } - return makeTransaction('TRANSFER', assetLink, metadata, outputs, inputs); + return makeTransaction('TRANSFER', assetLink, metadata, outputs, inputs) } diff --git a/src/transaction/serializeTransactionIntoCanonicalString.js b/src/transaction/serializeTransactionIntoCanonicalString.js index bf97d55..786cecf 100644 --- a/src/transaction/serializeTransactionIntoCanonicalString.js +++ b/src/transaction/serializeTransactionIntoCanonicalString.js @@ -1,5 +1,5 @@ -import stableStringify from 'json-stable-stringify'; -import clone from 'clone'; +import stableStringify from 'json-stable-stringify' +import clone from 'clone' /** @@ -10,8 +10,8 @@ import clone from 'clone'; */ export default function serializeTransactionIntoCanonicalString(transaction) { // BigchainDB signs fulfillments by serializing transactions into a "canonical" format where - const tx = clone(transaction); + const tx = clone(transaction) // TODO: set fulfillments to null // Sort the keys - return stableStringify(tx, (a, b) => (a.key > b.key ? 1 : -1)); -} \ No newline at end of file + return stableStringify(tx, (a, b) => (a.key > b.key ? 1 : -1)) +} diff --git a/src/transaction/signTransaction.js b/src/transaction/signTransaction.js index 7b974a0..4faa888 100644 --- a/src/transaction/signTransaction.js +++ b/src/transaction/signTransaction.js @@ -1,9 +1,9 @@ -import { Buffer } from 'buffer'; -import base58 from 'bs58'; -import cc from 'five-bells-condition'; -import clone from 'clone'; +import { Buffer } from 'buffer' +import base58 from 'bs58' +import cc from 'five-bells-condition' +import clone from 'clone' -import serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString'; +import serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString' /** @@ -19,17 +19,17 @@ import serializeTransactionIntoCanonicalString from './serializeTransactionIntoC * @returns {object} The signed version of `transaction`. */ export default function signTransaction(transaction, ...privateKeys) { - const signedTx = clone(transaction); + const signedTx = clone(transaction) signedTx.inputs.forEach((input, index) => { - const privateKey = privateKeys[index]; - const privateKeyBuffer = new Buffer(base58.decode(privateKey)); - const serializedTransaction = serializeTransactionIntoCanonicalString(transaction); - const ed25519Fulfillment = new cc.Ed25519(); - ed25519Fulfillment.sign(new Buffer(serializedTransaction), privateKeyBuffer); - const fulfillmentUri = ed25519Fulfillment.serializeUri(); + const privateKey = privateKeys[index] + const privateKeyBuffer = new Buffer(base58.decode(privateKey)) + const serializedTransaction = serializeTransactionIntoCanonicalString(transaction) + const ed25519Fulfillment = new cc.Ed25519() + ed25519Fulfillment.sign(new Buffer(serializedTransaction), privateKeyBuffer) + const fulfillmentUri = ed25519Fulfillment.serializeUri() - input.fulfillment = fulfillmentUri; - }); + input.fulfillment = fulfillmentUri + }) - return signedTx; + return signedTx } diff --git a/src/transaction/utils/ccJsonLoad.js b/src/transaction/utils/ccJsonLoad.js index a6707cc..1a3ac7c 100644 --- a/src/transaction/utils/ccJsonLoad.js +++ b/src/transaction/utils/ccJsonLoad.js @@ -1,6 +1,6 @@ -import base58 from 'bs58'; -import cc from 'five-bells-condition'; -import { Buffer } from 'buffer'; +import { Buffer } from 'buffer' +import base58 from 'bs58' +import cc from 'five-bells-condition' /** * @public @@ -9,41 +9,41 @@ import { Buffer } from 'buffer'; * @returns {cc.Condition} Ed25519 Condition (that will need to wrapped in an Output) */ export default function ccJsonLoad(conditionJson) { - if ('hash' in conditionJson) { - let condition = new cc.Condition(); - condition.type = conditionJson.type_id; - condition.bitmask = conditionJson.bitmask; - condition.hash = new Buffer(base58.decode(conditionJson.hash)); - condition.maxFulfillmentLength = parseInt(conditionJson.max_fulfillment_length, 10); + const condition = new cc.Condition() + condition.type = conditionJson.type_id + condition.bitmask = conditionJson.bitmask + condition.hash = new Buffer(base58.decode(conditionJson.hash)) + condition.maxFulfillmentLength = parseInt(conditionJson.max_fulfillment_length, 10) return condition } else { - let fulfillment; + let fulfillment if (conditionJson.type_id === 2) { - fulfillment = new cc.ThresholdSha256(); - fulfillment.threshold = conditionJson.threshold; - conditionJson.subfulfillments.forEach((subfulfillment) => { - subfulfillment = ccJsonLoad(subfulfillment); - if ('getConditionUri' in subfulfillment) - fulfillment.addSubfulfillment(subfulfillment); - else if ('serializeUri' in subfulfillment) + fulfillment = new cc.ThresholdSha256() + fulfillment.threshold = conditionJson.threshold + conditionJson.subfulfillments.forEach((subfulfillmentJson) => { + const subfulfillment = ccJsonLoad(subfulfillmentJson) + if ('getConditionUri' in subfulfillment) { + fulfillment.addSubfulfillment(subfulfillment) + } else if ('serializeUri' in subfulfillment) { fulfillment.addSubcondition(subfulfillment) + } }) } if (conditionJson.type_id === 0) { - fulfillment = new cc.PreimageSha256(); - fulfillment.preimage = new Buffer(conditionJson.preimage); - + fulfillment = new cc.PreimageSha256() + fulfillment.preimage = new Buffer(conditionJson.preimage) } if (conditionJson.type_id === 4) { - fulfillment = new cc.Ed25519(); - fulfillment.publicKey = new Buffer(base58.decode(conditionJson.public_key)); - if (conditionJson.signature) - fulfillment.signature = new Buffer(base58.decode(conditionJson.signature)); + fulfillment = new cc.Ed25519() + fulfillment.publicKey = new Buffer(base58.decode(conditionJson.public_key)) + if (conditionJson.signature) { + fulfillment.signature = new Buffer(base58.decode(conditionJson.signature)) + } } - return fulfillment; + return fulfillment } } diff --git a/src/transaction/utils/ccJsonify.js b/src/transaction/utils/ccJsonify.js index f8ae532..c80ec24 100644 --- a/src/transaction/utils/ccJsonify.js +++ b/src/transaction/utils/ccJsonify.js @@ -1,36 +1,36 @@ -import base58 from 'bs58'; +import base58 from 'bs58' /** * @public * Serializes a crypto-condition class (Condition or Fulfillment) into a BigchainDB-compatible JSON - * @param {cc.Fulfillment} fulfillment base58 encoded Ed25519 public key for the recipient of the Transaction + * @param {cc.Fulfillment} fulfillment base58 encoded Ed25519 public key for recipient of the Transaction * @returns {object} Ed25519 Condition (that will need to wrapped in an Output) */ export default function ccJsonify(fulfillment) { + let conditionUri - let conditionUri; + if ('getConditionUri' in fulfillment) { + conditionUri = fulfillment.getConditionUri() + } else if ('serializeUri' in fulfillment) { + conditionUri = fulfillment.serializeUri() + } - if ('getConditionUri' in fulfillment) - conditionUri = fulfillment.getConditionUri(); - else if ('serializeUri' in fulfillment) - conditionUri = fulfillment.serializeUri(); - - let jsonBody = { + const jsonBody = { 'details': {}, 'uri': conditionUri, - }; + } if (fulfillment.getTypeId() === 0) { - jsonBody.details.type_id = 0; - jsonBody.details.bitmask = 3; + jsonBody.details.type_id = 0 + jsonBody.details.bitmask = 3 if ('preimage' in fulfillment) { - jsonBody.details.preimage = fulfillment.preimage.toString(); - jsonBody.details.type = 'fulfillment'; + jsonBody.details.preimage = fulfillment.preimage.toString() + jsonBody.details.type = 'fulfillment' } } - if (fulfillment.getTypeId() === 2) + if (fulfillment.getTypeId() === 2) { return { 'details': { 'type_id': 2, @@ -38,30 +38,31 @@ export default function ccJsonify(fulfillment) { 'bitmask': fulfillment.getBitmask(), 'threshold': fulfillment.threshold, 'subfulfillments': fulfillment.subconditions.map((subcondition) => { - const subconditionJson = ccJsonify(subcondition.body); - subconditionJson.details.weight = 1; - return subconditionJson.details; + const subconditionJson = ccJsonify(subcondition.body) + subconditionJson.details.weight = 1 + return subconditionJson.details }) }, 'uri': conditionUri, - }; + } + } if (fulfillment.getTypeId() === 4) { - jsonBody.details.type_id = 4; - jsonBody.details.bitmask = 32; + jsonBody.details.type_id = 4 + jsonBody.details.bitmask = 32 if ('publicKey' in fulfillment) { - jsonBody.details.signature = null; - jsonBody.details.public_key = base58.encode(fulfillment.publicKey); - jsonBody.details.type = 'fulfillment'; + jsonBody.details.signature = null + jsonBody.details.public_key = base58.encode(fulfillment.publicKey) + jsonBody.details.type = 'fulfillment' } } if ('hash' in fulfillment) { - jsonBody.details.hash = base58.encode(fulfillment.hash); - jsonBody.details.max_fulfillment_length = fulfillment.maxFulfillmentLength; - jsonBody.details.type = 'condition'; + jsonBody.details.hash = base58.encode(fulfillment.hash) + jsonBody.details.max_fulfillment_length = fulfillment.maxFulfillmentLength + jsonBody.details.type = 'condition' } - return jsonBody; + return jsonBody } diff --git a/webpack.config.js b/webpack.config.js index 9b2dc9c..7aba528 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,24 +1,24 @@ /* eslint-disable strict, no-console, object-shorthand */ -'use strict'; +'use strict' -const path = require('path'); +const path = require('path') -const webpack = require('webpack'); +const webpack = require('webpack') -const PRODUCTION = process.env.NODE_ENV === 'production'; +const PRODUCTION = process.env.NODE_ENV === 'production' const PATHS = { ENTRY: path.resolve(__dirname, './src/index.js'), BUNDLE: path.resolve(__dirname, 'dist/bundle'), NODE_MODULES: path.resolve(__dirname, 'node_modules'), -}; +} /** PLUGINS **/ const PLUGINS = [ new webpack.NoEmitOnErrorsPlugin(), -]; +] const PROD_PLUGINS = [ new webpack.optimize.UglifyJsPlugin({ @@ -34,10 +34,10 @@ const PROD_PLUGINS = [ debug: false, minimize: true, }), -]; +] if (PRODUCTION) { - PLUGINS.push(...PROD_PLUGINS); + PLUGINS.push(...PROD_PLUGINS) } @@ -75,6 +75,6 @@ const config = { }, ], }, -}; +} -module.exports = config; +module.exports = config