theQRL/block-explorer

View on GitHub
imports/ui/components/tx/tx.js

Summary

Maintainability
F
5 days
Test Coverage
/* eslint no-console: 0 */
import JSONFormatter from 'json-formatter-js'
import './tx.html'
import CryptoJS from 'crypto-js'
import sha256 from 'sha256'
import $ from 'jquery'
import _ from 'underscore'
import qrlNft from '@theqrl/nft-providers'
import 'fomantic-ui-css/semantic.js'
import 'fomantic-ui-css/semantic.css'
import { numberToString, SHOR_PER_QUANTA, formatBytes } from '../../../startup/both/index.js'

const renderTxBlock = () => {
  const txId = FlowRouter.getParam('txId')
  if (txId) {
    Meteor.call('txhash', txId, (err, res) => {
      if (err) {
        Session.set('txhash', { error: err, id: txId, found: false })
        return false
      }
      if (res.found) {
        Session.set('txhash', res)
      } else {
        Session.set('txhash', { found: false, id: txId })
        return false
      }
      return true
    })
    Meteor.call('QRLvalue', (err, res) => {
      if (err) {
        Session.set('qrl', 'Error getting value from API')
      } else {
        Session.set('qrl', res)
      }
    })
    Meteor.call('status', (err, res) => {
      if (err) {
        Session.set('status', { error: err })
      } else {
        Session.set('status', res)
      }
    })
  }
}

/* eslint-disable */
function toHexString(byteArray) {
  return Array.from(byteArray, function(byte) {
    return ('0' + (byte & 0xFF).toString(16)).slice(-2);
  }).join('')
}

function hexToBytes(hex) {
  for (var bytes = [], c = 0; c < hex.length; c += 2)
    bytes.push(parseInt(hex.substr(c, 2), 16));
  return bytes;
}
function bytesToHex(bytes) {
  for (var hex = [], i = 0; i < bytes.length; i++) {
    hex.push((bytes[i] >>> 4).toString(16));
    hex.push((bytes[i] & 0xf).toString(16));
  }
  return hex.join("");
}
function byte2bits(a) {
  var tmp = "";
  for (var i = 128; i >= 1; i /= 2) tmp += a & i ? "1" : "0";
  return tmp;
}
function split2Bits(a, n) {
  var buff = "";
  var b = [];
  for (var i = 0; i < a.length; i++) {
    buff += byte2bits(a[i]);
    while (buff.length >= n) {
      b.push(buff.substr(0, n));
      buff = buff.substr(n);
    }
  }
  return [b, buff];
}

function toByteArray(hexString) {
  var result = [];
  while (hexString.length >= 2) {
    result.push(parseInt(hexString.substring(0, 2), 16));
    hexString = hexString.substring(2, hexString.length);
  }
  return result;
}
/* eslint-enable */

