diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..c617a9b40 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,27 @@ +name: Release + +on: + release: + types: [published] + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v2 + + - name: Login to Docker Hub + run: docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Inject slug/short variables + uses: rlespinasse/github-slug-action@v2.x + + - name: Build docker image + run: | + docker build -t sifchain/block-explorer:${{ env.GITHUB_REF_SLUG }} . + + - name: Push docker images + run: | + docker push sifchain/block-explorer:${{ env.GITHUB_REF_SLUG }} diff --git a/.meteor/packages b/.meteor/packages index 6bb8758b3..922d4d84e 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -30,3 +30,8 @@ jquery universe:i18n server-render@0.3.1 fourseven:scss +meteorhacks:npm + + +npm-container +meteorhacks:async diff --git a/.meteor/versions b/.meteor/versions index 776482dfc..d032be907 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -43,6 +43,8 @@ lmieulet:meteor-coverage@3.2.0 logging@1.1.20 meteor@1.9.3 meteor-base@1.4.0 +meteorhacks:async@1.0.0 +meteorhacks:npm@1.5.0 meteortesting:browser-tests@1.3.4 meteortesting:mocha@1.1.5 meteortesting:mocha-core@7.0.1 @@ -58,6 +60,7 @@ mongo@1.10.0 mongo-decimal@0.1.1 mongo-dev-server@1.1.0 mongo-id@1.0.7 +npm-container@1.2.0 npm-mongo@3.8.1 ordered-dict@1.1.0 promise@0.11.2 @@ -79,7 +82,7 @@ templating-tools@1.1.2 tracker@1.2.0 typescript@3.7.6 underscore@1.0.10 -universe:i18n@1.30.0 +universe:i18n@1.31.0 url@1.3.1 webapp@1.9.1 webapp-hashing@1.0.9 diff --git a/Dockerfile b/Dockerfile index dcf999e1e..486f77c63 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,9 @@ ENV SCRIPTS_FOLDER /docker RUN apk --no-cache add \ bash \ - ca-certificates + ca-certificates \ + curl \ + jq COPY --from=1 $SCRIPTS_FOLDER $SCRIPTS_FOLDER/ diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..54872ffac --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +REVISION = $(shell git rev-parse HEAD) +export START_TIME:=$(shell date -u +%s) + +FILE=settings.json + +help: + @echo "help\t\tPrint this help" + @echo "build\t\tBuild Docker images" + @echo "up\t\tStart Docker containers defined in docker-compose.yml" + @echo "down\t\tStop Docker containers" + @echo "clean\t\tRemove dangling images and exited containers" + @echo "clean_volumes\tPrunes volumes and replay sifchain from the beginning" + +elapsed_time: + @echo "$$(( `date -u +%s` - $(START_TIME) )) seconds elapsed" + +build: + @docker-compose build + @make elapsed_time + @echo "All built 🏛" + +up: + METEOR_SETTINGS=`cat $(FILE)` docker-compose up -d + @docker-compose logs --tail 10 -f + +down: + @docker-compose stop + +clean: + @echo "Deleting exited containers..." + @docker ps -a -q -f status=exited | xargs docker rm -v + @echo "Deleting dangling images..." + @docker images -q -f dangling=true | xargs docker rmi + @make elapsed_time + @echo "All clean 🛀" + +clean_volumes: + @echo "Prune volumes (WARNING: includes your existing database volume if your containers are stopped)..." + @docker volume prune + @make elapsed_time + @echo "All clean 🛀" + +shdb: + @docker exec -it $(docker ps | grep db | cut -d' ' -f1) /bin/bash + diff --git a/both/document.html b/both/document.html index 36e534fd7..837670415 100644 --- a/both/document.html +++ b/both/document.html @@ -1,11 +1,11 @@ - Big Dipper | Block Explorer by Forbole + Sifchain - Block Explorer - - + + diff --git a/both/i18n/en-us.i18n.yml b/both/i18n/en-us.i18n.yml index a565b91da..b3a91237f 100644 --- a/both/i18n/en-us.i18n.yml +++ b/both/i18n/en-us.i18n.yml @@ -19,7 +19,7 @@ common: hash: 'Hash' more: 'More' fullStop: '.' - searchPlaceholder: 'Search with tx hash / block height / address' + searchPlaceholder: 'Search with Tx Hash / Block Height / Address' cancel: 'Cancel' retry: 'Retry' rewards: 'Rewards' @@ -143,6 +143,8 @@ transactions: distribution: 'Distribution' governance: 'Governance' slashing: 'Slashing' + clp: "CLP" + peg: "Peg" proposals: notFound: 'No proposal found.' listOfProposals: 'Here is a list of governance proposals.' diff --git a/both/utils/coins.js b/both/utils/coins.js index 46f5fd76b..d9f31668a 100644 --- a/both/utils/coins.js +++ b/both/utils/coins.js @@ -1,85 +1,85 @@ -import { Meteor } from 'meteor/meteor'; -import numbro from 'numbro'; +import { Meteor } from 'meteor/meteor' +import numbro from 'numbro' autoformat = (value) => { - let formatter = '0,0.0000'; - value = Math.round(value * 1000) / 1000 - if (Math.round(value) === value) - formatter = '0,0' - else if (Math.round(value*10) === value*10) - formatter = '0,0.0' - else if (Math.round(value*100) === value*100) - formatter = '0,0.00' - else if (Math.round(value*1000) === value*1000) - formatter = '0,0.000' - return numbro(value).format(formatter) + let formatter = '0,0.0000' + value = Math.round(value * 1000) / 1000 + if (Math.round(value) === value) formatter = '0,0' + else if (Math.round(value * 10) === value * 10) formatter = '0,0.0' + else if (Math.round(value * 100) === value * 100) formatter = '0,0.00' + else if (Math.round(value * 1000) === value * 1000) formatter = '0,0.000' + return numbro(value).format(formatter) } -const coinList = Meteor.settings.public.coins; +const coinList = Meteor.settings.public.coins for (let i in coinList) { - const coin = coinList[i]; - if (!coin.displayNamePlural) { - coin.displayNamePlural = coin.displayName; - } + const coin = coinList[i] + if (!coin.displayNamePlural) { + coin.displayNamePlural = coin.displayName + } } - export default class Coin { - static StakingCoin = coinList.find(coin => coin.denom === Meteor.settings.public.bondDenom); - static MinStake = 1 / Number(Coin.StakingCoin.fraction); - - constructor(amount, denom=Meteor.settings.public.bondDenom) { - const lowerDenom = denom.toLowerCase(); - this._coin = coinList.find(coin => - coin.denom.toLowerCase() === lowerDenom || coin.displayName.toLowerCase() === lowerDenom - ); + static StakingCoin = coinList.find((coin) => coin.denom === Meteor.settings.public.bondDenom) + static MinStake = 1 / Number(Coin.StakingCoin.fraction) + + constructor(amount, denom = Meteor.settings.public.bondDenom) { + const lowerDenom = denom.toLowerCase() + this._coin = coinList.find( + (coin) => coin.denom.toLowerCase() === lowerDenom || coin.displayName.toLowerCase() === lowerDenom + ) - if (this._coin){ - if (lowerDenom === this._coin.denom.toLowerCase()) { - this._amount = Number(amount); - } else if (lowerDenom === this._coin.displayName.toLowerCase()) { - this._amount = Number(amount) * this._coin.fraction; - } - } - else { - this._coin = ""; - this._amount = Number(amount); - } - } + if (this._coin) { + if (lowerDenom === this._coin.denom.toLowerCase()) { + this._amount = Number(amount) + } else if (lowerDenom === this._coin.displayName.toLowerCase()) { + this._amount = Number(amount) * this._coin.fraction + } + } else { + this._coin = '' + this._amount = Number(amount) + } + } - get amount () { - return this._amount; - } + get amount() { + return this._amount + } - get stakingAmount () { - return (this._coin)?this._amount / this._coin.fraction:this._amount; - } + get stakingAmount() { + return this._coin ? this._amount / this._coin.fraction : this._amount + } - toString (precision) { - // default to display in mint denom if it has more than 4 decimal places - let minStake = Coin.StakingCoin.fraction/(precision?Math.pow(10, precision):10000) - if (this.amount < minStake) { - return `${numbro(this.amount).format('0,0.0000' )} ${this._coin.denom}`; - } else { - return `${precision?numbro(this.stakingAmount).format('0,0.' + '0'.repeat(precision)):autoformat(this.stakingAmount)} ${this._coin.displayName}` - } - } + toString(precision) { + // default to display in mint denom if it has more than 4 decimal places + let minStake = Coin.StakingCoin.fraction / (precision ? Math.pow(10, precision) : 10000) + // console.log('minStake', this.amount, minStake) + if (this.amount < minStake) { + return `${numbro(this.amount).format('0,0.0000')} ${this._coin.denom}` + } else { + return `${ + precision + ? numbro(this.stakingAmount).format('0,0.' + '0'.repeat(precision)) + : autoformat(this.stakingAmount) + } ${this._coin.displayName}` + } + } - mintString (formatter) { - let amount = this.amount - if (formatter) { - amount = numbro(amount).format(formatter) - } + mintString(formatter) { + let amount = this.amount + if (formatter) { + amount = numbro(amount).format(formatter) + } - let denom = (this._coin == "")?Coin.StakingCoin.displayName:this._coin.denom; - return `${amount} ${denom}`; - } + let denom = this._coin == '' ? Coin.StakingCoin.displayName : this._coin.denom + return `${amount} ${denom}` + } + + stakeString(formatter) { + let amount = this.stakingAmount + if (formatter) { + amount = numbro(amount).format(formatter) + } + return `${amount} ${Coin.StakingCoin.displayName}` + } +} - stakeString (formatter) { - let amount = this.stakingAmount - if (formatter) { - amount = numbro(amount).format(formatter) - } - return `${amount} ${Coin.StakingCoin.displayName}`; - } -} \ No newline at end of file diff --git a/client/styles.scss b/client/styles.scss index 12ca5dc56..f88159e68 100644 --- a/client/styles.scss +++ b/client/styles.scss @@ -1,12 +1,57 @@ -$forbole-red: #bd081c; +$forbole-red: #CAA93A; $primary: $forbole-red !default; @import "materia/variables"; @import "bootstrap"; @import "materia/bootswatch"; +@import url('https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400&display=swap'); + +.nav-item { cursor: pointer } +.quick-note { font-weight: 900; + width: 100%; + border-top: 1px solid; + display: block;} + +.card { + box-shadow: none; + border: 1px solid rgba(0,0,0,.5); +} + +.navbar { + box-shadow: none; +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 1); + font-weight: 900; +} + +#account > div:nth-child(2) > div > h3 > span > span > a { + color: white +} +#queryString {box-shadow: inset 0 -2px 0 #fff} +h1 { color: white } +#footer { display: none !important} + +.bgw { background: white} + +#validator-list > .card { border: none} +#validator-list > div.validator-list.row > div > div:nth-child(1) {} +#validator-list > div.bgw.mt15 > div > div > div{border: none} +.mt15 { margin-top: 15px} +.p15 { padding: 15px} + +#main > div:nth-child(3) > div:nth-child(2) > div > div > div.col-md-8 > ul.nav-pills { background: white; padding: 15px} +#account > div:nth-child(2) > div > h3 > span > i { color: #343434 !important } +#account > div:nth-child(2) > div > h3 > span > span {overflow: hidden !important} + + +::placeholder { color: white !important; opacity: 1} body { font-size: inherit !important; + font-family: "PT Serif"; + background: #caa93a; } .no-select { diff --git a/docker-compose.yml b/docker-compose.yml index 5c5cb54a2..3abea53ae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3' +version: '3.7' services: app: @@ -9,11 +9,20 @@ services: - '3080:3000' depends_on: - mongo + - kafka environment: ROOT_URL: ${APP_ROOT_URL:-http://localhost} MONGO_URL: mongodb://mongo:27017/meteor PORT: 3000 METEOR_SETTINGS: ${METEOR_SETTINGS} + KAFKA_BOOTSTRAP_SERVER: kafka:19092 + networks: + - poc_net + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" mongo: image: mongo:latest @@ -21,6 +30,55 @@ services: - --storageEngine=wiredTiger volumes: - data:/data/db + networks: + - poc_net + depends_on: + - kafka + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" + + zookeeper: + image: wurstmeister/zookeeper + ports: + - "2181:2181" + networks: + - poc_net + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" + kafka: + image: confluentinc/cp-kafka:latest + ports: + - "9092:9092" + environment: + KAFKA_CREATE_TOPICS: "Trades:1:1:delegate, ShortPuts:1:1,LM_snapshot:1:1,Pricing:1:1,Volume:1:1,Analytics:1:1" + KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:19092,OUTSIDE://${DOCKER_HOST_IP:-127.0.0.1}:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_BROKER_ID: 1 + KAFKA_offsets_topic_replication_factor: 1 + networks: + - poc_net + volumes: + - /var/run/docker.sock:/var/run/docker.sock + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" + depends_on: + - zookeeper + + volumes: data: + +networks: + poc_net: diff --git a/imports/api/blocks/server/methods.js b/imports/api/blocks/server/methods.js index 2de247e75..665fec14e 100644 --- a/imports/api/blocks/server/methods.js +++ b/imports/api/blocks/server/methods.js @@ -1,17 +1,19 @@ -import { Meteor } from 'meteor/meteor'; -import { HTTP } from 'meteor/http'; -import { Promise } from "meteor/promise"; -import { Blockscon } from '/imports/api/blocks/blocks.js'; -import { Chain } from '/imports/api/chain/chain.js'; -import { ValidatorSets } from '/imports/api/validator-sets/validator-sets.js'; -import { Validators } from '/imports/api/validators/validators.js'; -import { ValidatorRecords, Analytics, VPDistributions} from '/imports/api/records/records.js'; -import { VotingPowerHistory } from '/imports/api/voting-power/history.js'; -import { Transactions } from '../../transactions/transactions.js'; -import { Evidences } from '../../evidences/evidences.js'; -import { sha256 } from 'js-sha256'; -import { getAddress } from 'tendermint/lib/pubkey'; -import * as cheerio from 'cheerio'; +import { Meteor } from 'meteor/meteor' +import { HTTP } from 'meteor/http' +import { Promise } from 'meteor/promise' +import { Blockscon } from '/imports/api/blocks/blocks.js' +import { Chain } from '/imports/api/chain/chain.js' +import { ValidatorSets } from '/imports/api/validator-sets/validator-sets.js' +import { Validators } from '/imports/api/validators/validators.js' +import { ValidatorRecords, Analytics, VPDistributions } from '/imports/api/records/records.js' +import { VotingPowerHistory } from '/imports/api/voting-power/history.js' +import { Transactions } from '../../transactions/transactions.js' +import { Evidences } from '../../evidences/evidences.js' +import { sha256 } from 'js-sha256' +import { getAddress } from 'tendermint/lib/pubkey' +import * as cheerio from 'cheerio' + +const { kafka } = require('../../kafka/kafka') // import Block from '../../../ui/components/Block'; @@ -25,31 +27,35 @@ import * as cheerio from 'cheerio'; getRemovedValidators = (prevValidators, validators) => { // let removeValidators = []; - for (p in prevValidators){ - for (v in validators){ - if (prevValidators[p].address == validators[v].address){ - prevValidators.splice(p,1); + for (p in prevValidators) { + for (v in validators) { + if (prevValidators[p].address == validators[v].address) { + prevValidators.splice(p, 1) } } } - return prevValidators; + return prevValidators } getValidatorProfileUrl = (identity) => { - if (identity.length == 16){ - let response = HTTP.get(`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`) + if (identity.length == 16) { + let response = HTTP.get( + `https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures` + ) if (response.statusCode == 200) { let them = response.data.them - return them && them.length && them[0].pictures && them[0].pictures.primary && them[0].pictures.primary.url; + return ( + them && them.length && them[0].pictures && them[0].pictures.primary && them[0].pictures.primary.url + ) } else { console.log(JSON.stringify(response)) } - } else if (identity.indexOf("keybase.io/team/")>0){ - let teamPage = HTTP.get(identity); - if (teamPage.statusCode == 200){ - let page = cheerio.load(teamPage.content); - return page(".kb-main-card img").attr('src'); + } else if (identity.indexOf('keybase.io/team/') > 0) { + let teamPage = HTTP.get(identity) + if (teamPage.statusCode == 200) { + let page = cheerio.load(teamPage.content) + return page('.kb-main-card img').attr('src') } else { console.log(JSON.stringify(teamPage)) } @@ -60,225 +66,251 @@ getValidatorProfileUrl = (identity) => { // console.log(filtered); // [4] Meteor.methods({ - 'blocks.averageBlockTime'(address){ - let blocks = Blockscon.find({proposerAddress:address}).fetch(); + 'blocks.averageBlockTime'(address) { + let blocks = Blockscon.find({ proposerAddress: address }).fetch() let heights = blocks.map((block, i) => { - return block.height; - }); - let blocksStats = Analytics.find({height:{$in:heights}}).fetch(); + return block.height + }) + let blocksStats = Analytics.find({ height: { $in: heights } }).fetch() // console.log(blocksStats); - let totalBlockDiff = 0; - for (b in blocksStats){ - totalBlockDiff += blocksStats[b].timeDiff; + let totalBlockDiff = 0 + for (b in blocksStats) { + totalBlockDiff += blocksStats[b].timeDiff } - return totalBlockDiff/heights.length; + return totalBlockDiff / heights.length }, - 'blocks.findUpTime'(address){ - let collection = ValidatorRecords.rawCollection(); + 'blocks.findUpTime'(address) { + let collection = ValidatorRecords.rawCollection() // let aggregateQuery = Meteor.wrapAsync(collection.aggregate, collection); var pipeline = [ - {$match:{"address":address}}, + { $match: { address: address } }, // {$project:{address:1,height:1,exists:1}}, - {$sort:{"height":-1}}, - {$limit:(Meteor.settings.public.uptimeWindow-1)}, - {$unwind: "$_id"}, - {$group:{ - "_id": "$address", - "uptime": { - "$sum":{ - $cond: [{$eq: ['$exists', true]}, 1, 0] - } - } - } - }]; + { $sort: { height: -1 } }, + { $limit: Meteor.settings.public.uptimeWindow - 1 }, + { $unwind: '$_id' }, + { + $group: { + _id: '$address', + uptime: { + $sum: { + $cond: [{ $eq: ['$exists', true] }, 1, 0], + }, + }, + }, + }, + ] // let result = aggregateQuery(pipeline, { cursor: {} }); - return Promise.await(collection.aggregate(pipeline).toArray()); - // return .aggregate() + return Promise.await(collection.aggregate(pipeline).toArray()) + // return .aggregate() }, - 'blocks.getLatestHeight': function() { - this.unblock(); - let url = RPC+'/status'; - try{ - let response = HTTP.get(url); - let status = JSON.parse(response.content); - return (status.result.sync_info.latest_block_height); - } - catch (e){ - return 0; + 'blocks.getLatestHeight': function () { + this.unblock() + let url = RPC + '/status' + try { + let response = HTTP.get(url) + let status = JSON.parse(response.content) + return status.result.sync_info.latest_block_height + } catch (e) { + return 0 } }, - 'blocks.getCurrentHeight': function() { - this.unblock(); - let currHeight = Blockscon.find({},{sort:{height:-1},limit:1}).fetch(); + 'blocks.getCurrentHeight': function () { + this.unblock() + let currHeight = Blockscon.find({}, { sort: { height: -1 }, limit: 1 }).fetch() // console.log("currentHeight:"+currHeight); - let startHeight = Meteor.settings.params.startHeight; + let startHeight = Meteor.settings.params.startHeight if (currHeight && currHeight.length == 1) { - let height = currHeight[0].height; - if (height > startHeight) - return height + let height = currHeight[0].height + if (height > startHeight) return height } return startHeight }, - 'blocks.blocksUpdate': function() { - if (SYNCING) - return "Syncing..."; - else console.log("start to sync"); + 'kafka.update': async function (message) { + this.unblock() + const producer = kafka.producer() + await producer.connect() + try { + const responses = await producer.send({ + topic: 'Pricing', + messages: [ + { + key: 'analytics_event', + value: JSON.stringify(message), + }, + ], + }) + console.log('published message', { responses }) + } catch (error) { + console.error('Error publishing message', error) + } + }, + 'blocks.blocksUpdate': function () { + if (SYNCING) return 'Syncing...' + else console.log('start to sync') // Meteor.clearInterval(Meteor.timerHandle); // get the latest height - let until = Meteor.call('blocks.getLatestHeight'); + let until = Meteor.call('blocks.getLatestHeight') // console.log(until); // get the current height in db - let curr = Meteor.call('blocks.getCurrentHeight'); - console.log(curr); + let curr = Meteor.call('blocks.getCurrentHeight') + console.log(curr) // loop if there's update in db if (until > curr) { - SYNCING = true; - + SYNCING = true let validatorSet = {} // get latest validator candidate information - url = LCD+'/staking/validators'; - - try{ - response = HTTP.get(url); - JSON.parse(response.content).result.forEach((validator) => validatorSet[validator.consensus_pubkey] = validator); - } - catch(e){ - console.log(url); - console.log(e); + url = LCD + '/staking/validators' + + try { + response = HTTP.get(url) + JSON.parse(response.content).result.forEach( + (validator) => (validatorSet[validator.consensus_pubkey] = validator) + ) + } catch (e) { + console.log(url) + console.log(e) } - url = LCD+'/staking/validators?status=unbonding'; + url = LCD + '/staking/validators?status=unbonding' - try{ - response = HTTP.get(url); - JSON.parse(response.content).result.forEach((validator) => validatorSet[validator.consensus_pubkey] = validator) - } - catch(e){ - console.log(url); - console.log(e); + try { + response = HTTP.get(url) + JSON.parse(response.content).result.forEach( + (validator) => (validatorSet[validator.consensus_pubkey] = validator) + ) + } catch (e) { + console.log(url) + console.log(e) } - url = LCD+'/staking/validators?status=unbonded'; + url = LCD + '/staking/validators?status=unbonded' - try{ - response = HTTP.get(url); - JSON.parse(response.content).result.forEach((validator) => validatorSet[validator.consensus_pubkey] = validator) - } - catch(e){ - console.log(url); - console.log(e); + try { + response = HTTP.get(url) + JSON.parse(response.content).result.forEach( + (validator) => (validatorSet[validator.consensus_pubkey] = validator) + ) + } catch (e) { + console.log(url) + console.log(e) } - let totalValidators = Object.keys(validatorSet).length; - console.log("all validators: "+ totalValidators); - for (let height = curr+1 ; height <= until ; height++) { - let startBlockTime = new Date(); + let totalValidators = Object.keys(validatorSet).length + console.log('all validators: ' + totalValidators) + for (let height = curr + 1; height <= until; height++) { + let startBlockTime = new Date() // add timeout here? and outside this loop (for catched up and keep fetching)? - this.unblock(); - let url = RPC+'/block?height=' + height; - let analyticsData = {}; - - console.log(url); - try{ - const bulkValidators = Validators.rawCollection().initializeUnorderedBulkOp(); - const bulkValidatorRecords = ValidatorRecords.rawCollection().initializeUnorderedBulkOp(); - const bulkVPHistory = VotingPowerHistory.rawCollection().initializeUnorderedBulkOp(); - const bulkTransations = Transactions.rawCollection().initializeUnorderedBulkOp(); - - let startGetHeightTime = new Date(); - let response = HTTP.get(url); - if (response.statusCode == 200){ - let block = JSON.parse(response.content); - block = block.result; + this.unblock() + let url = RPC + '/block?height=' + height + let analyticsData = {} + + console.log(url) + try { + const bulkValidators = Validators.rawCollection().initializeUnorderedBulkOp() + const bulkValidatorRecords = ValidatorRecords.rawCollection().initializeUnorderedBulkOp() + const bulkVPHistory = VotingPowerHistory.rawCollection().initializeUnorderedBulkOp() + const bulkTransations = Transactions.rawCollection().initializeUnorderedBulkOp() + + let startGetHeightTime = new Date() + let response = HTTP.get(url) + if (response.statusCode == 200) { + let block = JSON.parse(response.content) + block = block.result // store height, hash, numtransaction and time in db - let blockData = {}; - blockData.height = height; - blockData.hash = block.block_id.hash; - blockData.transNum = block.block.data.txs?block.block.data.txs.length:0; - blockData.time = new Date(block.block.header.time); - blockData.lastBlockHash = block.block.header.last_block_id.hash; - blockData.proposerAddress = block.block.header.proposer_address; - blockData.validators = []; + let blockData = {} + blockData.height = height + blockData.hash = block.block_id.hash + blockData.transNum = block.block.data.txs ? block.block.data.txs.length : 0 + blockData.time = new Date(block.block.header.time) + blockData.lastBlockHash = block.block.header.last_block_id.hash + blockData.proposerAddress = block.block.header.proposer_address + blockData.validators = [] // Tendermint v0.33 start using "signatures" in last block instead of "precommits" - let precommits = block.block.last_commit.signatures; - if (precommits != null){ + let precommits = block.block.last_commit.signatures + if (precommits != null) { // console.log(precommits.length); - for (let i=0; i 0){ - for (t in block.block.data.txs){ - Meteor.call('Transactions.index', sha256(Buffer.from(block.block.data.txs[t], 'base64')), blockData.time, (err, result) => { - if (err){ - console.log(err); + if (block.block.data.txs && block.block.data.txs.length > 0) { + for (t in block.block.data.txs) { + Meteor.call( + 'Transactions.index', + sha256(Buffer.from(block.block.data.txs[t], 'base64')), + blockData.time, + (err, result) => { + if (err) { + console.log(err) + } } - }); + ) } } // save double sign evidences - if (block.block.evidence.evidence){ + if (block.block.evidence.evidence) { Evidences.insert({ height: height, - evidence: block.block.evidence.evidence - }); + evidence: block.block.evidence.evidence, + }) } - blockData.precommitsCount = blockData.validators.length; - - analyticsData.height = height; + blockData.precommitsCount = blockData.validators.length - let endGetHeightTime = new Date(); - console.log("Get height time: "+((endGetHeightTime-startGetHeightTime)/1000)+"seconds."); + analyticsData.height = height + let endGetHeightTime = new Date() + console.log('Get height time: ' + (endGetHeightTime - startGetHeightTime) / 1000 + 'seconds.') - let startGetValidatorsTime = new Date(); + let startGetValidatorsTime = new Date() // update chain status - url = RPC+`/validators?height=${height}&page=1&per_page=100`; - response = HTTP.get(url); - console.log(url); - let validators = JSON.parse(response.content); - validators.result.block_height = parseInt(validators.result.block_height); - ValidatorSets.insert(validators.result); - - blockData.validatorsCount = validators.result.validators.length; - let startBlockInsertTime = new Date(); - Blockscon.insert(blockData); - let endBlockInsertTime = new Date(); - console.log("Block insert time: "+((endBlockInsertTime-startBlockInsertTime)/1000)+"seconds."); + url = RPC + `/validators?height=${height}&page=1&per_page=100` + response = HTTP.get(url) + console.log(url) + let validators = JSON.parse(response.content) + validators.result.block_height = parseInt(validators.result.block_height) + ValidatorSets.insert(validators.result) + + blockData.validatorsCount = validators.result.validators.length + let startBlockInsertTime = new Date() + Blockscon.insert(blockData) + let endBlockInsertTime = new Date() + console.log( + 'Block insert time: ' + (endBlockInsertTime - startBlockInsertTime) / 1000 + 'seconds.' + ) // store valdiators exist records - let existingValidators = Validators.find({address:{$exists:true}}).fetch(); + let existingValidators = Validators.find({ address: { $exists: true } }).fetch() - if (height > 1){ + if (height > 1) { // record precommits and calculate uptime // only record from block 2 - for (i in validators.result.validators){ - let address = validators.result.validators[i].address; + for (i in validators.result.validators) { + let address = validators.result.validators[i].address let record = { height: height, address: address, exists: false, - voting_power: parseInt(validators.result.validators[i].voting_power)//getValidatorVotingPower(existingValidators, address) + voting_power: parseInt(validators.result.validators[i].voting_power), //getValidatorVotingPower(existingValidators, address) } - for (j in precommits){ - if (precommits[j] != null){ - if (address == precommits[j].validator_address){ - record.exists = true; - precommits.splice(j,1); - break; + for (j in precommits) { + if (precommits[j] != null) { + if (address == precommits[j].validator_address) { + record.exists = true + precommits.splice(j, 1) + break } } } @@ -286,107 +318,131 @@ Meteor.methods({ // calculate the uptime based on the records stored in previous blocks // only do this every 15 blocks ~ - if ((height % 15) == 0){ + if (height % 15 == 0) { // let startAggTime = new Date(); - let numBlocks = Meteor.call('blocks.findUpTime', address); - let uptime = 0; + let numBlocks = Meteor.call('blocks.findUpTime', address) + let uptime = 0 // let endAggTime = new Date(); // console.log("Get aggregated uptime for "+existingValidators[i].address+": "+((endAggTime-startAggTime)/1000)+"seconds."); - if ((numBlocks[0] != null) && (numBlocks[0].uptime != null)){ - uptime = numBlocks[0].uptime; + if (numBlocks[0] != null && numBlocks[0].uptime != null) { + uptime = numBlocks[0].uptime } - let base = Meteor.settings.public.uptimeWindow; - if (height < base){ - base = height; + let base = Meteor.settings.public.uptimeWindow + if (height < base) { + base = height } - if (record.exists){ - if (uptime < base){ - uptime++; + if (record.exists) { + if (uptime < base) { + uptime++ } - uptime = (uptime / base)*100; - bulkValidators.find({address:address}).upsert().updateOne({$set:{uptime:uptime, lastSeen:blockData.time}}); - } - else{ - uptime = (uptime / base)*100; - bulkValidators.find({address:address}).upsert().updateOne({$set:{uptime:uptime}}); + uptime = (uptime / base) * 100 + bulkValidators + .find({ address: address }) + .upsert() + .updateOne({ $set: { uptime: uptime, lastSeen: blockData.time } }) + } else { + uptime = (uptime / base) * 100 + bulkValidators + .find({ address: address }) + .upsert() + .updateOne({ $set: { uptime: uptime } }) } } - bulkValidatorRecords.insert(record); + bulkValidatorRecords.insert(record) // ValidatorRecords.update({height:height,address:record.address},record); } } - let chainStatus = Chain.findOne({chainId:block.block.header.chain_id}); - let lastSyncedTime = chainStatus?chainStatus.lastSyncedTime:0; - let timeDiff; - let blockTime = Meteor.settings.params.defaultBlockTime; - if (lastSyncedTime){ - let dateLatest = blockData.time; - let dateLast = new Date(lastSyncedTime); - timeDiff = Math.abs(dateLatest.getTime() - dateLast.getTime()); - blockTime = (chainStatus.blockTime * (blockData.height - 1) + timeDiff) / blockData.height; + let chainStatus = Chain.findOne({ chainId: block.block.header.chain_id }) + let lastSyncedTime = chainStatus ? chainStatus.lastSyncedTime : 0 + let timeDiff + let blockTime = Meteor.settings.params.defaultBlockTime + if (lastSyncedTime) { + let dateLatest = blockData.time + let dateLast = new Date(lastSyncedTime) + timeDiff = Math.abs(dateLatest.getTime() - dateLast.getTime()) + blockTime = (chainStatus.blockTime * (blockData.height - 1) + timeDiff) / blockData.height } - let endGetValidatorsTime = new Date(); - console.log("Get height validators time: "+((endGetValidatorsTime-startGetValidatorsTime)/1000)+"seconds."); + let endGetValidatorsTime = new Date() + console.log( + 'Get height validators time: ' + + (endGetValidatorsTime - startGetValidatorsTime) / 1000 + + 'seconds.' + ) - Chain.update({chainId:block.block.header.chain_id}, {$set:{lastSyncedTime:blockData.time, blockTime:blockTime}}); + Chain.update( + { chainId: block.block.header.chain_id }, + { $set: { lastSyncedTime: blockData.time, blockTime: blockTime } } + ) - analyticsData.averageBlockTime = blockTime; - analyticsData.timeDiff = timeDiff; + analyticsData.averageBlockTime = blockTime + analyticsData.timeDiff = timeDiff - analyticsData.time = blockData.time; + analyticsData.time = blockData.time // initialize validator data at first block // if (height == 1){ // Validators.remove({}); // } - analyticsData.voting_power = 0; + analyticsData.voting_power = 0 - let startFindValidatorsNameTime = new Date(); - if (validators.result){ + let startFindValidatorsNameTime = new Date() + if (validators.result) { // validators are all the validators in the current height - console.log("validatorSet size: "+validators.result.validators.length); - for (v in validators.result.validators){ + console.log('validatorSet size: ' + validators.result.validators.length) + for (v in validators.result.validators) { // Validators.insert(validators.result.validators[v]); - let validator = validators.result.validators[v]; - validator.voting_power = parseInt(validator.voting_power); - validator.proposer_priority = parseInt(validator.proposer_priority); + let validator = validators.result.validators[v] + validator.voting_power = parseInt(validator.voting_power) + validator.proposer_priority = parseInt(validator.proposer_priority) - let valExist = Validators.findOne({"pub_key.value":validator.pub_key.value}); - if (!valExist){ - console.log(`validator pub_key ${validator.address} ${validator.pub_key.value} not in db`); + let valExist = Validators.findOne({ 'pub_key.value': validator.pub_key.value }) + if (!valExist) { + console.log(`validator pub_key ${validator.address} ${validator.pub_key.value} not in db`) // let command = Meteor.settings.bin.gaiadebug+" pubkey "+validator.pub_key.value; // console.log(command); // let tempVal = validator; - validator.address = getAddress(validator.pub_key); - validator.accpub = Meteor.call('pubkeyToBech32', validator.pub_key, Meteor.settings.public.bech32PrefixAccPub); - validator.operator_pubkey = Meteor.call('pubkeyToBech32', validator.pub_key, Meteor.settings.public.bech32PrefixValPub); - validator.consensus_pubkey = Meteor.call('pubkeyToBech32', validator.pub_key, Meteor.settings.public.bech32PrefixConsPub); + validator.address = getAddress(validator.pub_key) + validator.accpub = Meteor.call( + 'pubkeyToBech32', + validator.pub_key, + Meteor.settings.public.bech32PrefixAccPub + ) + validator.operator_pubkey = Meteor.call( + 'pubkeyToBech32', + validator.pub_key, + Meteor.settings.public.bech32PrefixValPub + ) + validator.consensus_pubkey = Meteor.call( + 'pubkeyToBech32', + validator.pub_key, + Meteor.settings.public.bech32PrefixConsPub + ) let validatorData = validatorSet[validator.consensus_pubkey] - if (validatorData){ + if (validatorData) { if (validatorData.description.identity) - validator.profile_url = getValidatorProfileUrl(validatorData.description.identity) - validator.operator_address = validatorData.operator_address; - validator.delegator_address = Meteor.call('getDelegator', validatorData.operator_address); - validator.jailed = validatorData.jailed; - validator.status = validatorData.status; - validator.min_self_delegation = validatorData.min_self_delegation; - validator.tokens = validatorData.tokens; - validator.delegator_shares = validatorData.delegator_shares; - validator.description = validatorData.description; - validator.bond_height = validatorData.bond_height; - validator.bond_intra_tx_counter = validatorData.bond_intra_tx_counter; - validator.unbonding_height = validatorData.unbonding_height; - validator.unbonding_time = validatorData.unbonding_time; - validator.commission = validatorData.commission; - validator.self_delegation = validator.delegator_shares; + validator.profile_url = getValidatorProfileUrl(validatorData.description.identity) + validator.operator_address = validatorData.operator_address + validator.delegator_address = Meteor.call('getDelegator', validatorData.operator_address) + validator.jailed = validatorData.jailed + validator.status = validatorData.status + validator.min_self_delegation = validatorData.min_self_delegation + validator.tokens = validatorData.tokens + validator.delegator_shares = validatorData.delegator_shares + validator.description = validatorData.description + validator.bond_height = validatorData.bond_height + validator.bond_intra_tx_counter = validatorData.bond_intra_tx_counter + validator.unbonding_height = validatorData.unbonding_height + validator.unbonding_time = validatorData.unbonding_time + validator.commission = validatorData.commission + validator.self_delegation = validator.delegator_shares // validator.removed = false, // validator.removedAt = 0 // validatorSet.splice(val, 1); @@ -395,7 +451,7 @@ Meteor.methods({ } // bulkValidators.insert(validator); - bulkValidators.find({address: validator.address}).upsert().updateOne({$set:validator}); + bulkValidators.find({ address: validator.address }).upsert().updateOne({ $set: validator }) // console.log("validator first appears: "+bulkValidators.length); bulkVPHistory.insert({ address: validator.address, @@ -403,8 +459,8 @@ Meteor.methods({ voting_power: validator.voting_power, type: 'add', height: blockData.height, - block_time: blockData.time - }); + block_time: blockData.time, + }) // Meteor.call('runCode', command, function(error, result){ // validator.address = result.match(/\s[0-9A-F]{40}$/igm); @@ -418,240 +474,281 @@ Meteor.methods({ // validator.consensus_pubkey = result.match(/cosmosvalconspub.*$/igm); // validator.consensus_pubkey = validator.consensus_pubkey[0].trim(); - - // }); - } - else{ + } else { let validatorData = validatorSet[valExist.consensus_pubkey] - if (validatorData){ - if (validatorData.description && (!valExist.description || validatorData.description.identity !== valExist.description.identity)) - validator.profile_url = getValidatorProfileUrl(validatorData.description.identity) - validator.jailed = validatorData.jailed; - validator.status = validatorData.status; - validator.tokens = validatorData.tokens; - validator.delegator_shares = validatorData.delegator_shares; - validator.description = validatorData.description; - validator.bond_height = validatorData.bond_height; - validator.bond_intra_tx_counter = validatorData.bond_intra_tx_counter; - validator.unbonding_height = validatorData.unbonding_height; - validator.unbonding_time = validatorData.unbonding_time; - validator.commission = validatorData.commission; + if (validatorData) { + if ( + validatorData.description && + (!valExist.description || + validatorData.description.identity !== valExist.description.identity) + ) + validator.profile_url = getValidatorProfileUrl(validatorData.description.identity) + validator.jailed = validatorData.jailed + validator.status = validatorData.status + validator.tokens = validatorData.tokens + validator.delegator_shares = validatorData.delegator_shares + validator.description = validatorData.description + validator.bond_height = validatorData.bond_height + validator.bond_intra_tx_counter = validatorData.bond_intra_tx_counter + validator.unbonding_height = validatorData.unbonding_height + validator.unbonding_time = validatorData.unbonding_time + validator.commission = validatorData.commission // calculate self delegation percentage every 30 blocks - if (height % 30 == 1){ - try{ - let response = HTTP.get(LCD + '/staking/delegators/'+valExist.delegator_address+'/delegations/'+valExist.operator_address); - - if (response.statusCode == 200){ - let selfDelegation = JSON.parse(response.content).result; - if (selfDelegation.shares){ - validator.self_delegation = parseFloat(selfDelegation.shares)/parseFloat(validator.delegator_shares); + if (height % 30 == 1) { + try { + let response = HTTP.get( + LCD + + '/staking/delegators/' + + valExist.delegator_address + + '/delegations/' + + valExist.operator_address + ) + + if (response.statusCode == 200) { + let selfDelegation = JSON.parse(response.content).result + if (selfDelegation.shares) { + validator.self_delegation = + parseFloat(selfDelegation.shares) / parseFloat(validator.delegator_shares) } } - } - catch(e){ + } catch (e) { // console.log(e); } } - bulkValidators.find({consensus_pubkey: valExist.consensus_pubkey}).updateOne({$set:validator}); + bulkValidators + .find({ consensus_pubkey: valExist.consensus_pubkey }) + .updateOne({ $set: validator }) // console.log("validator exisits: "+bulkValidators.length); // validatorSet.splice(val, 1); - } else { + } else { console.log('no con pub key?') } - let prevVotingPower = VotingPowerHistory.findOne({address:validator.address}, {height:-1, limit:1}); - - if (prevVotingPower){ - if (prevVotingPower.voting_power != validator.voting_power){ - let changeType = (prevVotingPower.voting_power > validator.voting_power)?'down':'up'; + let prevVotingPower = VotingPowerHistory.findOne( + { address: validator.address }, + { height: -1, limit: 1 } + ) + + if (prevVotingPower) { + if (prevVotingPower.voting_power != validator.voting_power) { + let changeType = prevVotingPower.voting_power > validator.voting_power ? 'down' : 'up' let changeData = { address: validator.address, prev_voting_power: prevVotingPower.voting_power, voting_power: validator.voting_power, type: changeType, height: blockData.height, - block_time: blockData.time - }; + block_time: blockData.time, + } // console.log('voting power changed.'); // console.log(changeData); - bulkVPHistory.insert(changeData); + bulkVPHistory.insert(changeData) } } - } - // console.log(validator); - analyticsData.voting_power += validator.voting_power; + analyticsData.voting_power += validator.voting_power } // if there is validator removed - let prevValidators = ValidatorSets.findOne({block_height:height-1}); + let prevValidators = ValidatorSets.findOne({ block_height: height - 1 }) - if (prevValidators){ - let removedValidators = getRemovedValidators(prevValidators.validators, validators.result.validators); + if (prevValidators) { + let removedValidators = getRemovedValidators( + prevValidators.validators, + validators.result.validators + ) - for (r in removedValidators){ + for (r in removedValidators) { bulkVPHistory.insert({ address: removedValidators[r].address, prev_voting_power: removedValidators[r].voting_power, voting_power: 0, type: 'remove', height: blockData.height, - block_time: blockData.time - }); + block_time: blockData.time, + }) } } - } - // check if there's any validator not in db 14400 blocks(~1 day) - if (height % 14400 == 0){ + if (height % 14400 == 0) { try { console.log('Checking all validators against db...') let dbValidators = {} - Validators.find({}, {fields: {consensus_pubkey: 1, status: 1}} - ).forEach((v) => dbValidators[v.consensus_pubkey] = v.status) + Validators.find({}, { fields: { consensus_pubkey: 1, status: 1 } }).forEach( + (v) => (dbValidators[v.consensus_pubkey] = v.status) + ) Object.keys(validatorSet).forEach((conPubKey) => { - let validatorData = validatorSet[conPubKey]; + let validatorData = validatorSet[conPubKey] // Active validators should have been updated in previous steps - if (validatorData.status === 2) - return + if (validatorData.status === 2) return if (dbValidators[conPubKey] == undefined) { - console.log(`validator with consensus_pubkey ${conPubKey} not in db`); - let pubkeyType = Meteor.settings.public.secp256k1?'tendermint/PubKeySecp256k1':'tendermint/PubKeyEd25519'; + console.log(`validator with consensus_pubkey ${conPubKey} not in db`) + let pubkeyType = Meteor.settings.public.secp256k1 + ? 'tendermint/PubKeySecp256k1' + : 'tendermint/PubKeyEd25519' validatorData.pub_key = { - "type" : pubkeyType, - "value": Meteor.call('bech32ToPubkey', conPubKey) + type: pubkeyType, + value: Meteor.call('bech32ToPubkey', conPubKey), } - validatorData.address = getAddress(validatorData.pub_key); - validatorData.delegator_address = Meteor.call('getDelegator', validatorData.operator_address); - - validatorData.accpub = Meteor.call('pubkeyToBech32', validatorData.pub_key, Meteor.settings.public.bech32PrefixAccPub); - validatorData.operator_pubkey = Meteor.call('pubkeyToBech32', validatorData.pub_key, Meteor.settings.public.bech32PrefixValPub); + validatorData.address = getAddress(validatorData.pub_key) + validatorData.delegator_address = Meteor.call( + 'getDelegator', + validatorData.operator_address + ) + + validatorData.accpub = Meteor.call( + 'pubkeyToBech32', + validatorData.pub_key, + Meteor.settings.public.bech32PrefixAccPub + ) + validatorData.operator_pubkey = Meteor.call( + 'pubkeyToBech32', + validatorData.pub_key, + Meteor.settings.public.bech32PrefixValPub + ) console.log(JSON.stringify(validatorData)) - bulkValidators.find({consensus_pubkey: conPubKey}).upsert().updateOne({$set:validatorData}); + bulkValidators + .find({ consensus_pubkey: conPubKey }) + .upsert() + .updateOne({ $set: validatorData }) } else if (dbValidators[conPubKey] == 2) { - bulkValidators.find({consensus_pubkey: conPubKey}).upsert().updateOne({$set:validatorData}); + bulkValidators + .find({ consensus_pubkey: conPubKey }) + .upsert() + .updateOne({ $set: validatorData }) } }) - } catch (e){ + } catch (e) { console.log(e) } } // fetching keybase every 14400 blocks(~1 day) - if (height % 14400 == 1){ + if (height % 14400 == 1) { console.log('Fetching keybase...') Validators.find({}).forEach((validator) => { try { - let profileUrl = getValidatorProfileUrl(validator.description.identity) + let profileUrl = getValidatorProfileUrl(validator.description.identity) if (profileUrl) { - bulkValidators.find({address: validator.address} - ).upsert().updateOne({$set:{'profile_url':profileUrl}}); + bulkValidators + .find({ address: validator.address }) + .upsert() + .updateOne({ $set: { profile_url: profileUrl } }) } } catch (e) { - console.log(profileUrl); + console.log(profileUrl) console.log(e) } }) } - let endFindValidatorsNameTime = new Date(); - console.log("Get validators name time: "+((endFindValidatorsNameTime-startFindValidatorsNameTime)/1000)+"seconds."); + let endFindValidatorsNameTime = new Date() + console.log( + 'Get validators name time: ' + + (endFindValidatorsNameTime - startFindValidatorsNameTime) / 1000 + + 'seconds.' + ) // record for analytics - let startAnayticsInsertTime = new Date(); - Analytics.insert(analyticsData); - let endAnalyticsInsertTime = new Date(); - console.log("Analytics insert time: "+((endAnalyticsInsertTime-startAnayticsInsertTime)/1000)+"seconds."); - - let startVUpTime = new Date(); - if (bulkValidators.length > 0){ + let startAnayticsInsertTime = new Date() + Analytics.insert(analyticsData) + Meteor.call('kafka.update', analyticsData) + + let endAnalyticsInsertTime = new Date() + console.log( + 'Analytics insert time: ' + + (endAnalyticsInsertTime - startAnayticsInsertTime) / 1000 + + 'seconds.' + ) + + let startVUpTime = new Date() + if (bulkValidators.length > 0) { // console.log(bulkValidators.length); bulkValidators.execute((err, result) => { - if (err){ - console.log(err); + if (err) { + console.log(err) } - if (result){ + if (result) { // console.log(result); } - }); + }) } - let endVUpTime = new Date(); - console.log("Validator update time: "+((endVUpTime-startVUpTime)/1000)+"seconds."); + let endVUpTime = new Date() + console.log('Validator update time: ' + (endVUpTime - startVUpTime) / 1000 + 'seconds.') - let startVRTime = new Date(); - if (bulkValidatorRecords.length > 0){ + let startVRTime = new Date() + if (bulkValidatorRecords.length > 0) { bulkValidatorRecords.execute((err, result) => { - if (err){ - console.log(err); + if (err) { + console.log(err) } - }); + }) } - let endVRTime = new Date(); - console.log("Validator records update time: "+((endVRTime-startVRTime)/1000)+"seconds."); + let endVRTime = new Date() + console.log('Validator records update time: ' + (endVRTime - startVRTime) / 1000 + 'seconds.') - if (bulkVPHistory.length > 0){ + if (bulkVPHistory.length > 0) { bulkVPHistory.execute((err, result) => { - if (err){ - console.log(err); + if (err) { + console.log(err) } - }); + }) } - if (bulkTransations.length > 0){ + if (bulkTransations.length > 0) { bulkTransations.execute((err, result) => { - if (err){ - console.log(err); + if (err) { + console.log(err) } - }); + }) } // calculate voting power distribution every 60 blocks ~ 5mins - if (height % 60 == 1){ - console.log("===== calculate voting power distribution ====="); - let activeValidators = Validators.find({status:2,jailed:false},{sort:{voting_power:-1}}).fetch(); - let numTopTwenty = Math.ceil(activeValidators.length*0.2); - let numBottomEighty = activeValidators.length - numTopTwenty; - - let topTwentyPower = 0; - let bottomEightyPower = 0; - - let numTopThirtyFour = 0; - let numBottomSixtySix = 0; - let topThirtyFourPercent = 0; - let bottomSixtySixPercent = 0; - - - - for (v in activeValidators){ - if (v < numTopTwenty){ - topTwentyPower += activeValidators[v].voting_power; + if (height % 60 == 1) { + console.log('===== calculate voting power distribution =====') + let activeValidators = Validators.find( + { status: 2, jailed: false }, + { sort: { voting_power: -1 } } + ).fetch() + let numTopTwenty = Math.ceil(activeValidators.length * 0.2) + let numBottomEighty = activeValidators.length - numTopTwenty + + let topTwentyPower = 0 + let bottomEightyPower = 0 + + let numTopThirtyFour = 0 + let numBottomSixtySix = 0 + let topThirtyFourPercent = 0 + let bottomSixtySixPercent = 0 + + for (v in activeValidators) { + if (v < numTopTwenty) { + topTwentyPower += activeValidators[v].voting_power + } else { + bottomEightyPower += activeValidators[v].voting_power } - else{ - bottomEightyPower += activeValidators[v].voting_power; - } - - if (topThirtyFourPercent < 0.34){ - topThirtyFourPercent += activeValidators[v].voting_power / analyticsData.voting_power; - numTopThirtyFour++; + if (topThirtyFourPercent < 0.34) { + topThirtyFourPercent += activeValidators[v].voting_power / analyticsData.voting_power + numTopThirtyFour++ } } - bottomSixtySixPercent = 1 - topThirtyFourPercent; - numBottomSixtySix = activeValidators.length - numTopThirtyFour; + bottomSixtySixPercent = 1 - topThirtyFourPercent + numBottomSixtySix = activeValidators.length - numTopThirtyFour let vpDist = { height: height, @@ -666,39 +763,41 @@ Meteor.methods({ numValidators: activeValidators.length, totalVotingPower: analyticsData.voting_power, blockTime: blockData.time, - createAt: new Date() + createAt: new Date(), } - console.log(vpDist); + console.log(vpDist) - VPDistributions.insert(vpDist); + VPDistributions.insert(vpDist) } } + } catch (e) { + console.log(url) + console.log(e) + SYNCING = false + return 'Stopped' } - catch (e){ - console.log(url); - console.log(e); - SYNCING = false; - return "Stopped"; - } - let endBlockTime = new Date(); - console.log("This block used: "+((endBlockTime-startBlockTime)/1000)+"seconds."); + let endBlockTime = new Date() + console.log('This block used: ' + (endBlockTime - startBlockTime) / 1000 + 'seconds.') } - SYNCING = false; - Chain.update({chainId:Meteor.settings.public.chainId}, {$set:{lastBlocksSyncedTime:new Date(), totalValidators:totalValidators}}); + SYNCING = false + Chain.update( + { chainId: Meteor.settings.public.chainId }, + { $set: { lastBlocksSyncedTime: new Date(), totalValidators: totalValidators } } + ) } - return until; + return until }, - 'addLimit': function(limit) { - // console.log(limit+10) - return (limit+10); + addLimit: function (limit) { + // console.log(limit+10) + return limit + 10 }, - 'hasMore': function(limit) { + hasMore: function (limit) { if (limit > Meteor.call('getCurrentHeight')) { - return (false); + return false } else { - return (true); + return true } - } -}); + }, +}) diff --git a/imports/api/chain/server/methods.js b/imports/api/chain/server/methods.js index e8a9cfe73..f2f6e63af 100644 --- a/imports/api/chain/server/methods.js +++ b/imports/api/chain/server/methods.js @@ -193,10 +193,10 @@ Meteor.methods({ }, mint: genesis.app_state.mint, distr: { - communityTax: distr.community_tax, - baseProposerReward: distr.base_proposer_reward, - bonusProposerReward: distr.bonus_proposer_reward, - withdrawAddrEnabled: distr.withdraw_addr_enabled + communityTax: null, // distr.community_tax, + baseProposerReward: null, // distr.base_proposer_reward, + bonusProposerReward: null, // distr.bonus_proposer_reward, + withdrawAddrEnabled: null, // distr.withdraw_addr_enabled }, gov: { startingProposalId: 0, @@ -205,7 +205,7 @@ Meteor.methods({ tallyParams: {} }, slashing:{ - params: genesis.app_state.slashing.params + params: null // }, supply: genesis.app_state.supply, crisis: genesis.app_state.crisis diff --git a/imports/api/kafka/kafka.js b/imports/api/kafka/kafka.js new file mode 100644 index 000000000..ad269b40f --- /dev/null +++ b/imports/api/kafka/kafka.js @@ -0,0 +1,8 @@ +const { Kafka } = Meteor.npmRequire('kafkajs') + +const bootStrap = process.env.KAFKA_BOOTSTRAP_SERVER || 'localhost:9092' + +export const kafka = new Kafka({ + clientId: 'block-explorer', + brokers: [bootStrap], +}) diff --git a/imports/api/transactions/server/publications.js b/imports/api/transactions/server/publications.js index bc2cad0db..cc6b7a758 100644 --- a/imports/api/transactions/server/publications.js +++ b/imports/api/transactions/server/publications.js @@ -28,7 +28,15 @@ publishComposite('transactions.validator', function(validatorAddress, delegatorA } if (!validatorAddress && delegatorAddress){ - query = {"logs.events.attributes.value":delegatorAddress} + query = {$or:[ + {"logs.events.attributes.value":delegatorAddress}, + {"tx.value.msg.value.cosmos_receiver": delegatorAddress }, + {"tx.value.msg.value.cosmos_sender": delegatorAddress }, + {"tx.value.msg.value.delegator_address": delegatorAddress }, + {"tx.value.msg.value.from_address": delegatorAddress }, + {"tx.value.msg.value.Signer": delegatorAddress }, + {"tx.value.msg.value.delegator_address": delegatorAddress }, + ]} } return { diff --git a/imports/ui/accounts/Account.jsx b/imports/ui/accounts/Account.jsx index 3b1c91c38..e61af2c9e 100644 --- a/imports/ui/accounts/Account.jsx +++ b/imports/ui/accounts/Account.jsx @@ -282,7 +282,7 @@ export default class AccountDetails extends Component{ else if (this.state.accountExists){ return
- Account Details of {this.state.address} on Cosmos Hub | The Big Dipper + Account Details of {this.state.address} on Sifchain | The Big Dipper diff --git a/imports/ui/blocks/Block.jsx b/imports/ui/blocks/Block.jsx index c018139a0..909f85d68 100644 --- a/imports/ui/blocks/Block.jsx +++ b/imports/ui/blocks/Block.jsx @@ -20,19 +20,25 @@ export default class Block extends Component{ distributionTxs: {}, governanceTxs: {}, slashingTxs: {}, + clpTxs: {}, + pegTxs: {}, + }; } componentDidUpdate(prevProps){ if (this.props != prevProps){ if (this.props.transactionsExist){ - // console.log("have txs."); + // console.log("have txs.", this.props); this.setState({ transferTxs: this.props.transferTxs, stakingTxs: this.props.stakingTxs, distributionTxs: this.props.distributionTxs, governanceTxs: this.props.governanceTxs, - slashingTxs: this.props.slashingTxs + slashingTxs: this.props.slashingTxs, + clpTxs: this.props.clpTxs, + pegTxs: this.props.pegTxs + }) } } @@ -54,7 +60,7 @@ export default class Block extends Component{ return - Block {numbro(block.height).format("0,0")} on Cosmos Hub | The Big Dipper + Block {numbro(block.height).format("0,0")} on Sifchain | The Big Dipper

blocks.block {numbro(block.height).format("0,0")}

@@ -79,6 +85,8 @@ export default class Block extends Component{ distributionTxs={this.state.distributionTxs} governanceTxs={this.state.governanceTxs} slashingTxs={this.state.slashingTxs} + clpTxs={this.state.clpTxs} + pegTxs={this.state.pegTxs} />
} diff --git a/imports/ui/blocks/BlockContainer.js b/imports/ui/blocks/BlockContainer.js index 9e895e802..560a5c287 100644 --- a/imports/ui/blocks/BlockContainer.js +++ b/imports/ui/blocks/BlockContainer.js @@ -37,6 +37,21 @@ export default BlockContainer = withTracker((props) => { blockExist, transactionsExist, block: blockExist ? block : {}, + clpTxs: transactionsExist ? Transactions.find({ + $or: [ + {"tx.value.msg.type":"clp/Swap"}, + {"tx.value.msg.type":"clp/AddLiquidity"}, + {"tx.value.msg.type":"clp/CreatePool"}, + {"tx.value.msg.type":"clp/RemoveLiquidity"} + ] + }).fetch() : {}, + pegTxs: transactionsExist ? Transactions.find({ + $or: [ + {"tx.value.msg.type":"ethbridge/MsgLock"}, + {"tx.value.msg.type":"ethbridge/MsgBurn"}, + {"tx.value.msg.type":"ethbridge/MsgCreateEthBridgeClaim"}, + ] + }).fetch() : {}, transferTxs: transactionsExist ? Transactions.find({ $or: [ {"tx.value.msg.type":"cosmos-sdk/MsgSend"}, diff --git a/imports/ui/blocks/BlocksTable.jsx b/imports/ui/blocks/BlocksTable.jsx index 975167fb4..6528860a2 100644 --- a/imports/ui/blocks/BlocksTable.jsx +++ b/imports/ui/blocks/BlocksTable.jsx @@ -77,13 +77,14 @@ export default class BlocksTable extends Component { render(){ return
- Latest Blocks on Cosmos Hub | The Big Dipper + Latest Blocks on Sifchain | The Big Dipper

blocks.latestBlocks

+
} @@ -105,6 +106,7 @@ export default class BlocksTable extends Component { +
} } diff --git a/imports/ui/components/Activities.jsx b/imports/ui/components/Activities.jsx index 5173432ca..f0f90d341 100644 --- a/imports/ui/components/Activities.jsx +++ b/imports/ui/components/Activities.jsx @@ -1,102 +1,381 @@ -import React, {Component } from 'react'; -import { MsgType } from './MsgType.jsx'; -import { Link } from 'react-router-dom'; -import Account from '../components/Account.jsx'; -import i18n from 'meteor/universe:i18n'; -import Coin from '/both/utils/coins.js' -import JSONPretty from 'react-json-pretty'; -import _ from 'lodash'; +import React, { Component } from "react"; +import { MsgType } from "./MsgType.jsx"; +import { Link } from "react-router-dom"; +import Account from "../components/Account.jsx"; +import i18n from "meteor/universe:i18n"; +import Coin from "/both/utils/coins.js"; +import JSONPretty from "react-json-pretty"; +import _ from "lodash"; const T = i18n.createComponent(); MultiSend = (props) => { - return
-

activities.single activities.happened

-

activities.senders -

    - {props.msg.value.inputs.map((data,i) =>{ - return
  • activities.sent {data.coins.map((coin, j) =>{ - return {new Coin(coin.amount, coin.denom).toString()} - })} -
  • + return ( +
    +

    + activities.single {" "} + activities.happened +

    +

    + activities.senders +

      + {props.msg.value.inputs.map((data, i) => { + return ( +
    • + activities.sent{" "} + {data.coins.map((coin, j) => { + return ( + + {new Coin(coin.amount, coin.denom).toString()} + + ); })} -
    - activities.receivers -
      - {props.msg.value.outputs.map((data,i) =>{ - return
    • activities.received {data.coins.map((coin,j) =>{ - return {new Coin(coin.amount, coin.denom).toString()} - })}
    • + + ); + })} +
    + activities.receivers +
      + {props.msg.value.outputs.map((data, i) => { + return ( +
    • + activities.received{" "} + {data.coins.map((coin, j) => { + return ( + + {new Coin(coin.amount, coin.denom).toString()} + + ); })} -
    -

    + + ); + })} +
+

-} + ); +}; +function getCoinDecimals (symbol) { + // console.log(Meteor.settings.public.coins) + const coin = Meteor.settings.public.coins.find((item) => { + return item.denom === symbol + }) + return coin.fraction.toString().length - 1 +} export default class Activites extends Component { - constructor(props){ - super(props); + constructor(props) { + super(props); + } + + render() { + const msg = this.props.msg; + const events = []; + for (let i in this.props.events) { + events[this.props.events[i].type] = this.props.events[i].attributes; } - render(){ - // console.log(this.props); - const msg = this.props.msg; - const events = []; - for (let i in this.props.events){ - events[this.props.events[i].type] = this.props.events[i].attributes - } - - switch (msg.type){ - // bank - case "cosmos-sdk/MsgSend": - let amount = ''; - amount = msg.value.amount.map((coin) => new Coin(coin.amount, coin.denom).toString()).join(', ') - return

{(this.props.invalid)?activities.failedTo:''} {amount} activities.to common.fullStop

- case "cosmos-sdk/MsgMultiSend": - return - - // staking - case "cosmos-sdk/MsgCreateValidator": - return

{(this.props.invalid)?activities.failedTo:''} activities.operatingAt activities.withMoniker {msg.value.description.moniker}common.fullStop

- case "cosmos-sdk/MsgEditValidator": - return

{(this.props.invalid)?activities.failedTo:''}

- case "cosmos-sdk/MsgDelegate": - return

{(this.props.invalid)?activities.failedTo:''} {new Coin(msg.value.amount.amount, msg.value.amount.denom).toString(6)} activities.to common.fullStop

- case "cosmos-sdk/MsgUndelegate": - return

{(this.props.invalid)?activities.failedTo:''} {new Coin(msg.value.amount.amount, msg.value.amount.denom).toString(6)} activities.from common.fullStop

- case "cosmos-sdk/MsgBeginRedelegate": - return

{(this.props.invalid)?activities.failedTo:''} {new Coin(msg.value.amount.amount, msg.value.amount.denom).toString(6)} activities.from activities.to common.fullStop

- - // gov - case "cosmos-sdk/MsgSubmitProposal": - const proposalId = _.get(this.props, 'events[2].attributes[0].value', null) - const proposalLink = proposalId ? `/proposals/${proposalId}` : "#"; - return

activities.withTitle {msg.value.content.value.title}common.fullStop

- case "cosmos-sdk/MsgDeposit": - return

{(this.props.invalid)?activities.failedTo:''} {msg.value.amount.map((amount,i) =>new Coin(amount.amount, amount.denom).toString(6)).join(', ')} activities.to proposals.proposal {msg.value.proposal_id}common.fullStop

- case "cosmos-sdk/MsgVote": - return

{(this.props.invalid)?activities.failedTo:''} proposals.proposal {msg.value.proposal_id} activities.withA {msg.value.option}common.fullStop

- - // distribution - case "cosmos-sdk/MsgWithdrawValidatorCommission": - return

{(this.props.invalid)?activities.failedTo:''} {(!this.props.invalid)?activities.withAmount:''}common.fullStop

- case "cosmos-sdk/MsgWithdrawDelegationReward": - return

{(this.props.invalid)?activities.failedTo:''} {(!this.props.invalid)?activities.withAmount:''} activities.from common.fullStop

- case "cosmos-sdk/MsgModifyWithdrawAddress": - return

{(this.props.invalid)?activities.failedTo:''}

- - // slashing - case "cosmos-sdk/MsgUnjail": - return

{(this.props.invalid)?activities.failedTo:''}common.fullStop

- - // ibc - case "cosmos-sdk/IBCTransferMsg": - return - case "cosmos-sdk/IBCReceiveMsg": - return - - default: - return
- } + switch (msg.type) { + // bank + case "cosmos-sdk/MsgSend": + let amount = ""; + amount = msg.value.amount + .map((coin) => new Coin(coin.amount, coin.denom).toString()) + .join(", "); + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + {" "} + {amount} activities.to{" "} + + + + common.fullStop +

+ ); + case "cosmos-sdk/MsgMultiSend": + return ; + + // staking + case "cosmos-sdk/MsgCreateValidator": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + activities.operatingAt{" "} + + + {" "} + activities.withMoniker{" "} + {msg.value.description.moniker} + common.fullStop +

+ ); + case "cosmos-sdk/MsgEditValidator": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + +

+ ); + case "cosmos-sdk/MsgDelegate": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + {" "} + + {new Coin( + msg.value.amount.amount, + msg.value.amount.denom + ).toString(6)} + {" "} + activities.to{" "} + + common.fullStop +

+ ); + case "cosmos-sdk/MsgUndelegate": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + {" "} + + {new Coin( + msg.value.amount.amount, + msg.value.amount.denom + ).toString(6)} + {" "} + activities.from{" "} + + common.fullStop +

+ ); + case "cosmos-sdk/MsgBeginRedelegate": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + {" "} + + {new Coin( + msg.value.amount.amount, + msg.value.amount.denom + ).toString(6)} + {" "} + activities.from{" "} + {" "} + activities.to{" "} + + common.fullStop +

+ ); + + // gov + case "cosmos-sdk/MsgSubmitProposal": + const proposalId = _.get( + this.props, + "events[2].attributes[0].value", + null + ); + const proposalLink = proposalId ? `/proposals/${proposalId}` : "#"; + return ( +

+ {" "} + activities.withTitle{" "} + {msg.value.content.value.title} + common.fullStop +

+ ); + case "cosmos-sdk/MsgDeposit": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + {" "} + + {msg.value.amount + .map((amount, i) => + new Coin(amount.amount, amount.denom).toString(6) + ) + .join(", ")} + {" "} + activities.to{" "} + + proposals.proposal {msg.value.proposal_id} + + common.fullStop +

+ ); + case "cosmos-sdk/MsgVote": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + {" "} + + proposals.proposal {msg.value.proposal_id} + {" "} + activities.withA{" "} + {msg.value.option} + common.fullStop +

+ ); + + // distribution + case "cosmos-sdk/MsgWithdrawValidatorCommission": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + + + {" "} + {!this.props.invalid ? ( + + activities.withAmount + + ) : ( + "" + )} + common.fullStop + +

+ ); + case "cosmos-sdk/MsgWithdrawDelegationReward": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + {" "} + {!this.props.invalid ? ( + + activities.withAmount + + ) : ( + "" + )}{" "} + activities.from{" "} + + common.fullStop +

+ ); + case "cosmos-sdk/MsgModifyWithdrawAddress": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + +

+ ); + + // slashing + case "cosmos-sdk/MsgUnjail": + return ( +

+ {" "} + {this.props.invalid ? activities.failedTo : ""} + + common.fullStop +

+ ); + + // ibc + case "cosmos-sdk/IBCTransferMsg": + return ; + case "cosmos-sdk/IBCReceiveMsg": + return ; + + // clp + case "clp/Swap": + // console.log(getCoinDecimals(msg.value.SentAsset.symbol)) + return ( +
+ {/*{" "} +   + + {new Coin( + msg.value.SentAmount, + msg.value.SentAsset.symbol + ).toString(6)} + {" "} + + for + + {new Coin( + msg.value.MinReceivingAmount, + msg.value.ReceivedAsset.symbol + ).toString(6)} + {" "}*/} + + Swap +
+ ) + + case "ethbridge/MsgLock": + return ( +
+ + Unpeg +
+ ) + + case "ethbridge/MsgBurn": + return ( +
+ + Unpeg +
+ ) + + case "ethbridge/MsgCreateEthBridgeClaim": + return ( +
+ + Peg +
+ ) + + case "clp/AddLiquidity": + return ( +
+ + Add Liquidity +
+ ) + + case "clp/RemoveLiquidity": + return ( +
+ + Remove Liquidity +
+ ) + + + + default: + return ( +
+ + {msg.type} +
+ ); } + } } diff --git a/imports/ui/components/Header.jsx b/imports/ui/components/Header.jsx index 565cdf1fb..b3f2e2915 100644 --- a/imports/ui/components/Header.jsx +++ b/imports/ui/components/Header.jsx @@ -169,11 +169,15 @@ export default class Header extends Component { let signedInAddress = getUser(); return ( - navbar.siteName {this.state.version} - + + +{/* navbar.siteName  + {this.state.version} */} + +{/* {Meteor.settings.public.chainId} {this.state.networks} - + */} @@ -191,9 +195,12 @@ export default class Header extends Component { navbar.proposals - navbar.votingPower + navbar.votingPower - + + Trade + +{/* {!signedInAddress?: @@ -215,7 +222,7 @@ export default class Header extends Component { } - + */} diff --git a/imports/ui/components/MsgType.jsx b/imports/ui/components/MsgType.jsx index 999f57570..eacbebf8d 100644 --- a/imports/ui/components/MsgType.jsx +++ b/imports/ui/components/MsgType.jsx @@ -49,7 +49,9 @@ export const MsgType = (props) => { return messageTypes.IBCTransfer; case "cosmos-sdk/IBCReceiveMsg": return messageTypes.IBCReceive; - + // clp + case "clp/Swap": + return Swap; default: return {props.type}; } diff --git a/imports/ui/components/Transactions.jsx b/imports/ui/components/Transactions.jsx index 2e8ced6c8..2b4ead365 100644 --- a/imports/ui/components/Transactions.jsx +++ b/imports/ui/components/Transactions.jsx @@ -14,6 +14,8 @@ export default class ValidatorTransactions extends Component{ distributionTxs: {}, governanceTxs: {}, slashingTxs: {}, + clpTxs: {}, + pegTxs: {} }; } @@ -26,7 +28,9 @@ export default class ValidatorTransactions extends Component{ stakingTxs: this.props.stakingTxs, distributionTxs: this.props.distributionTxs, governanceTxs: this.props.governanceTxs, - slashingTxs: this.props.slashingTxs + slashingTxs: this.props.slashingTxs, + clpTxs: this.props.clpTxs, + pegTxs: this.props.pegTxs }) } } @@ -43,6 +47,8 @@ export default class ValidatorTransactions extends Component{ distributionTxs={this.state.distributionTxs} governanceTxs={this.state.governanceTxs} slashingTxs={this.state.slashingTxs} + clpTxs={this.state.clpTxs} + pegTxs={this.state.pegTxs} /> } else { diff --git a/imports/ui/components/TransactionsContainer.js b/imports/ui/components/TransactionsContainer.js index 0122e6445..c293903ea 100644 --- a/imports/ui/components/TransactionsContainer.js +++ b/imports/ui/components/TransactionsContainer.js @@ -27,6 +27,21 @@ export default TransactionsContainer = withTracker((props) => { return { loading, transactionsExist, + clpTxs: transactionsExist ? Transactions.find({ + $or: [ + {"tx.value.msg.type":"clp/Swap"}, + {"tx.value.msg.type":"clp/AddLiquidity"}, + {"tx.value.msg.type":"clp/CreatePool"}, + {"tx.value.msg.type":"clp/RemoveLiquidity"} + ] + }).fetch() : {}, + pegTxs: transactionsExist ? Transactions.find({ + $or: [ + {"tx.value.msg.type":"ethbridge/MsgLock"}, + {"tx.value.msg.type":"ethbridge/MsgBurn"}, + {"tx.value.msg.type":"ethbridge/MsgCreateEthBridgeClaim"}, + ] + }).fetch() : {}, transferTxs: transactionsExist ? Transactions.find({ $or: [ {"tx.value.msg.type":"cosmos-sdk/MsgSend"}, diff --git a/imports/ui/home/Home.jsx b/imports/ui/home/Home.jsx index 42d5ba55e..5fe38d984 100644 --- a/imports/ui/home/Home.jsx +++ b/imports/ui/home/Home.jsx @@ -16,7 +16,7 @@ export default class Home extends Component{ render() { return
- The Big Dipper | Cosmos Explorer by Forbole + Sifchain Explorer by Forbole diff --git a/imports/ui/proposals/Proposals.jsx b/imports/ui/proposals/Proposals.jsx index 1fc3ab395..d482d62f9 100644 --- a/imports/ui/proposals/Proposals.jsx +++ b/imports/ui/proposals/Proposals.jsx @@ -11,7 +11,7 @@ const T = i18n.createComponent(); const ProposalList = (props) => { return
-

proposals.listOfProposals

+ {/*

proposals.listOfProposals

*/} @@ -27,17 +27,19 @@ export default class Proposals extends Component{ render() { return
- Governance Proposals on Cosmos Hub | The Big Dipper + Governance Proposals on Sifchain | The Big Dipper

proposals.proposals

- - - - +
+ + + + +
} diff --git a/imports/ui/transactions/Transaction.jsx b/imports/ui/transactions/Transaction.jsx index 30b3dbcbd..46462c41a 100644 --- a/imports/ui/transactions/Transaction.jsx +++ b/imports/ui/transactions/Transaction.jsx @@ -1,85 +1,179 @@ -import React, { Component } from 'react'; -import { Container, Row, Col, Card, CardBody, Alert, Spinner } from 'reactstrap'; -import { TxIcon } from '../components/Icons.jsx'; -import Activities from '../components/Activities.jsx'; -import CosmosErrors from '../components/CosmosErrors.jsx'; -import { Link } from 'react-router-dom'; -import { Markdown } from 'react-showdown'; -import numbro from 'numbro'; -import { Helmet } from 'react-helmet'; -import i18n from 'meteor/universe:i18n'; -import Coin from '/both/utils/coins.js'; -import TimeStamp from '../components/TimeStamp.jsx'; +import React, { Component } from "react"; +import { + Container, + Row, + Col, + Card, + CardBody, + Alert, + Spinner, +} from "reactstrap"; +import { TxIcon } from "../components/Icons.jsx"; +import Activities from "../components/Activities.jsx"; +import CosmosErrors from "../components/CosmosErrors.jsx"; +import { Link } from "react-router-dom"; +import { Markdown } from "react-showdown"; +import numbro from "numbro"; +import { Helmet } from "react-helmet"; +import i18n from "meteor/universe:i18n"; +import Coin from "/both/utils/coins.js"; +import TimeStamp from "../components/TimeStamp.jsx"; const T = i18n.createComponent(); -export default class Transaction extends Component{ - constructor(props){ - super(props); - let showdown = require('showdown'); - showdown.setFlavor('github'); - let denom = this.props.denom; - } +export default class Transaction extends Component { + constructor(props) { + super(props); + let showdown = require("showdown"); + showdown.setFlavor("github"); + let denom = this.props.denom; + } - render(){ - - - if (this.props.loading){ - return - - - } - else{ - if (this.props.transactionExist){ - let tx = this.props.transaction; - return - - Transaction {tx.txhash} on Cosmos Hub | The Big Dipper - - -

transactions.transaction {(!tx.code)?:}

- {(tx.code)? - - - - :''} - -
common.information
- - - common.hash - {tx.txhash} - common.height - - {numbro(tx.height).format("0,0")} - {tx.block()? :null} - - transactions.fee - {(tx.tx.value.fee.amount.length > 0)?tx.tx.value.fee.amount.map((fee,i) => { - return {((fee.amount/Meteor.settings.public.stakingFraction)>=1)?(new Coin(parseFloat(fee.amount), fee.denom)).stakeString():(new Coin(parseFloat(fee.amount), fee.denom)).mintString()} - }):transactions.noFee} - transactions.gasUsedWanted - {numbro(tx.gas_used).format("0,0")} / {numbro(tx.gas_wanted).format("0,0")} - transactions.memo - - - - -
- -
transactions.activities
+ render() { + if (this.props.loading) { + return ( + + + + ); + } else { + if (this.props.transactionExist) { + let tx = this.props.transaction; + return ( + + + + Transaction {tx.txhash} on Cosmos Hub | The Big Dipper + + + +

+ transactions.transaction{" "} + {!tx.code ? : } +

+ {tx.code ? ( + + + + + + + + ) : ( + "" + )} + +
+ common.information +
+ + + + common.hash + + + {tx.txhash} + + + common.height + + + + {numbro(tx.height).format("0,0")} + + {tx.block() ? ( + + {" "} + + + ) : null} + + + transactions.fee + + + {tx.tx.value.fee.amount.length > 0 ? ( + tx.tx.value.fee.amount.map((fee, i) => { + return ( + + {" "} + {fee.amount / + Meteor.settings.public.stakingFraction >= + 1 + ? new Coin( + parseFloat(fee.amount), + fee.denom + ).stakeString() + : new Coin( + parseFloat(fee.amount), + fee.denom + ).mintString()}{" "} + + ); + }) + ) : ( + + transactions.noFee + + )} + + + transactions.gasUsedWanted + + + {numbro(tx.gas_used).format("0,0")} /{" "} + {numbro(tx.gas_wanted).format("0,0")} + + + transactions.memo + + + + + + +
+ +
+ transactions.activities +
+
+ {tx.tx.value.msg && tx.tx.value.msg.length > 0 + ? tx.tx.value.msg.map((msg, i) => { + return ( + + - {(tx.tx.value.msg && tx.tx.value.msg.length >0)?tx.tx.value.msg.map((msg,i) => { - return - }):''} -
- } - else{ - return
transactions.noTxFound
- } - } + ); + }) + : ""} +
+ ); + } else { + return ( + +
+ transactions.noTxFound +
+
+ ); + } } -} \ No newline at end of file + } +} diff --git a/imports/ui/transactions/TransactionRow.jsx b/imports/ui/transactions/TransactionRow.jsx index 8e92dd4d0..a14d36a25 100644 --- a/imports/ui/transactions/TransactionRow.jsx +++ b/imports/ui/transactions/TransactionRow.jsx @@ -1,46 +1,147 @@ +import React, { Component } from "react"; +import { Link } from "react-router-dom"; +import { + Row, + Col, + Card, + Alert, + UncontrolledPopover, + PopoverHeader, + PopoverBody, +} from "reactstrap"; +import { TxIcon } from "../components/Icons.jsx"; +import Activities from "../components/Activities.jsx"; +import CosmosErrors from "../components/CosmosErrors.jsx"; +import TimeAgo from "../components/TimeAgo.jsx"; +import numbro from "numbro"; +import Coin from "/both/utils/coins.js"; +import SentryBoundary from "../components/SentryBoundary.jsx"; +import { Markdown } from "react-showdown"; -import React, { Component } from 'react'; -import { Link } from 'react-router-dom'; -import { Row, Col, Card, Alert, UncontrolledPopover, PopoverHeader, PopoverBody } from 'reactstrap'; -import { TxIcon } from '../components/Icons.jsx'; -import Activities from '../components/Activities.jsx'; -import CosmosErrors from '../components/CosmosErrors.jsx'; -import TimeAgo from '../components/TimeAgo.jsx'; -import numbro from 'numbro'; -import Coin from '/both/utils/coins.js' -import SentryBoundary from '../components/SentryBoundary.jsx'; -import { Markdown } from 'react-showdown'; - -let showdown = require('showdown'); -showdown.setFlavor('github'); +let showdown = require("showdown"); +showdown.setFlavor("github"); export const TransactionRow = (props) => { - let tx = props.tx; - - return - {(tx.tx.value.msg && tx.tx.value.msg.length >0)?tx.tx.value.msg.map((msg,i) => { - return - }):''} - {tx.txhash} - schedule {tx.block()?:''}{(tx.tx.value.memo && tx.tx.value.memo != "")? - message - - - - :""} - {(!props.blockList)? {numbro(tx.height).format("0,0")}:''} - {(!tx.code)?:} - monetization_on {(tx.tx.value.fee.amount.length > 0)?tx.tx.value.fee.amount.map((fee,i) => { - return {(new Coin(parseFloat(fee.amount), (fee)?fee.denom:null)).stakeString()} - }):No fee} - {(tx.code)? + let tx = props.tx; + + return ( + + + + {tx.tx.value.msg && tx.tx.value.msg.length > 0 + ? tx.tx.value.msg.map((msg, i) => { + return ( + + + + ); + }) + : ""} + + + {" "} + {tx.txhash} + + + schedule{" "} + {tx.block() ? : ""} + {tx.tx.value.memo && tx.tx.value.memo != "" ? ( + + + message + + + + + + + + ) : ( + "" + )} + + {!props.blockList ? ( + + {" "} + + {numbro(tx.height).format("0,0")} + + + ) : ( + "" + )} + + {!tx.code ? : } + + + monetization_on{" "} + {tx.tx.value.fee.amount.length > 0 ? ( + tx.tx.value.fee.amount.map((fee, i) => { + return ( + + {new Coin( + parseFloat(fee.amount), + fee ? fee.denom : null + ).stakeString()} + + ); + }) + ) : ( + No fee + )} + + {tx.code ? ( + - + - :''} - -} + + ) : ( + "" + )} + + + ); +}; diff --git a/imports/ui/transactions/TransactionTabs.jsx b/imports/ui/transactions/TransactionTabs.jsx index 712b667cc..2ef5d2230 100644 --- a/imports/ui/transactions/TransactionTabs.jsx +++ b/imports/ui/transactions/TransactionTabs.jsx @@ -16,7 +16,9 @@ export default class TransactionTabs extends Component{ stakingTxs: {}, distributionTxs: {}, governanceTxs: {}, - slashingTxs: {} + slashingTxs: {}, + clpTxs: {}, + pegTxs: {} } } @@ -35,7 +37,9 @@ export default class TransactionTabs extends Component{ stakingTxs: this.props.stakingTxs, distributionTxs: this.props.distributionTxs, governanceTxs: this.props.governanceTxs, - slashingTxs: this.props.slashingTxs + slashingTxs: this.props.slashingTxs, + clpTxs: this.props.clpTxs, + pegTxs: this.props.pegTxs }) } } @@ -85,6 +89,22 @@ export default class TransactionTabs extends Component{ transactions.slashing ({numbro(this.state.slashingTxs.length).format("0,0")}) + + { this.toggle('tx-clp'); }} + > + transactions.clp ({numbro(this.state.clpTxs.length).format("0,0")}) + + + + { this.toggle('tx-peg'); }} + > + transactions.peg ({numbro(this.state.pegTxs.length).format("0,0")}) + + @@ -157,6 +177,34 @@ export default class TransactionTabs extends Component{
+ + + + {(this.state.clpTxs.length > 0)?this.state.clpTxs.map((tx, i) => { + return + }):''} + + + + + + + {(this.state.pegTxs.length > 0)?this.state.pegTxs.map((tx, i) => { + return + }):''} + + + diff --git a/imports/ui/transactions/TransactionsList.jsx b/imports/ui/transactions/TransactionsList.jsx index fae5057f8..cada0a77f 100644 --- a/imports/ui/transactions/TransactionsList.jsx +++ b/imports/ui/transactions/TransactionsList.jsx @@ -85,31 +85,33 @@ export default class Transactions extends Component{ render(){ return
- Latest Transactions on Cosmos Hub | The Big Dipper - + Latest Transactions on Sifchain | The Big Dipper +

transactions.transactions

- - } - open={this.state.sidebarOpen} - onSetOpen={this.onSetSidebarOpen} - styles={{ sidebar: { - background: "white", - position: "fixed", - width: '85%', - zIndex: 4 - },overlay: { - zIndex: 3 - } }} - > - } /> - - - +
+ + } + open={this.state.sidebarOpen} + onSetOpen={this.onSetSidebarOpen} + styles={{ sidebar: { + background: "white", + position: "fixed", + width: '85%', + zIndex: 4 + },overlay: { + zIndex: 3 + } }} + > + } /> + + + +
} } \ No newline at end of file diff --git a/imports/ui/validators/Validator.jsx b/imports/ui/validators/Validator.jsx index a0ecf77bd..246cdb443 100644 --- a/imports/ui/validators/Validator.jsx +++ b/imports/ui/validators/Validator.jsx @@ -190,7 +190,7 @@ export default class Validator extends Component{ return - { moniker } - Cosmos Validator | The Big Dipper + { moniker } - Sifchain Validator | The Big Dipper @@ -264,7 +264,7 @@ export default class Validator extends Component{ -