bet architecturebet’s code is organized into four layers between the application
code (the dealer, player, cashier and blinder programs that humans
think about) and the Verus blockchain. Each layer hides the one below
it from its caller: a function in dealer.c that wants to read a
table’s CMM never has to know whether the daemon is being reached
through HTTP or through popen("verus -chain=VRSCTEST ..."), and the
RPC layer doesn’t know what a poker table is. This document maps each
layer to the source files that implement it, names the functions that
make up the inter-layer contract, and shows how a typical call
descends and returns.
The layering is intentional rather than incidental. The Verus migration replaced a nanomsg pub-sub layer that ran in roughly the same architectural slot as today’s RPC layer, and keeping the boundaries clean means the same swap could be made again — for a different chain, for a different identity-backed key-value store, or for a mock during testing — without touching the poker code.
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ dealer.c, player.c, cashier.c, game.c, blinder.c │
│ │
│ Example calls: │
│ poker_get_key_json(table_id, T_TABLE_INFO_KEY, 0) │
│ poker_join_table() │
│ poker_list_dealers() │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Poker API Layer (poker_vdxf.c) │
│ Poker-specific operations with key parsing │
│ │
│ Functions: │
│ poker_get_key_str() - Read string from a poker key │
│ poker_get_key_json() - Read JSON from a poker key │
│ poker_append_key_json() - Append data under a poker key │
│ poker_update_key_json() - Replace a poker key's value │
│ poker_join_table() - Join a poker table │
│ poker_list_dealers() - List registered dealers │
│ poker_is_table_full() - Check table capacity │
│ poker_print_table_keys() - Inspect a table identity │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Generic VDXF Layer (vdxf.c) │
│ Verus ID and ContentMultiMap operations │
│ │
│ Functions: │
│ get_cmm() - Read identity CMM │
│ update_cmm() - Replace identity CMM │
│ get_cmm_from_height() - History via │
│ getidentitycontent │
│ append_cmm_from_id_key_data_*() - Read-modify-write a │
│ single key │
│ update_cmm_from_id_key_data_*() - Overwrite a single key │
│ update_with_retry() - Wraps updateidentity │
│ with block/UTXO │
│ settle-time handling │
│ is_id_exists() / id_cansignfor()/ id_canspendfor() │
│ get_vdxf_id() - Hash a key name to its │
│ vdxfid │
│ verus_sendcurrency_data() - Submit a sendcurrency │
│ transaction │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ RPC Abstraction Layer (commands.c) │
│ Unified interface for blockchain RPC calls │
│ │
│ Function: │
│ chips_rpc(method, params, &result) │
│ │
│ Internally selects transport based on configuration: │
│ if (rpc_config.use_rest_api) │
│ → HTTP POST via libcurl │
│ else │
│ → CLI execution via popen │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Transport Layer │
│ │
│ REST API (libcurl): │
│ HTTP POST to rpc_config.url (default │
│ http://127.0.0.1:22778) │
│ JSON-RPC payload: {"method":"getidentity","params":[]} │
│ │
│ CLI (popen): │
│ verus -chain=VRSCTEST getidentity "id@" -1 │
└─────────────────────────────────────────────────────────────┘
The application and the layers below it read three configuration files at startup. They are split deliberately so the chain and the identity layout can be retargeted independently.
poker/config/blockchain.ini selects which chain and which CLI
binary bet talks to. The local VRSCTEST regtest used for
development:
[blockchain]
blockchain_cli = "verus -chain=VRSCTEST"
currency = VRSCTEST
new_block = "Y"
blockchain_cli is the literal command prefix used by the CLI
transport path; currency is the asset name passed to sendcurrency
for payins and payouts.
poker/config/keys.ini declares the identity names and the CMM key
prefix the application will read and write. On the dev regtest:
[identities]
cashier_id = cashier.sg777z.VRSCTEST@
dealers_id = dealers.sg777z.VRSCTEST@
All identity fields read from any INI must be fully-qualified Verus
IDs (containing @); the parser rejects bare short names.
The VDXF key namespace is not configured in keys.ini. The
prefix (chips.vrsc::poker.) and every key name are defined
as compile-time macros in poker/include/vdxf.h — every *_KEY
macro composes the single VDXF_POKER_KEYS_PREFIX macro with a
fixed suffix. The full readable string is hashed to a vdxfid by
get_vdxf_id at runtime. The prefix is opaque to Verus and does
not have to match the chain name.
When REST transport is enabled, chips_rpc reads HTTP credentials
from poker/.rpccredentials. The file is gitignored; a template
lives at poker/.rpccredentials.example:
[rpc]
rpc_url = http://127.0.0.1:22778
rpc_user = your_rpc_user
rpc_password = your_rpc_password
use_rest_api = yes
The default in code (config.c:771) is use_rest_api = 0 — meaning
the CLI transport path is used unless this file explicitly turns REST
on. REST is faster (one TCP connection vs. one process fork per call)
but requires the credentials file to be present and correctly
populated; CLI works out of the box but pays a process-fork per RPC.
| File | Purpose |
|---|---|
poker/src/poker_vdxf.c |
Poker-specific API layer |
poker/include/poker_vdxf.h |
Poker API declarations |
poker/src/vdxf.c |
Generic VDXF operations and update_with_retry |
poker/include/vdxf.h |
VDXF declarations and key-name macros |
poker/src/commands.c |
RPC abstraction (chips_rpc) and CLI/HTTP routing |
poker/src/config.c |
Configuration parsing for all three .ini files |
#include "poker_vdxf.h"
// Application code in dealer.c:
cJSON *table_info = poker_get_key_json(table_id, T_TABLE_INFO_KEY, 0);
The call descends as follows:
poker_get_key_json in poker_vdxf.c forwards the full key
name (T_TABLE_INFO_KEY, already prefixed via the
VDXF_POKER_KEYS_PREFIX macro in vdxf.h) to
get_cJSON_from_id_key in vdxf.c.get_cJSON_from_id_key resolves the identity, decides which
read variant to use (current vs. historical), and calls
chips_rpc("getidentity", params, &result).chips_rpc in commands.c consults rpc_config.use_rest_api:
rpc_config.url via libcurl, parses the response body.blockchain_cli + method +
params, runs it via popen, captures stdout, and parses the
JSON output.result flows back up through the layers. Nothing
above the RPC layer ever sees the transport choice.The same pattern holds for the write path. update_with_retry in
vdxf.c is the choke point for every updateidentity call — it
issues the RPC, waits for the resulting transaction to confirm via
wait_for_a_blocktime plus check_if_tx_exists, and surfaces the
final txid (or an error) to its caller. Single-writer-per-identity
(see verus-overview.md) means there’s no contention to worry about
above this layer — update_with_retry is the only place that has to
know what “settled on-chain” means.
The layered design is what made the original nanomsg → Verus
migration tractable. The application code in dealer.c/player.c
never had to learn about the new transport; it kept calling the same
poker-layer functions, and the bottom three layers were rewritten
under them. The same property holds for any future replatforming:
swapping CLI for REST is a one-line config change, swapping REST for
a different RPC dialect is a commands.c-only change, and swapping
Verus for a different identity-backed key-value store would touch
only vdxf.c.
bet uses Verus identities