Template.tx.helpers({
  hasMessage() {
    try {
      if (this.tx.transfer.message_data.length > 0) {
        return true
      }
      return false
    } catch (e) {
      return false
    }
  },
  tfMessage() {
    return this.tx.transfer.message_data
  },
  bech32() {
    return Session.equals('addressFormat', 'bech32')
  },
  tx() {
    try {
      if (Session.get('txhash').error) {
        return { found: false, parameter: FlowRouter.getParam('txId') }
      }
      const txhash = Session.get('txhash').transaction
      return txhash
    } catch (e) {
      return false
    }
  },
  txSize() {
    try {
      const bytes = Session.get('txhash').transaction.size
      return formatBytes(bytes)
    } catch (e) {
      return false
    }
  },
  id() {
    return FlowRouter.getParam('txId')
  },
  ots_key() {
    try {
      if (Session.get('txhash').found) {
        const txhash = Session.get('txhash').transaction
        const otsKey = parseInt(txhash.tx.signature.substring(0, 8), 16)
        return otsKey
      }
      return ''
    } catch (e) {
      return false
    }
  },
  notFound() {
    try {
      if (Session.get('txhash').found === false) {
        return true
      }
      return false
    } catch (e) {
      return false
    }
  },
  header() {
    try {
      return Session.get('txhash').transaction.header
    } catch (e) {
      return false
    }
  },
  qrl() {
    const txhash = Session.get('txhash')
    try {
      const value = txhash.transaction.tx.amount
      const x = Session.get('qrl')
      const y = Math.round((x * value) * 100) / 100
      if (y !== 0) { return y }
    } catch (e) {
      return '...'
    }
    return '...'
  },
  amount() {
    if (this.tx.coinbase) {
      return numberToString(this.tx.coinbase.amount / SHOR_PER_QUANTA)
    }
    if (this.tx.transactionType === 'transfer') {
      return `${numberToString(this.tx.transfer.totalTransferred)} Quanta`
    }
    if (this.tx.transactionType === 'transfer_token') {
      return `${numberToString(this.tx.transfer_token.totalTransferred)} ${this.tx.transfer_token.symbol}`
    }

    return ''
  },
  isConfirmed() {
    // const x = Session.get('status')
    try {
      if (this.header.block_number !== null) {
        return true
      }
      return false
    } catch (e) {
      return false
    }
  },
  confirmations() {
    const x = Session.get('status')
    try {
      let confirmations = x.node_info.block_height - this.header.block_number
      confirmations += 1
      return confirmations
    } catch (e) {
      return 0
    }
  },
  ts() {
    if (this.header) {
      const x = moment.unix(this.header.timestamp_seconds)
      return moment(x).format('HH:mm D MMM YYYY')
    }
    const x = moment.unix(this.timestamp_seconds)
    return moment(x).format('HH:mm D MMM YYYY')
  },
  color() {
    if (this.tx.transactionType === 'coinbase') {
      return 'teal'
    }
    if (this.tx.transactionType === 'stake') {
      return 'red'
    }
    if (this.tx.transactionType === 'transfer') {
      return 'yellow'
    }
    return ''
  },
  isToken() {
    if (this.explorer.type === 'CREATE TOKEN') {
      return true
    }
    return false
  },
  isTransfer() {
    if (this.explorer.type === 'TRANSFER') {
      return true
    }
    return false
  },
  isTokenTransfer() {
    if (this.explorer.type === 'TRANSFER TOKEN' || this.explorer.type === 'TRANSFER NFT') {
      return true
    }
    return false
  },
  isNotCoinbase() {
    if (this.explorer.type !== 'COINBASE') {
      return true
    }
    return false
  },
  isKeybase() {
    if (this.explorer.type !== 'KEYBASE') {
      return false
    }
    return true
  },
  isMessage() {
    if (this.explorer.type === 'MESSAGE') {
      return true
    }
    return false
  },
  isMultiSigCreate() {
    if (this.explorer.type === 'MULTISIG_CREATE') {
      return true
    }
    return false
  },
  isMultiSigSpend() {
    if (this.explorer.type === 'MULTISIG_SPEND') {
      return true
    }
    return false
  },
  isMultiSigVote() {
    if (this.explorer.type === 'MULTISIG_VOTE') {
      return true
    }
    return false
  },
  isLattice() {
    if (this.explorer.type === 'LATTICE PK') {
      return true
    }
    return false
  },
  isTransferNFT() {
    if (this.explorer.type === 'TRANSFER NFT') {
      return true
    }
    return false
  },
  isCreateNFT() {
    if (this.explorer.type === 'CREATE NFT') {
      return true
    }
    return false
  },
  isDocumentNotarisation() {
    if (this.explorer.type === 'DOCUMENT_NOTARISATION') {
      return true
    }
    return false
  },
  isNotMessage() {
    if ((this.explorer.type !== 'MESSAGE') && (this.explorer.type !== 'DOCUMENT_NOTARISATION') && (this.tx.transactionType !== 'message')) {
      return true
    }
    return false
  },
  isNotMultiSig() {
    if ((this.explorer.type !== 'MULTISIG_CREATE') && (this.explorer.type !== 'MULTISIG_SPEND') && (this.explorer.type !== 'MULTISIG_VOTE')) {
      return true
    }
    return false
  },
  isNotLattice() {
    if ((this.explorer.type !== 'LATTICE PK')) {
      return true
    }
    return false
  },
  providerID() {
    return `0x${this.explorer.nft.id}`
  },
  knownProvider() {
    const { id } = this.explorer.nft
    const from = this.explorer.from_hex
    let known = false
    _.each(qrlNft.providers, (provider) => {
      if (provider.id === `0x${id}`) {
        _.each(provider.addresses, (address) => {
          if (address === from) {
            known = true
          }
        })
      }
    })
    return known
  },
  providerURL() {
    const { id } = this.explorer.nft
    let url = ''
    _.each(qrlNft.providers, (provider) => {
      if (provider.id === `0x${id}`) {
        url = provider.url
      }
    })
    return url
  },
  providerName() {
    const { id } = this.explorer.nft
    let name = ''
    _.each(qrlNft.providers, (provider) => {
      if (provider.id === `0x${id}`) {
        name = provider.name
      }
    })
    return name
  },
  nftHash() {
    return this.explorer.nft.hash
  },
  documentNotarisationVerificationMessage() {
    const message = Session.get('documentNotarisationVerificationMessage')
    return message
  },
  documentNotarisationError() {
    const message = Session.get('documentNotarisationError')
    return message
  },
  multiSigSignatories(ms) {
    const output = []
    if (ms) {
      _.each(ms.signatories, (item, index) => {
        output.push({ address_hex: `Q${item}`, weight: ms.weights[index] })
      })
      return output
    }
    return false
  },
  mso(ms) {
    const output = []
    if (ms) {
      _.each(ms.addrs_to, (item, index) => {
        output.push({ address: `Q${item}`, amount: (ms.amounts[index] / SHOR_PER_QUANTA) })
      })
      return output
    }
    return false
  },
  multiSigAddress() {
    const desc = hexToBytes('110000')
    const txhash = hexToBytes(Session.get('txhash').transaction.tx.transaction_hash)
    const arr = desc.concat(txhash)
    const prevHash = hexToBytes(sha256(arr))
    const newArr = desc.concat(prevHash)
    const newHash = hexToBytes(sha256(newArr).slice(56, 64))
    const q1 = desc.concat(prevHash)
    const q = q1.concat(newHash)
    return `Q${toHexString(q)}`
  },
  multiSigSpendAddress() {
    try {
      return `Q${this.tx.multi_sig_spend.multi_sig_address}`
    } catch (error) {
      return null
    }
  },
  bf(b) {
    return Buffer.from(b).toString('hex')
  },
})

