@dotrino/lobby 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/lobby",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Lobby + matchmaking reciclable para juegos del ecosistema Dotrino: salas, asientos, espectadores, motor de turnos autoritativo, reputación y contactos sobre el proxy/identity/reputation compartidos",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -33,7 +33,7 @@
33
33
  ],
34
34
  "peerDependencies": {
35
35
  "@dotrino/identity": ">=0.5.0",
36
- "@dotrino/proxy-client": "^0.10.1",
36
+ "@dotrino/proxy-client": ">=0.13.1",
37
37
  "@dotrino/reputation": ">=0.3.0"
38
38
  },
39
39
  "peerDependenciesMeta": {
package/src/engine.js CHANGED
File without changes
package/src/index.d.ts CHANGED
File without changes
package/src/index.js CHANGED
File without changes
package/src/lobby.js CHANGED
File without changes
package/src/protocol.js CHANGED
File without changes
package/src/reputation.js CHANGED
@@ -68,21 +68,42 @@ export async function rankRooms (rooms, { reputation, contacts, preferContacts =
68
68
  /** Payload canónico que ambos jugadores firman (debe coincidir con el server). */
69
69
  export function receiptPayload (a, b, ts) { return { op: 'receipt', a, b, ts } }
70
70
 
71
- /** Firma mi mitad del recibo con el vault. Devuelve la firma base64. */
71
+ /**
72
+ * Firma MI mitad, y devuelve el paquete entero: `{ sig, signer, chain }`.
73
+ *
74
+ * Antes devolvía solo la firma, y con eso el registro no puede comprobar nada: firma el
75
+ * APARATO y el recibo (o el evento) es entre dos PERSONAS, así que hace falta la cadena que
76
+ * dice que ese aparato habla por esa identidad. Sin ella, una partida jugada desde el
77
+ * teléfono no contaba — daba «recibo inválido» o «co-firma inválida».
78
+ */
79
+ async function firmarMitad (identity, payload) {
80
+ const signed = await identity.signData(payload)
81
+ if (typeof signed === 'string') {
82
+ throw new Error('dotrino-lobby: signData debe devolver { signature, publickey, chain } (vault ≥ 0.84)')
83
+ }
84
+ if (!Array.isArray(signed.chain) || !signed.chain.length) {
85
+ throw new Error('dotrino-lobby: la firma vino sin cadena; el registro no podría comprobar quién firma por ti')
86
+ }
87
+ return { sig: signed.signature, signer: signed.publickey, chain: signed.chain }
88
+ }
89
+
90
+ /** Firma mi mitad del recibo con el vault. → `{ sig, signer, chain }`. */
72
91
  export async function signReceiptHalf (identity, a, b, ts) {
73
- const signed = await identity.signData(receiptPayload(a, b, ts))
74
- return typeof signed === 'string' ? signed : signed.signature
92
+ return firmarMitad(identity, receiptPayload(a, b, ts))
75
93
  }
76
94
 
77
95
  // ── Evento de indicador derivado co-firmado (p.ej. ELO) ────────────
78
96
  /** Payload canónico del evento que ambas partes firman (debe coincidir con el
79
97
  * server de reputation: {op:'event', indicator, scope, a, b, outcome, ts}). */
80
- export function eventPayload (indicator, scope, a, b, outcome, ts) { return { op: 'event', indicator, scope, a, b, outcome, ts } }
98
+ export function eventPayload (indicator, scope, a, b, outcome, ts, aud) {
99
+ // PARA QUIÉN es el evento va DENTRO de lo que firman los dos. Ponerlo al publicar no
100
+ // valdría de nada: la firma no lo cubriría, y el registro rechaza lo que no le nombra.
101
+ return { op: 'event', aud, indicator, scope, a, b, outcome, ts }
102
+ }
81
103
 
82
- /** Firma mi mitad del evento con el vault. */
83
- export async function signEventHalf (identity, indicator, scope, a, b, outcome, ts) {
84
- const signed = await identity.signData(eventPayload(indicator, scope, a, b, outcome, ts))
85
- return typeof signed === 'string' ? signed : signed.signature
104
+ /** Firma mi mitad del evento con el vault. → `{ sig, signer, chain }`. */
105
+ export async function signEventHalf (identity, indicator, scope, a, b, outcome, ts, aud) {
106
+ return firmarMitad(identity, eventPayload(indicator, scope, a, b, outcome, ts, aud))
86
107
  }
87
108
 
88
109
  /**
package/src/room.js CHANGED
@@ -373,7 +373,10 @@ export class Room extends Emitter {
373
373
  _hostReceiptSign (from, d) {
374
374
  const rec = this._pendingReceiptSig.get(d.receiptId)
375
375
  if (!rec || from !== rec.token) return // sólo el destinatario del recibo puede co-firmarlo
376
- const full = { a: rec.a, b: rec.b, ts: rec.ts, sigA: rec.sigA, sigB: d.sig }
376
+ // Sin la mitad completa del otro no hay recibo: se descarta en vez de guardar uno que
377
+ // el registro va a rechazar (y que haría fallar la calificación entera).
378
+ if (!d.sig || !d.signer || !Array.isArray(d.chain) || !d.chain.length) { this._pendingReceiptSig.delete(d.receiptId); return }
379
+ const full = { a: rec.a, b: rec.b, ts: rec.ts, sigA: rec.sigA, signerA: rec.signerA, chainA: rec.chainA, sigB: d.sig, signerB: d.signer, chainB: d.chain }
377
380
  this._receipts.set(rec.peerPubkey, full)
378
381
  this._pendingReceiptSig.delete(d.receiptId)
379
382
  this.emit('receipt', { pubkey: rec.peerPubkey, receipt: full })
@@ -604,10 +607,13 @@ export class Room extends Emitter {
604
607
  try {
605
608
  const a = this.myPubkey, b = s.pubkey
606
609
  const ts = clock.now() + i // ts distinto por par → receiptId no adivinable entre peers
607
- const sigA = await signReceiptHalf(this.identity, a, b, ts)
610
+ const mitadA = await signReceiptHalf(this.identity, a, b, ts)
608
611
  const receiptId = `${ts}:${ids[i]}`
609
- this._pendingReceiptSig.set(receiptId, { a, b, ts, sigA, peerPubkey: b, token: s.token })
610
- this._sendTo(s.token, K.RECEIPT_OFFER, { receiptId, receipt: { a, b, ts, sigA } })
612
+ // La mitad viaja entera (firma + quién firmó + su cadena): el registro comprueba
613
+ // que ese aparato habla por ese jugador, no que la llave del jugador firmó.
614
+ const media = { sigA: mitadA.sig, signerA: mitadA.signer, chainA: mitadA.chain }
615
+ this._pendingReceiptSig.set(receiptId, { a, b, ts, ...media, peerPubkey: b, token: s.token })
616
+ this._sendTo(s.token, K.RECEIPT_OFFER, { receiptId, receipt: { a, b, ts, ...media } })
611
617
  } catch (_) {}
612
618
  }
613
619
  }
@@ -628,11 +634,15 @@ export class Room extends Emitter {
628
634
  const a = this.myPubkey, b = s.pubkey
629
635
  const outcome = winnerPub ? (samePubkey(winnerPub, a) ? 'a' : (samePubkey(winnerPub, b) ? 'b' : 'draw')) : 'draw'
630
636
  const ts = clock.now() + i
631
- const data = eventPayload(indicator, scope, a, b, outcome, ts)
632
- const sigA = await signEventHalf(this.identity, indicator, scope, a, b, outcome, ts)
637
+ // El destinatario del registro va DENTRO de lo que firman los dos. Sin registro
638
+ // cableado no hay a quién publicar, así que no se co-firma nada.
639
+ const aud = this.reputation?.audience
640
+ if (!aud) continue
641
+ const data = eventPayload(indicator, scope, a, b, outcome, ts, aud)
642
+ const mitadA = await signEventHalf(this.identity, indicator, scope, a, b, outcome, ts, aud)
633
643
  const resultId = `res:${ts}:${ids[i]}`
634
- this._pendingResults.set(resultId, { data, sigA, token: s.token })
635
- this._sendTo(s.token, K.RESULT_OFFER, { resultId, data, sigA })
644
+ this._pendingResults.set(resultId, { data, sigA: mitadA.sig, signerA: mitadA.signer, chainA: mitadA.chain, token: s.token })
645
+ this._sendTo(s.token, K.RESULT_OFFER, { resultId, data, sigA: mitadA.sig, signerA: mitadA.signer, chainA: mitadA.chain })
636
646
  } catch (_) {}
637
647
  }
638
648
  }
@@ -641,7 +651,8 @@ export class Room extends Emitter {
641
651
  const rec = this._pendingResults.get(d.resultId)
642
652
  if (!rec || from !== rec.token) return // sólo el destinatario co-firma su resultado
643
653
  this._pendingResults.delete(d.resultId)
644
- const coSigned = { data: rec.data, sigA: rec.sigA, sigB: d.sig }
654
+ if (!d.sig || !d.signer || !Array.isArray(d.chain) || !d.chain.length) return
655
+ const coSigned = { data: rec.data, sigA: rec.sigA, signerA: rec.signerA, chainA: rec.chainA, sigB: d.sig, signerB: d.signer, chainB: d.chain }
645
656
  this.emit('result', { indicator: rec.data.indicator, scope: rec.data.scope, a: rec.data.a, b: rec.data.b, outcome: rec.data.outcome, coSigned })
646
657
  }
647
658
 
@@ -664,9 +675,12 @@ export class Room extends Emitter {
664
675
  // Anti-trampa: sólo co-firmo si el resultado del offer coincide con el que YO vi.
665
676
  if (this._relativeWinner(a, b, this._public.result) !== outcome) return
666
677
  try {
667
- const sigB = await signEventHalf(this.identity, indicator, scope, a, b, outcome, ts)
668
- this._sendHost(K.RESULT_SIGN, { resultId: d.resultId, sig: sigB })
669
- this.emit('result', { indicator, scope, a, b, outcome, coSigned: { data: d.data, sigA: d.sigA, sigB } })
678
+ // Se co-firma el `data` TAL CUAL vino, destinatario incluido: si el host propusiera
679
+ // otro registro, se firmaría para ese así que se comprueba antes de firmar.
680
+ if (this.reputation?.audience && d.data.aud !== this.reputation.audience) return
681
+ const mitadB = await signEventHalf(this.identity, indicator, scope, a, b, outcome, ts, d.data.aud)
682
+ this._sendHost(K.RESULT_SIGN, { resultId: d.resultId, sig: mitadB.sig, signer: mitadB.signer, chain: mitadB.chain })
683
+ this.emit('result', { indicator, scope, a, b, outcome, coSigned: { data: d.data, sigA: d.sigA, signerA: d.signerA, chainA: d.chainA, sigB: mitadB.sig, signerB: mitadB.signer, chainB: mitadB.chain } })
670
684
  } catch (_) {}
671
685
  }
672
686
 
@@ -732,10 +746,10 @@ export class Room extends Emitter {
732
746
  // ...y la contraparte tiene que ser el host real (pubkey conocido por el STATE).
733
747
  if (!samePubkey(peer, this._public.hostPubkey)) return
734
748
  try {
735
- const sigB = await signReceiptHalf(this.identity, a, b, ts)
736
- const full = { a, b, ts, sigA: d.receipt.sigA, sigB }
749
+ const mitadB = await signReceiptHalf(this.identity, a, b, ts)
750
+ const full = { a, b, ts, sigA: d.receipt.sigA, signerA: d.receipt.signerA, chainA: d.receipt.chainA, sigB: mitadB.sig, signerB: mitadB.signer, chainB: mitadB.chain }
737
751
  this._receipts.set(peer, full)
738
- this._sendHost(K.RECEIPT_SIGN, { receiptId: d.receiptId, sig: sigB })
752
+ this._sendHost(K.RECEIPT_SIGN, { receiptId: d.receiptId, sig: mitadB.sig, signer: mitadB.signer, chain: mitadB.chain })
739
753
  this.emit('receipt', { pubkey: peer, receipt: full })
740
754
  } catch (_) {}
741
755
  }
package/src/transport.js CHANGED
File without changes
package/src/util.js CHANGED
File without changes