@block52/poker-vm-sdk 1.3.1 → 1.3.3

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/dist/index.js CHANGED
@@ -56053,6 +56053,11 @@ exports.NonPlayerActionType = void 0;
56053
56053
  NonPlayerActionType["JOIN"] = "join";
56054
56054
  NonPlayerActionType["LEAVE"] = "leave";
56055
56055
  NonPlayerActionType["NEW_HAND"] = "new-hand";
56056
+ // Entry big blind auto-posted when a post-now joiner activates off the blind
56057
+ // seats (poker-vm#2553). Recorded in ANTE; counted toward the pot + the
56058
+ // player's bet (they're matched) but excluded from the first-to-act /
56059
+ // has-acted scans (it's a non-player action, like DEAD_SMALL_BLIND).
56060
+ NonPlayerActionType["POST"] = "post";
56056
56061
  NonPlayerActionType["SIT_IN"] = "sit-in";
56057
56062
  // Sit in and wait for the big blind — distinct action from SIT_IN (post now)
56058
56063
  // so each has a single responsibility. Sets WAITING_FOR_BIG_BLIND. (#2139)
@@ -57193,6 +57198,92 @@ class Deck {
57193
57198
  // Explicitly update hash after shuffling
57194
57199
  this.createHash();
57195
57200
  }
57201
+ /**
57202
+ * Shuffle deterministically from a 32-byte client entropy seed, byte-for-byte
57203
+ * matching the Cosmos chain so the PVM and the chain derive the SAME deck for
57204
+ * the same seed. This is a faithful port of
57205
+ * `pokerchain/x/poker/types/deck.go:ShuffleFromVRF` (poker-vm#2460): without
57206
+ * it the PVM forks from the chain on a client-seed new-hand (#2418).
57207
+ *
57208
+ * DEPRECATED interim: the client-chosen seed is grindable. Superseded by the
57209
+ * validator-entropy VRF re-hardening tracked in poker-vm#2459.
57210
+ *
57211
+ * @param seedHex 32-byte seed as 64 hex chars (the FE's `seed=` value).
57212
+ */
57213
+ shuffleFromSeed(seedHex) {
57214
+ const seed = Buffer.from(seedHex, "hex");
57215
+ if (seed.length !== Deck.PLAYER_SEED_LEN) {
57216
+ throw new Error(`player seed must be ${Deck.PLAYER_SEED_LEN} bytes, got ${seed.length}`);
57217
+ }
57218
+ this.seedHash = crypto$1.createHash("sha256").update(seed).digest("hex");
57219
+ // HKDF-SHA256 expand (salt = HashLen zero bytes, info = VRF_INFO),
57220
+ // consumed as an on-demand byte stream — mirrors Go hkdf.New(...).Read.
57221
+ const read = Deck.hkdfExpandReader(seed, Buffer.alloc(32), Buffer.from(Deck.VRF_INFO));
57222
+ // Fisher-Yates with crypto/rand.Int rejection sampling (Go semantics),
57223
+ // so the HKDF stream is consumed identically to the chain.
57224
+ for (let i = this.cards.length - 1; i > 0; i--) {
57225
+ const j = Deck.randInt(read, i + 1);
57226
+ [this.cards[i], this.cards[j]] = [this.cards[j], this.cards[i]];
57227
+ }
57228
+ this.createHash();
57229
+ }
57230
+ /**
57231
+ * Streaming HKDF-Expand (RFC 5869, SHA-256), matching Go
57232
+ * `hkdf.New(sha256.New, ikm, salt, info).Read`:
57233
+ * PRK = HMAC(salt, ikm); output = T(1)||T(2)||…, T(n) = HMAC(PRK, T(n-1)||info||n).
57234
+ * salt is HashLen zero bytes (Go's nil-salt default). Returns a reader that
57235
+ * yields the next `n` stream bytes on each call.
57236
+ */
57237
+ static hkdfExpandReader(ikm, salt, info) {
57238
+ const prk = crypto$1.createHmac("sha256", salt).update(ikm).digest();
57239
+ let buffer = Buffer.alloc(0);
57240
+ let t = Buffer.alloc(0);
57241
+ let counter = 1;
57242
+ return (n) => {
57243
+ while (buffer.length < n) {
57244
+ if (counter > 255) {
57245
+ throw new Error("HKDF stream exhausted (>255 blocks)");
57246
+ }
57247
+ t = crypto$1.createHmac("sha256", prk)
57248
+ .update(Buffer.concat([t, info, Buffer.from([counter])]))
57249
+ .digest();
57250
+ buffer = Buffer.concat([buffer, t]);
57251
+ counter++;
57252
+ }
57253
+ const out = Buffer.from(buffer.subarray(0, n)); // copy before advancing
57254
+ buffer = buffer.subarray(n);
57255
+ return out;
57256
+ };
57257
+ }
57258
+ /**
57259
+ * Uniform integer in [0, max) by rejection sampling — a port of Go
57260
+ * `crypto/rand.Int`. The byte count, high-byte mask and reject test match
57261
+ * the stdlib exactly so the HKDF byte stream is consumed the same way the
57262
+ * chain consumes it. `max` here is always ≤ 52, so the result fits a number.
57263
+ */
57264
+ static randInt(read, max) {
57265
+ const nMax = max - 1; // largest valid value; bit length is taken over max-1
57266
+ if (nMax === 0) {
57267
+ return 0; // max === 1: only 0 is valid, no bytes consumed
57268
+ }
57269
+ const bitLen = 32 - Math.clz32(nMax);
57270
+ const k = Math.ceil(bitLen / 8);
57271
+ let b = bitLen % 8;
57272
+ if (b === 0) {
57273
+ b = 8;
57274
+ }
57275
+ for (;;) {
57276
+ const bytes = read(k);
57277
+ bytes[0] &= (1 << b) - 1; // clear high bits of the top byte
57278
+ let n = 0;
57279
+ for (const byte of bytes) {
57280
+ n = n * 256 + byte; // big-endian, matching big.Int.SetBytes
57281
+ }
57282
+ if (n < max) {
57283
+ return n;
57284
+ }
57285
+ }
57286
+ }
57196
57287
  getCardMnemonic(suit, rank) {
57197
57288
  const rankNum = Number(rank);
57198
57289
  const rankStr = Deck.RANK_MAP[rankNum] !== undefined ? Deck.RANK_MAP[rankNum] : rankNum.toString();
@@ -57319,6 +57410,10 @@ Deck.SUIT_MAP = {
57319
57410
  [exports.SUIT.HEARTS]: "H",
57320
57411
  [exports.SUIT.SPADES]: "S"
57321
57412
  };
57413
+ // Matches pokerchain types.PlayerSeedLen and VRFInputPrefix — the two sides
57414
+ // MUST agree on these or the derived decks diverge.
57415
+ Deck.PLAYER_SEED_LEN = 32;
57416
+ Deck.VRF_INFO = "poker/deck/v1";
57322
57417
 
57323
57418
  /**
57324
57419
  * The community board for a Texas Hold'em hand.