Template.tx.events({
  'click .close': () => {
    $('.message').hide()
  },
  'click .jsonclick': () => {
    if (!($('.json').html())) {
      const myJSON = Session.get('txhash').transaction
      const formatter = new JSONFormatter(myJSON)
      $('.json').html(formatter.render())
    }
    $('.jsonbox').toggle()
  },
  'submit #notariseVerificationForm': (event) => {
    event.preventDefault()
    event.stopPropagation()

    $('#documentVerified').hide()
    $('#documentVerifcationFailed').hide()

    const notaryDocuments = $('#notaryDocument').prop('files')
    const notaryDocument = notaryDocuments[0]

    // Get notary details from txn
    const txhash = Session.get('txhash').transaction
    const txnHashFunction = txhash.explorer.hash_function
    const txnFileHash = txhash.explorer.hash
    let txnNotary
    if (Session.equals('addressFormat', 'bech32')) {
      txnNotary = txhash.explorer.from_b32
    } else {
      txnNotary = txhash.explorer.from_hex
    }

    let txnNotaryDate
    if (txhash.header) {
      const x = moment.unix(txhash.header.timestamp_seconds)
      txnNotaryDate = moment(x).format('HH:mm D MMM YYYY')
    } else {
      const x = moment.unix(txhash.timestamp_seconds)
      txnNotaryDate = moment(x).format('HH:mm D MMM YYYY')
    }

    // Verify user supplied file against txn hash
    const reader = new FileReader()
    reader.onloadend = function onloadend() {
      try {
        let fileHash

        // Convert FileReader ArrayBuffer to WordArray first
        const resultWordArray = CryptoJS.lib.WordArray.create(reader.result)

        if (txnHashFunction === 'SHA1') {
          fileHash = CryptoJS.SHA1(resultWordArray).toString(CryptoJS.enc.Hex)
        } else if (txnHashFunction === 'SHA256') {
          fileHash = CryptoJS.SHA256(resultWordArray).toString(CryptoJS.enc.Hex)
        } else if (txnHashFunction === 'MD5') {
          fileHash = CryptoJS.MD5(resultWordArray).toString(CryptoJS.enc.Hex)
        }

        // Verify the txnFileHash is the same as provided file
        if (txnFileHash === fileHash) {
          // Valid document notarisation
          const successMessage = String.raw`The file '${notaryDocument.name}' has been verifiably
          notarised by '${txnNotary}' on ${txnNotaryDate} using the hash function '${txnHashFunction}'
          resulting in the hash '${txnFileHash}'.`

          Session.set('documentNotarisationVerificationMessage', successMessage)
          $('#documentVerified').show()
        } else {
          Session.set('documentNotarisationError', 'The file provided does not match the notary hash in this transaction.')
          $('#documentVerifcationFailed').show()
        }
      } catch (err) {
        console.log(err)
        // Invalid file format
        Session.set('documentNotarisationError', 'Unable to open Document - Are you sure you selected a document to verify?')
        $('#documentVerifcationFailed').show()
      }
    }

    // Verify user selected a document to notarise
    if (notaryDocument === undefined) {
      Session.set('documentNotarisationError', 'Unable to open Document - Are you sure you selected a document to verify?')
      $('#documentVerifcationFailed').show()
    } else {
      console.log('reading file ', notaryDocument)
      reader.readAsArrayBuffer(notaryDocument)
    }
  },
})

Template.tx.onRendered(() => {
  $('.value').popup()
  Tracker.autorun(() => {
    FlowRouter.watchPathChange()
    Session.set('txhash', {})
    Session.set('qrl', 0)
    Session.set('status', {})
    renderTxBlock()
  })
})