/* Import core modules. */ const debug = require('debug')('cashfusion:test') const net = require('net') const numeral = require('numeral') const path = require('path') const protobuf = require('protobufjs') const GENESIS = '6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000' const HOST = 'cashfusion.electroncash.dk' const PORT = 8787 const VERSION = 'alpha13' /* Initialize magic (bytes). */ const magic = Buffer.from('765be8b4e4396dcf', 'hex') /* Initialize protobuf classes. */ // const pbEnums = ['Phase', 'Reason'] /* Initialize protobuf types. */ const pbTypes = [ 'InputComponent', 'OutputComponent', 'BlankComponent', 'Component', 'InitialCommitment', 'Proof', 'ClientHello', 'ServerHello', 'JoinPools', 'TierStatusUpdate', 'FusionBegin', 'StartRound', 'PlayerCommit', 'BlindSigResponses', 'AllCommitments', 'CovertComponent', 'ShareCovertComponents', 'CovertTransactionSignature', 'FusionResult', 'MyProofsList', 'TheirProofsList', 'Blames', 'RestartRound', 'Error', 'Ping', 'OK', 'ClientMessage', 'ServerMessage', 'CovertMessage', 'CovertResponse' ] /* Initialize tiers output. */ let tiersOutput = null /** * Parse Players */ const _parsePlayers = (_tier) => { if (_tier.timeRemaining) { /* Set time remaining. */ const remaining = (_tier.timeRemaining / 60).toFixed(1) + ' mins remaining' return `${_tier.players} of ${_tier.minPlayers} [ ${remaining} ]` } else { return `${_tier.players} of ${_tier.minPlayers}` } } /* Initialize protobuf. */ const PB = { root: protobuf.loadSync(path.join(__dirname, 'libs', 'fusion', 'fusion.proto')) } /* Loop through ALL protobuf types. */ for (let oneTypeName of pbTypes) { PB[oneTypeName] = PB.root.lookupType(oneTypeName) } // debug('Protocol buffer', PB) /** * Greeting */ const greet = function () { console.info('\n\tGreeting CashFusion server...\n') // eslint-disable-line no-console /* Set message. */ const message = PB.ClientMessage.create({ clienthello: PB.ClientHello.create({ version: Buffer.from(VERSION), genesisHash: Buffer.from(GENESIS, 'hex') }) }) /* Verify message. */ const errMsg = PB.ClientMessage.verify(message) if (errMsg) { // debug('Protobuf verification failed!', errMsg) throw new Error(errMsg) } /* Encode message. */ const buffer = PB.ClientMessage.encode(message).finish() // debug('Encoded message:', buffer.toString('hex')) // const decoded = PB.ClientMessage.decode(buffer) // debug('Decoded:', decoded) /* Return packed message. */ return packMessage(buffer) } /** * Register */ const register = function () { console.info('\n\tRegistering tiers...\n') // eslint-disable-line no-console // All tiers // tiersOutput = [ // 10000, 12000, 15000, 18000, 22000, 27000, 33000, 39000, 47000, 56000, 68000, 82000, // ] tiersOutput = [ 10000, 12000, 15000, 18000, 22000, 27000, 33000, 39000, 47000, 56000, 68000, 82000, 100000, 120000, 150000, 180000, 220000, 270000, 330000, 390000, 470000, 560000, 680000, 820000, 1000000, 1200000, 1500000, 1800000, 2200000, 2700000, 3300000, 3900000, 4700000, 5600000, 6800000, 8200000, 10000000, 12000000, 15000000, 18000000, 22000000, 27000000, 33000000, 39000000, 47000000, 56000000, 68000000, 82000000 ] debug('Tiers output:', tiersOutput.length, tiersOutput) /* Set message. */ const message = PB.ClientMessage.create({ joinpools: PB.JoinPools.create({ tiers: tiersOutput, tags: [PB.JoinPools.PoolTag.create({ // FIXME: Figure out what this id/tag means?? id: Buffer.from('c58594f6f26f0315f459f06d4106084ae53058e3', 'hex'), limit: 1 })] }) }) /* Verify message. */ const errMsg = PB.ClientMessage.verify(message) if (errMsg) { // debug('Protobuf verification failed!', errMsg) throw new Error(errMsg) } /* Encode message. */ const buffer = PB.ClientMessage.encode(message).finish() // debug('Encoded message:', buffer.toString('hex')) // const decoded = PB.ClientMessage.decode(buffer) // debug('Decoded:', decoded) /* Return packed message. */ return packMessage(buffer) } /** * Pack Message * * Encode (pack) a message into a prototype buffer (protobuf) object. */ function packMessage (_packets) { // debug('Packing message:', _packets) /* Initialize message length. */ const messageLength = Buffer.allocUnsafe(4) /* Calculate message length. */ const lengthDec = _packets.length /* Set message length. */ // NOTE: 4-bytes in big-endian (bytes order). messageLength.writeUIntBE(lengthDec, 0, 4) // debug('Message length:', messageLength) /* Build packet. */ const packet = Buffer.concat([ magic, messageLength, _packets ]) // debug('Packet message:', packet) return packet // _packets = _.isArray(_packets) ? _packets : [_packets] // const messageBuffer = _packets /* Initialize length suffix. */ // const lengthSuffix = Buffer.alloc(4) /* Set length suffix. */ // lengthSuffix.writeUIntBE(messageBuffer.length, 0, 4) /* Set message components. */ // const messageComponents = [magic, lengthSuffix, messageBuffer] /* Set full message. */ // const fullMessage = Buffer.concat(messageComponents) /* Return message object. */ // return { // unpacked: _packets, // packed: fullMessage, // components: messageToBuffers(fullMessage) // } } /* Initialize client. */ const client = new net.Socket() /** * Connect * * Opens a new connection to the host. */ client.connect(PORT, HOST, function () { debug('Connected! (host/port):', HOST, PORT) /* Initialize greeting. */ const greeting = greet() debug('Greeting', greeting.toString('hex')) /* Send greeting. */ client.write(greeting) }) /** * Handle Data * * Processes all data received from the host. */ client.on('data', function (_data) { // debug('RECEIVED DATA:', _data) debug('Incoming data (from server):', Buffer.from(_data).toString('hex')) const message = _data.slice(magic.length + 4) // debug('Incoming data (message):', message.toString('hex')) const decoded = PB.ServerMessage.decode(message) debug('Incoming data (decoded):', decoded) /* Handle message. */ if (decoded.serverhello) { /* Validate data. */ if (!PB.ServerHello.verify(decoded.serverhello)) { debug('ServerHello (tiers):', decoded.serverhello.tiers) debug('ServerHello (numComponents):', decoded.serverhello.numComponents) debug('ServerHello (componentFeerate):', decoded.serverhello.componentFeerate) debug('ServerHello (minExcessFee):', decoded.serverhello.minExcessFee) debug('ServerHello (maxExcessFee):', decoded.serverhello.maxExcessFee) debug('ServerHello (donationAddress):', decoded.serverhello.donationAddress) /* Initialize registration. */ const registration = register() debug('Registration', registration.toString('hex')) /* Send greeting. */ client.write(registration) } else { throw new Error(PB.ServerHello.verify(decoded.serverhello)) } } /* Handle data. */ if (decoded.tierstatusupdate) { // console.log('DEBUG:', decoded.tierstatusupdate) const statuses = decoded.tierstatusupdate.statuses // debug('Tier statuses::', statuses) const keys = Object.keys(statuses) // debug('Tier keys:', decodeURIComponent(keys)) /* Display each key status. */ keys.forEach((key, i) => { // debug(i, decodeURIComponent(key), statuses[key]) debug(`Tier ${numeral(tiersOutput[i]).format('0,0')} - ${_parsePlayers(statuses[key])}`) }) client.destroy() // if (!PB.TierStatusUpdate.verify(decoded.tierstatusupdate)) { // debug('TierStatusUpdate (statuses):', decoded.tierstatusupdate.statuses) // } else if (decoded.tierstatusupdate) { // throw new Error(PB.TierStatusUpdate.verify(decoded.tierstatusupdate)) // } } // Close the client socket completely // client.destroy() }) /** * Handle Close * * Handles connection termination. */ client.on('close', function () { debug('Connection closed.') })