Skip to content

About

P2Block build of the Lazarus DATUM Prime (wavicles-prime): runtime controls (live fees, per-identity fee overrides, bans) and per-worker stats. Upstream: GaltRanch/wavicles-prime.

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

10 Commits

Folders and files

Repository files navigation

P2Block DATUM Prime (primed)

The pool side of the DATUM protocol for the BLAKE2b Bitcoin chain (BTCB2), as run by P2Block (datum.p2block.com:28915, 1 % with your own gateway, 2 % on the hosted stratum). This is a fork of GaltRanch/wavicles-prime (PyBLØCK's WAVICLES Prime), which is itself Lazarus primed plus the window-snapshot commitment and carry-forward described below. Everything P2Block added is listed in P2Block modifications; the protocol, the TIDES rule and the gateways supported are unchanged, so any stock datum_gateway or ratum-gateway points at this Prime unpatched.

Any stock datum_gateway — OCEAN, CONVOY, the BLAKE2b forks by FlyTheElephant1 and iohzrd, or the packaged StartOS build — and ratum-gateway point at this Prime (see Supported gateways). The gateway's own node builds every block template. The Prime never sees or chooses transactions; it does three things:

  1. Dictates the coinbase. When a gateway asks for a coinbaser, the Prime answers with the current TIDES split of the window: one output per miner, proportional to work, after the pool fee, then the pool's own output last so the list sums to the requested value. The gateway builds its coinbase from that list, so a block found by anyone's miner pays everyone in the window.
  2. Verifies every share. Each share is rebuilt into a full BLAKE2b header v2 from the job the gateway described (prevhash, nbits, merkle branches, coinbase halves) and the miner's nonce fields, hashed, and checked against the share target — and against the network target, so a block is recognised before the gateway says so. The coinbase inside the share is parsed and compared with the split the Prime actually issued for that job; a coinbase paying anywhere else is rejected.
  3. Keeps the ledger. Accepted work is credited to the miner's payout address in an append-only window sized in multiples of network difficulty. On a block, the split that was paid (or, for a stock gateway's pool-only "empty" coinbase, the amount the pool now owes the window) is recorded, and the Prime submits the assembled block to its own node too.

P2Block modifications

Branch p2block = upstream GaltRanch/wavicles-prime at the pinned commit + the commits below. Builds are tagged p2block-vX.Y.Z; the pool's pool-primed image is built from that tag.

Runtime controls (<data-dir>/controls.json)

The pool's control plane (poolcore, from the /admin console) writes this file; the Prime re-reads it within 5 s of any change. Nothing here needs a restart.

{
  "fee_bps": 100,                       // live DATUM-tier fee; null/absent = prime.toml fee-bps
  "stratum_fee_bps": 200,               // live hosted-tier fee; null/absent = prime.toml stratum-fee-bps
  "fee_overrides": { "bc1q…": 50 },     // per payout address, applies on BOTH paths (partners, promotions)
  "banned_identities": ["bc1q…"],       // every share from these identities is refused, nothing credited
  "banned_ips": ["203.0.113.9", "198.51.100.0/24"],   // sessions from these addresses are refused at accept
  "updated": "2026-09-15T04:00:00Z"
}

Rules: fees are basis points, 0–10000; override keys are canonicalised like usernames (bech32 lowercased, .worker stripped); unknown keys are rejected. A file that fails validation is logged and ignored, keeping the previous controls in force; a removed file returns to the prime.toml values with no bans. Every reload is logged with a one-line summary.

Per-identity fees in the split and in the commitment

tides::SplitParams gained fee_overrides; bps_for(identity) returns the (stratum, datum) basis points that apply. The uncapped and the capped ("pay like CHIRP") paths both honour it, and the capped fee is computed exactly per identity. The window snapshot is now v2: params.fee_overrides is committed alongside the tier fees and the rule text names it, so a block paying someone a different rate is still reproducible from the chain. tools/verify_wavicles_block.py recomputes v2 snapshots (and applies the hosted-tier rate to stratum_work on every path, as the Prime does).

Per-worker stats for DATUM-tier miners

A miner behind their own gateway is one identity to the pool, but the gateway forwards the full stratum username. The Prime now keeps (identity, worker) counters — accepted, work, last share, a 10-minute hashrate — and exposes them as workers[] in stats.json. Entries expire after an hour of silence. The pool's dashboard shows them as per-rig rows next to the hosted-tier ones.

