Engineering
Testing & interoperability
Test layers, canonical vectors, cross-language conformance, negative consensus cases, P2P and API contract testing, release checks, and disciplined protocol evolution.
This page documents the implemented alpha. Executable source, interoperability vectors, and tests remain authoritative if prose and code ever diverge.
What counts as protocol evidence
Luracoin’s specification has three layers, in descending order of executable authority:
- canonical serialization and validation code;
- shared vectors and boundary/negative tests;
- human documentation.
Documentation is centralized here for humans, but compatibility must be demonstrated with bytes and rejection behavior. If prose, fixtures, and implementation differ, treat that as a release-blocking defect—not permission to choose whichever result is convenient.
Test environments
Reference runtimes:
Python node >=3.10,<3.13
Wallet/site Node >=22.22.2
Redis reachable, isolated test database where integration tests require it
The Python package pins development-tool ranges in pyproject.toml; the JavaScript packages carry lockfiles. CI and local reproduction should install from those project manifests rather than globally guessed versions.
Standard checks
Python:
cd luracoin-python
uv sync --extra dev
uv run pytest
uv run flake8 luracoin tests
uv run mypy luracoin
Wallet:
cd luracoin-wallet
npm ci
npm run check
Website:
cd luracoin-website
npm ci
npm run check
npm run build
npm run test:e2e
Run the repository’s release checklist for an actual release. A targeted test during development does not replace the full suite for consensus or signing changes.
Shared transaction vector
vectors.json defines a public testnet transaction fixture shared by Python and JavaScript. Stable outputs include:
chain ID 1
private key byte 0x1f repeated 32 times (test only)
from address Lhic4rh3bPnjFMzReaoJDm291Sz23ZUmcu
unsigned length 85 bytes
public key raw length 64 bytes
signature length 64 bytes
unlock field length 128 bytes
complete length 213 bytes
transaction ID 2712bfd829c397e9fb32188bf705987115b1a2d180aac80ba28a2aa6edcbb9b6
The file also fixes the exact unsigned hex, single-SHA256 signing digest, raw x || y key, compact low-S r || s, unlocking field, and complete transaction hex.
This private key is intentionally public and must never fund anything of value. Test vectors are for deterministic conformance, not wallet examples.
Genesis vectors
The same vector file fixes educational genesis identity:
| Network | Chain ID | PoW nonce | Block ID |
|---|---|---|---|
| testnet | 1 | 14,121,310 | 0000009e2bcc8e56cee8b5e0fbdac9edc04513542af5c5791ea6165179c468bf |
| devnet | 2 | 11,943,016 | 00000033e4111007fd85423c7a527595e8efefe158a1250db62a32870857ef91 |
Initialization tests should reconstruct the canonical objects and verify these IDs, not simply insert the strings into local state.
Cross-language transaction conformance
Python and JavaScript must independently produce identical values for:
- mnemonic-to-seed behavior with BIP39 passphrase
LURA; - BIP32 root private/public key;
- compressed public key for address derivation;
- HASH160 and Base58Check address;
- unsigned field bounds and exact 85-byte order;
- SHA-256 signing digest;
- deterministic secp256k1 signature;
- low-S normalization;
- raw 64-byte public key and compact signature;
- exact 213-byte transaction;
- double-SHA256 transaction ID.
A JSON-level match is insufficient. Compare bytes at every stage so an endianness or encoding error is localized.
Negative signature suite
Acceptance tests need corresponding rejections:
- zero, negative-equivalent, or out-of-range
r/s; - high-S counterpart of an otherwise valid signature;
- DER signature where compact form is required;
- compressed key in the 64-byte raw-key slot;
- uncompressed SEC prefix included accidentally;
- public key not on secp256k1;
- public key whose derived address differs from sender;
- signature over JSON, hex text, txid, or double-SHA256 instead of the unsigned bytes;
- one-bit changes in every signed field;
- short, long, or trailing unlocking data.
The goal is canonical acceptance, not merely successful library verification.
Transaction state suite
Cover:
- exact minimum/maximum integer bounds;
- zero value rejection and zero fee acceptance;
- wrong chain ID;
- invalid zero destination sentinel;
- absent sender and insufficient balance;
- next nonce, replayed nonce, skipped nonce, and
uint32boundary; - self-transfer net fee behavior;
- receiver
uint64overflow; - contiguous multi-transaction sequence;
- Redis missing payload/reservation inconsistency;
- atomic same-slot races and exact idempotent retry;
- confirmation of a different transaction in the same sender/nonce slot;
- Redis outage failure semantics.
Tests should assert both return/error and resulting accounts/Redis keys. A rejection that leaves a reservation behind is still a bug.
Block conformance suite
Byte tests should fix:
- exact 118-byte transmitted header;
- declared-ID offset and omission from the hash preimage;
- transaction count insertion as little-endian
uint16; - big-endian timestamp amid little-endian numeric fields;
- ordered transaction commitment;
- exact size formula and every schedule boundary;
- trailing/unaligned/empty body rejection.
Context tests should cover:
- wrong height, predecessor, bits, proof, or version;
- ID exactly equal to target;
- missing difficulty history;
- timestamp equal to predecessor, +1, +500 future, and +501 future;
- missing, duplicate, misplaced, underpaying, and overpaying coinbase;
- coinbase nonce/miner/unlocking sentinel mismatch;
- duplicate transaction IDs;
- normal transaction spending earlier receipt;
- forbidden same-block spend of current coinbase;
- state overflow and all-or-nothing publication;
- stale mined template rejection.
Difficulty and emission suite
Test compact expansion/encoding as its own consensus codec. Include exponents below/at/above three, zero, >256-bit expansion, precision truncation, maximum target, and normalization.
Retarget fixtures need exact 480-block boundaries, both 4× clamps, values just inside/outside clamps, integer-floor cases, and absent history.
Reward fixtures need height zero manifest behavior, each 259,200-block era boundary, odd lurashi truncation, and the permanent one-LURA floor.
P2P protocol suite
For every command, test:
- exact 12-byte command encoding;
- correct payload and empty-payload checksum;
- wrong magic/checksum/length;
- partial header/body reads;
- global maximum and command-specific off-by-one values;
- unknown command and unknown inventory type;
- malformed count/length relationships;
- v1 14-byte versus v2 18-byte handshake;
- wrong protocol/chain/port and handshake order;
- post-handshake
version/verackrejection; - interleaved frames under a single dispatcher;
- concurrent send serialization and timeout cleanup.
Use real asyncio stream behavior in addition to unit mocks for framing-sensitive cases.
Synchronization and relay suite
Model honest, slow, silent, malformed, behind, height-lying, and disconnecting peers. Verify:
- one active sync peer;
- highest eligible advertised height selection;
- batches no larger than 50;
- strict next-height application;
- timeout and zero-progress failure;
- exponential host-based cooldown and cap;
- successful cooldown reset;
- best-peer failover;
- readiness transitions across task start/finish races;
- inventory-cache bounds and loop suppression;
- relay concurrency and partial success counts;
- 1,000-item backlog eviction, 100-item slices, and five-second budget;
- sender/nonce conflict pruning after a received block.
Explicitly test that equal heights with different tips do not magically converge; this documents the current limitation.
Persistence suite
Create temporary isolated data directories and verify:
- record prefix and exact location encoding;
- file rotation at 128 MiB boundaries without splitting a block;
- length-prefix/index disagreement failure;
- truncated record failure;
- concurrent read versus append lock behavior;
- adjacent append-before-index recovery at both possible positions;
- conflict fails closed;
- four-byte early-v2 location upgrade and batching;
- unknown location versions/sizes rejection;
- no cross-network state reuse;
- idempotent re-opening of initialized genesis.
Crash tests should inspect on-disk results, not only mocked method calls.
API contract suite
Private RPC tests should assert loopback rejection, token creation/load hardening, bearer comparison, request-size cap, string money, cursor behavior, readiness/Redis gates, error codes, and 202 receipt semantics.
Explorer tests should assert core operation without SQLite, capability truth, every index state, response models, page limits, before_height, ID/address validation, exact monetary strings, bounded block loading, CORS GET policy, and stable 503 errors.
Generate and diff OpenAPI intentionally when public models change.
Wallet suite
The wallet suite covers domain signing, encrypted vault round-trips and failures, RPC parsing, browser gateway bridge, history reconciliation, preferences, QR/payment URIs, and critical UI paths.
High-risk UI tests should include:
- password/mnemonic never displayed after expected transitions;
- incorrect password and corrupted authentication tag;
- lock on timer and platform events;
- no send while node not ready or Redis unavailable;
- BigInt amount validation and available-balance bounds;
- one pending outgoing transaction policy;
- transaction summary matches signed bytes;
- delete confirmation and vault removal;
- renderer cannot call arbitrary RPC/path/file operations.
Website suite
Static checks should cover every route at desktop/mobile widths, internal link integrity, documentation sidebar/search, heading anchors, explorer placeholder/live/error states, correct logo asset, wallet screenshot loading, no dark-theme regression, and accessible keyboard/focus behavior.
The build must succeed without a public explorer URL. A separate configuration test should build with a mock base origin.
Fuzzing priorities
High-value fuzz targets are:
- P2P 24-byte headers and all payload parsers;
- transaction and block deserializers;
- compact target codec;
- Base58Check/address inputs;
- RPC raw hex and cursors;
- explorer block/hash references and index adapters;
- block-location decoder and length-prefix reads;
- wallet payment URI and amount parsing.
Fuzzers should assert bounded resource use and clean rejection, not only absence of crashes.
Change classification
Before merging, label a change:
| Class | Examples | Required coordination |
|---|---|---|
| consensus | transaction/block bytes, validation, target, reward | new/updated vectors, migration/version plan, all clients |
| wire | P2P framing/payload/handshake | protocol version and cross-node fixtures |
| local API | RPC route/model/auth | wallet/gateway compatibility |
| public API | Explorer response/route | OpenAPI, frontend, compatibility notice |
| storage | RocksDB/file/index schema | crash tests, migration/rebuild/rollback plan |
| client | wallet UI without byte change | wallet tests and release packaging |
| presentation | website/docs | site build/E2E/content review |
Do not smuggle a consensus change inside a “refactor.” Serialized bytes and rejection sets are part of behavior.
Interoperability release gate
A new implementation or consensus release should not connect to a shared test network until it:
- matches every public vector byte-for-byte;
- passes shared negative fixtures;
- exchanges P2P v2 handshakes/messages with the Python node;
- reconstructs genesis IDs independently;
- validates and mines a controlled multi-block fixture chain;
- agrees on balances/nonces/IDs after every block;
- survives malformed and resource-boundary corpus tests;
- publishes its supported protocol/API/storage versions;
- documents any intentional incompatibility and requires a clean network/data namespace.
Agreement on a happy-path transaction alone is not interoperability.
Source anchors
Primary implementation files used for this chapter:
