CommChannel.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /* Import core modules. */
  2. // const _ = require('lodash')
  3. const debug = require('debug')('fusion:comm')
  4. const EventEmitter = require('events').EventEmitter
  5. const moment = require('moment')
  6. const WebSocket = require('ws')
  7. /* Import local modules. */
  8. const serverMessages = require('./serverMessages.js')
  9. /**
  10. * Communications Channel (Class)
  11. */
  12. class CommChannel extends EventEmitter {
  13. constructor (connectionOptions, shuffleRoundInstance) {
  14. super()
  15. /* Set instance properties. */
  16. for (let oneOption in connectionOptions) {
  17. this[oneOption] = connectionOptions[oneOption]
  18. }
  19. /* Set server URI. */
  20. this.serverUri = connectionOptions.serverUri
  21. /* Validate server URI. */
  22. if (!this.serverUri) {
  23. /* Set connection error. */
  24. const connectionError = new Error('BAD_SERVER_URI')
  25. /* Emit connection error. */
  26. this.emit('connectionError', connectionError)
  27. }
  28. /* Initialize Websocket client. */
  29. this._wsClient = undefined
  30. /* Initialize server messages. */
  31. this.msg = serverMessages
  32. /* Initialize shuffle round. */
  33. this.round = shuffleRoundInstance
  34. /* Initialize outbox. */
  35. // NOTE: Our internal records for sent and received server messages.
  36. // Used for debugging bad rounds.
  37. this.outbox = {
  38. sent: {}
  39. }
  40. /* Initialize inbox. */
  41. this.inbox = {}
  42. return this
  43. }
  44. /**
  45. * Connect
  46. *
  47. * Establish websockets connection with shuffle server.
  48. */
  49. async connect () {
  50. debug('Server Connection', this.serverUri)
  51. // TODO: This and all communication functionality will be moved to
  52. // a separate class. The `Round` should only touch messages
  53. // after they have been parsed, validated, and classified.
  54. this._wsClient = new WebSocket(this.serverUri, {
  55. origin: 'http://localhost'
  56. })
  57. /* Handle incoming message from CashShuffle server. */
  58. this._wsClient.on('message', (_buffer) => {
  59. debug('Websocket (buffer)', _buffer)
  60. })
  61. /* Handle a NEW Websockets connection with the CashShuffle server. */
  62. this._wsClient.on('open', () => {
  63. this._wsConnected = true
  64. debug('We are now connected to the CashFusion server', this.serverUri)
  65. this.emit('connected', this._wsClient)
  66. })
  67. /* Handle closing the Websockets connection. */
  68. this._wsClient.on('close', (details) => {
  69. if (!this.round.roundComplete) {
  70. // FIXME: Should we attempt to automatically reconnect
  71. // and restart the process??
  72. debug('Socket connection closed:', details)
  73. this.emit('disconnected', details)
  74. }
  75. })
  76. /* Handle Websockets errors. */
  77. this._wsClient.on('error', (someError) => {
  78. debug('THERE WAS A SOCKET ERROR!', someError)
  79. this.emit('connectionError', someError)
  80. })
  81. }
  82. /**
  83. * Send Message
  84. */
  85. sendMessage () {
  86. debug('Now sending message:', arguments)
  87. /* Set message type. */
  88. // const messageType = arguments[0]
  89. /* Set message parameters. */
  90. // const messageParams = [].slice.call(arguments, 1) // FIXME: Why this trailing space??
  91. // debug('Now sending message:', arguments,
  92. // 'type:', messageType,
  93. // 'params:', messageParams)
  94. /* Initialize packet message. */
  95. let packedMessage = null
  96. /* Validate message type. */
  97. // if (messageType && typeof this.msg[messageType] === 'function') {
  98. // try {
  99. // packedMessage = this.msg[messageType].apply(this, messageParams)
  100. // } catch (nope) {
  101. // debug('Couldnt create', messageType, 'message using params',
  102. // messageParams, '\n', nope)
  103. // // TODO: Throw exception?
  104. // }
  105. // } else {
  106. // // TODO: Should we throw an exception now?
  107. // }
  108. /* Add the message to our outbox, in case we need it later. */
  109. // const outboxEntry = {
  110. // messageType: messageType,
  111. // time: new Date().getTime(),
  112. // protobuffMessage: {
  113. // // packed: packedMessage.packed.toString('base64'),
  114. // unpacked: packedMessage.unpacked.toJSON(),
  115. // components: packedMessage.components
  116. // }
  117. // }
  118. /* Validate outbox. */
  119. // if (!this.outbox[messageType]) {
  120. // /* Initialize object. */
  121. // const obj = {}
  122. //
  123. // /* Initialize object's message type. */
  124. // obj[messageType] = []
  125. //
  126. // /* Update outbox. */
  127. // _.extend(this.outbox, obj)
  128. // }
  129. /* Set outbox sent message flag. */
  130. // this.outbox.sent[messageType] = true
  131. /* Add entry to outbox. */
  132. // this.outbox[messageType].push(outboxEntry)
  133. /* Send packed message. */
  134. this._wsClient.send(packedMessage.packed)
  135. }
  136. /**
  137. * Write Debug File
  138. */
  139. writeDebugFile () {
  140. /* Loop through inbox. */
  141. // for (let oneKey in this.inbox) {
  142. // if (_.isArray(this.inbox[oneKey])) {
  143. // this.inbox[oneKey] = _.sortBy(this.inbox[oneKey], ['time'], ['desc'])
  144. // }
  145. // }
  146. /* Loop through outbox. */
  147. // for (let oneKey in this.outbox) {
  148. // if (_.isArray(this.outbox[oneKey])) {
  149. // this.outbox[oneKey] = _.sortBy(this.outbox[oneKey], ['time'], ['desc'])
  150. // }
  151. // }
  152. /* Build data package (for disk). */
  153. const writeThisToDisk = {
  154. phase: this.round.phase,
  155. coin: this.round.coin,
  156. ephemeralKeypair: this.round.ephemeralKeypair,
  157. encryptionKeypair: this.round.encryptionKeypair,
  158. shuffled: this.round.shuffled,
  159. change: this.round.change,
  160. players: this.round.players,
  161. equivHashPlaintext: this.round.equivHashPlaintext,
  162. equivHash: this.round.equivHash,
  163. shuffleTx: {
  164. signatures: this.round.shuffleTx.signatures,
  165. hex: this.round.shuffleTx.hex,
  166. serialized: this.round.shuffleTx.tx ? this.round.shuffleTx.tx.toObject() : {},
  167. results: this.round.shuffleTx.results
  168. },
  169. inbox: this.inbox,
  170. outbox: this.outbox
  171. }
  172. /* Set data. */
  173. const data = JSON.stringify(writeThisToDisk, null, 2)
  174. /* Write data to disk. */
  175. require('fs').writeFileSync(`_failedFusion-${moment().unix()}.js`, 'module.exports = ' + data)
  176. /* Quit application. */
  177. process.exit(0)
  178. }
  179. }
  180. /* Export module. */
  181. module.exports = CommChannel