diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1bfbb2ec..44388c767f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added * storing balance, tx history and delegations locally to serve an old state faster @faboweb -* added error message for missing network config @faboweb ### Fixed +* added error message for missing network config @faboweb * testnets not properly available after download @faboweb * Tell the main process when we switch to the mock network. @NodeGuy diff --git a/README.md b/README.md index 5309ac70d6..831f84385a 100644 --- a/README.md +++ b/README.md @@ -169,26 +169,26 @@ Write down the 12 word secret phrase to be able to import an account that holds Copy the configuration files (assuming you are in the Voyager dir): ```bash -$ mkdir builds/testnets/local-testnet -$ cp ~/.gaiad-testnet/config/{genesis.json,config.toml} builds/testnets/local-testnet/ +$ mkdir app/networks/local-testnet +$ cp ~/.gaiad-testnet/config/{genesis.json,config.toml} app/networks/local-testnet/ ``` Enter your local node as a seed: ```bash -$ sed -i.bak 's/seeds = ""/seeds = "localhost"/g' ./builds/testnets/local-testnet/config.toml +$ sed -i.bak 's/seeds = ""/seeds = "localhost"/g' ./app/networks/local-testnet/config.toml ``` Activate TX indexing in your local node: ```bash -$ sed -i.bak 's/index_all_tags = true/index_all_tags = false/g' ./builds/testnets/local-testnet/config.toml +$ sed -i.bak 's/index_all_tags = true/index_all_tags = false/g' ./app/networks/local-testnet/config.toml ``` Store the gaia version used in your local testnet: ```bash -$ ./builds/Gaia/{OS}/gaiad version > ./builds/testnets/local-testnet/gaiaversion.txt +$ ./builds/Gaia/{OS}/gaiad version > ./app/networks/local-testnet/gaiaversion.txt ``` Start your local node: diff --git a/app/networks b/app/networks index b2adf3d0c6..82f11ca892 120000 --- a/app/networks +++ b/app/networks @@ -1 +1 @@ -../builds/testnets \ No newline at end of file +../builds/Gaia/networks/ \ No newline at end of file diff --git a/app/src/renderer/connectors/lcdClient.js b/app/src/renderer/connectors/lcdClient.js index 79d470fbd0..ff50f3b092 100644 --- a/app/src/renderer/connectors/lcdClient.js +++ b/app/src/renderer/connectors/lcdClient.js @@ -90,12 +90,67 @@ Object.assign(Client.prototype, { }, tx: argReq("GET", "/txs"), - // staking - updateDelegations: req("POST", "/stake/delegations"), + /* ============ STAKE ============ */ + + // Get all delegations information from a delegator + getDelegator: function(addr) { + return req("GET", `/stake/delegators/${addr}`).call(this) + }, + // Get all txs from a delegator + getDelegatorTxs: function(addr) { + return req("GET", `/stake/delegators/${addr}/txs`).call(this) + }, + // Get a specific tx from a delegator + getDelegatorTx: function(addr, id, types) { + if (types === "") { + return req("GET", `/stake/delegators/${addr}/txs`).call(this) + } else { + return req("GET", `/stake/delegators/${addr}/txs?type=${types}`).call( + this + ) + } + }, + // // Query all validators that a delegator is bonded to + // getDelegatorValidators: function(delegatorAddr) { + // return req("GET", `/stake/delegators/${delegatorAddr}/validators`).call(this) + // }, + // // Query a validator info that a delegator is bonded to + // getDelegatorValidator: function(delegatorAddr, validatorAddr) { + // return req("GET", `/stake/delegators/${delegatorAddr}/validators/${validatorAddr}`).call(this) + // }, + + // Get a list containing all the validator candidates + getValidators: req("GET", "/stakes/validators/"), + // Get information from a validator + getValidator: function(addr) { + return req("GET", `/stake/validators/${addr}`).call(this) + }, + // // Get all of the validator bonded delegators + // getValidatorDelegators: function(addr) { + // return req("GET", `/stake/validator/${addr}/delegators`).call(this) + // }, + + // Get the list of the validators in the latest validator set + getValidatorSet: req("GET", "/validatorsets/latest"), + + updateDelegations: function(delegatorAddr) { + return req("POST", `/stake/delegators/${delegatorAddr}/delegations`) + }, + candidates: req("GET", "/stake/validators"), getValidators: req("GET", "/validatorsets/latest"), - queryDelegation: function(delegator, validator) { - return req("GET", `/stake/${delegator}/delegation/${validator}`).call(this) + // Query a delegation between a delegator and a validator + queryDelegation: function(delegatorAddr, validatorAddr) { + return req( + "GET", + `/stake/delegators/${delegatorAddr}/delegations/${validatorAddr}` + ).call(this) + }, + queryUnbonding: function(delegatorAddr, validatorAddr) { + return req( + "GET", + `/stake/delegators/${delegatorAddr}/unbonding_delegations/${validatorAddr}` + ).call(this) } }) diff --git a/app/src/renderer/connectors/lcdClientMock.js b/app/src/renderer/connectors/lcdClientMock.js index a995f0ceb8..131859d6dc 100644 --- a/app/src/renderer/connectors/lcdClientMock.js +++ b/app/src/renderer/connectors/lcdClientMock.js @@ -357,9 +357,21 @@ module.exports = { return delegator[validatorAddress] }, async candidates() { + // sort validators by power + state.candidates.sort(function(candidate1, candidate2) { + let power1 = candidate1.tokens + candidate1.delegator_shares + let power2 = candidate2.tokens + candidate2.delegator_shares + return power2 - power1 + }) return state.candidates }, async getValidators() { + // sort validators by power + state.candidates.sort(function(candidate1, candidate2) { + let power1 = candidate1.tokens + candidate1.delegator_shares + let power2 = candidate2.tokens + candidate2.delegator_shares + return power2 - power1 // sort in DESC order + }) return { block_height: 1, validators: state.candidates diff --git a/app/src/renderer/vuex/modules/node.js b/app/src/renderer/vuex/modules/node.js index 39f0b781b0..30c4dd4528 100644 --- a/app/src/renderer/vuex/modules/node.js +++ b/app/src/renderer/vuex/modules/node.js @@ -36,15 +36,13 @@ export default function({ node }) { } const actions = { - setLastHeader({ state, rootState, dispatch }, header) { + setLastHeader({ state, rootState }, header) { state.lastHeader = header // TODO do this somewhere else probably if (!rootState.wallet.zoneIds.find(x => x === header.chain_id)) { rootState.wallet.zoneIds.unshift(header.chain_id) } - - dispatch("maybeUpdateValidators", header) }, async reconnect({ commit }) { if (state.stopConnecting) return @@ -93,6 +91,7 @@ export default function({ node }) { } ) + dispatch("validatorUpdateSubscribe") dispatch("walletSubscribe") dispatch("checkNodeHalted") dispatch("pollRPCConnection") diff --git a/app/src/renderer/vuex/modules/validators.js b/app/src/renderer/vuex/modules/validators.js index 198e8de8ce..2f03f5e4e3 100644 --- a/app/src/renderer/vuex/modules/validators.js +++ b/app/src/renderer/vuex/modules/validators.js @@ -2,6 +2,7 @@ export default ({ node }) => { const state = { validators: [], loading: false, + subscription: false, validatorHash: null } @@ -9,6 +10,12 @@ export default ({ node }) => { setValidators(state, validators) { state.validators = validators }, + updateValidator(state, index, updatedValidator) { + state.validators[index] = updatedValidator + }, + addValidator(state, validator) { + state.validators.push(validator) + }, setValidatorHash(state, validatorHash) { state.validatorHash = validatorHash } @@ -39,6 +46,41 @@ export default ({ node }) => { if (validatorHash === state.validatorHash) return commit("setValidatorHash", validatorHash) dispatch("getValidators") + }, + validatorUpdateSubscribe({ state, commit, dispatch }) { + // Return if already subscribed to updates + if (state.subscription) return + + // TODO move to common file since subscribeToBlock also uses it + function error(err) { + if (err.data === "already subscribed") { + console.error("Tryed to subscribe to an already subscribed rpc event") + return + } + dispatch("nodeHasHalted") + console.error( + `Error subscribing to new validator set updates: ${ + err.message + } ${err.data || ""}` + ) + } + node.rpc.subscribe( + { query: "tm.event = 'ValidatorSetUpdates'" }, + (err, event) => { + if (err) return error(err) + // https://github.com/tendermint/tendermint/pull/2161/files#diff-76d6626766e8d862e9acb34c166664b5 + for (var validator in event.data.value.validator_updates) { + if (validator.power == 0) { + let dumpedValIdx = state.validators.findIndex( + v => v.pubkey == validator.pubkey + ) + commit("updateValidator", dumpedValIdx, validator) + } else { + commit("addValidator", validator) + } + } + } + ) } } diff --git a/tasks/build/testnets/localBuild.sh b/tasks/build/testnets/localBuild.sh index c9035a03eb..dad2fae471 100755 --- a/tasks/build/testnets/localBuild.sh +++ b/tasks/build/testnets/localBuild.sh @@ -1,6 +1,6 @@ #!/bin/sh -if [ ! -f $(pwd)/../../../builds/gaia/linux_amd64/gaiad ]; then +if [ ! -f builds/Gaia/linux_amd64/gaiad ]; then printf "Gaia missing; building first.\n" (cd ../../.. && yarn build:gaia) fi diff --git a/tasks/runner.js b/tasks/runner.js index 3ac6417b5a..a7e4f8fe13 100644 --- a/tasks/runner.js +++ b/tasks/runner.js @@ -61,13 +61,6 @@ function startRendererServer() { } module.exports = async function(networkPath) { - if (!fs.existsSync(networkPath)) { - console.error( - "The network configuration for the network you want to connect to doesn't exist. Have you run `yarn build:testnets` to download the latest configurations?" - ) - process.exit() - } - let renderProcess = await startRendererServer() console.log( diff --git a/test/unit/specs/store/validators.spec.js b/test/unit/specs/store/validators.spec.js index 946f8993f4..906f2eb834 100644 --- a/test/unit/specs/store/validators.spec.js +++ b/test/unit/specs/store/validators.spec.js @@ -82,4 +82,19 @@ describe("Module: Validators", () => { store.dispatch("reconnected") expect(node.rpc.validators).not.toHaveBeenCalled() }) + + it("should not error when subscribing without previous subscription", async () => { + store.state.wallet.subscribed = false + store.dispatch("validatorUpdateSubscribe") + }) + + it("should handle ignore already subscribed errors", () => { + console.error = jest.fn() + node.rpc.subscribe = (query, cb) => { + cb({ message: "expected error", data: "already subscribed" }) + } + store.dispatch("validatorUpdateSubscribe") + expect(console.error.mock.calls.length).toBe(1) + expect(store.dispatch).not.toHaveBeenCalledWith("nodeHasHalted") + }) }) diff --git a/yarn.lock b/yarn.lock index a43cf69aca..402b893c11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2689,8 +2689,8 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" escodegen@^1.6.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.10.0.tgz#f647395de22519fbd0d928ffcf1d17e0dec2603e" + version "1.11.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" dependencies: esprima "^3.1.3" estraverse "^4.2.0"