A stateless execution layer for poker game logic on the Block52 blockchain network.
The Poker VM (PVM) is a pure execution layer that processes poker game logic without maintaining persistent state. All game state is stored on the blockchain, making the PVM stateless, horizontally scalable, and fault-tolerant.
- Stateless Execution: PVM reads state from blockchain, executes game logic, returns results
- No Database: No MongoDB, Redis, or external storage - blockchain is the single source of truth
- Pure Functions: Game logic is deterministic and side-effect free
- Horizontal Scaling: Multiple PVM instances can run in parallel
- Real-time Updates: WebSocket connections for live game state synchronization
- PVM Core: TypeScript-based poker game logic engine
- RPC Interface: JSON-RPC endpoint for transaction submission and queries
- WebSocket Server: Real-time game state updates for connected clients
- REST API: Health checks and utility endpoints
- ✅ No Data Migration: Spin up new PVM instances without data sync
- ✅ Instant Failover: Any instance can handle any request
- ✅ Global Distribution: Deploy PVM nodes globally for low latency
- ✅ Cost Efficient: No database hosting or maintenance costs
- ✅ Deterministic: Same input always produces same output
- ✅ Testable: Pure functions make testing straightforward
- Node.js 20+
- Yarn
- A running Block52 blockchain node (for production use)
-
Clone the repository:
git clone https://github.com/block52/poker-vm.git cd poker-vm -
Start the PVM server:
cd pvm/ts yarn install yarn dev -
Start the UI (optional):
cd ui yarn install yarn dev -
Access services:
- 🔧 PVM RPC: http://localhost:8545
- 🔧 Health Check: http://localhost:8545/health
- 🎰 Poker UI: http://localhost:5173 (if running)
- � WebSocket: ws://localhost:8545/ws
The PVM connects to a Block52 blockchain node for state storage:
# Set blockchain node endpoint (optional, defaults to localhost:26657)
export BLOCKCHAIN_RPC=https://node1.block52.xyz/rpc/
cd pvm/ts
yarn build
yarn startA public PVM instance is available for development and testing:
| Endpoint | URL |
|---|---|
| RPC | https://pvm.block52.xyz |
| Health | https://pvm.block52.xyz/health |
Example: Query the PVM
# Health check
curl https://pvm.block52.xyz/health
# Get client info
curl -X POST https://pvm.block52.xyz \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "get_client", "params": [], "id": 1}'Available RPC Methods:
| Category | Methods |
|---|---|
| Read | find_contract, get_account, get_block, get_blocks, get_block_by_hash, get_client, get_contract_schema, get_game_state, get_last_block, get_mempool, get_nodes, get_shared_secret, get_transaction, get_transactions |
| Write | block, burn, create_account, create_contract_schema, deploy_contract, mine, mined_block_hash, mint, new_hand, new_table, perform_action, transfer, withdraw |
| Network | RPC | REST | gRPC | WebSocket |
|---|---|---|---|---|
| Localhost | http://localhost:26657 | http://localhost:1317 | http://localhost:9090 | ws://localhost:26657/ws |
| Texas Hodl | https://texashodl.net/rpc | https://node.texashodl.net | grpcs://texashodl.net:9443 | wss://texashodl.net/ws |
| Block52 | https://node1.block52.xyz/rpc/ | https://node1.block52.xyz | grpcs://node1.block52.xyz:9443 | wss://node1.block52.xyz/ws |
The PVM is a stateless execution layer with three main interfaces:
-
RPC Endpoint (
http://localhost:8545):- Submit transactions
- Query game state
- JSON-RPC 2.0 compatible
-
WebSocket Server (
ws://localhost:8545/ws):- Real-time game state updates
- Connect with
?tableAddress=<table_id>&playerId=<player_address> - Automatic reconnection handling
-
REST API (
http://localhost:8545/health):- Health checks
- Metrics and diagnostics
Run unit tests:
cd pvm/ts
yarn testRun integration tests:
cd pvm/ts
yarn test:integrationThe engine supports replaying hands in PHH (Poker Hand History) format for fuzz testing and validation against real-world hand histories.
cd pvm/ts
yarn test tests/phh/Supported PHH Features:
- No-Limit Texas Hold'em (
variant = "NT") - Action mapping:
cbr(check/bet/raise),cc(check/call),f(fold),sm(show) - Automatic blind posting and dealing
- Total-to-additional amount conversion for raises
Example: Running a PHH hand through the engine
import { PhhRunner } from "./src/testing/phhRunner";
const runner = new PhhRunner();
const result = await runner.runHand(`
variant = "NT"
blinds_or_straddles = [100, 200]
starting_stacks = [10000, 10000]
players = ["P1", "P2"]
actions = [
"d dh p1 AcKc",
"d dh p2 2h3h",
"p1 cbr 600",
"p2 cc",
"d db Jc3d5c",
"p1 cbr 800",
"p2 f",
]
`);
console.log(result.success); // true
console.log(result.actionsExecuted); // 7See GitHub Issue #1578 for integration progress with the PHH Dataset.
cd pvm/ts
yarn buildThe build outputs to dist/ and can be run with:
node dist/index.jsBuild and run the PVM in a container:
cd pvm/ts
docker build -t poker-vm .
docker run -p 8545:8545 poker-vm| Variable | Default | Description |
|---|---|---|
PORT |
8545 |
HTTP server port (always used for PVM) |
BLOCKCHAIN_RPC |
http://localhost:26657 |
Block52 blockchain RPC endpoint |
NODE_ENV |
development |
Environment mode |
Since the PVM is stateless, you can run multiple instances behind a load balancer:
# Instance 1
PORT=8545 node dist/index.js
# Instance 2
PORT=8546 node dist/index.js
# Instance 3
PORT=8547 node dist/index.jsConfigure your load balancer (nginx, HAProxy, etc.) to distribute requests across instances.
The PVM provides health check endpoints for monitoring:
curl http://localhost:8545/healthResponse:
{
"status": "healthy",
"uptime": 12345,
"version": "1.0.0"
}The PVM tracks various player states throughout the game:
| Status | Description | Can Act | Receives Cards | In Dealer Rotation | Notes |
|---|---|---|---|---|---|
ACTIVE |
Player is actively participating | ✅ | ✅ | ✅ | Default status for players in a hand |
FOLDED |
Player has folded their hand | ❌ | ❌ | ❌ | Cannot act until next hand |
ALL_IN |
Player has bet all their chips | ❌ | ✅ | ❌ | Eligible for side pots |
SITTING_OUT |
Player is away or joined mid-hand | ❌ | ❌ | ❌ | Skipped in dealing and betting |
SHOWING |
Player is showing cards at showdown | ❌ | ✅ | ✅ | Cards revealed to table |
BUSTED |
Player has no chips left | ❌ | ❌ | ❌ | Eliminated from tournament |
Status Transitions:
stateDiagram-v2
[*] --> SITTING_OUT : Join mid-hand
[*] --> ACTIVE : Join before hand
SITTING_OUT --> ACTIVE : New hand starts\nor Sit-in action
ACTIVE --> FOLDED : Fold
ACTIVE --> ALL_IN : Bet all chips
ACTIVE --> SITTING_OUT : Sit out action\nor 0 chips (cash)
ACTIVE --> SHOWING : Showdown
ACTIVE --> BUSTED : Lose all chips\n(tournament)
ALL_IN --> SHOWING : Showdown
FOLDED --> ACTIVE : New hand starts
SHOWING --> ACTIVE : New hand starts
ALL_IN --> ACTIVE : New hand starts\n(if chips won)
BUSTED --> [*] : Eliminated
stateDiagram-v2
[*] --> ANTE : Game starts
ANTE --> PREFLOP : Blinds posted\n& cards dealt
PREFLOP --> FLOP : Betting complete
PREFLOP --> SHOWDOWN : All-in called
PREFLOP --> END : All fold
FLOP --> TURN : Betting complete
FLOP --> SHOWDOWN : All-in called
FLOP --> END : All fold
TURN --> RIVER : Betting complete
TURN --> SHOWDOWN : All-in called
TURN --> END : All fold
RIVER --> SHOWDOWN : Betting complete
RIVER --> END : All fold
SHOWDOWN --> END : Winner determined
END --> ANTE : New hand
END --> [*] : Game over
flowchart TD
A[Player Requests Join] --> B{Hand in Progress?}
B -->|No - ANTE before blinds| C[Set ACTIVE]
B -->|No - END round| C
B -->|Yes - Blinds posted| D[Set SITTING_OUT]
B -->|Yes - Any betting round| D
C --> E[✅ Participate in current/next hand]
D --> F[⏳ Wait for next hand]
F --> G{New Hand Starts}
G --> H[reInit activates player]
H --> I[Set ACTIVE]
I --> E
sequenceDiagram
participant P1 as Player 1 (SB)
participant P2 as Player 2 (BB)
participant P3 as Player 3 (joins mid-hand)
participant Game as Texas Holdem
Note over Game: Round: ANTE
P1->>Game: POST SMALL_BLIND
P2->>Game: POST BIG_BLIND
P1->>Game: DEAL
Note over Game: Round: PREFLOP
P3->>Game: JOIN (mid-hand)
Game-->>P3: Status: SITTING_OUT
Note over P3: No cards dealt
P1->>Game: CALL
P2->>Game: CHECK
Note over Game: Round: FLOP
P1->>Game: CHECK
P2->>Game: BET
P1->>Game: FOLD
Note over P1: Status: FOLDED
Note over Game: Round: END
Game-->>P2: Winner!
P2->>Game: NEW_HAND
Note over Game: reInit() called
Game-->>P1: Status: ACTIVE
Game-->>P3: Status: ACTIVE
Note over P3: Now eligible for cards
Players joining at different game phases receive different initial statuses:
| Current Round | Blinds Posted | Player Status on Join | Can Play This Hand |
|---|---|---|---|
ANTE |
No | ACTIVE |
✅ Yes |
ANTE |
Yes | SITTING_OUT |
❌ No (next hand) |
PREFLOP |
Yes | SITTING_OUT |
❌ No (next hand) |
FLOP |
Yes | SITTING_OUT |
❌ No (next hand) |
TURN |
Yes | SITTING_OUT |
❌ No (next hand) |
RIVER |
Yes | SITTING_OUT |
❌ No (next hand) |
SHOWDOWN |
Yes | SITTING_OUT |
❌ No (next hand) |
END |
N/A | ACTIVE |
✅ Yes (next hand) |
The dealer manager uses player status to determine positions and turn order:
| Player Status | Eligible for Dealer | Eligible for Blinds | Included in Turn Order | Receives Cards |
|---|---|---|---|---|
ACTIVE |
✅ | ✅ | ✅ | ✅ |
FOLDED |
❌ | ❌ | ❌ | ❌ |
ALL_IN |
❌ | ❌ | ❌ | ✅ (already) |
SITTING_OUT |
❌ | ❌ | ❌ | ❌ |
SHOWING |
✅ | ✅ | ✅ | ✅ (already) |
BUSTED |
❌ | ❌ | ❌ | ❌ |
Which actions each player status can perform:
| Player Status | Bet/Raise | Call | Check | Fold | All-In | Show | Muck | Sit Out | Sit In |
|---|---|---|---|---|---|---|---|---|---|
ACTIVE |
✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
FOLDED |
❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
ALL_IN |
❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |
SITTING_OUT |
❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
SHOWING |
❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
BUSTED |
❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Connect to a live poker table for real-time updates:
const ws = new WebSocket("ws://localhost:8545/ws?tableAddress=<table_id>&playerId=<player_address>");
ws.onmessage = event => {
const message = JSON.parse(event.data);
if (message.type === "gameStateUpdate") {
console.log("Game state updated:", message.gameState);
}
};Message Types:
connected: WebSocket connection establishedgameStateUpdate: Game state changed (player action, new round, etc.)error: Error occurred during processing
Submit transactions via JSON-RPC:
curl -X POST http://localhost:8545 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "poker.action",
"params": {
"tableId": "0x123...",
"playerId": "block52...",
"action": "bet",
"amount": 100
},
"id": 1
}'The Block52 SDK provides TypeScript/JavaScript interfaces for interacting with the PVM:
import { Block52Client } from "@bitcoinbrisbane/block52";
const client = new Block52Client({
rpc: "https://node1.block52.xyz/rpc/",
rest: "https://node1.block52.xyz",
grpc: "grpcs://node1.block52.xyz:9443"
});
// Submit a poker action
await client.poker.bet({
tableId: "0x123...",
playerId: "block52...",
amount: 100
});
// Query table state
const tableState = await client.poker.getTableState("0x123...");npm install @bitcoinbrisbane/block52
# or
yarn add @bitcoinbrisbane/block52cd sdk
nvm use 20.18
yarn prepare && yarn publishFor detailed SDK documentation, see sdk/README.md.
The PVM operates as a stateless execution layer on top of the Block52 blockchain:
┌─────────────────────────────────┐
│ Client (UI/Bot/SDK) │
│ (Submit transactions) │
└─────────────┬───────────────────┘
↓
┌─────────────────────────────────┐
│ PVM (Execution Layer) │ ← Stateless poker logic
│ - Validate transactions │
│ - Execute game rules │
│ - Calculate outcomes │
└─────────────┬───────────────────┘
↓
┌─────────────────────────────────┐
│ Block52 Blockchain │ ← State storage
│ - Store game state │
│ - Manage player accounts │
│ - Handle chip escrow │
└─────────────────────────────────┘
- Client → PVM: Submit poker action (bet, fold, call, etc.)
- PVM → Blockchain: Read current game state
- PVM: Execute game logic, validate action
- PVM → Blockchain: Write updated state
- PVM → Client: Broadcast update via WebSocket
| Contract | Description | Address | Network |
|---|---|---|---|
| Bridge | Deposit stables to poker VM | 0x092eEA7cE31C187Ff2DC26d0C250B011AEC1a97d |
mainnet |
| Vault | Validator staking | 0x893c26846d7cE76445230B2b6285a663BF4C3BF5 |
mainnet |
poker-vm/
├── pvm/ts/ # PVM execution layer (TypeScript)
│ ├── src/
│ │ ├── core/ # Game logic engine
│ │ ├── rpc.ts # JSON-RPC interface
│ │ ├── websocket.ts # Real-time connections
│ │ └── index.ts # Server entry point
│ └── tests/ # Unit and integration tests
├── sdk/ # TypeScript SDK for clients
├── ui/ # React-based poker UI
├── contracts/ # Solidity smart contracts
├── bot/ # Bot implementations
│ ├── ts/ # TypeScript bots
│ └── python/ # Python bot framework
└── tests/ # End-to-end tests
pitboss at block 52 dot xyz
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: 406F D0E4 4DC5 B26E F121 58C4 36F2 5F29 0099 78F4
Comment: Pit Boss <pitboss@block52.xyz>
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
1MIHZBpp/rvs
=XIiJ
-----END PGP PUBLIC KEY BLOCK-----
This repository has been analyzed for the critical CVE-2025-55182 vulnerability affecting React Server Components and Next.js. The poker-vm repository is NOT vulnerable as it uses React 18.3.1 and does not use Next.js or React Server Components.
For full details, see SECURITY-CVE-2025-55182.md.
To verify the repository remains free of CVE-2025-55182 vulnerabilities:
./scripts/check-react-cve-2025-55182.shThis script checks all package.json and lock files for vulnerable React Server or Next.js versions.
Contributions are welcome! Please see our contributing guidelines for more information.
MIT License - see LICENSE for details.