stats.json additions

pool.fee_bps / pool.stratum_fee_bps are the effective values (pool.config_fee_bps / pool.config_stratum_fee_bps keep the file values); every window.miners[] row carries its fee_bps / stratum_fee_bps; controls summarises what is loaded; workers[] as above.

Keeping up with upstream

git remote add upstream https://github.com/GaltRanch/wavicles-prime
git fetch upstream && git rebase upstream/main p2block      # our commits are few and self-contained
cargo test && cargo fmt --all -- --check
git tag p2block-vX.Y.Z && git push --force-with-lease origin p2block && git push origin p2block-vX.Y.Z

Then bump WAVICLES_REF in the pool repo's deploy/images/primed/Dockerfile. Generic pieces (the controls file, per-worker stats) are candidates for upstream pull requests; the fee override and ban policy are P2Block's.

WAVICLES (upstream: PyBLØCK)

Upstream is the Prime behind WAVICLES, PyBLØCK's DATUM pool. It is Lazarus primed plus two things that make the TIDES split verifiable from the chain and keep the pool from ever holding a balance for anyone (both kept unchanged here):

  • Window-snapshot commitment. Every coinbaser is computed from a window state; that state (identities and work, carry, parameters, the resulting split) is serialized as canonical JSON, hashed with BLAKE2b-256, and committed as the first output of the coinbaser: OP_RETURN <snapshot-tag> || hash (zero sats). The snapshot is published at /snapshot/<hash> on the stats port. tools/verify_wavicles_block.py --height H checks that the block's commitment hashes the published snapshot, recomputes the split from it, and compares every output on chain by scriptPubKey. What still rests on the pool is that the window reflects the shares gateways really sent; signed share receipts are the next step.
  • Carry-forward instead of "owed". What a coinbase could not pay — dust under the minimum payout, outputs a gateway's coinbase class dropped, or a block found on an empty window — is recorded per identity in carry.json once the block settles and paid by the next coinbases out of the pool's own output, fee included. The pool nets exactly its fee over time and never custodies payouts. stats.json exposes it under wavicles.carry.

Config keys: commit-snapshot, snapshot-tag, carry-forward (all on by default). The wire format, the gateways supported and the TIDES rule are unchanged; a stock gateway pays the OP_RETURN like any other dictated output.

Credit: the Prime itself is AwokenLazarus/Bitcoin prime/ (MIT); the WAVICLES changes are PyBLØCK's (GaltRanch/wavicles-prime); the modifications above are P2Block's. All MIT.

Layout

prime/
  Cargo.toml          workspace
  wire/               datum-wire: framing, NaCl session, messages, coinbaser v2, BLAKE2b PoW, share verify
  tides/              tides: share window, split computation, append-only ledger, block log
  primed/             the daemon: sessions, node poller, stats HTTP, CLI
  prime.toml.example
  scripts/regtest-e2e.sh          end-to-end test against a real C datum_gateway on regtest
  scripts/regtest-divergence.sh   two-node test: gateway template survives a pool node with a different mempool/tip

datum-wire and tides have no async or I/O dependencies beyond std; every protocol and accounting rule is unit-tested in isolation. primed is the only crate that touches sockets.

Build and run

cd prime
cargo build --release
cp prime.toml.example prime.toml      # edit payout-address, data-dir, rpc, rpc-cookie
target/release/primed -c prime.toml check
target/release/primed -c prime.toml pubkey   # give this to gateway operators
target/release/primed -c prime.toml run

