Getting Started
CURS3D is a quantum-resistant Layer 1 blockchain written entirely in Rust from scratch. It is not a fork of any existing blockchain. Every component -- consensus, cryptography, networking, storage, smart contracts, and APIs -- is implemented from zero.
The project currently has 20 modules, 67 tests, and ships with a full REST API, block explorer, Docker support, and CI pipeline.
Quick Start
# Clone and build
git clone https://github.com/Pazificateur69/curs3d.git
cd curs3d
cargo build --release
# Create a wallet
./target/release/curs3d wallet --output my_wallet.json
# Start a node
./target/release/curs3d node --validator-wallet my_wallet.json
# Run all 67 tests
cargo test
Installation
Prerequisites
- Rust 1.94+ (edition 2024)
- A C compiler (for pqcrypto-dilithium native bindings)
- Git
Build Commands
cargo build --release # Build optimized binary
cargo test # Run all 67 tests
cargo clippy # Lint (must pass with no warnings)
cargo fmt --check # Format check
CLI: node
Start a full CURS3D node with optional validator mode, custom data directory, bootnodes, and genesis configuration.
curs3d node [OPTIONS]
| Flag | Default | Description |
|---|---|---|
| -p, --port | 4337 | P2P listen port |
| -d, --data-dir | curs3d_data | Data directory for sled storage |
| --validator-wallet | None | Path to wallet file (enables validator mode) |
| --bootnode | [] | Multiaddr of bootnodes (repeatable) |
| --rpc-addr | 127.0.0.1:9545 | TCP RPC listen address |
| --genesis-config | None | Path to genesis config JSON |
CLI: wallet
Create a new CURS3D wallet with CRYSTALS-Dilithium Level 5 keypair, encrypted with AES-256-GCM using an Argon2-derived key.
curs3d wallet [OPTIONS]
| Flag | Default | Description |
|---|---|---|
| -o, --output | wallet.json | Output file path for the encrypted wallet |
The wallet file is encrypted at rest. You will be prompted for a password. Addresses follow the format CUR + 40 hex characters (derived from SHA-3 of the public key, first 20 bytes).
CLI: info
Display wallet address and public key from an encrypted wallet file.
curs3d info --wallet my_wallet.json
| Flag | Default | Description |
|---|---|---|
| -w, --wallet | wallet.json | Path to wallet file |
CLI: send
Send CUR tokens to another address via RPC.
curs3d send --wallet sender.json --to CUR7f3a...9c2d --amount 100 --fee 1000
| Flag | Default | Description |
|---|---|---|
| -w, --wallet | wallet.json | Sender wallet file |
| -t, --to | required | Recipient address (CUR + 40 hex) |
| -a, --amount | required | Amount in CUR |
| -f, --fee | 1000 | Transaction fee (microtokens) |
| --data-dir | curs3d_data | Data directory |
| --rpc-addr | 127.0.0.1:9545 | RPC address |
CLI: stake
Stake CUR tokens to become a validator. Minimum stake: 1,000 CUR (1,000,000,000 microtokens).
curs3d stake --wallet validator.json --amount 1000 --fee 1000
| Flag | Default | Description |
|---|---|---|
| -w, --wallet | wallet.json | Wallet file |
| -a, --amount | required | Stake amount in CUR |
| -f, --fee | 1000 | Transaction fee (microtokens) |
| --data-dir | curs3d_data | Data directory |
| --rpc-addr | 127.0.0.1:9545 | RPC address |
CLI: status
Query the chain status from a running node via RPC.
curs3d status --data-dir curs3d_data
| Flag | Default | Description |
|---|---|---|
| -s, --data-dir | curs3d_data | Data directory |
| --rpc-addr | None | Optional RPC address for remote status |
Architecture: 20 Modules
CURS3D is organized into the following module structure:
src/
main.rs # CLI entry point (clap)
lib.rs # Module declarations
api/mod.rs # HTTP REST API (hyper 1.x, port 8080)
consensus/mod.rs # BFT PoS, FinalityVote, FinalityTracker, slashing
core/
block.rs # BlockHeader, Block, genesis, signatures
blocktree.rs # BlockTree, fork choice, pruning
chain.rs # Blockchain struct, state, validation, reorg
receipt.rs # Transaction receipts with logs
transaction.rs # Transaction, TransactionKind (6 types)
crypto/
dilithium.rs # CRYSTALS-Dilithium Level 5
hash.rs # SHA-3, double_hash, merkle_root
network/mod.rs # libp2p P2P, Gossipsub, mDNS, sync
rpc/mod.rs # TCP JSON RPC (port 9545)
storage/mod.rs # sled DB with schema versioning
vm/
mod.rs # WASM VM (Wasmer), contract execution
gas.rs # Gas cost constants (15 operations)
state.rs # ContractState (per-contract storage)
wallet/mod.rs # AES-256-GCM + Argon2 encrypted wallets
State Model
CURS3D uses an account-based state model. Every address has an AccountState:
pub struct AccountState {
pub balance: u64,
pub nonce: u64,
pub staked_balance: u64,
pub public_key: Option<Vec<u8>>,
}
Every block contains a state root -- the Merkle root of all accounts and contract storage. Block hashes use double-SHA3: sha3(sha3(bincode(header))).
Addresses are 20 bytes internally, displayed as CUR + 40 hex characters. The address is derived from SHA-3(public_key)[0..20].
Smart Contracts: WASM VM
CURS3D runs a deterministic WebAssembly virtual machine powered by the Wasmer runtime. Contracts are compiled to WASM and deployed on-chain.
Transaction Types
- DeployContract -- Deploy WASM bytecode. The
datafield contains the bytecode. A new contract address is derived from the deployer and nonce. - CallContract -- Call a deployed contract. The
tofield is the contract address. Thedatafield contains the function selector and arguments.
Host Functions
Contracts can call the following host functions provided by the VM:
storage_read(key_ptr, key_len)-- Read from contract storage (200 gas)storage_write(key_ptr, key_len, val_ptr, val_len)-- Write to contract storage (5,000 gas)emit_log(data_ptr, data_len)-- Emit a log entry (375 gas)get_caller()-- Get the caller addressget_input()-- Get the input dataset_return(data_ptr, data_len)-- Set the return data
Contract Storage
Each contract has its own key-value storage namespace. Keys and values are arbitrary byte arrays. Storage is persisted in the sled database and included in the state root calculation.
Gas Model
Every operation in the VM consumes gas. The block gas limit is 10,000,000 (configurable in genesis). Base fee follows an EIP-1559-style adjustment mechanism.
| Operation | Gas Cost | Constant |
|---|---|---|
| GAS_BASE_TX | 21,000 | Base cost per transaction |
| GAS_DEPLOY | 32,000 | Additional cost for contract deployment |
| GAS_CALL | 2,600 | Additional cost for contract call |
| GAS_STORAGE_READ | 200 | Read from contract storage |
| GAS_STORAGE_WRITE | 5,000 | Write to contract storage |
| GAS_LOG | 375 | Emit a log entry |
| GAS_PER_BYTE | 16 | Per byte of transaction data |
| GAS_MEMORY_READ_BYTE | 3 | Read byte from WASM memory |
| GAS_MEMORY_WRITE_BYTE | 6 | Write byte to WASM memory |
| GAS_HOST_CALL_OVERHEAD | 40 | Overhead per host function call |
| GAS_WASM_DEFAULT_OP | 2 | Default WASM instruction cost |
| GAS_WASM_MEMORY_OP | 8 | Memory-related WASM ops |
| GAS_WASM_CONTROL_OP | 4 | Control flow WASM ops |
| GAS_WASM_CALL_OP | 12 | WASM call/call_indirect |
| GAS_WASM_NUMERIC_OP | 3 | Numeric WASM ops |
Transaction Receipts
Every transaction that executes produces a Receipt containing:
- status -- Success or failure with error message
- gas_used -- Total gas consumed
- logs -- Array of LogEntry objects (contract_id, data)
- return_data -- Bytes returned by the contract (if any)
Receipts are stored alongside the block and accessible via the REST API when querying transactions.
Consensus: BFT Proof of Stake
CURS3D uses Byzantine Fault Tolerant Proof of Stake consensus. The protocol guarantees safety as long as fewer than 1/3 of the staked tokens are controlled by malicious actors.
Validator Selection
Validators are selected from the epoch snapshot -- a frozen set of validators captured at epoch boundaries. Selection is stake-weighted and deterministic: given the same block height and epoch snapshot, every node computes the same proposer.
Finality
Blocks achieve finality through the FinalityVote and CheckpointVote system. When 2/3 of the stake in the current epoch votes for a block, it becomes finalized. Finalized blocks cannot be reverted.
Epoch Snapshots
The validator set is frozen at epoch boundaries (every 32 blocks by default). This ensures consistent proposer selection within an epoch, even if stake changes occur mid-epoch.
Each EpochSnapshot records the active validators and their stakes at the start of the epoch. The snapshot is used for all block production and finality voting within that epoch.
Provable Slashing
If a validator signs two different blocks at the same height (equivocation), anyone can submit an EquivocationEvidence containing both signed block headers.
- The evidence is cryptographically verified using the validator's Dilithium public key
- 33% of the validator's stake is slashed (burned)
- The validator is jailed for a configurable duration (default: 64 blocks)
- Jailed validators cannot propose blocks or vote during the jail period
Fork Choice
CURS3D uses a BlockTree that tracks all known blocks, including forks. The fork choice rule selects the heaviest chain by cumulative proposer stake.
- Automatic reorg when a heavier fork is detected
- Finality boundary prevents reorganizations below finalized height
- Non-canonical branches are pruned after finalization
- Common ancestor calculation for efficient chain comparison
Cryptography
Signatures
All signatures use CRYSTALS-Dilithium Level 5 (NIST FIPS 204), the highest security level of the NIST post-quantum digital signature standard. This includes block signatures, transaction signatures, and finality votes.
Hashing
SHA-3 Keccak-256 is used for all hashing operations. Blocks use double-hashing: sha3(sha3(bincode(header))). Merkle trees are computed over accounts and contract storage for the state root.
Wallet Encryption
Wallet files at rest are encrypted with AES-256-GCM. The encryption key is derived from the user's password using Argon2 (memory-hard KDF). Legacy plaintext wallets are auto-migrated to encrypted format on first load.
Networking
CURS3D uses libp2p for peer-to-peer communication with the following components:
- Gossipsub -- Publish/subscribe protocol for broadcasting blocks, transactions, and votes
- mDNS -- Automatic local peer discovery
- Bootnodes -- Configurable bootstrap nodes for initial connection
Network Messages
The following message types are exchanged over the network:
NewBlock-- Broadcast a newly produced blockNewTransaction-- Broadcast a new transactionRequestBlocks/BlockResponse-- Block sync (batches of 50)HeightAnnounce-- Signed height announcement (every 30s) with protocol versionSlashingEvidence-- Broadcast equivocation proofFinalityVote-- Validator finality attestationRequestSnapshot/SnapshotManifest/SnapshotChunk-- State sync
Sync Protocol
Blocks are synced in batches of 50 with a 15-second timeout and 3 retries. Block deduplication prevents processing the same block twice. Incompatible peers (different protocol version) are detected and disconnected.
Storage
CURS3D uses sled as its embedded key-value database with schema-versioned storage and automatic migration support.
The following data is persisted:
- Blocks (by height and hash)
- Account states
- Contract storage (per-contract key-value namespaces)
- Pending transactions
- Equivocation evidence
- Chain metadata (height, genesis hash, schema version, genesis config)
REST API
The HTTP REST API runs on port 8080 with full CORS support. All responses follow the format:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": "message" }
GET /api/status
Returns chain status including chain_id, chain_name, epoch, epoch_start_height, height, finalized_height, latest_hash, genesis_hash, pending_transactions, active_validators, and protocol_version.
curl http://localhost:8080/api/status
GET /api/blocks?from=0&limit=20
Returns recent blocks with pagination. Maximum limit is 100.
curl "http://localhost:8080/api/blocks?from=0&limit=20"
GET /api/block/:height
Returns full block detail including all transactions, state root, and merkle root.
curl http://localhost:8080/api/block/42
GET /api/account/:address
Returns balance, nonce, and staked_balance for any address. Accepts addresses with or without the CUR prefix.
curl http://localhost:8080/api/account/CUR7f3a1b2c...9c2d
GET /api/tx/:hash
Look up a transaction by its hex-encoded hash. Returns kind, from, to, amount, fee, and receipt.
GET /api/pending
Returns all pending transactions in the mempool.
GET /api/validators
Returns the active validator set with address, public_key, and stake for each validator.
GET /api/faucet/:address
Testnet faucet endpoint. Behavior depends on chain configuration (may be disabled in favor of genesis allocations).
POST /api/tx/submit
Submit a signed transaction as a JSON body. Supports optional Authorization header for auth-token-gated access. Maximum body size: 1 MB.
curl -X POST http://localhost:8080/api/tx/submit \
-H "Content-Type: application/json" \
-d '{"chain_id":"curs3d-devnet","kind":"Transfer",...}'
Genesis Configuration
The genesis block is configured via a JSON file passed to the node with --genesis-config. All fields have sensible defaults.
| Field | Default | Description |
|---|---|---|
| chain_id | "curs3d-devnet" | Unique chain identifier |
| chain_name | "curs3d-devnet" | Human-readable chain name |
| block_reward | 50,000,000 | Block reward in microtokens (50 CUR) |
| minimum_stake | 1,000,000,000 | Min stake in microtokens (1000 CUR) |
| unstake_delay_blocks | 10 | Blocks before unstake completes |
| epoch_length | 32 | Blocks per epoch |
| jail_duration_blocks | 64 | Blocks a slashed validator is jailed |
| block_gas_limit | 10,000,000 | Max gas per block |
| initial_base_fee_per_gas | 1 | Starting base fee |
| base_fee_change_denominator | 8 | Base fee adjustment rate |
| allocations | [] | Initial balance and stake allocations |
| upgrades | [] | Protocol upgrade schedule |
{
"chain_id": "curs3d-devnet",
"chain_name": "My Local Chain",
"block_reward": 50000000,
"minimum_stake": 1000000000,
"epoch_length": 32,
"block_gas_limit": 10000000,
"allocations": [
{
"public_key": "<hex_encoded_dilithium5_pk>",
"balance": 100000000000,
"staked_balance": 1000000000
}
],
"upgrades": [
{
"height": 1000,
"version": 2,
"description": "Enable feature X"
}
]
}
State Sync
New nodes can fast-sync from a checkpoint instead of replaying the entire chain history.
- SnapshotManifest -- Describes a state snapshot: height, state root, chunk count, and chunk hashes
- StateChunks -- Individual pieces of the state, each with a Merkle proof for verification
- The syncing node requests the manifest, then downloads and verifies each chunk
- Once all chunks are verified against the state root, the node resumes normal sync from that height
Protocol Governance
CURS3D supports protocol versioning with upgrade-at-height activation.
- Each
ProtocolUpgradespecifies aheight,version, anddescription - Upgrades are defined in the genesis config and activate automatically at the specified height
- Nodes running an older protocol version are detected via
HeightAnnouncemessages and warned - The current protocol version is reported in the
/api/statusresponse
Docker
CURS3D ships with a multi-stage Dockerfile and docker-compose configuration for running multi-node networks.
# Build the Docker image
docker build -t curs3d .
# Run a 2-node network with docker-compose
docker-compose up
# Or run a single node
docker run -p 4337:4337 -p 8080:8080 -p 9545:9545 curs3d \
curs3d node --validator-wallet /data/wallet.json
The docker-compose setup creates two nodes that automatically discover each other via mDNS and begin syncing.
CI Pipeline
GitHub Actions runs on every push and pull request:
cargo check-- Compilation checkcargo test-- All 67 testscargo clippy-- Zero warnings requiredcargo fmt --check-- Format verification