serverMessages.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. /* Import core modules. */
  2. const _ = require('lodash')
  3. const bch = require('bitcore-lib-cash')
  4. const debug = require('debug')('shuffle:server-messages')
  5. const path = require('path')
  6. const protobuf = require('protobufjs')
  7. /* Import local modules. */
  8. const BetterMessage = require('./BetterMessage.js')
  9. /* Initialize magic (bytes). */
  10. const magic = Buffer.from('42bcc32669467873', 'hex')
  11. /* Initialize protobuf classes. */
  12. const pbEnums = ['Phase', 'Reason']
  13. /* Initialize protobuf types. */
  14. const pbTypes = [
  15. 'Signed', 'Packet', 'Coins', 'Signatures', 'Message', 'Address',
  16. 'Registration', 'VerificationKey', 'EncryptionKey', 'DecryptionKey',
  17. 'Hash', 'Signature', 'Transaction', 'Blame', 'Invalid', 'Inputs',
  18. 'Packets'
  19. ]
  20. /* Initialize protobuf. */
  21. // NOTE: Problem loading from local file, so copied to web root. See issue:
  22. // https://github.com/protobufjs/protobuf.js/issues/1023#issuecomment-629165262
  23. // TODO: Translate `.proto` to `.json` and use JSON load.
  24. let PB = {}
  25. let protoFile = null
  26. if (typeof window !== 'undefined') {
  27. protoFile = 'shuffle.proto'
  28. } else {
  29. protoFile = path.join(__dirname, 'shuffle.proto')
  30. }
  31. protobuf.load(protoFile, (err, root) => {
  32. if (err) return console.error(err) // eslint-disable-line no-console
  33. /* Set root. */
  34. PB.root = root
  35. /* Loop through ALL protobuf types. */
  36. for (let oneTypeName of pbTypes) {
  37. PB[oneTypeName] = PB.root.lookupType(oneTypeName)
  38. }
  39. /* Loop through ALL protobuf classes. */
  40. for (let oneClassName of pbEnums) {
  41. PB[oneClassName] = PB.root.lookupEnum(oneClassName)
  42. }
  43. })
  44. /**
  45. * Message to Buffers
  46. */
  47. function messageToBuffers (someBase64Message) {
  48. /* Initialize message buffer. */
  49. const messageBuffer = Buffer.from(someBase64Message, 'base64')
  50. /* Validate message buffer. */
  51. if (messageBuffer.length < 12) {
  52. throw new Error('bad_length')
  53. } else {
  54. /* Set message magic (bytes). */
  55. const messageMagic = messageBuffer.slice(0, 8)
  56. /* Validate message magic (bytes). */
  57. if (messageMagic.toString('hex') !== magic.toString('hex')) {
  58. throw new Error('message_magic')
  59. }
  60. /* Initialize message length. */
  61. let messageLength = messageBuffer.slice(8, 12)
  62. /* Set message length. */
  63. messageLength = messageLength.readUInt32BE(0)
  64. /* Set message payload. */
  65. // const messagePayload = messageBuffer.slice(12, ) // FIXME: Why do we have a trailing space??
  66. const messagePayload = messageBuffer.slice(12) // FIXME: Why do we have a trailing space??
  67. /* Validate message payload. */
  68. if (messagePayload.length !== messageLength) {
  69. console.error( // eslint-disable-line no-console
  70. 'Incorrect payload size', messagePayload.length,
  71. '!==', messageLength)
  72. throw new Error('message_payload')
  73. } else {
  74. /* Return message buffers. */
  75. return {
  76. magic: messageBuffer.slice(0, 8).toString('base64'),
  77. length: messageBuffer.slice(8, 12).toString('base64'),
  78. payload: messageBuffer.slice(12).toString('base64'),
  79. buffer: messageBuffer.toString('base64')
  80. }
  81. }
  82. }
  83. }
  84. const handleMessageBuffer = (messageBuffer) => {
  85. /* Validate message buffer. */
  86. if (messageBuffer.length < 12) {
  87. throw new Error('bad_length')
  88. }
  89. /* Set message magic (bytes). */
  90. const messageMagic = messageBuffer.slice(0, 8)
  91. /* Validate message magic (bytes). */
  92. if (messageMagic.toString('hex') !== magic.toString('hex')) {
  93. throw new Error('message_magic')
  94. }
  95. /* Initialize message length. */
  96. let messageLength = messageBuffer.slice(8, 12)
  97. /* Set message length. */
  98. messageLength = messageLength.readUInt32BE(0)
  99. /* Set message payload. */
  100. const messagePayload = messageBuffer.slice(12)
  101. /* Build server message. */
  102. const serverMessage = {
  103. packets: [],
  104. full: undefined,
  105. pruned: undefined,
  106. components: messageToBuffers(messageBuffer)
  107. }
  108. /* Validate message payload. */
  109. if (messagePayload.length !== messageLength) {
  110. debug(
  111. 'Incorrect payload size:', messagePayload.length,
  112. '!==', messageLength
  113. )
  114. throw new Error('message_payload')
  115. } else {
  116. /* Set decoded packets. */
  117. const decodedPackets = PB.Packets.decode(messagePayload)
  118. /* Loop through ALL decoded packets. */
  119. for (let onePacket of decodedPackets.packet) {
  120. serverMessage.packets.push(onePacket)
  121. }
  122. /* Set (full) server message. */
  123. serverMessage.full = decodedPackets.toJSON()
  124. /* Set (pruned) server message. */
  125. serverMessage.pruned = {
  126. message: _.get(serverMessage.full, 'packet[0].packet'),
  127. signature: _.get(serverMessage.full, 'packet[0].signature.signature')
  128. }
  129. }
  130. /* Validate (pruned) server message. */
  131. if (!serverMessage.pruned.message) {
  132. throw new Error('message_parsing')
  133. }
  134. /* Initialize message types. */
  135. // TODO: Pick more intuitive and more consistent message names.
  136. let messageTypes = [
  137. { name: 'playerCount', required: ['number'] },
  138. { name: 'serverGreeting', required: ['number', 'session'] },
  139. { name: 'announcementPhase', required: ['number', 'phase'] },
  140. { name: 'incomingVerificationKeys', required: ['session', 'fromKey.key', 'message.inputs'] },
  141. { name: 'incomingChangeAddress', required: ['session', 'number', 'fromKey.key', 'message.address.address', 'message.key.key', 'phase'] },
  142. /**
  143. * This message name will be changed before the `serverMessage`
  144. * event is emitted by the `CommChannel` class.
  145. *
  146. * We set the final message name there because that's where we
  147. * have access to round state data and the purpose of the message
  148. * (which should inform the name) changes based on the state of the
  149. * round.
  150. *
  151. * Yep, this is yet another hack to deal with the fact that there
  152. * is no support for a unique `messageName` field on the protocol
  153. * messages.
  154. */
  155. { name: '_unicast', required: ['number', 'session', 'fromKey.key', 'toKey.key', 'message.str'] },
  156. { name: 'incomingEquivCheck', required: ['number', 'session', 'phase', 'fromKey.key', 'message.hash.hash'] },
  157. { name: 'incomingInputAndSig', required: ['number', 'session', 'phase', 'fromKey.key', 'message.signatures'] },
  158. { name: 'finalTransactionOutputs', required: ['session', 'number', 'phase', 'fromKey.key', 'message.str'] },
  159. { name: 'blame', required: ['number', 'session', 'fromKey.key', 'message.blame', 'phase'] }
  160. ]
  161. // Order the message types so that the most
  162. // specific descriptions are seen first by
  163. // the function that attempts to find a match.
  164. messageTypes = _.orderBy(
  165. messageTypes,
  166. function (ot) {
  167. return ot.required.length
  168. },
  169. ['desc']
  170. )
  171. /* Set matching message type. */
  172. const matchingMessageType = _.reduce(messageTypes, function (winner, oneObject) {
  173. /* Set required parameter values. */
  174. const requiredParamValues = _.at(serverMessage.pruned.message, oneObject.required)
  175. /* Validate required parameter values. */
  176. // NOTE: If none of the required parameters are missing,
  177. // consider this object a match.
  178. const isMatch = oneObject.required.length === _.compact(requiredParamValues).length
  179. /* Validate match. */
  180. // NOTE: If our match has more matching params than
  181. // our previous match, use this one instead
  182. if (isMatch && winner.required.length < requiredParamValues.length) {
  183. return oneObject
  184. } else {
  185. return winner
  186. }
  187. }, { required: [] })
  188. /* Update server message. */
  189. Object.assign(serverMessage.pruned, {
  190. messageType: matchingMessageType.name || 'UNKNOWN'
  191. })
  192. /* Return server message. */
  193. return serverMessage
  194. }
  195. /**
  196. * Decode and Classify
  197. *
  198. * TODO: Clean this up so it better handles multi-packet messages.
  199. */
  200. function decodeAndClassify (messageBuffer) {
  201. return new Promise(function (resolve, reject) {
  202. /* Validate and parse message data. */
  203. /* eslint-disable-next-line no-undef */
  204. if (messageBuffer && messageBuffer.data && messageBuffer.data instanceof Blob) {
  205. /* Initialize file reader. */
  206. const reader = new FileReader() // eslint-disable-line no-undef
  207. /* Handle onload. */
  208. reader.onload = () => {
  209. /* Handle message buffer. */
  210. resolve(handleMessageBuffer(Buffer.from(reader.result)))
  211. }
  212. /* Handle onerror. */
  213. reader.onerror = reject
  214. /* Read data as buffer. */
  215. reader.readAsArrayBuffer(messageBuffer.data)
  216. } else {
  217. /* Handle message buffer. */
  218. resolve(handleMessageBuffer(messageBuffer))
  219. }
  220. })
  221. }
  222. /**
  223. * Registration
  224. */
  225. function registration (protocolVersion, amount, key) {
  226. /* Validate key. */
  227. if (_.isObject(key) && typeof key.toString) {
  228. key = key.toString()
  229. }
  230. /* Set message. */
  231. const message = PB.Signed.create({
  232. packet: PB.Packet.create({
  233. fromKey: PB.VerificationKey.create({ key }),
  234. registration: PB.Registration.create({
  235. amount: amount,
  236. version: protocolVersion,
  237. // type: 'DEFAULT' // WHY AREN'T WE USING THIS??
  238. type: 'DEFAULT'
  239. })
  240. })
  241. })
  242. /* Return packed message. */
  243. return packMessage(message)
  244. }
  245. /**
  246. * Broadcast Transaction Input
  247. *
  248. * This function reveals the coin our client wishes to shuffle as well as our
  249. * verificationKey. Although we revealed our verificationKey in our server
  250. * registration message, that message isn't relayed to our peers. This is the
  251. * first message where our peers see the vk.
  252. */
  253. function broadcastTransactionInput (
  254. inputsObject,
  255. session,
  256. playerNumber,
  257. verificationPublicKey
  258. ) {
  259. /* Validate verification key. */
  260. if (_.isObject(verificationPublicKey) && typeof verificationPublicKey.toString) {
  261. verificationPublicKey = verificationPublicKey.toString()
  262. }
  263. /* Initialize message. */
  264. let message
  265. /* Set message. */
  266. message = PB.Signed.create({
  267. packet: PB.Packet.create({
  268. fromKey: PB.VerificationKey.create({
  269. key: verificationPublicKey
  270. }),
  271. message: PB.Message.create({
  272. inputs: {}
  273. }),
  274. session: session,
  275. number: playerNumber
  276. })
  277. })
  278. /* Loop through ALL input objects. */
  279. for (let key in inputsObject) {
  280. message.packet.message.inputs[key] = PB.Coins.create({
  281. coins: inputsObject[key]
  282. })
  283. }
  284. /* Return packed message. */
  285. return packMessage(message)
  286. }
  287. /*
  288. {
  289. "packet": [
  290. {
  291. "packet": {
  292. "session": "aGFIYURRd0JBWFN5MkJJNnBhdzZ6OQ==",
  293. "number": 2,
  294. "from_key": {
  295. "key": "03b9bf1605aa851945bd72e575d42f7ca874d9d7099f686c70893f927512010853"
  296. },
  297. "to_key": {
  298. "key": "03aa863d01fd4c44043b73fccd820101f8bdc3bdf59a2472f1f1ecf6822ce4ad7b"
  299. },
  300. "phase": 2,
  301. "message": {
  302. "str": "QklFMQNiC79dSfQjlIRKY/nHYE9KblxLkT6na8kelVoL8OIHW9/QqooDxTgtNm5Xhfh3R6kMWslw+uF6sYdhYZ53ce2sJBaaRWMLO8twqjfJGBPt/97XAAIVA57KNfzJOzdx6a8e/oUZ99xKPp6MRDBPGmME"
  303. }
  304. },
  305. "signature": {
  306. "signature": "H/zYhGuMsptl9hL76Wn9ylNUmHKAzO+ZQbEHAkTPIp1aP0DiAjtvsyFmS1ZK03nTS5d5/4Vb5GoKnty7UijANds="
  307. }
  308. },
  309. {
  310. "packet": {
  311. "session": "aGFIYURRd0JBWFN5MkJJNnBhdzZ6OQ==",
  312. "number": 2,
  313. "from_key": {
  314. "key": "03b9bf1605aa851945bd72e575d42f7ca874d9d7099f686c70893f927512010853"
  315. },
  316. "to_key": {
  317. "key": "03aa863d01fd4c44043b73fccd820101f8bdc3bdf59a2472f1f1ecf6822ce4ad7b"
  318. },
  319. "phase": 2,
  320. "message": {
  321. "str": "QklFMQJtTrG5IQiUX1C0ZR67t5cQbN4v72uSzrCOuy1QEtOI41wQ2CGGK7lgtxyS9g8tzd9YHe+4DyMaSyrCIx/Ft/U27P6dU5xR6lVhf3ekV3mIW8/vH2lpb2AWY3Djl0egotBFrIylX+2W0nC9MVaU98xZ"
  322. }
  323. },
  324. "signature": {
  325. "signature": "H8zKYj3OsPF/EUstjST/pzI8AqwcsK8OySDIxm9WABUYHWODcoBAzKsnEh1I7gLfABpAqnYkM0WK0SiyBeVUuq8="
  326. }
  327. }
  328. ]
  329. }
  330. */
  331. // this.comms.sendMessage('forwardEncryptedOutputs', [
  332. // this.session, me.playerNumber, encryptedOutputAddresses.success,
  333. // this.phase.toUpperCase(), nextPlayer.verificationKey, this.ephemeralKeypair.publicKey,
  334. // this.ephemeralKeypair.privateKey
  335. // ])
  336. /**
  337. * Forward Encrypted Outputs
  338. */
  339. function forwardEncryptedOutputs (
  340. session,
  341. fromPlayerNumber,
  342. arrayOfOutputs,
  343. phase,
  344. toVerificationKey,
  345. myVerificationPubKey,
  346. myVerificationPrivKey
  347. ) {
  348. /* Validate verification public key. */
  349. if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
  350. myVerificationPubKey = myVerificationPubKey.toString()
  351. }
  352. /* Validate (to) verification key. */
  353. if (_.isObject(toVerificationKey) && typeof toVerificationKey.toString) {
  354. toVerificationKey = toVerificationKey.toString()
  355. }
  356. // TODO: Make these server messages consistent with
  357. // respect to param validation and type checking.
  358. /* Initialize signed messages. */
  359. const signedMessages = []
  360. /* Loop through ALL outputs. */
  361. for (let oneEncryptedAddress of arrayOfOutputs) {
  362. /* Set message. */
  363. const message = PB.Signed.create({
  364. packet: PB.Packet.create({
  365. session: session,
  366. number: fromPlayerNumber,
  367. fromKey: PB.VerificationKey.create({
  368. key: myVerificationPubKey
  369. }),
  370. toKey: PB.VerificationKey.create({
  371. key: toVerificationKey
  372. }),
  373. phase: PB.Phase.values[phase.toUpperCase()],
  374. message: PB.Message.create({
  375. str: oneEncryptedAddress
  376. })
  377. })
  378. })
  379. /* Set message. */
  380. const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
  381. .toString('base64')
  382. /* Set signature. */
  383. const signature = new BetterMessage(msg, 'base64')
  384. .sign(bch.PrivateKey(myVerificationPrivKey))
  385. /* Set message signature. */
  386. message.signature = PB.Signature.create({ signature })
  387. /* Add message to signed messages. */
  388. signedMessages.push(message)
  389. }
  390. /* Return packed signed messages. */
  391. return packMessage(signedMessages)
  392. }
  393. /*
  394. {
  395. "packet": [
  396. {
  397. "packet": {
  398. "session": "OGlTZkdsSTZUQVgwNkU4Yk4wMkowTw==",
  399. "number": 1,
  400. "from_key": {
  401. "key": "0202135e4f7217957db961f26e3856a239e89023f6cd6088d6303775c3a61572bf"
  402. },
  403. "phase": 6,
  404. "message": {
  405. "signatures": [
  406. {
  407. "utxo": "3a019c3a44d5269edf8a6ca2588ead452b03f8fcdda2b622906c98e4d1d5778f:0",
  408. "signature": {
  409. "signature": "MzA0NDAyMjAwYjZiYzIyMDMzZTQwYzA5ZjJhNTdiZjZhOTc1YTEwMTk0OGU5ODAzMGY2OWRjMWZhYzlmZWY5ZTk0YTcxZTMzMDIyMDY4MDFlYzMzYWZiOTU5ZDZlZGZiMDQyMDM2NzcwYjA4MjI1NzczM2ExMjBhMDdmMTgzM2RlZTdhZTUzNDhlNzM0MQ=="
  410. }
  411. }
  412. ]
  413. }
  414. },
  415. "signature": {
  416. "signature": "H0fwrr75/6GcPzPB6etyWyZD4mLlDGacVIs/j+VTldLCdE8yAfactL8jdXrJRwE7RqYhCFJ6vIHFpTu6Xa+SW2Y="
  417. }
  418. }
  419. ]
  420. }
  421. */
  422. /**
  423. * Broadcast Signature and UTXO
  424. */
  425. function broadcastSignatureAndUtxo (
  426. session,
  427. fromPlayerNumber,
  428. coinUtxoData,
  429. signatureString,
  430. phase,
  431. myVerificationPubKey,
  432. myVerificationPrivKey
  433. ) {
  434. /* Validate verification public key. */
  435. if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
  436. myVerificationPubKey = myVerificationPubKey.toString()
  437. }
  438. /* Set message. */
  439. const message = PB.Signed.create({
  440. packet: PB.Packet.create({
  441. session: session,
  442. number: fromPlayerNumber,
  443. fromKey: PB.VerificationKey.create({
  444. key: myVerificationPubKey
  445. }),
  446. phase: PB.Phase.values[phase.toUpperCase()],
  447. message: PB.Message.create({
  448. signatures: []
  449. })
  450. })
  451. })
  452. /* Add packet message signatures. */
  453. message.packet.message.signatures.push(PB.Signature.create({
  454. utxo: coinUtxoData,
  455. signature: PB.Signature.create({
  456. signature: signatureString
  457. })
  458. }))
  459. /* Set message. */
  460. const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
  461. .toString('base64')
  462. /* Set signature. */
  463. const signature = new BetterMessage(msg, 'base64')
  464. .sign(bch.PrivateKey(myVerificationPrivKey))
  465. /* Set message signature. */
  466. message.signature = PB.Signature.create({ signature })
  467. /* Return packed message. */
  468. return packMessage(message)
  469. }
  470. /*
  471. {
  472. "packet": [
  473. {
  474. "packet": {
  475. "session": "c25hMmNwNm8xcXJIejlNMDhJdGFNZA==",
  476. "number": 2,
  477. "from_key": {
  478. "key": "03343954c832a7b870eb8758c1c280b954bfed8b8fb65a33d52f848aabdbf31dce"
  479. },
  480. "phase": 4,
  481. "message": {
  482. "hash": {
  483. "hash": "1WDdy4zstoNgSnuSjagCxL5P8aqDbBerN92WSs1c2hY="
  484. }
  485. }
  486. },
  487. "signature": {
  488. "signature": "ICz+h2V5JBhHTronVb2FB4rCLHrIDi3gmsCn/+VphuojdofZBx5LCjefnnoGhwyYVQ40pSPi1u+JPXduPurrOBo="
  489. }
  490. }
  491. ]
  492. }
  493. */
  494. /**
  495. * Broadcast Equivocation Check
  496. */
  497. function broadcastEquivCheck (
  498. session,
  499. fromPlayerNumber,
  500. equivCheckHash,
  501. phase,
  502. myVerificationPubKey,
  503. myVerificationPrivKey
  504. ) {
  505. /* Validate verification public key. */
  506. if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
  507. myVerificationPubKey = myVerificationPubKey.toString()
  508. }
  509. /* Set message. */
  510. const message = PB.Signed.create({
  511. packet: PB.Packet.create({
  512. session: session,
  513. number: fromPlayerNumber,
  514. fromKey: PB.VerificationKey.create({
  515. key: myVerificationPubKey
  516. }),
  517. phase: PB.Phase.values[phase.toUpperCase()],
  518. message: PB.Message.create({
  519. hash: PB.Hash.create({
  520. hash: equivCheckHash
  521. })
  522. })
  523. })
  524. })
  525. /* Set message. */
  526. const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
  527. .toString('base64')
  528. /* Set signature. */
  529. const signature = new BetterMessage(msg, 'base64')
  530. .sign(bch.PrivateKey(myVerificationPrivKey))
  531. /* Set message signature. */
  532. message.signature = PB.Signature.create({ signature })
  533. /* Return packed message. */
  534. return packMessage(message)
  535. }
  536. /**
  537. * Broadcast Final Output Addresses
  538. */
  539. function broadcastFinalOutputAddresses (
  540. session,
  541. fromPlayerNumber,
  542. arrayOfOutputs,
  543. phase,
  544. myVerificationPubKey,
  545. myVerificationPrivKey
  546. ) {
  547. /* Validate verification public key. */
  548. if (_.isObject(myVerificationPubKey) && typeof myVerificationPubKey.toString) {
  549. myVerificationPubKey = myVerificationPubKey.toString()
  550. }
  551. /* Initialize signed messages. */
  552. const signedMessages = []
  553. /* Loop through ALL outputs. */
  554. for (let onePlaintextAddress of arrayOfOutputs) {
  555. /* Set message. */
  556. const message = PB.Signed.create({
  557. packet: PB.Packet.create({
  558. session: session,
  559. number: fromPlayerNumber,
  560. fromKey: PB.VerificationKey.create({
  561. key: myVerificationPubKey
  562. }),
  563. phase: PB.Phase.values[phase.toUpperCase()],
  564. message: PB.Message.create({
  565. str: onePlaintextAddress
  566. })
  567. })
  568. })
  569. /* Set message. */
  570. const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
  571. .toString('base64')
  572. /* Set signature. */
  573. const signature = new BetterMessage(msg, 'base64')
  574. .sign(bch.PrivateKey(myVerificationPrivKey))
  575. /* Set message signature. */
  576. message.signature = PB.Signature.create({ signature })
  577. /* Add message to signed messages. */
  578. signedMessages.push(message)
  579. }
  580. /* Return packed signed messages. */
  581. return packMessage(signedMessages)
  582. }
  583. /**
  584. * Change Address Announcement
  585. */
  586. function changeAddressAnnounce (
  587. session,
  588. playerNumber,
  589. changeAddress,
  590. encryptionPublicKey,
  591. phase,
  592. verificationPublicKey,
  593. verificationPrivateKey
  594. ) {
  595. /* Validate encryption public key. */
  596. if (_.isObject(encryptionPublicKey) && typeof encryptionPublicKey.toString) {
  597. encryptionPublicKey = encryptionPublicKey.toString()
  598. }
  599. /* Validate verification public key. */
  600. if (_.isObject(verificationPublicKey) && typeof verificationPublicKey.toString) {
  601. verificationPublicKey = verificationPublicKey.toString()
  602. }
  603. /* Initialize message. */
  604. let message
  605. /* Set message. */
  606. message = PB.Signed.create({
  607. packet: PB.Packet.create({
  608. session: session,
  609. number: playerNumber,
  610. fromKey: PB.VerificationKey.create({
  611. key: verificationPublicKey
  612. }),
  613. phase: PB.Phase.values[phase.toUpperCase()],
  614. message: PB.Message.create({
  615. address: PB.Address.create({
  616. address: changeAddress
  617. }),
  618. key: PB.VerificationKey.create({
  619. key: encryptionPublicKey
  620. })
  621. })
  622. })
  623. })
  624. /* Set message. */
  625. const msg = Buffer.from(PB.Packet.encode(message.packet).finish())
  626. .toString('base64')
  627. /* Set signature. */
  628. const signature = new BetterMessage(msg, 'base64')
  629. .sign(bch.PrivateKey(verificationPrivateKey))
  630. /* Set message signature. */
  631. message.signature = PB.Signature.create({ signature })
  632. /* Return packed message. */
  633. return packMessage(message)
  634. }
  635. /**
  636. * Pack Message
  637. *
  638. * Encode (pack) a message into a prototype buffer (protobuf) object.
  639. */
  640. function packMessage (oneOrMorePackets) {
  641. oneOrMorePackets = _.isArray(oneOrMorePackets) ? oneOrMorePackets : [oneOrMorePackets]
  642. /* Set packets. */
  643. const packets = PB.Packets.create({ packet: oneOrMorePackets })
  644. /* Set message buffer. */
  645. const messageBuffer = Buffer.from(PB.Packets.encode(packets).finish())
  646. /* Initialize length suffix. */
  647. const lengthSuffix = Buffer.alloc(4)
  648. /* Set length suffix. */
  649. lengthSuffix.writeUIntBE(messageBuffer.length, 0, 4)
  650. /* Set message components. */
  651. const messageComponents = [magic, lengthSuffix, messageBuffer]
  652. /* Set full message. */
  653. const fullMessage = Buffer.concat(messageComponents)
  654. /* Return message object. */
  655. return {
  656. unpacked: packets,
  657. packed: fullMessage,
  658. components: messageToBuffers(fullMessage)
  659. }
  660. }
  661. /**
  662. * Check Packet Signature
  663. */
  664. function checkPacketSignature (oneSignedPacket) {
  665. /* Initialize verification key. */
  666. const verificationKey = oneSignedPacket.packet.fromKey.key
  667. /* Set signature. */
  668. const signature = Buffer.from(oneSignedPacket.signature.signature).toString('base64')
  669. /* Set packet. */
  670. const packet = PB.Packet.encode(oneSignedPacket.packet)
  671. /* Set public key. */
  672. const pubkey = new bch.PublicKey(verificationKey)
  673. /* Set address. */
  674. const address = pubkey.toAddress().toString()
  675. /* Set message. */
  676. const message = Buffer.from(packet.finish()).toString('base64')
  677. // debug('checkPacketSignature',
  678. // oneSignedPacket,
  679. // verificationKey,
  680. // signature,
  681. // packet,
  682. // pubkey,
  683. // address,
  684. // message
  685. // )
  686. /* Initialize result. */
  687. let result = false
  688. try {
  689. /* Set result. */
  690. result = new BetterMessage(message, 'base64').verify(address, signature)
  691. } catch (someError) {
  692. console.error('Error checking signature:', someError) // eslint-disable-line no-console
  693. }
  694. /* Return result. */
  695. return result
  696. }
  697. /*
  698. {
  699. reason: < enum string citing reason for blame accusation >,
  700. accused: < verification key in hex format of player who 's being accused >,
  701. invalid: < an array of protobuf packets that provide evidence of fault >,
  702. hash: < hash provided by accused which differs from our own >,
  703. keypair: {
  704. key: < private key >,
  705. public: < public key >
  706. }
  707. }
  708. Possible Ban Reasons:
  709. INSUFFICIENTFUNDS = 0
  710. DOUBLESPEND = 1
  711. EQUIVOCATIONFAILURE = 2
  712. SHUFFLEFAILURE = 3
  713. SHUFFLEANDEQUIVOCATIONFAILURE = 4
  714. INVALIDSIGNATURE = 5
  715. MISSINGOUTPUT = 6
  716. LIAR = 7
  717. INVALIDFORMAT = 8
  718. */
  719. /**
  720. * Blame Message
  721. */
  722. function blameMessage (
  723. options,
  724. mySessionid,
  725. myPlayerNumber,
  726. myVerificationPublicKey,
  727. myVerificationPrivateKey
  728. ) {
  729. /* Validate verification public key. */
  730. if (_.isObject(myVerificationPublicKey) && typeof myVerificationPublicKey.toString) {
  731. myVerificationPublicKey = myVerificationPublicKey.toString()
  732. }
  733. /* Validate verification private key. */
  734. if (_.isObject(myVerificationPrivateKey) && typeof myVerificationPrivateKey.toString) {
  735. myVerificationPrivateKey = myVerificationPrivateKey.toString()
  736. }
  737. /* Set blame message. */
  738. const blameMessage = _.reduce(_.keys(options), function (msg, oneOptionName) {
  739. switch (oneOptionName) {
  740. case 'reason':
  741. msg.packet.message.blame.reason = PB.Reason.values[
  742. options.reason ? options.reason.toUpperCase() : 'NONE'
  743. ]
  744. break
  745. case 'accused':
  746. msg.packet.message.blame.accused = PB.VerificationKey.create({
  747. key: options.accused
  748. })
  749. break
  750. case 'invalid':
  751. // msg.packet.message.blame.invalue = PB.Invalid.create({ invalid: invalidPackets })
  752. msg.packet.message.blame.invalue = PB.Invalid.create({
  753. invalid: options.invalidPackets
  754. })
  755. break
  756. case 'hash':
  757. msg.packet.message.hash = PB.Hash.create({
  758. hash: options.hash
  759. })
  760. break
  761. case 'keypair':
  762. msg.packet.message.blame.key = PB.DecryptionKey.create({
  763. key: options.keypair.key,
  764. public: options.keypair.public
  765. })
  766. break
  767. // case '':
  768. // msg.packet.message.
  769. // break
  770. default:
  771. break
  772. }
  773. /* Return message. */
  774. return msg
  775. }, PB.Signed.create({
  776. packet: PB.Packet.create({
  777. session: mySessionid,
  778. number: myPlayerNumber,
  779. fromKey: PB.VerificationKey.create({
  780. key: myVerificationPublicKey
  781. }),
  782. message: PB.Message.create({
  783. blame: PB.Blame.create({
  784. })
  785. }),
  786. phase: PB.Phase.values['BLAME']
  787. })
  788. }))
  789. /* Set message. */
  790. const msg = Buffer.from(PB.Packet.encode(blameMessage.packet).finish())
  791. .toString('base64')
  792. /* Set blame message signature. */
  793. blameMessage.signature = PB.Signature.create({
  794. signature: new BetterMessage(msg, 'base64')
  795. .sign(bch.PrivateKey(myVerificationPrivateKey))
  796. })
  797. debug('Compiled blame message:', blameMessage)
  798. /* Return packed message. */
  799. return packMessage(blameMessage)
  800. }
  801. /* Export module. */
  802. module.exports = {
  803. PB,
  804. broadcastSignatureAndUtxo,
  805. broadcastEquivCheck,
  806. broadcastFinalOutputAddresses,
  807. forwardEncryptedOutputs,
  808. messageToBuffers,
  809. decodeAndClassify,
  810. registration,
  811. broadcastTransactionInput,
  812. changeAddressAnnounce,
  813. packMessage,
  814. blameMessage,
  815. checkPacketSignature
  816. }