Subcommands: run (default), check, pubkey, window (dump the window as JSON), import-ledger <ledger.json> (seed an empty window from the previous Prime's export). Logging is RUST_LOG (info default; debug prints each share decision).

Taking over from lazarus-prime

An existing lazarus-prime.toml loads unchanged (activation-height, verify-shares and require-split-gateway are accepted and reported as no longer applying). A data dir the old Prime left behind keeps its identity: lazarus-prime.key (its 160-byte layout) is read when there is no prime.key, so the pool pubkey every gateway operator pinned stays the same — primed pubkey prints it to confirm. (P2Block runs a fresh key; the section is kept for operators migrating an old Prime.) Its ledger.json is the whole window; import it before the first run:

primed -c lazarus-prime.toml import-ledger /path/to/lazarus-prime/ledger.json
primed -c lazarus-prime.toml run

This is how the Lazarus pool was cut over on 2026-09-03: same key, same config, 9,021 credit rows / 570.5 M work / 18 identities imported, lazarus-gateway reconnected within a second, and its shares verified against the same window. Expect a short burst of bad-coinbase-outputs rejects right after any Prime restart: a gateway keeps publishing jobs built on the coinbaser it got from the previous instance until its next template, and a coinbase paying a split this instance never issued is refused by design.

Gateway side

In datum_gateway_config.json:

"datum": {
  "pool_host": "datum.p2block.com",
  "pool_port": 28915,
  "pool_pubkey": "<output of primed pubkey>",
  "pool_pass_workers": true,
  "pool_pass_full_users": true,
  "pooled_mining_only": true
}

mining.pool_address should be the pool's payout address (a stock gateway sends anything its template is worth beyond the issued list there). Miners authenticate to the gateway as <payout address>.<worker>; the address is the identity credited in the window. The identity ends at the first . or ~, so a ~modifier suffix that a gateway without stratum_username_mod forwards verbatim still credits the address. Nothing else changes for the gateway operator.

The pool's own public stratum, lazarus-gateway (in ../lazarus/), is a DATUM client too and speaks to this Prime as one; two habits of its are recognised as such. It sends the whole legacy coinbase as coinb1 with an empty coinb2 (a shape no stock gateway produces), and when its template is worth less than the value a split was issued for it scales every output down by the same ratio rather than dropping the ones that no longer fit.

Supported gateways

Four upstreams, two protocol generations. The refs are what the e2e script builds, and are the versions this Prime is checked against.

Name Upstream Ref tracked Generation
convoy CONVOYMining/datum_gateway master Convoy, configure v3
fte FlyTheElephant1/datum_gateway test/console-collapse-pr14-pr17 (now also master) OCEAN, configure v1
iohzrd iohzrd/datum_gateway master (has the blake2b branch and two commits more) OCEAN, configure v1
startos packaged by Retropex/datum-gateway-startos the datum_gateway submodule of the newest pow_* release OCEAN, configure v1

The StartOS package ships the gateway as a submodule, so a release pins one gateway commit rather than naming a branch. scripts/regtest-e2e.sh startos resolves that commit from the release and builds it, which keeps the test honest as new packages ship. Its unreleased pow-convoy branch is Convoy-lineage and reachable as startos-convoy.

Two upstream rules are load-bearing and are enforced here rather than discovered in production:

  • RDTS output scripts. Knots activates RDTS (BIP 110) as a flag day at the BLAKE2b fork height, which limits every output of the generation transaction to a 34-byte scriptPubKey (83 if it starts with OP_RETURN). The newest gateways enforce it too: an oversized miner payout is left out of the coinbase, and an oversized pool payout stops them serving work for the block at all. address::to_script therefore treats an address whose output script cannot fit — a witness program over 32 bytes is valid but does not — as unpayable, so its share stays in the pool remainder instead of becoming an output a gateway would drop. An unpayable payout-address is refused at startup.
  • The ABW flag. Convoy's configure v3 carries a flags byte, and this pool sets ABW_DISABLED because it runs no anti-withholding. Convoy builds from 2026-09-02 onward require it: without it the gateway waits for an assignment that never comes and serves no work. Convoy-lineage builds from before that date reject a non-zero flags byte outright, so they need a gateway update rather than a change here. Only the unreleased pow-convoy branch is still pinned that far back; every published StartOS package is OCEAN-lineage and never sees this byte.

Protocol

Recovered from the C client; the same bytes any gateway already speaks.

  • Frame: 4-byte header, one little-endian u32: len:22 | reserved:2 | signed | sealed | channel | cmd:5, XOR-obfuscated by a per-direction rolling key (feedback, a Murmur3-style mix reseeded from the hello). Payloads are plaintext, NaCl sealed boxes, or crypto_box on the session keys, optionally trailed by an ed25519 signature.
  • Handshake: client sends a crypto_box_sealed hello containing its long-term and session keys, user agent, and a seed; Convoy-lineage clients append DRS\x01 and an optional resume token. The Prime replies sealed to the session key with its MOTD and the seed acknowledgement. The generation is detected from the hello, and the matching configure layout is sent: v1 (32-bit prime id) for OCEAN/fte/iohzrd, v3 (64-bit id, resume token, ABW_DISABLED) for Convoy.
  • Mining command (0x05): coinbaser request → coinbaser v2 reply (id | (value u64, script len, script)…); pow submit with lazily-attached job (0x01) and coinbase (0x02) sections, the BLAKE2b section (0x03: 64-bit ntime and nonce) and the header time (0x04); share receipts with the client's reject-code vocabulary; block notify; full-block request and the transaction reply.
  • Lineage quirks handled: 8 vs 256 job slots; FLAG_BLAKE2B set (fte/Convoy) or implied by the 0x03 section alone (iohzrd); 12-byte extranonce with b10cf00d marker; sia_prevhash form of the previous block.

BLAKE2b header v2

A share is verified by reproducing exactly what Knots hashes:

H1     = tagged_sha256("Bitcoin block header 1",
           version‖prev‖height‖merkle_root‖time‖00‖nbits‖txcount‖flags‖clear_bits‖tagged(xor_key))
H2     = tagged_sha256("Merge-mining hook", H1 ‖ 32 zero bytes ‖ rhs)      the job commitment
coinb1 = 000000 ‖ H2 ‖ 00000000                                            39 bytes, Sia layout
root   = blake2b256(0x00 ‖ coinb1 ‖ extranonce(12))                        what the ASIC calls the merkle root
work   = hidden_prev(32) ‖ nonce(8) ‖ ntime(8) ‖ root(32)                  the 80-byte ASIC pass
hash   = reverse(blake2b256(work) XOR mask(xor_key, clear_bits))

hash <= share_target(pot) credits 2^pot work; hash <= nbits_target is a block. The Bitcoin merkle root folds the coinbase txid over the gateway-supplied branches; the coinbase is coinb1 ‖ 12 zero bytes ‖ coinb2 with the target byte patched in. A per-connection JobSlot caches the parsed coinbase and the H2 for each (coinbase id, target byte), so a repeat share on the same job costs one BLAKE2b, not a coinbase parse, a merkle fold, and two tagged SHA256s.

TIDES

The window holds credits (ts, identity, work, height) until its total work reaches window × network difficulty (converted to difficulty-1 shares), then trims from the oldest end. A split of value V pays fee = V × fee_bps / 10000 to the pool, then distributes the rest to identities proportional to their work in the window, dropping outputs below min-payout (their share stays with the pool and is reported as unpaid). The output list is capped by count and size so the coinbase fits in a gateway's largest coinbase class, with the pool's output — fee plus whatever could not be placed — appended last. The list therefore sums to the requested value; a stock gateway pays it verbatim and only adds a pool output of its own when the template turns out to be worth more, and lazarus-gateway, which writes exactly the list it is given, pays the fee instead of burning it.

Stock gateways build several coinbase sizes and hand small miners those with room for only the first few outputs, or none at all while a coinbaser reply is in flight. The Prime classifies every share's coinbase as Split (every issued miner output paid), Partial (a subset, each in full), PoolOnly (only the pool's script), or Foreign (anything else, rejected). An output is "paid" when it carries at least the issued amount scaled by actual value / issued value when the template is worth less than the split assumed (never more than issued) — this covers both a gateway that scales the list and one that drops outputs. The pool's own output is exempt from any minimum; it takes what is left. A block found on a Partial or PoolOnly coinbase records owed_sats — what the pool holds on behalf of the window — in blocks.jsonl.

