From c5f4fe6acd557edd6e3a28d1b360c1a5c859e16f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 8 Feb 2018 01:22:33 -0800 Subject: [PATCH 01/29] Move LCD client into this repo --- app/src/renderer/lcdClient.js | 107 ++++++++++++++++++++++++++++++++++ app/src/renderer/node.js | 2 +- 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 app/src/renderer/lcdClient.js diff --git a/app/src/renderer/lcdClient.js b/app/src/renderer/lcdClient.js new file mode 100644 index 0000000000..da93666298 --- /dev/null +++ b/app/src/renderer/lcdClient.js @@ -0,0 +1,107 @@ +'use strict' + +const request = require('axios') + +// returns an async function which makes a request for the given +// HTTP method (GET/POST/DELETE/etc) and path (/foo/bar) +function req (method, path) { + return async function (data) { + return await this.request(method, path, data) + } +} + +// returns an async function which makes a request for the given +// HTTP method and path, which accepts arguments to be appended +// to the path (/foo/{arg}/...) +function argReq (method, path) { + return async function (args, data) { + // `args` can either be a single value or an array + if (Array.isArray(args)) { + args = args.join('/') + } + return await this.request(method, `${path}/${args}`, data) + } +} + +class Client { + constructor (server = 'http://localhost:8998') { + this.server = server + } + + async request (method, path, data) { + try { + let res = await request({ + method, + url: this.server + path, + data + }) + return res.data + } catch (resError) { + let data = resError.response.data + if (!data) throw resError + // server responded with error message, create an Error from that + let error = Error(data.error) + error.code = data.code + throw error + } + } +} + +let fetchAccount = argReq('GET', '/query/account') +let fetchNonce = argReq('GET', '/query/nonce') + +Object.assign(Client.prototype, { + sign: req('POST', '/sign'), + postTx: req('POST', '/tx'), + + // keys + generateKey: req('POST', '/keys'), + listKeys: req('GET', '/keys'), + getKey: argReq('GET', '/keys'), + updateKey: argReq('PUT', '/keys'), + deleteKey: argReq('DELETE', '/keys'), + recoverKey: req('POST', '/keys/recover'), + + // coins + buildSend: req('POST', '/build/send'), + async queryAccount (address) { + try { + return await fetchAccount.call(this, address) + } catch (err) { + // if account not found, return null instead of throwing + if (err.message.includes('account bytes are empty')) { + return null + } + throw err + } + }, + coinTxs: argReq('GET', '/tx/coin'), + + // nonce + async queryNonce (address) { + try { + return await fetchNonce.call(this, address) + } catch (err) { + // if nonce not found, return 0 instead of throwing + if (err.message.includes('nonce empty')) { + return 0 + } + throw err + } + }, + + // Tendermint RPC + status: req('GET', '/tendermint/status'), + + // staking + candidate: argReq('GET', '/query/stake/candidates'), + candidates: req('GET', '/query/stake/candidates'), + buildDelegate: req('POST', '/build/stake/delegate'), + buildUnbond: req('POST', '/build/stake/unbond'), + bondingsByDelegator: argReq('GET', '/tx/bondings/delegator'), + bondingsByValidator: argReq('GET', '/tx/bondings/validator') + + // TODO: separate API registration for different modules +}) + +module.exports = Client diff --git a/app/src/renderer/node.js b/app/src/renderer/node.js index b04e44e673..b00d1f9e85 100644 --- a/app/src/renderer/node.js +++ b/app/src/renderer/node.js @@ -1,6 +1,6 @@ 'use strict' const RpcClient = require('tendermint') -const RestClient = require('cosmos-sdk') +const RestClient = require('./lcdClient.js') module.exports = function (nodeIP, relayPort, lcdPort) { if (JSON.parse(process.env.COSMOS_UI_ONLY || 'false')) { From f4e1da04e6554a6062b97119ca9e15deca98ea8e Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 8 Feb 2018 02:08:52 -0800 Subject: [PATCH 02/29] Removed cosmos-sdk npm dependency --- package.json | 1 - yarn.lock | 24 ++---------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 0e5d84a574..1fa6d6ea57 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,6 @@ "axios": "^0.17.0", "casual": "^1.5.19", "chart.js": "^2.6.0", - "cosmos-sdk": "^1.5.1", "deterministic-tar": "^0.1.2", "deterministic-zip": "^1.0.5", "electron": "^1.7.5", diff --git a/yarn.lock b/yarn.lock index 71e3b04cb2..3ae9d3bcf0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -399,13 +399,6 @@ aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -axios@^0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d" - dependencies: - follow-redirects "^1.2.3" - is-buffer "^1.1.5" - axios@^0.17.0, axios@^0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.1.tgz#2d8e3e5d0bdbd7327f91bc814f5c57660f81824d" @@ -1870,13 +1863,6 @@ cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: parse-json "^2.2.0" require-from-string "^1.1.0" -cosmos-sdk@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/cosmos-sdk/-/cosmos-sdk-1.5.1.tgz#4f648aa7eaa781883dfa1bef9ee78b4fba299cf3" - dependencies: - axios "^0.16.2" - old "^0.2.0" - crc32-stream@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-1.0.1.tgz#ce2c5dc3bd8ffb3830f9cb47f540222c63c90fab" @@ -3237,7 +3223,7 @@ flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" -follow-redirects@^1.2.3, follow-redirects@^1.2.5: +follow-redirects@^1.2.5: version "1.4.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.4.1.tgz#d8120f4518190f55aac65bb6fc7b85fcd666d6aa" dependencies: @@ -5775,12 +5761,6 @@ old@^0.1.3: dependencies: object-assign "^4.1.0" -old@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/old/-/old-0.2.0.tgz#ae75a9f33bae7cb3fe06312899b7ae5a73ba24ef" - dependencies: - object-assign "^4.1.0" - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -7889,7 +7869,7 @@ tar@^2.0.0, tar@^2.2.1: supercop.js "^2.0.1" varstruct "^5.3.0" -tendermint@2.0.5: +tendermint@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/tendermint/-/tendermint-2.0.5.tgz#681014ea04c77f36b8dbaed2e3c430e4a24d6cbc" dependencies: From a2a572e64837a6aea8189666fabb255b236187a9 Mon Sep 17 00:00:00 2001 From: Peng Zhong Date: Wed, 14 Feb 2018 13:19:32 +0800 Subject: [PATCH 03/29] init --- app/src/renderer/components/staking/LiDelegate.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/renderer/components/staking/LiDelegate.vue b/app/src/renderer/components/staking/LiDelegate.vue index 355fb587d1..d4aff3bb4f 100644 --- a/app/src/renderer/components/staking/LiDelegate.vue +++ b/app/src/renderer/components/staking/LiDelegate.vue @@ -12,7 +12,9 @@ .li-delegate__value.status span {{ delegateType }} template(v-if="userCanDelegate") - .li-delegate__value.checkbox#remove-from-cart(v-if="inCart" @click='rm(delegate)') + .li-delegate__value.checkbox(v-if="amountBonded > 0") + i.material-icons check_box + .li-delegate__value.checkbox#remove-from-cart(v-else-if="inCart" @click='rm(delegate)') i.material-icons check_box .li-delegate__value.checkbox#add-to-cart(v-else @click='add(delegate)') i.material-icons check_box_outline_blank @@ -38,7 +40,7 @@ export default { }, styles () { let value = '' - if (this.inCart) value += 'li-delegate-active ' + if (this.inCart || this.amountBonded > 0) value += 'li-delegate-active ' if (this.delegate.isValidator) value += 'li-delegate-validator ' return value }, From c621fb66e3b34936d5f6c2cb22ed736934ff933a Mon Sep 17 00:00:00 2001 From: Peng Zhong Date: Wed, 14 Feb 2018 13:56:46 +0800 Subject: [PATCH 04/29] lock delegates that user previously bonded to, to cart --- .../renderer/components/staking/LiDelegate.vue | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/renderer/components/staking/LiDelegate.vue b/app/src/renderer/components/staking/LiDelegate.vue index d4aff3bb4f..9faaa88b8a 100644 --- a/app/src/renderer/components/staking/LiDelegate.vue +++ b/app/src/renderer/components/staking/LiDelegate.vue @@ -8,12 +8,12 @@ span {{ num.prettyInt(delegate.voting_power) }} .bar(:style='vpStyles') .li-delegate__value.bonded_by_you - span {{ amountBonded }} + span {{ bondedByYou }} .li-delegate__value.status span {{ delegateType }} template(v-if="userCanDelegate") - .li-delegate__value.checkbox(v-if="amountBonded > 0") - i.material-icons check_box + .li-delegate__value.checkbox(v-if="bondedByYou > 0") + i.material-icons lock .li-delegate__value.checkbox#remove-from-cart(v-else-if="inCart" @click='rm(delegate)') i.material-icons check_box .li-delegate__value.checkbox#add-to-cart(v-else @click='add(delegate)') @@ -35,12 +35,12 @@ export default { }, computed: { ...mapGetters(['shoppingCart', 'delegates', 'config', 'committedDelegations', 'user']), - amountBonded () { + bondedByYou () { return this.num.prettyInt(this.committedDelegations[this.delegate.id]) }, styles () { let value = '' - if (this.inCart || this.amountBonded > 0) value += 'li-delegate-active ' + if (this.inCart || this.bondedByYou > 0) value += 'li-delegate-active ' if (this.delegate.isValidator) value += 'li-delegate-validator ' return value }, @@ -80,6 +80,13 @@ export default { methods: { add (delegate) { this.$store.commit('addToCart', delegate) }, rm (delegate) { this.$store.commit('removeFromCart', delegate.id) } + }, + watch: { + bondedByYou (newVal, oldVal) { + if (newVal > 0) { + this.$store.commit('addToCart', this.delegate) + } + } } } From 7e5f83b31fc98c536d8bc8b9314e60851187efb4 Mon Sep 17 00:00:00 2001 From: Peng Zhong Date: Wed, 14 Feb 2018 16:10:06 +0800 Subject: [PATCH 05/29] limit max atoms for bonded validator to remaining user atoms --- .../renderer/components/staking/PageBond.vue | 18 +++++++++++++----- package.json | 1 - yarn.lock | 8 ++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/src/renderer/components/staking/PageBond.vue b/app/src/renderer/components/staking/PageBond.vue index 8ff42fc5c9..82b8980bfe 100644 --- a/app/src/renderer/components/staking/PageBond.vue +++ b/app/src/renderer/components/staking/PageBond.vue @@ -54,10 +54,14 @@ page.page-bond(title="Bond Atoms") label.bond-delta span(v-if="d.deltaAtoms !== 0") {{ d.deltaAtoms }} field.bond-value__input( + :id="`${d.id}-atoms`" type="number" placeholder="Atoms" step="1" - v-model.number="d.atoms") + min="0" + :max="$v.fields.delegates.$each[index].atoms.$params.between.max" + v-model.number="d.atoms" + @change.native="limitMax(d, $event)") form-msg(name="Atoms" type="required" v-if="!$v.fields.delegates.$each[index].atoms.required") @@ -149,10 +153,6 @@ export default { oldUnbondedAtoms () { return this.totalAtoms - this.oldBondedAtoms }, - // not used - // newBondedAtoms () { - // return this.fields.delegates.reduce((sum, d) => sum + (d.atoms || 0), 0) - // }, newUnbondedAtoms () { return this.fields.delegates.reduce((atoms, d) => { let delta = d.oldAtoms - d.atoms @@ -325,6 +325,14 @@ export default { value = Math.round(ratio * 100) } return value + '%' + }, + limitMax (delegate, event) { + let max = parseInt(event.target.max) + if (delegate.atoms >= max) { + console.log(`${delegate.atoms} <= ${max}`) + delegate.atoms = max + return + } } }, async mounted () { diff --git a/package.json b/package.json index a87d63fcf0..46e92f446f 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,6 @@ "@nylira/vue-button": "^4.3.2", "@nylira/vue-field": "^1.1.17", "@nylira/vue-form-msg": "^1.0.3", - "@nylira/vue-input": "^3.2.0", "@nylira/vue-notifications": "^1.4.4", "@vue/test-utils": "^1.0.0-beta.11", "axios": "^0.17.0", diff --git a/yarn.lock b/yarn.lock index 0399e3f150..d1bd9e31e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11,17 +11,13 @@ resolved "https://registry.yarnpkg.com/@nylira/vue-button/-/vue-button-4.3.2.tgz#f27b90a931a1a3b5e27736dba11dfcaa305d6ddb" "@nylira/vue-field@^1.1.17": - version "1.1.17" - resolved "https://registry.yarnpkg.com/@nylira/vue-field/-/vue-field-1.1.17.tgz#a873c2d5e01b7ec664df0de71611b0fd9e066b97" + version "1.2.3" + resolved "https://registry.yarnpkg.com/@nylira/vue-field/-/vue-field-1.2.3.tgz#47b5cde79bbb9d2993b4f5734304e0424734dab1" "@nylira/vue-form-msg@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@nylira/vue-form-msg/-/vue-form-msg-1.0.3.tgz#257e3fcb1da345690f4fba959372b70e8abc9c20" -"@nylira/vue-input@^3.2.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@nylira/vue-input/-/vue-input-3.4.0.tgz#741346ce0613b831b756fccb253731015fa5c246" - "@nylira/vue-notifications@^1.4.4": version "1.4.4" resolved "https://registry.yarnpkg.com/@nylira/vue-notifications/-/vue-notifications-1.4.4.tgz#8ef4d3bbdbcbdf5570feaf17afd45c83c43d5496" From 7ca42c355c16b2887b0c59577c6dada54f4a3871 Mon Sep 17 00:00:00 2001 From: Peng Zhong Date: Wed, 14 Feb 2018 16:15:15 +0800 Subject: [PATCH 06/29] update snapshot --- .../components/staking/__snapshots__/PageBond.spec.js.snap | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap b/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap index e29f470808..7bab324743 100644 --- a/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap +++ b/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap @@ -242,6 +242,9 @@ exports[`PageBond has the expected html structure 1`] = ` Date: Wed, 14 Feb 2018 16:17:04 +0800 Subject: [PATCH 07/29] remove extra id --- app/src/renderer/components/staking/PageBond.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/renderer/components/staking/PageBond.vue b/app/src/renderer/components/staking/PageBond.vue index 82b8980bfe..ca78fc1374 100644 --- a/app/src/renderer/components/staking/PageBond.vue +++ b/app/src/renderer/components/staking/PageBond.vue @@ -54,7 +54,6 @@ page.page-bond(title="Bond Atoms") label.bond-delta span(v-if="d.deltaAtoms !== 0") {{ d.deltaAtoms }} field.bond-value__input( - :id="`${d.id}-atoms`" type="number" placeholder="Atoms" step="1" From b8bc18e94802b2b9460a649e82886969fecc79b5 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 14 Feb 2018 10:36:45 +0100 Subject: [PATCH 08/29] fixed bug with expanding bonding bar --- app/src/renderer/components/staking/PageBond.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/renderer/components/staking/PageBond.vue b/app/src/renderer/components/staking/PageBond.vue index ca78fc1374..d0a198226f 100644 --- a/app/src/renderer/components/staking/PageBond.vue +++ b/app/src/renderer/components/staking/PageBond.vue @@ -301,7 +301,6 @@ export default { d.deltaAtomsPercent = this.percent(this.delta(rawAtoms, d.oldAtoms), this.totalAtoms) } - return d.atoms }, setBondBarOuterWidth () { let outerBar = this.$el.querySelector('.bond-bar__outer') From ca2d95fdc97e0e6eaea4b0f0836a748532b6c916 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 14 Feb 2018 10:36:54 +0100 Subject: [PATCH 09/29] updated snapshots --- .../components/staking/__snapshots__/PageBond.spec.js.snap | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap b/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap index 7bab324743..eba8a4932d 100644 --- a/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap +++ b/test/unit/specs/components/staking/__snapshots__/PageBond.spec.js.snap @@ -242,7 +242,6 @@ exports[`PageBond has the expected html structure 1`] = ` Date: Wed, 14 Feb 2018 11:11:42 +0100 Subject: [PATCH 10/29] made bondbar discrete --- app/src/renderer/components/staking/PageBond.vue | 16 ++++++++++------ .../specs/components/staking/PageBond.spec.js | 5 +++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/src/renderer/components/staking/PageBond.vue b/app/src/renderer/components/staking/PageBond.vue index d0a198226f..c4023056c3 100644 --- a/app/src/renderer/components/staking/PageBond.vue +++ b/app/src/renderer/components/staking/PageBond.vue @@ -283,14 +283,17 @@ export default { restrictSize: { min: { width: offset } } }) .on('resizemove', (event) => { - let target = event.target - let ratio = (event.rect.width - offset) / (this.bondBarOuterWidth - offset) - let rawAtoms = ratio * this.totalAtoms + this.handleResize (event.target, event.rect.width) + }) + }, + handleResize (element, width) { + let offset = this.bondBarScrubWidth + let ratio = Math.round((width - offset) / (this.bondBarOuterWidth - offset) * 100) / 100 + let rawAtoms = ratio * this.totalAtoms - target.style.width = event.rect.width + 'px' + element.style.width = width + 'px' - this.updateDelegateAtoms(target.id.split('-')[1], rawAtoms) - }) + return this.updateDelegateAtoms(element.id.split('-')[1], rawAtoms) }, updateDelegateAtoms (delegateId, rawAtoms) { let d = this.fields.delegates.find(d => d.id === delegateId) @@ -300,6 +303,7 @@ export default { d.deltaAtoms = this.delta(rawAtoms, d.oldAtoms, 'int') d.deltaAtomsPercent = this.percent(this.delta(rawAtoms, d.oldAtoms), this.totalAtoms) + return d } }, setBondBarOuterWidth () { diff --git a/test/unit/specs/components/staking/PageBond.spec.js b/test/unit/specs/components/staking/PageBond.spec.js index 0df32b42d4..ebd2f03b6c 100644 --- a/test/unit/specs/components/staking/PageBond.spec.js +++ b/test/unit/specs/components/staking/PageBond.spec.js @@ -94,6 +94,11 @@ describe('PageBond', () => { expect(delegate.atoms).toBe(88) }) + it('only shows percent based on showing atoms', () => { + let updatedDelegate = wrapper.vm.handleResize(wrapper.vm.$el.querySelector('#delegate-pubkeyX'), 0.12) + expect(updatedDelegate.deltaAtomsPercent).toBe('100%') + }) + it('calculates delta', () => { expect(wrapper.vm.delta(100.23293423, 90.5304934)).toBe(9.70244083) expect(wrapper.vm.delta(100, 90, 'int')).toBe(10) From ddf62424b76e54c5747645ffe9f3a924fec55d4b Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 14 Feb 2018 11:13:04 +0100 Subject: [PATCH 11/29] set cursor to pointer for checkbox --- app/src/renderer/components/staking/LiDelegate.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/renderer/components/staking/LiDelegate.vue b/app/src/renderer/components/staking/LiDelegate.vue index 9faaa88b8a..063d845571 100644 --- a/app/src/renderer/components/staking/LiDelegate.vue +++ b/app/src/renderer/components/staking/LiDelegate.vue @@ -150,6 +150,7 @@ export default { &.checkbox justify-content center + cursor pointer span white-space nowrap From 06b50d6ad518e0866868db7448a149844abe5ace Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 14 Feb 2018 11:19:43 +0100 Subject: [PATCH 12/29] fixed candidates not showing validator flag after update --- app/src/renderer/components/staking/PageDelegates.vue | 2 -- app/src/renderer/vuex/modules/delegates.js | 8 +++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/renderer/components/staking/PageDelegates.vue b/app/src/renderer/components/staking/PageDelegates.vue index 8ab7d5ee48..62a01cf1bd 100644 --- a/app/src/renderer/components/staking/PageDelegates.vue +++ b/app/src/renderer/components/staking/PageDelegates.vue @@ -30,7 +30,6 @@ page(title='Validators and Candidates') diff --git a/app/src/renderer/vuex/modules/delegates.js b/app/src/renderer/vuex/modules/delegates.js index 5ec7ba6cd6..2a5698faef 100644 --- a/app/src/renderer/vuex/modules/delegates.js +++ b/app/src/renderer/vuex/modules/delegates.js @@ -1,4 +1,5 @@ import axios from 'axios' +import indicateValidators from 'scripts/indicateValidators' export default ({ dispatch, node }) => { const state = { @@ -24,14 +25,15 @@ export default ({ dispatch, node }) => { } const actions = { - async getDelegates ({ state, dispatch }) { + async getDelegates ({ state, dispatch, rootState }) { state.loading = true let delegatePubkeys = (await node.candidates()).data - let delegates = await Promise.all(delegatePubkeys.map(pubkey => { + await Promise.all(delegatePubkeys.map(pubkey => { return dispatch('getDelegate', pubkey) })) + state.delegates = indicateValidators(state.delegates, rootState.config.maxValidators) state.loading = false - return delegates + return state.delegates }, async getDelegate ({ commit }, pubkey) { let delegate = (await axios.get(`http://localhost:${node.relayPort}/query/stake/candidate/${pubkey.data}`)).data.data From 40f261e2730e86e4ec00b056375c4135930d239b Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 14 Feb 2018 12:14:26 +0100 Subject: [PATCH 13/29] trigger ci --- app/src/renderer/components/staking/PageDelegates.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/renderer/components/staking/PageDelegates.vue b/app/src/renderer/components/staking/PageDelegates.vue index 62a01cf1bd..0cba7c9a13 100644 --- a/app/src/renderer/components/staking/PageDelegates.vue +++ b/app/src/renderer/components/staking/PageDelegates.vue @@ -121,6 +121,7 @@ export default { left 0 right 0 z-index z(toolBar) + .label color bright line-height 2rem From 8e6533ba76da93cb19d184d9cdf3f1f3ef8bd614 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 14 Feb 2018 12:20:45 +0100 Subject: [PATCH 14/29] linted --- app/src/renderer/components/staking/PageBond.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/renderer/components/staking/PageBond.vue b/app/src/renderer/components/staking/PageBond.vue index c4023056c3..e9d4a4c831 100644 --- a/app/src/renderer/components/staking/PageBond.vue +++ b/app/src/renderer/components/staking/PageBond.vue @@ -283,7 +283,7 @@ export default { restrictSize: { min: { width: offset } } }) .on('resizemove', (event) => { - this.handleResize (event.target, event.rect.width) + this.handleResize(event.target, event.rect.width) }) }, handleResize (element, width) { From 39a5e897ad373938065c15174cf0385a8f2bc8b1 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Feb 2018 11:50:18 +0100 Subject: [PATCH 15/29] add default network to config --- app/src/network.js | 3 ++- config.js | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/network.js b/app/src/network.js index 0a743d5752..f7c417ca7f 100644 --- a/app/src/network.js +++ b/app/src/network.js @@ -1,9 +1,10 @@ let { join } = require('path') let { readFileSync } = require('fs') +let config = require('../../config.js') // this network gets used if none is specified via the // COSMOS_NETWORK env var -let DEFAULT_NETWORK = join(__dirname, '../networks/gaia-2') +let DEFAULT_NETWORK = join(__dirname, '../networks/' + config.default_network) let networkPath = process.env.COSMOS_NETWORK || DEFAULT_NETWORK let genesisText = readFileSync(join(networkPath, 'genesis.json'), 'utf8') diff --git a/config.js b/config.js index 04855b61b0..8eb5bf7214 100644 --- a/config.js +++ b/config.js @@ -31,6 +31,9 @@ let config = { platform: process.env.PLATFORM_TARGET || 'darwin,linux,win32', packageManager: 'yarn' } + }, + + default_network: 'gaia-2', } config.building.name = config.name From 3a296fa56aff441c2c8887c08ecdcf1a62b23996 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Feb 2018 11:50:43 +0100 Subject: [PATCH 16/29] add google analytics --- app/index.ejs | 13 +++++++++++++ config.js | 2 ++ webpack.renderer.config.js | 3 ++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/index.ejs b/app/index.ejs index 9f518f6bae..995a4d7b2f 100644 --- a/app/index.ejs +++ b/app/index.ejs @@ -23,3 +23,16 @@ + + +<% if (htmlWebpackPlugin.options.enableAnalytics) { %> + + + +<% } %> \ No newline at end of file diff --git a/config.js b/config.js index 8eb5bf7214..fa7d05e3ee 100644 --- a/config.js +++ b/config.js @@ -34,6 +34,8 @@ let config = { }, default_network: 'gaia-2', + analytics_networks: ['gaia-2', 'gaia-3-dev', 'gaia-3'], + google_analytics: 'UA-51029217-3', } config.building.name = config.name diff --git a/webpack.renderer.config.js b/webpack.renderer.config.js index 520697cbaf..83ad71f919 100644 --- a/webpack.renderer.config.js +++ b/webpack.renderer.config.js @@ -90,7 +90,8 @@ let rendererConfig = { appModules: process.env.NODE_ENV !== 'production' ? path.resolve(__dirname, 'app/node_modules') : false, - styles: stylus(fs.readFileSync('./app/src/renderer/styles/index.styl', 'utf8')).import('./app/src/renderer/styles/variables.styl').render() + styles: stylus(fs.readFileSync('./app/src/renderer/styles/index.styl', 'utf8')).import('./app/src/renderer/styles/variables.styl').render(), + enableAnalytics: process.env.NODE_ENV === 'production' && settings.analytics_networks.indexOf(settings.default_network) !== -1 }), new webpack.NoEmitOnErrorsPlugin(), // warnings caused by websocket-stream, which has a server-part that is unavailable on the the client From ca7d471289c1126061a06d0a445025a0297f376a Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Feb 2018 11:51:40 +0100 Subject: [PATCH 17/29] add sentry error reporting --- app/src/main/index.js | 36 +++++++++++++++++++++++++--------- app/src/renderer/main.js | 19 ++++++++++++++++++ config.js | 2 +- package.json | 2 ++ yarn.lock | 42 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/app/src/main/index.js b/app/src/main/index.js index b404989001..f9a1ba8e52 100644 --- a/app/src/main/index.js +++ b/app/src/main/index.js @@ -10,11 +10,12 @@ let semver = require('semver') let event = require('event-to-promise') let toml = require('toml') let axios = require('axios') +let Raven = require('raven') let pkg = require('../../../package.json') let relayServer = require('./relayServer.js') let addMenu = require('./menu.js') -let config = require('../../../config.js') +let settings = require('../../../config.js') let started = false let shuttingDown = false @@ -35,11 +36,11 @@ const TEST = JSON.parse(process.env.COSMOS_TEST || 'false') !== false const LOGGING = JSON.parse(process.env.LOGGING || 'true') !== false const MOCK = JSON.parse(process.env.MOCK || !TEST && DEV) !== false const winURL = DEV - ? `http://localhost:${config.wds_port}` + ? `http://localhost:${settings.wds_port}` : `file://${__dirname}/index.html` -const RELAY_PORT = DEV ? config.relay_port : config.relay_port_prod -const LCD_PORT = DEV ? config.lcd_port : config.lcd_port_prod -const NODE = process.env.COSMOS_NODE +const RELAY_PORT = DEV ? settings.relay_port : settings.relay_port_prod +const LCD_PORT = DEV ? settings.lcd_port : settings.lcd_port_prod +const NODE = process.env.COSMOS_NODEß let SERVER_BINARY = 'gaia' + (WIN ? '.exe' : '') @@ -177,6 +178,8 @@ function startProcess (name, args, env) { child.on('exit', (code) => !shuttingDown && log(`${name} exited with code ${code}`)) child.on('error', function (err) { if (!(shuttingDown && err.code === 'ECONNRESET')) { + // TODO test + Raven.captureException(err) // if we throw errors here, they are not handled by the main process console.error('[Uncaught Exception] Child', name, 'produced an unhandled exception:', err) console.log('Shutting down UI') @@ -303,15 +306,16 @@ function setupLogging (root) { if (!TEST) { process.on('exit', shutdown) + // on uncaught exceptions we wait so the sentry event can be sent process.on('uncaughtException', async function (err) { + await sleep(1000) logError('[Uncaught Exception]', err) - console.error('[Uncaught Exception]', err) await shutdown() process.exit(1) }) process.on('unhandledRejection', async function (err) { + await sleep(1000) logError('[Unhandled Promise Rejection]', err) - console.error('[Unhandled Promise Rejection]', err) await shutdown() process.exit(1) }) @@ -366,7 +370,21 @@ async function reconnect (seeds) { return nodeIP } +function setupAnalytics () { + let networkIsWhitelisted = settings.analytics_networks.indexOf(settings.default_network) !== -1 + if (networkIsWhitelisted) { + log('Adding analytics') + } + + // only enable sending of error events in production setups and if the network is a testnet + Raven.config(networkIsWhitelisted && process.env.NODE_ENV === 'production' ? settings.sentry_dsn : '', { + captureUnhandledRejections: true + }).install() +} + async function main () { + setupAnalytics() + let appVersionPath = join(root, 'app_version') let genesisPath = join(root, 'genesis.json') let configPath = join(root, 'config.toml') @@ -399,7 +417,7 @@ async function main () { // TODO: versions of the app with different data formats will need to learn how to // migrate old data throw Error(`Data was created with an incompatible app version - data=${existingVersion} app=${pkg.version}`) + data=${existingVersion} app=${pkg.version}`) } } else { throw Error(`The data directory (${root}) has missing files`) @@ -441,7 +459,7 @@ async function main () { // TODO: semver check, or exact match? if (gaiaVersion !== expectedGaiaVersion) { throw Error(`Requires gaia ${expectedGaiaVersion}, but got ${gaiaVersion}. - Please update your gaia installation or build with a newer binary.`) + Please update your gaia installation or build with a newer binary.`) } // read chainId from genesis.json diff --git a/app/src/renderer/main.js b/app/src/renderer/main.js index 5042de94d4..d7eedccf9a 100644 --- a/app/src/renderer/main.js +++ b/app/src/renderer/main.js @@ -5,16 +5,34 @@ import Router from 'vue-router' import Vuelidate from 'vuelidate' import shrinkStacktrace from '../helpers/shrink-stacktrace.js' import axios from 'axios' +import Raven from 'raven-js' + +const electron = window.require('electron') +const remote = electron.remote +const config = require('../../../config') import App from './App' import routes from './routes' import Node from './node' import Store from './vuex/store' +// setup sentry remote error reporting on testnets +const networkIsWhitelisted = config.analytics_networks.indexOf(config.default_network) !== -1 +Raven.config(networkIsWhitelisted && remote.getGlobal('process').env.NODE_ENV === 'production' ? config.sentry_dsn : '').install() + +// handle uncaught errors +window.addEventListener('unhandledrejection', function (event) { + Raven.captureException(event.reason) +}) +window.addEventListener('error', function (event) { + Raven.captureException(event.reason) +}) Vue.config.errorHandler = (error, vm, info) => { + Raven.captureException(error) shrinkStacktrace(error) return true } + Vue.use(Electron) Vue.use(Resource) Vue.use(Router) @@ -35,6 +53,7 @@ async function main () { node.lcdConnected() .then(connected => { + throw new Error('Expected') if (connected) { axios.get(`http://localhost:${relayPort}/startsuccess`) } diff --git a/config.js b/config.js index fa7d05e3ee..eb2dffe301 100644 --- a/config.js +++ b/config.js @@ -30,12 +30,12 @@ let config = { overwrite: true, platform: process.env.PLATFORM_TARGET || 'darwin,linux,win32', packageManager: 'yarn' - } }, default_network: 'gaia-2', analytics_networks: ['gaia-2', 'gaia-3-dev', 'gaia-3'], google_analytics: 'UA-51029217-3', + sentry_dsn: 'https://4dee9f70a7d94cc0959a265c45902d84:cbf160384aab4cdeafbe9a08dee3b961@sentry.io/288169' } config.building.name = config.name diff --git a/package.json b/package.json index a87d63fcf0..90b10b5e65 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,8 @@ "no-scroll": "^2.1.0", "numeral": "^2.0.6", "perfect-scrollbar": "^1.3.0", + "raven": "^2.4.1", + "raven-js": "^3.22.3", "semver": "^5.4.1", "shortid": "^2.2.8", "stacktrace-js": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index 0399e3f150..3314297e48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1503,6 +1503,10 @@ chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + chart.js@^2.6.0: version "2.7.1" resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.7.1.tgz#ae90b4aa4ff1f02decd6b1a2a8dabfd73c9f9886" @@ -1952,6 +1956,10 @@ cross-unzip@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/cross-unzip/-/cross-unzip-0.0.2.tgz#5183bc47a09559befcf98cc4657964999359372f" +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -4065,7 +4073,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.1.5: +is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -5222,6 +5230,14 @@ md5.js@^1.3.4: hash-base "^3.0.0" inherits "^2.0.1" +md5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -6689,6 +6705,20 @@ range-parser@^1.0.3, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" +raven-js@^3.22.3: + version "3.22.3" + resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.3.tgz#8330dcc102b699ffbc2f48790978b997bf4d8f75" + +raven@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raven/-/raven-2.4.1.tgz#7a6a6ff1c42d0a3892308f44c94273e7f88677fd" + dependencies: + cookie "0.3.1" + md5 "^2.2.1" + stack-trace "0.0.9" + timed-out "4.0.1" + uuid "3.0.0" + raw-body@2.3.2, raw-body@^2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" @@ -7579,6 +7609,10 @@ stack-generator@^2.0.1: dependencies: stackframe "^1.0.4" +stack-trace@0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" + stackframe@^1.0.3, stackframe@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.0.4.tgz#357b24a992f9427cba6b545d96a14ed2cbca187b" @@ -7990,7 +8024,7 @@ time-stamp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357" -timed-out@^4.0.0: +timed-out@4.0.1, timed-out@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -8333,6 +8367,10 @@ utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" +uuid@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728" + uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" From 831fcafd22c9edcb7496be705d0167a833971756 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Feb 2018 11:52:37 +0100 Subject: [PATCH 18/29] updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e52599f78..8342edd599 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,3 +57,5 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [0.4.3] - 2018-02-05 ### Changed * Renamed Cosmos UI to Cosmos Voyager. @nylira +* Added Google Analytics for testnet versions +* Added Sentry error reporting for testnet versions \ No newline at end of file From 5c5c23d9c4cea2e37529048e9209a6e196d8de4f Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Feb 2018 11:53:58 +0100 Subject: [PATCH 19/29] removed expected thrown error --- app/src/renderer/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/renderer/main.js b/app/src/renderer/main.js index d7eedccf9a..ab8455bda2 100644 --- a/app/src/renderer/main.js +++ b/app/src/renderer/main.js @@ -53,7 +53,6 @@ async function main () { node.lcdConnected() .then(connected => { - throw new Error('Expected') if (connected) { axios.get(`http://localhost:${relayPort}/startsuccess`) } From c37327e14f41732d6c2d15016fdf58febaba6bec Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Feb 2018 11:57:12 +0100 Subject: [PATCH 20/29] fixed typo --- app/src/main/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/index.js b/app/src/main/index.js index f9a1ba8e52..41e402033a 100644 --- a/app/src/main/index.js +++ b/app/src/main/index.js @@ -40,7 +40,7 @@ const winURL = DEV : `file://${__dirname}/index.html` const RELAY_PORT = DEV ? settings.relay_port : settings.relay_port_prod const LCD_PORT = DEV ? settings.lcd_port : settings.lcd_port_prod -const NODE = process.env.COSMOS_NODEß +const NODE = process.env.COSMOS_NODE let SERVER_BINARY = 'gaia' + (WIN ? '.exe' : '') From 0347c376303a6f4db98b83e67e1495eeb0b1b7f9 Mon Sep 17 00:00:00 2001 From: Jordan Bibla Date: Thu, 15 Feb 2018 15:19:32 -0500 Subject: [PATCH 21/29] changed message to accommodate bonding and unbonding, updated snap --- app/src/renderer/components/staking/PageBond.vue | 4 ++-- .../staking/__snapshots__/PageDelegates.spec.js.snap | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/renderer/components/staking/PageBond.vue b/app/src/renderer/components/staking/PageBond.vue index e9d4a4c831..dbe920a44c 100644 --- a/app/src/renderer/components/staking/PageBond.vue +++ b/app/src/renderer/components/staking/PageBond.vue @@ -205,8 +205,8 @@ export default { this.$store.commit('activateDelegation') try { await this.$store.dispatch('submitDelegation', this.fields) - this.$store.commit('notify', { title: 'Atoms Bonded', - body: 'You have successfully updated your delegations.' }) + this.$store.commit('notify', { title: 'Successful Delegation', + body: 'You have successfully bonded / unbonded.' }) this.$router.push('/staking') } catch (err) { this.$store.commit('notifyError', { title: 'Error While Bonding Atoms', diff --git a/test/unit/specs/components/staking/__snapshots__/PageDelegates.spec.js.snap b/test/unit/specs/components/staking/__snapshots__/PageDelegates.spec.js.snap index 479628e63a..2b8437e421 100644 --- a/test/unit/specs/components/staking/__snapshots__/PageDelegates.spec.js.snap +++ b/test/unit/specs/components/staking/__snapshots__/PageDelegates.spec.js.snap @@ -81,7 +81,7 @@ exports[`PageDelegates has the expected html structure 1`] = ` -
+
candidateY @@ -97,14 +97,14 @@ exports[`PageDelegates has the expected html structure 1`] = ` 0
- Candidate + Validator
check_box_outline_blank
-
+
candidateX @@ -120,7 +120,7 @@ exports[`PageDelegates has the expected html structure 1`] = ` 0
- Candidate + Validator
check_box_outline_blank From dfb42370625e9db6077a4a95546ebe01d9eeb509 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Feb 2018 20:57:07 +0000 Subject: [PATCH 22/29] fix tests --- test/unit/specs/main.spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/unit/specs/main.spec.js b/test/unit/specs/main.spec.js index c7a3bad2c7..3cf49aaf49 100644 --- a/test/unit/specs/main.spec.js +++ b/test/unit/specs/main.spec.js @@ -279,8 +279,7 @@ describe('Startup Process', () => { } catch (_err) { err = _err } - expect(err.message).toBe(`Data was created with an incompatible app version - data=0.1.0 app=1.1.1`) + expect(err.message).toContain(`incompatible app version`) let appVersion = fs.readFileSync(testRoot + 'app_version', 'utf8') expect(appVersion).toBe('0.1.0') From 5ed5c7c019119c933013189753e2adf8dab0ca93 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 16 Feb 2018 09:25:02 +0000 Subject: [PATCH 23/29] fix tests --- app/src/renderer/main.js | 3 +-- test/unit/specs/App.spec.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/renderer/main.js b/app/src/renderer/main.js index ab8455bda2..9454bec942 100644 --- a/app/src/renderer/main.js +++ b/app/src/renderer/main.js @@ -6,9 +6,8 @@ import Vuelidate from 'vuelidate' import shrinkStacktrace from '../helpers/shrink-stacktrace.js' import axios from 'axios' import Raven from 'raven-js' +import {remote} from 'electron' -const electron = window.require('electron') -const remote = electron.remote const config = require('../../../config') import App from './App' diff --git a/test/unit/specs/App.spec.js b/test/unit/specs/App.spec.js index 3f96ae0d62..838257c607 100644 --- a/test/unit/specs/App.spec.js +++ b/test/unit/specs/App.spec.js @@ -1,4 +1,17 @@ jest.mock('renderer/node.js', () => () => require('../helpers/node_mock')) +jest.mock('electron', () => ({ + require: jest.genMockFunction(), + match: jest.genMockFunction(), + app: jest.genMockFunction(), + remote: { + getGlobal: () => ({ + env: { + NODE_ENV: 'test' + } + }) + }, + dialog: jest.genMockFunction() +})) describe('App', () => { it('has all dependencies', async done => { From e018ecb27746913a5ece6bf1771c4b7501fba004 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 16 Feb 2018 09:42:57 +0000 Subject: [PATCH 24/29] renaming --- app/src/main/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/index.js b/app/src/main/index.js index 41e402033a..7b2cd451c0 100644 --- a/app/src/main/index.js +++ b/app/src/main/index.js @@ -15,7 +15,7 @@ let Raven = require('raven') let pkg = require('../../../package.json') let relayServer = require('./relayServer.js') let addMenu = require('./menu.js') -let settings = require('../../../config.js') +let config = require('../../../config.js') let started = false let shuttingDown = false @@ -36,10 +36,10 @@ const TEST = JSON.parse(process.env.COSMOS_TEST || 'false') !== false const LOGGING = JSON.parse(process.env.LOGGING || 'true') !== false const MOCK = JSON.parse(process.env.MOCK || !TEST && DEV) !== false const winURL = DEV - ? `http://localhost:${settings.wds_port}` + ? `http://localhost:${config.wds_port}` : `file://${__dirname}/index.html` -const RELAY_PORT = DEV ? settings.relay_port : settings.relay_port_prod -const LCD_PORT = DEV ? settings.lcd_port : settings.lcd_port_prod +const RELAY_PORT = DEV ? config.relay_port : config.relay_port_prod +const LCD_PORT = DEV ? config.lcd_port : config.lcd_port_prod const NODE = process.env.COSMOS_NODE let SERVER_BINARY = 'gaia' + (WIN ? '.exe' : '') @@ -371,13 +371,13 @@ async function reconnect (seeds) { } function setupAnalytics () { - let networkIsWhitelisted = settings.analytics_networks.indexOf(settings.default_network) !== -1 + let networkIsWhitelisted = config.analytics_networks.indexOf(config.default_network) !== -1 if (networkIsWhitelisted) { log('Adding analytics') } // only enable sending of error events in production setups and if the network is a testnet - Raven.config(networkIsWhitelisted && process.env.NODE_ENV === 'production' ? settings.sentry_dsn : '', { + Raven.config(networkIsWhitelisted && process.env.NODE_ENV === 'production' ? config.sentry_dsn : '', { captureUnhandledRejections: true }).install() } @@ -475,8 +475,8 @@ async function main () { } catch (e) { throw new Error(`Can't open config.toml: ${e.message}`) } - let config = toml.parse(configText) - let seeds = config.p2p.seeds.split(',').filter(x => x !== '') + let configTOML = toml.parse(configText) + let seeds = configTOML.p2p.seeds.split(',').filter(x => x !== '') if (seeds.length === 0) { throw new Error('No seeds specified in config.toml') } From cf64e8d599993e7096728e069939592ff10470bc Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 19 Feb 2018 14:20:59 +0100 Subject: [PATCH 25/29] Update PULL_REQUEST_TEMPLATE.md added the closes prefix --- PULL_REQUEST_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index a5c5776b89..73a70e213f 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -3,8 +3,8 @@ ### Issue - - + +closes: ISSUE ### Screenshots From 9adfd9852672623956d4acded322d22aa0667d0f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 19 Feb 2018 10:35:24 -0800 Subject: [PATCH 26/29] Fix zip nondeterminism --- tasks/release.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tasks/release.js b/tasks/release.js index ddd61fd143..5a17eb7cc2 100644 --- a/tasks/release.js +++ b/tasks/release.js @@ -124,9 +124,18 @@ function zipFolder (inDir, outDir, version) { return reject(err) } files - .filter(file => !fs.lstatSync(file).isDirectory()) .forEach(file => { - zip.file(path.relative(inDir, file), fs.readFileSync(file), {date: new Date('1987-08-16')}) // make the zip deterministic by changing all file times + // make the zip deterministic by changing all file times + if (fs.lstatSync(file).isDirectory()) { + zip.file(path.relative(inDir, file), null, { + dir: true, + date: new Date('1993-06-16') + }) + } else { + zip.file(path.relative(inDir, file), fs.readFileSync(file), { + date: new Date('1987-08-16') + }) + } }) resolve() }) From 10642f09eff74095a8c1ea2c1b27f5c43057b035 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 20 Feb 2018 02:42:30 -0800 Subject: [PATCH 27/29] Made tar bundling not follow symlinks --- tasks/release.js | 70 +++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/tasks/release.js b/tasks/release.js index 5a17eb7cc2..bb63e65632 100644 --- a/tasks/release.js +++ b/tasks/release.js @@ -151,34 +151,44 @@ function zipFolder (inDir, outDir, version) { }) } -function tarFolder (inDir, outDir, version) { - return new Promise(async (resolve, reject) => { - let name = path.parse(inDir).name - let outFile = path.join(outDir, `${name}_${version}.tar.gz`) - var pack = tar.pack() +async function tarFolder (inDir, outDir, version) { + let name = path.parse(inDir).name + let outFile = path.join(outDir, `${name}_${version}.tar.gz`) + var pack = tar.pack() - await new Promise((resolve) => { - glob(inDir + '/**/*', (err, files) => { - if (err) { - return reject(err) - } - // add files to tar - files - .filter(file => !fs.lstatSync(file).isDirectory()) - .forEach(file => { - try { - pack.entry({ name: path.relative(inDir, file) }, fs.readFileSync(file)) - } catch (err) { - console.error(`Couldn't pack file`, file, err) - // skip this file - } - }) - pack.finalize() - resolve() + let files = glob(inDir + '/**', { sync: true }) + + // add files to tar + for (let file of files) { + try { + let stats = fs.lstatSync(file) + + let contents, linkname, type + if (stats.isDirectory()) { + continue + } else if (stats.isSymbolicLink()) { + linkname = fs.readlinkSync(file) + type = 'symlink' + } else { + contents = fs.readFileSync(file) + type = 'file' + } + await new Promise((resolve) => { + pack.entry(Object.assign({}, stats, { + name: path.relative(inDir, file), + type, + linkname + }), contents, resolve) }) - }) + } catch (err) { + console.error(`Couldn't pack file`, file, err) + // skip this file + } + } + pack.finalize() - // make tar deterministic + // make tar deterministic + await new Promise((resolve) => { pack .pipe(deterministicTar()) // save tar to disc @@ -202,17 +212,17 @@ function deterministicTar () { var extract = tar.extract() .on('entry', function (header, stream, cb) { - if (header.type !== 'file') return cb() - header.mtime = header.atime = header.ctime = UNIXZERO header.uid = header.gid = 0 delete header.uname delete header.gname - header.mode = 0o777 - - stream.pipe(pack.entry(header, cb)) + if (header.type === 'file') { + stream.pipe(pack.entry(header, cb)) + } else { + pack.entry(header, cb) + } }) .on('finish', function () { pack.finalize() From 77f3854aee760dff7bff51c63a528ed0bfc3c8c8 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 21 Feb 2018 16:03:50 -0800 Subject: [PATCH 28/29] Added LCD Client spec --- app/src/renderer/lcdClient.js | 9 ++-- test/unit/specs/lcdClient.spec.js | 76 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 test/unit/specs/lcdClient.spec.js diff --git a/app/src/renderer/lcdClient.js b/app/src/renderer/lcdClient.js index da93666298..4d8cb7672c 100644 --- a/app/src/renderer/lcdClient.js +++ b/app/src/renderer/lcdClient.js @@ -1,6 +1,6 @@ 'use strict' -const request = require('axios') +const axios = require('axios') // returns an async function which makes a request for the given // HTTP method (GET/POST/DELETE/etc) and path (/foo/bar) @@ -30,13 +30,10 @@ class Client { async request (method, path, data) { try { - let res = await request({ - method, - url: this.server + path, - data - }) + let res = await axios[method.toLowerCase()](this.server + path, data) return res.data } catch (resError) { + if (!resError.response) throw resError let data = resError.response.data if (!data) throw resError // server responded with error message, create an Error from that diff --git a/test/unit/specs/lcdClient.spec.js b/test/unit/specs/lcdClient.spec.js new file mode 100644 index 0000000000..1b8b5de528 --- /dev/null +++ b/test/unit/specs/lcdClient.spec.js @@ -0,0 +1,76 @@ +let axios = require('axios') +let LcdClient = require('../../../app/src/renderer/lcdClient.js') + +describe('LCD Client', () => { + let client = new LcdClient() + + it('makes a GET request with no args', async () => { + axios.get = jest.fn() + .mockReturnValueOnce(Promise.resolve({ + data: { foo: 'bar' } + })) + + let res = await client.status() + expect(res).toEqual({ foo: 'bar' }) + expect(axios.get.mock.calls[0]).toEqual([ + 'http://localhost:8998/tendermint/status', + undefined + ]) + }) + + it('makes a GET request with one arg', async () => { + axios.get = jest.fn() + .mockReturnValueOnce(Promise.resolve({ + data: { foo: 'bar' } + })) + + let res = await client.getKey('myKey') + expect(res).toEqual({ foo: 'bar' }) + expect(axios.get.mock.calls[0]).toEqual([ + 'http://localhost:8998/keys/myKey', + undefined + ]) + }) + + it('makes a GET request with multiple args', async () => { + axios.get = jest.fn() + .mockReturnValueOnce(Promise.resolve({ + data: { foo: 'bar' } + })) + + let res = await client.bondingsByDelegator([ 'foo', 'bar' ]) + expect(res).toEqual({ foo: 'bar' }) + expect(axios.get.mock.calls[0]).toEqual([ + 'http://localhost:8998/tx/bondings/delegator/foo/bar', + undefined + ]) + }) + + it('makes a POST request', async () => { + axios.post = jest.fn() + .mockReturnValueOnce(Promise.resolve({ + data: { foo: 'bar' } + })) + + let res = await client.generateKey() + expect(res).toEqual({ foo: 'bar' }) + expect(axios.post.mock.calls[0]).toEqual([ + 'http://localhost:8998/keys', + undefined + ]) + }) + + it('makes a POST request with args and data', async () => { + axios.put = jest.fn() + .mockReturnValueOnce(Promise.resolve({ + data: { foo: 'bar' } + })) + + let res = await client.updateKey('myKey', { abc: 123 }) + expect(res).toEqual({ foo: 'bar' }) + expect(axios.put.mock.calls[0]).toEqual([ + 'http://localhost:8998/keys/myKey', + { abc: 123 } + ]) + }) +}) From 3ca44b13415a7de4f118a0cc9d8e1180cf669b64 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 21 Feb 2018 16:27:43 -0800 Subject: [PATCH 29/29] Added more test coverage --- app/src/renderer/lcdClient.js | 5 ++-- test/unit/specs/lcdClient.spec.js | 49 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/src/renderer/lcdClient.js b/app/src/renderer/lcdClient.js index 4d8cb7672c..ae4b76654c 100644 --- a/app/src/renderer/lcdClient.js +++ b/app/src/renderer/lcdClient.js @@ -33,9 +33,10 @@ class Client { let res = await axios[method.toLowerCase()](this.server + path, data) return res.data } catch (resError) { - if (!resError.response) throw resError + if (!resError.response || !resError.response.data) { + throw resError + } let data = resError.response.data - if (!data) throw resError // server responded with error message, create an Error from that let error = Error(data.error) error.code = data.code diff --git a/test/unit/specs/lcdClient.spec.js b/test/unit/specs/lcdClient.spec.js index 1b8b5de528..775db24a2e 100644 --- a/test/unit/specs/lcdClient.spec.js +++ b/test/unit/specs/lcdClient.spec.js @@ -73,4 +73,53 @@ describe('LCD Client', () => { { abc: 123 } ]) }) + + it('makes a GET request with an error', async () => { + axios.get = jest.fn() + .mockReturnValueOnce(Promise.reject({ + response: { + data: { + error: 'foo', + code: 123 + } + } + })) + + try { + await client.status() + } catch (err) { + expect(err.message).toBe('foo') + expect(err.code).toBe(123) + } + expect(axios.get.mock.calls[0]).toEqual([ + 'http://localhost:8998/tendermint/status', + undefined + ]) + }) + + it('does not throw error for empty results', async () => { + axios.get = jest.fn() + .mockReturnValueOnce(Promise.reject({ + response: { + data: { + error: 'account bytes are empty', + code: 1 + } + } + })) + let res = await client.queryAccount('address') + expect(res).toBe(null) + + axios.get = jest.fn() + .mockReturnValueOnce(Promise.reject({ + response: { + data: { + error: 'nonce empty', + code: 2 + } + } + })) + res = await client.queryNonce('address') + expect(res).toBe(0) + }) })