123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936 |
- /* Import core modules. */
- const _ = require('lodash')
- const bch = require('bitcore-lib-cash')
- const debug = require('debug')('shuffle:server-messages')
- const path = require('path')
- const protobuf = require('protobufjs')
- /* Import local modules. */
- const BetterMessage = require('./BetterMessage.js')
- /* Initialize magic (bytes). */
- const magic = Buffer.from('42bcc32669467873', 'hex')
- /* Initialize protobuf classes. */
- const pbEnums = ['Phase', 'Reason']
- /* Initialize protobuf types. */
- const pbTypes = [
- 'Signed', 'Packet', 'Coins', 'Signatures', 'Message', 'Address',
- 'Registration', 'VerificationKey', 'EncryptionKey', 'DecryptionKey',
- 'Hash', 'Signature', 'Transaction', 'Blame', 'Invalid', 'Inputs',
- 'Packets'
- ]
- /* Initialize protobuf. */
- // NOTE: Problem loading from local file, so copied to web root. See issue:
- // https://github.com/protobufjs/protobuf.js/issues/1023#issuecomment-629165262
- // TODO: Translate `.proto` to `.json` and use JSON load.
- let PB = {}
- let protoFile = null
- if (typeof window !== 'undefined') {
- protoFile = 'shuffle.proto'
- } else {
- protoFile = path.join(__dirname, 'shuffle.proto')
- }
- protobuf.load(protoFile, (err, root) => {
- if (err) return console.error(err) // eslint-disable-line no-console
- /* Set root. */
- PB.root = root
- /* Loop through ALL protobuf types. */
- for (let oneTypeName of pbTypes) {
- PB[oneTypeName] = PB.root.lookupType(oneTypeName)
- }
- /* Loop through ALL protobuf classes. */
- for (let oneClassName of pbEnums) {
- PB[oneClassName] = PB.root.lookupEnum(oneClassName)
- }
- })
- /**
- * Message to Buffers
- */
- function messageToBuffers (someBase64Message) {
- /* Initialize message buffer. */
- const messageBuffer = Buffer.from(someBase64Message, 'base64')
- /* Validate message buffer. */
- if (messageBuffer.length < 12) {
- throw new Error('bad_length')
- } else {
- /* Set message magic (bytes). */
- const messageMagic = messageBuffer.slice(0, 8)
- /* Validate message magic (bytes). */
- if (messageMagic.toString('hex') !== magic.toString('hex')) {
- throw new Error('message_magic')
- }
- /* Initialize message length. */
- let messageLength = messageBuffer.slice(8, 12)
- /* Set message length. */
- messageLength = messageLength.readUInt32BE(0)
- /* Set message payload. */
- // const messagePayload = messageBuffer.slice(12, ) // FIXME: Why do we have a trailing space??
- const messagePayload = messageBuffer.slice(12) // FIXME: Why do we have a trailing space??
- /* Validate message payload. */
- if (messagePayload.length !== messageLength) {
- console.error( // eslint-disable-line no-console
- 'Incorrect payload size', messagePayload.length,
- '!==', messageLength)
- throw new Error('message_payload')
- } else {
- /* Return message buffers. */
- return {
- magic: messageBuffer.slice(0, 8).toString('base64'),
- length: messageBuffer.slice(8, 12).toString('base64'),
- payload: messageBuffer.slice(12).toString('base64'),
- buffer: messageBuffer.toString('base64')
- }
- }
- }
- }
- const handleMessageBuffer = (messageBuffer) => {
- /* Validate message buffer. */
- if (messageBuffer.length < 12) {
- throw new Error('bad_length')
- }
- /* Set message magic (bytes). */
- const messageMagic = messageBuffer.slice(0, 8)
- /* Validate message magic (bytes). */
- if (messageMagic.toString('hex') !== magic.toString('hex')) {
- throw new Error('message_magic')
- }
- /* Initialize message length. */
- let messageLength = messageBuffer.slice(8, 12)
- /* Set message length. */
- messageLength = messageLength.readUInt32BE(0)
- /* Set message payload. */
- const messagePayload = messageBuffer.slice(12)
- /* Build server message. */
- const serverMessage = {
- packets: [],
- full: undefined,
- pruned: undefined,
- components: messageToBuffers(messageBuffer)
- }
- /* Validate message payload. */
- if (messagePayload.length !== messageLength) {
- debug(
- 'Incorrect payload size:', messagePayload.length,
- '!==', messageLength
- )
- throw new Error('message_payload')
- } else {
- /* Set decoded packets. */
- const decodedPackets = PB.Packets.decode(messagePayload)
- /* Loop through ALL decoded packets. */
- for (let onePacket of decodedPackets.packet) {
- serverMessage.packets.push(onePacket)
- }
- /* Set (full) server message. */
- serverMessage.full = decodedPackets.toJSON()
- /* Set (pruned) server message. */
- serverMessage.pruned = {
- message: _.get(serverMessage.full, 'packet[0].packet'),
- signature: _.get(serverMessage.full, 'packet[0].signature.signature')
- }
- }
- /* Validate (pruned) server message. */
- if (!serverMessage.pruned.message) {
- throw new Error('message_parsing')
- }
- /* Initialize message types. */
- // TODO: Pick more intuitive and more consistent message names.
- let messageTypes = [
- { name: 'playerCount', required: ['number'] },
- { name: 'serverGreeting', required: ['number', 'session'] },
- { name: 'announcementPhase', required: ['number', 'phase'] },
- { name: 'incomingVerificationKeys', required: ['session', 'fromKey.key', 'message.inputs'] },
- { name: 'incomingChangeAddress', required: ['session', 'number', 'fromKey.key', 'message.address.address', 'message.key.key', 'phase'] },
- /**
- * This message name will be changed before the `serverMessage`
- * event is emitted by the `CommChannel` class.
- *
- * We set the final message name there because that's where we
- * have access to round state data and the purpose of the message
- * (which should inform the name) changes based on the state of the
- * round.
- *
- * Yep, this is yet another hack to deal with the fact that there
- * is no support for a unique `messageName` field on the protocol
- * messages.
- */
- { name: '_unicast', required: ['number', 'session', 'fromKey.key', 'toKey.key', 'message.str'] },
- { name: 'incomingEquivCheck', required: ['number', 'session', 'phase', 'fromKey.key', 'message.hash.hash'] },
- { name: 'incomingInputAndSig', required: ['number', 'session', 'phase', 'fromKey.key', 'message.signatures'] },
- { name: 'finalTransactionOutputs', required: ['session', 'number', 'phase', 'fromKey.key', 'message.str'] },
- { name: 'blame', required: ['number', 'session', 'fromKey.key', 'message.blame', 'phase'] }
- ]
- // Order the message types so that the most
- // specific descriptions are seen first by
- // the function that attempts to find a match.
- messageTypes = _.orderBy(
- messageTypes,
- function (ot) {
- return ot.required.length
- },
- ['desc']
- )
- /* Set matching message type. */
- const matchingMessageType = _.reduce(messageTypes, function (winner, oneObject) {
- /* Set required parameter values. */
- const requiredParamValues = _.at(serverMessage.pruned.message, oneObject.required)
- /* Validate required parameter values. */
- // NOTE: If none of the required parameters are missing,
- // consider this object a match.
- const isMatch = oneObject.required.length === _.compact(requiredParamValues).length
- /* Validate match. */
- // NOTE: If our match has more matching params than
- // our previous match, use this one instead
- if (isMatch && winner.required.length < requiredParamValues.length) {
- return oneObject
- } else {
- return winner
- }
- }, { required: [] })
- /* Update server message. */
- Object.assign(serverMessage.pruned, {
- messageType: matchingMessageType.name || 'UNKNOWN'
- })
- /* Return server message. */
- return serverMessage
- }
- /**
- * Decode and Classify
- *
- * TODO: Clean this up so it better handles multi-packet messages.
- */
- function decodeAndClassify (messageBuffer) {
- return new Promise(function (resolve, reject) {
- /* Validate and parse message data. */
- /* eslint-disable-next-line no-undef */
- if (messageBuffer && messageBuffer.data && messageBuffer.data instanceof Blob) {
- /* Initialize file reader. */
- const reader = new FileReader() // eslint-disable-line no-undef
- /* Handle onload. */
- reader.onload = () => {
- /* Handle message buffer. */
- resolve(handleMessageBuffer(Buffer.from(reader.result)))
- }
- /* Handle onerror. */
- reader.onerror = reject
- /* Read data as buffer. */
- reader.readAsArrayBuffer(messageBuffer.data)
- } else {
- /* Handle message buffer. */
- resolve(handleMessageBuffer(messageBuffer))
- }
- })
- }
- /**
- * Registration
- */
- function registration (protocolVersion, amount, key) {
- /* Validate key. */
- if (_.isObject(key) && typeof key.toString) {
- key = key.toString()
- }
- /* Set message. */
- const message = PB.Signed.create({
- packet: PB.Packet.create({
- fromKey: PB.VerificationKey.create({ key }),
- registration: PB.Registration.create({
- amount: amount,
- version: protocolVersion,
- // type: 'DEFAULT' // WHY AREN'T WE USING THIS??
- type: 'DEFAULT'
- })
- })
- })
- /* Return packed message. */
- return packMessage(message)
- }
- /**
- * Broadcast Transaction Input
- *
- * This function reveals the coin our client wishes to shuffle as well as our
- * verificationKey. Although we revealed our verificationKey in our server
- * registration message, that message isn't relayed to our peers. This is the
- * first message where our peers see the vk.
- */
- function broadcastTransactionInput (
- inputsObject,
- session,
- playerNumber,
- verificationPublicKey
- ) {
- /* Validate verification key. */
- if (_.isObject(verificationPublicKey) && typeof verificationPublicKey.toString) {
- verificationPublicKey = verificationPublicKey.toString()
- }
- /* Initialize message. */
- let message
- /* Set message. */
- message = PB.Signed.create({
- packet: PB.Packet.create({
- fromKey: PB.VerificationKey.create({
- key: verificationPublicKey
- }),
- message: PB.Message.create({
- inputs: {}
- }),
- session: session,
- number: playerNumber
- })
- })
- /* Loop through ALL input objects. */
- for (let key in inputsObject) {
- message.packet.message.inputs[key] = PB.Coins.create({
- coins: inputsObject[key]
- })
- }
- /* Return packed message. */
- return packMessage(message)
- }
- /*
- {
- "packet": [
- {
- "packet": {
- "session": "aGFIYURRd0JBWFN5MkJJNnBhdzZ6OQ==",
- "number": 2,
- "from_key": {
- "key": "03b9bf1605aa851945bd72e575d42f7ca874d9d7099f686c70893f927512010853"
- },
- "to_key": {
- "key": "03aa863d01fd4c44043b73fccd820101f8bdc3bdf59a2472f1f1ecf6822ce4ad7b"
- },
- "phase": 2,
- "message": {
- "str": "QklFMQNiC79dSfQjlIRKY/nHYE9KblxLkT6na8kelVoL8OIHW9/QqooDxTgtNm5Xhfh3R6kMWslw+uF6sYdhYZ53ce2sJBaaRWMLO8twqjfJGBPt/97XAAIVA57KNfzJOzdx6a8e/oUZ99xKPp6MRDBPGmME"
- }
- },
- "signature": {
- "signature": "H/zYhGuMsptl9hL76Wn9ylNUmHKAzO+ZQbEHAkTPIp1aP0DiAjtvsyFmS1ZK03nTS5d5/4Vb5GoKnty7UijANds="
- }
- },
- {
- "packet": {
- "session": "aGFIYURRd0JBWFN5MkJJNnBhdzZ6OQ==",
- "number": 2,
- "from_key": {
- "key": "03b9bf1605aa851945bd72e575d42f7ca874d9d7099f686c70893f927512010853"
- },
- "to_key": {
- "key": "03aa863d01fd4c44043b73fccd820101f8bdc3bdf59a2472f1f1ecf6822ce4ad7b"
- },
- "phase": 2,
- "message": {
- "str": "QklFMQJtTrG5IQiUX1C0ZR67t5cQbN4v72uSzrCOuy1QEtOI41wQ2CGGK7lgtxyS9g8tzd9YHe+4DyMaSyrCIx/Ft/U27P6dU5xR6lVhf3ekV3mIW8/vH2lpb2AWY3Djl0egotBFrIylX+2W0nC9MVaU98xZ"
- }
- },
- "signature": {
- "signature": "H8zKYj3OsPF/EUstjST/pzI8AqwcsK8OySDIxm9WABUYHWODcoBAzKsnEh1I7gLfABpAqnYkM0WK0SiyBeVUuq8="
- }
- }
- ]
- }
- */
- // this.comms.sendMessage('forwardEncryptedOutputs', [
- // this.session, me.playerNumber, encryptedOutputAddresses.success,
- // this.phase.toUpperCase(), nextPlayer.verificationKey, this.ephemeralKeypair.publicKey,
- // this.ephemeralKeypair.privateKey
- // ])
- /**
- * Forward Encrypted Outputs
- */
- function forwardEncryptedOutputs (
- session,
- fromPlayerNumber,
- arrayOfOutputs,
- phase,
- toVerificationKey,
- myVerificationPubKey,
- myVerificationPrivKey
- ) {
- /* Validate verification public key. */
- if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
- myVerificationPubKey = myVerificationPubKey.toString()
- }
- /* Validate (to) verification key. */
- if (_.isObject(toVerificationKey) && typeof toVerificationKey.toString) {
- toVerificationKey = toVerificationKey.toString()
- }
- // TODO: Make these server messages consistent with
- // respect to param validation and type checking.
- /* Initialize signed messages. */
- const signedMessages = []
- /* Loop through ALL outputs. */
- for (let oneEncryptedAddress of arrayOfOutputs) {
- /* Set message. */
- const message = PB.Signed.create({
- packet: PB.Packet.create({
- session: session,
- number: fromPlayerNumber,
- fromKey: PB.VerificationKey.create({
- key: myVerificationPubKey
- }),
- toKey: PB.VerificationKey.create({
- key: toVerificationKey
- }),
- phase: PB.Phase.values[phase.toUpperCase()],
- message: PB.Message.create({
- str: oneEncryptedAddress
- })
- })
- })
- /* Set message. */
- const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
- .toString('base64')
- /* Set signature. */
- const signature = new BetterMessage(msg, 'base64')
- .sign(bch.PrivateKey(myVerificationPrivKey))
- /* Set message signature. */
- message.signature = PB.Signature.create({ signature })
- /* Add message to signed messages. */
- signedMessages.push(message)
- }
- /* Return packed signed messages. */
- return packMessage(signedMessages)
- }
- /*
- {
- "packet": [
- {
- "packet": {
- "session": "OGlTZkdsSTZUQVgwNkU4Yk4wMkowTw==",
- "number": 1,
- "from_key": {
- "key": "0202135e4f7217957db961f26e3856a239e89023f6cd6088d6303775c3a61572bf"
- },
- "phase": 6,
- "message": {
- "signatures": [
- {
- "utxo": "3a019c3a44d5269edf8a6ca2588ead452b03f8fcdda2b622906c98e4d1d5778f:0",
- "signature": {
- "signature": "MzA0NDAyMjAwYjZiYzIyMDMzZTQwYzA5ZjJhNTdiZjZhOTc1YTEwMTk0OGU5ODAzMGY2OWRjMWZhYzlmZWY5ZTk0YTcxZTMzMDIyMDY4MDFlYzMzYWZiOTU5ZDZlZGZiMDQyMDM2NzcwYjA4MjI1NzczM2ExMjBhMDdmMTgzM2RlZTdhZTUzNDhlNzM0MQ=="
- }
- }
- ]
- }
- },
- "signature": {
- "signature": "H0fwrr75/6GcPzPB6etyWyZD4mLlDGacVIs/j+VTldLCdE8yAfactL8jdXrJRwE7RqYhCFJ6vIHFpTu6Xa+SW2Y="
- }
- }
- ]
- }
- */
- /**
- * Broadcast Signature and UTXO
- */
- function broadcastSignatureAndUtxo (
- session,
- fromPlayerNumber,
- coinUtxoData,
- signatureString,
- phase,
- myVerificationPubKey,
- myVerificationPrivKey
- ) {
- /* Validate verification public key. */
- if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
- myVerificationPubKey = myVerificationPubKey.toString()
- }
- /* Set message. */
- const message = PB.Signed.create({
- packet: PB.Packet.create({
- session: session,
- number: fromPlayerNumber,
- fromKey: PB.VerificationKey.create({
- key: myVerificationPubKey
- }),
- phase: PB.Phase.values[phase.toUpperCase()],
- message: PB.Message.create({
- signatures: []
- })
- })
- })
- /* Add packet message signatures. */
- message.packet.message.signatures.push(PB.Signature.create({
- utxo: coinUtxoData,
- signature: PB.Signature.create({
- signature: signatureString
- })
- }))
- /* Set message. */
- const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
- .toString('base64')
- /* Set signature. */
- const signature = new BetterMessage(msg, 'base64')
- .sign(bch.PrivateKey(myVerificationPrivKey))
- /* Set message signature. */
- message.signature = PB.Signature.create({ signature })
- /* Return packed message. */
- return packMessage(message)
- }
- /*
- {
- "packet": [
- {
- "packet": {
- "session": "c25hMmNwNm8xcXJIejlNMDhJdGFNZA==",
- "number": 2,
- "from_key": {
- "key": "03343954c832a7b870eb8758c1c280b954bfed8b8fb65a33d52f848aabdbf31dce"
- },
- "phase": 4,
- "message": {
- "hash": {
- "hash": "1WDdy4zstoNgSnuSjagCxL5P8aqDbBerN92WSs1c2hY="
- }
- }
- },
- "signature": {
- "signature": "ICz+h2V5JBhHTronVb2FB4rCLHrIDi3gmsCn/+VphuojdofZBx5LCjefnnoGhwyYVQ40pSPi1u+JPXduPurrOBo="
- }
- }
- ]
- }
- */
- /**
- * Broadcast Equivocation Check
- */
- function broadcastEquivCheck (
- session,
- fromPlayerNumber,
- equivCheckHash,
- phase,
- myVerificationPubKey,
- myVerificationPrivKey
- ) {
- /* Validate verification public key. */
- if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
- myVerificationPubKey = myVerificationPubKey.toString()
- }
- /* Set message. */
- const message = PB.Signed.create({
- packet: PB.Packet.create({
- session: session,
- number: fromPlayerNumber,
- fromKey: PB.VerificationKey.create({
- key: myVerificationPubKey
- }),
- phase: PB.Phase.values[phase.toUpperCase()],
- message: PB.Message.create({
- hash: PB.Hash.create({
- hash: equivCheckHash
- })
- })
- })
- })
- /* Set message. */
- const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
- .toString('base64')
- /* Set signature. */
- const signature = new BetterMessage(msg, 'base64')
- .sign(bch.PrivateKey(myVerificationPrivKey))
- /* Set message signature. */
- message.signature = PB.Signature.create({ signature })
- /* Return packed message. */
- return packMessage(message)
- }
- /**
- * Broadcast Final Output Addresses
- */
- function broadcastFinalOutputAddresses (
- session,
- fromPlayerNumber,
- arrayOfOutputs,
- phase,
- myVerificationPubKey,
- myVerificationPrivKey
- ) {
- /* Validate verification public key. */
- if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
- myVerificationPubKey = myVerificationPubKey.toString()
- }
- /* Initialize signed messages. */
- const signedMessages = []
- /* Loop through ALL outputs. */
- for (let onePlaintextAddress of arrayOfOutputs) {
- /* Set message. */
- const message = PB.Signed.create({
- packet: PB.Packet.create({
- session: session,
- number: fromPlayerNumber,
- fromKey: PB.VerificationKey.create({
- key: myVerificationPubKey
- }),
- phase: PB.Phase.values[phase.toUpperCase()],
- message: PB.Message.create({
- str: onePlaintextAddress
- })
- })
- })
- /* Set message. */
- const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
- .toString('base64')
- /* Set signature. */
- const signature = new BetterMessage(msg, 'base64')
- .sign(bch.PrivateKey(myVerificationPrivKey))
- /* Set message signature. */
- message.signature = PB.Signature.create({ signature })
- /* Add message to signed messages. */
- signedMessages.push(message)
- }
- /* Return packed signed messages. */
- return packMessage(signedMessages)
- }
- /**
- * Change Address Announcement
- */
- function changeAddressAnnounce (
- session,
- playerNumber,
- changeAddress,
- encryptionPublicKey,
- phase,
- verificationPublicKey,
- verificationPrivateKey
- ) {
- /* Validate encryption public key. */
- if (_.isObject(encryptionPublicKey) && typeof encryptionPublicKey.toString) {
- encryptionPublicKey = encryptionPublicKey.toString()
- }
- /* Validate verification public key. */
- if (_.isObject(verificationPublicKey) && typeof verificationPublicKey.toString) {
- verificationPublicKey = verificationPublicKey.toString()
- }
- /* Initialize message. */
- let message
- /* Set message. */
- message = PB.Signed.create({
- packet: PB.Packet.create({
- session: session,
- number: playerNumber,
- fromKey: PB.VerificationKey.create({
- key: verificationPublicKey
- }),
- phase: PB.Phase.values[phase.toUpperCase()],
- message: PB.Message.create({
- address: PB.Address.create({
- address: changeAddress
- }),
- key: PB.VerificationKey.create({
- key: encryptionPublicKey
- })
- })
- })
- })
- /* Set message. */
- const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
- .toString('base64')
- /* Set signature. */
- const signature = new BetterMessage(msg, 'base64')
- .sign(bch.PrivateKey(verificationPrivateKey))
- /* Set message signature. */
- message.signature = PB.Signature.create({ signature })
- /* Return packed message. */
- return packMessage(message)
- }
- /**
- * Pack Message
- *
- * Encode (pack) a message into a prototype buffer (protobuf) object.
- */
- function packMessage (oneOrMorePackets) {
- oneOrMorePackets = _.isArray(oneOrMorePackets) ? oneOrMorePackets : [oneOrMorePackets]
- /* Set packets. */
- const packets = PB.Packets.create({ packet: oneOrMorePackets })
- /* Set message buffer. */
- const messageBuffer = Buffer.from(PB.Packets.encode(packets).finish())
- /* 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)
- }
- }
- /**
- * Check Packet Signature
- */
- function checkPacketSignature (oneSignedPacket) {
- /* Initialize verification key. */
- const verificationKey = oneSignedPacket.packet.fromKey.key
- /* Set signature. */
- const signature = Buffer.from(oneSignedPacket.signature.signature).toString('base64')
- /* Set packet. */
- const packet = PB.Packet.encode(oneSignedPacket.packet)
- /* Set public key. */
- const pubkey = new bch.PublicKey(verificationKey)
- /* Set address. */
- const address = pubkey.toAddress().toString()
- /* Set message. */
- const message = Buffer.from(packet.finish()).toString('base64')
- // debug('checkPacketSignature',
- // oneSignedPacket,
- // verificationKey,
- // signature,
- // packet,
- // pubkey,
- // address,
- // message
- // )
- /* Initialize result. */
- let result = false
- try {
- /* Set result. */
- result = new BetterMessage(message, 'base64').verify(address, signature)
- } catch (someError) {
- console.error('Error checking signature:', someError) // eslint-disable-line no-console
- }
- /* Return result. */
- return result
- }
- /*
- {
- reason: < enum string citing reason for blame accusation >,
- accused: < verification key in hex format of player who 's being accused >,
- invalid: < an array of protobuf packets that provide evidence of fault >,
- hash: < hash provided by accused which differs from our own >,
- keypair: {
- key: < private key >,
- public: < public key >
- }
- }
- Possible Ban Reasons:
- INSUFFICIENTFUNDS = 0
- DOUBLESPEND = 1
- EQUIVOCATIONFAILURE = 2
- SHUFFLEFAILURE = 3
- SHUFFLEANDEQUIVOCATIONFAILURE = 4
- INVALIDSIGNATURE = 5
- MISSINGOUTPUT = 6
- LIAR = 7
- INVALIDFORMAT = 8
- */
- /**
- * Blame Message
- */
- function blameMessage (
- options,
- mySessionid,
- myPlayerNumber,
- myVerificationPublicKey,
- myVerificationPrivateKey
- ) {
- /* Validate verification public key. */
- if (_.isObject(myVerificationPublicKey) && typeof myVerificationPublicKey.toString) {
- myVerificationPublicKey = myVerificationPublicKey.toString()
- }
- /* Validate verification private key. */
- if (_.isObject(myVerificationPrivateKey) && typeof myVerificationPrivateKey.toString) {
- myVerificationPrivateKey = myVerificationPrivateKey.toString()
- }
- /* Set blame message. */
- const blameMessage = _.reduce(_.keys(options), function (msg, oneOptionName) {
- switch (oneOptionName) {
- case 'reason':
- msg.packet.message.blame.reason = PB.Reason.values[
- options.reason ? options.reason.toUpperCase() : 'NONE'
- ]
- break
- case 'accused':
- msg.packet.message.blame.accused = PB.VerificationKey.create({
- key: options.accused
- })
- break
- case 'invalid':
- // msg.packet.message.blame.invalue = PB.Invalid.create({ invalid: invalidPackets })
- msg.packet.message.blame.invalue = PB.Invalid.create({
- invalid: options.invalidPackets
- })
- break
- case 'hash':
- msg.packet.message.hash = PB.Hash.create({
- hash: options.hash
- })
- break
- case 'keypair':
- msg.packet.message.blame.key = PB.DecryptionKey.create({
- key: options.keypair.key,
- public: options.keypair.public
- })
- break
- // case '':
- // msg.packet.message.
- // break
- default:
- break
- }
- /* Return message. */
- return msg
- }, PB.Signed.create({
- packet: PB.Packet.create({
- session: mySessionid,
- number: myPlayerNumber,
- fromKey: PB.VerificationKey.create({
- key: myVerificationPublicKey
- }),
- message: PB.Message.create({
- blame: PB.Blame.create({
- })
- }),
- phase: PB.Phase.values['BLAME']
- })
- }))
- /* Set message. */
- const msg = Buffer.from(PB.Packet.encode(blameMessage.packet).finish())
- .toString('base64')
- /* Set blame message signature. */
- blameMessage.signature = PB.Signature.create({
- signature: new BetterMessage(msg, 'base64')
- .sign(bch.PrivateKey(myVerificationPrivateKey))
- })
- debug('Compiled blame message:', blameMessage)
- /* Return packed message. */
- return packMessage(blameMessage)
- }
- /* Export module. */
- module.exports = {
- PB,
- broadcastSignatureAndUtxo,
- broadcastEquivCheck,
- broadcastFinalOutputAddresses,
- forwardEncryptedOutputs,
- messageToBuffers,
- decodeAndClassify,
- registration,
- broadcastTransactionInput,
- changeAddressAnnounce,
- packMessage,
- blameMessage,
- checkPacketSignature
- }
|