On disk (data-dir)

File What
prime.key ed25519 seed ‖ x25519 secret, 0600, generated once (lazarus-prime.key is read instead when present)
controls.json P2Block runtime controls (live fees, per-identity overrides, bans), written by the pool's control plane
credits.bin append-only 24-byte credit rows; replayed at start, compacted when the window trims
identities.txt interned identity table (one address per line, index = row id)
window.json window target work and lifetime counters
blocks.jsonl one JSON line per block event (candidate, submit outcome, settled/orphaned)
stats.json, ledger.json mirrors of the HTTP endpoints, rewritten atomically

Stats

GET /stats.json on stats-listen (also mirrored to data-dir/stats.json) is the document the pool UI reads: pool (pubkey, fee, window multiple, advertise address, uptime), node (height, tip hash, difficulty, tip age), window (target/total work, fill percent, per-miner work, shares, hashrate_ghs, share_percent, payout_sats at the current reward), clients (per gateway: generation, user agent, accepted/rejected, last reject reason), blocks, owed, totals, plus P2Block's workers and controls (see above). /ledger.json is the previous Prime's credits view for the UI's hashrate graph; /healthz returns ok.

Tests

cargo test                                  # wire (44), tides (15), primed (16)
cargo test --release -p primed --test replay_e2e -- --ignored --nocapture   # hostile gateway vs a real primed
scripts/regtest-e2e.sh convoy               # or fte | iohzrd | startos: real C gateway + real Knots on regtest
MINER_CMD='...' scripts/regtest-divergence.sh fte   # two nodes with different mempools and tips

