diff --git a/docs/architecture.md b/docs/architecture.md index 5abf289..786b4c3 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,595 +1,265 @@ -# Architecture - -go-blockchain is a pure Go implementation of the Lethean blockchain protocol. It -provides chain configuration, core cryptographic data types, consensus-critical -wire serialisation, and difficulty adjustment for the Lethean CryptoNote/Zano-fork -chain. - -Module path: `forge.lthn.ai/core/go-blockchain` - +--- +title: Architecture +description: Package structure, dependency graph, CGo boundary, and core data structures. --- -## Package Structure +# Architecture + +## Dependency Graph ``` -config/ Chain parameters (mainnet/testnet), hardfork schedule -types/ Core data types: Hash, PublicKey, Address, Block, Transaction -wire/ Binary serialisation (CryptoNote varint encoding) -difficulty/ PoW + PoS difficulty adjustment (LWMA variant) -crypto/ CGo bridge to vendored C++ libcryptonote (keys, signatures, proofs) -p2p/ CryptoNote P2P command types (handshake, sync, relay) -rpc/ Daemon JSON-RPC 2.0 client (12 endpoints) -chain/ Chain storage, indexing, and sync client (go-store backed) -consensus/ Three-layer block/transaction validation (structural, economic, crypto) -wallet/ Wallet core: key management, scanning, signing, TX construction -mining/ Solo PoW miner (daemon RPC, RandomX nonce grinding) + +------------+ + | consensus | + +------+-----+ + | + +------------+------------+ + | | + +-----+-----+ +-----+-----+ + | chain | | difficulty | + +-----+------+ +-----------+ + | + +--------+---------+ + | | | ++----+--+ +---+---+ +---+---+ +| p2p | | rpc | | wallet| ++----+--+ +---+---+ +---+---+ + | | | + +--------+---------+ + | + +----+----+ + | wire | + +----+----+ + | + +-------+-------+ + | | + +---+---+ +-----+-----+ + | types | | config | + +---+---+ +-----------+ + | + +---+---+ + | crypto | <-- CGo boundary + +--------+ ``` +### Key Relationships + +- **config** and **types** are leaf packages with no internal dependencies (stdlib only). +- **wire** depends on **types** for struct definitions and **config** for version-specific serialisation rules. +- **crypto** wraps the C++ library via CGo; it is used by **wire** (for hashing), **chain** (for signature verification), and **wallet** (for key derivation and tx signing). +- **p2p**, **rpc**, and **wallet** are higher-level packages that depend on wire-level serialisation. +- **chain** is the central coordinator: it validates blocks using **consensus** rules, adjusts **difficulty**, and stores state. +- **consensus** is standalone -- no dependency on **chain** or any storage layer. All functions are pure: they take types, config, and height, returning errors. + +## Package Details + ### config/ -Defines every consensus-critical constant for the Lethean chain, derived directly -from the canonical C++ source files `currency_config.h.in` and `default.cmake`. -Constants cover tokenomics, address prefixes, network ports, difficulty parameters, -block and transaction limits, version numbers, PoS parameters, P2P constants, -network identity, currency identity, and alias rules. +Every consensus-critical constant, derived from the C++ `currency_config.h.in` and `default.cmake`. The `ChainConfig` struct aggregates all parameters: -The `ChainConfig` struct aggregates all parameters into a single value. -Pre-populated `Mainnet` and `Testnet` variables are provided as package-level -globals. The hardfork schedule is defined in `hardfork.go` with lookup functions -for querying active versions at any block height. +```go +// Pre-populated globals for each network. +var Mainnet = config.ChainConfig{ + Name: "Lethean", + Abbreviation: "LTHN", + IsTestnet: false, + Coin: 1_000_000_000_000, // 10^12 atomic units + BlockReward: 1_000_000_000_000, // 1 LTHN per block + P2PPort: 36942, + RPCPort: 36941, + // ... all other parameters +} +``` + +The hardfork schedule is defined separately with lookup functions: + +```go +version := config.VersionAtHeight(config.MainnetForks, height) +active := config.IsHardForkActive(config.MainnetForks, config.HF4Zarcanum, height) +``` ### types/ -Fixed-size byte array types matching the CryptoNote specification: - -- `Hash` (32 bytes) -- Keccak-256 hash values -- `PublicKey` (32 bytes) -- Ed25519 public keys -- `SecretKey` (32 bytes) -- Ed25519 secret keys -- `KeyImage` (32 bytes) -- double-spend detection images -- `Signature` (64 bytes) -- cryptographic signatures - -Also contains the full address encoding/decoding implementation (CryptoNote -base58 with Keccak-256 checksums), block header and block structures, and all -transaction types across versions 0 through 3. - -### wire/ - -Consensus-critical binary serialisation for blocks, transactions, and all wire -primitives. All encoding is bit-identical to the C++ reference implementation. - -**Primitives:** -- `Encoder` / `Decoder` -- sticky-error streaming codec (call `Err()` once) -- `EncodeVarint` / `DecodeVarint` -- 7-bit LEB128 with MSB continuation -- `Keccak256` -- pre-NIST Keccak-256 (CryptoNote's `cn_fast_hash`) - -**Block serialisation:** -- `EncodeBlockHeader` / `DecodeBlockHeader` -- wire order: major, nonce(LE64), - prev_id, minor(varint), timestamp(varint), flags -- `EncodeBlock` / `DecodeBlock` -- header + miner_tx + tx_hashes -- `BlockHashingBlob` -- serialised header || tree_root || varint(tx_count) -- `BlockHash` -- Keccak-256 of varint(len) + block hashing blob - -**Transaction serialisation (v0/v1):** -- `EncodeTransactionPrefix` / `DecodeTransactionPrefix` -- version-dependent - field ordering (v0/v1: version, vin, vout, extra; v2+: version, vin, extra, vout) -- `EncodeTransaction` / `DecodeTransaction` -- prefix + signatures + attachment -- All variant tags match `SET_VARIANT_TAGS` from `currency_basic.h` -- Extra/attachment stored as raw wire bytes for bit-identical round-tripping - -**Hashing:** -- `TreeHash` -- CryptoNote Merkle tree (direct port of `crypto/tree-hash.c`) -- `TransactionPrefixHash` -- Keccak-256 of serialised prefix -- `TransactionHash` -- Keccak-256 of full serialised transaction - -### difficulty/ - -LWMA (Linear Weighted Moving Average) difficulty adjustment algorithm for both -PoW and PoS blocks. Examines a window of recent block timestamps and cumulative -difficulties to calculate the next target difficulty. - -### p2p/ - -CryptoNote P2P protocol command types for peer-to-peer communication. This -package provides encode/decode for all Levin protocol commands, built on the -`node/levin/` sub-package in go-p2p. - -The package depends on `forge.lthn.ai/core/go-p2p/node/levin` for the Levin -wire format (33-byte header, portable storage serialisation, framed TCP -connections) and defines the application-level command semantics: - -- **handshake.go** -- COMMAND_HANDSHAKE (1001): NodeData (network ID, peer ID, - local time, port) + CoreSyncData exchange. Peerlist decoding from packed - 24-byte entries. -- **timedsync.go** -- COMMAND_TIMED_SYNC (1002): periodic blockchain state sync. -- **ping.go** -- COMMAND_PING (1003): simple liveness check. -- **relay.go** -- Block relay (2001), transaction relay (2002), chain - request/response (2006/2007). `BlockCompleteEntry` for block + transaction - blob pairs. `BlockContextInfo` for chain entry response objects. -- **getobjects.go** -- `RequestGetObjects` (2003) and `ResponseGetObjects` - (2004) types for fetching blocks by hash during P2P sync. -- **sync.go** -- CoreSyncData type (current_height, top_id, checkpoint, - core_time, client_version, pruning mode). -- **commands.go** -- Command ID re-exports from the levin package. -- **integration_test.go** -- Build-tagged (`//go:build integration`) test that - TCP-connects to the C++ testnet daemon on localhost:46942 and performs a full - handshake + ping exchange. - -The Levin wire format in go-p2p includes: -- **node/levin/header.go** -- 33-byte packed header with signature validation. -- **node/levin/varint.go** -- Portable storage varint (2-bit size mark, NOT the - same as CryptoNote LEB128 varints in wire/). -- **node/levin/storage.go** -- Portable storage section encode/decode (epee KV - format with 12 type tags). -- **node/levin/connection.go** -- Framed TCP connection with header + payload - read/write. - -### rpc/ - -Typed JSON-RPC 2.0 client for querying the Lethean daemon. The `Client` struct -wraps `net/http` and provides Go methods for 11 core daemon endpoints. - -Eight endpoints use JSON-RPC 2.0 via `/json_rpc`. Two endpoints (`GetHeight`, -`GetTransactions`) use legacy JSON POST to dedicated URI paths (`/getheight`, -`/gettransactions`), as the C++ daemon registers these with `MAP_URI_AUTO_JON2` -rather than `MAP_JON_RPC`. - -**Client transport:** -- `client.go` -- `Client` struct with `call()` (JSON-RPC 2.0) and `legacyCall()` - (plain JSON POST). `RPCError` type for daemon error codes. -- `types.go` -- `BlockHeader`, `DaemonInfo`, `BlockDetails`, `TxInfo` shared types. - -**Endpoints:** -- `info.go` -- `GetInfo`, `GetHeight` (legacy), `GetBlockCount`. -- `blocks.go` -- `GetLastBlockHeader`, `GetBlockHeaderByHeight`, - `GetBlockHeaderByHash`, `GetBlocksDetails`. -- `transactions.go` -- `GetTxDetails`, `GetTransactions` (legacy). -- `mining.go` -- `GetBlockTemplate`, `SubmitBlock`. - -**Wallet endpoints:** -- `wallet.go` -- `GetRandomOutputs` (for ring decoy selection via `/getrandom_outs1`) - and `SendRawTransaction` (for transaction submission via `/sendrawtransaction`). - -**Testing:** -- Mock HTTP server tests for all endpoints and error paths. -- Build-tagged integration test (`//go:build integration`) against C++ testnet - daemon on `localhost:46941`. Verifies genesis block hash matches Phase 1 - result (`cb9d5455...`). - -### chain/ - -Stores and indexes the Lethean blockchain by syncing from a C++ daemon via RPC. -Uses go-store (pure-Go SQLite) for persistence with five storage groups mapping -to the C++ daemon's core containers. - -**Storage schema:** -- `blocks` -- blocks by height (zero-padded key), JSON metadata + hex blob. -- `block_index` -- hash-to-height reverse index. -- `transactions` -- tx hash to JSON metadata + hex blob. -- `spent_keys` -- key image to block height (double-spend index). -- `outputs:{amount}` -- global output index per amount. - -**Core operations:** -- `chain.go` -- `Chain` struct with `New()`, `Height()`, `TopBlock()`. -- `store.go` -- `PutBlock`, `GetBlockByHeight/Hash`, `PutTransaction`, - `GetTransaction`, `HasTransaction`. -- `index.go` -- `MarkSpent`, `IsSpent`, `PutOutput`, `GetOutput`, `OutputCount`. -- `validate.go` -- Header validation (previous block linkage, height sequence, - block size). -- `sync.go` -- `Sync(client)` blocking RPC poll loop. Fetches blocks in batches - of 10, decodes wire blobs, validates headers, indexes transactions/outputs/ - key images, verifies block hashes. `processBlockBlobs()` shared validation - path for both RPC and P2P sync. `resolveBlockBlobs()` reconstructs block data - from daemon JSON responses. Context cancellation and progress logging for - long syncs. - -**P2P sync:** -- `P2PConnection` interface abstracting peer communication (request/response - over any transport). -- `LevinP2PConn` adapter wrapping a Levin TCP connection to satisfy - `P2PConnection`. -- `P2PSync()` state machine running the REQUEST_CHAIN / REQUEST_GET_OBJECTS - loop until the peer has no more blocks. -- `SparseChainHistory()` builds exponentially-spaced block hash lists matching - the C++ `get_short_chain_history()` algorithm. -- `GetRingOutputs()` callback for ring signature verification during sync. - -**Testing:** -- Unit tests with go-store `:memory:` for all CRUD operations and validation. -- Mock RPC server sync tests. -- Build-tagged integration test (`//go:build integration`) syncing first 10 - blocks from C++ testnet daemon on `localhost:46941`. -- P2P sync integration test against C++ testnet daemon on `localhost:46942`. - -### consensus/ - -Standalone consensus validation package implementing three-layer block and -transaction verification. All functions are pure — they take types, config, -and height, returning errors. No dependency on `chain/` or any storage layer. - -**Layer 1 — Structural (no crypto):** -- `ValidateTransaction()` — 8 ordered checks matching C++ `validate_tx_semantic()`: - blob size, input count, input types, output validation, money overflow, - key image uniqueness, extra parsing, balance check. - -**Layer 2 — Economic:** -- `BaseReward()` / `BlockReward()` / `MinerReward()` — fixed 1 LTHN/block with - size penalty using 128-bit arithmetic (`math/bits.Mul64`). Pre-HF4 fees go to - miner; post-HF4 fees are burned. -- `TxFee()` — fee extraction with overflow detection. -- `ValidateBlockReward()` — miner output sum vs expected reward. - -**Layer 3 — Cryptographic (CGo):** -- `CheckDifficulty()` / `CheckPoWHash()` — 256-bit PoW hash comparison via - RandomX (vendored in `crypto/randomx/`, key `"LetheanRandomXv1"`). -- `VerifyTransactionSignatures()` — NLSAG (pre-HF4) and CLSAG (post-HF4) - signature verification. `verifyV1Signatures()` performs real NLSAG ring - signature verification via CGo, using a `RingOutputsFn` callback to fetch - ring member public keys from chain storage. - -**Block validation:** -- `CheckTimestamp()` — future time limit (7200s PoW, 1200s PoS) + median-of-60. -- `ValidateMinerTx()` — coinbase structure (1 input = PoW, 2 inputs = PoS). -- `ValidateBlock()` — orchestrator combining timestamp, miner tx, and reward. -- `IsPoS()` — flags bit 0 check. - -All validation is hardfork-aware via `config.IsHardForkActive()` with the fork -schedule passed as a parameter. The `chain/sync.go` integration calls -`ValidateMinerTx()` and `ValidateTransaction()` during sync, with optional -signature verification via `SyncOptions.VerifySignatures`. - -### wallet/ - -Full send+receive wallet for the Lethean blockchain. Uses interface-driven design -with four core abstractions so v1 (NLSAG) implementations ship now and v2+ -(Zarcanum/CLSAG) slot in later without changing callers. - -**Core interfaces:** -- `Scanner` -- detects outputs belonging to a wallet using ECDH derivation. - `V1Scanner` implements v0/v1 output detection. -- `Signer` -- produces ring signatures for transaction inputs. `NLSAGSigner` - wraps the CGo NLSAG ring signature primitives. -- `Builder` -- constructs signed transactions. `V1Builder` handles v1 - transactions with decoy ring selection and ECDH output derivation. -- `RingSelector` -- picks decoy outputs for ring signatures. - `RPCRingSelector` fetches random outputs from the daemon via RPC. - -**Account management:** -- `account.go` -- `Account` struct with spend and view key pairs. Key - derivation: `viewSecret = sc_reduce32(Keccak256(spendSecret))`, matching - the C++ `account_base::generate()` pattern. Three creation methods: - `GenerateAccount()`, `RestoreFromSeed()`, `RestoreViewOnly()`. -- `mnemonic.go` -- 25-word CryptoNote mnemonic encoding/decoding using the - Electrum 1626-word dictionary. CRC32 checksum word. -- Persistence with Argon2id (time=3, mem=64MB, threads=4) + AES-256-GCM - encryption via go-store. - -**Transaction extra:** -- `extra.go` -- minimal parser for three wallet-critical variant tags: tx - public key (tag 22), unlock time (tag 14), derivation hint (tag 11). - Unknown tags skipped. Raw bytes preserved for round-tripping. - -**Wallet orchestrator:** -- `wallet.go` -- `Wallet` struct tying scanning, building, and sending. - `Sync()` scans from last checkpoint to chain tip, detecting owned outputs - and spent key images. `Balance()` returns confirmed vs locked amounts. - `Send()` performs largest-first coin selection, builds, signs, and submits - transactions via RPC. `Transfers()` lists all tracked outputs. - -**Transfer storage:** -- `transfer.go` -- `Transfer` struct persisted as JSON in go-store group - `transfers`, keyed by key image hex. `IsSpendable()` checks spend status, - coinbase maturity (MinedMoneyUnlockWindow=10), and unlock time. - -**Testing:** -- Unit tests with go-store `:memory:` for all components. -- Build-tagged integration test (`//go:build integration`) syncing from - C++ testnet daemon and verifying balance/transfers. - -### mining/ - -Solo PoW miner that talks to a C++ daemon via JSON-RPC. Single-threaded -mining loop: fetches block templates, computes a header mining hash once -per template, then grinds nonces with RandomX until a solution is found -or the chain advances. - -**Core types:** -- `Config` -- daemon URL, wallet address, poll interval, callbacks. -- `Miner` -- mining loop with `Start(ctx)` (blocking) and `Stats()` (lock-free). -- `TemplateProvider` -- interface satisfied by `rpc.Client` for testability. - -**Mining flow:** -1. `GetBlockTemplate(walletAddr)` -- fetches template from daemon. -2. `HeaderMiningHash(block)` -- Keccak-256 of `BlockHashingBlob` with nonce=0. -3. Nonce loop: `RandomXHash("LetheanRandomXv1", headerHash || nonce_LE)`. -4. `CheckDifficulty(powHash, difficulty)` -- solution found? -5. `SubmitBlock(hexBlob)` -- submits the solved block. - -**Template refresh:** polls `GetInfo()` every `PollInterval` (default 3s) to -detect new blocks. Re-fetches the template when the chain height advances. - -**Testing:** Mock `TemplateProvider` for unit tests. Build-tagged integration -test against C++ testnet daemon on `localhost:46941`. - -### tui/ — Terminal Dashboard - -Model library for the blockchain TUI dashboard: - -| File | Purpose | -|------|---------| -| `node.go` | Node wrapper — polls chain for status snapshots | -| `status_model.go` | StatusModel (FrameModel) — chain sync header bar | -| `explorer_model.go` | ExplorerModel (FrameModel) — block list / block detail / tx detail | -| `keyhints_model.go` | KeyHintsModel (FrameModel) — context-sensitive key hints | -| `messages.go` | Custom bubbletea message types | - -### cmd/chain/ — TUI Binary - -Thin wiring: creates Node, StatusModel, ExplorerModel, KeyHintsModel, wires into -core/cli Frame ("HCF" layout), starts P2P sync in background, runs Frame. - ---- - -## Key Types - -### ChainConfig +Fixed-size byte arrays matching the CryptoNote specification: ```go -type ChainConfig struct { - Name string - Abbreviation string - IsTestnet bool - CurrencyFormationVersion uint64 - Coin uint64 - DisplayDecimalPoint uint8 - BlockReward uint64 - DefaultFee uint64 - MinimumFee uint64 - Premine uint64 - AddressPrefix uint64 - IntegratedAddressPrefix uint64 - AuditableAddressPrefix uint64 - AuditableIntegratedAddressPrefix uint64 - P2PPort uint16 - RPCPort uint16 - StratumPort uint16 - DifficultyPowTarget uint64 - DifficultyPosTarget uint64 - DifficultyWindow uint64 - DifficultyLag uint64 - DifficultyCut uint64 - DifficultyPowStarter uint64 - DifficultyPosStarter uint64 - MaxBlockNumber uint64 - TxMaxAllowedInputs uint64 - TxMaxAllowedOutputs uint64 - DefaultDecoySetSize uint64 - HF4MandatoryDecoySetSize uint64 - MinedMoneyUnlockWindow uint64 - P2PMaintainersPubKey string -} +type Hash [32]byte // Keccak-256 hash +type PublicKey [32]byte // Ed25519 public key +type SecretKey [32]byte // Ed25519 secret key +type KeyImage [32]byte // Double-spend detection +type Signature [64]byte // Cryptographic signature ``` -Pre-populated globals `Mainnet` and `Testnet` contain the complete parameter -sets for each network. Mainnet uses ports 36940-36942; testnet uses 46940-46942. +All types provide `String()` (hex encoding), `IsZero()`, and `FromHex()` methods. -### HardFork - -```go -type HardFork struct { - Version uint8 - Height uint64 - Mandatory bool - Description string -} -``` - -Seven hardfork versions are defined (HF0 through HF6). On mainnet, HF0 is -active from genesis, HF1 and HF2 activate after block 10,080, and HF3 through -HF6 are scheduled at height 999,999,999 (effectively future). On testnet, most -forks activate early for testing. - -### Address - -```go -type Address struct { - SpendPublicKey PublicKey - ViewPublicKey PublicKey - Flags uint8 -} -``` - -Four address types are supported via distinct prefixes: - -| Type | Prefix | Leading chars | -|------|--------|---------------| -| Standard | `0x1eaf7` | `iTHN` | -| Integrated | `0xdeaf7` | `iTHn` | -| Auditable | `0x3ceff7` | `iThN` | -| Auditable integrated | `0x8b077` | `iThn` | - -### Block and Transaction +### Block Structure ```go type BlockHeader struct { - MajorVersion uint8 - Nonce uint64 - PrevID Hash - MinorVersion uint64 // varint on wire - Timestamp uint64 // varint on wire - Flags uint8 + MajorVersion uint8 // Consensus rules version (0, 1, 2, 3) + Nonce uint64 // PoW nonce (8 bytes LE on wire) + PrevID Hash // Previous block hash (32 bytes) + MinorVersion uint64 // Soft-fork signalling (varint on wire) + Timestamp uint64 // Unix epoch seconds (varint on wire) + Flags uint8 // Bit 0: PoS flag (0=PoW, 1=PoS) } type Block struct { BlockHeader - MinerTx Transaction - TxHashes []Hash + MinerTx Transaction // Coinbase transaction + TxHashes []Hash // Hashes of included transactions } +``` +The wire serialisation order differs from the struct field order. Canonical format: `major_version`, `nonce`, `prev_id`, `minor_version`, `timestamp`, `flags`. + +### Transaction Structure + +```go type Transaction struct { - Version uint64 // varint on wire - Vin []TxInput - Vout []TxOutput - Extra []byte // raw wire bytes (variant vector) - Signatures [][]Signature // v0/v1 only - Attachment []byte // raw wire bytes (variant vector) - Proofs []byte // raw wire bytes (v2+ only) - HardforkID uint8 // v3+ only + Version uint64 // 0=genesis, 1=pre-HF4, 2=post-HF4, 3=post-HF5 + Vin []TxInput // Inputs (variant type) + Vout []TxOutput // Outputs (variant type) + Extra []byte // Raw wire bytes for bit-identical round-tripping + HardforkID uint8 // v3+ only + Signatures [][]Signature // v0/v1 ring signatures + SignaturesRaw []byte // v2+ raw signature bytes (CLSAG, etc.) + Attachment []byte // Service attachments + Proofs []byte // v2+ proofs (BP+, balance, surjection) } ``` -Transaction versions progress through the hardfork schedule: +Wire format differs between versions: +- **v0/v1:** `version, vin, vout, extra, [signatures, attachment]` +- **v2+:** `version, vin, extra, vout, [hardfork_id], [attachment, signatures, proofs]` -| Version | Era | Description | -|---------|-----|-------------| -| 0 | Genesis | Coinbase transactions | -| 1 | Pre-HF4 | Standard transparent transactions | -| 2 | Post-HF4 | Zarcanum confidential transactions (CLSAG) | -| 3 | Post-HF5 | Confidential assets with surjection proofs | +### Input Types -Input types: `TxInputGenesis` (coinbase, tag `0x00`) and `TxInputToKey` (standard -spend with ring signature, tag `0x01`). +| Type | Tag | Description | +|------|-----|-------------| +| `TxInputGenesis` | `0x00` | Coinbase input (block height only) | +| `TxInputToKey` | `0x01` | Standard spend with ring signature | +| `TxInputZC` | `0x25` | Zarcanum confidential input (no amount field) | -Output types: `TxOutputBare` (transparent, tag `0x24`) and `TxOutputZarcanum` -(confidential with Pedersen commitments, tag `0x26`). +### Output Types -Additional types: `TxOutToKey` (public key + mix_attr, 33 bytes on wire), -`TxOutRef` (variant: global index or ref_by_id). +| Type | Tag | Description | +|------|-----|-------------| +| `TxOutputBare` | `0x24` | Transparent output (visible amount) | +| `TxOutputZarcanum` | `0x26` | Confidential output (Pedersen commitment) | ---- +A `TxOutputZarcanum` contains: -## Design Decisions - -### Why CryptoNote Base58 - -CryptoNote uses its own base58 variant (not Bitcoin's base58check). The alphabet -omits `0`, `O`, `I`, and `l` to avoid visual ambiguity. Data is split into 8-byte -blocks, each encoded independently into 11 base58 characters. The final partial -block produces fewer characters according to a fixed mapping table -(`base58BlockSizes`). This block-based approach differs from Bitcoin's -whole-number division and produces different output for the same input bytes. - -The implementation uses `math/big` for block conversion rather than a lookup -table. This is correct for all uint64 ranges but is not optimised for -high-throughput address generation. Performance optimisation is deferred to a -later phase if profiling identifies it as a bottleneck. - -### Why Keccak-256 (Not SHA3-256) - -CryptoNote predates the NIST SHA-3 standard. It uses the original Keccak-256 -submission (`sha3.NewLegacyKeccak256()`), which differs from the finalised -SHA3-256 in padding. This is consensus-critical -- using SHA3-256 would produce -different checksums and break address compatibility with the C++ node. - -### Address Encoding Algorithm - -1. Encode the address prefix as a CryptoNote varint -2. Append the 32-byte spend public key -3. Append the 32-byte view public key -4. Append the 1-byte flags field -5. Compute Keccak-256 over bytes 1-4, take the first 4 bytes as checksum -6. Append the 4-byte checksum -7. Encode the entire blob using CryptoNote base58 - -Decoding reverses this process: base58 decode, extract and validate the varint -prefix, verify the Keccak-256 checksum, then extract the two keys and flags. - -### Block Hash Length Prefix - -The C++ code computes block hashes via `get_object_hash(get_block_hashing_blob(b))`. -Because `get_block_hashing_blob` returns a `blobdata` (std::string) and -`get_object_hash` serialises its argument through `binary_archive` before hashing, -the actual hash input is `varint(len(blob)) || blob` -- the binary archive -prepends a varint length when serialising a string. This CryptoNote convention is -replicated in Go's `BlockHash` function. - -### Extra as Raw Bytes - -Transaction extra, attachment, and proofs fields are stored as opaque raw wire -bytes rather than being fully parsed into Go structures. The `decodeRawVariantVector` -function reads variant vectors at the tag level to determine element boundaries but -preserves all bytes verbatim. This enables bit-identical round-tripping without -implementing every extra variant type (there are 20+ defined in the C++ code). - -### Varint Encoding - -The wire format uses 7-bit variable-length integers identical to protobuf -varints. Each byte carries 7 data bits in the low bits with the MSB set to 1 -if more bytes follow. A uint64 requires at most 10 bytes. The implementation -provides sentinel errors (`ErrVarintOverflow`, `ErrVarintEmpty`) for malformed -input. - -### Hardfork System (Reverse-Scan VersionAtHeight) - -`VersionAtHeight()` iterates all hardforks and returns the highest version whose -activation height has been passed. A fork with `Height=0` is active from genesis. -A fork with `Height=N` is active at heights strictly greater than N. - -This scan approach (rather than a sorted binary search) is deliberate: the fork -list is small (7 entries) and correctness is trivially verifiable. The same list -drives both `VersionAtHeight()` and `IsHardForkActive()`. - -### LWMA Difficulty Adjustment - -The difficulty algorithm uses the LWMA (Linear Weighted Moving Average) approach: - -``` -nextDiff = difficultyDelta * targetInterval / timeSpan +```go +type TxOutputZarcanum struct { + StealthAddress PublicKey // One-time stealth address + ConcealingPoint PublicKey // Group element Q (premultiplied by 1/8) + AmountCommitment PublicKey // Pedersen commitment (premultiplied by 1/8) + BlindedAssetID PublicKey // Asset type blinding (premultiplied by 1/8) + EncryptedAmount uint64 // XOR-encrypted amount + MixAttr uint8 // Mixing attribute +} ``` -The window examines up to 735 blocks (720 window + 15 lag). When fewer blocks -are available (early chain), the algorithm uses whatever data exists. Division -by zero is prevented by clamping the time span to a minimum of 1 second. -`StarterDifficulty` (value 1) is returned when insufficient data is available. +### Address Encoding -### crypto/ -- CGo Bridge to libcryptonote +Four address types via distinct base58 prefixes: -Bridges Go to the upstream CryptoNote C++ crypto library via CGo. The C++ code -is vendored in `crypto/upstream/` (37 files from Zano commit `fa1608cf`) and -built as a static library (`libcryptonote.a`) via CMake. +| Type | Prefix | Starts with | Auditable | Integrated | +|------|--------|-------------|-----------|------------| +| Standard | `0x1eaf7` | `iTHN` | No | No | +| Integrated | `0xdeaf7` | `iTHn` | No | Yes | +| Auditable | `0x3ceff7` | `iThN` | Yes | No | +| Auditable integrated | `0x8b077` | `iThn` | Yes | Yes | -**Build flow:** `CMakeLists.txt` → `cmake --build` → `libcryptonote.a` → CGo links. +Encoding format: +``` +base58(varint(prefix) || spend_pubkey(32) || view_pubkey(32) || flags(1) || keccak256_checksum(4)) +``` -**C API contract:** `bridge.h` defines the stable C boundary. Go code calls ONLY -these functions -- no C++ types cross the boundary. All parameters are raw -`uint8_t*` pointers with explicit sizes. This is the same CGo pattern used by -`core/go-ai` for the MLX backend. +CryptoNote base58 splits input into 8-byte blocks, each encoded independently into 11 characters. Uses legacy Keccak-256 (pre-NIST), not SHA3-256. -**Compat layer:** `crypto/compat/` provides minimal stubs replacing epee/Boost -dependencies (logging macros, warnings pragmas, zero-init, profile tools). The -upstream files are unmodified copies; all adaptation lives in the compat headers. +## wire/ -**Provenance:** `crypto/PROVENANCE.md` maps each vendored file to its upstream -origin path and modification status, with an update workflow for tracking Zano -upstream changes. +Consensus-critical binary serialisation. Key primitives: -**Exposed operations:** +- **Varint:** 7-bit LEB128 with MSB continuation (same as protobuf). Max 10 bytes per uint64. +- **Block hash:** `Keccak256(varint(len) || block_hashing_blob)` -- the length prefix comes from the C++ `binary_archive` serialisation of a `blobdata` type. +- **Tree hash:** CryptoNote Merkle tree over transaction hashes (direct port of `crypto/tree-hash.c`). -| Category | Functions | -|----------|-----------| -| Hashing | `FastHash` (Keccak-256) | -| Key ops | `GenerateKeys`, `SecretToPublic`, `CheckKey` | -| Key derivation | `GenerateKeyDerivation`, `DerivePublicKey`, `DeriveSecretKey` | -| Key images | `GenerateKeyImage`, `ValidateKeyImage` | -| Standard sigs | `GenerateSignature`, `CheckSignature` | -| Ring sigs (NLSAG) | `GenerateRingSignature`, `CheckRingSignature` | -| CLSAG (HF4+) | `GenerateCLSAGGG`, `VerifyCLSAGGG`, `VerifyCLSAGGGX`, `VerifyCLSAGGGXXG` | -| Point helpers | `PointMul8`, `PointDiv8` (cofactor 1/8 premultiplication) | -| Proof verification | `VerifyBPPE`, `VerifyBGE`, `VerifyZarcanum` (stubs -- Phase 4) | +Extra, attachment, and proof fields are stored as opaque raw wire bytes. This enables bit-identical round-tripping without implementing all 20+ extra variant types. -**Ring buffer convention:** Ring entries are flat byte arrays. CLSAG ring entries -pack 32-byte public keys per dimension (GG=64B, GGX=96B, GGXXG=128B per entry). -Signatures are serialised as flat buffers with documented layouts in `bridge.h`. +## consensus/ -**1/8 premultiplication:** On-chain commitments are stored premultiplied by the -cofactor inverse (1/8). The `PointMul8`/`PointDiv8` helpers convert between -representations. CLSAG generate takes full points; CLSAG verify takes -premultiplied values. +Three-layer validation, all hardfork-aware: -### ADR-001: Go Shell + C++ Crypto Library +**Layer 1 -- Structural (no crypto):** +Transaction size, input/output counts, key image uniqueness, extra parsing, version checks. -This package follows ADR-001. All protocol logic, data types, serialisation, -and configuration live in pure Go. The mathematically complex cryptographic -primitives (ring signatures, bulletproofs, Zarcanum proofs) are delegated to -the vendored C++ library in `crypto/` via CGo. This boundary keeps the pure Go -code testable without a C toolchain while preserving access to battle-tested -cryptographic implementations. +**Layer 2 -- Economic:** +Block reward (fixed 1 LTHN with size penalty using 128-bit arithmetic), fee extraction, balance checks. Pre-HF4 fees go to miner; post-HF4 fees are burned. + +```go +func BaseReward(height uint64) uint64 { + if height == 0 { + return config.Premine // Genesis block: 10M LTHN + } + return config.BlockReward // All other blocks: 1 LTHN +} +``` + +**Layer 3 -- Cryptographic (CGo):** +PoW hash verification (RandomX, key `"LetheanRandomXv1"`), NLSAG ring signatures (pre-HF4), CLSAG signatures (post-HF4), Bulletproofs+ range proofs. + +## chain/ + +Persistent blockchain storage using go-store (pure-Go SQLite). Five storage groups: + +| Group | Key | Value | +|-------|-----|-------| +| `blocks` | Height (zero-padded) | JSON metadata + hex blob | +| `block_index` | Block hash | Height | +| `transactions` | Tx hash | JSON metadata + hex blob | +| `spent_keys` | Key image | Block height | +| `outputs:{amount}` | Index | Global output entry | + +Supports two sync modes: +- **RPC sync:** Poll-based fetching from a JSON-RPC daemon. +- **P2P sync:** Levin protocol REQUEST_CHAIN / REQUEST_GET_OBJECTS loop. + +Both share a common `processBlockBlobs()` validation path. + +## wallet/ + +Interface-driven design with four core abstractions: + +| Interface | Purpose | v1 Implementation | +|-----------|---------|-------------------| +| `Scanner` | Detect owned outputs via ECDH | `V1Scanner` | +| `Signer` | Produce ring signatures | `NLSAGSigner` | +| `Builder` | Construct signed transactions | `V1Builder` | +| `RingSelector` | Pick decoy outputs | `RPCRingSelector` | + +Account key derivation: `viewSecret = sc_reduce32(Keccak256(spendSecret))`, matching the C++ `account_base::generate()` pattern. 25-word CryptoNote mnemonic encoding using the Electrum 1626-word dictionary. + +## mining/ + +Solo PoW miner flow: + +1. `GetBlockTemplate(walletAddr)` -- fetch template from daemon +2. `HeaderMiningHash(block)` -- Keccak-256 of `BlockHashingBlob` with nonce=0 +3. Nonce loop: `RandomXHash("LetheanRandomXv1", headerHash || nonce_LE)` +4. `CheckDifficulty(powHash, difficulty)` -- solution found? +5. `SubmitBlock(hexBlob)` -- submit the solved block + +## CGo Bridge Design + +The crypto package follows **ADR-001: Go Shell + C++ Crypto Library**: + +- `crypto/upstream/` -- 37 vendored C++ files from Zano commit `fa1608cf` +- `crypto/compat/` -- Compatibility stubs replacing epee/Boost dependencies +- `crypto/bridge.h` -- Stable C API (29 functions). Only `uint8_t*` pointers cross the boundary. +- `crypto/randomx/` -- Vendored RandomX source (26 files including x86_64 JIT) + +Build: `cmake -S crypto -B crypto/build && cmake --build crypto/build --parallel` + +All curve points on chain are stored premultiplied by the cofactor inverse (1/8). The `PointMul8`/`PointDiv8` helpers convert between representations. CLSAG generate takes full points; CLSAG verify takes premultiplied values. diff --git a/docs/cryptography.md b/docs/cryptography.md new file mode 100644 index 0000000..288972d --- /dev/null +++ b/docs/cryptography.md @@ -0,0 +1,263 @@ +--- +title: Cryptography +description: Cryptographic primitives provided by the CGo bridge to libcryptonote. +--- + +# Cryptography + +All cryptographic operations are provided by the `crypto/` package, which bridges Go to the vendored C++ CryptoNote library via CGo. This is the only package in the codebase that crosses the CGo boundary. + +## Build Requirements + +The C++ library must be built before running tests that require crypto: + +```bash +cmake -S crypto -B crypto/build -DCMAKE_BUILD_TYPE=Release +cmake --build crypto/build --parallel +``` + +This produces `libcryptonote.a` (~680KB) and `librandomx.a`. CGo links against both. + +Packages that do not require crypto (`config/`, `types/`, `wire/`, `difficulty/`) remain buildable without a C toolchain. + +## Hash Functions + +### Keccak-256 (`FastHash`) + +The primary hash function throughout the protocol. Uses the original Keccak-256 (pre-NIST), **not** SHA3-256. The two differ in padding and produce different outputs. + +```go +hash := crypto.FastHash(data) // returns [32]byte +``` + +Used for: transaction hashes, block hashes, key derivations, address checksums, and proof construction. + +### Tree Hash + +Merkle tree hash over a set of transaction hashes within a block. Uses Keccak-256 as the leaf/node hash. Implemented in `wire/treehash.go` as a direct port of the C++ `crypto/tree-hash.c`. + +### RandomX (PoW) + +Proof-of-Work hash function. The vendored RandomX source (26 files including x86_64 JIT compiler) is built as a separate static library. + +```go +hash := crypto.RandomXHash(key, input) // key = "LetheanRandomXv1" +``` + +Input format: `header_hash(32 bytes) || nonce(8 bytes LE)`. + +## Curve Operations + +All public keys, key images, commitments, and signatures are built on the **Ed25519** curve (Twisted Edwards form of Curve25519), providing 128-bit security. + +### Key Types + +| Type | Size | Description | +|------|------|-------------| +| `PublicKey` | 32 bytes | Compressed Ed25519 point | +| `SecretKey` | 32 bytes | Scalar modulo group order _l_ | +| `KeyImage` | 32 bytes | Double-spend detection tag | +| `KeyDerivation` | 32 bytes | Diffie-Hellman shared secret point | + +### Key Generation + +```go +pub, sec, err := crypto.GenerateKeys() // Random key pair +pub, err := crypto.SecretToPublic(sec) // Derive public from secret +valid := crypto.CheckKey(pub) // Validate curve point +``` + +### Scalar Reduction + +```go +crypto.ScReduce32(&key) // Reduce 32-byte value modulo group order l +``` + +Required when converting a hash output to a valid secret key scalar. Used in wallet view key derivation: `viewSecret = sc_reduce32(Keccak256(spendSecret))`. + +## Stealth Addresses + +CryptoNote stealth addresses ensure every output appears at a unique one-time address that only the recipient can identify and spend. + +### Key Derivation + +```go +// Sender: compute shared secret from recipient's view public key and tx secret key +derivation, err := crypto.GenerateKeyDerivation(viewPubKey, txSecretKey) + +// Derive per-output one-time public key +outputKey, err := crypto.DerivePublicKey(derivation, outputIndex, spendPubKey) +``` + +### Output Scanning (Recipient) + +```go +// Compute shared secret from tx public key and own view secret key +derivation, err := crypto.GenerateKeyDerivation(txPubKey, viewSecretKey) + +// Derive expected output public key +expected, err := crypto.DerivePublicKey(derivation, outputIndex, spendPubKey) + +// If expected == actual output key, this output belongs to us +// Then derive the spending secret key: +outputSecKey, err := crypto.DeriveSecretKey(derivation, outputIndex, spendSecretKey) +``` + +## Key Images + +Each output can only be spent once. When spent, its key image is revealed and recorded on chain. Any subsequent attempt to spend the same output produces an identical key image, which the network rejects. + +```go +keyImage, err := crypto.GenerateKeyImage(outputPubKey, outputSecKey) +valid := crypto.ValidateKeyImage(keyImage) +``` + +Key images are deterministic (same output always produces the same image) and unlinkable (the image cannot be traced back to the output without the secret key). + +## Signatures + +### Standard Signatures + +Non-ring Ed25519 signatures for general-purpose message signing: + +```go +sig, err := crypto.GenerateSignature(messageHash, pubKey, secKey) +valid := crypto.CheckSignature(messageHash, pubKey, sig) +``` + +### NLSAG Ring Signatures (Pre-HF4) + +Classic CryptoNote ring signatures. The spender proves ownership of one output within a ring of decoys without revealing which output is theirs. + +Ring size: 10 decoys + 1 real (pre-HF4, configurable via `config.DefaultDecoySetSize`). + +```go +// Generate a ring signature +sigs, err := crypto.GenerateRingSignature( + txHash, // Message being signed + keyImage, // Key image of the real input + ringPubKeys, // Public keys of all ring members ([][32]byte) + realSecretKey, // Secret key of the actual signer + realIndex, // Position of the real key in the ring +) + +// Verify a ring signature +valid := crypto.CheckRingSignature(txHash, keyImage, ringPubKeys, sigs) +``` + +### CLSAG Ring Signatures (Post-HF4) + +Compact Linkable Spontaneous Anonymous Group signatures, introduced with HF4 (Zarcanum). Smaller and faster than NLSAG. + +Ring size: 15 decoys + 1 real (from HF4, `config.HF4MandatoryDecoySetSize`). + +Three variants with increasing complexity: + +| Variant | Base Points | Used For | +|---------|-------------|----------| +| CLSAG-GG | G, G | Efficient ring signatures | +| CLSAG-GGX | G, G, X | Confidential transaction signatures | +| CLSAG-GGXXG | G, G, X, X, G | PoS stake signatures | + +```go +// Generate CLSAG-GG +sig, err := crypto.GenerateCLSAGGG(hash, ring, ringSize, + pseudoOut, keyImage, secretX, secretF, secretIndex) + +// Verify CLSAG-GG +valid := crypto.VerifyCLSAGGG(hash, ring, ringSize, pseudoOut, keyImage, sig) + +// Verify CLSAG-GGX (confidential transactions) +valid := crypto.VerifyCLSAGGGX(hash, ring, ringSize, + pseudoOutCommitment, pseudoOutAssetID, keyImage, sig) + +// Verify CLSAG-GGXXG (PoS staking) +valid := crypto.VerifyCLSAGGGXXG(hash, ring, ringSize, + pseudoOutCommitment, pseudoOutAssetID, extendedCommitment, keyImage, sig) +``` + +**Ring buffer convention:** Ring entries are flat byte arrays. Each entry packs 32-byte public keys per dimension: +- GG: 64 bytes per entry (stealth_addr + amount_commitment) +- GGX: 96 bytes per entry (+ blinded_asset_id) +- GGXXG: 128 bytes per entry (+ concealing_point) + +## Cofactor Handling (1/8 Premultiplication) + +On-chain curve points (commitments, blinded asset IDs) are stored premultiplied by the cofactor inverse (1/8). This is a Zarcanum convention for efficient batch verification. + +```go +// Convert full point to on-chain form +onChain, err := crypto.PointDiv8(fullPoint) + +// Convert on-chain form back to full point +full, err := crypto.PointMul8(onChainPoint) + +// Point subtraction +result, err := crypto.PointSub(a, b) +``` + +CLSAG **generate** takes full points; CLSAG **verify** takes premultiplied (on-chain) values. + +## Range Proofs + +### Bulletproofs+ (BP+) + +Zero-knowledge range proofs demonstrating that a committed value lies within [0, 2^64) without revealing the value. Used for transaction output amounts from HF4. + +```go +valid := crypto.VerifyBPP(proofBlob, commitments) // commitments premultiplied by 1/8 +``` + +### Bulletproofs++ Enhanced (BPPE) + +Extended variant used in Zarcanum PoS proofs. Proves the stake amount meets the minimum threshold without revealing the balance. + +```go +valid := crypto.VerifyBPPE(proofBlob, commitments) +``` + +## Asset Surjection Proofs (BGE) + +Bootle-Groth-Esgin one-out-of-many proofs. Each output proves its blinded asset ID corresponds to one of the input asset IDs, without revealing which. Introduced in HF5 (confidential assets). + +```go +valid := crypto.VerifyBGE(contextHash, ring, proofBlob) +``` + +## Zarcanum PoS Proofs + +Composite proof for Proof-of-Stake block production. Combines stake commitment proof, Schnorr response scalars, BPPE range proof, and CLSAG-GGXXG ring signature. + +```go +valid := crypto.VerifyZarcanum(hash, proofBlob) +``` + +## Pedersen Commitments + +Commits to a value _v_ with blinding factor _r_ as `C = v * H + r * G`. Additively homomorphic: `C(a) + C(b) = C(a+b)`, allowing verification that inputs and outputs balance without revealing amounts. + +The native coin asset ID `H` is a fixed curve point. From HF4, all transaction amounts use Pedersen commitments. + +## Wallet Encryption + +Wallet files are encrypted with ChaCha8 (8-round ChaCha stream cipher). The encryption key is derived from the user's password via Argon2id (time=3, memory=64MB, threads=4) with AES-256-GCM. + +## Summary + +| Primitive | Purpose | Available From | +|-----------|---------|----------------| +| Keccak-256 | Primary hash | Genesis | +| Tree hash | Tx Merkle root | Genesis | +| RandomX | PoW hash | Genesis | +| Ed25519 ops | Key arithmetic | Genesis | +| Key derivation | Stealth addresses | Genesis | +| Key images | Double-spend prevention | Genesis | +| NLSAG ring sigs | Input privacy (classic) | Genesis | +| CLSAG-GG | Efficient ring sigs | HF4 | +| CLSAG-GGX | Confidential tx sigs | HF4 | +| CLSAG-GGXXG | PoS stake sigs | HF4 | +| Bulletproofs+ | Amount range proofs | HF4 | +| Bulletproofs++ | Stake range proofs | HF4 | +| Zarcanum proofs | PoS composite proofs | HF4 | +| BGE surjection | Asset type proofs | HF5 | +| Pedersen commitments | Hidden amounts | HF4 | diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..3543414 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,132 @@ +--- +title: Lethean Go Blockchain +description: Pure Go implementation of the Lethean CryptoNote/Zano-fork blockchain protocol. +--- + +# Lethean Go Blockchain + +`go-blockchain` is a Go reimplementation of the Lethean blockchain protocol. It provides pure-Go implementations of chain logic, data structures, consensus rules, wallet operations, and networking, delegating only mathematically complex cryptographic operations (ring signatures, Bulletproofs+, Zarcanum proofs) to a cleaned C++ library via CGo. + +**Module path:** `forge.lthn.ai/core/go-blockchain` + +**Licence:** [European Union Public Licence (EUPL) version 1.2](https://joinup.ec.europa.eu/software/page/eupl/licence-eupl) + +## Lineage + +``` +CryptoNote (van Saberhagen, 2013) + | +IntenseCoin (2017) + | +Lethean (2017-present) + | +Zano rebase (2025) -- privacy upgrades: Zarcanum, CLSAG, Bulletproofs+, confidential assets + | +go-blockchain -- Go reimplementation of the Zano-fork protocol +``` + +The Lethean mainnet launched on **2026-02-12** with genesis timestamp `1770897600` (12:00 UTC). The chain runs a hybrid PoW/PoS consensus with 120-second block targets. + +## Package Structure + +``` +go-blockchain/ + config/ Chain parameters (mainnet/testnet), hardfork schedule + types/ Core data types: Hash, PublicKey, Address, Block, Transaction + wire/ Binary serialisation (consensus-critical, bit-identical to C++) + crypto/ CGo bridge to libcryptonote (ring sigs, BP+, Zarcanum, stealth) + difficulty/ PoW + PoS difficulty adjustment (LWMA variant) + consensus/ Three-layer block/transaction validation + chain/ Blockchain storage, block/tx validation, mempool + p2p/ Levin TCP protocol, peer discovery, handshake + rpc/ Daemon and wallet JSON-RPC client + wallet/ Key management, output scanning, tx construction + mining/ Solo PoW miner (RandomX nonce grinding) + tui/ Terminal dashboard (bubbletea + lipgloss) +``` + +## Design Principles + +1. **Consensus-critical code must be bit-identical** to the C++ implementation. The `wire/` package produces exactly the same binary output as the C++ serialisation for the same input. + +2. **No global state.** Chain parameters are passed via `config.ChainConfig` structs, not package-level globals. `Mainnet` and `Testnet` are pre-defined instances. + +3. **Interfaces at boundaries.** The `chain/` package defines interfaces for storage backends; the `wallet/` package uses Scanner, Signer, Builder, and RingSelector interfaces for v1/v2+ extensibility. + +4. **Test against real chain data.** Wherever possible, tests use actual mainnet block and transaction hex blobs as test vectors, ensuring compatibility with the C++ node. + +## Quick Start + +```go +import ( + "fmt" + + "forge.lthn.ai/core/go-blockchain/config" + "forge.lthn.ai/core/go-blockchain/rpc" + "forge.lthn.ai/core/go-blockchain/types" +) + +// Query the daemon +client := rpc.NewClient("http://localhost:36941") +info, err := client.GetInfo() +if err != nil { + panic(err) +} +fmt.Printf("Height: %d, Difficulty: %d\n", info.Height, info.PowDifficulty) + +// Decode an address +addr, prefix, err := types.DecodeAddress("iTHN...") +if err != nil { + panic(err) +} +fmt.Printf("Spend key: %s\n", addr.SpendPublicKey) +fmt.Printf("Auditable: %v\n", addr.IsAuditable()) + +// Check hardfork version at a given height +version := config.VersionAtHeight(config.MainnetForks, 15000) +fmt.Printf("Active hardfork at height 15000: HF%d\n", version) +``` + +## CGo Boundary + +The `crypto/` package is the **only** package that crosses the CGo boundary. All other packages are pure Go. + +``` +Go side C++ side (libcryptonote + librandomx) ++---------+ +---------------------------+ +| crypto/ | --- CGo calls ---> | cn_fast_hash() | +| | | generate_key_derivation | +| | | generate_key_image | +| | | check_ring_signature | +| | | CLSAG_GG/GGX/GGXXG_verify| +| | | bulletproof_plus_verify | +| | | zarcanum_verify | +| | | randomx_hash | ++---------+ +---------------------------+ +``` + +When CGo is disabled, stub implementations return errors, allowing the rest of the codebase to compile and run tests that do not require real cryptographic operations. + +## Development Phases + +The project follows a 9-phase development plan. See the [wiki Development Phases page](https://forge.lthn.ai/core/go-blockchain/wiki/Development-Phases) for detailed phase descriptions. + +| Phase | Scope | Status | +|-------|-------|--------| +| 0 | Config + Types | Complete | +| 1 | Wire Serialisation | Complete | +| 2 | CGo Crypto Bridge | Complete | +| 3 | P2P Protocol | Complete | +| 4 | RPC Client | Complete | +| 5 | Chain Storage | Complete | +| 6 | Wallet Core | Complete | +| 7 | Consensus Rules | Complete | +| 8 | Mining | Complete | + +## Further Reading + +- [Architecture](architecture.md) -- Package dependencies, CGo boundary, data structures +- [Cryptography](cryptography.md) -- Crypto primitives, hashing, signatures, proofs +- [Networking](networking.md) -- P2P protocol, peer discovery, message types +- [RPC Reference](rpc.md) -- Daemon and wallet JSON-RPC API +- [Chain Parameters](parameters.md) -- Tokenomics, emission, hardfork schedule diff --git a/docs/networking.md b/docs/networking.md new file mode 100644 index 0000000..71c085e --- /dev/null +++ b/docs/networking.md @@ -0,0 +1,304 @@ +--- +title: P2P Networking +description: Levin wire protocol, peer discovery, block/transaction propagation, and chain synchronisation. +--- + +# P2P Networking + +The Lethean P2P network uses the **Levin protocol**, a binary TCP protocol inherited from the CryptoNote/epee library. The Go implementation spans two packages: + +- **`go-p2p/node/levin/`** -- Wire format: 33-byte header, portable storage serialisation, framed TCP connections. +- **`go-blockchain/p2p/`** -- Application-level command semantics: handshake, sync, block/tx relay. + +## Levin Wire Format + +Every message is prefixed with a 33-byte header: + +``` +Offset Size Field Description +------ ---- ----- ----------- +0 8 m_signature Magic: 0x0101010101012101 (LE) +8 8 m_cb Payload length in bytes (LE) +16 1 m_have_to_return Boolean: expects a response +17 4 m_command Command ID (LE uint32) +21 4 m_return_code Return code (LE int32) +25 4 m_flags Packet type flags (LE uint32) +29 4 m_protocol_version Protocol version (LE uint32) +``` + +### Packet Types + +| Flag | Meaning | +|------|---------| +| `0x00000001` | Request (expects response if `m_have_to_return` is set) | +| `0x00000002` | Response to a prior request | + +### Header Validation + +On receiving a message, a node: + +1. Reads 8 bytes and verifies they match `0x0101010101012101`. +2. Reads the remaining 25 bytes of the header. +3. Validates payload length does not exceed `P2P_DEFAULT_PACKET_MAX_SIZE` (50 MB). +4. Reads `m_cb` bytes of payload. +5. Dispatches based on `m_command`. + +## Payload Serialisation + +Payloads use the **epee portable storage** binary format -- a TLV-style key-value serialisation supporting nested objects, arrays, integers, strings, and binary blobs. + +This is distinct from the consensus binary serialisation used for blocks and transactions in the `wire/` package. Notably, portable storage varints use a 2-bit size mark in the low bits (1/2/4/8 byte encoding), **not** the 7-bit LEB128 varints used on the consensus wire. + +## Command IDs + +Commands are divided into two pools, re-exported in the `p2p/` package: + +```go +const ( + CommandHandshake = 1001 // P2P handshake + CommandTimedSync = 1002 // Periodic state sync + CommandPing = 1003 // Connectivity check + CommandNewBlock = 2001 // Block propagation + CommandNewTransactions = 2002 // Transaction propagation + CommandRequestObjects = 2003 // Request blocks/txs by hash + CommandResponseObjects = 2004 // Response with blocks/txs + CommandRequestChain = 2006 // Request chain skeleton + CommandResponseChain = 2007 // Response with chain entry +) +``` + +## Handshake Protocol + +### Go Types + +```go +type NodeData struct { + NetworkID [16]byte + PeerID uint64 + LocalTime int64 + MyPort uint32 +} + +type HandshakeRequest struct { + NodeData NodeData + PayloadData CoreSyncData +} + +type HandshakeResponse struct { + NodeData NodeData + PayloadData CoreSyncData + PeerlistBlob []byte // Packed 24-byte entries +} +``` + +### Connection Flow + +1. **Initiator** connects via TCP and sends `COMMAND_HANDSHAKE` (1001) as a request. +2. **Responder** validates the network ID, client version, and peer uniqueness. +3. **Responder** replies with its own node data, sync state, and peer list. +4. Both sides begin periodic `COMMAND_TIMED_SYNC` (1002) every 60 seconds. + +### Handshake Request Payload + +``` +node_data { + network_id [16]byte // Derived from CURRENCY_FORMATION_VERSION + peer_id uint64 // Random peer identifier + local_time int64 // Node's Unix timestamp + my_port uint32 // Listening port for inbound connections +} +payload_data { + current_height uint64 + top_id [32]byte // Top block hash + last_checkpoint_height uint64 + core_time uint64 + client_version string // e.g. "6.0.1.2[go-blockchain]" + non_pruning_mode_enabled bool +} +``` + +### Handshake Rejection + +The responder rejects the handshake if: +- `network_id` does not match (different chain or testnet/mainnet mismatch) +- `client_version` is below the minimum for the current hardfork era +- The peer ID is already connected (duplicate connection) + +### Network Identity + +| Network | Formation Version | Network ID (byte 15) | +|---------|-------------------|---------------------| +| Mainnet | 84 | `0x54` | +| Testnet | 100 | `0x64` | + +```go +var NetworkIDMainnet = [16]byte{ + 0x11, 0x10, 0x01, 0x11, 0x01, 0x01, 0x11, 0x01, + 0x10, 0x11, 0x01, 0x11, 0x01, 0x11, 0x21, 0x54, +} +``` + +## Peer List Management + +### Peer Entry Format + +Peer lists are exchanged as packed binary blobs, 24 bytes per entry: + +```go +type PeerlistEntry struct { + IP uint32 // IPv4 (network byte order) [4 bytes] + Port uint32 // Port number [4 bytes] + ID uint64 // Peer ID [8 bytes] + LastSeen int64 // Unix timestamp of last contact [8 bytes] +} + +entries := p2p.DecodePeerlist(blob) // Splits packed blob into entries +``` + +### Two-Tier Peer Lists + +| List | Max Size | Description | +|------|----------|-------------| +| **White list** | 1,000 | Verified peers (successful handshake + ping) | +| **Grey list** | 5,000 | Unverified peers (received from other nodes) | + +### Connection Strategy + +| Parameter | Value | +|-----------|-------| +| Target outbound connections | 8 | +| White list preference | 70% | +| Peers exchanged per handshake | 250 | + +When establishing outbound connections, 70% are made to white list peers and 30% to grey list peers. Successful connections promote grey list peers to the white list. + +### Ping Verification + +Before adding a peer to the white list: + +1. Connect to the peer's advertised IP:port. +2. Send `COMMAND_PING` (1003). +3. Verify the response contains `status: "OK"` and a matching `peer_id`. +4. Only then promote from grey to white list. + +### Failure Handling + +| Parameter | Value | +|-----------|-------| +| Failures before blocking | 10 | +| Block duration | 24 hours | +| Failed address forget time | 5 minutes | +| Idle connection kill interval | 5 minutes | + +## Timed Sync + +After handshake, peers exchange `COMMAND_TIMED_SYNC` (1002) every 60 seconds. This keeps peers informed of each other's chain state and propagates peer list updates. + +## Block Propagation + +When a node mines or stakes a new block: + +``` +NOTIFY_NEW_BLOCK (2001) { + b { + block: blob // Serialised block + txs: []blob // Serialised transactions + } + current_blockchain_height: uint64 +} +``` + +This is a notification (no response expected). The receiving node validates the block and relays it to its own peers. + +## Transaction Propagation + +``` +NOTIFY_OR_INVOKE_NEW_TRANSACTIONS (2002) { + txs: []blob // Serialised transaction blobs +} +``` + +Up to `CURRENCY_RELAY_TXS_MAX_COUNT` (5) transactions per message. + +## Chain Synchronisation + +### Requesting the Chain Skeleton + +To sync, a node sends its known block IDs in a sparse pattern: + +``` +NOTIFY_REQUEST_CHAIN (2006) { + block_ids: []hash // Sparse: first 10 sequential, then 2^n offsets, genesis last +} +``` + +The Go implementation uses `SparseChainHistory()` which matches the C++ `get_short_chain_history()` algorithm: +- The 10 most recent block hashes +- Then every 2nd, 4th, 8th, 16th, 32nd, etc. +- Always ending with the genesis block hash + +### Chain Response + +``` +NOTIFY_RESPONSE_CHAIN_ENTRY (2007) { + start_height: uint64 + total_height: uint64 + m_block_ids: []block_context_info +} +``` + +### Fetching Blocks + +``` +NOTIFY_REQUEST_GET_OBJECTS (2003) { + txs: []hash // Transaction hashes to fetch + blocks: []hash // Block hashes to fetch +} + +NOTIFY_RESPONSE_GET_OBJECTS (2004) { + txs: []blob + blocks: []block_complete_entry + missed_ids: []hash + current_blockchain_height: uint64 +} +``` + +### P2P Sync State Machine + +The `chain.P2PSync()` function implements the full sync loop: + +1. Build sparse chain history from local chain state. +2. Send `REQUEST_CHAIN` to the peer. +3. Receive `RESPONSE_CHAIN_ENTRY` with block hashes. +4. Fetch blocks in batches via `REQUEST_GET_OBJECTS`. +5. Validate and store blocks through `processBlockBlobs()`. +6. Repeat until the peer has no more blocks. + +The first block in each `REQUEST_GET_OBJECTS` response overlaps with the last known block (to confirm chain continuity) and is skipped during processing. + +### Sync Limits + +| Parameter | Value | +|-----------|-------| +| Block IDs per chain request | 2,000 | +| Blocks per download batch | 200 | +| Max bytes per sync packet | 2 MB | +| Max blocks per get_objects | 500 | +| Max txs per get_objects | 500 | + +## Connection Timeouts + +| Parameter | Value | +|-----------|-------| +| TCP connection | 5,000 ms | +| Ping | 2,000 ms | +| Command invoke | 120,000 ms (2 min) | +| Handshake | 10,000 ms | +| Max packet size | 50 MB | + +## Network Ports + +| Network | P2P | RPC | Stratum | +|---------|-----|-----|---------| +| Mainnet | 36942 | 36941 | 36940 | +| Testnet | 46942 | 46941 | 46940 | diff --git a/docs/parameters.md b/docs/parameters.md new file mode 100644 index 0000000..c4d567b --- /dev/null +++ b/docs/parameters.md @@ -0,0 +1,262 @@ +--- +title: Chain Parameters +description: Consensus-critical constants, tokenomics, emission schedule, and hardfork schedule. +--- + +# Chain Parameters + +All values are sourced from the C++ `currency_config.h.in` and `default.cmake`, implemented in the `config/` package. These are consensus-critical constants. + +## Tokenomics + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| Atomic units per coin | 10^12 | `config.Coin` | +| Display decimal places | 12 | `config.DisplayDecimalPoint` | +| Block reward | 1.0 LTHN (10^12 atomic) | `config.BlockReward` | +| Default tx fee | 0.01 LTHN | `config.DefaultFee` | +| Minimum tx fee | 0.01 LTHN | `config.MinimumFee` | +| Premine | 10,000,000 LTHN | `config.Premine` | +| Dust threshold | 0 | `config.DefaultDustThreshold` | +| Ticker | LTHN | `config.CurrencyNameAbbreviation` | + +### Supply Model + +- **Block reward:** Fixed at 1 LTHN per block (no halving, but see HF6 for block time doubling). +- **Premine:** 10,000,000 LTHN reserved at genesis (coinswap allocation + initial premine). +- **Fee model:** Default and minimum fee of 0.01 LTHN. Pre-HF4, fees go to the miner. Post-HF4, fees are burned. + +```go +// Genesis block returns the premine; all others return 1 LTHN. +func BaseReward(height uint64) uint64 { + if height == 0 { + return config.Premine + } + return config.BlockReward +} +``` + +## Address Prefixes + +| Type | Prefix | Base58 starts with | Go Constant | +|------|--------|-------------------|-------------| +| Standard | `0x1eaf7` | `iTHN` | `config.AddressPrefix` | +| Integrated | `0xdeaf7` | `iTHn` | `config.IntegratedAddressPrefix` | +| Auditable | `0x3ceff7` | `iThN` | `config.AuditableAddressPrefix` | +| Auditable integrated | `0x8b077` | `iThn` | `config.AuditableIntegratedAddressPrefix` | + +Addresses are encoded using CryptoNote base58 with a 4-byte Keccak-256 checksum. The prefix is varint-encoded before the spend and view public keys (32 bytes each). + +## Block Timing + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| PoW block target | 120 seconds | `config.DifficultyPowTarget` | +| PoS block target | 120 seconds | `config.DifficultyPosTarget` | +| Combined target | 60 seconds | `config.DifficultyTotalTarget` | +| Blocks per day | ~1,440 | -- | +| PoS active from | Block 0 | `config.PosStartHeight` | + +### Post-HF6 Timing (Future) + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| PoW target | 240 seconds | `config.DifficultyPowTargetHF6` | +| PoS target | 240 seconds | `config.DifficultyPosTargetHF6` | +| Combined target | 120 seconds | `config.DifficultyTotalTargetHF6` | +| Blocks per day | ~720 | -- | + +## Difficulty + +The difficulty adjustment uses the **LWMA** (Linear Weighted Moving Average) algorithm. Each solve-time interval is weighted linearly by recency -- more recent intervals have greater influence. + +```go +// LWMA formula: next_D = total_work * T * (n+1) / (2 * weighted_solvetimes * n) +func NextDifficulty(timestamps []uint64, cumulativeDiffs []*big.Int, target uint64) *big.Int +``` + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| Window | 720 blocks | `config.DifficultyWindow` | +| Lag | 15 blocks | `config.DifficultyLag` | +| Cut | 60 timestamps | `config.DifficultyCut` | +| Blocks count | 735 (Window + Lag) | `config.DifficultyBlocksCount` | +| Initial PoW difficulty | 1 | `config.DifficultyPowStarter` | +| Initial PoS difficulty | 1 | `config.DifficultyPosStarter` | +| LWMA window (N) | 60 intervals | `difficulty.LWMAWindow` | + +Solve-times are clamped to [-6T, 6T] to limit timestamp manipulation impact. The algorithm returns `StarterDifficulty` (1) when insufficient data is available. + +## Transaction Limits + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| Max inputs | 256 | `config.TxMaxAllowedInputs` | +| Max outputs | 2,000 | `config.TxMaxAllowedOutputs` | +| Min outputs (HF4+) | 2 | `config.TxMinAllowedOutputs` | +| Ring size (pre-HF4) | 10 | `config.DefaultDecoySetSize` | +| Ring size (HF4+) | 15 | `config.HF4MandatoryDecoySetSize` | +| Min coinage (HF4+) | 10 blocks | `config.HF4MandatoryMinCoinage` | +| Coinbase maturity | 10 blocks | `config.MinedMoneyUnlockWindow` | +| Max tx blob size | 374,600 bytes | `config.MaxTransactionBlobSize` | + +### Transaction Versions + +| Version | Go Constant | Description | +|---------|-------------|-------------| +| 0 | `config.TransactionVersionInitial` | Genesis/coinbase | +| 1 | `config.TransactionVersionPreHF4` | Standard transparent | +| 2 | `config.TransactionVersionPostHF4` | Zarcanum confidential | +| 3 | `config.TransactionVersionPostHF5` | Confidential assets | + +## Block Limits + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| Max block height | 500,000,000 | `config.MaxBlockNumber` | +| Full reward zone | 125,000 bytes | `config.BlockGrantedFullRewardZone` | +| Coinbase reserved | 1,100 bytes | `config.CoinbaseBlobReservedSize` | +| Reward window | 400 blocks | `config.RewardBlocksWindow` | +| Free tx max size | 1,024 bytes | `config.FreeTxMaxBlobSize` | + +### Block Size Penalty + +Blocks within the full reward zone (125,000 bytes) receive the full base reward. Larger blocks incur a quadratic penalty: + +```go +// reward = baseReward * (2*median - size) * size / median^2 +// Uses 128-bit arithmetic (math/bits.Mul64) to avoid overflow. +func BlockReward(baseReward, blockSize, medianSize uint64) (uint64, error) +``` + +Blocks exceeding 2x the median size are rejected entirely. + +### Block Versions + +| Major Version | Go Constant | Hardfork | +|---------------|-------------|----------| +| 0 | `config.BlockMajorVersionInitial` | Pre-HF1 | +| 1 | `config.HF1BlockMajorVersion` | HF1 | +| 2 | `config.HF3BlockMajorVersion` | HF3 | +| 3 | `config.CurrentBlockMajorVersion` | HF4+ | + +## Timestamp Validation + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| PoW future limit | 7,200 seconds (2 hours) | `config.BlockFutureTimeLimit` | +| PoS future limit | 1,200 seconds (20 min) | `config.PosBlockFutureTimeLimit` | +| Median window | 60 blocks | `config.TimestampCheckWindow` | + +## PoS Parameters + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| Scan window | 600 seconds (10 min) | `config.PosScanWindow` | +| Scan step | 15 seconds | `config.PosScanStep` | +| Modifier interval | 10 | `config.PosModifierInterval` | +| Minimum coinstake age | 10 blocks | `config.PosMinimumCoinstakeAge` | +| Max consecutive PoS | 20 blocks | `config.PosStrictSequenceLimit` | + +## P2P Network + +### Ports + +| Network | P2P | RPC | Stratum | +|---------|-----|-----|---------| +| Mainnet | 36942 | 36941 | 36940 | +| Testnet | 46942 | 46941 | 46940 | + +### Peer Management + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| White peerlist limit | 1,000 | `config.P2PLocalWhitePeerlistLimit` | +| Grey peerlist limit | 5,000 | `config.P2PLocalGrayPeerlistLimit` | +| Default connections | 8 | `config.P2PDefaultConnectionsCount` | +| Handshake interval | 60 seconds | `config.P2PDefaultHandshakeInterval` | +| Max packet size | 50 MB | `config.P2PDefaultPacketMaxSize` | +| IP block time | 24 hours | `config.P2PIPBlockTime` | +| Failures before block | 10 | `config.P2PIPFailsBeforeBlock` | + +## Alias System + +| Parameter | Value | Go Constant | +|-----------|-------|-------------| +| Max aliases per block | 1,000 | `config.MaxAliasPerBlock` | +| Max name length | 255 | `config.AliasNameMaxLen` | +| Min public short name | 6 characters | `config.AliasMinimumPublicShortNameAllowed` | +| Valid characters | `0-9a-z-.` | `config.AliasValidChars` | +| Max comment size | 400 bytes | `config.AliasCommentMaxSizeBytes` | + +## Mempool + +| Parameter | Value | +|-----------|-------| +| Transaction lifetime | 345,600 seconds (4 days) | +| Alt block lifetime | ~10,080 blocks (~7 days) | +| Max alt blocks | 43,200 (~30 days) | +| Relay batch size | 5 transactions | + +## Hardfork Schedule + +Seven hardforks (HF0 through HF6) are defined. Heights are "after height" -- the fork activates at height N+1. + +```go +type HardFork struct { + Version uint8 // 0-6 + Height uint64 // Activates at heights > this value + Mandatory bool // Must support to stay on network + Description string +} +``` + +### Mainnet Schedule + +| HF | Height | Changes | +|----|--------|---------| +| HF0 | 0 (genesis) | CryptoNote base protocol. Hybrid PoW/PoS. Classic ring signatures (NLSAG). Transparent amounts. | +| HF1 | 10,080 (~7 days) | New transaction types: HTLC, multisig, service attachments. | +| HF2 | 10,080 | Block time adjustment. Activates simultaneously with HF1. | +| HF3 | 999,999,999 | Block version 2. Preparation for Zarcanum. Future activation. | +| HF4 | 999,999,999 | **Zarcanum privacy upgrade.** Confidential transactions, CLSAG ring signatures, Bulletproofs+, mandatory ring size 15, minimum 2 outputs per tx. | +| HF5 | 999,999,999 | **Confidential assets.** Asset deployment/emission/burn, BGE surjection proofs, tx version 3. | +| HF6 | 999,999,999 | **Block time halving.** PoW/PoS targets double to 240s, effectively halving emission rate. | + +### Testnet Schedule + +| HF | Height | Notes | +|----|--------|-------| +| HF0 | 0 | Active from genesis | +| HF1 | 0 | Active from genesis | +| HF2 | 10 | Early activation | +| HF3 | 0 | Active from genesis | +| HF4 | 100 | Zarcanum active early | +| HF5 | 200 | Confidential assets early | +| HF6 | 999,999,999 | Future | + +### Querying Hardfork State + +```go +// Highest active version at a given height +version := config.VersionAtHeight(config.MainnetForks, 15000) +// Returns: HF2 (since HF1 and HF2 activate after 10,080) + +// Check if a specific fork is active +active := config.IsHardForkActive(config.MainnetForks, config.HF4Zarcanum, 15000) +// Returns: false (HF4 not yet activated on mainnet) +``` + +### Pre-Hardfork Transaction Freeze + +From HF5 onwards, a freeze period of 60 blocks (`config.PreHardforkTxFreezePeriod`) applies before each hardfork activation. During this window, no new transactions (other than coinbase) are accepted into the mempool, ensuring chain stabilisation before consensus rule changes. + +## Genesis Block + +| Parameter | Value | +|-----------|-------| +| Timestamp | 2026-02-12 12:00 UTC | +| Unix timestamp | `1770897600` | +| Genesis nonce | `CURRENCY_FORMATION_VERSION + 101011010121` | +| Block major version | 1 | +| Block minor version | 0 | diff --git a/docs/rpc.md b/docs/rpc.md new file mode 100644 index 0000000..fbb3fe7 --- /dev/null +++ b/docs/rpc.md @@ -0,0 +1,355 @@ +--- +title: RPC Reference +description: Daemon and wallet JSON-RPC 2.0 API documentation. +--- + +# RPC Reference + +The Lethean node exposes two RPC interfaces: a **daemon** API for blockchain queries and node control, and a **wallet** API for account management and transactions. Both use JSON-RPC 2.0 over HTTP. + +- **Daemon RPC:** Port 36941 (mainnet), 46941 (testnet) +- **Wallet RPC:** User-configured port alongside the wallet process + +## Go Client + +The `rpc/` package provides a typed Go client: + +```go +import "forge.lthn.ai/core/go-blockchain/rpc" + +// Create a client (appends /json_rpc automatically) +client := rpc.NewClient("http://localhost:36941") + +// Or with a custom HTTP client +client := rpc.NewClientWithHTTP("http://localhost:36941", &http.Client{ + Timeout: 60 * time.Second, +}) +``` + +The client supports two transport modes: +- `call()` -- JSON-RPC 2.0 via `/json_rpc` (most endpoints) +- `legacyCall()` -- Plain JSON POST to dedicated URI paths (some endpoints registered via `MAP_URI_AUTO_JON2` in the C++ daemon) + +All daemon responses include a `"status": "OK"` field. The client checks this and returns an error for non-OK values. + +## Implemented Go Methods + +### Node Information + +```go +// Comprehensive daemon status +info, err := client.GetInfo() +// Returns: Height, TxCount, PowDifficulty, OutgoingConnectionsCount, +// IncomingConnectionsCount, BlockReward, TotalCoins, ... + +// Current blockchain height (legacy endpoint) +height, err := client.GetHeight() + +// Block count +count, err := client.GetBlockCount() +``` + +### Block Queries + +```go +// Most recent block header +header, err := client.GetLastBlockHeader() +// Returns: Height, Hash, Timestamp, MajorVersion, Difficulty, Reward, ... + +// Block header by height +header, err := client.GetBlockHeaderByHeight(1000) + +// Block header by hash +header, err := client.GetBlockHeaderByHash("ab3f...") + +// Full block details (range) +blocks, err := client.GetBlocksDetails(0, 10) +// Returns: []BlockDetails with transactions, cumulative difficulty, etc. +``` + +### Transaction Queries + +```go +// Detailed transaction info +txInfo, err := client.GetTxDetails("543b...") + +// Fetch raw transactions by hash (legacy endpoint) +txsHex, missed, err := client.GetTransactions([]string{"ab3f...", "cd5e..."}) +``` + +### Mining + +```go +// Submit a mined block +err := client.SubmitBlock("hexblob...") +``` + +### Wallet Support + +```go +// Fetch random decoy outputs for ring construction +outs, err := client.GetRandomOutputs(amount, 15) // 15 decoys for HF4+ + +// Broadcast a signed transaction +err := client.SendRawTransaction(txBlob) +``` + +## Go Response Types + +```go +type BlockHeader struct { + MajorVersion uint8 `json:"major_version"` + MinorVersion uint8 `json:"minor_version"` + Timestamp uint64 `json:"timestamp"` + PrevHash string `json:"prev_hash"` + Nonce uint64 `json:"nonce"` + OrphanStatus bool `json:"orphan_status"` + Height uint64 `json:"height"` + Depth uint64 `json:"depth"` + Hash string `json:"hash"` + Difficulty string `json:"difficulty"` + Reward uint64 `json:"reward"` +} + +type DaemonInfo struct { + Height uint64 `json:"height"` + TxCount uint64 `json:"tx_count"` + TxPoolSize uint64 `json:"tx_pool_size"` + AltBlocksCount uint64 `json:"alt_blocks_count"` + OutgoingConnectionsCount uint64 `json:"outgoing_connections_count"` + IncomingConnectionsCount uint64 `json:"incoming_connections_count"` + PowDifficulty uint64 `json:"pow_difficulty"` + PosDifficulty string `json:"pos_difficulty"` + BlockReward uint64 `json:"block_reward"` + DefaultFee uint64 `json:"default_fee"` + MinimumFee uint64 `json:"minimum_fee"` + TotalCoins string `json:"total_coins"` + PosAllowed bool `json:"pos_allowed"` + // ... +} +``` + +## Full Daemon RPC Reference + +The following tables document all methods available on the C++ daemon. Methods marked with **(Go)** have typed Go client implementations. + +### Node Information + +| Method | Description | Go | +|--------|-------------|-----| +| `getinfo` | Comprehensive node status | **(Go)** `GetInfo()` | +| `getheight` | Current blockchain height | **(Go)** `GetHeight()` | +| `getblockcount` | Total block count | **(Go)** `GetBlockCount()` | + +### Block Queries + +| Method | Description | Go | +|--------|-------------|-----| +| `getblockhash` | Hash of block at height | -- | +| `getblocktemplate` | Template for mining | -- | +| `submitblock` | Submit mined block | **(Go)** `SubmitBlock()` | +| `submitblock2` | Extended block submission | -- | +| `get_last_block_header` | Most recent block header | **(Go)** `GetLastBlockHeader()` | +| `get_block_header_by_hash` | Block header by hash | **(Go)** `GetBlockHeaderByHash()` | +| `get_block_header_by_height` | Block header by height | **(Go)** `GetBlockHeaderByHeight()` | +| `get_blocks_details` | Detailed block info (range) | **(Go)** `GetBlocksDetails()` | +| `get_block_details` | Single block details | -- | +| `get_alt_blocks_details` | Alternative chain blocks | -- | + +### Transaction Queries + +| Method | Description | Go | +|--------|-------------|-----| +| `gettransactions` | Fetch txs by hash | **(Go)** `GetTransactions()` | +| `get_tx_details` | Detailed tx information | **(Go)** `GetTxDetails()` | +| `decrypt_tx_details` | Decrypt private tx data | -- | +| `get_tx_global_outputs_indexes` | Global output indices | -- | +| `search_by_id` | Search blocks/txs by hash | -- | +| `get_est_height_from_date` | Estimate height from timestamp | -- | + +### Transaction Pool + +| Method | Description | +|--------|-------------| +| `get_pool_info` | Pool status and pending aliases | +| `get_tx_pool` | All pool transactions | +| `get_pool_txs_details` | Detailed pool tx info | +| `get_pool_txs_brief_details` | Brief pool tx summaries | +| `get_all_pool_tx_list` | List all pool tx hashes | +| `get_current_core_tx_expiration_median` | Expiration median timestamp | +| `reset_tx_pool` | Clear transaction pool | +| `remove_tx_from_pool` | Remove specific tx | + +### Key Images + +| Method | Description | +|--------|-------------| +| `check_keyimages` | Check spent status (1=unspent, 0=spent) | + +### Output Selection + +| Method | Description | Go | +|--------|-------------|-----| +| `get_random_outs` | Random outputs for mixing | **(Go)** `GetRandomOutputs()` | +| `get_random_outs3` | Version 3 random outputs | -- | +| `get_multisig_info` | Multisig output details | -- | +| `get_global_index_info` | Global output index stats | -- | + +### Alias Operations + +| Method | Description | +|--------|-------------| +| `get_alias_details` | Resolve alias to address | +| `get_alias_reward` | Cost to register alias | +| `get_all_aliases` | List all registered aliases | +| `get_aliases` | Paginated alias list | +| `get_aliases_by_address` | Aliases for an address | +| `get_integrated_address` | Create integrated address | + +### Asset Operations + +| Method | Description | +|--------|-------------| +| `get_asset_info` | Asset details by ID | +| `get_assets_list` | List registered assets | + +### Mining Control + +| Method | Description | +|--------|-------------| +| `start_mining` | Start PoW mining | +| `stop_mining` | Stop PoW mining | +| `get_pos_mining_details` | PoS staking details | + +### Raw Transaction + +| Method | Description | Go | +|--------|-------------|-----| +| `sendrawtransaction` | Broadcast raw tx | **(Go)** `SendRawTransaction()` | +| `force_relay` | Force relay tx blobs | -- | + +### Binary Endpoints + +Some methods use binary (epee portable storage) serialisation for performance: + +| Endpoint | Description | +|----------|-------------| +| `/getblocks.bin` | Fast block sync | +| `/get_o_indexes.bin` | Output global indices | +| `/getrandom_outs.bin` | Random outputs for ring construction | + +## Full Wallet RPC Reference + +### Wallet State + +| Method | Description | +|--------|-------------| +| `getbalance` | Account balance (total + unlocked) | +| `getaddress` | Wallet address | +| `get_wallet_info` | Wallet metadata | +| `get_wallet_restore_info` | Seed phrase for backup | +| `get_seed_phrase_info` | Validate a seed phrase | +| `store` | Save wallet to disk | + +### Transfers + +| Method | Description | +|--------|-------------| +| `transfer` | Send LTHN (destinations, fee, mixin, payment_id, comment) | +| `get_payments` | Payments by payment ID | +| `get_bulk_payments` | Payments for multiple IDs | +| `get_recent_txs_and_info` | Recent transaction history | +| `search_for_transactions` | Search wallet transactions | + +### Address Utilities + +| Method | Description | +|--------|-------------| +| `make_integrated_address` | Create integrated address | +| `split_integrated_address` | Decode integrated address | + +### Alias Management + +| Method | Description | +|--------|-------------| +| `register_alias` | Register new alias | +| `update_alias` | Update alias details | + +### Offline Signing + +| Method | Description | +|--------|-------------| +| `sign_transfer` | Sign an unsigned tx | +| `submit_transfer` | Broadcast signed tx | + +### Output Management + +| Method | Description | +|--------|-------------| +| `sweep_below` | Consolidate small outputs | +| `get_bare_outs_stats` | Statistics of bare outputs | +| `sweep_bare_outs` | Convert bare outputs to confidential | + +### HTLC (Hash Time-Locked Contracts) + +| Method | Description | +|--------|-------------| +| `create_htlc_proposal` | Create HTLC proposal | +| `get_list_of_active_htlc` | List active HTLCs | +| `redeem_htlc` | Redeem HTLC with preimage | +| `check_htlc_redeemed` | Check redemption status | + +### Ionic Swaps + +| Method | Description | +|--------|-------------| +| `ionic_swap_generate_proposal` | Create swap proposal | +| `ionic_swap_get_proposal_info` | Decode swap proposal | +| `ionic_swap_accept_proposal` | Accept and execute swap | + +### Asset Operations + +| Method | Description | +|--------|-------------| +| `assets_deploy` | Register new asset (ticker, name, supply) | +| `assets_emit` | Emit additional supply | +| `assets_update` | Update asset metadata | +| `assets_burn` | Burn asset supply | +| `assets_whitelist_get` | Get whitelisted assets | +| `assets_whitelist_add` | Whitelist an asset | +| `assets_whitelist_remove` | Remove from whitelist | + +### Escrow Contracts + +| Method | Description | +|--------|-------------| +| `contracts_send_proposal` | Send escrow proposal | +| `contracts_accept_proposal` | Accept escrow proposal | +| `contracts_get_all` | List all contracts | +| `contracts_release` | Release escrow funds | +| `contracts_request_cancel` | Request cancellation | +| `contracts_accept_cancel` | Accept cancellation | + +### Marketplace + +| Method | Description | +|--------|-------------| +| `marketplace_get_my_offers` | List own offers | +| `marketplace_push_offer` | Create offer | +| `marketplace_push_update_offer` | Update offer | +| `marketplace_cancel_offer` | Cancel offer | + +### Cryptographic Utilities + +| Method | Description | +|--------|-------------| +| `sign_message` | Sign arbitrary message | +| `encrypt_data` | Encrypt data | +| `decrypt_data` | Decrypt data | + +## Wire Format Notes + +- **Daemon JSON-RPC:** Standard JSON-RPC 2.0 at `/json_rpc`. Method calls use `{"method": "getinfo", "params": {...}}`. +- **Wallet JSON-RPC:** Same format at `/json_rpc` on the wallet RPC port. +- **Binary endpoints:** Use epee portable storage serialisation, accessed via direct HTTP POST to the endpoint path. +- **P2P protocol:** Uses the Levin binary protocol (see [Networking](networking.md)), not JSON-RPC.