replay_e2e starts a primed, speaks DATUM to it as a gateway would, grinds one genuine diff-1 share (~2^32 BLAKE2b hashes, 10–60 s across all cores) and replays it fourteen ways — job section flipped in an unread field, bare, on another slot, after a reconnect, from another key — asserting it is credited exactly once. PRIMED_BIN=/path/to/primed points the same attack at another build; against the pre-fix binary the first replay is accepted.

The unit tests pin the frame obfuscation, nonce derivation, hello round trip for both generations, configure v1/v3 byte layouts, coinbaser v2, pow submit parse/encode, tagged hashes, and share verification including grinding real BLAKE2b shares against an easy target.

The end-to-end script builds the named datum_gateway, points it at a primed on a local BLAKE2b regtest node, and checks that the handshake, configure, coinbaser, shares, block candidates and submitblock all happen. convoy, fte, iohzrd and startos all pass it at the heads in the table above, each reaching handshake, configure and coinbaser against a real Knots regtest node. startos-convoy fails on purpose: its pin predates Convoy's ABW flag, so it rejects the configure and the script says so. Earlier, a block found through a stock Convoy gateway paid the two-miner TIDES split on-chain exactly as issued (65.6% / 34.4% after the 0.5% fee), and the block the Prime assembled from the gateway's transaction reply was byte-identical to the one the gateway submitted. With the complete list, a Convoy-found block's coinbase carried the miner's 12.4375 and the pool's 0.0625 (0.5%) once each — no second pool output.

Template divergence

The e2e test runs the gateway and the Prime against the same node, so it cannot tell whether the block the Prime submits is the gateway's template or something the pool's node would have built. regtest-divergence.sh separates them: node A is the pool's node (the Prime submits there), node B is the gateway's node, a fresh datadir synced from A. It cuts the two apart and gives each transactions the other never sees (raw transactions signed by A's wallet but broadcast only to B; wallet sends on A), so getblocktemplate differs on the two nodes at the same height. It then mines through a stock fte gateway and asserts, from getblock on A:

  • the block A accepted contains every B-only transaction and none of A's own — the pool node accepted a block it could not have built, so the Prime reassembled the gateway's template byte-for-byte rather than substituting its node's view;
  • the coinbase carries the gateway's secondary tag (Lazarus␏divergence-gw) and the issued split (first block after start is pool-only, as the stock gateway publishes an empty coinbaser job until the first reply lands; the owed amount is recorded);
  • the gateway's own node B has the same block, so both sides agree without ever talking.

Then it drifts the tips. With B one block behind A (A mines a block B never hears about), the gateway's next solve is a competing block at A's tip height: the Prime accepts the share inside stale-grace-secs, A answers inconclusive (valid, not best), the 30 s confirm pass labels the record orphan:split, the next solve on B's branch reorgs A, and the label clears (back in the main chain, record split, settled). Finally A and B are reconnected and must agree. The whole run takes about a minute on regtest with a GPU miner.

Doing this by hand first surfaced three bugs, all fixed:

  • a record labelled orphan: was never re-checked, so a competing block that later won the reorg — exactly the lagging-gateway-node case above — stayed an orphan forever, and the pool UI read orphan state from the wrong field (submit rather than kind);
  • two block candidates solved on the same job (regtest does it every share; on mainnet it needs two solves of one job seconds apart) overwrote each other in the pending map, so the gateway's single transaction reply assembled and submitted only the later one. Every candidate for a job is now kept and submitted from the one transaction set;
  • when the gateway's node is two or more blocks ahead of the pool's node, every share is rejected as stale (correct: the Prime cannot verify work on a chain its node has not seen, and a stock gateway then reconnects every 30 s for lack of accepts). That is a lagging pool node, not a stale gateway, and it is now logged as such once a minute per session.

Design notes

  • Verification is cheap enough that every share is fully rebuilt and checked, always. The ignored verify_throughput test measures both paths on a single core: on a Ryzen 9950X3D, about 376k shares/s for a share carrying fresh job + coinbase sections against a 2000-tx template, and about 536k shares/s for later shares on the same job (cargo test --release -p datum-wire -- --ignored --nocapture verify_throughput). A whole pool's share flow fits on one core with orders of magnitude to spare.
  • One Tokio task per connection owns both halves of the socket; reads go through a cancel-safe growable buffer so select! over reads, tip broadcasts and keepalives can never desynchronise the frame stream.
  • The ledger is a Mutex held only for the microseconds a credit takes; nothing awaits under it. Coinbaser replies compute the split from a snapshot.
  • Block notify is fanned out with a broadcast channel; a tip change from the node poller or a block candidate from any session reaches every other gateway immediately.
  • Idle gateways get a zero-length INFO frame every 20 s (the client's global timeout is 60 s); a gateway silent for 300 s is dropped. Handshake must complete in 15 s.
  • Duplicate shares are caught by hash in one set shared by every session and keyed by block height. The hash commits to the job (prev, merkle, nBits, txcount, version) and the miner's nonces, so it is unique per height and needs no per-job scoping; nothing a gateway sends — a re-sent job section, a reconnect, a new key — can empty it. Housekeeping prunes heights below what the stale check still accepts, and at a hard cap new work is refused rather than old work forgotten. (Before 2026-09-06 the set was per session and per job and was cleared whenever the job section changed, which let one share be credited without limit.) Shares one height behind are accepted for stale-grace-secs after the tip moved, matching template refresh latency.
  • The coinbase check bounds miner outputs from both sides. An issued output may not be paid less than its share (scaled down when the template is worth less than the split assumed) and may not be paid more than Prime issued against its script (scaled up by the same ratio when the template is worth more, which is how lazarus-gateway rescales). The upper bound is what stops a gateway paying every miner exactly and sending the pool's remainder — fee, rounding, unplaced dust — to an address of its own choosing; it is held per script, not per identity, because two window identities may resolve to one scriptPubKey.
  • Identities are folded before interning: a bech32 address is lowercased (BIP 173 forbids mixed case, so this is safe and idempotent), base58 and non-addresses are kept byte-exact. One payout address is one TIDES row, however each rig's config cases it.
  • A gateway's txcount convention (does txn_count include the coinbase?) is learned from the first share that verifies on a job and pinned for the slot, so VerifiedShare::commitment is always the header the miner actually ground.
  • What a connection can make the Prime hold is bounded: max-connections (256) and max-connections-per-ip (8) at accept; a coinbase section over 20 000 bytes or a ninth coinbase id in a slot is refused; only the 16 most recently started job slots keep their sections; and session-coinbase-budget (4 MiB) caps the total. Coinbaser requests are token-bucketed (32, refilled one per second) because each one is a full split over the window under the ledger lock, and a session with 2 000 rejects or malformed messages in 10 s is dropped. Release builds keep overflow-checks on.
  • The Prime submits every candidate block to its own node as well as trusting the gateway to. duplicate from submitblock is the expected outcome and is recorded as such; inconclusive means a valid block that is not (yet) the best tip. Records settle when the node reports positive confirmations; a record called orphan: is re-checked for 100 blocks and un-labelled if a reorg brings it back.

License

MIT — see LICENSE, which also records provenance. This is not derived from Ratum (AGPL-3.0) and contains none of its code. The protocol was recovered from the MIT-licensed DATUM Gateway trees named there.

About

P2Block build of the Lazarus DATUM Prime (wavicles-prime): runtime controls (live fees, per-identity fee overrides, bans) and per-worker stats. Upstream: GaltRanch/wavicles-prime.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages